Skip to content

feat(playlist): auto-detect import method that parses pasted provider messages - #1445

Open
4gray wants to merge 17 commits into
masterfrom
claude/llm-credentials-parser-hndeb5
Open

feat(playlist): auto-detect import method that parses pasted provider messages#1445
4gray wants to merge 17 commits into
masterfrom
claude/llm-credentials-parser-hndeb5

Conversation

@4gray

@4gray 4gray commented Aug 15, 2026

Copy link
Copy Markdown
Owner

What changed

The Add playlist dialog gains a sixth method, Auto-detect: paste whatever the provider sent — links, Xtream credentials, a MAC address with device identity — and the app recognizes the source(s) and prefills the matching import form.

  • New deterministic parser detectProviderImportCandidates (libs/shared/interfaces): extracts URLs, MAC addresses, and labeled fields (username/password/host+port/serial/device IDs/signatures), classifies each finding as Xtream, Stalker, or an M3U link/body, and returns ranked candidates. Pure, synchronous, runs on every keystroke.
  • Handles real reseller message shapes, built against a corpus of 19 genuine handouts kept verbatim in the spec: Unicode "font" labels (math alphabets via NFKC; squared/negative-circled/regional-indicator glyphs via a code-point folder that leaves real flag emoji alone), arrow/dingbat separators (, , =>), separator-less hex serials (SN 38415…), dual device IDs (DEVICE ID=> 1&2 <hex>, ¹💥²), multi-MAC account lists (one candidate per MAC), bare URL + user + pass three-line handouts (low-confidence guess), a guard so ADULT PASS (parental PIN) is never read as the account password, and Turkish MAC ADRESİ labels.
  • New auto-import surface in libs/playlist/import/feature: candidate cards with confidence badges and masked passwords; picking one switches the dialog to the matching form with fields prefilled. The pasted text survives switching methods and back.
  • Safety rails: every extracted value is a verbatim substring of the (NFKC-folded) input or comes from an existing shared normalizer — the parser never invents or "fixes" a credential; detection only proposes, the target form's own validation and behavioral probes (Stalker endpoint discovery, Xtream connection test) remain the sole path into the store. Query-string credentials are mined only by URL parsing, never by label matching (URL spans are masked first). Works identically in Electron and PWA; nothing pasted leaves the app.
  • i18n keys added to all 19 language files; CLAUDE.md updated.

Why

Providers routinely share credentials as free-form chat messages without saying whether it is Xtream, Stalker, or M3U. Users had to know which of five import forms to pick and copy fields one by one; now they paste the message and confirm a prefilled form.

Release note

  • Added a note under .changes/ (playlist-auto-detect-import.md)
  • Not needed (test-only, docs, CI, or a refactor with no behavior change)

Checks

  • Tests added or updated for the changed behavior (parser spec with the 19-message corpus, auto-import component spec, dialog handoff/prefill specs — 300 tests in shared-interfaces, 78 in playlist-import-feature)
  • pnpm run lint and the affected pnpm nx test <project> pass (shared-interfaces, playlist-import-feature, playlist-shared-ui; pnpm nx build web green)

🤖 Generated with Claude Code

https://claude.ai/code/session_01JE1De2dfyRZMYh4H2iGXb4


Generated by Claude Code

claude added 6 commits August 11, 2026 11:41
… messages

Adds a deterministic parser (detectProviderImportCandidates in
libs/shared/interfaces) that scans a pasted provider message for URLs, MAC
addresses, and labeled credentials/identity fields (username, password,
serial, device IDs, signatures), classifies the source as Xtream, Stalker,
or an M3U link/body, and returns ranked candidates. Every extracted value is
a verbatim substring or a canonical form from an existing shared normalizer;
detection only proposes — the existing forms and their behavioral probes
(portal discovery, connection test) stay authoritative.

The Add playlist dialog gains a sixth "Auto-detect" method with a textarea
that runs the parser on every edit and renders candidate cards; picking one
switches to the matching import form with the fields prefilled.

Detection runs fully locally; pasted text never leaves the app.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JE1De2dfyRZMYh4H2iGXb4
… methods

The auto-detect surface is destroyed by the dialog's method switch, so
inspecting a prefilled form and coming back lost the paste. The text now
lives on the dialog and is fed back into the surface on re-entry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JE1De2dfyRZMYh4H2iGXb4
…port

Reseller messages dress labels in Unicode math alphabets and arrow
separators to slip past chat spam filters. The detector now NFKC-normalizes
the pasted text before scanning, so decorated labels fold back to the plain
ASCII the vocabulary matches, and accepts arrow/geometric/dingbat glyphs,
'>' and a space-surrounded dash as label separators alongside ':' and '='.
A hyphen glued inside a word stays prose, so hyphenated words are still not
read as labeled values.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JE1De2dfyRZMYh4H2iGXb4
…s in auto-detect

