@mars-sea/dsh-commandcode-provider 0.2.1 → 0.2.3
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 +21 -0
- package/LICENSE +1 -7
- package/NOTICE +10 -0
- package/README.md +57 -95
- package/README.zh-CN.md +57 -94
- package/assets/screenshots/model-picker.png +0 -0
- package/assets/screenshots/usage-dashboard.png +0 -0
- package/lib/client.js +666 -10
- package/lib/client.js.map +1 -1
- package/lib/index.d.ts +3 -3
- package/lib/index.js +26 -3
- package/lib/index.js.map +1 -1
- package/package.json +26 -4
package/lib/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":[],"sources":["../src/client/index.ts"],"sourcesContent":["/**\n * Browser half of the dsh-commandcode-provider bundle.\n *\n * The host rejects switching to a text-only model while the session already\n * contains images with a harness-level `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: this client plugin wraps 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\nimport type { Context } from '@deepseek-ai/cordis'\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/** The connection handle shape we read `api.sessions` from. */\ninterface ConnectionLike {\n api: { sessions: SessionsLike }\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/**\n * Client plugin body: install the selectModel wrapper on the connection's\n * shared api. `inject: ['connection']` gates activation until the connection\n * service is provided (the same pattern the harness's own client plugins\n * use), and `connection.api.sessions` is a stable object the model-selection\n * UI reads fresh on every call — so wrapping it once covers both the /model\n * popup and the composer seat, across reconnects.\n */\nexport function apply(ctx: Context): void {\n const connection = ctx.get('connection') as ConnectionLike | undefined\n if (connection === undefined) return\n connection.api.sessions = withFriendlyImageError(connection.api.sessions)\n}\n\nexport const inject: readonly string[] = ['connection']\n"],"mappings":";;;;;;;;EAqEA,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;;;;;;;;;EAUA,SAAgB,MAAM,KAAoB;GACxC,MAAM,aAAa,IAAI,IAAI,YAAY;GACvC,IAAI,eAAe,KAAA,GAAW;GAC9B,WAAW,IAAI,WAAW,uBAAuB,WAAW,IAAI,QAAQ;EAC1E;EAEA,MAAa,SAA4B,CAAC,YAAY"}
|
|
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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"}
|
package/lib/index.d.ts
CHANGED
|
@@ -102,7 +102,7 @@ declare const DEFAULT_MAX_OUTPUT_TOKENS = 65536;
|
|
|
102
102
|
/** Head-of-request timeout: how long to wait for the first response byte. */
|
|
103
103
|
declare const DEFAULT_REQUEST_TIMEOUT_MS = 60000;
|
|
104
104
|
/** Stream idle timeout: a generation that stalls this long is a dead connection. */
|
|
105
|
-
declare const DEFAULT_STREAM_IDLE_TIMEOUT_MS =
|
|
105
|
+
declare const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 300000;
|
|
106
106
|
/**
|
|
107
107
|
* Official display label for a model's minimum plan, or undefined for models
|
|
108
108
|
* outside the snapshot (e.g. future catalog additions).
|
|
@@ -142,7 +142,7 @@ interface CommandCodeConnectionOptions {
|
|
|
142
142
|
modelsCachePath: string;
|
|
143
143
|
/** Milliseconds to wait for the generate response's first byte (default 60s). */
|
|
144
144
|
requestTimeoutMs: number;
|
|
145
|
-
/** Milliseconds a stream may stall before it is treated as a dead connection (default
|
|
145
|
+
/** Milliseconds a stream may stall before it is treated as a dead connection (default 300s). */
|
|
146
146
|
streamIdleTimeoutMs: number;
|
|
147
147
|
}
|
|
148
148
|
/**
|
|
@@ -276,7 +276,7 @@ interface Config {
|
|
|
276
276
|
modelsCachePath?: string;
|
|
277
277
|
/** Milliseconds to wait for the generate response's first byte; defaults to 60s. */
|
|
278
278
|
requestTimeoutMs?: number;
|
|
279
|
-
/** Milliseconds a stream may stall before being treated as a dead connection; defaults to
|
|
279
|
+
/** Milliseconds a stream may stall before being treated as a dead connection; defaults to 300s. */
|
|
280
280
|
streamIdleTimeoutMs?: number;
|
|
281
281
|
}
|
|
282
282
|
declare const Config: z<Config>;
|
package/lib/index.js
CHANGED
|
@@ -383,7 +383,7 @@ const DEFAULT_MAX_OUTPUT_TOKENS = 65536;
|
|
|
383
383
|
/** Head-of-request timeout: how long to wait for the first response byte. */
|
|
384
384
|
const DEFAULT_REQUEST_TIMEOUT_MS = 6e4;
|
|
385
385
|
/** Stream idle timeout: a generation that stalls this long is a dead connection. */
|
|
386
|
-
const DEFAULT_STREAM_IDLE_TIMEOUT_MS =
|
|
386
|
+
const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 3e5;
|
|
387
387
|
const MODEL_CACHE_VERSION = 1;
|
|
388
388
|
function isRecord(value) {
|
|
389
389
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -446,6 +446,23 @@ function stringValue(value) {
|
|
|
446
446
|
function numberValue(value) {
|
|
447
447
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
448
448
|
}
|
|
449
|
+
function booleanValue(value) {
|
|
450
|
+
return typeof value === "boolean" ? value : void 0;
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Terminal stream-error markers from the official CLI (`Xw` in command-code's
|
|
454
|
+
* cli.mjs): these always mean "retrying cannot succeed", so the adapter must
|
|
455
|
+
* not classify them as transient server errors.
|
|
456
|
+
*/
|
|
457
|
+
const TERMINAL_STREAM_ERROR_MARKERS = [
|
|
458
|
+
"premium_credits_exhausted",
|
|
459
|
+
"model_not_in_plan",
|
|
460
|
+
"insufficient credits"
|
|
461
|
+
];
|
|
462
|
+
function hasTerminalStreamMarker(message) {
|
|
463
|
+
const lower = message.toLowerCase();
|
|
464
|
+
return TERMINAL_STREAM_ERROR_MARKERS.some((marker) => lower.includes(marker));
|
|
465
|
+
}
|
|
449
466
|
function recordOrEmpty(value) {
|
|
450
467
|
if (isRecord(value)) return value;
|
|
451
468
|
if (typeof value === "string") try {
|
|
@@ -1015,8 +1032,14 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
1015
1032
|
break;
|
|
1016
1033
|
}
|
|
1017
1034
|
case "error": {
|
|
1035
|
+
const err = isRecord(event.error) ? event.error : void 0;
|
|
1018
1036
|
const detail = isRecord(event.error) ? stringValue(event.error.message) ?? JSON.stringify(event.error) : stringValue(event.error) ?? stringValue(event.message) ?? "Stream error";
|
|
1019
|
-
|
|
1037
|
+
const statusCode = err ? numberValue(err.statusCode) : void 0;
|
|
1038
|
+
const isRetryable = err ? booleanValue(err.isRetryable) : void 0;
|
|
1039
|
+
const retryableStatus = statusCode !== void 0 && (statusCode === 429 || statusCode >= 500);
|
|
1040
|
+
const terminal = hasTerminalStreamMarker(detail);
|
|
1041
|
+
if (!(isRetryable === true || (statusCode !== void 0 ? retryableStatus : isRetryable !== false && !terminal))) throw new LlmError(`Command Code stream error: ${detail}`, "PROVIDER_STREAM_ERROR", statusCode !== void 0 ? { status: statusCode } : void 0);
|
|
1042
|
+
throw new LlmError(`Command Code stream error: ${detail}`, "SERVER", statusCode !== void 0 ? { status: statusCode } : void 0);
|
|
1020
1043
|
}
|
|
1021
1044
|
}
|
|
1022
1045
|
return chunks;
|
|
@@ -1209,7 +1232,7 @@ function resolveAdapterOptions(config) {
|
|
|
1209
1232
|
workingDir: config.workingDir ?? process.cwd(),
|
|
1210
1233
|
modelsCachePath: config.modelsCachePath ?? DEFAULT_MODELS_CACHE_PATH,
|
|
1211
1234
|
requestTimeoutMs: config.requestTimeoutMs ?? 6e4,
|
|
1212
|
-
streamIdleTimeoutMs: config.streamIdleTimeoutMs ??
|
|
1235
|
+
streamIdleTimeoutMs: config.streamIdleTimeoutMs ?? 3e5
|
|
1213
1236
|
};
|
|
1214
1237
|
}
|
|
1215
1238
|
function apply(ctx, config) {
|