@mars-sea/dsh-commandcode-provider 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/README.md +1 -0
- package/README.zh-CN.md +1 -0
- package/lib/client.js +669 -8
- package/lib/client.js.map +1 -1
- package/lib/index.d.ts +146 -3
- package/lib/index.js +379 -16
- package/lib/index.js.map +1 -1
- package/package.json +3 -1
package/lib/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":["Button","createSnapshotStore"],"sources":["../src/client/sessions.ts","../src/client/settings.ts","../src/client/section.tsx","../src/client/locales.ts","../src/client/index.ts"],"sourcesContent":["/**\n * Friendly-error wrapper for the harness's image-session gate.\n *\n * The host rejects switching to a text-only model while the session already\n * contains images with a `model-unavailable` error\n * (`dsh-host-apiproxy`'s `session.selectModel` handler). That rejection is\n * intentional and cannot be relaxed from the plugin side — the adapter's\n * `inputModalities` is exactly what makes the guard work. What we CAN do is\n * make the error message friendlier: wrap the shared\n * `connection.api.sessions.selectModel` face so a `model-unavailable`\n * rejection shows a clear, actionable hint (with the requested model name)\n * instead of the raw English harness message.\n *\n * The wrapper is deliberately narrow: only the `model-unavailable` code is\n * rewritten, only when the message matches the image-session gate, and only\n * the message text changes — the error code and details pass through\n * untouched so any caller that switches on `error.code` keeps working.\n *\n * The wire types are spelled structurally here (not imported from\n * `@deepseek-ai/dsh-host-apiproxy`) so this client bundle does not drag an\n * extra peer dependency into the package; the shapes are stable and the\n * client build inlines them anyway.\n *\n * This module is deliberately free of React and other client-platform\n * imports so the node test runner can exercise it directly.\n */\n\n/** The `model-unavailable` error details: provider + model id. */\ninterface ModelUnavailableDetails {\n provider: string\n model: string\n}\n\n/** The narrow slice of the RPC error we need to inspect and rewrite. */\ninterface RpcErrorLike {\n code: string\n message: string\n details?: ModelUnavailableDetails\n}\n\n/**\n * The narrow slice of a unary RPC result we need to inspect and rewrite.\n * The wire shape from `sessions.selectModel` (via `AbstractApiClient.callUnary`)\n * is the full envelope `{ rpcId, result: { ok, error? } }` — the error lives\n * under `result.result`, not at the top level. `RpcResultLike` models that.\n */\ninterface RpcResultLike {\n rpcId: string\n result:\n | { ok: true; value?: unknown }\n | { ok: false; error: RpcErrorLike }\n}\n\n/** One selectModel call: payload in, envelope out. */\ntype SelectModelCall = (\n payload: { sessionId: string; provider: string; model: string; reasoningEffort?: string },\n signal?: AbortSignal,\n) => Promise<RpcResultLike>\n\n/** The shared sessions wire face we wrap. */\ninterface SessionsLike {\n selectModel: SelectModelCall\n}\n\n/** Whether a selectModel rejection is the harness's image-session gate. */\nexport function isImageSessionRejection(\n result: RpcResultLike,\n): result is RpcResultLike & { result: { ok: false; error: RpcErrorLike } } {\n return (\n !result.result.ok &&\n result.result.error.code === 'model-unavailable' &&\n result.result.error.message.includes('does not accept image input')\n )\n}\n\n/** Wrap the shared sessions API so selectModel failures read friendlier. */\nexport function withFriendlyImageError(sessions: SessionsLike): SessionsLike {\n const selectModel = sessions.selectModel.bind(sessions)\n return {\n ...sessions,\n selectModel: async (payload, signal) => {\n const result = await selectModel(payload, signal)\n if (!isImageSessionRejection(result)) return result\n const model = result.result.error.details?.model ?? payload.model\n return {\n ...result,\n result: {\n ...result.result,\n error: {\n ...result.result.error,\n message:\n `当前会话已包含图片,而模型 ${model} 不支持图片输入;`\n + '请选择支持图片的模型,或先移除会话中的图片。',\n },\n },\n }\n },\n }\n}\n\n/** The connection handle shape we read `api.sessions` from. */\nexport interface ConnectionLike {\n api: { sessions: SessionsLike }\n}\n\n/** Install the wrapper on a connection's shared sessions face. */\nexport function installFriendlyImageError(connection: ConnectionLike): void {\n connection.api.sessions = withFriendlyImageError(connection.api.sessions)\n}\n","/**\n * Browser controller for the \"Command Code\" settings page.\n *\n * The page lives at the same settings-nav level as General / Models / Plugins\n * (a `settings.section` entry, id `commandcode`). It exists because the\n * Models page renders an unknown-adapter-family card for the `commandcode`\n * provider and deliberately disables its submit — the API key cannot be\n * configured there. This page owns the connection facts the plugin resolves\n * per request:\n *\n * - API key -> written through the credentials domain under the reference\n * the plugin resolves (`apiKeyEnv`, default\n * `COMMANDCODE_API_KEY`). The literal never rides a response,\n * so the control only reports whether one is configured.\n * - API base -> the `llm-commandcode` settings namespace (`apiBase`), same\n * namespace the Models page card addresses.\n * - Working dir, request/stream timeouts -> the same namespace.\n *\n * The controller mirrors the plugin-card pattern from the harness's own\n * settings UI: it binds the `llm-commandcode` namespace through the\n * `settingsScope` service, keeps a staged draft of edits, and writes them on\n * save through `scope.set` / the credentials domain. The Host stays the\n * single fact source; the snapshot is republished after each accepted write.\n *\n * This module is deliberately free of JSX — it only produces the state face\n * the React component renders.\n */\n\nimport type { SettingsScope } from '@deepseek-ai/dsh-client-runtime/client'\n\n/** The settings namespace the plugin registers (host half, src/index.ts). */\nexport const COMMANDCODE_NS = 'llm-commandcode'\n/** Default credential reference the plugin resolves when none is named. */\nexport const DEFAULT_API_KEY_REF = 'COMMANDCODE_API_KEY'\n\n/** The narrow slice of the wire face this controller needs. */\nexport interface SettingsPageApi {\n credentials: {\n describe(request: { refs: string[] }): Promise<{\n result: { ok: true; value: { credentials: Record<string, { configured: boolean; writable: boolean }> } } | { ok: false; error: { message: string } }\n }>\n set(request: { ref: string; value: string }): Promise<{ result: { ok: true; value?: unknown } | { ok: false; error: { message: string } } }>\n }\n}\n\n/** The Host-description observable the page reads the process cwd from. */\nexport interface HostDescriptionSource {\n getSnapshot(): { cwd?: string } | undefined\n subscribe(fn: () => void): () => void\n}\n\n/** One editable text field's staged state (blank = keep stored value). */\nexport interface StagedField {\n /** Live draft text the input shows. */\n text: string\n /** Whether the user explicitly cleared the field (reset to inherited). */\n clear: boolean\n /** Whether the user layer carries this field (marks it overridden). */\n overridden: boolean\n /** Whether the staged draft fails to parse (blocks save). */\n invalid: boolean\n}\n\n/** The page's full state face, projected from the scope + drafts + credential. */\nexport interface SettingsPageState {\n /** Whether the namespace snapshot is ready. */\n available: boolean\n /** Whether the Host document accepts writes. */\n writable: boolean\n /** Whether the API key is currently configured (Host-reported). */\n apiKeyConfigured: boolean\n /** Whether the credentials domain can store the key. */\n apiKeyWritable: boolean\n /** The API key draft (write-only; starts blank, never echoes the stored key). */\n apiKey: StagedField\n /** apiBase draft. */\n apiBase: StagedField\n /** workingDir draft. */\n workingDir: StagedField\n /**\n * The working directory a blank `workingDir` resolves to: the Host\n * process cwd (`host.describe().cwd`). Shown as the field's placeholder so\n * the user sees what \"leave it empty\" means — no configuration needed.\n */\n defaultWorkingDir: string | undefined\n /** requestTimeoutMs draft. */\n requestTimeoutMs: StagedField\n /** streamIdleTimeoutMs draft. */\n streamIdleTimeoutMs: StagedField\n /** Whether any staged edit differs from the stored section. */\n dirty: boolean\n /** Whether a staged numeric field fails to parse (save blocked). */\n invalid: boolean\n /** Whether a save is in flight. */\n saving: boolean\n /** Whether the last save failed (drafts retained for correction). */\n failed: boolean\n}\n\n/** Parsed outcome of one field's draft. */\ntype Parsed = { kind: 'set'; value: string | number } | { kind: 'clear' } | { kind: 'invalid' }\n\n/** One field's staged draft (internal; the public face adds derived flags). */\ninterface Staged {\n text: string\n clear: boolean\n}\n\n/** A field conversion spec. */\ninterface FieldSpec {\n field: string\n format(value: unknown): string\n parse(text: string): Parsed\n}\n\n/** A free-text field; an empty draft clears it. */\nfunction textField(field: string): FieldSpec {\n return {\n field,\n format: (value) => (typeof value === 'string' ? value : ''),\n parse: (text) => {\n const trimmed = text.trim()\n return trimmed === '' ? { kind: 'clear' } : { kind: 'set', value: trimmed }\n },\n }\n}\n\n/** A whole-number field; an empty draft clears it, anything non-numeric blocks save. */\nfunction numberField(field: string): FieldSpec {\n return {\n field,\n format: (value) => (typeof value === 'number' ? String(value) : ''),\n parse: (text) => {\n const trimmed = text.trim()\n if (trimmed === '') return { kind: 'clear' }\n const parsed = Number(trimmed)\n return Number.isFinite(parsed) ? { kind: 'set', value: parsed } : { kind: 'invalid' }\n },\n }\n}\n\n/** The fields this page edits inside the `llm-commandcode` namespace. */\nconst SECTION_FIELDS: FieldSpec[] = [\n textField('apiBase'),\n textField('workingDir'),\n numberField('requestTimeoutMs'),\n numberField('streamIdleTimeoutMs'),\n]\n\n/**\n * Controller bridging the `llm-commandcode` scope and the credentials domain\n * onto the page. Public API mirrors the harness's CardForm actions, so the\n * component stays thin.\n */\nexport class CommandCodeSettingsController {\n private readonly scope: SettingsScope<Record<string, unknown>>\n private readonly api: SettingsPageApi\n private readonly specs = new Map(SECTION_FIELDS.map((spec) => [spec.field, spec]))\n private readonly staged = new Map<string, Staged>()\n private readonly listeners = new Set<() => void>()\n private readonly disposers: Array<() => void> = []\n private disposed = false\n private defaultWorkingDir: string | undefined\n private credential = { ref: DEFAULT_API_KEY_REF, configured: false, writable: true }\n private saving = false\n private failed = false\n\n /**\n * @param scope - bound scope for the `llm-commandcode` namespace.\n * @param api - credentials wire face.\n * @param hostDescription - the Host-description observable whose `cwd` is\n * shown as the placeholder a blank `workingDir` field resolves to.\n */\n constructor(\n scope: SettingsScope<Record<string, unknown>>,\n api: SettingsPageApi,\n hostDescription?: HostDescriptionSource,\n ) {\n this.scope = scope\n this.api = api\n this.disposers.push(scope.subscribe(() => {\n this.recomputeCredentialRef()\n this.publish()\n }))\n if (hostDescription !== undefined) {\n this.defaultWorkingDir = hostDescription.getSnapshot()?.cwd\n this.disposers.push(hostDescription.subscribe(() => {\n if (this.disposed) return\n const cwd = hostDescription.getSnapshot()?.cwd\n if (cwd !== this.defaultWorkingDir) {\n this.defaultWorkingDir = cwd\n this.publish()\n }\n }))\n }\n this.recomputeCredentialRef()\n void this.readCredential()\n }\n\n /** Release every subscription held on external sources. Idempotent. */\n dispose(): void {\n if (this.disposed) return\n this.disposed = true\n for (const dispose of this.disposers) dispose()\n this.disposers.length = 0\n this.listeners.clear()\n }\n\n /**\n * The credential reference the section names, or the provider default. A\n * user who renamed `apiKeyEnv` in `settings.yaml` (or the composition\n * config) gets a page that addresses the renamed ref instead of silently\n * writing the default — mirroring the Models page's `refFor()`.\n */\n private recomputeCredentialRef(): void {\n const snapshot = this.scope.getSnapshot()\n const named = typeof snapshot.value?.apiKeyEnv === 'string' && snapshot.value.apiKeyEnv.length > 0\n ? snapshot.value.apiKeyEnv\n : DEFAULT_API_KEY_REF\n if (named === this.credential.ref) return\n this.credential = { ref: named, configured: false, writable: true }\n void this.readCredential()\n }\n\n /** Subscribe to state projections. @returns the disposer. */\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener)\n return () => this.listeners.delete(listener)\n }\n\n /** Build the current page state face. */\n state(): SettingsPageState {\n const snapshot = this.scope.getSnapshot()\n const plan = this.plan()\n return {\n available: snapshot.status === 'ready',\n writable: snapshot.writable,\n apiKeyConfigured: this.credential.configured,\n apiKeyWritable: this.credential.writable,\n apiKey: {\n text: this.staged.get('apiKey')?.text ?? '',\n clear: false,\n overridden: false,\n invalid: false,\n },\n apiBase: this.field('apiBase'),\n workingDir: this.field('workingDir'),\n defaultWorkingDir: this.defaultWorkingDir,\n requestTimeoutMs: this.field('requestTimeoutMs'),\n streamIdleTimeoutMs: this.field('streamIdleTimeoutMs'),\n dirty: plan.length > 0,\n invalid: plan.some((item) => item.run === undefined),\n saving: this.saving,\n failed: this.failed,\n }\n }\n\n /** Stage one field's draft text. */\n edit(field: string, text: string): void {\n this.staged.set(field, { text, clear: false })\n this.failed = false\n this.publish()\n }\n\n /** Reset one section field to its inherited (composition) value. */\n resetField(field: string): void {\n if (field === 'apiKey') {\n this.staged.delete('apiKey')\n this.failed = false\n this.publish()\n return\n }\n const spec = this.spec(field)\n this.staged.set(field, { text: spec.format(this.baseValue(field)), clear: true })\n this.failed = false\n this.publish()\n }\n\n /** Discard every staged edit. */\n discard(): void {\n if (this.staged.size === 0 && !this.failed) return\n this.staged.clear()\n this.failed = false\n this.publish()\n }\n\n /** Write every staged edit, then re-read the Host's accepted state. */\n async save(): Promise<void> {\n const plan = this.plan()\n if (plan.length === 0 || this.saving) return\n const runs: Array<() => Promise<boolean>> = []\n for (const item of plan) {\n if (item.run === undefined) return\n runs.push(item.run)\n }\n this.saving = true\n this.failed = false\n this.publish()\n let landed = true\n for (const run of runs) landed = (await run()) && landed\n this.saving = false\n this.failed = !landed\n if (landed) this.staged.clear()\n this.publish()\n }\n\n // -------------------------------------------------------------------------\n // Internals\n // -------------------------------------------------------------------------\n\n private spec(field: string): FieldSpec {\n const spec = this.specs.get(field)\n if (spec === undefined) throw new Error(`commandcode settings page has no field ${field}`)\n return spec\n }\n\n /** One field's rendered state: draft text, whether it is user-overridden, invalid. */\n private field(field: string): StagedField {\n const spec = this.spec(field)\n const staged = this.staged.get(field)\n if (staged === undefined) {\n return {\n text: spec.format(this.sectionValue(field)),\n clear: false,\n overridden: this.stored(field),\n invalid: false,\n }\n }\n const parsed = staged.clear ? { kind: 'clear' as const } : spec.parse(staged.text)\n return {\n text: staged.text,\n clear: staged.clear,\n overridden: parsed.kind === 'set',\n invalid: parsed.kind === 'invalid',\n }\n }\n\n private sectionValue(field: string): unknown {\n return this.scope.getSnapshot().value?.[field]\n }\n\n private baseValue(field: string): unknown {\n const base = this.scope.getSnapshot().base\n return typeof base === 'object' && base !== null && !Array.isArray(base)\n ? (base as Record<string, unknown>)[field]\n : undefined\n }\n\n private userLayer(): Record<string, unknown> | undefined {\n const user = this.scope.getSnapshot().user\n return typeof user === 'object' && user !== null && !Array.isArray(user)\n ? (user as Record<string, unknown>)\n : undefined\n }\n\n private stored(field: string): boolean {\n const user = this.userLayer()\n return user !== undefined && Object.prototype.hasOwnProperty.call(user, field)\n }\n\n /**\n * The writes a save would perform, in staged order. A field whose draft is\n * not a value its spec accepts carries no write (the save refuses).\n */\n private plan(): Array<{ field: string; run: (() => Promise<boolean>) | undefined }> {\n const plan: Array<{ field: string; run: (() => Promise<boolean>) | undefined }> = []\n for (const [field, staged] of this.staged) {\n if (field === 'apiKey') {\n const value = staged.text.trim()\n if (value !== '') {\n plan.push({ field, run: () => this.writeKey(value) })\n }\n continue\n }\n const spec = this.spec(field)\n if (staged.clear) {\n if (this.stored(field)) plan.push({ field, run: () => this.clear(field) })\n continue\n }\n if (staged.text === spec.format(this.sectionValue(field))) continue\n const parsed = spec.parse(staged.text)\n if (parsed.kind === 'invalid') plan.push({ field, run: undefined })\n else if (parsed.kind === 'clear') plan.push({ field, run: () => this.clear(field) })\n else plan.push({ field, run: () => this.store(field, parsed.value) })\n }\n return plan\n }\n\n private async clear(field: string): Promise<boolean> {\n await this.scope.unset(field)\n return !this.stored(field)\n }\n\n private async store(field: string, value: string | number): Promise<boolean> {\n await this.scope.set(field, value)\n return this.userLayer()?.[field] === value\n }\n\n /** Write the staged key, then re-read whether the Host now holds one. */\n private async writeKey(value: string): Promise<boolean> {\n try {\n const response = await this.api.credentials.set({ ref: this.credential.ref, value })\n if (!response.result.ok) return false\n } catch {\n return false\n }\n await this.readCredential()\n return this.credential.configured\n }\n\n /** Ask the credentials domain about the reference this page writes. */\n private async readCredential(): Promise<void> {\n const ref = this.credential.ref\n let response: Awaited<ReturnType<SettingsPageApi['credentials']['describe']>>\n try {\n response = await this.api.credentials.describe({ refs: [ref] })\n } catch {\n return\n }\n if (!response.result.ok) return\n const view = response.result.value.credentials[ref]\n const next = {\n ref,\n configured: view?.configured ?? false,\n writable: view?.writable ?? true,\n }\n if (next.configured === this.credential.configured && next.writable === this.credential.writable) return\n this.credential = next\n this.publish()\n }\n\n private publish(): void {\n if (this.disposed) return\n for (const listener of this.listeners) listener()\n }\n}\n","/**\n * React component for the \"Command Code\" settings page (browser half).\n *\n * Renders as a `settings.section` entry — a page at the same settings-nav\n * level as General / Models / Plugins. The shell supplies the nav row and\n * renders this body inside the content column. All copy comes from the\n * `settings.commandcode` locale namespace; all state comes from the\n * `CommandCodeSettingsController` injected by the slot registration.\n *\n * The layout mirrors the harness's settings pages: a max-width content\n * column, labelled fields with hints, a reset affordance, and a\n * save/discard footer. Styles are injected once by the client entry\n * (see src/client/index.ts) and class-prefixed `cc-` to stay local.\n */\n\nimport { Button } from '@deepseek-ai/dsh-client-ui-primitives'\nimport type { Translate } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { SettingsCommandCodeKey } from './locales.ts'\nimport type { SettingsPageState, StagedField } from './settings.ts'\n\n/** Props composed by the slot registration: locale seat + injected face. */\nexport interface CommandCodeSettingsProps {\n t: Translate<SettingsCommandCodeKey>\n useCommandCodeSettings<T>(selector: (state: SettingsPageState) => T): T\n edit(field: string, text: string): void\n resetField(field: string): void\n save(): void\n discard(): void\n}\n\n/** One labelled field row in the page body. */\nfunction Field({\n id,\n label,\n hint,\n state,\n disabled,\n numeric,\n placeholder,\n onEdit,\n onReset,\n t,\n}: {\n id: string\n label: string\n hint: string\n state: StagedField\n disabled: boolean\n numeric?: boolean\n placeholder?: string | undefined\n onEdit(text: string): void\n onReset(): void\n t: Translate<SettingsCommandCodeKey>\n}) {\n return (\n <div className=\"cc-field\">\n <div className=\"cc-fieldHead\">\n <label className=\"cc-label\" htmlFor={id}>{label}</label>\n <span className=\"cc-badges\">\n {state.overridden ? <span className=\"cc-badge\">{t('overridden')}</span> : null}\n <button type=\"button\" className=\"cc-reset\" disabled={disabled} onClick={onReset}>{t('reset')}</button>\n </span>\n </div>\n <input\n id={id}\n className={state.invalid ? 'cc-input cc-inputInvalid' : 'cc-input'}\n type=\"text\"\n inputMode={numeric ? 'numeric' : undefined}\n value={state.text}\n placeholder={placeholder}\n disabled={disabled}\n onChange={(event) => onEdit(event.target.value)}\n />\n <p className={state.invalid ? 'cc-invalid' : 'cc-hint'}>\n {state.invalid ? t('invalidNumber') : hint}\n </p>\n </div>\n )\n}\n\n/** The API-key control: write-only, reports configured state, never echoes the key. */\nfunction SecretKeyField({\n label,\n hint,\n state,\n disabled,\n configured,\n configuredLabel,\n unconfiguredLabel,\n onEdit,\n}: {\n label: string\n hint: string\n state: StagedField\n disabled: boolean\n configured: boolean\n configuredLabel: string\n unconfiguredLabel: string\n onEdit(text: string): void\n}) {\n return (\n <div className=\"cc-field\">\n <div className=\"cc-fieldHead\">\n <label className=\"cc-label\" htmlFor=\"cc-api-key\">{label}</label>\n <span className=\"cc-badges\">\n <span className={configured ? 'cc-badge' : 'cc-badgeMuted'}>\n {configured ? configuredLabel : unconfiguredLabel}\n </span>\n </span>\n </div>\n <input\n id=\"cc-api-key\"\n className=\"cc-input\"\n type=\"password\"\n autoComplete=\"off\"\n value={state.text}\n disabled={disabled}\n onChange={(event) => onEdit(event.target.value)}\n />\n <p className=\"cc-hint\">{hint}</p>\n </div>\n )\n}\n\n/** The settings page body: connection facts for the Command Code provider. */\nexport function CommandCodeSettingsPage(props: CommandCodeSettingsProps) {\n const { t } = props\n const state = props.useCommandCodeSettings((snapshot) => snapshot)\n const disabled = !state.writable\n const keyLocked = !state.apiKeyWritable\n return (\n <section className=\"cc-section\" aria-label={t('title')}>\n <h2 className=\"cc-title\">{t('title')}</h2>\n <p className=\"cc-intro\">{t('intro')}</p>\n {!state.writable ? <p className=\"cc-readOnly\" role=\"status\">{t('readOnly')}</p> : null}\n <div className=\"cc-card\">\n <SecretKeyField\n label={t('apiKey')}\n hint={keyLocked ? t('apiKeyLocked') : t('apiKeyHint')}\n state={state.apiKey}\n disabled={disabled || keyLocked}\n configured={state.apiKeyConfigured}\n configuredLabel={t('apiKeySet')}\n unconfiguredLabel={t('apiKeyUnset')}\n onEdit={(text) => props.edit('apiKey', text)}\n />\n <Field\n id=\"cc-api-base\"\n label={t('apiBase')}\n hint={t('apiBaseHint')}\n state={state.apiBase}\n disabled={disabled}\n onEdit={(text) => props.edit('apiBase', text)}\n onReset={() => props.resetField('apiBase')}\n t={t}\n />\n <Field\n id=\"cc-working-dir\"\n label={t('workingDir')}\n hint={t('workingDirHint')}\n state={state.workingDir}\n disabled={disabled}\n placeholder={state.defaultWorkingDir}\n onEdit={(text) => props.edit('workingDir', text)}\n onReset={() => props.resetField('workingDir')}\n t={t}\n />\n <Field\n id=\"cc-request-timeout\"\n label={t('requestTimeoutMs')}\n hint={t('requestTimeoutMsHint')}\n state={state.requestTimeoutMs}\n disabled={disabled}\n numeric\n onEdit={(text) => props.edit('requestTimeoutMs', text)}\n onReset={() => props.resetField('requestTimeoutMs')}\n t={t}\n />\n <Field\n id=\"cc-stream-idle-timeout\"\n label={t('streamIdleTimeoutMs')}\n hint={t('streamIdleTimeoutMsHint')}\n state={state.streamIdleTimeoutMs}\n disabled={disabled}\n numeric\n onEdit={(text) => props.edit('streamIdleTimeoutMs', text)}\n onReset={() => props.resetField('streamIdleTimeoutMs')}\n t={t}\n />\n </div>\n <div className=\"cc-footer\">\n {state.failed ? <p className=\"cc-failed\" role=\"status\">{t('saveFailed')}</p> : null}\n <Button variant=\"ghost\" size=\"sm\" disabled={!state.dirty || state.saving} onClick={props.discard}>\n {t('discard')}\n </Button>\n <Button\n variant=\"primary\"\n size=\"sm\"\n disabled={!state.dirty || state.invalid || state.saving}\n onClick={props.save}\n >\n {t(state.saving ? 'saving' : 'save')}\n </Button>\n </div>\n </section>\n )\n}\n","/**\n * Locale copy for the \"Command Code\" settings page, and the declaration that\n * merges the page's namespace into the framework's `LocaleNamespaceMap` so\n * `ctx.locale.register` / `ctx.slots.register(..., { locale })` are typed.\n *\n * zh is the source of truth for the key set (repo convention); en must carry\n * the exact same keys — a mismatch is a compile error at the register site.\n */\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** Copy of the Command Code settings page. */\n 'settings.commandcode': SettingsCommandCodeKey\n }\n}\n\n/** Dictionary keys of the Command Code settings page. */\nexport type SettingsCommandCodeKey =\n | 'nav'\n | 'title'\n | 'intro'\n | 'apiKey'\n | 'apiKeyHint'\n | 'apiKeySet'\n | 'apiKeyUnset'\n | 'apiKeyLocked'\n | 'apiBase'\n | 'apiBaseHint'\n | 'workingDir'\n | 'workingDirHint'\n | 'requestTimeoutMs'\n | 'requestTimeoutMsHint'\n | 'streamIdleTimeoutMs'\n | 'streamIdleTimeoutMsHint'\n | 'overridden'\n | 'reset'\n | 'invalidNumber'\n | 'readOnly'\n | 'unsaved'\n | 'save'\n | 'saving'\n | 'saveFailed'\n | 'discard'\n | 'cancel'\n\nexport const zh: Record<SettingsCommandCodeKey, string> = {\n nav: 'Command Code',\n title: 'Command Code',\n intro:\n '配置 Command Code Provider 连接。API 密钥仅保存在本机凭据服务中,不会回显;'\n + '其他字段写入用户设置,下次请求即生效。',\n apiKey: 'API 密钥',\n apiKeyHint: '在 commandcode.ai 控制台创建。留空保存不会覆盖已存储的密钥。',\n apiKeySet: '已配置',\n apiKeyUnset: '未配置',\n apiKeyLocked: '密钥由只读来源提供',\n apiBase: 'API 地址',\n apiBaseHint: '默认 https://api.commandcode.ai,一般无需修改。',\n workingDir: '工作目录',\n workingDirHint: '可选。留空时使用占位符显示的进程工作目录;仅在需要固定路径时填写。',\n requestTimeoutMs: '请求超时(毫秒)',\n requestTimeoutMsHint: '等待响应首个字节的超时;默认 60000。',\n streamIdleTimeoutMs: '流空闲超时(毫秒)',\n streamIdleTimeoutMsHint: '生成流停滞多久视为断连;默认 300000(长思考模型可静默数分钟,默认值刻意放宽)。',\n overridden: '已覆盖',\n reset: '重置',\n invalidNumber: '无效数字',\n readOnly: '当前配置为只读。',\n unsaved: '未保存',\n save: '保存',\n saving: '保存中',\n saveFailed: '保存失败,请重试。',\n discard: '放弃',\n cancel: '取消',\n}\n\nexport const en: Record<SettingsCommandCodeKey, string> = {\n nav: 'Command Code',\n title: 'Command Code',\n intro:\n 'Configure the Command Code Provider connection. The API key is stored only'\n + ' in the local credential service and never echoed; other fields are written'\n + ' to user settings and take effect on the next request.',\n apiKey: 'API key',\n apiKeyHint: 'Create one in the commandcode.ai console. Saving with this field'\n + ' blank keeps the stored key.',\n apiKeySet: 'Configured',\n apiKeyUnset: 'Not configured',\n apiKeyLocked: 'Key provided by a read-only source',\n apiBase: 'API base URL',\n apiBaseHint: 'Defaults to https://api.commandcode.ai; usually leave as-is.',\n workingDir: 'Working directory',\n workingDirHint: 'Optional. Leave blank to use the process cwd shown as the'\n + ' placeholder; fill in only to pin a specific path.',\n requestTimeoutMs: 'Request timeout (ms)',\n requestTimeoutMsHint: 'Time to wait for the first response byte; default 60000.',\n streamIdleTimeoutMs: 'Stream idle timeout (ms)',\n streamIdleTimeoutMsHint: 'How long a stalled stream is treated as dead; default 300000'\n + ' (deliberately generous — long-thinking models can stay silent for minutes).',\n overridden: 'Overridden',\n reset: 'Reset',\n invalidNumber: 'Invalid number',\n readOnly: 'Settings are read-only.',\n unsaved: 'Unsaved',\n save: 'Save',\n saving: 'Saving',\n saveFailed: 'Save failed, please retry.',\n discard: 'Discard',\n cancel: 'Cancel',\n}\n","/**\n * Browser half of the dsh-commandcode-provider bundle.\n *\n * Two responsibilities:\n *\n * 1. A \"Command Code\" settings page (a `settings.section` entry at the same\n * nav level as General / Models / Plugins). The Models page renders an\n * unknown-adapter-family card for the `commandcode` provider and disables\n * its submit, so the API key cannot be configured there; this page is the\n * dedicated surface. It writes the API key through the credentials domain\n * (the `COMMANDCODE_API_KEY` reference the plugin resolves) and the\n * connection facts through the `llm-commandcode` settings namespace, so a\n * saved key or endpoint reaches the very next request.\n *\n * 2. The friendly-error wrapper for the harness's image-session gate — see\n * `./sessions.ts`. The wrapper is deliberately narrow: only the\n * `model-unavailable` code is rewritten, only when the message matches the\n * image-session gate, and only the message text changes.\n *\n * The wire types are spelled structurally in `./sessions.ts` (not imported\n * from `@deepseek-ai/dsh-host-apiproxy`) so this client bundle does not drag\n * an extra peer dependency into the package; the shapes are stable and the\n * client build inlines them anyway.\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport { createSnapshotStore } from '@deepseek-ai/dsh-client-runtime/client'\n// Type-only imports that pull in the client-service augmentations\n// (`slots`/`remote`/`locale` on Context) and the `settings.section` SlotMap\n// entry (`settingsScope` arrives through dsh-client-ui-settings).\nimport type {} from '@deepseek-ai/dsh-api-remotes/client'\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport { installFriendlyImageError } from './sessions.ts'\nimport { CommandCodeSettingsController, COMMANDCODE_NS, type SettingsPageState } from './settings.ts'\nimport { CommandCodeSettingsPage } from './section.tsx'\nimport { zh, en } from './locales.ts'\n\nexport { isImageSessionRejection, withFriendlyImageError } from './sessions.ts'\nimport type { ConnectionLike } from './sessions.ts'\n\n/** CSS for the settings page, injected once (harness bundle convention). */\nconst PAGE_CSS = `\n.cc-section{max-width:720px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:12px;display:flex}\n.cc-title{margin:0;font-size:18px;font-weight:600}\n.cc-intro{color:var(--dsw-alias-label-tertiary);margin:0;font-size:13px;line-height:1.5}\n.cc-readOnly{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}\n.cc-card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;padding:4px 16px}\n.cc-field{flex-direction:column;gap:6px;padding:12px 0;display:flex}\n.cc-field+.cc-field{border-top:1px solid var(--dsw-alias-border-l2)}\n.cc-fieldHead{align-items:center;gap:8px;display:flex}\n.cc-label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}\n.cc-badges{align-items:center;gap:8px;display:inline-flex}\n.cc-badge{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}\n.cc-badgeMuted{white-space:nowrap;color:var(--dsw-alias-label-tertiary);border-radius:999px;padding:1px 8px;font-size:11px;line-height:17px}\n.cc-reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}\n.cc-reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}\n.cc-reset:disabled{cursor:default;opacity:.5}\n.cc-input{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}\n.cc-input:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}\n.cc-input:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}\n.cc-inputInvalid{border-color:var(--dsw-alias-label-error)}\n.cc-invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}\n.cc-hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}\n.cc-footer{justify-content:flex-end;align-items:center;gap:8px;display:flex}\n.cc-failed{min-width:0;color:var(--dsw-alias-label-error);flex:1;margin:0;font-size:12px;line-height:1.5}\n`\n\n/** Inject the page stylesheet once (idempotent per tag). */\nfunction injectPageCss(): void {\n if (typeof document === 'undefined') return\n const id = '@mars-sea/dsh-commandcode-provider/CommandCodeSettingsPage.module.css'\n if (document.querySelector(`style[data-plugin-css=\"${id}\"]`) !== null) return\n const tag = document.createElement('style')\n tag.dataset.plugin = '@mars-sea/dsh-commandcode-provider'\n tag.dataset.pluginCss = id\n tag.textContent = PAGE_CSS\n document.head.appendChild(tag)\n}\n\n/**\n * Client plugin body. Gates on the services the settings page needs\n * (`slots`, `locale`, `connection`, `remote`, `settingsScope`) plus the\n * `connection` used by the friendly-error wrapper — the same inject list the\n * harness's own settings-surface plugins declare.\n */\nexport function apply(ctx: Context): void {\n injectPageCss()\n\n // Friendly image-gate error wrapper (unchanged behaviour).\n const connection = ctx.get('connection') as ConnectionLike | undefined\n if (connection !== undefined) {\n installFriendlyImageError(connection)\n }\n\n // The \"Command Code\" settings page: register the section once the\n // `settings.section` declaration is on the ledger (ui-settings-general\n // owns the shell; registration order relative to it is not constrained —\n // `slots.inject` waits for the declaration).\n ctx.effect(() => ctx.locale.register('settings.commandcode', { zh, en }), 'dsh-commandcode-provider: page copy')\n\n const api = ctx.get('connection').api\n const hostDescription = ctx.get('connection').hostDescription\n const scope = ctx.settingsScope.bind<Record<string, unknown>>({ namespace: COMMANDCODE_NS })\n const controller = new CommandCodeSettingsController(scope, { credentials: api.credentials }, hostDescription)\n ctx.effect(() => () => controller.dispose(), 'dsh-commandcode-provider: settings controller')\n const store = createSnapshotStore<SettingsPageState>(controller.state())\n controller.subscribe(() => store.set(controller.state()))\n const injected = () => ({\n hooks: { commandCodeSettings: store },\n edit: (field: string, text: string) => controller.edit(field, text),\n resetField: (field: string) => controller.resetField(field),\n save: () => void controller.save(),\n discard: () => controller.discard(),\n })\n\n ctx.slots.inject('settings.section', () => ctx.slots.register({\n name: 'settings.section',\n id: 'commandcode',\n order: 12,\n label: () => ctx.locale.bind('settings.commandcode')('nav'),\n locale: 'settings.commandcode',\n inject: injected,\n }, CommandCodeSettingsPage))\n}\n\nexport const inject: readonly string[] = [\n 'slots',\n 'locale',\n 'connection',\n 'remote',\n 'settingsScope',\n]\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","names":["numberField","booleanField","Button","createSnapshotStore"],"sources":["../src/client/sessions.ts","../src/client/settings.ts","../src/client/usage.ts","../src/usage-wire.ts","../src/client/section.tsx","../src/client/locales.ts","../src/client/index.ts"],"sourcesContent":["/**\n * Friendly-error wrapper for the harness's image-session gate.\n *\n * The host rejects switching to a text-only model while the session already\n * contains images with a `model-unavailable` error\n * (`dsh-host-apiproxy`'s `session.selectModel` handler). That rejection is\n * intentional and cannot be relaxed from the plugin side — the adapter's\n * `inputModalities` is exactly what makes the guard work. What we CAN do is\n * make the error message friendlier: wrap the shared\n * `connection.api.sessions.selectModel` face so a `model-unavailable`\n * rejection shows a clear, actionable hint (with the requested model name)\n * instead of the raw English harness message.\n *\n * The wrapper is deliberately narrow: only the `model-unavailable` code is\n * rewritten, only when the message matches the image-session gate, and only\n * the message text changes — the error code and details pass through\n * untouched so any caller that switches on `error.code` keeps working.\n *\n * The wire types are spelled structurally here (not imported from\n * `@deepseek-ai/dsh-host-apiproxy`) so this client bundle does not drag an\n * extra peer dependency into the package; the shapes are stable and the\n * client build inlines them anyway.\n *\n * This module is deliberately free of React and other client-platform\n * imports so the node test runner can exercise it directly.\n */\n\n/** The `model-unavailable` error details: provider + model id. */\ninterface ModelUnavailableDetails {\n provider: string\n model: string\n}\n\n/** The narrow slice of the RPC error we need to inspect and rewrite. */\ninterface RpcErrorLike {\n code: string\n message: string\n details?: ModelUnavailableDetails\n}\n\n/**\n * The narrow slice of a unary RPC result we need to inspect and rewrite.\n * The wire shape from `sessions.selectModel` (via `AbstractApiClient.callUnary`)\n * is the full envelope `{ rpcId, result: { ok, error? } }` — the error lives\n * under `result.result`, not at the top level. `RpcResultLike` models that.\n */\ninterface RpcResultLike {\n rpcId: string\n result:\n | { ok: true; value?: unknown }\n | { ok: false; error: RpcErrorLike }\n}\n\n/** One selectModel call: payload in, envelope out. */\ntype SelectModelCall = (\n payload: { sessionId: string; provider: string; model: string; reasoningEffort?: string },\n signal?: AbortSignal,\n) => Promise<RpcResultLike>\n\n/** The shared sessions wire face we wrap. */\ninterface SessionsLike {\n selectModel: SelectModelCall\n}\n\n/** Whether a selectModel rejection is the harness's image-session gate. */\nexport function isImageSessionRejection(\n result: RpcResultLike,\n): result is RpcResultLike & { result: { ok: false; error: RpcErrorLike } } {\n return (\n !result.result.ok &&\n result.result.error.code === 'model-unavailable' &&\n result.result.error.message.includes('does not accept image input')\n )\n}\n\n/** Wrap the shared sessions API so selectModel failures read friendlier. */\nexport function withFriendlyImageError(sessions: SessionsLike): SessionsLike {\n const selectModel = sessions.selectModel.bind(sessions)\n return {\n ...sessions,\n selectModel: async (payload, signal) => {\n const result = await selectModel(payload, signal)\n if (!isImageSessionRejection(result)) return result\n const model = result.result.error.details?.model ?? payload.model\n return {\n ...result,\n result: {\n ...result.result,\n error: {\n ...result.result.error,\n message:\n `当前会话已包含图片,而模型 ${model} 不支持图片输入;`\n + '请选择支持图片的模型,或先移除会话中的图片。',\n },\n },\n }\n },\n }\n}\n\n/** The connection handle shape we read `api.sessions` from. */\nexport interface ConnectionLike {\n api: { sessions: SessionsLike }\n}\n\n/** Install the wrapper on a connection's shared sessions face. */\nexport function installFriendlyImageError(connection: ConnectionLike): void {\n connection.api.sessions = withFriendlyImageError(connection.api.sessions)\n}\n","/**\n * Browser controller for the \"Command Code\" settings page.\n *\n * The page lives at the same settings-nav level as General / Models / Plugins\n * (a `settings.section` entry, id `commandcode`). It exists because the\n * Models page renders an unknown-adapter-family card for the `commandcode`\n * provider and deliberately disables its submit — the API key cannot be\n * configured there. This page owns the connection facts the plugin resolves\n * per request:\n *\n * - API key -> written through the credentials domain under the reference\n * the plugin resolves (`apiKeyEnv`, default\n * `COMMANDCODE_API_KEY`). The literal never rides a response,\n * so the control only reports whether one is configured.\n * - API base -> the `llm-commandcode` settings namespace (`apiBase`), same\n * namespace the Models page card addresses.\n * - Working dir, request/stream timeouts -> the same namespace.\n *\n * The controller mirrors the plugin-card pattern from the harness's own\n * settings UI: it binds the `llm-commandcode` namespace through the\n * `settingsScope` service, keeps a staged draft of edits, and writes them on\n * save through `scope.set` / the credentials domain. The Host stays the\n * single fact source; the snapshot is republished after each accepted write.\n *\n * This module is deliberately free of JSX — it only produces the state face\n * the React component renders.\n */\n\nimport type { SettingsScope } from '@deepseek-ai/dsh-client-runtime/client'\n\n/** The settings namespace the plugin registers (host half, src/index.ts). */\nexport const COMMANDCODE_NS = 'llm-commandcode'\n/** Default credential reference the plugin resolves when none is named. */\nexport const DEFAULT_API_KEY_REF = 'COMMANDCODE_API_KEY'\n\n/** The narrow slice of the wire face this controller needs. */\nexport interface SettingsPageApi {\n credentials: {\n describe(request: { refs: string[] }): Promise<{\n result: { ok: true; value: { credentials: Record<string, { configured: boolean; writable: boolean }> } } | { ok: false; error: { message: string } }\n }>\n set(request: { ref: string; value: string }): Promise<{ result: { ok: true; value?: unknown } | { ok: false; error: { message: string } } }>\n }\n}\n\n/** The Host-description observable the page reads the process cwd from. */\nexport interface HostDescriptionSource {\n getSnapshot(): { cwd?: string } | undefined\n subscribe(fn: () => void): () => void\n}\n\n/** One editable text field's staged state (blank = keep stored value). */\nexport interface StagedField {\n /** Live draft text the input shows. */\n text: string\n /** Whether the user explicitly cleared the field (reset to inherited). */\n clear: boolean\n /** Whether the user layer carries this field (marks it overridden). */\n overridden: boolean\n /** Whether the staged draft fails to parse (blocks save). */\n invalid: boolean\n}\n\n/** The page's full state face, projected from the scope + drafts + credential. */\nexport interface SettingsPageState {\n /** Whether the namespace snapshot is ready. */\n available: boolean\n /** Whether the Host document accepts writes. */\n writable: boolean\n /** Whether the API key is currently configured (Host-reported). */\n apiKeyConfigured: boolean\n /** Whether the credentials domain can store the key. */\n apiKeyWritable: boolean\n /** The API key draft (write-only; starts blank, never echoes the stored key). */\n apiKey: StagedField\n /** apiBase draft. */\n apiBase: StagedField\n /** workingDir draft. */\n workingDir: StagedField\n /**\n * The working directory a blank `workingDir` resolves to: the Host\n * process cwd (`host.describe().cwd`). Shown as the field's placeholder so\n * the user sees what \"leave it empty\" means — no configuration needed.\n */\n defaultWorkingDir: string | undefined\n /** requestTimeoutMs draft. */\n requestTimeoutMs: StagedField\n /** streamIdleTimeoutMs draft. */\n streamIdleTimeoutMs: StagedField\n /**\n * filterModelsByPlan draft, staged as `'true'`/`'false'`/`''` (unset). The\n * component renders it as a toggle; `''` means \"inherit the default\" (on).\n */\n filterModelsByPlan: StagedField\n /** Whether any staged edit differs from the stored section. */\n dirty: boolean\n /** Whether a staged numeric field fails to parse (save blocked). */\n invalid: boolean\n /** Whether a save is in flight. */\n saving: boolean\n /** Whether the last save failed (drafts retained for correction). */\n failed: boolean\n}\n\n/** Parsed outcome of one field's draft. */\ntype Parsed = { kind: 'set'; value: string | number | boolean } | { kind: 'clear' } | { kind: 'invalid' }\n\n/** One field's staged draft (internal; the public face adds derived flags). */\ninterface Staged {\n text: string\n clear: boolean\n}\n\n/** A field conversion spec. */\ninterface FieldSpec {\n field: string\n format(value: unknown): string\n parse(text: string): Parsed\n}\n\n/** A free-text field; an empty draft clears it. */\nfunction textField(field: string): FieldSpec {\n return {\n field,\n format: (value) => (typeof value === 'string' ? value : ''),\n parse: (text) => {\n const trimmed = text.trim()\n return trimmed === '' ? { kind: 'clear' } : { kind: 'set', value: trimmed }\n },\n }\n}\n\n/** A whole-number field; an empty draft clears it, anything non-numeric blocks save. */\nfunction numberField(field: string): FieldSpec {\n return {\n field,\n format: (value) => (typeof value === 'number' ? String(value) : ''),\n parse: (text) => {\n const trimmed = text.trim()\n if (trimmed === '') return { kind: 'clear' }\n const parsed = Number(trimmed)\n return Number.isFinite(parsed) ? { kind: 'set', value: parsed } : { kind: 'invalid' }\n },\n }\n}\n\n/**\n * A boolean field, staged as the strings `'true'`/`'false'` (an empty draft\n * clears it). The component renders a toggle and only ever stages these two\n * strings; anything else blocks save.\n */\nfunction booleanField(field: string): FieldSpec {\n return {\n field,\n format: (value) => (typeof value === 'boolean' ? String(value) : ''),\n parse: (text) => {\n const trimmed = text.trim()\n if (trimmed === '') return { kind: 'clear' }\n if (trimmed === 'true') return { kind: 'set', value: true }\n if (trimmed === 'false') return { kind: 'set', value: false }\n return { kind: 'invalid' }\n },\n }\n}\n\n/** The fields this page edits inside the `llm-commandcode` namespace. */\nconst SECTION_FIELDS: FieldSpec[] = [\n textField('apiBase'),\n textField('workingDir'),\n numberField('requestTimeoutMs'),\n numberField('streamIdleTimeoutMs'),\n booleanField('filterModelsByPlan'),\n]\n\n/**\n * Controller bridging the `llm-commandcode` scope and the credentials domain\n * onto the page. Public API mirrors the harness's CardForm actions, so the\n * component stays thin.\n */\nexport class CommandCodeSettingsController {\n private readonly scope: SettingsScope<Record<string, unknown>>\n private readonly api: SettingsPageApi\n private readonly specs = new Map(SECTION_FIELDS.map((spec) => [spec.field, spec]))\n private readonly staged = new Map<string, Staged>()\n private readonly listeners = new Set<() => void>()\n private readonly disposers: Array<() => void> = []\n private disposed = false\n private defaultWorkingDir: string | undefined\n private credential = { ref: DEFAULT_API_KEY_REF, configured: false, writable: true }\n private saving = false\n private failed = false\n\n /**\n * @param scope - bound scope for the `llm-commandcode` namespace.\n * @param api - credentials wire face.\n * @param hostDescription - the Host-description observable whose `cwd` is\n * shown as the placeholder a blank `workingDir` field resolves to.\n */\n constructor(\n scope: SettingsScope<Record<string, unknown>>,\n api: SettingsPageApi,\n hostDescription?: HostDescriptionSource,\n ) {\n this.scope = scope\n this.api = api\n this.disposers.push(scope.subscribe(() => {\n this.recomputeCredentialRef()\n this.publish()\n }))\n if (hostDescription !== undefined) {\n this.defaultWorkingDir = hostDescription.getSnapshot()?.cwd\n this.disposers.push(hostDescription.subscribe(() => {\n if (this.disposed) return\n const cwd = hostDescription.getSnapshot()?.cwd\n if (cwd !== this.defaultWorkingDir) {\n this.defaultWorkingDir = cwd\n this.publish()\n }\n }))\n }\n this.recomputeCredentialRef()\n void this.readCredential()\n }\n\n /** Release every subscription held on external sources. Idempotent. */\n dispose(): void {\n if (this.disposed) return\n this.disposed = true\n for (const dispose of this.disposers) dispose()\n this.disposers.length = 0\n this.listeners.clear()\n }\n\n /**\n * The credential reference the section names, or the provider default. A\n * user who renamed `apiKeyEnv` in `settings.yaml` (or the composition\n * config) gets a page that addresses the renamed ref instead of silently\n * writing the default — mirroring the Models page's `refFor()`.\n */\n private recomputeCredentialRef(): void {\n const snapshot = this.scope.getSnapshot()\n const named = typeof snapshot.value?.apiKeyEnv === 'string' && snapshot.value.apiKeyEnv.length > 0\n ? snapshot.value.apiKeyEnv\n : DEFAULT_API_KEY_REF\n if (named === this.credential.ref) return\n this.credential = { ref: named, configured: false, writable: true }\n void this.readCredential()\n }\n\n /** Subscribe to state projections. @returns the disposer. */\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener)\n return () => this.listeners.delete(listener)\n }\n\n /** Build the current page state face. */\n state(): SettingsPageState {\n const snapshot = this.scope.getSnapshot()\n const plan = this.plan()\n return {\n available: snapshot.status === 'ready',\n writable: snapshot.writable,\n apiKeyConfigured: this.credential.configured,\n apiKeyWritable: this.credential.writable,\n apiKey: {\n text: this.staged.get('apiKey')?.text ?? '',\n clear: false,\n overridden: false,\n invalid: false,\n },\n apiBase: this.field('apiBase'),\n workingDir: this.field('workingDir'),\n defaultWorkingDir: this.defaultWorkingDir,\n requestTimeoutMs: this.field('requestTimeoutMs'),\n streamIdleTimeoutMs: this.field('streamIdleTimeoutMs'),\n filterModelsByPlan: this.field('filterModelsByPlan'),\n dirty: plan.length > 0,\n invalid: plan.some((item) => item.run === undefined),\n saving: this.saving,\n failed: this.failed,\n }\n }\n\n /** Stage one field's draft text. */\n edit(field: string, text: string): void {\n this.staged.set(field, { text, clear: false })\n this.failed = false\n this.publish()\n }\n\n /** Reset one section field to its inherited (composition) value. */\n resetField(field: string): void {\n if (field === 'apiKey') {\n this.staged.delete('apiKey')\n this.failed = false\n this.publish()\n return\n }\n const spec = this.spec(field)\n this.staged.set(field, { text: spec.format(this.baseValue(field)), clear: true })\n this.failed = false\n this.publish()\n }\n\n /** Discard every staged edit. */\n discard(): void {\n if (this.staged.size === 0 && !this.failed) return\n this.staged.clear()\n this.failed = false\n this.publish()\n }\n\n /** Write every staged edit, then re-read the Host's accepted state. */\n async save(): Promise<void> {\n const plan = this.plan()\n if (plan.length === 0 || this.saving) return\n const runs: Array<() => Promise<boolean>> = []\n for (const item of plan) {\n if (item.run === undefined) return\n runs.push(item.run)\n }\n this.saving = true\n this.failed = false\n this.publish()\n let landed = true\n for (const run of runs) landed = (await run()) && landed\n this.saving = false\n this.failed = !landed\n if (landed) this.staged.clear()\n this.publish()\n }\n\n // -------------------------------------------------------------------------\n // Internals\n // -------------------------------------------------------------------------\n\n private spec(field: string): FieldSpec {\n const spec = this.specs.get(field)\n if (spec === undefined) throw new Error(`commandcode settings page has no field ${field}`)\n return spec\n }\n\n /** One field's rendered state: draft text, whether it is user-overridden, invalid. */\n private field(field: string): StagedField {\n const spec = this.spec(field)\n const staged = this.staged.get(field)\n if (staged === undefined) {\n return {\n text: spec.format(this.sectionValue(field)),\n clear: false,\n overridden: this.stored(field),\n invalid: false,\n }\n }\n const parsed = staged.clear ? { kind: 'clear' as const } : spec.parse(staged.text)\n return {\n text: staged.text,\n clear: staged.clear,\n overridden: parsed.kind === 'set',\n invalid: parsed.kind === 'invalid',\n }\n }\n\n private sectionValue(field: string): unknown {\n return this.scope.getSnapshot().value?.[field]\n }\n\n private baseValue(field: string): unknown {\n const base = this.scope.getSnapshot().base\n return typeof base === 'object' && base !== null && !Array.isArray(base)\n ? (base as Record<string, unknown>)[field]\n : undefined\n }\n\n private userLayer(): Record<string, unknown> | undefined {\n const user = this.scope.getSnapshot().user\n return typeof user === 'object' && user !== null && !Array.isArray(user)\n ? (user as Record<string, unknown>)\n : undefined\n }\n\n private stored(field: string): boolean {\n const user = this.userLayer()\n return user !== undefined && Object.prototype.hasOwnProperty.call(user, field)\n }\n\n /**\n * The writes a save would perform, in staged order. A field whose draft is\n * not a value its spec accepts carries no write (the save refuses).\n */\n private plan(): Array<{ field: string; run: (() => Promise<boolean>) | undefined }> {\n const plan: Array<{ field: string; run: (() => Promise<boolean>) | undefined }> = []\n for (const [field, staged] of this.staged) {\n if (field === 'apiKey') {\n const value = staged.text.trim()\n if (value !== '') {\n plan.push({ field, run: () => this.writeKey(value) })\n }\n continue\n }\n const spec = this.spec(field)\n if (staged.clear) {\n if (this.stored(field)) plan.push({ field, run: () => this.clear(field) })\n continue\n }\n if (staged.text === spec.format(this.sectionValue(field))) continue\n const parsed = spec.parse(staged.text)\n if (parsed.kind === 'invalid') plan.push({ field, run: undefined })\n else if (parsed.kind === 'clear') plan.push({ field, run: () => this.clear(field) })\n else plan.push({ field, run: () => this.store(field, parsed.value) })\n }\n return plan\n }\n\n private async clear(field: string): Promise<boolean> {\n await this.scope.unset(field)\n return !this.stored(field)\n }\n\n private async store(field: string, value: string | number | boolean): Promise<boolean> {\n await this.scope.set(field, value)\n return this.userLayer()?.[field] === value\n }\n\n /** Write the staged key, then re-read whether the Host now holds one. */\n private async writeKey(value: string): Promise<boolean> {\n try {\n const response = await this.api.credentials.set({ ref: this.credential.ref, value })\n if (!response.result.ok) return false\n } catch {\n return false\n }\n await this.readCredential()\n return this.credential.configured\n }\n\n /** Ask the credentials domain about the reference this page writes. */\n private async readCredential(): Promise<void> {\n const ref = this.credential.ref\n let response: Awaited<ReturnType<SettingsPageApi['credentials']['describe']>>\n try {\n response = await this.api.credentials.describe({ refs: [ref] })\n } catch {\n return\n }\n if (!response.result.ok) return\n const view = response.result.value.credentials[ref]\n const next = {\n ref,\n configured: view?.configured ?? false,\n writable: view?.writable ?? true,\n }\n if (next.configured === this.credential.configured && next.writable === this.credential.writable) return\n this.credential = next\n this.publish()\n }\n\n private publish(): void {\n if (this.disposed) return\n for (const listener of this.listeners) listener()\n }\n}\n","/**\n * Browser controller for the settings page's account-usage card.\n *\n * The card renders the same account/usage/credit facts the `/commandcode`\n * command prints, fetched Host-side through the `commandcode/report` Remote\n * (the browser never holds the API key). This controller owns the fetch\n * lifecycle — idle/loading/ready/error, one in-flight request at a time,\n * stale-response dropping — and the display formatting, so the React\n * component stays a thin renderer and node tests can drive everything.\n *\n * Deliberately JSX-free, mirroring `./settings.ts`.\n *\n * @module dsh-commandcode-provider/client/usage\n */\n\nimport type { CommandCodeUsageReport } from '../adapter.ts'\nimport type { RemoteResult } from '@deepseek-ai/dsh-typert-protocol'\n\n/**\n * Merge the plugin's Remote endpoint into the harness's typed client Remote\n * surface (the same declaration pattern the harness's generated\n * typert.remote-client files use), so `ctx.remote.commandcode.report()` is\n * typed once the contribution is mounted.\n */\ndeclare module '@deepseek-ai/dsh-typert-protocol' {\n interface TypertRemoteMap {\n 'commandcode/report': () => Promise<RemoteResult<CommandCodeUsageReport>>\n }\n interface TypertRemoteNamespaceMap {\n commandcode: {\n report: () => Promise<RemoteResult<CommandCodeUsageReport>>\n }\n }\n}\n\n/** The narrow slice of the mounted Remote this controller calls. */\nexport interface UsageRemote {\n report(): Promise<\n | { ok: true; value: CommandCodeUsageReport }\n | { ok: false; error: { message: string } }\n >\n}\n\n/** The card's fetch lifecycle. */\nexport type UsageStatus =\n /** Never fetched (no API key configured yet, or not requested). */\n | 'idle'\n /** A fetch is in flight; `report` retains the last good data if any. */\n | 'loading'\n /** The last fetch succeeded. */\n | 'ready'\n /** The last fetch failed (no key, unreachable host, old plugin). */\n | 'error'\n\n/** The card's full state face. */\nexport interface UsagePageState {\n status: UsageStatus\n /** The last successfully fetched report (retained across refetches). */\n report: CommandCodeUsageReport | undefined\n /** The last failure's message (error status). */\n error: string | undefined\n /** Millis timestamp of the last successful fetch. */\n fetchedAt: number | undefined\n}\n\nconst IDLE: UsagePageState = { status: 'idle', report: undefined, error: undefined, fetchedAt: undefined }\n\n/**\n * Controller bridging the `commandcode/report` Remote onto the card. Public\n * API mirrors {@link CommandCodeSettingsController}: `state()` projections,\n * `subscribe`, and one `refresh()` action.\n */\nexport class CommandCodeUsageController {\n private readonly remote: UsageRemote\n private readonly listeners = new Set<() => void>()\n private current: UsagePageState = IDLE\n private generation = 0\n private inFlight = false\n private disposed = false\n\n constructor(remote: UsageRemote) {\n this.remote = remote\n }\n\n /** Release every subscription. Idempotent; in-flight results are dropped. */\n dispose(): void {\n this.disposed = true\n this.generation += 1\n this.listeners.clear()\n }\n\n /** Subscribe to state projections. @returns the disposer. */\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener)\n return () => this.listeners.delete(listener)\n }\n\n /** The current card state face. */\n state(): UsagePageState {\n return this.current\n }\n\n /**\n * Fetch (or refetch) the report. Concurrent refreshes collapse onto one\n * request; a superseded fetch's late result is dropped, never published.\n */\n async refresh(): Promise<void> {\n if (this.disposed || this.inFlight) return\n const generation = ++this.generation\n this.inFlight = true\n this.current = { ...this.current, status: 'loading', error: undefined }\n this.publish()\n try {\n const response = await this.remote.report()\n if (this.disposed || generation !== this.generation) return\n if (response.ok) {\n this.current = { status: 'ready', report: response.value, error: undefined, fetchedAt: Date.now() }\n } else {\n this.current = { ...this.current, status: 'error', error: response.error.message }\n }\n } catch (error: unknown) {\n if (this.disposed || generation !== this.generation) return\n this.current = {\n ...this.current,\n status: 'error',\n error: error instanceof Error ? error.message : String(error),\n }\n } finally {\n if (generation === this.generation) this.inFlight = false\n }\n this.publish()\n }\n\n private publish(): void {\n if (this.disposed) return\n for (const listener of this.listeners) listener()\n }\n}\n\n// ---------------------------------------------------------------------------\n// Display formatting (shared by the component, covered by node tests)\n// ---------------------------------------------------------------------------\n\n/** Format a dollar amount compactly (2 decimals). */\nexport function formatMoney(value: number): string {\n return `$${value.toFixed(2)}`\n}\n\n/** Format a dollar amount precisely (4 decimals) for small totals. */\nexport function formatMoneyExact(value: number): string {\n return `$${value.toFixed(4)}`\n}\n\n/** Format a large token count compactly (1.9M style). */\nexport function formatTokensCompact(value: number): string {\n if (value >= 1e9) return `${(value / 1e9).toFixed(1)}B`\n if (value >= 1e6) return `${(value / 1e6).toFixed(1)}M`\n if (value >= 1e3) return `${(value / 1e3).toFixed(1)}K`\n return String(value)\n}\n\n/** One window's fill ratio in [0, 1]; 0 when uncapped. */\nexport function windowRatio(used: number, cap: number): number {\n if (cap <= 0) return 0\n return Math.max(0, Math.min(1, used / cap))\n}\n\n/** Format a millis timestamp as a local short date-time; empty when unset. */\nexport function formatResetAt(ms: number): string {\n if (ms <= 0) return ''\n return new Date(ms).toLocaleString()\n}\n","/**\n * Wire contract for the Command Code account-usage Remote\n * (`commandcode/report`).\n *\n * The settings page renders the same account/usage/credit facts the\n * `/commandcode` command prints, but the browser never holds the API key —\n * the report must be produced Host-side and cross the Connection RPC carrier.\n * The harness exposes plugin-defined Host methods through the Typert Gateway:\n * the Host half registers a strict invocation descriptor against a Cordis\n * service (`src/usage-remote.ts`), and the browser half mounts the matching\n * Remote contribution on `ctx.remote` (`src/client/index.ts`).\n *\n * This module is the single source both halves share: the result validator\n * (a hand-rolled {@link TypertSchema}, so neither half needs a schema library)\n * and the exact descriptor object, so the endpoint can never drift apart.\n * It is deliberately dependency-free — the client bundle inlines it, and only\n * `import type` edges leave it (erased at build).\n *\n * @module dsh-commandcode-provider/usage-wire\n */\n\nimport type { CommandCodeUsageReport } from './adapter.ts'\nimport type { InvocationDescriptor, TypertRemoteContribution, TypertSchema } from '@deepseek-ai/dsh-typert-protocol'\n\n/** The npm package identity both contribution registrations claim. */\nexport const USAGE_REMOTE_PACKAGE = '@mars-sea/dsh-commandcode-provider'\n\n/** Canonical `<namespace>/<method>` endpoint of the usage report Remote. */\nexport const USAGE_REPORT_ENDPOINT = 'commandcode/report'\n\n/** Reject one boundary value with a field-naming error. */\nfunction reject(field: string): never {\n throw new TypeError(`commandcode/report result: invalid ${field}`)\n}\n\n/** Read one required finite number field (`field` is the dotted error label). */\nfunction numberField(source: Record<string, unknown>, key: string, field: string): number {\n const value = source[key]\n if (typeof value !== 'number' || !Number.isFinite(value)) reject(field)\n return value\n}\n\n/** Read one required string field (`field` is the dotted error label). */\nfunction stringField(source: Record<string, unknown>, key: string, field: string): string {\n const value = source[key]\n if (typeof value !== 'string') reject(field)\n return value\n}\n\n/** Read one required boolean field (`field` is the dotted error label). */\nfunction booleanField(source: Record<string, unknown>, key: string, field: string): boolean {\n const value = source[key]\n if (typeof value !== 'boolean') reject(field)\n return value\n}\n\n/** Narrow an unknown value to a plain record, or reject. */\nfunction record(value: unknown, field: string): Record<string, unknown> {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) reject(field)\n return value as Record<string, unknown>\n}\n\n/** Validate one window-limit block (`fiveHour` / `weekly`). */\nfunction windowLimit(value: unknown, field: string): { used: number; cap: number; exceeded: boolean; resetAt: number } {\n const source = record(value, field)\n return {\n used: numberField(source, 'used', `${field}.used`),\n cap: numberField(source, 'cap', `${field}.cap`),\n exceeded: booleanField(source, 'exceeded', `${field}.exceeded`),\n resetAt: numberField(source, 'resetAt', `${field}.resetAt`),\n }\n}\n\n/**\n * Parse one untrusted boundary value into a {@link CommandCodeUsageReport}.\n * Optional sections stay optional; every present field is shape-checked so a\n * malformed frame fails the boundary instead of rendering garbage.\n */\nfunction parseUsageReport(value: unknown): CommandCodeUsageReport {\n const source = record(value, 'report')\n const failures = source.failures\n if (!Array.isArray(failures) || failures.some((entry) => typeof entry !== 'string')) reject('failures')\n const report: CommandCodeUsageReport = { failures: failures as string[] }\n\n if (source.account !== undefined) {\n const account = record(source.account, 'account')\n report.account = {\n id: stringField(account, 'id', 'account.id'),\n name: stringField(account, 'name', 'account.name'),\n userName: stringField(account, 'userName', 'account.userName'),\n }\n }\n\n if (source.usage !== undefined) {\n const usage = record(source.usage, 'usage')\n report.usage = {\n totalCount: numberField(usage, 'totalCount', 'usage.totalCount'),\n totalCost: numberField(usage, 'totalCost', 'usage.totalCost'),\n successRate: numberField(usage, 'successRate', 'usage.successRate'),\n completedCount: numberField(usage, 'completedCount', 'usage.completedCount'),\n failedCount: numberField(usage, 'failedCount', 'usage.failedCount'),\n totalTokensIn: numberField(usage, 'totalTokensIn', 'usage.totalTokensIn'),\n totalTokensOut: numberField(usage, 'totalTokensOut', 'usage.totalTokensOut'),\n totalCredits: numberField(usage, 'totalCredits', 'usage.totalCredits'),\n periodBasis: stringField(usage, 'periodBasis', 'usage.periodBasis'),\n }\n }\n\n if (source.credits !== undefined) {\n const credits = record(source.credits, 'credits')\n report.credits = {\n monthlyCredits: numberField(credits, 'monthlyCredits', 'credits.monthlyCredits'),\n purchasedCredits: numberField(credits, 'purchasedCredits', 'credits.purchasedCredits'),\n freeCredits: numberField(credits, 'freeCredits', 'credits.freeCredits'),\n fiveHour: windowLimit(credits.fiveHour, 'credits.fiveHour'),\n weekly: windowLimit(credits.weekly, 'credits.weekly'),\n }\n }\n\n if (source.plan !== undefined) {\n const plan = record(source.plan, 'plan')\n const monthly = plan.monthlyCredits\n if (monthly !== null && (typeof monthly !== 'number' || !Number.isFinite(monthly))) reject('plan.monthlyCredits')\n report.plan = {\n planId: stringField(plan, 'planId', 'plan.planId'),\n name: stringField(plan, 'name', 'plan.name'),\n status: stringField(plan, 'status', 'plan.status'),\n monthlyCredits: monthly as number | null,\n currentPeriodEnd: numberField(plan, 'currentPeriodEnd', 'plan.currentPeriodEnd'),\n }\n }\n\n return report\n}\n\n/**\n * The strict result codec both halves attach to the descriptor. Hand-rolled:\n * the client bundle may not require a schema library, and `TypertSchema` is\n * deliberately minimal so one `parse` function satisfies it.\n */\nexport const usageReportSchema: TypertSchema<CommandCodeUsageReport> = {\n parse: parseUsageReport,\n}\n\n/**\n * The one invocation descriptor, shared verbatim by the Host registration and\n * the Client mount. `service` names the Cordis key the Gateway resolves the\n * receiver from; `namespace`/`method` name the wire endpoint.\n */\nexport const USAGE_REPORT_DESCRIPTOR: InvocationDescriptor = {\n id: `${USAGE_REMOTE_PACKAGE}#${USAGE_REPORT_ENDPOINT}`,\n service: 'commandcodeUsage',\n namespace: 'commandcode',\n method: 'report',\n invocation: { kind: 'direct' },\n parameters: [],\n result: {\n mode: 'strict',\n typeSymbol: `${USAGE_REMOTE_PACKAGE}#CommandCodeUsageReport`,\n schema: usageReportSchema,\n },\n}\n\n/** The Host-face contribution registered on `ctx.typert`. */\nexport const USAGE_HOST_CONTRIBUTION = {\n package: USAGE_REMOTE_PACKAGE,\n face: 'host' as const,\n schemas: [],\n invocations: [USAGE_REPORT_DESCRIPTOR],\n}\n\n/** The Client-face contribution mounted on `ctx.remote`. */\nexport const USAGE_REMOTE_CONTRIBUTION: TypertRemoteContribution = {\n package: USAGE_REMOTE_PACKAGE,\n descriptors: [USAGE_REPORT_DESCRIPTOR],\n}\n","/**\n * React component for the \"Command Code\" settings page (browser half).\n *\n * Renders as a `settings.section` entry — a page at the same settings-nav\n * level as General / Models / Plugins. The shell supplies the nav row and\n * renders this body inside the content column. All copy comes from the\n * `settings.commandcode` locale namespace; all state comes from the\n * `CommandCodeSettingsController` injected by the slot registration.\n *\n * The layout mirrors the harness's settings pages: a max-width content\n * column, labelled fields with hints, a reset affordance, and a\n * save/discard footer. Styles are injected once by the client entry\n * (see src/client/index.ts) and class-prefixed `cc-` to stay local.\n */\n\nimport { useEffect } from 'react'\nimport { Button } from '@deepseek-ai/dsh-client-ui-primitives'\nimport type { Translate } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { CommandCodeCredits } from '../adapter.ts'\nimport type { SettingsCommandCodeKey } from './locales.ts'\nimport type { SettingsPageState, StagedField } from './settings.ts'\nimport type { UsagePageState } from './usage.ts'\nimport { formatMoney, formatMoneyExact, formatResetAt, formatTokensCompact, windowRatio } from './usage.ts'\n\n/** Props composed by the slot registration: locale seat + injected face. */\nexport interface CommandCodeSettingsProps {\n t: Translate<SettingsCommandCodeKey>\n useCommandCodeSettings<T>(selector: (state: SettingsPageState) => T): T\n useCommandCodeUsage<T>(selector: (state: UsagePageState) => T): T\n edit(field: string, text: string): void\n resetField(field: string): void\n save(): void\n discard(): void\n refreshUsage(): void\n}\n\n/** One labelled field row in the page body. */\nfunction Field({\n id,\n label,\n hint,\n state,\n disabled,\n numeric,\n placeholder,\n onEdit,\n onReset,\n t,\n}: {\n id: string\n label: string\n hint: string\n state: StagedField\n disabled: boolean\n numeric?: boolean\n placeholder?: string | undefined\n onEdit(text: string): void\n onReset(): void\n t: Translate<SettingsCommandCodeKey>\n}) {\n return (\n <div className=\"cc-field\">\n <div className=\"cc-fieldHead\">\n <label className=\"cc-label\" htmlFor={id}>{label}</label>\n <span className=\"cc-badges\">\n {state.overridden ? <span className=\"cc-badge\">{t('overridden')}</span> : null}\n <button type=\"button\" className=\"cc-reset\" disabled={disabled} onClick={onReset}>{t('reset')}</button>\n </span>\n </div>\n <input\n id={id}\n className={state.invalid ? 'cc-input cc-inputInvalid' : 'cc-input'}\n type=\"text\"\n inputMode={numeric ? 'numeric' : undefined}\n value={state.text}\n placeholder={placeholder}\n disabled={disabled}\n onChange={(event) => onEdit(event.target.value)}\n />\n <p className={state.invalid ? 'cc-invalid' : 'cc-hint'}>\n {state.invalid ? t('invalidNumber') : hint}\n </p>\n </div>\n )\n}\n\n/**\n * One boolean field row rendered as a toggle. The staged text is `'true'` /\n * `'false'` / `''` (unset → `defaultChecked`); toggling stages the string the\n * boolean field spec parses back into a real boolean on save.\n */\nfunction ToggleField({\n id,\n label,\n hint,\n state,\n disabled,\n defaultChecked,\n onEdit,\n onReset,\n t,\n}: {\n id: string\n label: string\n hint: string\n state: StagedField\n disabled: boolean\n defaultChecked: boolean\n onEdit(text: string): void\n onReset(): void\n t: Translate<SettingsCommandCodeKey>\n}) {\n const checked = state.text === '' ? defaultChecked : state.text === 'true'\n return (\n <div className=\"cc-field\">\n <div className=\"cc-fieldHead\">\n <label className=\"cc-label\" htmlFor={id}>{label}</label>\n <span className=\"cc-badges\">\n {state.overridden ? <span className=\"cc-badge\">{t('overridden')}</span> : null}\n <button type=\"button\" className=\"cc-reset\" disabled={disabled} onClick={onReset}>{t('reset')}</button>\n </span>\n </div>\n <label className=\"cc-toggleRow\">\n <input\n id={id}\n className=\"cc-toggle\"\n type=\"checkbox\"\n role=\"switch\"\n checked={checked}\n disabled={disabled}\n onChange={(event) => onEdit(event.target.checked ? 'true' : 'false')}\n />\n <span className=\"cc-hint\">{hint}</span>\n </label>\n </div>\n )\n}\n\n/** The API-key control: write-only, reports configured state, never echoes the key. */\nfunction SecretKeyField({\n label,\n hint,\n state,\n disabled,\n configured,\n configuredLabel,\n unconfiguredLabel,\n onEdit,\n}: {\n label: string\n hint: string\n state: StagedField\n disabled: boolean\n configured: boolean\n configuredLabel: string\n unconfiguredLabel: string\n onEdit(text: string): void\n}) {\n return (\n <div className=\"cc-field\">\n <div className=\"cc-fieldHead\">\n <label className=\"cc-label\" htmlFor=\"cc-api-key\">{label}</label>\n <span className=\"cc-badges\">\n <span className={configured ? 'cc-badge' : 'cc-badgeMuted'}>\n {configured ? configuredLabel : unconfiguredLabel}\n </span>\n </span>\n </div>\n <input\n id=\"cc-api-key\"\n className=\"cc-input\"\n type=\"password\"\n autoComplete=\"off\"\n value={state.text}\n disabled={disabled}\n onChange={(event) => onEdit(event.target.value)}\n />\n <p className=\"cc-hint\">{hint}</p>\n </div>\n )\n}\n\n/** One stat tile in the account card's summary grid. */\nfunction UsageStat({ label, value, sub }: { label: string; value: string; sub?: string | undefined }) {\n return (\n <div className=\"cc-usageStat\">\n <span className=\"cc-usageStatLabel\">{label}</span>\n <span className=\"cc-usageStatValue\">{value}</span>\n {sub !== undefined && sub !== '' ? <span className=\"cc-usageStatSub\">{sub}</span> : null}\n </div>\n )\n}\n\n/** One window-limit row: label, used/cap, a fill bar, and the reset time. */\nfunction UsageWindow({\n label,\n limit: { used, cap, exceeded, resetAt },\n t,\n}: {\n label: string\n limit: CommandCodeCredits['fiveHour']\n t: Translate<SettingsCommandCodeKey>\n}) {\n const ratio = windowRatio(used, cap)\n const reset = formatResetAt(resetAt)\n return (\n <div className=\"cc-usageWindow\">\n <div className=\"cc-usageWindowHead\">\n <span className=\"cc-usageWindowLabel\">{label}</span>\n {exceeded ? <span className=\"cc-usageExceeded\">{t('usageExceeded')}</span> : null}\n <span className=\"cc-usageWindowValue\">{cap > 0 ? `${formatMoney(used)} / ${formatMoney(cap)}` : formatMoney(used)}</span>\n </div>\n <div className=\"cc-usageBar\" role=\"progressbar\" aria-valuemin={0} aria-valuemax={100} aria-valuenow={Math.round(ratio * 100)}>\n <div className={exceeded ? 'cc-usageBarFill cc-usageBarFillWarn' : 'cc-usageBarFill'} style={{ width: `${ratio * 100}%` }} />\n </div>\n {reset !== '' ? <p className=\"cc-usageWindowReset\">{t('usageReset')} {reset}</p> : null}\n </div>\n )\n}\n\n/**\n * The account-usage card: the `/commandcode` dashboard's facts (account,\n * totals, credits, window limits) rendered as a native settings card. Data\n * arrives through the `commandcode/report` Remote; the API key never leaves\n * the Host.\n */\nfunction UsageCard({ t, usage, apiKeyConfigured, onRefresh }: {\n t: Translate<SettingsCommandCodeKey>\n usage: UsagePageState\n apiKeyConfigured: boolean\n onRefresh(): void\n}) {\n // First paint with a configured key fetches automatically; later fetches\n // are explicit (refresh button) or follow a landed save.\n useEffect(() => {\n if (apiKeyConfigured && usage.status === 'idle') onRefresh()\n }, [apiKeyConfigured, usage.status, onRefresh])\n\n const loading = usage.status === 'loading'\n const report = usage.report\n const account = report?.account\n const accountName = account === undefined ? '' : account.userName || account.name\n const credits = report?.credits\n const plan = report?.plan\n const planName = plan?.name ?? ''\n const planStatus = plan !== undefined && plan.status !== '' && plan.status !== 'active' ? plan.status : ''\n\n return (\n <div className=\"cc-usageCard\" aria-label={t('usageTitle')}>\n <div className=\"cc-usageHead\">\n <h3 className=\"cc-usageTitle\">{t('usageTitle')}</h3>\n {accountName !== '' ? <span className=\"cc-usageAccount\">{accountName}</span> : null}\n {planName !== '' ? <span className=\"cc-usagePlan\">{planName}</span> : null}\n {planStatus !== '' ? <span className=\"cc-usagePlanStatus\">{planStatus}</span> : null}\n <button type=\"button\" className=\"cc-usageRefresh\" disabled={loading || !apiKeyConfigured} onClick={onRefresh}>\n {loading ? t('usageRefreshing') : t('usageRefresh')}\n </button>\n </div>\n\n {!apiKeyConfigured ? <p className=\"cc-usageHint\">{t('usageNoKey')}</p> : null}\n {apiKeyConfigured && report === undefined && loading ? <p className=\"cc-usageHint\">{t('usageLoading')}</p> : null}\n {usage.status === 'error' ? (\n <p className=\"cc-usageError\" role=\"status\">\n <span>{t('usageError')}{usage.error !== undefined && usage.error !== '' ? ` — ${usage.error}` : ''}</span>\n </p>\n ) : null}\n\n {report?.usage !== undefined ? (\n <div className=\"cc-usageStats\">\n <UsageStat\n label={t('usageRequests')}\n value={String(report.usage.completedCount)}\n sub={`${t('usageFailed')} ${report.usage.failedCount}`}\n />\n <UsageStat label={t('usageSuccessRate')} value={`${report.usage.successRate}%`} />\n <UsageStat\n label={t('usageCost')}\n value={formatMoneyExact(report.usage.totalCost)}\n sub={`${formatMoney(report.usage.totalCredits)} credits`}\n />\n <UsageStat\n label={t('usageTokens')}\n value={formatTokensCompact(report.usage.totalTokensIn + report.usage.totalTokensOut)}\n sub={`${formatTokensCompact(report.usage.totalTokensIn)} ${t('usageTokensIn')} / ${formatTokensCompact(report.usage.totalTokensOut)} ${t('usageTokensOut')}`}\n />\n </div>\n ) : null}\n\n {credits !== undefined ? (\n <div className=\"cc-usageStats\">\n <UsageStat label={t('usageMonthly')} value={formatMoney(credits.monthlyCredits)} />\n <UsageStat label={t('usagePurchased')} value={formatMoney(credits.purchasedCredits)} />\n <UsageStat label={t('usageFree')} value={formatMoney(credits.freeCredits)} />\n </div>\n ) : null}\n\n {credits !== undefined ? (\n <div className=\"cc-usageWindows\">\n <UsageWindow label={t('usageFiveHour')} limit={credits.fiveHour} t={t} />\n <UsageWindow label={t('usageWeekly')} limit={credits.weekly} t={t} />\n </div>\n ) : null}\n\n {report !== undefined ? (\n <div className=\"cc-usageMeta\">\n {plan !== undefined && plan.currentPeriodEnd > 0 ? (\n <p className=\"cc-usageUpdated\">{t('usagePeriodEnd')} {new Date(plan.currentPeriodEnd).toLocaleDateString()}</p>\n ) : null}\n {usage.fetchedAt !== undefined ? (\n <p className=\"cc-usageUpdated\">{t('usageUpdated')} {new Date(usage.fetchedAt).toLocaleTimeString()}</p>\n ) : null}\n <span className=\"cc-usageMetaSpacer\" />\n {report.failures.length > 0 ? <p className=\"cc-usagePartial\" title={report.failures.join('; ')}>{t('usagePartial')}</p> : null}\n </div>\n ) : null}\n </div>\n )\n}\n\n/** The settings page body: connection facts for the Command Code provider. */\nexport function CommandCodeSettingsPage(props: CommandCodeSettingsProps) {\n const { t } = props\n const state = props.useCommandCodeSettings((snapshot) => snapshot)\n const usage = props.useCommandCodeUsage((snapshot) => snapshot)\n const disabled = !state.writable\n const keyLocked = !state.apiKeyWritable\n return (\n <section className=\"cc-section\" aria-label={t('title')}>\n <h2 className=\"cc-title\">{t('title')}</h2>\n <p className=\"cc-intro\">{t('intro')}</p>\n {!state.writable ? <p className=\"cc-readOnly\" role=\"status\">{t('readOnly')}</p> : null}\n <UsageCard\n t={t}\n usage={usage}\n apiKeyConfigured={state.apiKeyConfigured}\n onRefresh={props.refreshUsage}\n />\n <div className=\"cc-card\">\n <SecretKeyField\n label={t('apiKey')}\n hint={keyLocked ? t('apiKeyLocked') : t('apiKeyHint')}\n state={state.apiKey}\n disabled={disabled || keyLocked}\n configured={state.apiKeyConfigured}\n configuredLabel={t('apiKeySet')}\n unconfiguredLabel={t('apiKeyUnset')}\n onEdit={(text) => props.edit('apiKey', text)}\n />\n <Field\n id=\"cc-api-base\"\n label={t('apiBase')}\n hint={t('apiBaseHint')}\n state={state.apiBase}\n disabled={disabled}\n onEdit={(text) => props.edit('apiBase', text)}\n onReset={() => props.resetField('apiBase')}\n t={t}\n />\n <Field\n id=\"cc-working-dir\"\n label={t('workingDir')}\n hint={t('workingDirHint')}\n state={state.workingDir}\n disabled={disabled}\n placeholder={state.defaultWorkingDir}\n onEdit={(text) => props.edit('workingDir', text)}\n onReset={() => props.resetField('workingDir')}\n t={t}\n />\n <Field\n id=\"cc-request-timeout\"\n label={t('requestTimeoutMs')}\n hint={t('requestTimeoutMsHint')}\n state={state.requestTimeoutMs}\n disabled={disabled}\n numeric\n onEdit={(text) => props.edit('requestTimeoutMs', text)}\n onReset={() => props.resetField('requestTimeoutMs')}\n t={t}\n />\n <Field\n id=\"cc-stream-idle-timeout\"\n label={t('streamIdleTimeoutMs')}\n hint={t('streamIdleTimeoutMsHint')}\n state={state.streamIdleTimeoutMs}\n disabled={disabled}\n numeric\n onEdit={(text) => props.edit('streamIdleTimeoutMs', text)}\n onReset={() => props.resetField('streamIdleTimeoutMs')}\n t={t}\n />\n <ToggleField\n id=\"cc-filter-models-by-plan\"\n label={t('filterModelsByPlan')}\n hint={t('filterModelsByPlanHint')}\n state={state.filterModelsByPlan}\n disabled={disabled}\n defaultChecked\n onEdit={(text) => props.edit('filterModelsByPlan', text)}\n onReset={() => props.resetField('filterModelsByPlan')}\n t={t}\n />\n </div>\n <div className=\"cc-footer\">\n {state.failed ? <p className=\"cc-failed\" role=\"status\">{t('saveFailed')}</p> : null}\n <Button variant=\"ghost\" size=\"sm\" disabled={!state.dirty || state.saving} onClick={props.discard}>\n {t('discard')}\n </Button>\n <Button\n variant=\"primary\"\n size=\"sm\"\n disabled={!state.dirty || state.invalid || state.saving}\n onClick={props.save}\n >\n {t(state.saving ? 'saving' : 'save')}\n </Button>\n </div>\n </section>\n )\n}\n","/**\n * Locale copy for the \"Command Code\" settings page, and the declaration that\n * merges the page's namespace into the framework's `LocaleNamespaceMap` so\n * `ctx.locale.register` / `ctx.slots.register(..., { locale })` are typed.\n *\n * zh is the source of truth for the key set (repo convention); en must carry\n * the exact same keys — a mismatch is a compile error at the register site.\n */\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** Copy of the Command Code settings page. */\n 'settings.commandcode': SettingsCommandCodeKey\n }\n}\n\n/** Dictionary keys of the Command Code settings page. */\nexport type SettingsCommandCodeKey =\n | 'nav'\n | 'title'\n | 'intro'\n | 'apiKey'\n | 'apiKeyHint'\n | 'apiKeySet'\n | 'apiKeyUnset'\n | 'apiKeyLocked'\n | 'apiBase'\n | 'apiBaseHint'\n | 'workingDir'\n | 'workingDirHint'\n | 'requestTimeoutMs'\n | 'requestTimeoutMsHint'\n | 'streamIdleTimeoutMs'\n | 'streamIdleTimeoutMsHint'\n | 'filterModelsByPlan'\n | 'filterModelsByPlanHint'\n | 'overridden'\n | 'reset'\n | 'invalidNumber'\n | 'readOnly'\n | 'unsaved'\n | 'save'\n | 'saving'\n | 'saveFailed'\n | 'discard'\n | 'cancel'\n | 'usageTitle'\n | 'usageRefresh'\n | 'usageRefreshing'\n | 'usageLoading'\n | 'usageNoKey'\n | 'usageError'\n | 'usageRequests'\n | 'usageFailed'\n | 'usageSuccessRate'\n | 'usageCost'\n | 'usageTokens'\n | 'usageTokensIn'\n | 'usageTokensOut'\n | 'usageMonthly'\n | 'usagePurchased'\n | 'usageFree'\n | 'usageFiveHour'\n | 'usageWeekly'\n | 'usageExceeded'\n | 'usageReset'\n | 'usagePartial'\n | 'usageUpdated'\n | 'usagePeriodEnd'\n\nexport const zh: Record<SettingsCommandCodeKey, string> = {\n nav: 'Command Code',\n title: 'Command Code',\n intro:\n '配置 Command Code Provider 连接。API 密钥仅保存在本机凭据服务中,不会回显;'\n + '其他字段写入用户设置,下次请求即生效。',\n apiKey: 'API 密钥',\n apiKeyHint: '在 commandcode.ai 控制台创建。留空保存不会覆盖已存储的密钥。',\n apiKeySet: '已配置',\n apiKeyUnset: '未配置',\n apiKeyLocked: '密钥由只读来源提供',\n apiBase: 'API 地址',\n apiBaseHint: '默认 https://api.commandcode.ai,一般无需修改。',\n workingDir: '工作目录',\n workingDirHint: '可选。留空时使用占位符显示的进程工作目录;仅在需要固定路径时填写。',\n requestTimeoutMs: '请求超时(毫秒)',\n requestTimeoutMsHint: '等待响应首个字节的超时;默认 60000。',\n streamIdleTimeoutMs: '流空闲超时(毫秒)',\n streamIdleTimeoutMsHint: '生成流停滞多久视为断连;默认 300000(长思考模型可静默数分钟,默认值刻意放宽)。',\n filterModelsByPlan: '隐藏套餐外模型',\n filterModelsByPlanHint: '开启后,模型选择器只列出当前套餐可用的模型;账户持有按需余额时会显示全部。',\n overridden: '已覆盖',\n reset: '重置',\n invalidNumber: '无效数字',\n readOnly: '当前配置为只读。',\n unsaved: '未保存',\n save: '保存',\n saving: '保存中',\n saveFailed: '保存失败,请重试。',\n discard: '放弃',\n cancel: '取消',\n usageTitle: '账户用量',\n usageRefresh: '刷新',\n usageRefreshing: '刷新中…',\n usageLoading: '正在获取账户用量…',\n usageNoKey: '配置 API 密钥后,这里会显示账户的用量与额度状态。',\n usageError: '用量获取失败',\n usageRequests: '请求',\n usageFailed: '失败',\n usageSuccessRate: '成功率',\n usageCost: '花费',\n usageTokens: 'Token',\n usageTokensIn: '入',\n usageTokensOut: '出',\n usageMonthly: '月额度',\n usagePurchased: '已购',\n usageFree: '赠送',\n usageFiveHour: '5 小时窗口',\n usageWeekly: '每周窗口',\n usageExceeded: '已超限',\n usageReset: '重置于',\n usagePartial: '部分端点数据不可用',\n usageUpdated: '更新于',\n usagePeriodEnd: '账期截止',\n}\n\nexport const en: Record<SettingsCommandCodeKey, string> = {\n nav: 'Command Code',\n title: 'Command Code',\n intro:\n 'Configure the Command Code Provider connection. The API key is stored only'\n + ' in the local credential service and never echoed; other fields are written'\n + ' to user settings and take effect on the next request.',\n apiKey: 'API key',\n apiKeyHint: 'Create one in the commandcode.ai console. Saving with this field'\n + ' blank keeps the stored key.',\n apiKeySet: 'Configured',\n apiKeyUnset: 'Not configured',\n apiKeyLocked: 'Key provided by a read-only source',\n apiBase: 'API base URL',\n apiBaseHint: 'Defaults to https://api.commandcode.ai; usually leave as-is.',\n workingDir: 'Working directory',\n workingDirHint: 'Optional. Leave blank to use the process cwd shown as the'\n + ' placeholder; fill in only to pin a specific path.',\n requestTimeoutMs: 'Request timeout (ms)',\n requestTimeoutMsHint: 'Time to wait for the first response byte; default 60000.',\n streamIdleTimeoutMs: 'Stream idle timeout (ms)',\n streamIdleTimeoutMsHint: 'How long a stalled stream is treated as dead; default 300000'\n + ' (deliberately generous — long-thinking models can stay silent for minutes).',\n filterModelsByPlan: 'Hide out-of-plan models',\n filterModelsByPlanHint: 'When on, the model picker lists only models your subscription'\n + ' includes; any on-demand credit balance shows the full catalog.',\n overridden: 'Overridden',\n reset: 'Reset',\n invalidNumber: 'Invalid number',\n readOnly: 'Settings are read-only.',\n unsaved: 'Unsaved',\n save: 'Save',\n saving: 'Saving',\n saveFailed: 'Save failed, please retry.',\n discard: 'Discard',\n cancel: 'Cancel',\n usageTitle: 'Account usage',\n usageRefresh: 'Refresh',\n usageRefreshing: 'Refreshing…',\n usageLoading: 'Fetching account usage…',\n usageNoKey: 'Configure an API key to see this account’s usage and credit state here.',\n usageError: 'Could not fetch usage',\n usageRequests: 'Requests',\n usageFailed: 'failed',\n usageSuccessRate: 'Success rate',\n usageCost: 'Spend',\n usageTokens: 'Tokens',\n usageTokensIn: 'in',\n usageTokensOut: 'out',\n usageMonthly: 'Monthly',\n usagePurchased: 'Purchased',\n usageFree: 'Free',\n usageFiveHour: '5-hour window',\n usageWeekly: 'Weekly window',\n usageExceeded: 'Exceeded',\n usageReset: 'Resets',\n usagePartial: 'Some endpoint data unavailable',\n usageUpdated: 'Updated',\n usagePeriodEnd: 'Period ends',\n}\n","/**\n * Browser half of the dsh-commandcode-provider bundle.\n *\n * Two responsibilities:\n *\n * 1. A \"Command Code\" settings page (a `settings.section` entry at the same\n * nav level as General / Models / Plugins). The Models page renders an\n * unknown-adapter-family card for the `commandcode` provider and disables\n * its submit, so the API key cannot be configured there; this page is the\n * dedicated surface. It writes the API key through the credentials domain\n * (the `COMMANDCODE_API_KEY` reference the plugin resolves) and the\n * connection facts through the `llm-commandcode` settings namespace, so a\n * saved key or endpoint reaches the very next request.\n *\n * 2. The friendly-error wrapper for the harness's image-session gate — see\n * `./sessions.ts`. The wrapper is deliberately narrow: only the\n * `model-unavailable` code is rewritten, only when the message matches the\n * image-session gate, and only the message text changes.\n *\n * The wire types are spelled structurally in `./sessions.ts` (not imported\n * from `@deepseek-ai/dsh-host-apiproxy`) so this client bundle does not drag\n * an extra peer dependency into the package; the shapes are stable and the\n * client build inlines them anyway.\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport { createSnapshotStore } from '@deepseek-ai/dsh-client-runtime/client'\n// Type-only imports that pull in the client-service augmentations\n// (`slots`/`remote`/`locale` on Context) and the `settings.section` SlotMap\n// entry (`settingsScope` arrives through dsh-client-ui-settings).\nimport type {} from '@deepseek-ai/dsh-api-remotes/client'\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport { installFriendlyImageError } from './sessions.ts'\nimport { CommandCodeSettingsController, COMMANDCODE_NS, type SettingsPageState } from './settings.ts'\nimport { CommandCodeUsageController, type UsagePageState, type UsageRemote } from './usage.ts'\nimport { USAGE_REMOTE_CONTRIBUTION } from '../usage-wire.ts'\nimport { CommandCodeSettingsPage } from './section.tsx'\nimport { zh, en } from './locales.ts'\n\nexport { isImageSessionRejection, withFriendlyImageError } from './sessions.ts'\nimport type { ConnectionLike } from './sessions.ts'\n\n/** CSS for the settings page, injected once (harness bundle convention). */\nconst PAGE_CSS = `\n.cc-section{max-width:720px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:12px;display:flex}\n.cc-title{margin:0;font-size:18px;font-weight:600}\n.cc-intro{color:var(--dsw-alias-label-tertiary);margin:0;font-size:13px;line-height:1.5}\n.cc-readOnly{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}\n.cc-card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;padding:4px 16px}\n.cc-field{flex-direction:column;gap:6px;padding:12px 0;display:flex}\n.cc-field+.cc-field{border-top:1px solid var(--dsw-alias-border-l2)}\n.cc-fieldHead{align-items:center;gap:8px;display:flex}\n.cc-label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}\n.cc-badges{align-items:center;gap:8px;display:inline-flex}\n.cc-badge{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}\n.cc-badgeMuted{white-space:nowrap;color:var(--dsw-alias-label-tertiary);border-radius:999px;padding:1px 8px;font-size:11px;line-height:17px}\n.cc-reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}\n.cc-reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}\n.cc-reset:disabled{cursor:default;opacity:.5}\n.cc-input{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}\n.cc-input:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}\n.cc-input:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}\n.cc-inputInvalid{border-color:var(--dsw-alias-label-error)}\n.cc-invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}\n.cc-hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}\n.cc-footer{justify-content:flex-end;align-items:center;gap:8px;display:flex}\n.cc-toggleRow{align-items:center;gap:8px;cursor:pointer;display:flex}\n.cc-toggleRow:has(.cc-toggle:disabled){cursor:default}\n.cc-toggle{appearance:none;flex-shrink:0;background:var(--dsw-alias-border-l2);border-radius:999px;width:30px;height:18px;margin:0;cursor:pointer;position:relative;transition:background .15s ease}\n.cc-toggle:checked{background:var(--dsw-alias-brand-primary)}\n.cc-toggle::after{content:'';background:#fff;border-radius:50%;width:14px;height:14px;position:absolute;top:2px;left:2px;transition:left .15s ease}\n.cc-toggle:checked::after{left:14px}\n.cc-toggle:disabled{cursor:default;opacity:.5}\n.cc-failed{min-width:0;color:var(--dsw-alias-label-error);flex:1;margin:0;font-size:12px;line-height:1.5}\n.cc-usageCard{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;padding:14px 16px;flex-direction:column;gap:12px;display:flex}\n.cc-usageHead{align-items:center;gap:8px;display:flex}\n.cc-usageTitle{color:var(--dsw-alias-label-primary);flex:1;margin:0;font-size:13px;font-weight:600;line-height:1.5}\n.cc-usageAccount{max-width:40%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}\n.cc-usagePlan{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-brand-primary);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:600;line-height:17px}\n.cc-usagePlanStatus{white-space:nowrap;color:var(--dsw-alias-label-error);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}\n.cc-usageRefresh{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}\n.cc-usageRefresh:hover:not(:disabled){color:var(--dsw-alias-label-primary)}\n.cc-usageRefresh:disabled{cursor:default;opacity:.5}\n.cc-usageHint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}\n.cc-usageError{align-items:center;gap:8px;color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5;display:flex}\n.cc-usageStats{grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:8px;display:grid}\n.cc-usageStat{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:8px;padding:8px 10px;flex-direction:column;gap:2px;display:flex}\n.cc-usageStatLabel{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:1.5}\n.cc-usageStatValue{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}\n.cc-usageStatSub{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:1.5}\n.cc-usageWindows{flex-direction:column;gap:16px;display:flex}\n.cc-usageWindow{flex-direction:column;gap:6px;display:flex}\n.cc-usageWindowHead{align-items:baseline;gap:8px;display:flex}\n.cc-usageWindowLabel{color:var(--dsw-alias-label-secondary);flex:1;font-size:12px;font-weight:500;line-height:1.5}\n.cc-usageWindowValue{color:var(--dsw-alias-label-primary);font-size:12px;font-weight:500;line-height:1.5}\n.cc-usageExceeded{color:var(--dsw-alias-label-error);font-size:11px;font-weight:500;line-height:1.5}\n.cc-usageBar{overflow:hidden;background:var(--dsw-alias-bg-layer-1);border-radius:999px;height:6px}\n.cc-usageBarFill{background:var(--dsw-alias-brand-primary);border-radius:999px;height:100%;transition:width .3s ease}\n.cc-usageBarFillWarn{background:var(--dsw-alias-label-error)}\n.cc-usageWindowReset{color:var(--dsw-alias-label-tertiary);margin:0;font-size:11px;line-height:1.5}\n.cc-usageMeta{align-items:center;gap:8px;display:flex}\n.cc-usageMetaSpacer{flex:1}\n.cc-usageUpdated{color:var(--dsw-alias-label-tertiary);margin:0;font-size:11px;line-height:1.5}\n.cc-usagePartial{color:var(--dsw-alias-label-error);margin:0;font-size:11px;line-height:1.5}\n`\n\n/** Inject the page stylesheet once (idempotent per tag). */\nfunction injectPageCss(): void {\n if (typeof document === 'undefined') return\n const id = '@mars-sea/dsh-commandcode-provider/CommandCodeSettingsPage.module.css'\n if (document.querySelector(`style[data-plugin-css=\"${id}\"]`) !== null) return\n const tag = document.createElement('style')\n tag.dataset.plugin = '@mars-sea/dsh-commandcode-provider'\n tag.dataset.pluginCss = id\n tag.textContent = PAGE_CSS\n document.head.appendChild(tag)\n}\n\n/**\n * Client plugin body. Gates on the services the settings page needs\n * (`slots`, `locale`, `connection`, `remote`, `settingsScope`) plus the\n * `connection` used by the friendly-error wrapper — the same inject list the\n * harness's own settings-surface plugins declare.\n */\nexport function apply(ctx: Context): void {\n injectPageCss()\n\n // Friendly image-gate error wrapper (unchanged behaviour).\n const connection = ctx.get('connection') as ConnectionLike | undefined\n if (connection !== undefined) {\n installFriendlyImageError(connection)\n }\n\n // The \"Command Code\" settings page: register the section once the\n // `settings.section` declaration is on the ledger (ui-settings-general\n // owns the shell; registration order relative to it is not constrained —\n // `slots.inject` waits for the declaration).\n ctx.effect(() => ctx.locale.register('settings.commandcode', { zh, en }), 'dsh-commandcode-provider: page copy')\n\n const api = ctx.get('connection').api\n const hostDescription = ctx.get('connection').hostDescription\n const scope = ctx.settingsScope.bind<Record<string, unknown>>({ namespace: COMMANDCODE_NS })\n const controller = new CommandCodeSettingsController(scope, { credentials: api.credentials }, hostDescription)\n ctx.effect(() => () => controller.dispose(), 'dsh-commandcode-provider: settings controller')\n const store = createSnapshotStore<SettingsPageState>(controller.state())\n controller.subscribe(() => store.set(controller.state()))\n\n // The account-usage card: mount the shared Remote contribution, then resolve\n // the `remote.commandcode` namespace through a scoped inject. Cordis only\n // serves services a fiber declares in `inject`, and the namespace service\n // exists only after the mount — a static inject would deadlock the plugin\n // (the mounter would wait for its own mount), so the inject is registered\n // dynamically once the mount lands. A Host half that predates the Remote\n // fails the call instead, and the card renders its error branch.\n let usageNamespace: (typeof ctx.remote)['commandcode'] | undefined\n let usageMountError: string | undefined\n ctx.effect(() => {\n let cancelled = false\n let unmount: (() => Promise<void>) | undefined\n void ctx.remote.$mount(USAGE_REMOTE_CONTRIBUTION).then((dispose) => {\n if (cancelled) {\n void dispose()\n return\n }\n unmount = dispose\n ctx.inject(['remote.commandcode'], (namespaceCtx) => {\n usageNamespace = namespaceCtx.remote.commandcode\n namespaceCtx.effect(() => () => {\n usageNamespace = undefined\n }, 'dsh-commandcode-provider: usage namespace')\n })\n }, (error: unknown) => {\n // A mount failure (e.g. a harness without the Remote mount) leaves the\n // namespace unset; keep the reason so the card can surface it.\n usageMountError = error instanceof Error ? error.message : String(error)\n })\n return () => {\n cancelled = true\n usageNamespace = undefined\n if (unmount !== undefined) void unmount()\n }\n }, 'dsh-commandcode-provider: usage remote')\n const usageRemote: UsageRemote = {\n report: async () => {\n const namespace = usageNamespace\n if (namespace === undefined) {\n return { ok: false, error: { message: usageMountError ?? 'commandcode/report remote is not mounted' } }\n }\n return namespace.report()\n },\n }\n const usageController = new CommandCodeUsageController(usageRemote)\n ctx.effect(() => () => usageController.dispose(), 'dsh-commandcode-provider: usage controller')\n const usageStore = createSnapshotStore<UsagePageState>(usageController.state())\n usageController.subscribe(() => usageStore.set(usageController.state()))\n\n const injected = () => ({\n hooks: { commandCodeSettings: store, commandCodeUsage: usageStore },\n edit: (field: string, text: string) => controller.edit(field, text),\n resetField: (field: string) => controller.resetField(field),\n // A landed save can change the key or endpoint the usage endpoints read,\n // so the account card refetches; a failed save keeps the old data.\n save: () => void controller.save().then(() => {\n const settled = controller.state()\n if (!settled.failed && settled.apiKeyConfigured) void usageController.refresh()\n }),\n discard: () => controller.discard(),\n refreshUsage: () => void usageController.refresh(),\n })\n\n ctx.slots.inject('settings.section', () => ctx.slots.register({\n name: 'settings.section',\n id: 'commandcode',\n order: 12,\n label: () => ctx.locale.bind('settings.commandcode')('nav'),\n locale: 'settings.commandcode',\n inject: injected,\n }, CommandCodeSettingsPage))\n}\n\nexport const inject: readonly string[] = [\n 'slots',\n 'locale',\n 'connection',\n 'remote',\n 'settingsScope',\n]\n"],"mappings":";;;;;;;;;;;;EAiEA,SAAgB,wBACd,QAC0E;GAC1E,OACE,CAAC,OAAO,OAAO,MACf,OAAO,OAAO,MAAM,SAAS,uBAC7B,OAAO,OAAO,MAAM,QAAQ,SAAS,6BAA6B;EAEtE;;EAGA,SAAgB,uBAAuB,UAAsC;GAC3E,MAAM,cAAc,SAAS,YAAY,KAAK,QAAQ;GACtD,OAAO;IACL,GAAG;IACH,aAAa,OAAO,SAAS,WAAW;KACtC,MAAM,SAAS,MAAM,YAAY,SAAS,MAAM;KAChD,IAAI,CAAC,wBAAwB,MAAM,GAAG,OAAO;KAC7C,MAAM,QAAQ,OAAO,OAAO,MAAM,SAAS,SAAS,QAAQ;KAC5D,OAAO;MACL,GAAG;MACH,QAAQ;OACN,GAAG,OAAO;OACV,OAAO;QACL,GAAG,OAAO,OAAO;QACjB,SACE,iBAAiB,MAAM;OAE3B;MACF;KACF;IACF;GACF;EACF;;EAQA,SAAgB,0BAA0B,YAAkC;GAC1E,WAAW,IAAI,WAAW,uBAAuB,WAAW,IAAI,QAAQ;EAC1E;;;;EC7EA,MAAa,iBAAiB;;EAE9B,MAAa,sBAAsB;;EAwFnC,SAAS,UAAU,OAA0B;GAC3C,OAAO;IACL;IACA,SAAS,UAAW,OAAO,UAAU,WAAW,QAAQ;IACxD,QAAQ,SAAS;KACf,MAAM,UAAU,KAAK,KAAK;KAC1B,OAAO,YAAY,KAAK,EAAE,MAAM,QAAQ,IAAI;MAAE,MAAM;MAAO,OAAO;KAAQ;IAC5E;GACF;EACF;;EAGA,SAASA,cAAY,OAA0B;GAC7C,OAAO;IACL;IACA,SAAS,UAAW,OAAO,UAAU,WAAW,OAAO,KAAK,IAAI;IAChE,QAAQ,SAAS;KACf,MAAM,UAAU,KAAK,KAAK;KAC1B,IAAI,YAAY,IAAI,OAAO,EAAE,MAAM,QAAQ;KAC3C,MAAM,SAAS,OAAO,OAAO;KAC7B,OAAO,OAAO,SAAS,MAAM,IAAI;MAAE,MAAM;MAAO,OAAO;KAAO,IAAI,EAAE,MAAM,UAAU;IACtF;GACF;EACF;;;;;;EAOA,SAASC,eAAa,OAA0B;GAC9C,OAAO;IACL;IACA,SAAS,UAAW,OAAO,UAAU,YAAY,OAAO,KAAK,IAAI;IACjE,QAAQ,SAAS;KACf,MAAM,UAAU,KAAK,KAAK;KAC1B,IAAI,YAAY,IAAI,OAAO,EAAE,MAAM,QAAQ;KAC3C,IAAI,YAAY,QAAQ,OAAO;MAAE,MAAM;MAAO,OAAO;KAAK;KAC1D,IAAI,YAAY,SAAS,OAAO;MAAE,MAAM;MAAO,OAAO;KAAM;KAC5D,OAAO,EAAE,MAAM,UAAU;IAC3B;GACF;EACF;;EAGA,MAAM,iBAA8B;GAClC,UAAU,SAAS;GACnB,UAAU,YAAY;GACtBD,cAAY,kBAAkB;GAC9BA,cAAY,qBAAqB;GACjCC,eAAa,oBAAoB;EACnC;;;;;;EAOA,IAAa,gCAAb,MAA2C;GACzC;GACA;GACA,QAAyB,IAAI,IAAI,eAAe,KAAK,SAAS,CAAC,KAAK,OAAO,IAAI,CAAC,CAAC;GACjF,yBAA0B,IAAI,IAAoB;GAClD,4BAA6B,IAAI,IAAgB;GACjD,YAAgD,CAAC;GACjD,WAAmB;GACnB;GACA,aAAqB;IAAE,KAAK;IAAqB,YAAY;IAAO,UAAU;GAAK;GACnF,SAAiB;GACjB,SAAiB;;;;;;;GAQjB,YACE,OACA,KACA,iBACA;IACA,KAAK,QAAQ;IACb,KAAK,MAAM;IACX,KAAK,UAAU,KAAK,MAAM,gBAAgB;KACxC,KAAK,uBAAuB;KAC5B,KAAK,QAAQ;IACf,CAAC,CAAC;IACF,IAAI,oBAAoB,KAAA,GAAW;KACjC,KAAK,oBAAoB,gBAAgB,YAAY,CAAC,EAAE;KACxD,KAAK,UAAU,KAAK,gBAAgB,gBAAgB;MAClD,IAAI,KAAK,UAAU;MACnB,MAAM,MAAM,gBAAgB,YAAY,CAAC,EAAE;MAC3C,IAAI,QAAQ,KAAK,mBAAmB;OAClC,KAAK,oBAAoB;OACzB,KAAK,QAAQ;MACf;KACF,CAAC,CAAC;IACJ;IACA,KAAK,uBAAuB;IAC5B,KAAU,eAAe;GAC3B;;GAGA,UAAgB;IACd,IAAI,KAAK,UAAU;IACnB,KAAK,WAAW;IAChB,KAAK,MAAM,WAAW,KAAK,WAAW,QAAQ;IAC9C,KAAK,UAAU,SAAS;IACxB,KAAK,UAAU,MAAM;GACvB;;;;;;;GAQA,yBAAuC;IACrC,MAAM,WAAW,KAAK,MAAM,YAAY;IACxC,MAAM,QAAQ,OAAO,SAAS,OAAO,cAAc,YAAY,SAAS,MAAM,UAAU,SAAS,IAC7F,SAAS,MAAM,YACf;IACJ,IAAI,UAAU,KAAK,WAAW,KAAK;IACnC,KAAK,aAAa;KAAE,KAAK;KAAO,YAAY;KAAO,UAAU;IAAK;IAClE,KAAU,eAAe;GAC3B;;GAGA,UAAU,UAAkC;IAC1C,KAAK,UAAU,IAAI,QAAQ;IAC3B,aAAa,KAAK,UAAU,OAAO,QAAQ;GAC7C;;GAGA,QAA2B;IACzB,MAAM,WAAW,KAAK,MAAM,YAAY;IACxC,MAAM,OAAO,KAAK,KAAK;IACvB,OAAO;KACL,WAAW,SAAS,WAAW;KAC/B,UAAU,SAAS;KACnB,kBAAkB,KAAK,WAAW;KAClC,gBAAgB,KAAK,WAAW;KAChC,QAAQ;MACN,MAAM,KAAK,OAAO,IAAI,QAAQ,CAAC,EAAE,QAAQ;MACzC,OAAO;MACP,YAAY;MACZ,SAAS;KACX;KACA,SAAS,KAAK,MAAM,SAAS;KAC7B,YAAY,KAAK,MAAM,YAAY;KACnC,mBAAmB,KAAK;KACxB,kBAAkB,KAAK,MAAM,kBAAkB;KAC/C,qBAAqB,KAAK,MAAM,qBAAqB;KACrD,oBAAoB,KAAK,MAAM,oBAAoB;KACnD,OAAO,KAAK,SAAS;KACrB,SAAS,KAAK,MAAM,SAAS,KAAK,QAAQ,KAAA,CAAS;KACnD,QAAQ,KAAK;KACb,QAAQ,KAAK;IACf;GACF;;GAGA,KAAK,OAAe,MAAoB;IACtC,KAAK,OAAO,IAAI,OAAO;KAAE;KAAM,OAAO;IAAM,CAAC;IAC7C,KAAK,SAAS;IACd,KAAK,QAAQ;GACf;;GAGA,WAAW,OAAqB;IAC9B,IAAI,UAAU,UAAU;KACtB,KAAK,OAAO,OAAO,QAAQ;KAC3B,KAAK,SAAS;KACd,KAAK,QAAQ;KACb;IACF;IACA,MAAM,OAAO,KAAK,KAAK,KAAK;IAC5B,KAAK,OAAO,IAAI,OAAO;KAAE,MAAM,KAAK,OAAO,KAAK,UAAU,KAAK,CAAC;KAAG,OAAO;IAAK,CAAC;IAChF,KAAK,SAAS;IACd,KAAK,QAAQ;GACf;;GAGA,UAAgB;IACd,IAAI,KAAK,OAAO,SAAS,KAAK,CAAC,KAAK,QAAQ;IAC5C,KAAK,OAAO,MAAM;IAClB,KAAK,SAAS;IACd,KAAK,QAAQ;GACf;;GAGA,MAAM,OAAsB;IAC1B,MAAM,OAAO,KAAK,KAAK;IACvB,IAAI,KAAK,WAAW,KAAK,KAAK,QAAQ;IACtC,MAAM,OAAsC,CAAC;IAC7C,KAAK,MAAM,QAAQ,MAAM;KACvB,IAAI,KAAK,QAAQ,KAAA,GAAW;KAC5B,KAAK,KAAK,KAAK,GAAG;IACpB;IACA,KAAK,SAAS;IACd,KAAK,SAAS;IACd,KAAK,QAAQ;IACb,IAAI,SAAS;IACb,KAAK,MAAM,OAAO,MAAM,SAAU,MAAM,IAAI,KAAM;IAClD,KAAK,SAAS;IACd,KAAK,SAAS,CAAC;IACf,IAAI,QAAQ,KAAK,OAAO,MAAM;IAC9B,KAAK,QAAQ;GACf;GAMA,KAAa,OAA0B;IACrC,MAAM,OAAO,KAAK,MAAM,IAAI,KAAK;IACjC,IAAI,SAAS,KAAA,GAAW,MAAM,IAAI,MAAM,0CAA0C,OAAO;IACzF,OAAO;GACT;;GAGA,MAAc,OAA4B;IACxC,MAAM,OAAO,KAAK,KAAK,KAAK;IAC5B,MAAM,SAAS,KAAK,OAAO,IAAI,KAAK;IACpC,IAAI,WAAW,KAAA,GACb,OAAO;KACL,MAAM,KAAK,OAAO,KAAK,aAAa,KAAK,CAAC;KAC1C,OAAO;KACP,YAAY,KAAK,OAAO,KAAK;KAC7B,SAAS;IACX;IAEF,MAAM,SAAS,OAAO,QAAQ,EAAE,MAAM,QAAiB,IAAI,KAAK,MAAM,OAAO,IAAI;IACjF,OAAO;KACL,MAAM,OAAO;KACb,OAAO,OAAO;KACd,YAAY,OAAO,SAAS;KAC5B,SAAS,OAAO,SAAS;IAC3B;GACF;GAEA,aAAqB,OAAwB;IAC3C,OAAO,KAAK,MAAM,YAAY,CAAC,CAAC,QAAQ;GAC1C;GAEA,UAAkB,OAAwB;IACxC,MAAM,OAAO,KAAK,MAAM,YAAY,CAAC,CAAC;IACtC,OAAO,OAAO,SAAS,YAAY,SAAS,QAAQ,CAAC,MAAM,QAAQ,IAAI,IAClE,KAAiC,SAClC,KAAA;GACN;GAEA,YAAyD;IACvD,MAAM,OAAO,KAAK,MAAM,YAAY,CAAC,CAAC;IACtC,OAAO,OAAO,SAAS,YAAY,SAAS,QAAQ,CAAC,MAAM,QAAQ,IAAI,IAClE,OACD,KAAA;GACN;GAEA,OAAe,OAAwB;IACrC,MAAM,OAAO,KAAK,UAAU;IAC5B,OAAO,SAAS,KAAA,KAAa,OAAO,UAAU,eAAe,KAAK,MAAM,KAAK;GAC/E;;;;;GAMA,OAAoF;IAClF,MAAM,OAA4E,CAAC;IACnF,KAAK,MAAM,CAAC,OAAO,WAAW,KAAK,QAAQ;KACzC,IAAI,UAAU,UAAU;MACtB,MAAM,QAAQ,OAAO,KAAK,KAAK;MAC/B,IAAI,UAAU,IACZ,KAAK,KAAK;OAAE;OAAO,WAAW,KAAK,SAAS,KAAK;MAAE,CAAC;MAEtD;KACF;KACA,MAAM,OAAO,KAAK,KAAK,KAAK;KAC5B,IAAI,OAAO,OAAO;MAChB,IAAI,KAAK,OAAO,KAAK,GAAG,KAAK,KAAK;OAAE;OAAO,WAAW,KAAK,MAAM,KAAK;MAAE,CAAC;MACzE;KACF;KACA,IAAI,OAAO,SAAS,KAAK,OAAO,KAAK,aAAa,KAAK,CAAC,GAAG;KAC3D,MAAM,SAAS,KAAK,MAAM,OAAO,IAAI;KACrC,IAAI,OAAO,SAAS,WAAW,KAAK,KAAK;MAAE;MAAO,KAAK,KAAA;KAAU,CAAC;UAC7D,IAAI,OAAO,SAAS,SAAS,KAAK,KAAK;MAAE;MAAO,WAAW,KAAK,MAAM,KAAK;KAAE,CAAC;UAC9E,KAAK,KAAK;MAAE;MAAO,WAAW,KAAK,MAAM,OAAO,OAAO,KAAK;KAAE,CAAC;IACtE;IACA,OAAO;GACT;GAEA,MAAc,MAAM,OAAiC;IACnD,MAAM,KAAK,MAAM,MAAM,KAAK;IAC5B,OAAO,CAAC,KAAK,OAAO,KAAK;GAC3B;GAEA,MAAc,MAAM,OAAe,OAAoD;IACrF,MAAM,KAAK,MAAM,IAAI,OAAO,KAAK;IACjC,OAAO,KAAK,UAAU,CAAC,GAAG,WAAW;GACvC;;GAGA,MAAc,SAAS,OAAiC;IACtD,IAAI;KAEF,IAAI,EAAC,MADkB,KAAK,IAAI,YAAY,IAAI;MAAE,KAAK,KAAK,WAAW;MAAK;KAAM,CAAC,EAAA,CACrE,OAAO,IAAI,OAAO;IAClC,QAAQ;KACN,OAAO;IACT;IACA,MAAM,KAAK,eAAe;IAC1B,OAAO,KAAK,WAAW;GACzB;;GAGA,MAAc,iBAAgC;IAC5C,MAAM,MAAM,KAAK,WAAW;IAC5B,IAAI;IACJ,IAAI;KACF,WAAW,MAAM,KAAK,IAAI,YAAY,SAAS,EAAE,MAAM,CAAC,GAAG,EAAE,CAAC;IAChE,QAAQ;KACN;IACF;IACA,IAAI,CAAC,SAAS,OAAO,IAAI;IACzB,MAAM,OAAO,SAAS,OAAO,MAAM,YAAY;IAC/C,MAAM,OAAO;KACX;KACA,YAAY,MAAM,cAAc;KAChC,UAAU,MAAM,YAAY;IAC9B;IACA,IAAI,KAAK,eAAe,KAAK,WAAW,cAAc,KAAK,aAAa,KAAK,WAAW,UAAU;IAClG,KAAK,aAAa;IAClB,KAAK,QAAQ;GACf;GAEA,UAAwB;IACtB,IAAI,KAAK,UAAU;IACnB,KAAK,MAAM,YAAY,KAAK,WAAW,SAAS;GAClD;EACF;;;EC5YA,MAAM,OAAuB;GAAE,QAAQ;GAAQ,QAAQ,KAAA;GAAW,OAAO,KAAA;GAAW,WAAW,KAAA;EAAU;;;;;;EAOzG,IAAa,6BAAb,MAAwC;GACtC;GACA,4BAA6B,IAAI,IAAgB;GACjD,UAAkC;GAClC,aAAqB;GACrB,WAAmB;GACnB,WAAmB;GAEnB,YAAY,QAAqB;IAC/B,KAAK,SAAS;GAChB;;GAGA,UAAgB;IACd,KAAK,WAAW;IAChB,KAAK,cAAc;IACnB,KAAK,UAAU,MAAM;GACvB;;GAGA,UAAU,UAAkC;IAC1C,KAAK,UAAU,IAAI,QAAQ;IAC3B,aAAa,KAAK,UAAU,OAAO,QAAQ;GAC7C;;GAGA,QAAwB;IACtB,OAAO,KAAK;GACd;;;;;GAMA,MAAM,UAAyB;IAC7B,IAAI,KAAK,YAAY,KAAK,UAAU;IACpC,MAAM,aAAa,EAAE,KAAK;IAC1B,KAAK,WAAW;IAChB,KAAK,UAAU;KAAE,GAAG,KAAK;KAAS,QAAQ;KAAW,OAAO,KAAA;IAAU;IACtE,KAAK,QAAQ;IACb,IAAI;KACF,MAAM,WAAW,MAAM,KAAK,OAAO,OAAO;KAC1C,IAAI,KAAK,YAAY,eAAe,KAAK,YAAY;KACrD,IAAI,SAAS,IACX,KAAK,UAAU;MAAE,QAAQ;MAAS,QAAQ,SAAS;MAAO,OAAO,KAAA;MAAW,WAAW,KAAK,IAAI;KAAE;UAElG,KAAK,UAAU;MAAE,GAAG,KAAK;MAAS,QAAQ;MAAS,OAAO,SAAS,MAAM;KAAQ;IAErF,SAAS,OAAgB;KACvB,IAAI,KAAK,YAAY,eAAe,KAAK,YAAY;KACrD,KAAK,UAAU;MACb,GAAG,KAAK;MACR,QAAQ;MACR,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;KAC9D;IACF,UAAU;KACR,IAAI,eAAe,KAAK,YAAY,KAAK,WAAW;IACtD;IACA,KAAK,QAAQ;GACf;GAEA,UAAwB;IACtB,IAAI,KAAK,UAAU;IACnB,KAAK,MAAM,YAAY,KAAK,WAAW,SAAS;GAClD;EACF;;EAOA,SAAgB,YAAY,OAAuB;GACjD,OAAO,IAAI,MAAM,QAAQ,CAAC;EAC5B;;EAGA,SAAgB,iBAAiB,OAAuB;GACtD,OAAO,IAAI,MAAM,QAAQ,CAAC;EAC5B;;EAGA,SAAgB,oBAAoB,OAAuB;GACzD,IAAI,SAAS,KAAK,OAAO,IAAI,QAAQ,IAAA,CAAK,QAAQ,CAAC,EAAE;GACrD,IAAI,SAAS,KAAK,OAAO,IAAI,QAAQ,IAAA,CAAK,QAAQ,CAAC,EAAE;GACrD,IAAI,SAAS,KAAK,OAAO,IAAI,QAAQ,IAAA,CAAK,QAAQ,CAAC,EAAE;GACrD,OAAO,OAAO,KAAK;EACrB;;EAGA,SAAgB,YAAY,MAAc,KAAqB;GAC7D,IAAI,OAAO,GAAG,OAAO;GACrB,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,OAAO,GAAG,CAAC;EAC5C;;EAGA,SAAgB,cAAc,IAAoB;GAChD,IAAI,MAAM,GAAG,OAAO;GACpB,OAAO,IAAI,KAAK,EAAE,CAAC,CAAC,eAAe;EACrC;;;;EClJA,MAAa,uBAAuB;;EAGpC,MAAa,wBAAwB;;EAGrC,SAAS,OAAO,OAAsB;GACpC,MAAM,IAAI,UAAU,sCAAsC,OAAO;EACnE;;EAGA,SAAS,YAAY,QAAiC,KAAa,OAAuB;GACxF,MAAM,QAAQ,OAAO;GACrB,IAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,GAAG,OAAO,KAAK;GACtE,OAAO;EACT;;EAGA,SAAS,YAAY,QAAiC,KAAa,OAAuB;GACxF,MAAM,QAAQ,OAAO;GACrB,IAAI,OAAO,UAAU,UAAU,OAAO,KAAK;GAC3C,OAAO;EACT;;EAGA,SAAS,aAAa,QAAiC,KAAa,OAAwB;GAC1F,MAAM,QAAQ,OAAO;GACrB,IAAI,OAAO,UAAU,WAAW,OAAO,KAAK;GAC5C,OAAO;EACT;;EAGA,SAAS,OAAO,OAAgB,OAAwC;GACtE,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK,GAAG,OAAO,KAAK;GACrF,OAAO;EACT;;EAGA,SAAS,YAAY,OAAgB,OAAkF;GACrH,MAAM,SAAS,OAAO,OAAO,KAAK;GAClC,OAAO;IACL,MAAM,YAAY,QAAQ,QAAQ,GAAG,MAAM,MAAM;IACjD,KAAK,YAAY,QAAQ,OAAO,GAAG,MAAM,KAAK;IAC9C,UAAU,aAAa,QAAQ,YAAY,GAAG,MAAM,UAAU;IAC9D,SAAS,YAAY,QAAQ,WAAW,GAAG,MAAM,SAAS;GAC5D;EACF;;;;;;EAOA,SAAS,iBAAiB,OAAwC;GAChE,MAAM,SAAS,OAAO,OAAO,QAAQ;GACrC,MAAM,WAAW,OAAO;GACxB,IAAI,CAAC,MAAM,QAAQ,QAAQ,KAAK,SAAS,MAAM,UAAU,OAAO,UAAU,QAAQ,GAAG,OAAO,UAAU;GACtG,MAAM,SAAiC,EAAY,SAAqB;GAExE,IAAI,OAAO,YAAY,KAAA,GAAW;IAChC,MAAM,UAAU,OAAO,OAAO,SAAS,SAAS;IAChD,OAAO,UAAU;KACf,IAAI,YAAY,SAAS,MAAM,YAAY;KAC3C,MAAM,YAAY,SAAS,QAAQ,cAAc;KACjD,UAAU,YAAY,SAAS,YAAY,kBAAkB;IAC/D;GACF;GAEA,IAAI,OAAO,UAAU,KAAA,GAAW;IAC9B,MAAM,QAAQ,OAAO,OAAO,OAAO,OAAO;IAC1C,OAAO,QAAQ;KACb,YAAY,YAAY,OAAO,cAAc,kBAAkB;KAC/D,WAAW,YAAY,OAAO,aAAa,iBAAiB;KAC5D,aAAa,YAAY,OAAO,eAAe,mBAAmB;KAClE,gBAAgB,YAAY,OAAO,kBAAkB,sBAAsB;KAC3E,aAAa,YAAY,OAAO,eAAe,mBAAmB;KAClE,eAAe,YAAY,OAAO,iBAAiB,qBAAqB;KACxE,gBAAgB,YAAY,OAAO,kBAAkB,sBAAsB;KAC3E,cAAc,YAAY,OAAO,gBAAgB,oBAAoB;KACrE,aAAa,YAAY,OAAO,eAAe,mBAAmB;IACpE;GACF;GAEA,IAAI,OAAO,YAAY,KAAA,GAAW;IAChC,MAAM,UAAU,OAAO,OAAO,SAAS,SAAS;IAChD,OAAO,UAAU;KACf,gBAAgB,YAAY,SAAS,kBAAkB,wBAAwB;KAC/E,kBAAkB,YAAY,SAAS,oBAAoB,0BAA0B;KACrF,aAAa,YAAY,SAAS,eAAe,qBAAqB;KACtE,UAAU,YAAY,QAAQ,UAAU,kBAAkB;KAC1D,QAAQ,YAAY,QAAQ,QAAQ,gBAAgB;IACtD;GACF;GAEA,IAAI,OAAO,SAAS,KAAA,GAAW;IAC7B,MAAM,OAAO,OAAO,OAAO,MAAM,MAAM;IACvC,MAAM,UAAU,KAAK;IACrB,IAAI,YAAY,SAAS,OAAO,YAAY,YAAY,CAAC,OAAO,SAAS,OAAO,IAAI,OAAO,qBAAqB;IAChH,OAAO,OAAO;KACZ,QAAQ,YAAY,MAAM,UAAU,aAAa;KACjD,MAAM,YAAY,MAAM,QAAQ,WAAW;KAC3C,QAAQ,YAAY,MAAM,UAAU,aAAa;KACjD,gBAAgB;KAChB,kBAAkB,YAAY,MAAM,oBAAoB,uBAAuB;IACjF;GACF;GAEA,OAAO;EACT;;EAuCA,MAAa,4BAAsD;GACjE,SAAS;GACT,aAAa,CAAC;IAxBd,IAAI,GAAG,qBAAqB,GAAG;IAC/B,SAAS;IACT,WAAW;IACX,QAAQ;IACR,YAAY,EAAE,MAAM,SAAS;IAC7B,YAAY,CAAC;IACb,QAAQ;KACN,MAAM;KACN,YAAY,GAAG,qBAAqB;KACpC,QAAQ,EAlBV,OAAO,iBAkBG;IACV;GAcc,CAAuB;EACvC;;;;;;;;;;;;;;;;;;EC1IA,SAAS,MAAM,EACb,IACA,OACA,MACA,OACA,UACA,SACA,aACA,QACA,SACA,KAYC;GACD,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;MAAK,WAAU;MAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;OAAO,WAAU;OAAW,SAAS;OAAK,UAAA;MAAa,CAAA,GACvD,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;OAAM,WAAU;OAAhB,UAAA,CACG,MAAM,aAAa,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;QAAM,WAAU;QAAY,UAAA,EAAE,YAAY;OAAQ,CAAA,IAAI,MAC1E,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;QAAQ,MAAK;QAAS,WAAU;QAAqB;QAAU,SAAS;QAAU,UAAA,EAAE,OAAO;OAAU,CAAA,CACjG;MACH,CAAA,CAAA;;KACL,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;MACM;MACJ,WAAW,MAAM,UAAU,6BAA6B;MACxD,MAAK;MACL,WAAW,UAAU,YAAY,KAAA;MACjC,OAAO,MAAM;MACA;MACH;MACV,WAAW,UAAU,OAAO,MAAM,OAAO,KAAK;KAC/C,CAAA;KACD,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;MAAG,WAAW,MAAM,UAAU,eAAe;MAC1C,UAAA,MAAM,UAAU,EAAE,eAAe,IAAI;KACrC,CAAA;IACA;;EAET;;;;;;EAOA,SAAS,YAAY,EACnB,IACA,OACA,MACA,OACA,UACA,gBACA,QACA,SACA,KAWC;GACD,MAAM,UAAU,MAAM,SAAS,KAAK,iBAAiB,MAAM,SAAS;GACpE,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;MAAO,WAAU;MAAW,SAAS;MAAK,UAAA;KAAa,CAAA,GACvD,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;MAAM,WAAU;MAAhB,UAAA,CACG,MAAM,aAAa,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;OAAM,WAAU;OAAY,UAAA,EAAE,YAAY;MAAQ,CAAA,IAAI,MAC1E,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;OAAQ,MAAK;OAAS,WAAU;OAAqB;OAAU,SAAS;OAAU,UAAA,EAAE,OAAO;MAAU,CAAA,CACjG;KACH,CAAA,CAAA;IACL,CAAA,GAAA,iBAAA,GAAA,kBAAA,KAAA,CAAC,SAAD;KAAO,WAAU;KAAjB,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;MACM;MACJ,WAAU;MACV,MAAK;MACL,MAAK;MACI;MACC;MACV,WAAW,UAAU,OAAO,MAAM,OAAO,UAAU,SAAS,OAAO;KACpE,CAAA,GACD,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,WAAU;MAAW,UAAA;KAAW,CAAA,CACjC;IACJ,CAAA,CAAA;;EAET;;EAGA,SAAS,eAAe,EACtB,OACA,MACA,OACA,UACA,YACA,iBACA,mBACA,UAUC;GACD,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;MAAK,WAAU;MAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;OAAO,WAAU;OAAW,SAAQ;OAAc,UAAA;MAAa,CAAA,GAC/D,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;OAAM,WAAU;OACd,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;QAAM,WAAW,aAAa,aAAa;QACxC,UAAA,aAAa,kBAAkB;OAC5B,CAAA;MACF,CAAA,CACH;;KACL,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;MACE,IAAG;MACH,WAAU;MACV,MAAK;MACL,cAAa;MACb,OAAO,MAAM;MACH;MACV,WAAW,UAAU,OAAO,MAAM,OAAO,KAAK;KAC/C,CAAA;KACD,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;MAAG,WAAU;MAAW,UAAA;KAAQ,CAAA;IAC7B;;EAET;;EAGA,SAAS,UAAU,EAAE,OAAO,OAAO,OAAmE;GACpG,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,WAAU;MAAqB,UAAA;KAAY,CAAA;KACjD,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,WAAU;MAAqB,UAAA;KAAY,CAAA;KAChD,QAAQ,KAAA,KAAa,QAAQ,KAAK,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,WAAU;MAAmB,UAAA;KAAU,CAAA,IAAI;IACjF;;EAET;;EAGA,SAAS,YAAY,EACnB,OACA,OAAO,EAAE,MAAM,KAAK,UAAU,WAC9B,KAKC;GACD,MAAM,QAAQ,YAAY,MAAM,GAAG;GACnC,MAAM,QAAQ,cAAc,OAAO;GACnC,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;MAAK,WAAU;MAAf,UAAA;OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;QAAM,WAAU;QAAuB,UAAA;OAAY,CAAA;OAClD,WAAW,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;QAAM,WAAU;QAAoB,UAAA,EAAE,eAAe;OAAQ,CAAA,IAAI;OAC7E,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;QAAM,WAAU;QAAuB,UAAA,MAAM,IAAI,GAAG,YAAY,IAAI,EAAE,KAAK,YAAY,GAAG,MAAM,YAAY,IAAI;OAAQ,CAAA;MACrH;;KACL,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,WAAU;MAAc,MAAK;MAAc,iBAAe;MAAG,iBAAe;MAAK,iBAAe,KAAK,MAAM,QAAQ,GAAG;MACzH,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;OAAK,WAAW,WAAW,wCAAwC;OAAmB,OAAO,EAAE,OAAO,GAAG,QAAQ,IAAI,GAAG;MAAI,CAAA;KACzH,CAAA;KACJ,UAAU,KAAK,iBAAA,GAAA,kBAAA,KAAA,CAAC,KAAD;MAAG,WAAU;MAAb,UAAA;OAAoC,EAAE,YAAY;OAAE;OAAE;MAAS;KAAI,CAAA,IAAA;IAChF;;EAET;;;;;;;EAQA,SAAS,UAAU,EAAE,GAAG,OAAO,kBAAkB,aAK9C;GAGD,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,IAAI,oBAAoB,MAAM,WAAW,QAAQ,UAAU;GAC7D,GAAG;IAAC;IAAkB,MAAM;IAAQ;GAAS,CAAC;GAE9C,MAAM,UAAU,MAAM,WAAW;GACjC,MAAM,SAAS,MAAM;GACrB,MAAM,UAAU,QAAQ;GACxB,MAAM,cAAc,YAAY,KAAA,IAAY,KAAK,QAAQ,YAAY,QAAQ;GAC7E,MAAM,UAAU,QAAQ;GACxB,MAAM,OAAO,QAAQ;GACrB,MAAM,WAAW,MAAM,QAAQ;GAC/B,MAAM,aAAa,SAAS,KAAA,KAAa,KAAK,WAAW,MAAM,KAAK,WAAW,WAAW,KAAK,SAAS;GAExG,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;IAAK,WAAU;IAAe,cAAY,EAAE,YAAY;IAAxD,UAAA;KACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;MAAK,WAAU;MAAf,UAAA;OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD;QAAI,WAAU;QAAiB,UAAA,EAAE,YAAY;OAAM,CAAA;OAClD,gBAAgB,KAAK,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;QAAM,WAAU;QAAmB,UAAA;OAAkB,CAAA,IAAI;OAC9E,aAAa,KAAK,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;QAAM,WAAU;QAAgB,UAAA;OAAe,CAAA,IAAI;OACrE,eAAe,KAAK,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;QAAM,WAAU;QAAsB,UAAA;OAAiB,CAAA,IAAI;OAChF,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;QAAQ,MAAK;QAAS,WAAU;QAAkB,UAAU,WAAW,CAAC;QAAkB,SAAS;QAChG,UAAA,UAAU,EAAE,iBAAiB,IAAI,EAAE,cAAc;OAC5C,CAAA;MACL;;KAEJ,CAAC,mBAAmB,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;MAAG,WAAU;MAAgB,UAAA,EAAE,YAAY;KAAK,CAAA,IAAI;KACxE,oBAAoB,WAAW,KAAA,KAAa,UAAU,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;MAAG,WAAU;MAAgB,UAAA,EAAE,cAAc;KAAK,CAAA,IAAI;KAC5G,MAAM,WAAW,UAChB,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;MAAG,WAAU;MAAgB,MAAK;MAChC,UAAA,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD,EAAA,UAAA,CAAO,EAAE,YAAY,GAAG,MAAM,UAAU,KAAA,KAAa,MAAM,UAAU,KAAK,MAAM,MAAM,UAAU,EAAS,EAAA,CAAA;KACxG,CAAA,IACD;KAEH,QAAQ,UAAU,KAAA,IACjB,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;MAAK,WAAU;MAAf,UAAA;OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,WAAD;QACE,OAAO,EAAE,eAAe;QACxB,OAAO,OAAO,OAAO,MAAM,cAAc;QACzC,KAAK,GAAG,EAAE,aAAa,EAAE,GAAG,OAAO,MAAM;OAC1C,CAAA;OACD,iBAAA,GAAA,kBAAA,IAAA,CAAC,WAAD;QAAW,OAAO,EAAE,kBAAkB;QAAG,OAAO,GAAG,OAAO,MAAM,YAAY;OAAK,CAAA;OACjF,iBAAA,GAAA,kBAAA,IAAA,CAAC,WAAD;QACE,OAAO,EAAE,WAAW;QACpB,OAAO,iBAAiB,OAAO,MAAM,SAAS;QAC9C,KAAK,GAAG,YAAY,OAAO,MAAM,YAAY,EAAE;OAChD,CAAA;OACD,iBAAA,GAAA,kBAAA,IAAA,CAAC,WAAD;QACE,OAAO,EAAE,aAAa;QACtB,OAAO,oBAAoB,OAAO,MAAM,gBAAgB,OAAO,MAAM,cAAc;QACnF,KAAK,GAAG,oBAAoB,OAAO,MAAM,aAAa,EAAE,GAAG,EAAE,eAAe,EAAE,KAAK,oBAAoB,OAAO,MAAM,cAAc,EAAE,GAAG,EAAE,gBAAgB;OAC1J,CAAA;MACE;KACH,CAAA,IAAA;KAEH,YAAY,KAAA,IACX,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;MAAK,WAAU;MAAf,UAAA;OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,WAAD;QAAW,OAAO,EAAE,cAAc;QAAG,OAAO,YAAY,QAAQ,cAAc;OAAI,CAAA;OAClF,iBAAA,GAAA,kBAAA,IAAA,CAAC,WAAD;QAAW,OAAO,EAAE,gBAAgB;QAAG,OAAO,YAAY,QAAQ,gBAAgB;OAAI,CAAA;OACtF,iBAAA,GAAA,kBAAA,IAAA,CAAC,WAAD;QAAW,OAAO,EAAE,WAAW;QAAG,OAAO,YAAY,QAAQ,WAAW;OAAI,CAAA;MACzE;KACH,CAAA,IAAA;KAEH,YAAY,KAAA,IACX,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;MAAK,WAAU;MAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,aAAD;OAAa,OAAO,EAAE,eAAe;OAAG,OAAO,QAAQ;OAAa;MAAI,CAAA,GACxE,iBAAA,GAAA,kBAAA,IAAA,CAAC,aAAD;OAAa,OAAO,EAAE,aAAa;OAAG,OAAO,QAAQ;OAAW;MAAI,CAAA,CACjE;KACH,CAAA,IAAA;KAEH,WAAW,KAAA,IACV,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;MAAK,WAAU;MAAf,UAAA;OACG,SAAS,KAAA,KAAa,KAAK,mBAAmB,IAC7C,iBAAA,GAAA,kBAAA,KAAA,CAAC,KAAD;QAAG,WAAU;QAAb,UAAA;SAAgC,EAAE,gBAAgB;SAAE;SAAE,IAAI,KAAK,KAAK,gBAAgB,CAAC,CAAC,mBAAmB;QAAK;OAC5G,CAAA,IAAA;OACH,MAAM,cAAc,KAAA,IACnB,iBAAA,GAAA,kBAAA,KAAA,CAAC,KAAD;QAAG,WAAU;QAAb,UAAA;SAAgC,EAAE,cAAc;SAAE;SAAE,IAAI,KAAK,MAAM,SAAS,CAAC,CAAC,mBAAmB;QAAK;OACpG,CAAA,IAAA;OACJ,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAM,WAAU,qBAAsB,CAAA;OACrC,OAAO,SAAS,SAAS,IAAI,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;QAAG,WAAU;QAAkB,OAAO,OAAO,SAAS,KAAK,IAAI;QAAI,UAAA,EAAE,cAAc;OAAK,CAAA,IAAI;MACvH;KACH,CAAA,IAAA;IACD;;EAET;;EAGA,SAAgB,wBAAwB,OAAiC;GACvE,MAAM,EAAE,MAAM;GACd,MAAM,QAAQ,MAAM,wBAAwB,aAAa,QAAQ;GACjE,MAAM,QAAQ,MAAM,qBAAqB,aAAa,QAAQ;GAC9D,MAAM,WAAW,CAAC,MAAM;GACxB,MAAM,YAAY,CAAC,MAAM;GACzB,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,WAAD;IAAS,WAAU;IAAa,cAAY,EAAE,OAAO;IAArD,UAAA;KACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD;MAAI,WAAU;MAAY,UAAA,EAAE,OAAO;KAAM,CAAA;KACzC,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;MAAG,WAAU;MAAY,UAAA,EAAE,OAAO;KAAK,CAAA;KACtC,CAAC,MAAM,WAAW,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;MAAG,WAAU;MAAc,MAAK;MAAU,UAAA,EAAE,UAAU;KAAK,CAAA,IAAI;KAClF,iBAAA,GAAA,kBAAA,IAAA,CAAC,WAAD;MACK;MACI;MACP,kBAAkB,MAAM;MACxB,WAAW,MAAM;KAClB,CAAA;KACD,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;MAAK,WAAU;MAAf,UAAA;OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,gBAAD;QACE,OAAO,EAAE,QAAQ;QACjB,MAAM,YAAY,EAAE,cAAc,IAAI,EAAE,YAAY;QACpD,OAAO,MAAM;QACb,UAAU,YAAY;QACtB,YAAY,MAAM;QAClB,iBAAiB,EAAE,WAAW;QAC9B,mBAAmB,EAAE,aAAa;QAClC,SAAS,SAAS,MAAM,KAAK,UAAU,IAAI;OAC5C,CAAA;OACD,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;QACE,IAAG;QACH,OAAO,EAAE,SAAS;QAClB,MAAM,EAAE,aAAa;QACrB,OAAO,MAAM;QACH;QACV,SAAS,SAAS,MAAM,KAAK,WAAW,IAAI;QAC5C,eAAe,MAAM,WAAW,SAAS;QACtC;OACJ,CAAA;OACD,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;QACE,IAAG;QACH,OAAO,EAAE,YAAY;QACrB,MAAM,EAAE,gBAAgB;QACxB,OAAO,MAAM;QACH;QACV,aAAa,MAAM;QACnB,SAAS,SAAS,MAAM,KAAK,cAAc,IAAI;QAC/C,eAAe,MAAM,WAAW,YAAY;QACzC;OACJ,CAAA;OACD,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;QACE,IAAG;QACH,OAAO,EAAE,kBAAkB;QAC3B,MAAM,EAAE,sBAAsB;QAC9B,OAAO,MAAM;QACH;QACV,SAAA;QACA,SAAS,SAAS,MAAM,KAAK,oBAAoB,IAAI;QACrD,eAAe,MAAM,WAAW,kBAAkB;QAC/C;OACJ,CAAA;OACD,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;QACE,IAAG;QACH,OAAO,EAAE,qBAAqB;QAC9B,MAAM,EAAE,yBAAyB;QACjC,OAAO,MAAM;QACH;QACV,SAAA;QACA,SAAS,SAAS,MAAM,KAAK,uBAAuB,IAAI;QACxD,eAAe,MAAM,WAAW,qBAAqB;QAClD;OACJ,CAAA;OACD,iBAAA,GAAA,kBAAA,IAAA,CAAC,aAAD;QACE,IAAG;QACH,OAAO,EAAE,oBAAoB;QAC7B,MAAM,EAAE,wBAAwB;QAChC,OAAO,MAAM;QACH;QACV,gBAAA;QACA,SAAS,SAAS,MAAM,KAAK,sBAAsB,IAAI;QACvD,eAAe,MAAM,WAAW,oBAAoB;QACjD;OACJ,CAAA;MACE;;KACL,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;MAAK,WAAU;MAAf,UAAA;OACG,MAAM,SAAS,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;QAAG,WAAU;QAAY,MAAK;QAAU,UAAA,EAAE,YAAY;OAAK,CAAA,IAAI;OAC/E,iBAAA,GAAA,kBAAA,IAAA,CAACC,sCAAAA,QAAD;QAAQ,SAAQ;QAAQ,MAAK;QAAK,UAAU,CAAC,MAAM,SAAS,MAAM;QAAQ,SAAS,MAAM;QACtF,UAAA,EAAE,SAAS;OACN,CAAA;OACR,iBAAA,GAAA,kBAAA,IAAA,CAACA,sCAAAA,QAAD;QACE,SAAQ;QACR,MAAK;QACL,UAAU,CAAC,MAAM,SAAS,MAAM,WAAW,MAAM;QACjD,SAAS,MAAM;QAEd,UAAA,EAAE,MAAM,SAAS,WAAW,MAAM;OAC7B,CAAA;MACL;;IACE;;EAEb;;;EC9VA,MAAa,KAA6C;GACxD,KAAK;GACL,OAAO;GACP,OACE;GAEF,QAAQ;GACR,YAAY;GACZ,WAAW;GACX,aAAa;GACb,cAAc;GACd,SAAS;GACT,aAAa;GACb,YAAY;GACZ,gBAAgB;GAChB,kBAAkB;GAClB,sBAAsB;GACtB,qBAAqB;GACrB,yBAAyB;GACzB,oBAAoB;GACpB,wBAAwB;GACxB,YAAY;GACZ,OAAO;GACP,eAAe;GACf,UAAU;GACV,SAAS;GACT,MAAM;GACN,QAAQ;GACR,YAAY;GACZ,SAAS;GACT,QAAQ;GACR,YAAY;GACZ,cAAc;GACd,iBAAiB;GACjB,cAAc;GACd,YAAY;GACZ,YAAY;GACZ,eAAe;GACf,aAAa;GACb,kBAAkB;GAClB,WAAW;GACX,aAAa;GACb,eAAe;GACf,gBAAgB;GAChB,cAAc;GACd,gBAAgB;GAChB,WAAW;GACX,eAAe;GACf,aAAa;GACb,eAAe;GACf,YAAY;GACZ,cAAc;GACd,cAAc;GACd,gBAAgB;EAClB;EAEA,MAAa,KAA6C;GACxD,KAAK;GACL,OAAO;GACP,OACE;GAGF,QAAQ;GACR,YAAY;GAEZ,WAAW;GACX,aAAa;GACb,cAAc;GACd,SAAS;GACT,aAAa;GACb,YAAY;GACZ,gBAAgB;GAEhB,kBAAkB;GAClB,sBAAsB;GACtB,qBAAqB;GACrB,yBAAyB;GAEzB,oBAAoB;GACpB,wBAAwB;GAExB,YAAY;GACZ,OAAO;GACP,eAAe;GACf,UAAU;GACV,SAAS;GACT,MAAM;GACN,QAAQ;GACR,YAAY;GACZ,SAAS;GACT,QAAQ;GACR,YAAY;GACZ,cAAc;GACd,iBAAiB;GACjB,cAAc;GACd,YAAY;GACZ,YAAY;GACZ,eAAe;GACf,aAAa;GACb,kBAAkB;GAClB,WAAW;GACX,aAAa;GACb,eAAe;GACf,gBAAgB;GAChB,cAAc;GACd,gBAAgB;GAChB,WAAW;GACX,eAAe;GACf,aAAa;GACb,eAAe;GACf,YAAY;GACZ,cAAc;GACd,cAAc;GACd,gBAAgB;EAClB;;;;EC5IA,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgEjB,SAAS,gBAAsB;GAC7B,IAAI,OAAO,aAAa,aAAa;GACrC,MAAM,KAAK;GACX,IAAI,SAAS,cAAc,0BAA0B,GAAG,GAAG,MAAM,MAAM;GACvE,MAAM,MAAM,SAAS,cAAc,OAAO;GAC1C,IAAI,QAAQ,SAAS;GACrB,IAAI,QAAQ,YAAY;GACxB,IAAI,cAAc;GAClB,SAAS,KAAK,YAAY,GAAG;EAC/B;;;;;;;EAQA,SAAgB,MAAM,KAAoB;GACxC,cAAc;GAGd,MAAM,aAAa,IAAI,IAAI,YAAY;GACvC,IAAI,eAAe,KAAA,GACjB,0BAA0B,UAAU;GAOtC,IAAI,aAAa,IAAI,OAAO,SAAS,wBAAwB;IAAE;IAAI;GAAG,CAAC,GAAG,qCAAqC;GAE/G,MAAM,MAAM,IAAI,IAAI,YAAY,CAAC,CAAC;GAClC,MAAM,kBAAkB,IAAI,IAAI,YAAY,CAAC,CAAC;GAE9C,MAAM,aAAa,IAAI,8BADT,IAAI,cAAc,KAA8B,EAAE,WAAW,eAAe,CACrC,GAAO,EAAE,aAAa,IAAI,YAAY,GAAG,eAAe;GAC7G,IAAI,mBAAmB,WAAW,QAAQ,GAAG,+CAA+C;GAC5F,MAAM,SAAA,GAAQC,uCAAAA,oBAAAA,CAAuC,WAAW,MAAM,CAAC;GACvE,WAAW,gBAAgB,MAAM,IAAI,WAAW,MAAM,CAAC,CAAC;GASxD,IAAI;GACJ,IAAI;GACJ,IAAI,aAAa;IACf,IAAI,YAAY;IAChB,IAAI;IACJ,IAAS,OAAO,OAAO,yBAAyB,CAAC,CAAC,MAAM,YAAY;KAClE,IAAI,WAAW;MACb,QAAa;MACb;KACF;KACA,UAAU;KACV,IAAI,OAAO,CAAC,oBAAoB,IAAI,iBAAiB;MACnD,iBAAiB,aAAa,OAAO;MACrC,aAAa,mBAAmB;OAC9B,iBAAiB,KAAA;MACnB,GAAG,2CAA2C;KAChD,CAAC;IACH,IAAI,UAAmB;KAGrB,kBAAkB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;IACzE,CAAC;IACD,aAAa;KACX,YAAY;KACZ,iBAAiB,KAAA;KACjB,IAAI,YAAY,KAAA,GAAW,QAAa;IAC1C;GACF,GAAG,wCAAwC;GAU3C,MAAM,kBAAkB,IAAI,2BAA2B,EARrD,QAAQ,YAAY;IAClB,MAAM,YAAY;IAClB,IAAI,cAAc,KAAA,GAChB,OAAO;KAAE,IAAI;KAAO,OAAO,EAAE,SAAS,mBAAmB,2CAA2C;IAAE;IAExG,OAAO,UAAU,OAAO;GAC1B,EAE+D,CAAC;GAClE,IAAI,mBAAmB,gBAAgB,QAAQ,GAAG,4CAA4C;GAC9F,MAAM,cAAA,GAAaA,uCAAAA,oBAAAA,CAAoC,gBAAgB,MAAM,CAAC;GAC9E,gBAAgB,gBAAgB,WAAW,IAAI,gBAAgB,MAAM,CAAC,CAAC;GAEvE,MAAM,kBAAkB;IACtB,OAAO;KAAE,qBAAqB;KAAO,kBAAkB;IAAW;IAClE,OAAO,OAAe,SAAiB,WAAW,KAAK,OAAO,IAAI;IAClE,aAAa,UAAkB,WAAW,WAAW,KAAK;IAG1D,YAAY,KAAK,WAAW,KAAK,CAAC,CAAC,WAAW;KAC5C,MAAM,UAAU,WAAW,MAAM;KACjC,IAAI,CAAC,QAAQ,UAAU,QAAQ,kBAAkB,gBAAqB,QAAQ;IAChF,CAAC;IACD,eAAe,WAAW,QAAQ;IAClC,oBAAoB,KAAK,gBAAgB,QAAQ;GACnD;GAEA,IAAI,MAAM,OAAO,0BAA0B,IAAI,MAAM,SAAS;IAC5D,MAAM;IACN,IAAI;IACJ,OAAO;IACP,aAAa,IAAI,OAAO,KAAK,sBAAsB,CAAC,CAAC,KAAK;IAC1D,QAAQ;IACR,QAAQ;GACV,GAAG,uBAAuB,CAAC;EAC7B;EAEA,MAAa,SAA4B;GACvC;GACA;GACA;GACA;GACA;EACF"}
|
package/lib/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import z from "@deepseek-ai/schemastery";
|
|
2
2
|
import { GenerateOptions, LlmAdapter, LlmModelInfo, LlmResolvedModelInfo, ResolvedRetryPolicy, StreamChunk } from "@deepseek-ai/dsh-llm";
|
|
3
3
|
import { CredentialRef } from "@deepseek-ai/dsh-credentials";
|
|
4
|
+
import { TypertRemoteService, TypertSchema } from "@deepseek-ai/dsh-typert-protocol";
|
|
4
5
|
import { Context } from "@deepseek-ai/cordis";
|
|
5
6
|
import { AttachmentStore } from "@deepseek-ai/dsh-attachment";
|
|
6
7
|
import { CommandDefinition } from "@deepseek-ai/dsh-commands";
|
|
@@ -70,6 +71,56 @@ declare function compareByPlan(a: {
|
|
|
70
71
|
id: string;
|
|
71
72
|
name: string;
|
|
72
73
|
}): number;
|
|
74
|
+
/**
|
|
75
|
+
* Subscription plan table, synced from the official CLI bundle's plan maps
|
|
76
|
+
* (`Nn`/`$n` in command-code@1.26.0 `dist/cli.mjs`): subscription `planId`
|
|
77
|
+
* prefix → display name and the plan's monthly credit total. This is the
|
|
78
|
+
* account's own subscription (from `/alpha/billing/subscriptions`) — distinct
|
|
79
|
+
* from {@link KNOWN_PLANS}, which maps catalog models to their minimum tier.
|
|
80
|
+
*
|
|
81
|
+
* `tierWeight` is plugin-added (not from the CLI maps): the plan's rank on
|
|
82
|
+
* the {@link PLAN_ORDER} scale, used by the picker's plan filter
|
|
83
|
+
* ({@link modelVisibleInPlan}) to hide models above the account's tier.
|
|
84
|
+
*/
|
|
85
|
+
declare const KNOWN_SUBSCRIPTION_PLANS: Readonly<Record<string, {
|
|
86
|
+
name: string;
|
|
87
|
+
monthlyCredits: number;
|
|
88
|
+
tierWeight: number;
|
|
89
|
+
}>>;
|
|
90
|
+
/**
|
|
91
|
+
* Resolve a subscription `planId` (e.g. `individual-pro-v1`) to its display
|
|
92
|
+
* name and monthly credit total, mirroring the CLI's `getPlanInfo`:
|
|
93
|
+
* normalize (lowercase, `_` → `-`), then longest-prefix match so
|
|
94
|
+
* `individual-pro-v1` wins over `individual-pro`. Unknown ids return
|
|
95
|
+
* `undefined`.
|
|
96
|
+
*/
|
|
97
|
+
declare function subscriptionPlanInfo(planId: string): {
|
|
98
|
+
name: string;
|
|
99
|
+
monthlyCredits: number;
|
|
100
|
+
tierWeight: number;
|
|
101
|
+
} | undefined;
|
|
102
|
+
/**
|
|
103
|
+
* The billing facts the picker's plan filter needs, fetched by mirroring the
|
|
104
|
+
* CLI's `createBilling` flow (whoami → orgId, then `/alpha/billing/subscriptions`
|
|
105
|
+
* for the plan id and `/alpha/billing/credits` for the on-demand balances).
|
|
106
|
+
*/
|
|
107
|
+
interface CommandCodeBillingAccess {
|
|
108
|
+
/** Account plan tier weight on the {@link PLAN_ORDER} scale; undefined when the plan is unknown. */
|
|
109
|
+
tierWeight: number | undefined;
|
|
110
|
+
/**
|
|
111
|
+
* Purchased + free on-demand credit balance. The official access model
|
|
112
|
+
* (`evaluateModelAccess` in the CLI) allows every model when the account
|
|
113
|
+
* holds any on-demand credits — the plan gate only applies at zero balance.
|
|
114
|
+
*/
|
|
115
|
+
onDemandCredits: number;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Whether the picker lists `modelId` for an account with the given billing
|
|
119
|
+
* access. Fails open at every uncertainty: no billing data, an unknown plan,
|
|
120
|
+
* or a model outside {@link KNOWN_PLANS} all keep the model visible — the
|
|
121
|
+
* server remains the final gate (`403 MODEL_NOT_IN_PLAN`).
|
|
122
|
+
*/
|
|
123
|
+
declare function modelVisibleInPlan(modelId: string, access: CommandCodeBillingAccess | undefined): boolean;
|
|
73
124
|
/**
|
|
74
125
|
* Active pricing deals per the official pricing page
|
|
75
126
|
* (`/docs/resources/pricing-limits#deals`). Each entry records the model's
|
|
@@ -125,6 +176,8 @@ declare const COMMAND_CODE_CLI_VERSION = "1.26.0";
|
|
|
125
176
|
declare const DEFAULT_API_BASE = "https://api.commandcode.ai";
|
|
126
177
|
declare const DEFAULT_GENERATE_MAX_TOKENS = 64000;
|
|
127
178
|
declare const DEFAULT_MAX_OUTPUT_TOKENS = 65536;
|
|
179
|
+
/** How long the picker's plan-filter billing facts stay cached before refetching. */
|
|
180
|
+
declare const BILLING_ACCESS_TTL_MS: number;
|
|
128
181
|
/** Head-of-request timeout: how long to wait for the first response byte. */
|
|
129
182
|
declare const DEFAULT_REQUEST_TIMEOUT_MS = 60000;
|
|
130
183
|
/** Stream idle timeout: a generation that stalls this long is a dead connection. */
|
|
@@ -175,6 +228,13 @@ interface CommandCodeConnectionOptions {
|
|
|
175
228
|
requestTimeoutMs: number;
|
|
176
229
|
/** Milliseconds a stream may stall before it is treated as a dead connection (default 300s). */
|
|
177
230
|
streamIdleTimeoutMs: number;
|
|
231
|
+
/**
|
|
232
|
+
* Whether the picker hides models above the account's subscription tier
|
|
233
|
+
* (default true). The filter fails open: unknown plan, billing-endpoint
|
|
234
|
+
* failure, a positive on-demand credit balance, or an unmapped model all
|
|
235
|
+
* keep the full catalog visible. Set false to always list every model.
|
|
236
|
+
*/
|
|
237
|
+
filterModelsByPlan?: boolean;
|
|
178
238
|
}
|
|
179
239
|
/**
|
|
180
240
|
* Resolve the durable attachment service, or undefined when the host does not
|
|
@@ -231,11 +291,25 @@ interface CommandCodeCredits {
|
|
|
231
291
|
resetAt: number;
|
|
232
292
|
};
|
|
233
293
|
}
|
|
294
|
+
/** Subscription plan state from `/alpha/billing/subscriptions`. */
|
|
295
|
+
interface CommandCodePlan {
|
|
296
|
+
/** Raw subscription plan id (e.g. `individual-pro`); empty when unreported. */
|
|
297
|
+
planId: string;
|
|
298
|
+
/** Display name (e.g. `Pro`); falls back to the raw id for unknown plans. */
|
|
299
|
+
name: string;
|
|
300
|
+
/** Raw subscription status (`active`, `trialing`, `past_due`, …); empty when unreported. */
|
|
301
|
+
status: string;
|
|
302
|
+
/** The plan's monthly credit total per {@link KNOWN_SUBSCRIPTION_PLANS}; null for unknown plans. */
|
|
303
|
+
monthlyCredits: number | null;
|
|
304
|
+
/** Billing period end in millis; 0 when the endpoint did not report one. */
|
|
305
|
+
currentPeriodEnd: number;
|
|
306
|
+
}
|
|
234
307
|
/** Everything the usage endpoints report, fetched together. */
|
|
235
308
|
interface CommandCodeUsageReport {
|
|
236
309
|
account?: CommandCodeAccount;
|
|
237
310
|
usage?: CommandCodeUsage;
|
|
238
311
|
credits?: CommandCodeCredits;
|
|
312
|
+
plan?: CommandCodePlan;
|
|
239
313
|
/** Endpoint failures degrade the report instead of failing it. */
|
|
240
314
|
failures: string[];
|
|
241
315
|
}
|
|
@@ -244,6 +318,8 @@ declare class CommandCodeAdapter<C extends CommandCodeConnectionOptions = Comman
|
|
|
244
318
|
private catalog;
|
|
245
319
|
private readonly fetchImpl;
|
|
246
320
|
private readonly resolveAttachments;
|
|
321
|
+
private billingAccess;
|
|
322
|
+
private billingAccessInflight;
|
|
247
323
|
constructor(deps: CommandCodeAdapterDeps<C>);
|
|
248
324
|
/**
|
|
249
325
|
* Command Code is a metered subscription API: 429 (rate limit) and 5xx
|
|
@@ -258,9 +334,28 @@ declare class CommandCodeAdapter<C extends CommandCodeConnectionOptions = Comman
|
|
|
258
334
|
private loadCatalog;
|
|
259
335
|
listModels(provider: string): Promise<readonly LlmModelInfo[]>;
|
|
260
336
|
resolveModel(provider: string, model: string, signal?: AbortSignal): Promise<LlmResolvedModelInfo>;
|
|
337
|
+
/** The headers every authenticated account endpoint shares. */
|
|
338
|
+
private accountHeaders;
|
|
261
339
|
/**
|
|
262
|
-
*
|
|
263
|
-
*
|
|
340
|
+
* The billing facts behind the picker's plan filter, cached for
|
|
341
|
+
* {@link BILLING_ACCESS_TTL_MS} and shared across concurrent callers.
|
|
342
|
+
* `undefined` means "unknown — show everything" (fail-open).
|
|
343
|
+
*/
|
|
344
|
+
private loadBillingAccess;
|
|
345
|
+
/**
|
|
346
|
+
* The billing facts behind the picker's plan filter, mirroring the CLI's
|
|
347
|
+
* `createBilling` flow: whoami yields the org id, then the subscriptions
|
|
348
|
+
* and credits endpoints answer in parallel. The plan id is honored only
|
|
349
|
+
* when the subscription reports an active-ish status (the CLI's rule); when
|
|
350
|
+
* the subscriptions endpoint fails entirely, `credits.planId` is the
|
|
351
|
+
* fallback (the CLI stamps plan identity from it too). Any failure resolves
|
|
352
|
+
* to `undefined` (fail-open) rather than breaking the picker.
|
|
353
|
+
*/
|
|
354
|
+
private fetchBillingAccess;
|
|
355
|
+
/**
|
|
356
|
+
* Fetch account, usage, credit, and subscription state from the Command
|
|
357
|
+
* Code account endpoints (`/alpha/whoami`, `/alpha/usage/summary`,
|
|
358
|
+
* `/alpha/billing/credits`, `/alpha/billing/subscriptions`).
|
|
264
359
|
* Each endpoint degrades independently: a failed one lands in `failures`
|
|
265
360
|
* while the rest still report, so a transient outage never blanks the whole
|
|
266
361
|
* view. Requires a usable API key (throws `MISSING_CREDENTIAL` otherwise).
|
|
@@ -280,6 +375,47 @@ declare function commandDefinition<C extends CommandCodeConnectionOptions>(deps:
|
|
|
280
375
|
/** Register the command on `ctx.commands` (called from the plugin entry). */
|
|
281
376
|
declare function applyCommands<C extends CommandCodeConnectionOptions>(ctx: Context, deps: CommandCodeCommandDeps<C>): void;
|
|
282
377
|
//#endregion
|
|
378
|
+
//#region src/usage-remote.d.ts
|
|
379
|
+
/** Everything the usage service needs beyond its Cordis context. */
|
|
380
|
+
interface CommandCodeUsageDeps<C extends CommandCodeConnectionOptions = CommandCodeConnectionOptions> {
|
|
381
|
+
/** The registered adapter (for getUsage). */
|
|
382
|
+
adapter: CommandCodeAdapter<C>;
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* The Remote receiver: a Cordis service the Gateway resolves by key
|
|
386
|
+
* (`commandcodeUsage`) and binds to the wire namespace (`commandcode`). The
|
|
387
|
+
* base class stamps the `typertRemote` binding the Gateway validates on every
|
|
388
|
+
* dispatch; no decorators are needed because the descriptor is registered
|
|
389
|
+
* explicitly (strict path) rather than discovered from source markers.
|
|
390
|
+
*/
|
|
391
|
+
declare class CommandCodeUsageService<C extends CommandCodeConnectionOptions = CommandCodeConnectionOptions> extends TypertRemoteService {
|
|
392
|
+
private readonly deps;
|
|
393
|
+
constructor(ctx: Context, deps: CommandCodeUsageDeps<C>);
|
|
394
|
+
/**
|
|
395
|
+
* Account, usage, and credit state for the settings page's account card.
|
|
396
|
+
* Degrades per endpoint like the `/commandcode` command (failures land in
|
|
397
|
+
* `report.failures`); throws `MISSING_CREDENTIAL` when no key resolves, which
|
|
398
|
+
* the Gateway folds into the failure branch the page renders as a hint.
|
|
399
|
+
*/
|
|
400
|
+
report(): Promise<CommandCodeUsageReport>;
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Provide the usage service and register its Remote descriptor. The registry
|
|
404
|
+
* contribution is tied to this fiber's lifetime: the registry's own
|
|
405
|
+
* `register()` effect would otherwise outlive the plugin.
|
|
406
|
+
*/
|
|
407
|
+
declare function applyUsageRemote<C extends CommandCodeConnectionOptions>(ctx: Context, deps: CommandCodeUsageDeps<C>): void;
|
|
408
|
+
//#endregion
|
|
409
|
+
//#region src/usage-wire.d.ts
|
|
410
|
+
/** Canonical `<namespace>/<method>` endpoint of the usage report Remote. */
|
|
411
|
+
declare const USAGE_REPORT_ENDPOINT = "commandcode/report";
|
|
412
|
+
/**
|
|
413
|
+
* The strict result codec both halves attach to the descriptor. Hand-rolled:
|
|
414
|
+
* the client bundle may not require a schema library, and `TypertSchema` is
|
|
415
|
+
* deliberately minimal so one `parse` function satisfies it.
|
|
416
|
+
*/
|
|
417
|
+
declare const usageReportSchema: TypertSchema<CommandCodeUsageReport>;
|
|
418
|
+
//#endregion
|
|
283
419
|
//#region src/index.d.ts
|
|
284
420
|
declare const name = "llm-commandcode";
|
|
285
421
|
declare const inject: string[];
|
|
@@ -309,6 +445,13 @@ interface Config {
|
|
|
309
445
|
requestTimeoutMs?: number;
|
|
310
446
|
/** Milliseconds a stream may stall before being treated as a dead connection; defaults to 300s. */
|
|
311
447
|
streamIdleTimeoutMs?: number;
|
|
448
|
+
/**
|
|
449
|
+
* Whether the model picker hides models above the account's subscription
|
|
450
|
+
* tier; defaults to true. The filter fails open (unknown plan, billing
|
|
451
|
+
* endpoint failure, or a positive on-demand credit balance all keep the
|
|
452
|
+
* full catalog visible). Set false to always list every model.
|
|
453
|
+
*/
|
|
454
|
+
filterModelsByPlan?: boolean;
|
|
312
455
|
}
|
|
313
456
|
declare const Config: z<Config>;
|
|
314
457
|
/** One resolution's complete request facts: connection plus credential reference. */
|
|
@@ -324,5 +467,5 @@ interface ResolvedCommandCodeOptions extends CommandCodeConnectionOptions {
|
|
|
324
467
|
declare function resolveAdapterOptions(config: Config): ResolvedCommandCodeOptions;
|
|
325
468
|
declare function apply(ctx: Context, config: Config): void;
|
|
326
469
|
//#endregion
|
|
327
|
-
export { COMMAND_CODE_CLI_VERSION, CommandCodeAdapter, type CommandCodeAdapterDeps, type CommandCodeCommandDeps, type CommandCodeConnectionOptions, type CommandCodeUsageReport, Config, DEFAULT_API_BASE, DEFAULT_GENERATE_MAX_TOKENS, DEFAULT_MAX_OUTPUT_TOKENS, DEFAULT_MODELS_CACHE_PATH, DEFAULT_REQUEST_TIMEOUT_MS, DEFAULT_STREAM_IDLE_TIMEOUT_MS, KNOWN_DEALS, KNOWN_EFFORTS, KNOWN_IMAGE_MODELS, KNOWN_PEAK_PRICING, KNOWN_PLANS, KNOWN_THINKING_MODELS, PLAN_LABELS, PLAN_ORDER, PROVIDER, type ResolveAttachments, ResolvedCommandCodeOptions, apply, applyCommands, capabilityDescription, commandDefinition, compareByPlan, dealLabel, formatContext, inject, name, peakPricingLabel, peakPricingState, planLabel, projectSlugFromPath, resolveAdapterOptions, resolveAuthFileApiKey };
|
|
470
|
+
export { BILLING_ACCESS_TTL_MS, COMMAND_CODE_CLI_VERSION, CommandCodeAdapter, type CommandCodeAdapterDeps, type CommandCodeBillingAccess, type CommandCodeCommandDeps, type CommandCodeConnectionOptions, type CommandCodeUsageDeps, type CommandCodeUsageReport, CommandCodeUsageService, Config, DEFAULT_API_BASE, DEFAULT_GENERATE_MAX_TOKENS, DEFAULT_MAX_OUTPUT_TOKENS, DEFAULT_MODELS_CACHE_PATH, DEFAULT_REQUEST_TIMEOUT_MS, DEFAULT_STREAM_IDLE_TIMEOUT_MS, KNOWN_DEALS, KNOWN_EFFORTS, KNOWN_IMAGE_MODELS, KNOWN_PEAK_PRICING, KNOWN_PLANS, KNOWN_SUBSCRIPTION_PLANS, KNOWN_THINKING_MODELS, PLAN_LABELS, PLAN_ORDER, PROVIDER, type ResolveAttachments, ResolvedCommandCodeOptions, USAGE_REPORT_ENDPOINT, apply, applyCommands, applyUsageRemote, capabilityDescription, commandDefinition, compareByPlan, dealLabel, formatContext, inject, modelVisibleInPlan, name, peakPricingLabel, peakPricingState, planLabel, projectSlugFromPath, resolveAdapterOptions, resolveAuthFileApiKey, subscriptionPlanInfo, usageReportSchema };
|
|
328
471
|
//# sourceMappingURL=index.d.ts.map
|