Extends the deterministic detector for three real reseller message shapes:

- Negative-circled / squared / regional-indicator 'font' capitals and circled
  digits (🅤🅢🅔🅡, 🅜➌🅤) that NFKC leaves alone are folded to the ASCII they
  depict before scanning, so decorated USER/PASS/URL labels are recognized.
- 'DEVICE ID=> 1&2 <hex>' hands one hex value to both device-ID slots.
- 'S N', 'S/N', 'S.N' serial labels, the Turkish 'MAC ADRESİ' label, and a
  guard so 'ADULT PASS' (the parental PIN) is not read as the account password.

All three pasted messages are covered verbatim by new parser tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JE1De2dfyRZMYh4H2iGXb4
Regional-indicator runs of exactly two are real flag emoji (🇳🇱); folding
them glued letters onto adjacent text and could break a following label's
word boundary. Only runs of three or more — fancy-text words like 🇺🇸🇪🇷 —
fold to ASCII now.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JE1De2dfyRZMYh4H2iGXb4
…, decorated dual device IDs

Second corpus batch (14 real reseller messages; 11 already parsed). New:

- Bare 'URL, token, token' handouts with no labels at all become a
  low-confidence Xtream guess — only when the message contains exactly those
  three lines, a single generic URL, and no label/MAC produced anything.
- Separator-less serials ('SN 38415545307A3') are accepted when the value is
  hex-shaped, so prose cannot match.
- The dual device-ID marker now allows any symbol between 1 and 2 (¹💥², 1/2)
  and whitespace-only label separators, again guarded by the hex value shape.
- A separately labeled port completes a port-less 'Portal: http://host' URL.

Nine messages from the batch join the test corpus verbatim.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JE1De2dfyRZMYh4H2iGXb4
@greptile-apps

greptile-apps Bot commented Aug 15, 2026

Copy link
Copy Markdown

Greptile Summary

The PR adds an auto-detect playlist-import method that parses pasted provider messages and prefills the existing M3U, Xtream, or Stalker forms.

  • Adds deterministic provider-message scanning and ranked import candidates.
  • Adds the auto-import UI, dialog handoff, localization, tests, documentation, and a release note.
  • The multi-MAC identity cross-pairing fix is present, but account extraction still truncates lists after twelve MAC addresses.

Confidence Score: 4/5

The PR should not merge until multi-account messages stop silently losing every MAC account after the twelfth.

The earlier candidate-limit issue remains reachable because extractMacAddresses still slices detected accounts to twelve before candidate assembly, so larger provider handouts produce incomplete import choices despite the claimed fix.

Files Needing Attention: libs/shared/interfaces/src/lib/provider-import-scan.util.ts

Important Files Changed

Filename Overview
libs/shared/interfaces/src/lib/provider-import-scan.util.ts Adds provider-message scanning and raises extraction caps, but the twelve-MAC slice still silently drops later accounts.
libs/shared/interfaces/src/lib/provider-import-detection.util.ts Assembles ranked candidates and now avoids attaching global identity or ambiguous portals across multiple MAC accounts.
libs/playlist/import/feature/src/lib/auto-import/auto-import.component.ts Adds the local auto-detection surface, candidate summaries, password masking, and selection output.
libs/playlist/import/feature/src/lib/add-playlist-dialog/add-playlist-dialog.component.ts Integrates auto-detect into the dialog and hands selected candidates to the appropriate existing import form.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Paste provider message] --> B[Normalize and scan]
  B --> C[Rank import candidates]
  C --> D{Selected candidate}
  D -->|Xtream| E[Prefilled Xtream form]
  D -->|Stalker| F[Prefilled Stalker form]
  D -->|M3U URL| G[Prefilled URL form]
  D -->|M3U body| H[Prefilled text form]
  E --> I[Existing validation and import]
  F --> I
  G --> I
  H --> I
Loading
Prompt To Fix All With AI
### Issue 1
libs/shared/interfaces/src/lib/provider-import-scan.util.ts:404
**Account extraction still truncates**

When a provider message contains thirteen or more distinct MAC accounts, `extractMacAddresses` keeps only the first twelve before candidate assembly, causing every later account to disappear from the selectable import results.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (2): Last reviewed commit: "ci: retrigger checks after category-mana..." | Re-trigger Greptile

Comment thread libs/shared/interfaces/src/lib/provider-import-detection.util.ts
Comment thread libs/shared/interfaces/src/lib/provider-import-scan.util.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e17a4ba641

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +109 to +110
const portal = pickStalkerPortalUrl(urls, labeled);
for (const macAddress of macs) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep each Stalker record correlated with its MAC

When the pasted message contains multiple complete Stalker accounts, the portal and labeled identity fields are selected once before this loop and then reused for every detected MAC. For example, two portal/MAC/device-ID blocks produce a second candidate containing the first block's portal and device ID, which can make authentication fail or submit another device's identity with that MAC. Parse and associate fields per account block rather than combining every MAC with the first global values.

