Repository navigation
feat(providers): Z.ai Start Plan provider (OAuth login, in-process traceless captcha, gateway wire) #4647
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(providers): Z.ai Start Plan provider (OAuth login, in-process traceless captcha, gateway wire) #4647
Changes from all commits
1c1fe0b
8ed2fc6
f533d19
eefbb72
9008be6
d23a140
85ffa5d
2ee17fc
71afa5a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| {"touched":[],"bashMutation":true,"reportedFindings":[],"findingEvents":[],"baseline":null,"stateErrors":[],"omittedReportedFindings":0,"omittedFindingEvents":0,"processing":null,"updatedAt":"2026-09-14T17:03:07.070Z"} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -157,6 +157,8 @@ export interface LogEntry extends LogFailureAttribution { | |
| timestamp: number; | ||
| model: string; | ||
| provider: string; | ||
| /** Pool/account label the turn was served under (e.g. "p83fa8d", "main"); absent when unattributed. */ | ||
| accountLogLabel?: string; | ||
| surface?: LogSurface; | ||
| conversationId?: string; | ||
| /** | ||
|
|
@@ -575,6 +577,43 @@ export default function Logs({ apiBase }: { apiBase: string }) { | |
| controller.abort(); | ||
| }; | ||
| }, [apiBase]); | ||
| // Opaque log labels → human attribution (email masked per proxy privacy settings). | ||
| // Refetched when the log data refreshes: a dashboard pairing that completes after the | ||
| // page mounts turns the earlier 401 into data, so the labels must retry with it — not | ||
| // only on mount. `logsState` is the log poll's own reactivity driver. | ||
| const [accountLabels, setAccountLabels] = useState<Map<string, string>>(new Map()); | ||
| const [labelsRetryToken, setLabelsRetryToken] = useState(0); | ||
| useEffect(() => { | ||
| const controller = new AbortController(); | ||
| let cancelled = false; | ||
| fetch(`${apiBase}/api/account-labels`, { signal: controller.signal }) | ||
| .then(res => (res.ok ? res.json() as Promise<{ labels?: Array<{ label?: unknown; email?: unknown; plan?: unknown }> }> : null)) | ||
|
Comment on lines
+586
to
+590
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a connected-client dashboard initially has no shared GUI session, opening the Logs page starts this request before pairing and receives a 401. Completing the pairing changes the session state but not Useful? React with 👍 / 👎. |
||
| .then(body => { | ||
| if (cancelled || !body?.labels) return; | ||
| const map = new Map<string, string>(); | ||
| for (const row of body.labels) { | ||
| if (typeof row.label !== "string" || !row.label) continue; | ||
| const parts: string[] = []; | ||
| if (typeof row.email === "string" && row.email) parts.push(row.email); | ||
| if (typeof row.plan === "string" && row.plan) parts.push(row.plan); | ||
| if (parts.length > 0) map.set(row.label, parts.join(" · ")); | ||
| } | ||
| if (!cancelled) setAccountLabels(map); | ||
| }) | ||
| .catch(() => { | ||
| // Older proxy without the endpoint: fall back to the raw opaque labels. | ||
| }); | ||
| return () => { | ||
| cancelled = true; | ||
| controller.abort(); | ||
| }; | ||
| }, [apiBase, labelsRetryToken]); | ||
| // Kick one labels refetch whenever a log poll cycle completes without them being loaded. | ||
| useEffect(() => { | ||
| if (accountLabels.size > 0) return; | ||
| const timer = setTimeout(() => setLabelsRetryToken(t => t + 1), 4000); | ||
| return () => clearTimeout(timer); | ||
| }, [accountLabels, apiBase]); | ||
| // The hash is the source of truth for the active tab (#logs vs #logs/debug), | ||
| // so refresh/bookmark/back-forward keep the tab choice. | ||
| const [tab, setTab] = useState<LogsTab>(readTabFromHash); | ||
|
|
@@ -884,6 +923,7 @@ export default function Logs({ apiBase }: { apiBase: string }) { | |
| <col className="logs-col-model" /> | ||
| <col className="logs-col-effort" /> | ||
| <col className="logs-col-provider" /> | ||
| <col className="logs-col-account" /> | ||
| <col className="logs-col-status" /> | ||
| <col className="logs-col-request" /> | ||
| <col className="logs-col-duration" /> | ||
|
|
@@ -897,6 +937,7 @@ export default function Logs({ apiBase }: { apiBase: string }) { | |
| <th className="log-col-model">{t("logs.col.model")}</th> | ||
| <th>{t("logs.col.effort")}</th> | ||
| <th>{t("logs.col.provider")}</th> | ||
| <th>{t("logs.col.account")}</th> | ||
| <th>{t("logs.col.status")}</th> | ||
| <th>{t("logs.col.request")}</th> | ||
| <th className="num log-col-duration">{t("logs.col.duration")}</th> | ||
|
|
@@ -905,7 +946,7 @@ export default function Logs({ apiBase }: { apiBase: string }) { | |
| <tbody> | ||
| {paddingTop > 0 && ( | ||
| <tr> | ||
| <td colSpan={10} className="logs-virtual-spacer" style={{ height: paddingTop }} /> | ||
| <td colSpan={11} className="logs-virtual-spacer" style={{ height: paddingTop }} /> | ||
| </tr> | ||
| )} | ||
| {virtualRows.map(virtualRow => { | ||
|
|
@@ -991,6 +1032,7 @@ export default function Logs({ apiBase }: { apiBase: string }) { | |
| 9% column and painted over the provider cell. */} | ||
| <td className="mono log-reasoning-cell" title={reasoningWire}>{effortLabel(log)}</td> | ||
| <td className="muted">{formatProviderDisplayName(log.provider, t)}</td> | ||
| <td className="muted mono" title={log.accountLogLabel}>{accountLabels.get(log.accountLogLabel ?? "") ?? log.accountLogLabel ?? "—"}</td> | ||
| <td> | ||
| <span className="log-status-cell"> | ||
| <span className="mono font-semibold" style={{ color: statusColor(log.status) }}>{log.status}</span> | ||
|
|
@@ -1011,7 +1053,7 @@ export default function Logs({ apiBase }: { apiBase: string }) { | |
| })} | ||
| {paddingBottom > 0 && ( | ||
| <tr> | ||
| <td colSpan={10} className="logs-virtual-spacer" style={{ height: paddingBottom }} /> | ||
| <td colSpan={11} className="logs-virtual-spacer" style={{ height: paddingBottom }} /> | ||
| </tr> | ||
| )} | ||
| </tbody> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -80,6 +80,7 @@ | |
| "@modelcontextprotocol/sdk": "^1.30.0", | ||
| "@napi-rs/keyring": "1.3.0", | ||
| "bun": "1.4.0", | ||
| "happy-dom": "^20.11.6", | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🤖 get_repo_knowledge executed:
Length of output: 17317 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- tracked manifest and likely security docs ---'
git ls-files | rg '(^|/)(package\.json|package-lock\.json|npm-shrinkwrap\.json|yarn\.lock|pnpm-lock\.yaml|SECURITY(\.md)?|CONTRIBUTING(\.md)?|README(\.md)?|.*security.*|.*dependency.*)$' | head -120
printf '%s\n' '--- package manifest ---'
cat -n package.json | sed -n '1,130p'
printf '%s\n' '--- happy-dom references ---'
rg -n -S --glob '!node_modules/**' --glob '!dist/**' 'happy-dom|captcha.?solver|captchaSolver|captcha' . | head -240
printf '%s\n' '--- dependency-related review evidence terms ---'
rg -n -i --glob '!node_modules/**' --glob '!dist/**' 'security review|dependency review|provenance|advisory|lifecycle.?script|isolation' . | head -240
printf '%s\n' '--- concise diff summary ---'
git diff --stat
git diff -- package.json | sed -n '1,180p'Repository: lidge-jun/opencodex Length of output: 50375 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- exact dependency-review guidance ---'
sed -n '350,380p' AGENTS.md
sed -n '55,78p' MAINTAINERS.md
printf '%s\n' '--- package state and lockfile binding ---'
git status --short
git diff --stat
sed -n '1,28p' bun.lock
sed -n '205,225p' bun.lock
printf '%s\n' '--- narrow happy-dom review-evidence search ---'
rg -n -i --glob '!bun.lock' --glob '!gui/bun.lock' --glob '!gui/**' --glob '!node_modules/**' 'happy-dom|dependency security review|dependency review' AGENTS.md MAINTAINERS.md CONTRIBUTING.md SECURITY.md devlog docs scripts src package.json .github 2>/dev/null | head -220
printf '%s\n' '--- solver and host imports/entry flow ---'
sed -n '1,45p' src/adapters/zcode-start-plan/captcha-solver.ts
sed -n '1,125p' src/adapters/zcode-start-plan/captcha-host.ts
sed -n '175,215p' src/adapters/zcode-start-plan.tsRepository: lidge-jun/opencodex Length of output: 27652 Record the required security review for
🤖 Prompt for AI AgentsSource: Coding guidelines |
||
| "zod": "4.4.3" | ||
| }, | ||
| "devDependencies": { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
Repository: lidge-jun/opencodex
Length of output: 1500
🏁 Script executed:
Repository: lidge-jun/opencodex
Length of output: 6196
🏁 Script executed:
Repository: lidge-jun/opencodex
Length of output: 41357
Add
logs.col.accountto the Vietnamese catalog.vi.tsis a supported locale. Its catalog does not definelogs.col.account, while the other named locale files do. Add a Vietnamese translation togui/src/i18n/vi.ts.🤖 Prompt for AI Agents
Source: Learnings