Useful? React with 👍 / 👎.

*/
const LABEL_SEPARATOR = String.raw`(?:\s*(?:[:=>]|[←-⇿■-◿☀-➿⬀-⯿])+\s*|\s+[-–—]+\s+)`;

const DEFAULT_VALUE = String.raw`[^\s,;|]+`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve punctuation in labeled credentials

When a labeled username or password contains a comma, semicolon, or pipe, this value pattern stops at that character and silently prefills a truncated credential—for example, PASS: abc,123 becomes abc. These characters are accepted by the target forms, and the masked password field makes the corruption easy to miss, resulting in failed connection checks; delimiters should be identified from the field boundary without removing valid credential characters.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deliberate trade-off rather than an oversight. ,/;/| double as inline pair delimiters in real handouts (User: a, Pass: b on one line — present in the collected corpus), and there is no deterministic way to tell that comma from one inside a password. The cost asymmetry drives the choice: panel software (Xtream-UI/XUI) generates alphanumeric passwords, so comma-in-password is rare, while inline pairs are common — and the truncated value lands in an editable form the user reviews before anything runs (the form shows the password in clear, only the candidate card masks it). If corpus samples with punctuated passwords show up, the planned fix is boundary-aware capture (delimiter only when followed by whitespace or a known label), not dropping the delimiters.


Generated by Claude Code

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an Auto-detect method to the Add playlist dialog, enabling users to paste a provider message and have IPTVnator detect likely import sources (Xtream/Stalker/M3U) and prefill the appropriate existing import form.

Changes:

  • Introduces deterministic provider-message scanning + candidate ranking via detectProviderImportCandidates in libs/shared/interfaces.
  • Adds the Auto-detect UI surface (textarea + candidate cards) and dialog handoff/prefill wiring.
  • Extends playlist-type unions, adds i18n strings across languages, updates docs, and adds a release note.

Reviewed changes

Copilot reviewed 36 out of 36 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
libs/workspace/shell/util/src/lib/workspace-shell-actions.ts Extends workspace playlist-type union to include auto.
libs/portal/shared/util/src/lib/portal-shell-actions.ts Extends portal playlist-type union to include auto.
libs/playlist/shared/ui/src/lib/add-playlist-menu/playlist-type.ts Extends shared playlist type to include auto.
libs/shared/interfaces/src/lib/provider-import-scan.util.ts Adds low-level scanners (URLs, labeled fields, MACs, Unicode folding) for auto-detect.
libs/shared/interfaces/src/lib/provider-import-detection.util.ts Adds detectProviderImportCandidates candidate assembly/ranking and helpers.
libs/shared/interfaces/src/lib/provider-import-detection.util.spec.ts Adds comprehensive detection tests covering multiple real-world message shapes.
libs/shared/interfaces/src/index.ts Exports provider-import-detection.util from shared interfaces package.
libs/playlist/import/feature/src/lib/auto-import/auto-import.component.ts Implements auto-detect UI component (paste area + candidate list + selection emit).
libs/playlist/import/feature/src/lib/auto-import/auto-import.component.html Template for auto-detect textarea and candidate cards.
libs/playlist/import/feature/src/lib/auto-import/auto-import.component.scss Styles for the auto-detect surface and candidate cards.
libs/playlist/import/feature/src/lib/auto-import/auto-import.component.spec.ts Unit tests for candidate computation, restoring text, and event emission.
libs/playlist/import/feature/src/lib/add-playlist-dialog/add-playlist-dialog.component.ts Wires auto-detect into the dialog and applies prefills to target forms.
libs/playlist/import/feature/src/lib/add-playlist-dialog/add-playlist-dialog.component.html Adds auto switch case rendering the auto-detect component.
libs/playlist/import/feature/src/lib/add-playlist-dialog/add-playlist-dialog.component.scss Updates method grid layout to accommodate 6 methods.
libs/playlist/import/feature/src/lib/add-playlist-dialog/add-playlist-dialog.component.spec.ts Adds tests for auto-detect candidate handoff and form prefills.
CLAUDE.md Updates repo documentation to mention the new auto-detect import method.
apps/web/src/assets/i18n/en.json Adds auto-detect method strings and auto-detect UI strings.
apps/web/src/assets/i18n/de.json Adds auto-detect method strings and auto-detect UI strings.
apps/web/src/assets/i18n/el.json Adds auto-detect method strings and auto-detect UI strings.
apps/web/src/assets/i18n/es.json Adds auto-detect method strings and auto-detect UI strings.
apps/web/src/assets/i18n/fr.json Adds auto-detect method strings and auto-detect UI strings.
apps/web/src/assets/i18n/hu.json Adds auto-detect method strings and auto-detect UI strings.
apps/web/src/assets/i18n/it.json Adds auto-detect method strings and auto-detect UI strings.
apps/web/src/assets/i18n/ja.json Adds auto-detect method strings and auto-detect UI strings.
apps/web/src/assets/i18n/ko.json Adds auto-detect method strings and auto-detect UI strings.
apps/web/src/assets/i18n/nl.json Adds auto-detect method strings and auto-detect UI strings.
apps/web/src/assets/i18n/pl.json Adds auto-detect method strings and auto-detect UI strings.
apps/web/src/assets/i18n/pt.json Adds auto-detect method strings and auto-detect UI strings.
apps/web/src/assets/i18n/ru.json Adds auto-detect method strings and auto-detect UI strings.
apps/web/src/assets/i18n/tr.json Adds auto-detect method strings and auto-detect UI strings.
apps/web/src/assets/i18n/zh.json Adds auto-detect method strings and auto-detect UI strings.
apps/web/src/assets/i18n/zhtw.json Adds auto-detect method strings and auto-detect UI strings.
apps/web/src/assets/i18n/ar.json Adds auto-detect method strings and auto-detect UI strings.
apps/web/src/assets/i18n/ary.json Adds auto-detect method strings and auto-detect UI strings.
apps/web/src/assets/i18n/by.json Adds auto-detect method strings and auto-detect UI strings.
.changes/playlist-auto-detect-import.md Adds a user-facing release note for the new auto-detect import method.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +53 to +54
const WRAPPING_CHARS = /^["'`«<([]+|["'`»>)\]]+$/g;

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pattern is an alternation, not a single anchored expression: /^[wrappers]+|[wrappers]+$/g — with the g flag, replace removes the leading run and the trailing run in one pass. Verified: stripWrapping('"user"') === 'user', stripWrapping('(pass)') === 'pass', stripWrapping('«val»') === 'val'. An anchored-whole-string reading would require /^[wrappers]+$/, which this is not.


Generated by Claude Code

Comment on lines +320 to +327
const key = [
candidate.kind,
candidate.url ?? '',
candidate.serverUrl ?? '',
candidate.username ?? '',
candidate.portalUrl ?? '',
candidate.macAddress ?? '',
].join('|');
…MACs, honest caps, password in dedupe key

- Multi-MAC messages no longer attach globally scanned serial/device-ID/
  signature/credential labels to every MAC: with several accounts in one
  message there is no reliable owner for a first-match label, and a device ID
  submitted with the wrong MAC is pinned by the portal permanently. Identity
  fields now attach only when exactly one MAC is present.
- MAC and candidate caps raised (12/16) so real multi-account lists are never
  silently truncated; the caps remain only as pathological-paste guards.
- The candidate dedupe key includes the password, so two accounts sharing a
  server and username (e.g. a rotation message) both stay selectable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JE1De2dfyRZMYh4H2iGXb4
@4gray
4gray requested a lite review from Copilot August 15, 2026 10:24

4gray commented Aug 15, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Round-1 findings are addressed in 98c3b44 (no identity cross-pairing across multiple MACs, raised truncation caps, password included in the candidate dedupe key); the two declined threads carry replies with reasoning. Requesting a fresh pass on the current head.


Generated by Claude Code

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 98c3b442f0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +118 to +125
const portal = pickStalkerPortalUrl(urls, labeled);
const identity: LabeledFields = macs.length === 1 ? labeled : {};
for (const macAddress of macs) {
candidates.push(
compact({
kind: 'stalker',
confidence: portal?.confidence ?? 'low',
portalUrl: portal?.url,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Correlate each Stalker portal with its MAC

When a paste contains two complete Stalker blocks using different portals, portal is still selected once from the first URL and then assigned to every detected MAC, so the second candidate is prefilled with the first account's portal and cannot authenticate. The 98c3b44 follow-up only suppresses identity fields for multi-MAC input; it leaves this cross-pair intact. Associate portals and MACs by account block, or omit the portal when the association is ambiguous.

Useful? React with 👍 / 👎.

Comment on lines +102 to +104
const normalized = foldDecorativeAlphabets(raw.normalize('NFKC'));
const urls = extractUrls(normalized);
const labeled = extractLabeledFields(normalized, urls);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve credential bytes while normalizing labels

When a provider uses a valid Unicode compatibility character in a username or password—for example PASS: pass123—normalizing the entire paste before extraction rewrites the credential to pass123, and selecting the candidate submits different bytes so authentication fails. Normalize only text used to recognize labels, or retain source spans and copy credential values from the original paste.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deliberate, and documented at the fold site: the whole point of the NFKC+decorative-fold pass is that chat handouts dress ASCII in compatibility glyphs — labels (𝙋𝙰𝚂𝚂) and values alike. Panel software (Xtream-UI/XUI) generates ASCII credentials; a fullwidth pass123 in a Telegram paste is the same decoration artifact as the labels around it, and folding it back to pass123 recovers the bytes the panel actually knows — submitting the fullwidth original is what would fail. Characters NFKC leaves alone (Cyrillic, Arabic, plain ASCII) pass through untouched. Span-mapping values back through a length-changing normalization would add real complexity to guard a shape absent from the 19-message corpus this was built against; if a genuinely non-compatibility credential shows up there, this gets revisited with evidence.


Generated by Claude Code


const WRAPPING_CHARS = /^["'`«<([]+|["'`»>)\]]+$/g;

const MAX_URLS = 8;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Do not discard URLs before applying the candidate cap

When a provider message contains nine or more distinct playlist links, extractUrls stops at eight, so later links never become candidates even though the public candidate cap was raised to 16. A nine-link paste currently returns only eight results; raise this scanner cap consistently or apply a single cap after candidate assembly.

Useful? React with 👍 / 👎.

…nner and candidate caps

- Several MACs meeting portal-shaped URLs on different hosts now yield
  MAC-only candidates: there is no deterministic owner for either portal, and
  prefilling half the accounts with the wrong panel only fails later at
  authentication. Same-host Real/Panel pairs and one portal shared by a MAC
  list stay unambiguous.
- The URL scanner cap is raised to match the candidate cap (16), so a
  nine-link handout is no longer truncated to eight before assembly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JE1De2dfyRZMYh4H2iGXb4

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 36 out of 36 changed files in this pull request and generated no new comments.

Suppressed comments (2)

libs/playlist/import/feature/src/lib/auto-import/auto-import.component.ts:135

  • The candidate summary for m3u-url renders the full URL verbatim. For common Xtream-style links (get.php?...username=...&password=...) this exposes credentials in the UI even though passwords are otherwise masked on candidate cards. Consider masking sensitive URL userinfo/query params (at least username/password) for display, while keeping the original candidate.url unchanged for prefilling the form.
        switch (candidate.kind) {
            case 'm3u-url':
                push('HOME.URL_UPLOAD.PLAYLIST_URL', candidate.url);
                break;

libs/shared/interfaces/src/lib/provider-import-detection.util.ts:29

  • The doc comment claims every detected value is a verbatim substring of the NFKC-normalized input (or an existing normalizer output), but the implementation can also synthesize URLs via labeledHostUrl() (adds http:// and/or a separately labeled port). Updating the comment to mention this exception would keep the contract accurate for future maintainers.

The packaged frame-copy smoke on the linux portable build timed out waiting
for the first rendered frame (llvmpipe runner); the same job passed on the
previous commits of this branch and nothing in this PR touches playback.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JE1De2dfyRZMYh4H2iGXb4
@4gray

4gray commented Aug 15, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9b58ed505a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const detected: DetectedUrl[] = [];
const seen = new Set<string>();
for (const match of allMatches(URL_PATTERN, text)) {
const raw = match[0].replace(TRAILING_PUNCTUATION, '');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve terminal punctuation in URL credentials

When an Xtream URL contains an unescaped password ending in valid URL punctuation, such as get.php?username=u&password=abc!, this unconditional cleanup strips the final character before query parsing and prefills abc instead of abc!; it also alters the fallback M3U URL. Strip punctuation only when it is provably prose surrounding the URL, not from a successfully parsed query credential.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declined as the lesser harm, deterministically unresolvable either way: …password=abc! (credential ends in !) and Check http://host/get.php?username=u&password=abc! (sentence exclamation after the URL) are byte-identical inputs — no parser can tell them apart. The trade-off picks by frequency and cost: URLs wrapped in prose punctuation are pervasive in chat handouts (the corpus includes one), panel-generated passwords are alphanumeric, and a truncated credential fails visibly at Test Connection in an editable field. Stripping only "provably prose" punctuation is not implementable — the proof does not exist in the string. If corpus samples with punctuation-terminal query credentials appear, the candidate fix is offering both the stripped and unstripped variant, not disabling the strip.


Generated by Claude Code

Comment on lines +165 to +169
creds?.username ??
(macs.length === 0 ? labeled.username : undefined);
const password =
creds?.password ??
(macs.length === 0 ? labeled.password : undefined);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Correlate labeled Xtream credentials per account

When a paste contains multiple Xtream blocks whose API URLs do not carry query credentials, every URL receives the first globally extracted username and password. For example, separate one.test/player_api.php/alice and two.test/player_api.php/bob blocks produce a second candidate for two.test with alice's credentials, so the prefilled connection fails; associate labeled fields with their surrounding URL/account block rather than reusing labeled for the whole paste.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declined for the inverse reason the MAC case was fixed. Multiple API URLs sharing one labeled credential pair is the COMMON real shape — reseller "main + backup server" handouts, one account valid on both hosts — and suppressing or first-URL-only attachment would break exactly that. Two separate accounts as separate labeled blocks in one paste is absent from the 19-message corpus. The cost asymmetry also differs from the Stalker fix: a wrong Xtream credential fails visibly at Test Connection in an editable form and costs nothing, while a device ID submitted with the wrong MAC is pinned by the portal permanently — that irreversibility is why multi-MAC got the conservative treatment and this stays as-is. Revisited if a multi-account Xtream block sample lands in the corpus.


Generated by Claude Code

Comment on lines +124 to +128
const stalkerHosts = new Set(
urls
.filter((url) => url.role === 'stalker')
.map((url) => url.parsed.hostname)
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include generic portals in the ambiguity check

When multiple MAC blocks use different root or otherwise generic portal URLs, this host set remains empty because it includes only URLs already classified as stalker; pickStalkerPortalUrl nevertheless accepts the first generic URL, so every MAC is still paired with that first portal. Fresh evidence in the current tip is this mismatch between the new ambiguity guard and the existing generic-URL fallback; include generic portal candidates in the multi-host ambiguity check or omit the portal in this case.

Useful? React with 👍 / 👎.

claude added 2 commits August 15, 2026 12:07
… ambiguity guard

The round-2 guard judged ambiguity over portal-shaped URLs only, while the
portal picker falls back to a generic URL when no shaped one exists — two
root-URL panels with several MACs still cross-paired through that fallback.
The guard now inspects the exact pool the picker draws from.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JE1De2dfyRZMYh4H2iGXb4
One predicate timeout in category-management.e2e.ts on the macOS runner
(115 passed / 1 failed); the same suite is green on Ubuntu and Windows in
the same run, and nothing in this PR touches category management. The
earlier frame-copy smoke flake passed on this run, confirming both as
runner timing flakes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JE1De2dfyRZMYh4H2iGXb4

4gray commented Aug 15, 2026

Copy link
Copy Markdown
Owner Author

@greptileai review
@codex review

Requesting fresh passes on the current head (c0df0df). Note for the Greptile summary above: its 3/5 verdict reviewed e17a4ba and is stale — both blocking P1s are fixed since 98c3b44 (identity fields no longer attach across multiple MACs; MAC/URL/candidate caps raised to 12/16/16 so multi-account lists surface fully), plus follow-ups 1d59932/3248c54 extend the same guard to ambiguous portals including the generic-URL fallback. Declined threads carry replies with reasoning.


Generated by Claude Code

for (const match of allMatches(BARE_INFOMIR_MAC_PATTERN, text)) {
push(normalizeStalkerMacAddress(match[0]));
}
return macs.slice(0, MAX_MACS);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Account extraction still truncates

When a provider message contains thirteen or more distinct MAC accounts, extractMacAddresses keeps only the first twelve before candidate assembly, causing every later account to disappear from the selectable import results.

Prompt To Fix With AI
This is a comment left during a code review.
Path: libs/shared/interfaces/src/lib/provider-import-scan.util.ts
Line: 404

Comment:
**Account extraction still truncates**

When a provider message contains thirteen or more distinct MAC accounts, `extractMacAddresses` keeps only the first twelve before candidate assembly, causing every later account to disappear from the selectable import results.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c0df0dfabd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

: urls.filter((url) => url.role === 'generic');
const portalAmbiguous =
macs.length > 1 &&
new Set(portalPool.map((url) => url.parsed.hostname)).size > 1;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Treat distinct portal origins as ambiguous

When a multi-account paste contains two Stalker panels on the same DNS name but different ports, such as http://panel.example:8080/c/ and http://panel.example:9090/c/, both hostname values compare equal, so this guard treats the paste as unambiguous and assigns the first portal URL to every MAC. Fresh evidence in the current head is that the ambiguity check still ignores the port; compare origins or otherwise include the port so candidates are not prefilled with the wrong panel.

Useful? React with 👍 / 👎.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 36 out of 36 changed files in this pull request and generated no new comments.

Suppressed comments (2)

libs/playlist/import/feature/src/lib/auto-import/auto-import.component.ts:134

  • The auto-detect cards currently render m3u-url candidates using the raw URL string. For get.php?...&password=... links this exposes the password in cleartext on the candidate card, even though other password fields are masked. Mask query-string passwords before displaying the URL (keep the full URL value in the candidate object for prefilling).
        switch (candidate.kind) {
            case 'm3u-url':
                push('HOME.URL_UPLOAD.PLAYLIST_URL', candidate.url);
                break;

libs/playlist/import/feature/src/lib/add-playlist-dialog/add-playlist-dialog.component.ts:184

  • pendingPrefill is only cleared after a successful prefill. If the user selects an auto-detect candidate and then clicks a different method before the target child component is instantiated, the candidate stays pending and can later prefill unexpectedly when the user visits that method. Clear the pending candidate when the current method no longer matches the candidate’s intended target form.
    private applyPendingPrefill(): void {
        const candidate = this.pendingPrefill();
        if (!candidate) {
            return;
        }
        switch (candidate.kind) {

…ortal ambiguity

- The MAC and candidate caps are raised to 64/96: they exist to keep a
  pathological paste (a log full of MAC-shaped hex) from rendering thousands
  of cards, not to bound real handouts — scanner dumps top out around a
  couple dozen accounts, so no real multi-account message is truncated.
- The multi-MAC portal ambiguity guard compares URL origins instead of
  hostnames, so two panels on one DNS name but different ports no longer
  cross-pair; URL normalization drops default ports, keeping the Real/Panel
  ':80'-vs-bare pairs of scanner dumps unambiguous.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JE1De2dfyRZMYh4H2iGXb4
@4gray

4gray commented Aug 15, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c67f0f8eb4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

export function extractUrls(text: string): DetectedUrl[] {
const detected: DetectedUrl[] = [];
const seen = new Set<string>();
for (const match of allMatches(URL_PATTERN, text)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Stop URL scanning once the candidate cap is reached

When a large provider dump or log contains many URL-shaped strings, allMatches materializes every regex match before this loop begins, so the MAX_URLS break does not bound scanning or allocation. Because detection runs synchronously on every textarea edit, a sufficiently large paste can freeze the renderer or consume substantial memory despite the stated DOM-safety cap; iterate the matcher lazily so processing can stop after enough valid unique URLs are collected.

Useful? React with 👍 / 👎.

Comment on lines +32 to +35
.auto-import__candidate-head {
display: flex;
align-items: center;
gap: 8px;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Allow candidate headers to wrap on phone widths

On narrow phone viewports, this single-line flex row cannot fit the fixed icon, non-wrapping confidence badge, candidate title, gaps, and Material action button inside the viewport-clamped dialog. At roughly 320–375 px wide, the header overflows horizontally and can clip or require horizontal scrolling to reach “Fill the form”; add a narrow-width wrapping or stacked layout for the badge and action.

Useful? React with 👍 / 👎.

claude added 2 commits August 15, 2026 19:20
- URL and MAC extraction drive their regex matchers lazily and stop at the
  caps instead of materializing every match of a pathological paste first —
  detection runs on each keystroke, so allocation must stop where the cap
  stops accepting.
- The candidate card header wraps on narrow phone dialogs instead of
  overflowing horizontally past the Fill-the-form action.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JE1De2dfyRZMYh4H2iGXb4
…ect prefills, doc the assembled-host exception

Addresses Copilot's suppressed review comments:

- The m3u-url candidate card no longer shows a get.php password in clear —
  the display masks the query value while the untouched URL still prefills
  the form.
- A picked candidate is dropped if the user switches to another method
  before its target form mounts, so it cannot lie in wait and prefill a
  later visit.
- The parser contract now names its one assembled-value exception
  (labeledHostUrl completing a scheme-less host with http:// and a labeled
  port).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JE1De2dfyRZMYh4H2iGXb4
@4gray

4gray commented Aug 15, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 526e8abc3e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const URL_PATTERN = /https?:\/\/[^\s<>"'`]+/gi;
const TRAILING_PUNCTUATION = /[),.;:!?\]»›]+$/;
const M3U_PATH_PATTERN = /\.m3u8?$/i;
const XTREAM_API_PATH_PATTERN = /\/(?:get|player_api|panel_api)\.php$/i;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Strip panel_api.php from detected Xtream server URLs

When a provider supplies http://host/panel_api.php with labeled credentials, this pattern classifies it as an Xtream API URL, but normalizeXtreamServerUrl() strips only get.php and player_api.php. The candidate therefore prefills http://host/panel_api.php, and the Xtream transport subsequently appends /player_api.php, requesting the invalid /panel_api.php/player_api.php path. Either normalize this recognized endpoint to its base URL or stop classifying it as supported.

Useful? React with 👍 / 👎.

Comment on lines +142 to +144
const portalAmbiguous =
macs.length > 1 &&
new Set(portalPool.map((url) => url.parsed.origin)).size > 1;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Distinguish Stalker panels hosted under different paths

When two MAC blocks use distinct installations on one origin, such as https://panel.example/a/stalker_portal/c/ and https://panel.example/b/stalker_portal/c/, this origin-only set has size one, so both candidates receive the first portal and the second account cannot authenticate. Fresh evidence beyond the earlier host/port cases is that different installation paths are still collapsed; include the normalized portal installation path in the ambiguity key while preserving known alternate endpoints for the same installation.

Useful? React with 👍 / 👎.

…uity for tenant installs

- normalizeXtreamServerUrl strips panel_api.php like the other API
  endpoints, so a panel_api.php handout (pasted or auto-detected) prefills
  the server base instead of a URL the transport would double-suffix.
- The multi-MAC portal ambiguity key is origin plus the installation base
  path: tenant installs sharing one origin (/a/stalker_portal/c/ vs /b/...)
  no longer collapse, while alternate endpoints of one install (/c/,
  portal.php, server/load.php, Real/Panel pairs) still compare equal.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JE1De2dfyRZMYh4H2iGXb4
@4gray

4gray commented Aug 15, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6e81d7034d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

compact({
kind: 'xtream',
confidence: username && password ? 'high' : 'medium',
serverUrl: safeXtreamServerUrl(url.raw),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor separately labeled ports on Xtream API URLs

When a provider supplies Server: http://panel.example/player_api.php and Port: 8080 on separate lines, labeledHostUrl() correctly assembles the port, but this URL-shaped branch ignores that result and normalizes the raw URL instead. The candidate therefore prefills http://panel.example, and connection checks target the wrong origin; use the assembled labeled URL when the detected API URL is the labeled host.

Useful? React with 👍 / 👎.

Comment on lines +298 to +300
const shaped = urls.find((url) => url.role === 'stalker');
if (shaped) {
return { url: shaped.raw, confidence: 'high' };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor separately labeled ports on shaped Stalker URLs

When a handout contains Portal: http://panel.example/c/ plus a separate Port: 8080, finding the shaped URL here returns it before labeledHostUrl() can add the port. The prefilled portal consequently omits :8080 and cannot authenticate against panels listening there; prefer the assembled labeled URL when the shaped URL came from the portal label.

Useful? React with 👍 / 👎.

Comment on lines +133 to +137
shapedPortals.length > 0
? shapedPortals
: labeledHostUrl(labeled) !== undefined
? []
: urls.filter((url) => url.role === 'generic');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include generic panels in mixed portal ambiguity checks

When a multi-account paste contains one shaped portal such as http://panel-a.example/c/ and one root portal such as http://panel-b.example, this conditional excludes the generic URL because shapedPortals is nonempty, so ambiguity remains false and every MAC receives panel A. Fresh evidence beyond the earlier all-generic ambiguity report is that the new guard handles only homogeneous URL-role pools; include distinct generic portal installations when evaluating a mixed pool.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declined: merging generic URLs into the pool once a shaped portal exists would misfire on the shapes the corpus actually contains. When a shaped portal is present, the generic URLs around it are, in observed handouts, either the same panel's "Real" line (same origin — already compares equal under the installation key) or decoration links (t.me channels, provider homepages) that are not portals at all; treating those as competing installations would strip the portal from legitimate single-panel MAC lists — the corpus #6 shape — far more often than it would catch the hypothesized two-panel paste, which is absent from the corpus. The cost stays bounded either way: multi-MAC candidates carry no identity fields, so a wrong portal is one failed login with a MAC, not a pinned device. Revisited if a mixed shaped+root two-panel handout shows up in the corpus.


Generated by Claude Code

…tal URLs

'Portal: http://host/…' plus 'Port: 8080' on its own line is a real handout
shape; the labeled port now completes port-less Xtream API URLs, shaped
Stalker portals, and the generic-URL fallback alike. A URL stating its own
port explicitly is never overridden.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JE1De2dfyRZMYh4H2iGXb4
@4gray

4gray commented Aug 15, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f3723eb8e3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +284 to +286
while (
detected.length < MAX_URLS &&
(match = matcher.exec(text)) !== null

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Mask URLs beyond the candidate cap

When a paste contains more than 16 distinct URLs, this loop stops returning URLs, but extractLabeledFields() later masks only the returned set. Query parameters in every later URL remain visible to the label regexes; for example, an early credential-less player_api.php, 15 playlist links, and a 17th URL containing ?username=wrong&password=wrongpass produces a high-confidence candidate for the early panel using the later URL's credentials. Keep the result cap, but mask spans for all URL-shaped text or otherwise reject labeled matches inside capped-out URLs.

Useful? React with 👍 / 👎.

Comment on lines +138 to +140
const portalAmbiguous =
macs.length > 1 &&
new Set(portalPool.map(portalInstallationKey)).size > 1;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Apply the labeled port before checking portal ambiguity

When a multi-MAC dump lists alternate URLs for one panel as http://panel:8080/c/ and http://panel/c/ plus Port: 8080, the second URL would be completed to the same origin by completeWithLabeledPort(), but this check compares the raw origins first and declares them different installations. Both Stalker candidates consequently lose the otherwise unambiguous portal and become low-confidence MAC-only entries; normalize each pool entry with the labeled port before deriving its installation key.

Useful? React with 👍 / 👎.

… extraction cap

- The portal installation key is derived after labeled-port completion, so
  'http://panel:8080/c/' next to 'http://panel/c/' plus a 'Port: 8080' line
  reads as one panel instead of stripping the portal from its MAC list.
- Label matchers mask EVERY URL-shaped span, not just the capped extraction
  result: a URL past the candidate cap still carries a label-shaped query,
  and its credentials must not attach to an earlier credential-less panel.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JE1De2dfyRZMYh4H2iGXb4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants