@norman-else/dsh-claude 0.1.5 → 0.1.7

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.
@@ -1,4 +1,4 @@
1
- import { l as redactText } from "./events-qlmU1KrH.mjs";
1
+ import { l as redactText } from "./events-DP0Ov_Re.mjs";
2
2
  //#region src/command-bridge.ts
3
3
  const COMMAND_NAME = /^[a-z][a-z0-9_-]*$/u;
4
4
  const MAX_DESCRIPTION_CHARS = 300;
@@ -299,4 +299,4 @@ const CLAUDE_PRESENTER_NAMES = new Set(claudePresenterDefinitions().map((definit
299
299
  //#endregion
300
300
  export { ClaudeCommandBridge as a, CLAUDE_COMMANDS_SERVICE as i, claudePresenterDefinitions as n, dynamicPresenterDefinition as r, CLAUDE_PRESENTER_NAMES as t };
301
301
 
302
- //# sourceMappingURL=presenters-DbfG-9KQ.mjs.map
302
+ //# sourceMappingURL=presenters-YMR2WMjn.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presenters-YMR2WMjn.mjs","names":["#target","#live"],"sources":["../src/command-bridge.ts","../src/presenters.ts"],"sourcesContent":["import type { SlashCommand } from '@anthropic-ai/claude-agent-sdk'\r\nimport type { CommandDefinition, CommandDescriptor } from '@deepseek-ai/dsh-commands'\r\nimport { redactText } from './events.ts'\r\n\r\nconst COMMAND_NAME = /^[a-z][a-z0-9_-]*$/u\r\nconst MAX_DESCRIPTION_CHARS = 300\r\nconst MAX_HINT_CHARS = 120\r\n\r\n/** DSH registry names reject `:`, but plugin-qualified Claude skills carry it\r\n * (e.g. `awesome-skills:ci-deploy`). Derive a registry-safe public name while\r\n * forwarding keeps the exact Claude-side name. */\r\nfunction registryName(claudeName: string): string | undefined {\r\n if (COMMAND_NAME.test(claudeName)) return claudeName\r\n const short = claudeName.includes(':') ? claudeName.slice(claudeName.lastIndexOf(':') + 1) : undefined\r\n if (short !== undefined && COMMAND_NAME.test(short)) return short\r\n const sanitized = claudeName.replaceAll(':', '-').toLowerCase()\r\n return COMMAND_NAME.test(sanitized) ? sanitized : undefined\r\n}\r\n\r\n/**\r\n * Names registered as CLIENT-side commandUi contributions (not host\r\n * commands). The DSH web command palette fails its whole command group when\r\n * a host command name equals a contribution name\r\n * (\"contribution /model collides with a host command\"), so these can never\r\n * be registered by this bridge. Keep in sync with the client bundles:\r\n * dsh-client-ui-model-selection registers \"model\".\r\n */\r\nconst CLIENT_CONTRIBUTION_NAMES: ReadonlySet<string> = new Set(['model'])\r\n\r\nexport interface ClaudeCommandTarget {\r\n list(): readonly Pick<CommandDescriptor, 'name'>[]\r\n register(definition: CommandDefinition): () => void\r\n forward(line: string): void\r\n}\r\n\r\n/**\r\n * Agent-scope command service contract. The commands service is unreachable\r\n * from the host plugin's view of `agent.ctx` (\"without inject\"), but the\r\n * preset route plugin runs INSIDE the agent's preset composition with\r\n * `commands` injected. Registrations made through the provided service land\r\n * in the agent's scope layer, which the command registry inherits into\r\n * `list(agent)` for exactly that agent — invisible to every other session.\r\n */\r\nexport interface ClaudeAgentCommandService {\r\n list(agent: unknown): readonly Pick<CommandDescriptor, 'name'>[]\r\n register(definition: CommandDefinition): () => void\r\n}\r\n\r\n/**\r\n * Cordis service name the preset route provides (behind an entry-local\r\n * isolate realm, so each session gets its own instance) and the host reads\r\n * back with dsh-agent-presets' official `serviceForAgent(ctx, agent, name)`\r\n * — the supported cross-scope read for callers that already hold the agent.\r\n */\r\nexport const CLAUDE_COMMANDS_SERVICE = 'claudeCommands'\r\n\r\ndeclare module '@deepseek-ai/cordis' {\r\n interface Context {\r\n claudeCommands?: ClaudeAgentCommandService\r\n }\r\n}\r\n\r\nexport interface ClaudeCommandView {\r\n publicName: string\r\n claudeName: string\r\n prefixed: boolean\r\n}\r\n\r\ninterface DesiredCommand extends ClaudeCommandView {\r\n signature: string\r\n definition: CommandDefinition\r\n}\r\n\r\ninterface LiveCommand {\r\n signature: string\r\n dispose: () => void\r\n}\r\n\r\nfunction bounded(value: string, maxChars: number): string {\r\n return redactText(value, maxChars)\r\n}\r\n\r\nfunction desiredCommands(\r\n catalog: readonly SlashCommand[],\r\n reservedNames: ReadonlySet<string>,\r\n forward: (line: string) => void,\r\n): Map<string, DesiredCommand> {\r\n const desired = new Map<string, DesiredCommand>()\r\n const assigned = new Set<string>()\r\n for (const command of catalog) {\r\n const names = [command.name, ...(command.aliases ?? [])]\r\n for (const claudeName of names) {\r\n const base = registryName(claudeName)\r\n if (base === undefined) continue\r\n let publicName = base\r\n let prefixed = false\r\n if (reservedNames.has(publicName) || assigned.has(publicName)) {\r\n publicName = `claude-${base}`\r\n prefixed = true\r\n }\r\n if (!COMMAND_NAME.test(publicName) || reservedNames.has(publicName) || assigned.has(publicName)) continue\r\n const description = bounded(command.description || `Claude Code /${command.name}`, MAX_DESCRIPTION_CHARS)\r\n const hint = bounded(command.argumentHint ?? '', MAX_HINT_CHARS)\r\n const signature = JSON.stringify({ publicName, claudeName, description, hint })\r\n desired.set(publicName, {\r\n publicName,\r\n claudeName,\r\n prefixed,\r\n signature,\r\n definition: {\r\n name: publicName,\r\n description,\r\n ...(hint.length === 0 ? {} : { input: { hint } }),\r\n recordInput: false,\r\n handler: ({ rawInput }) => {\r\n forward(`/${claudeName}${rawInput}`)\r\n return { kind: 'success' }\r\n },\r\n },\r\n })\r\n assigned.add(publicName)\r\n }\r\n }\r\n return desired\r\n}\r\n\r\nexport class ClaudeCommandBridge {\r\n readonly #target: ClaudeCommandTarget\r\n readonly #live = new Map<string, LiveCommand>()\r\n\r\n constructor(target: ClaudeCommandTarget) {\r\n this.#target = target\r\n }\r\n\r\n refresh(catalog: readonly SlashCommand[]): readonly ClaudeCommandView[] {\r\n const owned = new Set(this.#live.keys())\r\n const reserved = new Set(\r\n this.#target.list()\r\n .map(command => command.name)\r\n .filter(name => !owned.has(name)),\r\n )\r\n // Client-side commandUi contributions (e.g. /model from\r\n // dsh-client-ui-model-selection) are invisible to the host registry, but\r\n // the web palette throws away its ENTIRE command group when a host\r\n // command collides with a contribution. Reserve those names so Claude's\r\n // same-named commands take the claude- prefix instead.\r\n for (const name of CLIENT_CONTRIBUTION_NAMES) reserved.add(name)\r\n const desired = desiredCommands(catalog, reserved, line => this.#target.forward(line))\r\n\r\n for (const [name, live] of [...this.#live]) {\r\n const next = desired.get(name)\r\n if (next?.signature === live.signature) continue\r\n live.dispose()\r\n this.#live.delete(name)\r\n }\r\n for (const [name, next] of desired) {\r\n if (this.#live.has(name)) continue\r\n this.#live.set(name, {\r\n signature: next.signature,\r\n dispose: this.#target.register(next.definition),\r\n })\r\n }\r\n return [...desired.values()].map(({ publicName, claudeName, prefixed }) => ({\r\n publicName,\r\n claudeName,\r\n prefixed,\r\n }))\r\n }\r\n\r\n dispose(): void {\r\n for (const command of this.#live.values()) command.dispose()\r\n this.#live.clear()\r\n }\r\n}\r\n","/**\r\n * Presentation-only tool definitions for the Claude Code preset. Claude Code\r\n * owns tool execution; these definitions exist solely so the host's tool\r\n * presentation pipeline (`viewFor` → `presentCall`/`presentResult`) can\r\n * compute native render intents for the mirrored `tool/call`/`tool/result`\r\n * events, giving Claude tool rows the same cards DSH-executed tools get.\r\n */\r\nimport type { ToolDefinition, ToolResult } from '@deepseek-ai/dsh-tools'\r\nimport type { ToolCallView, ToolResultView } from '@deepseek-ai/dsh-tools/presentation'\r\n\r\nfunction record(value: unknown): Record<string, unknown> | undefined {\r\n return value !== null && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : undefined\r\n}\r\n\r\nfunction str(value: unknown): string | undefined {\r\n return typeof value === 'string' && value.length > 0 ? value : undefined\r\n}\r\n\r\nfunction resultText(result: ToolResult): string {\r\n return result.content\r\n .filter((block): block is Extract<typeof block, { type: 'text' }> => block.type === 'text')\r\n .map(block => block.text)\r\n .join('\\n')\r\n}\r\n\r\n/** Bash: a terminal card headed by the command, described above by Claude's own summary. */\r\nexport function bashCallView(args: unknown): ToolCallView | undefined {\r\n const command = str(record(args)?.command)\r\n if (command === undefined) return undefined\r\n const description = str(record(args)?.description)\r\n return { card: 'terminal', title: command, ...(description === undefined ? {} : { description }) }\r\n}\r\n\r\nexport function terminalResultView(_args: unknown, result: ToolResult): ToolResultView | undefined {\r\n const output = resultText(result)\r\n return output.length === 0 ? undefined : { card: 'terminal', output }\r\n}\r\n\r\n/** Read: a generic read card that follow-alongs the file window. */\r\nexport function readCallView(args: unknown): ToolCallView | undefined {\r\n const arguments_ = record(args)\r\n const path = str(arguments_?.file_path)\r\n if (path === undefined) return undefined\r\n const offset = typeof arguments_?.offset === 'number' ? arguments_.offset : undefined\r\n return {\r\n card: 'generic',\r\n kind: 'read',\r\n title: `Read ${path}`,\r\n locations: [{ path, ...(offset === undefined ? {} : { line: offset }) }],\r\n }\r\n}\r\n\r\nfunction fileDiffs(args: unknown): { path: string; oldText: string | null; newText: string }[] | undefined {\r\n const arguments_ = record(args)\r\n const path = str(arguments_?.file_path)\r\n if (path === undefined) return undefined\r\n if (Array.isArray(arguments_?.edits)) {\r\n const diffs: { path: string; oldText: string | null; newText: string }[] = []\r\n for (const edit of arguments_.edits) {\r\n const item = record(edit)\r\n const oldText = str(item?.old_string)\r\n const newText = str(item?.new_string)\r\n if (newText === undefined) continue\r\n diffs.push({ path, oldText: oldText ?? null, newText })\r\n }\r\n return diffs.length > 0 ? diffs : undefined\r\n }\r\n const newText = str(arguments_?.new_string) ?? arguments_?.content\r\n if (typeof newText !== 'string' || newText.length === 0) return undefined\r\n const oldText = str(arguments_?.old_string)\r\n return [{ path, oldText: oldText ?? null, newText }]\r\n}\r\n\r\n/** Edit / MultiEdit / Write: inline diff cards derived from the call arguments. */\r\nexport function diffCallView(args: unknown): ToolCallView | undefined {\r\n const arguments_ = record(args)\r\n const path = str(arguments_?.file_path)\r\n const diffs = fileDiffs(args)\r\n if (path === undefined || diffs === undefined) return undefined\r\n const verb = typeof arguments_?.new_string === 'string' || Array.isArray(arguments_?.edits) ? 'Edit' : 'Write'\r\n return { card: 'diff', title: `${verb} ${path}`, diffs, locations: [{ path }] }\r\n}\r\n\r\n/** Grep / Glob / WebSearch: search-category cards titled by the query. */\r\nexport function searchCallView(args: unknown): ToolCallView | undefined {\r\n const arguments_ = record(args)\r\n const query = str(arguments_?.pattern) ?? str(arguments_?.query)\r\n if (query === undefined) return undefined\r\n const scope = str(arguments_?.path)\r\n return {\r\n card: 'generic',\r\n kind: 'search',\r\n title: scope === undefined ? query : `${query} · ${scope}`,\r\n rawInput: args,\r\n }\r\n}\r\n\r\n/** WebFetch: a fetch-category card titled by the URL. */\r\nexport function fetchCallView(args: unknown): ToolCallView | undefined {\r\n const url = str(record(args)?.url)\r\n if (url === undefined) return undefined\r\n return { card: 'generic', kind: 'fetch', title: url, rawInput: args }\r\n}\r\n\r\n/** Task: a subagent card titled by Claude's task description. */\r\nexport function taskCallView(args: unknown): ToolCallView | undefined {\r\n const arguments_ = record(args)\r\n const title = str(arguments_?.description) ?? str(arguments_?.subagent_type) ?? 'Subagent'\r\n return { card: 'generic', kind: 'other', title, rawInput: args }\r\n}\r\n\r\nfunction genericTitle(title: string): ToolCallView {\r\n return { card: 'generic', kind: 'other', title }\r\n}\r\n\r\nfunction presenterDefinition(\r\n name: string,\r\n description: string,\r\n presentCall?: (args: unknown) => ToolCallView | undefined,\r\n presentResult?: (args: unknown, result: ToolResult) => ToolResultView | undefined,\r\n): ToolDefinition {\r\n return {\r\n name,\r\n description,\r\n parameters: { type: 'object', properties: {} },\r\n output: {\r\n schema: { type: 'object', properties: {} } as ToolDefinition['output']['schema'],\r\n render: () => [],\r\n },\r\n execute: async () => {\r\n throw new Error(`dsh-claude: Claude Code owns execution of ${name}`)\r\n },\r\n ...(presentCall === undefined ? {} : { presentCall }),\r\n ...(presentResult === undefined ? {} : { presentResult }),\r\n }\r\n}\r\n\r\nconst PRESENTATION_NOTE = 'Presentation mirror of the Claude Code tool; execution is owned by Claude Code.'\r\n\r\n/** Generic call view for dynamically observed tools (MCP tools, new built-ins). */\r\nexport function genericCallView(toolName: string): (args: unknown) => ToolCallView | undefined {\r\n return (args: unknown) => {\r\n const description = str(record(args)?.description)\r\n return { card: 'generic', kind: 'other', title: description ?? toolName, rawInput: args }\r\n }\r\n}\r\n\r\n/** One presenter-only mirror for a tool name discovered at runtime. */\r\nexport function dynamicPresenterDefinition(name: string): ToolDefinition {\r\n return presenterDefinition(name, PRESENTATION_NOTE, genericCallView(name))\r\n}\r\n\r\n/** The presentation-only registry contributed to the Claude Code preset scope. */\r\nexport function claudePresenterDefinitions(): ToolDefinition[] {\r\n return [\r\n presenterDefinition('Bash', PRESENTATION_NOTE, bashCallView, terminalResultView),\r\n presenterDefinition('Read', PRESENTATION_NOTE, readCallView),\r\n presenterDefinition('Edit', PRESENTATION_NOTE, diffCallView),\r\n presenterDefinition('MultiEdit', PRESENTATION_NOTE, diffCallView),\r\n presenterDefinition('Write', PRESENTATION_NOTE, diffCallView),\r\n presenterDefinition('NotebookEdit', PRESENTATION_NOTE, args => {\r\n const path = str(record(args)?.notebook_path)\r\n return path === undefined ? undefined : genericTitle(`NotebookEdit ${path}`)\r\n }),\r\n presenterDefinition('Grep', PRESENTATION_NOTE, searchCallView),\r\n presenterDefinition('Glob', PRESENTATION_NOTE, searchCallView),\r\n presenterDefinition('WebSearch', PRESENTATION_NOTE, searchCallView),\r\n presenterDefinition('WebFetch', PRESENTATION_NOTE, fetchCallView),\r\n presenterDefinition('Task', PRESENTATION_NOTE, taskCallView),\r\n presenterDefinition('TodoWrite', PRESENTATION_NOTE, () => genericTitle('Update todos')),\r\n ]\r\n}\r\n\r\n/** Names covered by the static preset-scope registry; dynamic mirrors skip them. */\r\nexport const CLAUDE_PRESENTER_NAMES: ReadonlySet<string> = new Set(claudePresenterDefinitions().map(definition => definition.name))\r\n"],"mappings":";;AAIA,MAAM,eAAe;AACrB,MAAM,wBAAwB;AAC9B,MAAM,iBAAiB;;;;AAKvB,SAAS,aAAa,YAAwC;CAC5D,IAAI,aAAa,KAAK,UAAU,GAAG,OAAO;CAC1C,MAAM,QAAQ,WAAW,SAAS,GAAG,IAAI,WAAW,MAAM,WAAW,YAAY,GAAG,IAAI,CAAC,IAAI,KAAA;CAC7F,IAAI,UAAU,KAAA,KAAa,aAAa,KAAK,KAAK,GAAG,OAAO;CAC5D,MAAM,YAAY,WAAW,WAAW,KAAK,GAAG,CAAC,CAAC,YAAY;CAC9D,OAAO,aAAa,KAAK,SAAS,IAAI,YAAY,KAAA;AACpD;;;;;;;;;AAUA,MAAM,4CAAiD,IAAI,IAAI,CAAC,OAAO,CAAC;;;;;;;AA2BxE,MAAa,0BAA0B;AAwBvC,SAAS,QAAQ,OAAe,UAA0B;CACxD,OAAO,WAAW,OAAO,QAAQ;AACnC;AAEA,SAAS,gBACP,SACA,eACA,SAC6B;CAC7B,MAAM,0BAAU,IAAI,IAA4B;CAChD,MAAM,2BAAW,IAAI,IAAY;CACjC,KAAK,MAAM,WAAW,SAAS;EAC7B,MAAM,QAAQ,CAAC,QAAQ,MAAM,GAAI,QAAQ,WAAW,CAAC,CAAE;EACvD,KAAK,MAAM,cAAc,OAAO;GAC9B,MAAM,OAAO,aAAa,UAAU;GACpC,IAAI,SAAS,KAAA,GAAW;GACxB,IAAI,aAAa;GACjB,IAAI,WAAW;GACf,IAAI,cAAc,IAAI,UAAU,KAAK,SAAS,IAAI,UAAU,GAAG;IAC7D,aAAa,UAAU;IACvB,WAAW;GACb;GACA,IAAI,CAAC,aAAa,KAAK,UAAU,KAAK,cAAc,IAAI,UAAU,KAAK,SAAS,IAAI,UAAU,GAAG;GACjG,MAAM,cAAc,QAAQ,QAAQ,eAAe,gBAAgB,QAAQ,QAAQ,qBAAqB;GACxG,MAAM,OAAO,QAAQ,QAAQ,gBAAgB,IAAI,cAAc;GAC/D,MAAM,YAAY,KAAK,UAAU;IAAE;IAAY;IAAY;IAAa;GAAK,CAAC;GAC9E,QAAQ,IAAI,YAAY;IACtB;IACA;IACA;IACA;IACA,YAAY;KACV,MAAM;KACN;KACA,GAAI,KAAK,WAAW,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE;KAC/C,aAAa;KACb,UAAU,EAAE,eAAe;MACzB,QAAQ,IAAI,aAAa,UAAU;MACnC,OAAO,EAAE,MAAM,UAAU;KAC3B;IACF;GACF,CAAC;GACD,SAAS,IAAI,UAAU;EACzB;CACF;CACA,OAAO;AACT;AAEA,IAAa,sBAAb,MAAiC;CAC/B;CACA,wBAAiB,IAAI,IAAyB;CAE9C,YAAY,QAA6B;EACvC,KAAKA,UAAU;CACjB;CAEA,QAAQ,SAAgE;EACtE,MAAM,QAAQ,IAAI,IAAI,KAAKC,MAAM,KAAK,CAAC;EACvC,MAAM,WAAW,IAAI,IACnB,KAAKD,QAAQ,KAAK,CAAC,CAChB,KAAI,YAAW,QAAQ,IAAI,CAAC,CAC5B,QAAO,SAAQ,CAAC,MAAM,IAAI,IAAI,CAAC,CACpC;EAMA,KAAK,MAAM,QAAQ,2BAA2B,SAAS,IAAI,IAAI;EAC/D,MAAM,UAAU,gBAAgB,SAAS,WAAU,SAAQ,KAAKA,QAAQ,QAAQ,IAAI,CAAC;EAErF,KAAK,MAAM,CAAC,MAAM,SAAS,CAAC,GAAG,KAAKC,KAAK,GAAG;GAE1C,IADa,QAAQ,IAAI,IAClB,CAAC,EAAE,cAAc,KAAK,WAAW;GACxC,KAAK,QAAQ;GACb,KAAKA,MAAM,OAAO,IAAI;EACxB;EACA,KAAK,MAAM,CAAC,MAAM,SAAS,SAAS;GAClC,IAAI,KAAKA,MAAM,IAAI,IAAI,GAAG;GAC1B,KAAKA,MAAM,IAAI,MAAM;IACnB,WAAW,KAAK;IAChB,SAAS,KAAKD,QAAQ,SAAS,KAAK,UAAU;GAChD,CAAC;EACH;EACA,OAAO,CAAC,GAAG,QAAQ,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,YAAY,YAAY,gBAAgB;GAC1E;GACA;GACA;EACF,EAAE;CACJ;CAEA,UAAgB;EACd,KAAK,MAAM,WAAW,KAAKC,MAAM,OAAO,GAAG,QAAQ,QAAQ;EAC3D,KAAKA,MAAM,MAAM;CACnB;AACF;;;ACnKA,SAAS,OAAO,OAAqD;CACnE,OAAO,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IAAI,QAAmC,KAAA;AACnH;AAEA,SAAS,IAAI,OAAoC;CAC/C,OAAO,OAAO,UAAU,YAAY,MAAM,SAAS,IAAI,QAAQ,KAAA;AACjE;AAEA,SAAS,WAAW,QAA4B;CAC9C,OAAO,OAAO,QACX,QAAQ,UAA4D,MAAM,SAAS,MAAM,CAAC,CAC1F,KAAI,UAAS,MAAM,IAAI,CAAC,CACxB,KAAK,IAAI;AACd;;AAGA,SAAgB,aAAa,MAAyC;CACpE,MAAM,UAAU,IAAI,OAAO,IAAI,CAAC,EAAE,OAAO;CACzC,IAAI,YAAY,KAAA,GAAW,OAAO,KAAA;CAClC,MAAM,cAAc,IAAI,OAAO,IAAI,CAAC,EAAE,WAAW;CACjD,OAAO;EAAE,MAAM;EAAY,OAAO;EAAS,GAAI,gBAAgB,KAAA,IAAY,CAAC,IAAI,EAAE,YAAY;CAAG;AACnG;AAEA,SAAgB,mBAAmB,OAAgB,QAAgD;CACjG,MAAM,SAAS,WAAW,MAAM;CAChC,OAAO,OAAO,WAAW,IAAI,KAAA,IAAY;EAAE,MAAM;EAAY;CAAO;AACtE;;AAGA,SAAgB,aAAa,MAAyC;CACpE,MAAM,aAAa,OAAO,IAAI;CAC9B,MAAM,OAAO,IAAI,YAAY,SAAS;CACtC,IAAI,SAAS,KAAA,GAAW,OAAO,KAAA;CAC/B,MAAM,SAAS,OAAO,YAAY,WAAW,WAAW,WAAW,SAAS,KAAA;CAC5E,OAAO;EACL,MAAM;EACN,MAAM;EACN,OAAO,QAAQ;EACf,WAAW,CAAC;GAAE;GAAM,GAAI,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM,OAAO;EAAG,CAAC;CACzE;AACF;AAEA,SAAS,UAAU,MAAwF;CACzG,MAAM,aAAa,OAAO,IAAI;CAC9B,MAAM,OAAO,IAAI,YAAY,SAAS;CACtC,IAAI,SAAS,KAAA,GAAW,OAAO,KAAA;CAC/B,IAAI,MAAM,QAAQ,YAAY,KAAK,GAAG;EACpC,MAAM,QAAqE,CAAC;EAC5E,KAAK,MAAM,QAAQ,WAAW,OAAO;GACnC,MAAM,OAAO,OAAO,IAAI;GACxB,MAAM,UAAU,IAAI,MAAM,UAAU;GACpC,MAAM,UAAU,IAAI,MAAM,UAAU;GACpC,IAAI,YAAY,KAAA,GAAW;GAC3B,MAAM,KAAK;IAAE;IAAM,SAAS,WAAW;IAAM;GAAQ,CAAC;EACxD;EACA,OAAO,MAAM,SAAS,IAAI,QAAQ,KAAA;CACpC;CACA,MAAM,UAAU,IAAI,YAAY,UAAU,KAAK,YAAY;CAC3D,IAAI,OAAO,YAAY,YAAY,QAAQ,WAAW,GAAG,OAAO,KAAA;CAEhE,OAAO,CAAC;EAAE;EAAM,SADA,IAAI,YAAY,UACD,KAAK;EAAM;CAAQ,CAAC;AACrD;;AAGA,SAAgB,aAAa,MAAyC;CACpE,MAAM,aAAa,OAAO,IAAI;CAC9B,MAAM,OAAO,IAAI,YAAY,SAAS;CACtC,MAAM,QAAQ,UAAU,IAAI;CAC5B,IAAI,SAAS,KAAA,KAAa,UAAU,KAAA,GAAW,OAAO,KAAA;CAEtD,OAAO;EAAE,MAAM;EAAQ,OAAO,GADjB,OAAO,YAAY,eAAe,YAAY,MAAM,QAAQ,YAAY,KAAK,IAAI,SAAS,QACjE,GAAG;EAAQ;EAAO,WAAW,CAAC,EAAE,KAAK,CAAC;CAAE;AAChF;;AAGA,SAAgB,eAAe,MAAyC;CACtE,MAAM,aAAa,OAAO,IAAI;CAC9B,MAAM,QAAQ,IAAI,YAAY,OAAO,KAAK,IAAI,YAAY,KAAK;CAC/D,IAAI,UAAU,KAAA,GAAW,OAAO,KAAA;CAChC,MAAM,QAAQ,IAAI,YAAY,IAAI;CAClC,OAAO;EACL,MAAM;EACN,MAAM;EACN,OAAO,UAAU,KAAA,IAAY,QAAQ,GAAG,MAAM,KAAK;EACnD,UAAU;CACZ;AACF;;AAGA,SAAgB,cAAc,MAAyC;CACrE,MAAM,MAAM,IAAI,OAAO,IAAI,CAAC,EAAE,GAAG;CACjC,IAAI,QAAQ,KAAA,GAAW,OAAO,KAAA;CAC9B,OAAO;EAAE,MAAM;EAAW,MAAM;EAAS,OAAO;EAAK,UAAU;CAAK;AACtE;;AAGA,SAAgB,aAAa,MAAyC;CACpE,MAAM,aAAa,OAAO,IAAI;CAE9B,OAAO;EAAE,MAAM;EAAW,MAAM;EAAS,OAD3B,IAAI,YAAY,WAAW,KAAK,IAAI,YAAY,aAAa,KAAK;EAChC,UAAU;CAAK;AACjE;AAEA,SAAS,aAAa,OAA6B;CACjD,OAAO;EAAE,MAAM;EAAW,MAAM;EAAS;CAAM;AACjD;AAEA,SAAS,oBACP,MACA,aACA,aACA,eACgB;CAChB,OAAO;EACL;EACA;EACA,YAAY;GAAE,MAAM;GAAU,YAAY,CAAC;EAAE;EAC7C,QAAQ;GACN,QAAQ;IAAE,MAAM;IAAU,YAAY,CAAC;GAAE;GACzC,cAAc,CAAC;EACjB;EACA,SAAS,YAAY;GACnB,MAAM,IAAI,MAAM,6CAA6C,MAAM;EACrE;EACA,GAAI,gBAAgB,KAAA,IAAY,CAAC,IAAI,EAAE,YAAY;EACnD,GAAI,kBAAkB,KAAA,IAAY,CAAC,IAAI,EAAE,cAAc;CACzD;AACF;AAEA,MAAM,oBAAoB;;AAG1B,SAAgB,gBAAgB,UAA+D;CAC7F,QAAQ,SAAkB;EAExB,OAAO;GAAE,MAAM;GAAW,MAAM;GAAS,OADrB,IAAI,OAAO,IAAI,CAAC,EAAE,WACoB,KAAK;GAAU,UAAU;EAAK;CAC1F;AACF;;AAGA,SAAgB,2BAA2B,MAA8B;CACvE,OAAO,oBAAoB,MAAM,mBAAmB,gBAAgB,IAAI,CAAC;AAC3E;;AAGA,SAAgB,6BAA+C;CAC7D,OAAO;EACL,oBAAoB,QAAQ,mBAAmB,cAAc,kBAAkB;EAC/E,oBAAoB,QAAQ,mBAAmB,YAAY;EAC3D,oBAAoB,QAAQ,mBAAmB,YAAY;EAC3D,oBAAoB,aAAa,mBAAmB,YAAY;EAChE,oBAAoB,SAAS,mBAAmB,YAAY;EAC5D,oBAAoB,gBAAgB,oBAAmB,SAAQ;GAC7D,MAAM,OAAO,IAAI,OAAO,IAAI,CAAC,EAAE,aAAa;GAC5C,OAAO,SAAS,KAAA,IAAY,KAAA,IAAY,aAAa,gBAAgB,MAAM;EAC7E,CAAC;EACD,oBAAoB,QAAQ,mBAAmB,cAAc;EAC7D,oBAAoB,QAAQ,mBAAmB,cAAc;EAC7D,oBAAoB,aAAa,mBAAmB,cAAc;EAClE,oBAAoB,YAAY,mBAAmB,aAAa;EAChE,oBAAoB,QAAQ,mBAAmB,YAAY;EAC3D,oBAAoB,aAAa,yBAAyB,aAAa,cAAc,CAAC;CACxF;AACF;;AAGA,MAAa,yBAA8C,IAAI,IAAI,2BAA2B,CAAC,CAAC,KAAI,eAAc,WAAW,IAAI,CAAC"}
@@ -1,7 +1,7 @@
1
- import { _ as LEGACY_CLAUDE_CODE_PRESET_ID, f as CLAUDE_CODE_PRESET_ID, l as redactText } from "./events-qlmU1KrH.mjs";
1
+ import { f as CLAUDE_CODE_PRESET_ID, l as redactText } from "./events-DP0Ov_Re.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { link, lstat, mkdir, readFile, readdir, rm, rmdir, writeFile } from "node:fs/promises";
4
- import { dirname, join } from "node:path";
4
+ import { dirname, isAbsolute, join } from "node:path";
5
5
  import { dshHomePath } from "@deepseek-ai/dsh-home-paths";
6
6
  import { homedir } from "node:os";
7
7
  import { fileURLToPath } from "node:url";
@@ -31,7 +31,7 @@ function abortError(error) {
31
31
  async function resolveClaudeExecutable(runtime, configuredPath, signal) {
32
32
  const searched = [];
33
33
  const candidates = configuredPath === void 0 ? ["claude", ...fallbackCandidates()] : [configuredPath];
34
- if (configuredPath !== void 0 && !configuredPath.startsWith("/")) throw new Error(`Claude Code executable path must be absolute: ${configuredPath}`);
34
+ if (configuredPath !== void 0 && !isAbsolute(configuredPath)) throw new Error(`Claude Code executable path must be absolute: ${configuredPath}`);
35
35
  let lastError;
36
36
  for (const candidate of candidates) {
37
37
  if (searched.includes(candidate)) continue;
@@ -173,8 +173,7 @@ function defaultManagedPresetPaths(dshHome) {
173
173
  const packageRoot = fileURLToPath(new URL("../", import.meta.url));
174
174
  return {
175
175
  sourceDir: join(packageRoot, "legacy-preset"),
176
- targetDir: dshHome === void 0 ? dshHomePath(".agent-presets", CLAUDE_CODE_PRESET_ID) : join(dshHome, ".agent-presets", CLAUDE_CODE_PRESET_ID),
177
- legacyTargetDir: dshHome === void 0 ? dshHomePath(".agent-presets", LEGACY_CLAUDE_CODE_PRESET_ID) : join(dshHome, ".agent-presets", LEGACY_CLAUDE_CODE_PRESET_ID)
176
+ targetDir: dshHome === void 0 ? dshHomePath(".agent-presets", CLAUDE_CODE_PRESET_ID) : join(dshHome, ".agent-presets", CLAUDE_CODE_PRESET_ID)
178
177
  };
179
178
  }
180
179
  async function managedContents(paths) {
@@ -240,22 +239,8 @@ async function ensureManagedPreset(paths = defaultManagedPresetPaths()) {
240
239
  }
241
240
  changed = await atomicWrite(target, content) || changed;
242
241
  }
243
- if (paths.legacyTargetDir !== void 0) changed = await removeLegacyManagedPreset(paths.legacyTargetDir) || changed;
244
242
  return changed ? "installed" : "unchanged";
245
243
  }
246
- async function removeLegacyManagedPreset(targetDir) {
247
- await assertSafeTargetDirectory(targetDir);
248
- const agent = await readIfPresent(join(targetDir, "agent.cordis.yml"));
249
- const preset = await readIfPresent(join(targetDir, "preset.yml"));
250
- if (agent === void 0 && preset === void 0) return false;
251
- const managedAgent = agent !== void 0 && agent.includes("id: claude-code-route") && (agent.includes("dsh-claude-code/preset-route") || agent.includes("lib/preset-route.mjs"));
252
- const managedPreset = preset !== void 0 && preset.includes("Managed by dsh-claude-code") && preset.includes("name: Claude Code");
253
- if (!managedAgent || !managedPreset) return false;
254
- await rm(join(targetDir, "agent.cordis.yml"));
255
- await rm(join(targetDir, "preset.yml"));
256
- if ((await readdir(targetDir)).length === 0) await rmdir(targetDir);
257
- return true;
258
- }
259
244
  /** Reject a target directory that is a symlink (or occupies the path as a file)
260
245
  * so the managed preset never writes through an attacker-controlled link. */
261
246
  async function assertSafeTargetDirectory(targetDir) {
@@ -289,4 +274,4 @@ async function removeManagedPreset(paths = defaultManagedPresetPaths()) {
289
274
  //#endregion
290
275
  export { resolveClaudeExecutable as a, parseClaudeVersion as i, ensureManagedPreset as n, runClaudeDoctor as o, removeManagedPreset as r, ManagedPresetConflictError as t };
291
276
 
292
- //# sourceMappingURL=preset-installer-D8q3_-wJ.mjs.map
277
+ //# sourceMappingURL=preset-installer-BKw-SdOm.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preset-installer-BKw-SdOm.mjs","names":[],"sources":["../src/executable.ts","../src/preset-installer.ts"],"sourcesContent":["import { homedir } from 'node:os'\r\nimport { isAbsolute, join } from 'node:path'\r\nimport type { SubprocessHandle, SubprocessRuntime } from '@deepseek-ai/dsh-subprocess'\r\nimport { redactText } from './events.ts'\r\n\r\nconst VERSION_PATTERN = /(?:Claude Code\\s+)?v?(\\d+\\.\\d+\\.\\d+(?:[-+][\\w.-]+)?)/i\r\nconst MAX_PROBE_STDOUT = 64 * 1024\r\nconst MAX_PROBE_STDERR = 8 * 1024\r\n\r\nexport type ExecutableRuntime = Pick<SubprocessRuntime, 'resolveExecutable' | 'spawn'>\r\n\r\nexport class ClaudeExecutableNotFoundError extends Error {\r\n readonly searched: readonly string[]\r\n\r\n constructor(searched: readonly string[], options?: ErrorOptions) {\r\n super(`Claude Code executable not found. Searched: ${searched.join(', ')}`, options)\r\n this.name = 'ClaudeExecutableNotFoundError'\r\n this.searched = [...searched]\r\n }\r\n}\r\n\r\nexport interface ClaudeExecutableResolution {\r\n path: string\r\n searched: readonly string[]\r\n}\r\n\r\nexport interface ClaudeDoctorReport {\r\n executable: {\r\n status: 'found' | 'missing'\r\n path?: string\r\n searched: readonly string[]\r\n }\r\n version: {\r\n status: 'ok' | 'error' | 'not-run'\r\n value?: string\r\n message?: string\r\n }\r\n authentication: {\r\n status: 'signed-in' | 'signed-out' | 'unknown' | 'not-run'\r\n method?: string\r\n provider?: string\r\n subscription?: string\r\n message?: string\r\n }\r\n handshake: 'not-run' | 'ok' | 'error'\r\n}\r\n\r\nfunction fallbackCandidates(): string[] {\r\n if (process.platform !== 'darwin') return []\r\n return [\r\n join(homedir(), '.local', 'bin', 'claude'),\r\n '/opt/homebrew/bin/claude',\r\n '/usr/local/bin/claude',\r\n ]\r\n}\r\n\r\nfunction abortError(error: unknown): boolean {\r\n return error instanceof Error && (error.name === 'AbortError' || error.name === 'TimeoutError')\r\n}\r\n\r\nexport async function resolveClaudeExecutable(\r\n runtime: ExecutableRuntime,\r\n configuredPath?: string,\r\n signal?: AbortSignal,\r\n): Promise<ClaudeExecutableResolution> {\r\n const searched: string[] = []\r\n const candidates = configuredPath === undefined\r\n ? ['claude', ...fallbackCandidates()]\r\n : [configuredPath]\r\n if (configuredPath !== undefined && !isAbsolute(configuredPath)) {\r\n throw new Error(`Claude Code executable path must be absolute: ${configuredPath}`)\r\n }\r\n\r\n let lastError: unknown\r\n for (const candidate of candidates) {\r\n if (searched.includes(candidate)) continue\r\n searched.push(candidate)\r\n try {\r\n const path = await runtime.resolveExecutable(candidate, undefined, signal)\r\n return { path, searched }\r\n } catch (error) {\r\n if (abortError(error) || signal?.aborted === true) throw error\r\n lastError = error\r\n }\r\n }\r\n throw new ClaudeExecutableNotFoundError(searched, lastError === undefined ? undefined : { cause: lastError })\r\n}\r\n\r\ninterface CollectedCommand {\r\n exitCode: number | null\r\n signal: NodeJS.Signals | null\r\n stdout: string\r\n stderr: string\r\n}\r\n\r\nasync function collect(handle: SubprocessHandle): Promise<CollectedCommand> {\r\n const outcome = await handle.done\r\n const stdout = handle.collected.stdout?.readFrom(0).text ?? ''\r\n const stderr = handle.collected.stderr?.readFrom(0).text ?? ''\r\n return { ...outcome, stdout, stderr }\r\n}\r\n\r\nasync function runProbe(\r\n runtime: ExecutableRuntime,\r\n executable: string,\r\n args: readonly string[],\r\n cwd: string,\r\n signal?: AbortSignal,\r\n): Promise<CollectedCommand> {\r\n return collect(runtime.spawn({\r\n argv: [executable, ...args],\r\n cwd,\r\n stdio: {\r\n stdin: 'ignore',\r\n stdout: { maxBytes: MAX_PROBE_STDOUT },\r\n stderr: { maxBytes: MAX_PROBE_STDERR },\r\n },\r\n graceMs: 2_000,\r\n ...(signal === undefined ? {} : { signal }),\r\n env: {},\r\n }))\r\n}\r\n\r\nexport function parseClaudeVersion(output: string): string | undefined {\r\n return VERSION_PATTERN.exec(output)?.[1]\r\n}\r\n\r\nexport async function probeClaudeVersion(\r\n runtime: ExecutableRuntime,\r\n executable: string,\r\n cwd: string,\r\n signal?: AbortSignal,\r\n): Promise<string> {\r\n const result = await runProbe(runtime, executable, ['--version'], cwd, signal)\r\n const version = parseClaudeVersion(`${result.stdout}\\n${result.stderr}`)\r\n if (result.exitCode !== 0 || version === undefined) {\r\n throw new Error(`Claude Code version probe failed (${result.exitCode ?? result.signal ?? 'unknown exit'})`)\r\n }\r\n return version\r\n}\r\n\r\nexport async function probeClaudeAuthentication(\r\n runtime: ExecutableRuntime,\r\n executable: string,\r\n cwd: string,\r\n signal?: AbortSignal,\r\n): Promise<ClaudeDoctorReport['authentication']> {\r\n const result = await runProbe(runtime, executable, ['auth', 'status', '--json'], cwd, signal)\r\n if (result.exitCode !== 0) {\r\n return { status: 'unknown', message: 'Claude authentication status command failed' }\r\n }\r\n try {\r\n const value = JSON.parse(result.stdout) as Record<string, unknown>\r\n const report: ClaudeDoctorReport['authentication'] = {\r\n status: value.loggedIn === true ? 'signed-in' : value.loggedIn === false ? 'signed-out' : 'unknown',\r\n }\r\n if (typeof value.authMethod === 'string') report.method = redactText(value.authMethod, 100)\r\n if (typeof value.apiProvider === 'string') report.provider = redactText(value.apiProvider, 100)\r\n if (typeof value.subscriptionType === 'string') report.subscription = redactText(value.subscriptionType, 100)\r\n return report\r\n } catch {\r\n return { status: 'unknown', message: 'Claude authentication status was not valid JSON' }\r\n }\r\n}\r\n\r\nexport async function runClaudeDoctor(\r\n runtime: ExecutableRuntime,\r\n options: { configuredPath?: string; cwd: string; signal?: AbortSignal },\r\n): Promise<ClaudeDoctorReport> {\r\n let resolution: ClaudeExecutableResolution\r\n try {\r\n resolution = await resolveClaudeExecutable(runtime, options.configuredPath, options.signal)\r\n } catch (error) {\r\n if (error instanceof ClaudeExecutableNotFoundError) {\r\n return {\r\n executable: { status: 'missing', searched: error.searched },\r\n version: { status: 'not-run' },\r\n authentication: { status: 'not-run' },\r\n handshake: 'not-run',\r\n }\r\n }\r\n throw error\r\n }\r\n\r\n const report: ClaudeDoctorReport = {\r\n executable: { status: 'found', path: resolution.path, searched: resolution.searched },\r\n version: { status: 'not-run' },\r\n authentication: { status: 'not-run' },\r\n handshake: 'not-run',\r\n }\r\n try {\r\n report.version = {\r\n status: 'ok',\r\n value: await probeClaudeVersion(runtime, resolution.path, options.cwd, options.signal),\r\n }\r\n } catch (error) {\r\n report.version = {\r\n status: 'error',\r\n message: error instanceof Error ? error.message : 'Version probe failed',\r\n }\r\n }\r\n try {\r\n report.authentication = await probeClaudeAuthentication(runtime, resolution.path, options.cwd, options.signal)\r\n } catch (error) {\r\n report.authentication = {\r\n status: 'unknown',\r\n message: error instanceof Error ? error.message : 'Authentication probe failed',\r\n }\r\n }\r\n return report\r\n}\r\n","import { randomUUID } from 'node:crypto'\r\nimport { link, lstat, mkdir, readFile, readdir, rm, rmdir, writeFile } from 'node:fs/promises'\r\nimport { dirname, join } from 'node:path'\r\nimport { fileURLToPath } from 'node:url'\r\nimport { dshHomePath } from '@deepseek-ai/dsh-home-paths'\r\nimport { CLAUDE_CODE_PRESET_ID } from './constants.ts'\r\n\r\nexport const MANAGED_PRESET_FILES = ['agent.cordis.yml', 'preset.yml'] as const\r\n\r\n/** Package specifier kept in the shipped template. DSH Desktop's resolver hook\r\n * only rewrites bare specifiers issued by the root include; preset subtrees\r\n * resolve through Node's internal loader with an unrelated base and cannot\r\n * find linked packages. The installer therefore substitutes the absolute\r\n * built entry path, which the preset tree imports directly as a file URL. */\r\nconst PRESET_ROUTE_PACKAGE_SPECIFIER = '@norman-else/dsh-claude/preset-route'\r\n\r\nexport class ManagedPresetConflictError extends Error {\r\n readonly path: string\r\n\r\n constructor(path: string) {\r\n super(`dsh-claude: refusing to overwrite user-modified preset file ${path}`)\r\n this.name = 'ManagedPresetConflictError'\r\n this.path = path\r\n }\r\n}\r\n\r\nexport interface ManagedPresetPaths {\r\n sourceDir: string\r\n targetDir: string\r\n}\r\n\r\nexport function defaultManagedPresetPaths(dshHome?: string): ManagedPresetPaths {\r\n const packageRoot = fileURLToPath(new URL('../', import.meta.url))\r\n return {\r\n sourceDir: join(packageRoot, 'legacy-preset'),\r\n targetDir: dshHome === undefined\r\n ? dshHomePath('.agent-presets', CLAUDE_CODE_PRESET_ID)\r\n : join(dshHome, '.agent-presets', CLAUDE_CODE_PRESET_ID),\r\n }\r\n}\r\n\r\ninterface ManagedContent {\r\n file: string\r\n /** Content this installer version writes. */\r\n content: string\r\n /** Older installer-written contents that may be silently upgraded/removed. */\r\n legacy: readonly string[]\r\n /** Legacy detection for contents that predate the current template. */\r\n isLegacy(current: string): boolean\r\n}\r\n\r\nasync function managedContents(paths: ManagedPresetPaths): Promise<ManagedContent[]> {\r\n const routeEntry = join(paths.sourceDir, '..', 'lib', 'preset-route.mjs')\r\n return await Promise.all(MANAGED_PRESET_FILES.map(async (file): Promise<ManagedContent> => {\r\n const source = await readFile(join(paths.sourceDir, file), 'utf8')\r\n const nameRow = `name: '${PRESET_ROUTE_PACKAGE_SPECIFIER}'`\r\n const legacyNameRow = `name: ${PRESET_ROUTE_PACKAGE_SPECIFIER}`\r\n if (file !== 'agent.cordis.yml' || !source.includes(nameRow)) {\r\n return { file, content: source, legacy: [], isLegacy: () => false }\r\n }\r\n return {\r\n file,\r\n content: source.replace(nameRow, `name: '${routeEntry}'`),\r\n legacy: [source],\r\n // Any earlier installer generation wrote this same entry id with the\r\n // route reference in either supported form (bare package specifier or\r\n // absolute built-module path); treat those as safe to upgrade regardless\r\n // of comment or field drift in the template.\r\n isLegacy: current =>\r\n current.includes('id: claude-code-route')\r\n && (current.includes(nameRow)\r\n || current.includes(legacyNameRow)\r\n || current.includes('lib/preset-route.mjs')),\r\n }\r\n }))\r\n}\r\n\r\nasync function readIfPresent(path: string): Promise<string | undefined> {\r\n try {\r\n return await readFile(path, 'utf8')\r\n } catch (error) {\r\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined\r\n throw error\r\n }\r\n}\r\n\r\nasync function atomicWrite(path: string, content: string): Promise<boolean> {\r\n await mkdir(dirname(path), { recursive: true })\r\n const temporary = `${path}.${randomUUID()}.tmp`\r\n try {\r\n await writeFile(temporary, content, { encoding: 'utf8', mode: 0o600, flag: 'wx' })\r\n try {\r\n await link(temporary, path)\r\n return true\r\n } catch (error) {\r\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error\r\n if (await readIfPresent(path) === content) return false\r\n throw new ManagedPresetConflictError(path)\r\n }\r\n } finally {\r\n await rm(temporary, { force: true })\r\n }\r\n}\r\n\r\nexport async function ensureManagedPreset(paths = defaultManagedPresetPaths()): Promise<'installed' | 'unchanged'> {\r\n await assertSafeTargetDirectory(paths.targetDir)\r\n const expected = await managedContents(paths)\r\n let changed = false\r\n for (const { file, content, legacy, isLegacy } of expected) {\r\n const target = join(paths.targetDir, file)\r\n const current = await readIfPresent(target)\r\n if (current === content) continue\r\n if (current !== undefined) {\r\n // Upgrade installer-written legacy content in place; never touch user edits.\r\n if (!legacy.includes(current) && !isLegacy(current)) throw new ManagedPresetConflictError(target)\r\n await rm(target)\r\n }\r\n changed = await atomicWrite(target, content) || changed\r\n }\r\n return changed ? 'installed' : 'unchanged'\r\n}\r\n\r\n/** Reject a target directory that is a symlink (or occupies the path as a file)\r\n * so the managed preset never writes through an attacker-controlled link. */\r\nasync function assertSafeTargetDirectory(targetDir: string): Promise<void> {\r\n try {\r\n const stat = await lstat(targetDir)\r\n if (!stat.isDirectory() || stat.isSymbolicLink()) {\r\n throw new ManagedPresetConflictError(targetDir)\r\n }\r\n } catch (error) {\r\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return\r\n throw error\r\n }\r\n}\r\n\r\nexport async function removeManagedPreset(paths = defaultManagedPresetPaths()): Promise<'removed' | 'absent'> {\r\n await assertSafeTargetDirectory(paths.targetDir)\r\n const expected = await managedContents(paths)\r\n const managedTargets: string[] = []\r\n for (const { file, content, legacy, isLegacy } of expected) {\r\n const target = join(paths.targetDir, file)\r\n const current = await readIfPresent(target)\r\n if (current === undefined) continue\r\n if (current !== content && !legacy.includes(current) && !isLegacy(current)) throw new ManagedPresetConflictError(target)\r\n managedTargets.push(target)\r\n }\r\n for (const target of managedTargets) await rm(target)\r\n try {\r\n if ((await readdir(paths.targetDir)).length === 0) await rmdir(paths.targetDir)\r\n } catch (error) {\r\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\r\n }\r\n return managedTargets.length > 0 ? 'removed' : 'absent'\r\n}\r\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import { p as CLAUDE_CODE_PROVIDER } from "./events-qlmU1KrH.mjs";
2
- import { i as CLAUDE_COMMANDS_SERVICE, n as claudePresenterDefinitions } from "./presenters-DbfG-9KQ.mjs";
1
+ import { p as CLAUDE_CODE_PROVIDER } from "./events-DP0Ov_Re.mjs";
2
+ import { i as CLAUDE_COMMANDS_SERVICE, n as claudePresenterDefinitions } from "./presenters-YMR2WMjn.mjs";
3
3
  //#region src/preset-route.ts
4
4
  const name = "claude-code-preset-route";
5
5
  const inject = ["tools", "commands"];
@@ -1 +1 @@
1
- {"version":3,"file":"preset-route.mjs","names":[],"sources":["../src/preset-route.ts"],"sourcesContent":["import type { Context } from '@deepseek-ai/cordis'\nimport type {} from '@deepseek-ai/dsh-agent'\nimport type {} from '@deepseek-ai/dsh-commands'\nimport type {} from '@deepseek-ai/dsh-tools'\nimport { CLAUDE_CODE_PROVIDER } from './constants.ts'\nimport { CLAUDE_COMMANDS_SERVICE } from './command-bridge.ts'\nimport { claudePresenterDefinitions } from './presenters.ts'\n\nexport const name = 'claude-code-preset-route'\nexport const inject = ['tools', 'commands']\n\nexport interface Config {\n model?: string\n}\n\nexport function apply(ctx: Context, config: Config = {}): void {\n ctx.on('agent/request', async (_payload, next) => {\n const upstream = await next()\n return {\n ...upstream,\n provider: CLAUDE_CODE_PROVIDER,\n model: config.model ?? upstream.model ?? 'default',\n }\n })\n // Provide this agent-scope commands service so the host-side command\n // bridge can register Claude's catalog into exactly this agent's scope\n // layer. The preset row isolates the service per entry (per session); the\n // host reads it via serviceForAgent(ctx, agent, CLAUDE_COMMANDS_SERVICE).\n ctx.provide(CLAUDE_COMMANDS_SERVICE, {\n list: agent => ctx.commands.list(agent as never),\n register: definition => ctx.commands.register(definition),\n })\n // Presentation-only tool mirrors, scoped to this preset's agents: they let\n // the host compute native render intents for the mirrored Claude tool\n // events. Claude Code owns execution; the stub `execute` never runs.\n for (const definition of claudePresenterDefinitions()) {\n ctx.effect(() => ctx.tools.register(definition), `dsh-claude: ${definition.name} presentation`)\n }\n}\n"],"mappings":";;;AAQA,MAAa,OAAO;AACpB,MAAa,SAAS,CAAC,SAAS,UAAU;AAM1C,SAAgB,MAAM,KAAc,SAAiB,CAAC,GAAS;CAC7D,IAAI,GAAG,iBAAiB,OAAO,UAAU,SAAS;EAChD,MAAM,WAAW,MAAM,KAAK;EAC5B,OAAO;GACL,GAAG;GACH,UAAU;GACV,OAAO,OAAO,SAAS,SAAS,SAAS;EAC3C;CACF,CAAC;CAKD,IAAI,QAAQ,yBAAyB;EACnC,OAAM,UAAS,IAAI,SAAS,KAAK,KAAc;EAC/C,WAAU,eAAc,IAAI,SAAS,SAAS,UAAU;CAC1D,CAAC;CAID,KAAK,MAAM,cAAc,2BAA2B,GAClD,IAAI,aAAa,IAAI,MAAM,SAAS,UAAU,GAAG,eAAe,WAAW,KAAK,cAAc;AAElG"}
1
+ {"version":3,"file":"preset-route.mjs","names":[],"sources":["../src/preset-route.ts"],"sourcesContent":["import type { Context } from '@deepseek-ai/cordis'\r\nimport type {} from '@deepseek-ai/dsh-agent'\r\nimport type {} from '@deepseek-ai/dsh-commands'\r\nimport type {} from '@deepseek-ai/dsh-tools'\r\nimport { CLAUDE_CODE_PROVIDER } from './constants.ts'\r\nimport { CLAUDE_COMMANDS_SERVICE } from './command-bridge.ts'\r\nimport { claudePresenterDefinitions } from './presenters.ts'\r\n\r\nexport const name = 'claude-code-preset-route'\r\nexport const inject = ['tools', 'commands']\r\n\r\nexport interface Config {\r\n model?: string\r\n}\r\n\r\nexport function apply(ctx: Context, config: Config = {}): void {\r\n ctx.on('agent/request', async (_payload, next) => {\r\n const upstream = await next()\r\n return {\r\n ...upstream,\r\n provider: CLAUDE_CODE_PROVIDER,\r\n model: config.model ?? upstream.model ?? 'default',\r\n }\r\n })\r\n // Provide this agent-scope commands service so the host-side command\r\n // bridge can register Claude's catalog into exactly this agent's scope\r\n // layer. The preset row isolates the service per entry (per session); the\r\n // host reads it via serviceForAgent(ctx, agent, CLAUDE_COMMANDS_SERVICE).\r\n ctx.provide(CLAUDE_COMMANDS_SERVICE, {\r\n list: agent => ctx.commands.list(agent as never),\r\n register: definition => ctx.commands.register(definition),\r\n })\r\n // Presentation-only tool mirrors, scoped to this preset's agents: they let\r\n // the host compute native render intents for the mirrored Claude tool\r\n // events. Claude Code owns execution; the stub `execute` never runs.\r\n for (const definition of claudePresenterDefinitions()) {\r\n ctx.effect(() => ctx.tools.register(definition), `dsh-claude: ${definition.name} presentation`)\r\n }\r\n}\r\n"],"mappings":";;;AAQA,MAAa,OAAO;AACpB,MAAa,SAAS,CAAC,SAAS,UAAU;AAM1C,SAAgB,MAAM,KAAc,SAAiB,CAAC,GAAS;CAC7D,IAAI,GAAG,iBAAiB,OAAO,UAAU,SAAS;EAChD,MAAM,WAAW,MAAM,KAAK;EAC5B,OAAO;GACL,GAAG;GACH,UAAU;GACV,OAAO,OAAO,SAAS,SAAS,SAAS;EAC3C;CACF,CAAC;CAKD,IAAI,QAAQ,yBAAyB;EACnC,OAAM,UAAS,IAAI,SAAS,KAAK,KAAc;EAC/C,WAAU,eAAc,IAAI,SAAS,SAAS,UAAU;CAC1D,CAAC;CAID,KAAK,MAAM,cAAc,2BAA2B,GAClD,IAAI,aAAa,IAAI,MAAM,SAAS,UAAU,GAAG,eAAe,WAAW,KAAK,cAAc;AAElG"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@norman-else/dsh-claude",
3
3
  "description": "Run the local Claude Code CLI as a first-class main conversation inside DeepSeek Harness",
4
- "version": "0.1.5",
4
+ "version": "0.1.7",
5
5
  "type": "module",
6
6
  "main": "lib/index.mjs",
7
7
  "types": "lib/index.d.mts",
@@ -91,6 +91,7 @@
91
91
  "@deepseek-ai/dsh-subprocess": "*",
92
92
  "@deepseek-ai/dsh-tools": "*",
93
93
  "@deepseek-ai/dsh-user-approval": "*",
94
+ "@deepseek-ai/dsh-user-questions": "*",
94
95
  "@deepseek-ai/schemastery": "^3.18.1",
95
96
  "react": "^18.2.0",
96
97
  "react-dom": "^18.2.0"
@@ -114,6 +115,7 @@
114
115
  "@deepseek-ai/dsh-subprocess": "0.1.0-rc.6",
115
116
  "@deepseek-ai/dsh-tools": "0.1.0-rc.6",
116
117
  "@deepseek-ai/dsh-user-approval": "0.1.0-rc.6",
118
+ "@deepseek-ai/dsh-user-questions": "0.1.0-rc.6",
117
119
  "@deepseek-ai/schemastery": "3.18.1",
118
120
  "@types/node": "^22.20.0",
119
121
  "@types/react": "~18.3.1",
@@ -1,11 +1,11 @@
1
- # Shipped by dsh-claude. Claude Code owns the inner agent loop and tools;
2
- # this system preset contributes only the request-route override. DSH resolves
3
- # the bare package specifier from the host profile when mounting system roots.
4
- - id: claude-code-route
5
- name: '@norman-else/dsh-claude/preset-route'
6
- # Entry-local isolate realm: each session's preset mount gets its own
7
- # claudeCommands service instance (keeps mountPreset's leaked-service check
8
- # happy and prevents cross-session collisions). The host reads it through
9
- # dsh-agent-presets' serviceForAgent().
10
- isolate:
11
- claudeCommands: true
1
+ # Shipped by dsh-claude. Claude Code owns the inner agent loop and tools;
2
+ # this system preset contributes only the request-route override. DSH resolves
3
+ # the bare package specifier from the host profile when mounting system roots.
4
+ - id: claude-code-route
5
+ name: '@norman-else/dsh-claude/preset-route'
6
+ # Entry-local isolate realm: each session's preset mount gets its own
7
+ # claudeCommands service instance (keeps mountPreset's leaked-service check
8
+ # happy and prevents cross-session collisions). The host reads it through
9
+ # dsh-agent-presets' serviceForAgent().
10
+ isolate:
11
+ claudeCommands: true
@@ -1,4 +1,4 @@
1
- # Shipped by dsh-claude. Copy this system preset under a new id to customize it.
2
- name: Claude
3
- description: Use the local Claude Code as the complete agent runtime inside DSH.
4
- order: 10
1
+ # Shipped by dsh-claude. Copy this system preset under a new id to customize it.
2
+ name: Claude
3
+ description: Use the local Claude Code as the complete agent runtime inside DSH.
4
+ order: 10
@@ -1 +0,0 @@
1
- {"version":3,"file":"events-qlmU1KrH.mjs","names":[],"sources":["../src/constants.ts","../src/events.ts"],"sourcesContent":["export const CLAUDE_CODE_PROVIDER = 'claude'\nexport const CLAUDE_CODE_PRESET_ID = 'claude'\nexport const LEGACY_CLAUDE_CODE_PROVIDER = 'claude-code-cli'\nexport const LEGACY_CLAUDE_CODE_PRESET_ID = 'claude-code-cli'\nexport const CLAUDE_CODE_PROVIDER_IDS = [CLAUDE_CODE_PROVIDER, LEGACY_CLAUDE_CODE_PROVIDER] as const\nexport function isClaudePresetId(value: string | undefined): boolean {\n return value === CLAUDE_CODE_PRESET_ID || value === LEGACY_CLAUDE_CODE_PRESET_ID\n}\nexport function isClaudeProvider(value: string | undefined): boolean {\n return value === CLAUDE_CODE_PROVIDER || value === LEGACY_CLAUDE_CODE_PROVIDER\n}\nexport const CLAUDE_SESSION_BOUND_EVENT = 'claude-code/session-bound'\nexport const CLAUDE_ACTIVITY_EVENT = 'claude-code/activity'\nexport const CLAUDE_CONTEXT_USAGE_EVENT = 'claude-code/context-usage'\nexport const CLAUDE_TASKS_EVENT = 'claude-code/tasks'\n/** Claude's subagent dispatch tools; rendered as plugin-owned group cards\n * gathering subagent activity instead of native tool cards. */\nexport const TASK_TOOL_NAMES: ReadonlySet<string> = new Set(['Task', 'Agent'])\nexport const SDK_VERSION = '0.3.233'\nexport const CLAUDE_DOCTOR_PATH = '/plugins/dsh-claude/doctor'\nexport const CLAUDE_PROJECTION_PATH = '/plugins/dsh-claude/projection'\n","import type { SessionEvent } from '@deepseek-ai/dsh-session'\nimport {\n CLAUDE_ACTIVITY_EVENT,\n CLAUDE_CONTEXT_USAGE_EVENT,\n CLAUDE_SESSION_BOUND_EVENT,\n CLAUDE_TASKS_EVENT,\n} from './constants.ts'\n\nexport type ClaudeActivityKind =\n | 'status'\n | 'thinking'\n | 'tool-call'\n | 'tool-result'\n | 'permission'\n | 'subagent'\n | 'usage'\n | 'warning'\n | 'error'\n\nexport type ClaudeActivityPhase =\n | 'started'\n | 'updated'\n | 'completed'\n | 'denied'\n | 'failed'\n\nexport interface ClaudeUsage {\n inputTokens?: number\n outputTokens?: number\n cacheReadTokens?: number\n cacheCreationTokens?: number\n cumulativeCostUsd?: number\n}\n\nexport interface ClaudeSessionBoundEvent {\n claudeSessionId: string\n cliVersion?: string\n sdkVersion: string\n cwd: string\n}\n\nexport interface ClaudeActivityEvent {\n turn: number\n step: number\n ordinal: number\n kind: ClaudeActivityKind\n phase?: ClaudeActivityPhase\n /** Claude task-board identity for lifecycle activity; never a transcript path. */\n taskId?: string\n toolUseId?: string\n /** Enclosing Claude tool call for subagent-nested activity. */\n parentToolUseId?: string\n toolName?: string\n title?: string\n summary?: string\n detail?: string\n isError?: boolean\n usage?: ClaudeUsage\n}\n\nexport interface ClaudeContextUsageCategory {\n name: string\n tokens: number\n color: string\n isDeferred?: boolean\n}\n\nexport interface ClaudeContextUsageEvent {\n model: string\n totalTokens: number\n maxTokens: number\n percentage: number\n categories: readonly ClaudeContextUsageCategory[]\n}\n\nexport interface ClaudeContextUsageInput {\n model: unknown\n totalTokens: unknown\n maxTokens: unknown\n percentage: unknown\n categories: readonly {\n name?: unknown\n tokens?: unknown\n color?: unknown\n isDeferred?: unknown\n }[]\n}\n\ndeclare module '@deepseek-ai/dsh-session/types' {\n interface SessionEventMap {\n 'claude-code/session-bound': ClaudeSessionBoundEvent\n 'claude-code/activity': ClaudeActivityEvent\n 'claude-code/context-usage': ClaudeContextUsageEvent\n 'claude-code/tasks': ClaudeTasksEvent\n }\n}\n\nconst SECRET_KEY = /(?:^|[_-])(password|passwd|secret|token|api[_-]?key|authorization|credential|private[_-]?key|session[_-]?key|env|environ|environment)(?:$|[_-])/i\nconst MAX_SUMMARY_CHARS = 1_000\nconst MAX_DETAIL_CHARS = 4_000\nconst MAX_DEPTH = 6\nconst MAX_ARRAY_ITEMS = 40\nconst MAX_OBJECT_KEYS = 60\nconst REDACTED = '[REDACTED]'\nconst TRUNCATED = '…[truncated]'\nconst SECRET_ASSIGNMENT = /((?:password|passwd|secret|token|api[_-]?key|authorization|credential|private[_-]?key|session[_-]?key)\\s*(?:=|:)\\s*)(?:\"[^\"]*\"|'[^']*'|[^\\s,;&]+)/giu\nconst BEARER_TOKEN = /(\\bbearer\\s+)[A-Za-z0-9._~+/=-]+/giu\nconst PREFIXED_TOKEN = /\\b(?:sk-(?:ant-|proj-)?|xox[baprs]-|ghp_|github_pat_)[A-Za-z0-9_-]{8,}/giu\nconst JWT_TOKEN = /\\beyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\b/gu\nconst URL_USERINFO = /([a-z][a-z0-9+.-]*:\\/\\/[^:\\s/@]+:)[^@\\s/]+@/giu\nconst URL_SECRET_PARAM = /([?&](?:password|secret|token|api[_-]?key|access[_-]?token|refresh[_-]?token)=)[^&#\\s]+/giu\n\nexport function boundText(value: string, maxChars: number): string {\n if (value.length <= maxChars) return value\n return `${value.slice(0, Math.max(0, maxChars - TRUNCATED.length))}${TRUNCATED}`\n}\n\nexport function redactText(value: string, maxChars = MAX_DETAIL_CHARS): string {\n return boundText(\n value\n .replace(JWT_TOKEN, REDACTED)\n .replace(PREFIXED_TOKEN, REDACTED)\n .replace(BEARER_TOKEN, `$1${REDACTED}`)\n .replace(URL_USERINFO, `$1${REDACTED}@`)\n .replace(URL_SECRET_PARAM, `$1${REDACTED}`)\n .replace(SECRET_ASSIGNMENT, `$1${REDACTED}`),\n maxChars,\n )\n}\n\nexport function redactValue(value: unknown, depth = 0, seen = new WeakSet<object>()): unknown {\n if (depth > MAX_DEPTH) return '[max-depth]'\n if (value === null || typeof value === 'boolean' || typeof value === 'number') return value\n if (typeof value === 'string') return redactText(value)\n if (typeof value === 'bigint') return value.toString()\n if (typeof value === 'undefined') return null\n if (typeof value === 'function' || typeof value === 'symbol') return `[${typeof value}]`\n if (value instanceof Error) {\n return { name: value.name, message: redactText(value.message, MAX_SUMMARY_CHARS) }\n }\n if (typeof value !== 'object') return String(value)\n if (seen.has(value)) return '[circular]'\n seen.add(value)\n try {\n if (Array.isArray(value)) {\n const items = value.slice(0, MAX_ARRAY_ITEMS).map(item => redactValue(item, depth + 1, seen))\n if (value.length > MAX_ARRAY_ITEMS) items.push(`[${value.length - MAX_ARRAY_ITEMS} more items]`)\n return items\n }\n const result: Record<string, unknown> = {}\n const entries = Object.entries(value as Record<string, unknown>)\n for (const [key, item] of entries.slice(0, MAX_OBJECT_KEYS)) {\n result[key] = SECRET_KEY.test(key) ? REDACTED : redactValue(item, depth + 1, seen)\n }\n if (entries.length > MAX_OBJECT_KEYS) result.__truncatedKeys = entries.length - MAX_OBJECT_KEYS\n return result\n } finally {\n seen.delete(value)\n }\n}\n\nexport function safeDetail(value: unknown): string | undefined {\n if (value === undefined) return undefined\n const redacted = redactValue(value)\n const text = typeof redacted === 'string' ? redacted : JSON.stringify(redacted)\n return boundText(text, MAX_DETAIL_CHARS)\n}\n\nexport function normalizeActivity(\n activity: Omit<ClaudeActivityEvent, 'summary' | 'detail'> & {\n summary?: unknown\n detail?: unknown\n },\n): ClaudeActivityEvent {\n const normalized: ClaudeActivityEvent = {\n turn: activity.turn,\n step: activity.step,\n ordinal: activity.ordinal,\n kind: activity.kind,\n }\n if (activity.phase !== undefined) normalized.phase = activity.phase\n if (activity.taskId !== undefined) normalized.taskId = redactText(activity.taskId, 128)\n if (activity.toolUseId !== undefined) normalized.toolUseId = redactText(activity.toolUseId, 256)\n if (activity.parentToolUseId !== undefined) normalized.parentToolUseId = redactText(activity.parentToolUseId, 256)\n if (activity.toolName !== undefined) normalized.toolName = redactText(activity.toolName, 256)\n if (activity.title !== undefined) normalized.title = redactText(activity.title, MAX_SUMMARY_CHARS)\n if (activity.summary !== undefined) {\n normalized.summary = redactText(\n typeof activity.summary === 'string' ? activity.summary : safeDetail(activity.summary) ?? '',\n MAX_SUMMARY_CHARS,\n )\n }\n const detail = safeDetail(activity.detail)\n if (detail !== undefined) normalized.detail = detail\n if (activity.isError !== undefined) normalized.isError = activity.isError\n if (activity.usage !== undefined) normalized.usage = { ...activity.usage }\n return normalized\n}\n\nconst MAX_CONTEXT_CATEGORIES = 24\nconst FALLBACK_CONTEXT_COLOR = '#8b95a5'\nconst SAFE_CONTEXT_COLOR = /^#[0-9a-f]{3,8}$/iu\n\nfunction nonNegativeInteger(value: unknown): number {\n return typeof value === 'number' && Number.isFinite(value)\n ? Math.max(0, Math.floor(value))\n : 0\n}\n\nexport function normalizeContextUsage(input: ClaudeContextUsageInput): ClaudeContextUsageEvent {\n return {\n model: redactText(typeof input.model === 'string' ? input.model : 'unknown', 128),\n totalTokens: nonNegativeInteger(input.totalTokens),\n maxTokens: nonNegativeInteger(input.maxTokens),\n percentage: Math.min(100, nonNegativeInteger(input.percentage)),\n categories: input.categories.slice(0, MAX_CONTEXT_CATEGORIES).map(category => ({\n name: redactText(typeof category.name === 'string' ? category.name : 'Unknown', 128),\n tokens: nonNegativeInteger(category.tokens),\n color: typeof category.color === 'string' && SAFE_CONTEXT_COLOR.test(category.color)\n ? category.color\n : FALLBACK_CONTEXT_COLOR,\n ...(category.isDeferred === true ? { isDeferred: true } : {}),\n })),\n }\n}\n\nexport function latestClaudeContextUsage(\n events: readonly SessionEvent[],\n): ClaudeContextUsageEvent | undefined {\n for (let index = events.length - 1; index >= 0; index -= 1) {\n const event = events[index]\n if (event?.type === CLAUDE_CONTEXT_USAGE_EVENT) return event.data as ClaudeContextUsageEvent\n }\n return undefined\n}\n\nexport type ClaudeTaskStatus = 'running' | 'completed' | 'failed' | 'stopped' | 'killed'\n\nexport interface ClaudeTaskUsage {\n totalTokens?: number\n toolUses?: number\n durationMs?: number\n}\n\nexport interface ClaudeTaskInfo {\n taskId: string\n description: string\n status: ClaudeTaskStatus\n /** DSH turn during which this task was first observed, when known. */\n originTurn?: number\n subagentType?: string\n taskType?: string\n lastToolName?: string\n summary?: string\n usage?: ClaudeTaskUsage\n /** True while the task runs detached (background command/subagent). */\n backgrounded?: boolean\n}\n\n/** Level snapshot of one session's Claude task board, REPLACE semantics. */\nexport interface ClaudeTasksEvent {\n tasks: readonly ClaudeTaskInfo[]\n}\n\nconst MAX_TASKS_PER_SNAPSHOT = 50\nconst MAX_TASK_TEXT_CHARS = 300\n\nconst TASK_STATUSES: ReadonlySet<string> = new Set(['running', 'completed', 'failed', 'stopped', 'killed'])\n\nfunction normalizeTaskUsage(input: ClaudeTaskUsage | undefined): ClaudeTaskUsage | undefined {\n if (input === undefined) return undefined\n const usage: ClaudeTaskUsage = {}\n if (input.totalTokens !== undefined) usage.totalTokens = nonNegativeInteger(input.totalTokens)\n if (input.toolUses !== undefined) usage.toolUses = nonNegativeInteger(input.toolUses)\n if (input.durationMs !== undefined) usage.durationMs = nonNegativeInteger(input.durationMs)\n return Object.keys(usage).length === 0 ? undefined : usage\n}\n\nexport function normalizeTasksEvent(tasks: readonly ClaudeTaskInfo[]): ClaudeTasksEvent {\n return {\n tasks: tasks.slice(0, MAX_TASKS_PER_SNAPSHOT).map(task => {\n const usage = normalizeTaskUsage(task.usage)\n return {\n taskId: redactText(String(task.taskId), 128),\n description: redactText(String(task.description), MAX_TASK_TEXT_CHARS),\n status: TASK_STATUSES.has(task.status) ? task.status : 'running',\n ...(task.originTurn === undefined ? {} : { originTurn: nonNegativeInteger(task.originTurn) }),\n ...(task.subagentType === undefined ? {} : { subagentType: redactText(task.subagentType, 64) }),\n ...(task.taskType === undefined ? {} : { taskType: redactText(task.taskType, 64) }),\n ...(task.lastToolName === undefined ? {} : { lastToolName: redactText(task.lastToolName, 64) }),\n ...(task.summary === undefined ? {} : { summary: redactText(task.summary, MAX_TASK_TEXT_CHARS) }),\n ...(usage === undefined ? {} : { usage }),\n ...(task.backgrounded === true ? { backgrounded: true } : {}),\n }\n }),\n }\n}\n\nexport function latestClaudeTasks(\n events: readonly SessionEvent[],\n): ClaudeTasksEvent | undefined {\n for (let index = events.length - 1; index >= 0; index -= 1) {\n const event = events[index]\n if (event?.type === CLAUDE_TASKS_EVENT) return event.data as ClaudeTasksEvent\n }\n return undefined\n}\n\nexport type ClaudeActivityInput = Omit<\n ClaudeActivityEvent,\n 'turn' | 'step' | 'ordinal' | 'summary' | 'detail'\n> & {\n summary?: unknown\n detail?: unknown\n}\n\nexport interface ClaudeActivityCursor {\n turn: number\n step: number\n nextOrdinal: number\n}\n\n/** Derive the current DSH turn/step; activity ordinals are completed from the sidecar. */\nexport function currentClaudeActivityCursor(events: readonly SessionEvent[]): ClaudeActivityCursor {\n let turn = 0\n let step = 0\n for (const event of events) {\n if (event.type !== 'step/start') continue\n const data = event.data as { turn: number; step: number }\n turn = data.turn\n step = data.step\n }\n if (turn < 1 || step < 1) {\n throw new Error('dsh-claude: Claude activity requires an open DSH step')\n }\n return { turn, step, nextOrdinal: 0 }\n}\n\nexport function latestClaudeSessionBinding(\n events: readonly SessionEvent[],\n): ClaudeSessionBoundEvent | undefined {\n for (let index = events.length - 1; index >= 0; index -= 1) {\n const event = events[index]\n if (event?.type === CLAUDE_SESSION_BOUND_EVENT) {\n return event.data as ClaudeSessionBoundEvent\n }\n }\n return undefined\n}\n"],"mappings":";AAAA,MAAa,uBAAuB;AACpC,MAAa,wBAAwB;AACrC,MAAa,8BAA8B;AAC3C,MAAa,+BAA+B;AAC5C,MAAa,2BAA2B,CAAC,sBAAsB,2BAA2B;AAC1F,SAAgB,iBAAiB,OAAoC;CACnE,OAAO,UAAA,YAAmC,UAAA;AAC5C;AAKA,MAAa,wBAAwB;;;AAKrC,MAAa,kCAAuC,IAAI,IAAI,CAAC,QAAQ,OAAO,CAAC;AAC7E,MAAa,cAAc;AAC3B,MAAa,qBAAqB;AAClC,MAAa,yBAAyB;;;AC6EtC,MAAM,aAAa;AACnB,MAAM,oBAAoB;AAC1B,MAAM,mBAAmB;AACzB,MAAM,YAAY;AAClB,MAAM,kBAAkB;AACxB,MAAM,kBAAkB;AACxB,MAAM,WAAW;AACjB,MAAM,YAAY;AAClB,MAAM,oBAAoB;AAC1B,MAAM,eAAe;AACrB,MAAM,iBAAiB;AACvB,MAAM,YAAY;AAClB,MAAM,eAAe;AACrB,MAAM,mBAAmB;AAEzB,SAAgB,UAAU,OAAe,UAA0B;CACjE,IAAI,MAAM,UAAU,UAAU,OAAO;CACrC,OAAO,GAAG,MAAM,MAAM,GAAG,KAAK,IAAI,GAAG,WAAW,EAAgB,CAAC,IAAI;AACvE;AAEA,SAAgB,WAAW,OAAe,WAAW,kBAA0B;CAC7E,OAAO,UACL,MACG,QAAQ,WAAW,QAAQ,CAAC,CAC5B,QAAQ,gBAAgB,QAAQ,CAAC,CACjC,QAAQ,cAAc,KAAK,UAAU,CAAC,CACtC,QAAQ,cAAc,KAAK,SAAS,EAAE,CAAC,CACvC,QAAQ,kBAAkB,KAAK,UAAU,CAAC,CAC1C,QAAQ,mBAAmB,KAAK,UAAU,GAC7C,QACF;AACF;AAEA,SAAgB,YAAY,OAAgB,QAAQ,GAAG,uBAAO,IAAI,QAAgB,GAAY;CAC5F,IAAI,QAAQ,WAAW,OAAO;CAC9B,IAAI,UAAU,QAAQ,OAAO,UAAU,aAAa,OAAO,UAAU,UAAU,OAAO;CACtF,IAAI,OAAO,UAAU,UAAU,OAAO,WAAW,KAAK;CACtD,IAAI,OAAO,UAAU,UAAU,OAAO,MAAM,SAAS;CACrD,IAAI,OAAO,UAAU,aAAa,OAAO;CACzC,IAAI,OAAO,UAAU,cAAc,OAAO,UAAU,UAAU,OAAO,IAAI,OAAO,MAAM;CACtF,IAAI,iBAAiB,OACnB,OAAO;EAAE,MAAM,MAAM;EAAM,SAAS,WAAW,MAAM,SAAS,iBAAiB;CAAE;CAEnF,IAAI,OAAO,UAAU,UAAU,OAAO,OAAO,KAAK;CAClD,IAAI,KAAK,IAAI,KAAK,GAAG,OAAO;CAC5B,KAAK,IAAI,KAAK;CACd,IAAI;EACF,IAAI,MAAM,QAAQ,KAAK,GAAG;GACxB,MAAM,QAAQ,MAAM,MAAM,GAAG,eAAe,CAAC,CAAC,KAAI,SAAQ,YAAY,MAAM,QAAQ,GAAG,IAAI,CAAC;GAC5F,IAAI,MAAM,SAAS,iBAAiB,MAAM,KAAK,IAAI,MAAM,SAAS,gBAAgB,aAAa;GAC/F,OAAO;EACT;EACA,MAAM,SAAkC,CAAC;EACzC,MAAM,UAAU,OAAO,QAAQ,KAAgC;EAC/D,KAAK,MAAM,CAAC,KAAK,SAAS,QAAQ,MAAM,GAAG,eAAe,GACxD,OAAO,OAAO,WAAW,KAAK,GAAG,IAAI,WAAW,YAAY,MAAM,QAAQ,GAAG,IAAI;EAEnF,IAAI,QAAQ,SAAS,iBAAiB,OAAO,kBAAkB,QAAQ,SAAS;EAChF,OAAO;CACT,UAAU;EACR,KAAK,OAAO,KAAK;CACnB;AACF;AAEA,SAAgB,WAAW,OAAoC;CAC7D,IAAI,UAAU,KAAA,GAAW,OAAO,KAAA;CAChC,MAAM,WAAW,YAAY,KAAK;CAElC,OAAO,UADM,OAAO,aAAa,WAAW,WAAW,KAAK,UAAU,QAAQ,GACvD,gBAAgB;AACzC;AAEA,SAAgB,kBACd,UAIqB;CACrB,MAAM,aAAkC;EACtC,MAAM,SAAS;EACf,MAAM,SAAS;EACf,SAAS,SAAS;EAClB,MAAM,SAAS;CACjB;CACA,IAAI,SAAS,UAAU,KAAA,GAAW,WAAW,QAAQ,SAAS;CAC9D,IAAI,SAAS,WAAW,KAAA,GAAW,WAAW,SAAS,WAAW,SAAS,QAAQ,GAAG;CACtF,IAAI,SAAS,cAAc,KAAA,GAAW,WAAW,YAAY,WAAW,SAAS,WAAW,GAAG;CAC/F,IAAI,SAAS,oBAAoB,KAAA,GAAW,WAAW,kBAAkB,WAAW,SAAS,iBAAiB,GAAG;CACjH,IAAI,SAAS,aAAa,KAAA,GAAW,WAAW,WAAW,WAAW,SAAS,UAAU,GAAG;CAC5F,IAAI,SAAS,UAAU,KAAA,GAAW,WAAW,QAAQ,WAAW,SAAS,OAAO,iBAAiB;CACjG,IAAI,SAAS,YAAY,KAAA,GACvB,WAAW,UAAU,WACnB,OAAO,SAAS,YAAY,WAAW,SAAS,UAAU,WAAW,SAAS,OAAO,KAAK,IAC1F,iBACF;CAEF,MAAM,SAAS,WAAW,SAAS,MAAM;CACzC,IAAI,WAAW,KAAA,GAAW,WAAW,SAAS;CAC9C,IAAI,SAAS,YAAY,KAAA,GAAW,WAAW,UAAU,SAAS;CAClE,IAAI,SAAS,UAAU,KAAA,GAAW,WAAW,QAAQ,EAAE,GAAG,SAAS,MAAM;CACzE,OAAO;AACT;AAEA,MAAM,yBAAyB;AAC/B,MAAM,yBAAyB;AAC/B,MAAM,qBAAqB;AAE3B,SAAS,mBAAmB,OAAwB;CAClD,OAAO,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,IACrD,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,CAAC,IAC7B;AACN;AAEA,SAAgB,sBAAsB,OAAyD;CAC7F,OAAO;EACL,OAAO,WAAW,OAAO,MAAM,UAAU,WAAW,MAAM,QAAQ,WAAW,GAAG;EAChF,aAAa,mBAAmB,MAAM,WAAW;EACjD,WAAW,mBAAmB,MAAM,SAAS;EAC7C,YAAY,KAAK,IAAI,KAAK,mBAAmB,MAAM,UAAU,CAAC;EAC9D,YAAY,MAAM,WAAW,MAAM,GAAG,sBAAsB,CAAC,CAAC,KAAI,cAAa;GAC7E,MAAM,WAAW,OAAO,SAAS,SAAS,WAAW,SAAS,OAAO,WAAW,GAAG;GACnF,QAAQ,mBAAmB,SAAS,MAAM;GAC1C,OAAO,OAAO,SAAS,UAAU,YAAY,mBAAmB,KAAK,SAAS,KAAK,IAC/E,SAAS,QACT;GACJ,GAAI,SAAS,eAAe,OAAO,EAAE,YAAY,KAAK,IAAI,CAAC;EAC7D,EAAE;CACJ;AACF;AAEA,SAAgB,yBACd,QACqC;CACrC,KAAK,IAAI,QAAQ,OAAO,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG;EAC1D,MAAM,QAAQ,OAAO;EACrB,IAAI,OAAO,SAAA,6BAAqC,OAAO,MAAM;CAC/D;AAEF;AA8BA,MAAM,yBAAyB;AAC/B,MAAM,sBAAsB;AAE5B,MAAM,gCAAqC,IAAI,IAAI;CAAC;CAAW;CAAa;CAAU;CAAW;AAAQ,CAAC;AAE1G,SAAS,mBAAmB,OAAiE;CAC3F,IAAI,UAAU,KAAA,GAAW,OAAO,KAAA;CAChC,MAAM,QAAyB,CAAC;CAChC,IAAI,MAAM,gBAAgB,KAAA,GAAW,MAAM,cAAc,mBAAmB,MAAM,WAAW;CAC7F,IAAI,MAAM,aAAa,KAAA,GAAW,MAAM,WAAW,mBAAmB,MAAM,QAAQ;CACpF,IAAI,MAAM,eAAe,KAAA,GAAW,MAAM,aAAa,mBAAmB,MAAM,UAAU;CAC1F,OAAO,OAAO,KAAK,KAAK,CAAC,CAAC,WAAW,IAAI,KAAA,IAAY;AACvD;AAEA,SAAgB,oBAAoB,OAAoD;CACtF,OAAO,EACL,OAAO,MAAM,MAAM,GAAG,sBAAsB,CAAC,CAAC,KAAI,SAAQ;EACxD,MAAM,QAAQ,mBAAmB,KAAK,KAAK;EAC3C,OAAO;GACL,QAAQ,WAAW,OAAO,KAAK,MAAM,GAAG,GAAG;GAC3C,aAAa,WAAW,OAAO,KAAK,WAAW,GAAG,mBAAmB;GACrE,QAAQ,cAAc,IAAI,KAAK,MAAM,IAAI,KAAK,SAAS;GACvD,GAAI,KAAK,eAAe,KAAA,IAAY,CAAC,IAAI,EAAE,YAAY,mBAAmB,KAAK,UAAU,EAAE;GAC3F,GAAI,KAAK,iBAAiB,KAAA,IAAY,CAAC,IAAI,EAAE,cAAc,WAAW,KAAK,cAAc,EAAE,EAAE;GAC7F,GAAI,KAAK,aAAa,KAAA,IAAY,CAAC,IAAI,EAAE,UAAU,WAAW,KAAK,UAAU,EAAE,EAAE;GACjF,GAAI,KAAK,iBAAiB,KAAA,IAAY,CAAC,IAAI,EAAE,cAAc,WAAW,KAAK,cAAc,EAAE,EAAE;GAC7F,GAAI,KAAK,YAAY,KAAA,IAAY,CAAC,IAAI,EAAE,SAAS,WAAW,KAAK,SAAS,mBAAmB,EAAE;GAC/F,GAAI,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM;GACvC,GAAI,KAAK,iBAAiB,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;EAC7D;CACF,CAAC,EACH;AACF;AAEA,SAAgB,kBACd,QAC8B;CAC9B,KAAK,IAAI,QAAQ,OAAO,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG;EAC1D,MAAM,QAAQ,OAAO;EACrB,IAAI,OAAO,SAAA,qBAA6B,OAAO,MAAM;CACvD;AAEF;;AAiBA,SAAgB,4BAA4B,QAAuD;CACjG,IAAI,OAAO;CACX,IAAI,OAAO;CACX,KAAK,MAAM,SAAS,QAAQ;EAC1B,IAAI,MAAM,SAAS,cAAc;EACjC,MAAM,OAAO,MAAM;EACnB,OAAO,KAAK;EACZ,OAAO,KAAK;CACd;CACA,IAAI,OAAO,KAAK,OAAO,GACrB,MAAM,IAAI,MAAM,uDAAuD;CAEzE,OAAO;EAAE;EAAM;EAAM,aAAa;CAAE;AACtC;AAEA,SAAgB,2BACd,QACqC;CACrC,KAAK,IAAI,QAAQ,OAAO,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG;EAC1D,MAAM,QAAQ,OAAO;EACrB,IAAI,OAAO,SAAA,6BACT,OAAO,MAAM;CAEjB;AAEF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"presenters-DbfG-9KQ.mjs","names":["#target","#live"],"sources":["../src/command-bridge.ts","../src/presenters.ts"],"sourcesContent":["import type { SlashCommand } from '@anthropic-ai/claude-agent-sdk'\nimport type { CommandDefinition, CommandDescriptor } from '@deepseek-ai/dsh-commands'\nimport { redactText } from './events.ts'\n\nconst COMMAND_NAME = /^[a-z][a-z0-9_-]*$/u\nconst MAX_DESCRIPTION_CHARS = 300\nconst MAX_HINT_CHARS = 120\n\n/** DSH registry names reject `:`, but plugin-qualified Claude skills carry it\n * (e.g. `awesome-skills:ci-deploy`). Derive a registry-safe public name while\n * forwarding keeps the exact Claude-side name. */\nfunction registryName(claudeName: string): string | undefined {\n if (COMMAND_NAME.test(claudeName)) return claudeName\n const short = claudeName.includes(':') ? claudeName.slice(claudeName.lastIndexOf(':') + 1) : undefined\n if (short !== undefined && COMMAND_NAME.test(short)) return short\n const sanitized = claudeName.replaceAll(':', '-').toLowerCase()\n return COMMAND_NAME.test(sanitized) ? sanitized : undefined\n}\n\n/**\n * Names registered as CLIENT-side commandUi contributions (not host\n * commands). The DSH web command palette fails its whole command group when\n * a host command name equals a contribution name\n * (\"contribution /model collides with a host command\"), so these can never\n * be registered by this bridge. Keep in sync with the client bundles:\n * dsh-client-ui-model-selection registers \"model\".\n */\nconst CLIENT_CONTRIBUTION_NAMES: ReadonlySet<string> = new Set(['model'])\n\nexport interface ClaudeCommandTarget {\n list(): readonly Pick<CommandDescriptor, 'name'>[]\n register(definition: CommandDefinition): () => void\n forward(line: string): void\n}\n\n/**\n * Agent-scope command service contract. The commands service is unreachable\n * from the host plugin's view of `agent.ctx` (\"without inject\"), but the\n * preset route plugin runs INSIDE the agent's preset composition with\n * `commands` injected. Registrations made through the provided service land\n * in the agent's scope layer, which the command registry inherits into\n * `list(agent)` for exactly that agent — invisible to every other session.\n */\nexport interface ClaudeAgentCommandService {\n list(agent: unknown): readonly Pick<CommandDescriptor, 'name'>[]\n register(definition: CommandDefinition): () => void\n}\n\n/**\n * Cordis service name the preset route provides (behind an entry-local\n * isolate realm, so each session gets its own instance) and the host reads\n * back with dsh-agent-presets' official `serviceForAgent(ctx, agent, name)`\n * — the supported cross-scope read for callers that already hold the agent.\n */\nexport const CLAUDE_COMMANDS_SERVICE = 'claudeCommands'\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n claudeCommands?: ClaudeAgentCommandService\n }\n}\n\nexport interface ClaudeCommandView {\n publicName: string\n claudeName: string\n prefixed: boolean\n}\n\ninterface DesiredCommand extends ClaudeCommandView {\n signature: string\n definition: CommandDefinition\n}\n\ninterface LiveCommand {\n signature: string\n dispose: () => void\n}\n\nfunction bounded(value: string, maxChars: number): string {\n return redactText(value, maxChars)\n}\n\nfunction desiredCommands(\n catalog: readonly SlashCommand[],\n reservedNames: ReadonlySet<string>,\n forward: (line: string) => void,\n): Map<string, DesiredCommand> {\n const desired = new Map<string, DesiredCommand>()\n const assigned = new Set<string>()\n for (const command of catalog) {\n const names = [command.name, ...(command.aliases ?? [])]\n for (const claudeName of names) {\n const base = registryName(claudeName)\n if (base === undefined) continue\n let publicName = base\n let prefixed = false\n if (reservedNames.has(publicName) || assigned.has(publicName)) {\n publicName = `claude-${base}`\n prefixed = true\n }\n if (!COMMAND_NAME.test(publicName) || reservedNames.has(publicName) || assigned.has(publicName)) continue\n const description = bounded(command.description || `Claude Code /${command.name}`, MAX_DESCRIPTION_CHARS)\n const hint = bounded(command.argumentHint ?? '', MAX_HINT_CHARS)\n const signature = JSON.stringify({ publicName, claudeName, description, hint })\n desired.set(publicName, {\n publicName,\n claudeName,\n prefixed,\n signature,\n definition: {\n name: publicName,\n description,\n ...(hint.length === 0 ? {} : { input: { hint } }),\n recordInput: false,\n handler: ({ rawInput }) => {\n forward(`/${claudeName}${rawInput}`)\n return { kind: 'success' }\n },\n },\n })\n assigned.add(publicName)\n }\n }\n return desired\n}\n\nexport class ClaudeCommandBridge {\n readonly #target: ClaudeCommandTarget\n readonly #live = new Map<string, LiveCommand>()\n\n constructor(target: ClaudeCommandTarget) {\n this.#target = target\n }\n\n refresh(catalog: readonly SlashCommand[]): readonly ClaudeCommandView[] {\n const owned = new Set(this.#live.keys())\n const reserved = new Set(\n this.#target.list()\n .map(command => command.name)\n .filter(name => !owned.has(name)),\n )\n // Client-side commandUi contributions (e.g. /model from\n // dsh-client-ui-model-selection) are invisible to the host registry, but\n // the web palette throws away its ENTIRE command group when a host\n // command collides with a contribution. Reserve those names so Claude's\n // same-named commands take the claude- prefix instead.\n for (const name of CLIENT_CONTRIBUTION_NAMES) reserved.add(name)\n const desired = desiredCommands(catalog, reserved, line => this.#target.forward(line))\n\n for (const [name, live] of [...this.#live]) {\n const next = desired.get(name)\n if (next?.signature === live.signature) continue\n live.dispose()\n this.#live.delete(name)\n }\n for (const [name, next] of desired) {\n if (this.#live.has(name)) continue\n this.#live.set(name, {\n signature: next.signature,\n dispose: this.#target.register(next.definition),\n })\n }\n return [...desired.values()].map(({ publicName, claudeName, prefixed }) => ({\n publicName,\n claudeName,\n prefixed,\n }))\n }\n\n dispose(): void {\n for (const command of this.#live.values()) command.dispose()\n this.#live.clear()\n }\n}\n","/**\n * Presentation-only tool definitions for the Claude Code preset. Claude Code\n * owns tool execution; these definitions exist solely so the host's tool\n * presentation pipeline (`viewFor` → `presentCall`/`presentResult`) can\n * compute native render intents for the mirrored `tool/call`/`tool/result`\n * events, giving Claude tool rows the same cards DSH-executed tools get.\n */\nimport type { ToolDefinition, ToolResult } from '@deepseek-ai/dsh-tools'\nimport type { ToolCallView, ToolResultView } from '@deepseek-ai/dsh-tools/presentation'\n\nfunction record(value: unknown): Record<string, unknown> | undefined {\n return value !== null && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : undefined\n}\n\nfunction str(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined\n}\n\nfunction resultText(result: ToolResult): string {\n return result.content\n .filter((block): block is Extract<typeof block, { type: 'text' }> => block.type === 'text')\n .map(block => block.text)\n .join('\\n')\n}\n\n/** Bash: a terminal card headed by the command, described above by Claude's own summary. */\nexport function bashCallView(args: unknown): ToolCallView | undefined {\n const command = str(record(args)?.command)\n if (command === undefined) return undefined\n const description = str(record(args)?.description)\n return { card: 'terminal', title: command, ...(description === undefined ? {} : { description }) }\n}\n\nexport function terminalResultView(_args: unknown, result: ToolResult): ToolResultView | undefined {\n const output = resultText(result)\n return output.length === 0 ? undefined : { card: 'terminal', output }\n}\n\n/** Read: a generic read card that follow-alongs the file window. */\nexport function readCallView(args: unknown): ToolCallView | undefined {\n const arguments_ = record(args)\n const path = str(arguments_?.file_path)\n if (path === undefined) return undefined\n const offset = typeof arguments_?.offset === 'number' ? arguments_.offset : undefined\n return {\n card: 'generic',\n kind: 'read',\n title: `Read ${path}`,\n locations: [{ path, ...(offset === undefined ? {} : { line: offset }) }],\n }\n}\n\nfunction fileDiffs(args: unknown): { path: string; oldText: string | null; newText: string }[] | undefined {\n const arguments_ = record(args)\n const path = str(arguments_?.file_path)\n if (path === undefined) return undefined\n if (Array.isArray(arguments_?.edits)) {\n const diffs: { path: string; oldText: string | null; newText: string }[] = []\n for (const edit of arguments_.edits) {\n const item = record(edit)\n const oldText = str(item?.old_string)\n const newText = str(item?.new_string)\n if (newText === undefined) continue\n diffs.push({ path, oldText: oldText ?? null, newText })\n }\n return diffs.length > 0 ? diffs : undefined\n }\n const newText = str(arguments_?.new_string) ?? arguments_?.content\n if (typeof newText !== 'string' || newText.length === 0) return undefined\n const oldText = str(arguments_?.old_string)\n return [{ path, oldText: oldText ?? null, newText }]\n}\n\n/** Edit / MultiEdit / Write: inline diff cards derived from the call arguments. */\nexport function diffCallView(args: unknown): ToolCallView | undefined {\n const arguments_ = record(args)\n const path = str(arguments_?.file_path)\n const diffs = fileDiffs(args)\n if (path === undefined || diffs === undefined) return undefined\n const verb = typeof arguments_?.new_string === 'string' || Array.isArray(arguments_?.edits) ? 'Edit' : 'Write'\n return { card: 'diff', title: `${verb} ${path}`, diffs, locations: [{ path }] }\n}\n\n/** Grep / Glob / WebSearch: search-category cards titled by the query. */\nexport function searchCallView(args: unknown): ToolCallView | undefined {\n const arguments_ = record(args)\n const query = str(arguments_?.pattern) ?? str(arguments_?.query)\n if (query === undefined) return undefined\n const scope = str(arguments_?.path)\n return {\n card: 'generic',\n kind: 'search',\n title: scope === undefined ? query : `${query} · ${scope}`,\n rawInput: args,\n }\n}\n\n/** WebFetch: a fetch-category card titled by the URL. */\nexport function fetchCallView(args: unknown): ToolCallView | undefined {\n const url = str(record(args)?.url)\n if (url === undefined) return undefined\n return { card: 'generic', kind: 'fetch', title: url, rawInput: args }\n}\n\n/** Task: a subagent card titled by Claude's task description. */\nexport function taskCallView(args: unknown): ToolCallView | undefined {\n const arguments_ = record(args)\n const title = str(arguments_?.description) ?? str(arguments_?.subagent_type) ?? 'Subagent'\n return { card: 'generic', kind: 'other', title, rawInput: args }\n}\n\nfunction genericTitle(title: string): ToolCallView {\n return { card: 'generic', kind: 'other', title }\n}\n\nfunction presenterDefinition(\n name: string,\n description: string,\n presentCall?: (args: unknown) => ToolCallView | undefined,\n presentResult?: (args: unknown, result: ToolResult) => ToolResultView | undefined,\n): ToolDefinition {\n return {\n name,\n description,\n parameters: { type: 'object', properties: {} },\n output: {\n schema: { type: 'object', properties: {} } as ToolDefinition['output']['schema'],\n render: () => [],\n },\n execute: async () => {\n throw new Error(`dsh-claude: Claude Code owns execution of ${name}`)\n },\n ...(presentCall === undefined ? {} : { presentCall }),\n ...(presentResult === undefined ? {} : { presentResult }),\n }\n}\n\nconst PRESENTATION_NOTE = 'Presentation mirror of the Claude Code tool; execution is owned by Claude Code.'\n\n/** Generic call view for dynamically observed tools (MCP tools, new built-ins). */\nexport function genericCallView(toolName: string): (args: unknown) => ToolCallView | undefined {\n return (args: unknown) => {\n const description = str(record(args)?.description)\n return { card: 'generic', kind: 'other', title: description ?? toolName, rawInput: args }\n }\n}\n\n/** One presenter-only mirror for a tool name discovered at runtime. */\nexport function dynamicPresenterDefinition(name: string): ToolDefinition {\n return presenterDefinition(name, PRESENTATION_NOTE, genericCallView(name))\n}\n\n/** The presentation-only registry contributed to the Claude Code preset scope. */\nexport function claudePresenterDefinitions(): ToolDefinition[] {\n return [\n presenterDefinition('Bash', PRESENTATION_NOTE, bashCallView, terminalResultView),\n presenterDefinition('Read', PRESENTATION_NOTE, readCallView),\n presenterDefinition('Edit', PRESENTATION_NOTE, diffCallView),\n presenterDefinition('MultiEdit', PRESENTATION_NOTE, diffCallView),\n presenterDefinition('Write', PRESENTATION_NOTE, diffCallView),\n presenterDefinition('NotebookEdit', PRESENTATION_NOTE, args => {\n const path = str(record(args)?.notebook_path)\n return path === undefined ? undefined : genericTitle(`NotebookEdit ${path}`)\n }),\n presenterDefinition('Grep', PRESENTATION_NOTE, searchCallView),\n presenterDefinition('Glob', PRESENTATION_NOTE, searchCallView),\n presenterDefinition('WebSearch', PRESENTATION_NOTE, searchCallView),\n presenterDefinition('WebFetch', PRESENTATION_NOTE, fetchCallView),\n presenterDefinition('Task', PRESENTATION_NOTE, taskCallView),\n presenterDefinition('TodoWrite', PRESENTATION_NOTE, () => genericTitle('Update todos')),\n ]\n}\n\n/** Names covered by the static preset-scope registry; dynamic mirrors skip them. */\nexport const CLAUDE_PRESENTER_NAMES: ReadonlySet<string> = new Set(claudePresenterDefinitions().map(definition => definition.name))\n"],"mappings":";;AAIA,MAAM,eAAe;AACrB,MAAM,wBAAwB;AAC9B,MAAM,iBAAiB;;;;AAKvB,SAAS,aAAa,YAAwC;CAC5D,IAAI,aAAa,KAAK,UAAU,GAAG,OAAO;CAC1C,MAAM,QAAQ,WAAW,SAAS,GAAG,IAAI,WAAW,MAAM,WAAW,YAAY,GAAG,IAAI,CAAC,IAAI,KAAA;CAC7F,IAAI,UAAU,KAAA,KAAa,aAAa,KAAK,KAAK,GAAG,OAAO;CAC5D,MAAM,YAAY,WAAW,WAAW,KAAK,GAAG,CAAC,CAAC,YAAY;CAC9D,OAAO,aAAa,KAAK,SAAS,IAAI,YAAY,KAAA;AACpD;;;;;;;;;AAUA,MAAM,4CAAiD,IAAI,IAAI,CAAC,OAAO,CAAC;;;;;;;AA2BxE,MAAa,0BAA0B;AAwBvC,SAAS,QAAQ,OAAe,UAA0B;CACxD,OAAO,WAAW,OAAO,QAAQ;AACnC;AAEA,SAAS,gBACP,SACA,eACA,SAC6B;CAC7B,MAAM,0BAAU,IAAI,IAA4B;CAChD,MAAM,2BAAW,IAAI,IAAY;CACjC,KAAK,MAAM,WAAW,SAAS;EAC7B,MAAM,QAAQ,CAAC,QAAQ,MAAM,GAAI,QAAQ,WAAW,CAAC,CAAE;EACvD,KAAK,MAAM,cAAc,OAAO;GAC9B,MAAM,OAAO,aAAa,UAAU;GACpC,IAAI,SAAS,KAAA,GAAW;GACxB,IAAI,aAAa;GACjB,IAAI,WAAW;GACf,IAAI,cAAc,IAAI,UAAU,KAAK,SAAS,IAAI,UAAU,GAAG;IAC7D,aAAa,UAAU;IACvB,WAAW;GACb;GACA,IAAI,CAAC,aAAa,KAAK,UAAU,KAAK,cAAc,IAAI,UAAU,KAAK,SAAS,IAAI,UAAU,GAAG;GACjG,MAAM,cAAc,QAAQ,QAAQ,eAAe,gBAAgB,QAAQ,QAAQ,qBAAqB;GACxG,MAAM,OAAO,QAAQ,QAAQ,gBAAgB,IAAI,cAAc;GAC/D,MAAM,YAAY,KAAK,UAAU;IAAE;IAAY;IAAY;IAAa;GAAK,CAAC;GAC9E,QAAQ,IAAI,YAAY;IACtB;IACA;IACA;IACA;IACA,YAAY;KACV,MAAM;KACN;KACA,GAAI,KAAK,WAAW,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE;KAC/C,aAAa;KACb,UAAU,EAAE,eAAe;MACzB,QAAQ,IAAI,aAAa,UAAU;MACnC,OAAO,EAAE,MAAM,UAAU;KAC3B;IACF;GACF,CAAC;GACD,SAAS,IAAI,UAAU;EACzB;CACF;CACA,OAAO;AACT;AAEA,IAAa,sBAAb,MAAiC;CAC/B;CACA,wBAAiB,IAAI,IAAyB;CAE9C,YAAY,QAA6B;EACvC,KAAKA,UAAU;CACjB;CAEA,QAAQ,SAAgE;EACtE,MAAM,QAAQ,IAAI,IAAI,KAAKC,MAAM,KAAK,CAAC;EACvC,MAAM,WAAW,IAAI,IACnB,KAAKD,QAAQ,KAAK,CAAC,CAChB,KAAI,YAAW,QAAQ,IAAI,CAAC,CAC5B,QAAO,SAAQ,CAAC,MAAM,IAAI,IAAI,CAAC,CACpC;EAMA,KAAK,MAAM,QAAQ,2BAA2B,SAAS,IAAI,IAAI;EAC/D,MAAM,UAAU,gBAAgB,SAAS,WAAU,SAAQ,KAAKA,QAAQ,QAAQ,IAAI,CAAC;EAErF,KAAK,MAAM,CAAC,MAAM,SAAS,CAAC,GAAG,KAAKC,KAAK,GAAG;GAE1C,IADa,QAAQ,IAAI,IAClB,CAAC,EAAE,cAAc,KAAK,WAAW;GACxC,KAAK,QAAQ;GACb,KAAKA,MAAM,OAAO,IAAI;EACxB;EACA,KAAK,MAAM,CAAC,MAAM,SAAS,SAAS;GAClC,IAAI,KAAKA,MAAM,IAAI,IAAI,GAAG;GAC1B,KAAKA,MAAM,IAAI,MAAM;IACnB,WAAW,KAAK;IAChB,SAAS,KAAKD,QAAQ,SAAS,KAAK,UAAU;GAChD,CAAC;EACH;EACA,OAAO,CAAC,GAAG,QAAQ,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,YAAY,YAAY,gBAAgB;GAC1E;GACA;GACA;EACF,EAAE;CACJ;CAEA,UAAgB;EACd,KAAK,MAAM,WAAW,KAAKC,MAAM,OAAO,GAAG,QAAQ,QAAQ;EAC3D,KAAKA,MAAM,MAAM;CACnB;AACF;;;ACnKA,SAAS,OAAO,OAAqD;CACnE,OAAO,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IAAI,QAAmC,KAAA;AACnH;AAEA,SAAS,IAAI,OAAoC;CAC/C,OAAO,OAAO,UAAU,YAAY,MAAM,SAAS,IAAI,QAAQ,KAAA;AACjE;AAEA,SAAS,WAAW,QAA4B;CAC9C,OAAO,OAAO,QACX,QAAQ,UAA4D,MAAM,SAAS,MAAM,CAAC,CAC1F,KAAI,UAAS,MAAM,IAAI,CAAC,CACxB,KAAK,IAAI;AACd;;AAGA,SAAgB,aAAa,MAAyC;CACpE,MAAM,UAAU,IAAI,OAAO,IAAI,CAAC,EAAE,OAAO;CACzC,IAAI,YAAY,KAAA,GAAW,OAAO,KAAA;CAClC,MAAM,cAAc,IAAI,OAAO,IAAI,CAAC,EAAE,WAAW;CACjD,OAAO;EAAE,MAAM;EAAY,OAAO;EAAS,GAAI,gBAAgB,KAAA,IAAY,CAAC,IAAI,EAAE,YAAY;CAAG;AACnG;AAEA,SAAgB,mBAAmB,OAAgB,QAAgD;CACjG,MAAM,SAAS,WAAW,MAAM;CAChC,OAAO,OAAO,WAAW,IAAI,KAAA,IAAY;EAAE,MAAM;EAAY;CAAO;AACtE;;AAGA,SAAgB,aAAa,MAAyC;CACpE,MAAM,aAAa,OAAO,IAAI;CAC9B,MAAM,OAAO,IAAI,YAAY,SAAS;CACtC,IAAI,SAAS,KAAA,GAAW,OAAO,KAAA;CAC/B,MAAM,SAAS,OAAO,YAAY,WAAW,WAAW,WAAW,SAAS,KAAA;CAC5E,OAAO;EACL,MAAM;EACN,MAAM;EACN,OAAO,QAAQ;EACf,WAAW,CAAC;GAAE;GAAM,GAAI,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM,OAAO;EAAG,CAAC;CACzE;AACF;AAEA,SAAS,UAAU,MAAwF;CACzG,MAAM,aAAa,OAAO,IAAI;CAC9B,MAAM,OAAO,IAAI,YAAY,SAAS;CACtC,IAAI,SAAS,KAAA,GAAW,OAAO,KAAA;CAC/B,IAAI,MAAM,QAAQ,YAAY,KAAK,GAAG;EACpC,MAAM,QAAqE,CAAC;EAC5E,KAAK,MAAM,QAAQ,WAAW,OAAO;GACnC,MAAM,OAAO,OAAO,IAAI;GACxB,MAAM,UAAU,IAAI,MAAM,UAAU;GACpC,MAAM,UAAU,IAAI,MAAM,UAAU;GACpC,IAAI,YAAY,KAAA,GAAW;GAC3B,MAAM,KAAK;IAAE;IAAM,SAAS,WAAW;IAAM;GAAQ,CAAC;EACxD;EACA,OAAO,MAAM,SAAS,IAAI,QAAQ,KAAA;CACpC;CACA,MAAM,UAAU,IAAI,YAAY,UAAU,KAAK,YAAY;CAC3D,IAAI,OAAO,YAAY,YAAY,QAAQ,WAAW,GAAG,OAAO,KAAA;CAEhE,OAAO,CAAC;EAAE;EAAM,SADA,IAAI,YAAY,UACD,KAAK;EAAM;CAAQ,CAAC;AACrD;;AAGA,SAAgB,aAAa,MAAyC;CACpE,MAAM,aAAa,OAAO,IAAI;CAC9B,MAAM,OAAO,IAAI,YAAY,SAAS;CACtC,MAAM,QAAQ,UAAU,IAAI;CAC5B,IAAI,SAAS,KAAA,KAAa,UAAU,KAAA,GAAW,OAAO,KAAA;CAEtD,OAAO;EAAE,MAAM;EAAQ,OAAO,GADjB,OAAO,YAAY,eAAe,YAAY,MAAM,QAAQ,YAAY,KAAK,IAAI,SAAS,QACjE,GAAG;EAAQ;EAAO,WAAW,CAAC,EAAE,KAAK,CAAC;CAAE;AAChF;;AAGA,SAAgB,eAAe,MAAyC;CACtE,MAAM,aAAa,OAAO,IAAI;CAC9B,MAAM,QAAQ,IAAI,YAAY,OAAO,KAAK,IAAI,YAAY,KAAK;CAC/D,IAAI,UAAU,KAAA,GAAW,OAAO,KAAA;CAChC,MAAM,QAAQ,IAAI,YAAY,IAAI;CAClC,OAAO;EACL,MAAM;EACN,MAAM;EACN,OAAO,UAAU,KAAA,IAAY,QAAQ,GAAG,MAAM,KAAK;EACnD,UAAU;CACZ;AACF;;AAGA,SAAgB,cAAc,MAAyC;CACrE,MAAM,MAAM,IAAI,OAAO,IAAI,CAAC,EAAE,GAAG;CACjC,IAAI,QAAQ,KAAA,GAAW,OAAO,KAAA;CAC9B,OAAO;EAAE,MAAM;EAAW,MAAM;EAAS,OAAO;EAAK,UAAU;CAAK;AACtE;;AAGA,SAAgB,aAAa,MAAyC;CACpE,MAAM,aAAa,OAAO,IAAI;CAE9B,OAAO;EAAE,MAAM;EAAW,MAAM;EAAS,OAD3B,IAAI,YAAY,WAAW,KAAK,IAAI,YAAY,aAAa,KAAK;EAChC,UAAU;CAAK;AACjE;AAEA,SAAS,aAAa,OAA6B;CACjD,OAAO;EAAE,MAAM;EAAW,MAAM;EAAS;CAAM;AACjD;AAEA,SAAS,oBACP,MACA,aACA,aACA,eACgB;CAChB,OAAO;EACL;EACA;EACA,YAAY;GAAE,MAAM;GAAU,YAAY,CAAC;EAAE;EAC7C,QAAQ;GACN,QAAQ;IAAE,MAAM;IAAU,YAAY,CAAC;GAAE;GACzC,cAAc,CAAC;EACjB;EACA,SAAS,YAAY;GACnB,MAAM,IAAI,MAAM,6CAA6C,MAAM;EACrE;EACA,GAAI,gBAAgB,KAAA,IAAY,CAAC,IAAI,EAAE,YAAY;EACnD,GAAI,kBAAkB,KAAA,IAAY,CAAC,IAAI,EAAE,cAAc;CACzD;AACF;AAEA,MAAM,oBAAoB;;AAG1B,SAAgB,gBAAgB,UAA+D;CAC7F,QAAQ,SAAkB;EAExB,OAAO;GAAE,MAAM;GAAW,MAAM;GAAS,OADrB,IAAI,OAAO,IAAI,CAAC,EAAE,WACoB,KAAK;GAAU,UAAU;EAAK;CAC1F;AACF;;AAGA,SAAgB,2BAA2B,MAA8B;CACvE,OAAO,oBAAoB,MAAM,mBAAmB,gBAAgB,IAAI,CAAC;AAC3E;;AAGA,SAAgB,6BAA+C;CAC7D,OAAO;EACL,oBAAoB,QAAQ,mBAAmB,cAAc,kBAAkB;EAC/E,oBAAoB,QAAQ,mBAAmB,YAAY;EAC3D,oBAAoB,QAAQ,mBAAmB,YAAY;EAC3D,oBAAoB,aAAa,mBAAmB,YAAY;EAChE,oBAAoB,SAAS,mBAAmB,YAAY;EAC5D,oBAAoB,gBAAgB,oBAAmB,SAAQ;GAC7D,MAAM,OAAO,IAAI,OAAO,IAAI,CAAC,EAAE,aAAa;GAC5C,OAAO,SAAS,KAAA,IAAY,KAAA,IAAY,aAAa,gBAAgB,MAAM;EAC7E,CAAC;EACD,oBAAoB,QAAQ,mBAAmB,cAAc;EAC7D,oBAAoB,QAAQ,mBAAmB,cAAc;EAC7D,oBAAoB,aAAa,mBAAmB,cAAc;EAClE,oBAAoB,YAAY,mBAAmB,aAAa;EAChE,oBAAoB,QAAQ,mBAAmB,YAAY;EAC3D,oBAAoB,aAAa,yBAAyB,aAAa,cAAc,CAAC;CACxF;AACF;;AAGA,MAAa,yBAA8C,IAAI,IAAI,2BAA2B,CAAC,CAAC,KAAI,eAAc,WAAW,IAAI,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"preset-installer-D8q3_-wJ.mjs","names":[],"sources":["../src/executable.ts","../src/preset-installer.ts"],"sourcesContent":["import { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport type { SubprocessHandle, SubprocessRuntime } from '@deepseek-ai/dsh-subprocess'\nimport { redactText } from './events.ts'\n\nconst VERSION_PATTERN = /(?:Claude Code\\s+)?v?(\\d+\\.\\d+\\.\\d+(?:[-+][\\w.-]+)?)/i\nconst MAX_PROBE_STDOUT = 64 * 1024\nconst MAX_PROBE_STDERR = 8 * 1024\n\nexport type ExecutableRuntime = Pick<SubprocessRuntime, 'resolveExecutable' | 'spawn'>\n\nexport class ClaudeExecutableNotFoundError extends Error {\n readonly searched: readonly string[]\n\n constructor(searched: readonly string[], options?: ErrorOptions) {\n super(`Claude Code executable not found. Searched: ${searched.join(', ')}`, options)\n this.name = 'ClaudeExecutableNotFoundError'\n this.searched = [...searched]\n }\n}\n\nexport interface ClaudeExecutableResolution {\n path: string\n searched: readonly string[]\n}\n\nexport interface ClaudeDoctorReport {\n executable: {\n status: 'found' | 'missing'\n path?: string\n searched: readonly string[]\n }\n version: {\n status: 'ok' | 'error' | 'not-run'\n value?: string\n message?: string\n }\n authentication: {\n status: 'signed-in' | 'signed-out' | 'unknown' | 'not-run'\n method?: string\n provider?: string\n subscription?: string\n message?: string\n }\n handshake: 'not-run' | 'ok' | 'error'\n}\n\nfunction fallbackCandidates(): string[] {\n if (process.platform !== 'darwin') return []\n return [\n join(homedir(), '.local', 'bin', 'claude'),\n '/opt/homebrew/bin/claude',\n '/usr/local/bin/claude',\n ]\n}\n\nfunction abortError(error: unknown): boolean {\n return error instanceof Error && (error.name === 'AbortError' || error.name === 'TimeoutError')\n}\n\nexport async function resolveClaudeExecutable(\n runtime: ExecutableRuntime,\n configuredPath?: string,\n signal?: AbortSignal,\n): Promise<ClaudeExecutableResolution> {\n const searched: string[] = []\n const candidates = configuredPath === undefined\n ? ['claude', ...fallbackCandidates()]\n : [configuredPath]\n if (configuredPath !== undefined && !configuredPath.startsWith('/')) {\n throw new Error(`Claude Code executable path must be absolute: ${configuredPath}`)\n }\n\n let lastError: unknown\n for (const candidate of candidates) {\n if (searched.includes(candidate)) continue\n searched.push(candidate)\n try {\n const path = await runtime.resolveExecutable(candidate, undefined, signal)\n return { path, searched }\n } catch (error) {\n if (abortError(error) || signal?.aborted === true) throw error\n lastError = error\n }\n }\n throw new ClaudeExecutableNotFoundError(searched, lastError === undefined ? undefined : { cause: lastError })\n}\n\ninterface CollectedCommand {\n exitCode: number | null\n signal: NodeJS.Signals | null\n stdout: string\n stderr: string\n}\n\nasync function collect(handle: SubprocessHandle): Promise<CollectedCommand> {\n const outcome = await handle.done\n const stdout = handle.collected.stdout?.readFrom(0).text ?? ''\n const stderr = handle.collected.stderr?.readFrom(0).text ?? ''\n return { ...outcome, stdout, stderr }\n}\n\nasync function runProbe(\n runtime: ExecutableRuntime,\n executable: string,\n args: readonly string[],\n cwd: string,\n signal?: AbortSignal,\n): Promise<CollectedCommand> {\n return collect(runtime.spawn({\n argv: [executable, ...args],\n cwd,\n stdio: {\n stdin: 'ignore',\n stdout: { maxBytes: MAX_PROBE_STDOUT },\n stderr: { maxBytes: MAX_PROBE_STDERR },\n },\n graceMs: 2_000,\n ...(signal === undefined ? {} : { signal }),\n env: {},\n }))\n}\n\nexport function parseClaudeVersion(output: string): string | undefined {\n return VERSION_PATTERN.exec(output)?.[1]\n}\n\nexport async function probeClaudeVersion(\n runtime: ExecutableRuntime,\n executable: string,\n cwd: string,\n signal?: AbortSignal,\n): Promise<string> {\n const result = await runProbe(runtime, executable, ['--version'], cwd, signal)\n const version = parseClaudeVersion(`${result.stdout}\\n${result.stderr}`)\n if (result.exitCode !== 0 || version === undefined) {\n throw new Error(`Claude Code version probe failed (${result.exitCode ?? result.signal ?? 'unknown exit'})`)\n }\n return version\n}\n\nexport async function probeClaudeAuthentication(\n runtime: ExecutableRuntime,\n executable: string,\n cwd: string,\n signal?: AbortSignal,\n): Promise<ClaudeDoctorReport['authentication']> {\n const result = await runProbe(runtime, executable, ['auth', 'status', '--json'], cwd, signal)\n if (result.exitCode !== 0) {\n return { status: 'unknown', message: 'Claude authentication status command failed' }\n }\n try {\n const value = JSON.parse(result.stdout) as Record<string, unknown>\n const report: ClaudeDoctorReport['authentication'] = {\n status: value.loggedIn === true ? 'signed-in' : value.loggedIn === false ? 'signed-out' : 'unknown',\n }\n if (typeof value.authMethod === 'string') report.method = redactText(value.authMethod, 100)\n if (typeof value.apiProvider === 'string') report.provider = redactText(value.apiProvider, 100)\n if (typeof value.subscriptionType === 'string') report.subscription = redactText(value.subscriptionType, 100)\n return report\n } catch {\n return { status: 'unknown', message: 'Claude authentication status was not valid JSON' }\n }\n}\n\nexport async function runClaudeDoctor(\n runtime: ExecutableRuntime,\n options: { configuredPath?: string; cwd: string; signal?: AbortSignal },\n): Promise<ClaudeDoctorReport> {\n let resolution: ClaudeExecutableResolution\n try {\n resolution = await resolveClaudeExecutable(runtime, options.configuredPath, options.signal)\n } catch (error) {\n if (error instanceof ClaudeExecutableNotFoundError) {\n return {\n executable: { status: 'missing', searched: error.searched },\n version: { status: 'not-run' },\n authentication: { status: 'not-run' },\n handshake: 'not-run',\n }\n }\n throw error\n }\n\n const report: ClaudeDoctorReport = {\n executable: { status: 'found', path: resolution.path, searched: resolution.searched },\n version: { status: 'not-run' },\n authentication: { status: 'not-run' },\n handshake: 'not-run',\n }\n try {\n report.version = {\n status: 'ok',\n value: await probeClaudeVersion(runtime, resolution.path, options.cwd, options.signal),\n }\n } catch (error) {\n report.version = {\n status: 'error',\n message: error instanceof Error ? error.message : 'Version probe failed',\n }\n }\n try {\n report.authentication = await probeClaudeAuthentication(runtime, resolution.path, options.cwd, options.signal)\n } catch (error) {\n report.authentication = {\n status: 'unknown',\n message: error instanceof Error ? error.message : 'Authentication probe failed',\n }\n }\n return report\n}\n","import { randomUUID } from 'node:crypto'\nimport { link, lstat, mkdir, readFile, readdir, rm, rmdir, writeFile } from 'node:fs/promises'\nimport { dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { dshHomePath } from '@deepseek-ai/dsh-home-paths'\nimport { CLAUDE_CODE_PRESET_ID, LEGACY_CLAUDE_CODE_PRESET_ID } from './constants.ts'\n\nexport const MANAGED_PRESET_FILES = ['agent.cordis.yml', 'preset.yml'] as const\n\n/** Package specifier kept in the shipped template. DSH Desktop's resolver hook\n * only rewrites bare specifiers issued by the root include; preset subtrees\n * resolve through Node's internal loader with an unrelated base and cannot\n * find linked packages. The installer therefore substitutes the absolute\n * built entry path, which the preset tree imports directly as a file URL. */\nconst PRESET_ROUTE_PACKAGE_SPECIFIER = '@norman-else/dsh-claude/preset-route'\n\nexport class ManagedPresetConflictError extends Error {\n readonly path: string\n\n constructor(path: string) {\n super(`dsh-claude: refusing to overwrite user-modified preset file ${path}`)\n this.name = 'ManagedPresetConflictError'\n this.path = path\n }\n}\n\nexport interface ManagedPresetPaths {\n sourceDir: string\n targetDir: string\n legacyTargetDir?: string\n}\n\nexport function defaultManagedPresetPaths(dshHome?: string): ManagedPresetPaths {\n const packageRoot = fileURLToPath(new URL('../', import.meta.url))\n return {\n sourceDir: join(packageRoot, 'legacy-preset'),\n targetDir: dshHome === undefined\n ? dshHomePath('.agent-presets', CLAUDE_CODE_PRESET_ID)\n : join(dshHome, '.agent-presets', CLAUDE_CODE_PRESET_ID),\n legacyTargetDir: dshHome === undefined\n ? dshHomePath('.agent-presets', LEGACY_CLAUDE_CODE_PRESET_ID)\n : join(dshHome, '.agent-presets', LEGACY_CLAUDE_CODE_PRESET_ID),\n }\n}\n\ninterface ManagedContent {\n file: string\n /** Content this installer version writes. */\n content: string\n /** Older installer-written contents that may be silently upgraded/removed. */\n legacy: readonly string[]\n /** Legacy detection for contents that predate the current template. */\n isLegacy(current: string): boolean\n}\n\nasync function managedContents(paths: ManagedPresetPaths): Promise<ManagedContent[]> {\n const routeEntry = join(paths.sourceDir, '..', 'lib', 'preset-route.mjs')\n return await Promise.all(MANAGED_PRESET_FILES.map(async (file): Promise<ManagedContent> => {\n const source = await readFile(join(paths.sourceDir, file), 'utf8')\n const nameRow = `name: '${PRESET_ROUTE_PACKAGE_SPECIFIER}'`\n const legacyNameRow = `name: ${PRESET_ROUTE_PACKAGE_SPECIFIER}`\n if (file !== 'agent.cordis.yml' || !source.includes(nameRow)) {\n return { file, content: source, legacy: [], isLegacy: () => false }\n }\n return {\n file,\n content: source.replace(nameRow, `name: '${routeEntry}'`),\n legacy: [source],\n // Any earlier installer generation wrote this same entry id with the\n // route reference in either supported form (bare package specifier or\n // absolute built-module path); treat those as safe to upgrade regardless\n // of comment or field drift in the template.\n isLegacy: current =>\n current.includes('id: claude-code-route')\n && (current.includes(nameRow)\n || current.includes(legacyNameRow)\n || current.includes('lib/preset-route.mjs')),\n }\n }))\n}\n\nasync function readIfPresent(path: string): Promise<string | undefined> {\n try {\n return await readFile(path, 'utf8')\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined\n throw error\n }\n}\n\nasync function atomicWrite(path: string, content: string): Promise<boolean> {\n await mkdir(dirname(path), { recursive: true })\n const temporary = `${path}.${randomUUID()}.tmp`\n try {\n await writeFile(temporary, content, { encoding: 'utf8', mode: 0o600, flag: 'wx' })\n try {\n await link(temporary, path)\n return true\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error\n if (await readIfPresent(path) === content) return false\n throw new ManagedPresetConflictError(path)\n }\n } finally {\n await rm(temporary, { force: true })\n }\n}\n\nexport async function ensureManagedPreset(paths = defaultManagedPresetPaths()): Promise<'installed' | 'unchanged'> {\n await assertSafeTargetDirectory(paths.targetDir)\n const expected = await managedContents(paths)\n let changed = false\n for (const { file, content, legacy, isLegacy } of expected) {\n const target = join(paths.targetDir, file)\n const current = await readIfPresent(target)\n if (current === content) continue\n if (current !== undefined) {\n // Upgrade installer-written legacy content in place; never touch user edits.\n if (!legacy.includes(current) && !isLegacy(current)) throw new ManagedPresetConflictError(target)\n await rm(target)\n }\n changed = await atomicWrite(target, content) || changed\n }\n if (paths.legacyTargetDir !== undefined) {\n changed = await removeLegacyManagedPreset(paths.legacyTargetDir) || changed\n }\n return changed ? 'installed' : 'unchanged'\n}\n\nasync function removeLegacyManagedPreset(targetDir: string): Promise<boolean> {\n await assertSafeTargetDirectory(targetDir)\n const agent = await readIfPresent(join(targetDir, 'agent.cordis.yml'))\n const preset = await readIfPresent(join(targetDir, 'preset.yml'))\n if (agent === undefined && preset === undefined) return false\n\n // Only migrate the prior installer-owned template. Any user edit leaves the\n // complete legacy preset untouched so existing sessions remain recoverable.\n const managedAgent = agent !== undefined\n && agent.includes('id: claude-code-route')\n && (agent.includes('dsh-claude-code/preset-route') || agent.includes('lib/preset-route.mjs'))\n const managedPreset = preset !== undefined\n && preset.includes('Managed by dsh-claude-code')\n && preset.includes('name: Claude Code')\n if (!managedAgent || !managedPreset) return false\n\n await rm(join(targetDir, 'agent.cordis.yml'))\n await rm(join(targetDir, 'preset.yml'))\n if ((await readdir(targetDir)).length === 0) await rmdir(targetDir)\n return true\n}\n\n/** Reject a target directory that is a symlink (or occupies the path as a file)\n * so the managed preset never writes through an attacker-controlled link. */\nasync function assertSafeTargetDirectory(targetDir: string): Promise<void> {\n try {\n const stat = await lstat(targetDir)\n if (!stat.isDirectory() || stat.isSymbolicLink()) {\n throw new ManagedPresetConflictError(targetDir)\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return\n throw error\n }\n}\n\nexport async function removeManagedPreset(paths = defaultManagedPresetPaths()): Promise<'removed' | 'absent'> {\n await assertSafeTargetDirectory(paths.targetDir)\n const expected = await managedContents(paths)\n const managedTargets: string[] = []\n for (const { file, content, legacy, isLegacy } of expected) {\n const target = join(paths.targetDir, file)\n const current = await readIfPresent(target)\n if (current === undefined) continue\n if (current !== content && !legacy.includes(current) && !isLegacy(current)) throw new ManagedPresetConflictError(target)\n managedTargets.push(target)\n }\n for (const target of managedTargets) await rm(target)\n try {\n if ((await readdir(paths.targetDir)).length === 0) await rmdir(paths.targetDir)\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n }\n return managedTargets.length > 0 ? 'removed' : 'absent'\n}\n"],"mappings":";;;;;;;;AAKA,MAAM,kBAAkB;AACxB,MAAM,mBAAmB;AACzB,MAAM,mBAAmB;AAIzB,IAAa,gCAAb,cAAmD,MAAM;CACvD;CAEA,YAAY,UAA6B,SAAwB;EAC/D,MAAM,+CAA+C,SAAS,KAAK,IAAI,KAAK,OAAO;EACnF,KAAK,OAAO;EACZ,KAAK,WAAW,CAAC,GAAG,QAAQ;CAC9B;AACF;AA4BA,SAAS,qBAA+B;CACtC,IAAI,QAAQ,aAAa,UAAU,OAAO,CAAC;CAC3C,OAAO;EACL,KAAK,QAAQ,GAAG,UAAU,OAAO,QAAQ;EACzC;EACA;CACF;AACF;AAEA,SAAS,WAAW,OAAyB;CAC3C,OAAO,iBAAiB,UAAU,MAAM,SAAS,gBAAgB,MAAM,SAAS;AAClF;AAEA,eAAsB,wBACpB,SACA,gBACA,QACqC;CACrC,MAAM,WAAqB,CAAC;CAC5B,MAAM,aAAa,mBAAmB,KAAA,IAClC,CAAC,UAAU,GAAG,mBAAmB,CAAC,IAClC,CAAC,cAAc;CACnB,IAAI,mBAAmB,KAAA,KAAa,CAAC,eAAe,WAAW,GAAG,GAChE,MAAM,IAAI,MAAM,iDAAiD,gBAAgB;CAGnF,IAAI;CACJ,KAAK,MAAM,aAAa,YAAY;EAClC,IAAI,SAAS,SAAS,SAAS,GAAG;EAClC,SAAS,KAAK,SAAS;EACvB,IAAI;GAEF,OAAO;IAAE,MAAA,MADU,QAAQ,kBAAkB,WAAW,KAAA,GAAW,MAAM;IAC1D;GAAS;EAC1B,SAAS,OAAO;GACd,IAAI,WAAW,KAAK,KAAK,QAAQ,YAAY,MAAM,MAAM;GACzD,YAAY;EACd;CACF;CACA,MAAM,IAAI,8BAA8B,UAAU,cAAc,KAAA,IAAY,KAAA,IAAY,EAAE,OAAO,UAAU,CAAC;AAC9G;AASA,eAAe,QAAQ,QAAqD;CAC1E,MAAM,UAAU,MAAM,OAAO;CAC7B,MAAM,SAAS,OAAO,UAAU,QAAQ,SAAS,CAAC,CAAC,CAAC,QAAQ;CAC5D,MAAM,SAAS,OAAO,UAAU,QAAQ,SAAS,CAAC,CAAC,CAAC,QAAQ;CAC5D,OAAO;EAAE,GAAG;EAAS;EAAQ;CAAO;AACtC;AAEA,eAAe,SACb,SACA,YACA,MACA,KACA,QAC2B;CAC3B,OAAO,QAAQ,QAAQ,MAAM;EAC3B,MAAM,CAAC,YAAY,GAAG,IAAI;EAC1B;EACA,OAAO;GACL,OAAO;GACP,QAAQ,EAAE,UAAU,iBAAiB;GACrC,QAAQ,EAAE,UAAU,iBAAiB;EACvC;EACA,SAAS;EACT,GAAI,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO;EACzC,KAAK,CAAC;CACR,CAAC,CAAC;AACJ;AAEA,SAAgB,mBAAmB,QAAoC;CACrE,OAAO,gBAAgB,KAAK,MAAM,CAAC,GAAG;AACxC;AAEA,eAAsB,mBACpB,SACA,YACA,KACA,QACiB;CACjB,MAAM,SAAS,MAAM,SAAS,SAAS,YAAY,CAAC,WAAW,GAAG,KAAK,MAAM;CAC7E,MAAM,UAAU,mBAAmB,GAAG,OAAO,OAAO,IAAI,OAAO,QAAQ;CACvE,IAAI,OAAO,aAAa,KAAK,YAAY,KAAA,GACvC,MAAM,IAAI,MAAM,qCAAqC,OAAO,YAAY,OAAO,UAAU,eAAe,EAAE;CAE5G,OAAO;AACT;AAEA,eAAsB,0BACpB,SACA,YACA,KACA,QAC+C;CAC/C,MAAM,SAAS,MAAM,SAAS,SAAS,YAAY;EAAC;EAAQ;EAAU;CAAQ,GAAG,KAAK,MAAM;CAC5F,IAAI,OAAO,aAAa,GACtB,OAAO;EAAE,QAAQ;EAAW,SAAS;CAA8C;CAErF,IAAI;EACF,MAAM,QAAQ,KAAK,MAAM,OAAO,MAAM;EACtC,MAAM,SAA+C,EACnD,QAAQ,MAAM,aAAa,OAAO,cAAc,MAAM,aAAa,QAAQ,eAAe,UAC5F;EACA,IAAI,OAAO,MAAM,eAAe,UAAU,OAAO,SAAS,WAAW,MAAM,YAAY,GAAG;EAC1F,IAAI,OAAO,MAAM,gBAAgB,UAAU,OAAO,WAAW,WAAW,MAAM,aAAa,GAAG;EAC9F,IAAI,OAAO,MAAM,qBAAqB,UAAU,OAAO,eAAe,WAAW,MAAM,kBAAkB,GAAG;EAC5G,OAAO;CACT,QAAQ;EACN,OAAO;GAAE,QAAQ;GAAW,SAAS;EAAkD;CACzF;AACF;AAEA,eAAsB,gBACpB,SACA,SAC6B;CAC7B,IAAI;CACJ,IAAI;EACF,aAAa,MAAM,wBAAwB,SAAS,QAAQ,gBAAgB,QAAQ,MAAM;CAC5F,SAAS,OAAO;EACd,IAAI,iBAAiB,+BACnB,OAAO;GACL,YAAY;IAAE,QAAQ;IAAW,UAAU,MAAM;GAAS;GAC1D,SAAS,EAAE,QAAQ,UAAU;GAC7B,gBAAgB,EAAE,QAAQ,UAAU;GACpC,WAAW;EACb;EAEF,MAAM;CACR;CAEA,MAAM,SAA6B;EACjC,YAAY;GAAE,QAAQ;GAAS,MAAM,WAAW;GAAM,UAAU,WAAW;EAAS;EACpF,SAAS,EAAE,QAAQ,UAAU;EAC7B,gBAAgB,EAAE,QAAQ,UAAU;EACpC,WAAW;CACb;CACA,IAAI;EACF,OAAO,UAAU;GACf,QAAQ;GACR,OAAO,MAAM,mBAAmB,SAAS,WAAW,MAAM,QAAQ,KAAK,QAAQ,MAAM;EACvF;CACF,SAAS,OAAO;EACd,OAAO,UAAU;GACf,QAAQ;GACR,SAAS,iBAAiB,QAAQ,MAAM,UAAU;EACpD;CACF;CACA,IAAI;EACF,OAAO,iBAAiB,MAAM,0BAA0B,SAAS,WAAW,MAAM,QAAQ,KAAK,QAAQ,MAAM;CAC/G,SAAS,OAAO;EACd,OAAO,iBAAiB;GACtB,QAAQ;GACR,SAAS,iBAAiB,QAAQ,MAAM,UAAU;EACpD;CACF;CACA,OAAO;AACT;;;AC3MA,MAAa,uBAAuB,CAAC,oBAAoB,YAAY;;;;;;AAOrE,MAAM,iCAAiC;AAEvC,IAAa,6BAAb,cAAgD,MAAM;CACpD;CAEA,YAAY,MAAc;EACxB,MAAM,+DAA+D,MAAM;EAC3E,KAAK,OAAO;EACZ,KAAK,OAAO;CACd;AACF;AAQA,SAAgB,0BAA0B,SAAsC;CAC9E,MAAM,cAAc,cAAc,IAAI,IAAI,OAAO,YAAY,GAAG,CAAC;CACjE,OAAO;EACL,WAAW,KAAK,aAAa,eAAe;EAC5C,WAAW,YAAY,KAAA,IACnB,YAAY,kBAAkB,qBAAqB,IACnD,KAAK,SAAS,kBAAkB,qBAAqB;EACzD,iBAAiB,YAAY,KAAA,IACzB,YAAY,kBAAkB,4BAA4B,IAC1D,KAAK,SAAS,kBAAkB,4BAA4B;CAClE;AACF;AAYA,eAAe,gBAAgB,OAAsD;CACnF,MAAM,aAAa,KAAK,MAAM,WAAW,MAAM,OAAO,kBAAkB;CACxE,OAAO,MAAM,QAAQ,IAAI,qBAAqB,IAAI,OAAO,SAAkC;EACzF,MAAM,SAAS,MAAM,SAAS,KAAK,MAAM,WAAW,IAAI,GAAG,MAAM;EACjE,MAAM,UAAU,UAAU,+BAA+B;EACzD,MAAM,gBAAgB,SAAS;EAC/B,IAAI,SAAS,sBAAsB,CAAC,OAAO,SAAS,OAAO,GACzD,OAAO;GAAE;GAAM,SAAS;GAAQ,QAAQ,CAAC;GAAG,gBAAgB;EAAM;EAEpE,OAAO;GACL;GACA,SAAS,OAAO,QAAQ,SAAS,UAAU,WAAW,EAAE;GACxD,QAAQ,CAAC,MAAM;GAKf,WAAU,YACR,QAAQ,SAAS,uBAAuB,MACpC,QAAQ,SAAS,OAAO,KACvB,QAAQ,SAAS,aAAa,KAC9B,QAAQ,SAAS,sBAAsB;EAChD;CACF,CAAC,CAAC;AACJ;AAEA,eAAe,cAAc,MAA2C;CACtE,IAAI;EACF,OAAO,MAAM,SAAS,MAAM,MAAM;CACpC,SAAS,OAAO;EACd,IAAK,MAAgC,SAAS,UAAU,OAAO,KAAA;EAC/D,MAAM;CACR;AACF;AAEA,eAAe,YAAY,MAAc,SAAmC;CAC1E,MAAM,MAAM,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;CAC9C,MAAM,YAAY,GAAG,KAAK,GAAG,WAAW,EAAE;CAC1C,IAAI;EACF,MAAM,UAAU,WAAW,SAAS;GAAE,UAAU;GAAQ,MAAM;GAAO,MAAM;EAAK,CAAC;EACjF,IAAI;GACF,MAAM,KAAK,WAAW,IAAI;GAC1B,OAAO;EACT,SAAS,OAAO;GACd,IAAK,MAAgC,SAAS,UAAU,MAAM;GAC9D,IAAI,MAAM,cAAc,IAAI,MAAM,SAAS,OAAO;GAClD,MAAM,IAAI,2BAA2B,IAAI;EAC3C;CACF,UAAU;EACR,MAAM,GAAG,WAAW,EAAE,OAAO,KAAK,CAAC;CACrC;AACF;AAEA,eAAsB,oBAAoB,QAAQ,0BAA0B,GAAuC;CACjH,MAAM,0BAA0B,MAAM,SAAS;CAC/C,MAAM,WAAW,MAAM,gBAAgB,KAAK;CAC5C,IAAI,UAAU;CACd,KAAK,MAAM,EAAE,MAAM,SAAS,QAAQ,cAAc,UAAU;EAC1D,MAAM,SAAS,KAAK,MAAM,WAAW,IAAI;EACzC,MAAM,UAAU,MAAM,cAAc,MAAM;EAC1C,IAAI,YAAY,SAAS;EACzB,IAAI,YAAY,KAAA,GAAW;GAEzB,IAAI,CAAC,OAAO,SAAS,OAAO,KAAK,CAAC,SAAS,OAAO,GAAG,MAAM,IAAI,2BAA2B,MAAM;GAChG,MAAM,GAAG,MAAM;EACjB;EACA,UAAU,MAAM,YAAY,QAAQ,OAAO,KAAK;CAClD;CACA,IAAI,MAAM,oBAAoB,KAAA,GAC5B,UAAU,MAAM,0BAA0B,MAAM,eAAe,KAAK;CAEtE,OAAO,UAAU,cAAc;AACjC;AAEA,eAAe,0BAA0B,WAAqC;CAC5E,MAAM,0BAA0B,SAAS;CACzC,MAAM,QAAQ,MAAM,cAAc,KAAK,WAAW,kBAAkB,CAAC;CACrE,MAAM,SAAS,MAAM,cAAc,KAAK,WAAW,YAAY,CAAC;CAChE,IAAI,UAAU,KAAA,KAAa,WAAW,KAAA,GAAW,OAAO;CAIxD,MAAM,eAAe,UAAU,KAAA,KAC1B,MAAM,SAAS,uBAAuB,MACrC,MAAM,SAAS,8BAA8B,KAAK,MAAM,SAAS,sBAAsB;CAC7F,MAAM,gBAAgB,WAAW,KAAA,KAC5B,OAAO,SAAS,4BAA4B,KAC5C,OAAO,SAAS,mBAAmB;CACxC,IAAI,CAAC,gBAAgB,CAAC,eAAe,OAAO;CAE5C,MAAM,GAAG,KAAK,WAAW,kBAAkB,CAAC;CAC5C,MAAM,GAAG,KAAK,WAAW,YAAY,CAAC;CACtC,KAAK,MAAM,QAAQ,SAAS,EAAA,CAAG,WAAW,GAAG,MAAM,MAAM,SAAS;CAClE,OAAO;AACT;;;AAIA,eAAe,0BAA0B,WAAkC;CACzE,IAAI;EACF,MAAM,OAAO,MAAM,MAAM,SAAS;EAClC,IAAI,CAAC,KAAK,YAAY,KAAK,KAAK,eAAe,GAC7C,MAAM,IAAI,2BAA2B,SAAS;CAElD,SAAS,OAAO;EACd,IAAK,MAAgC,SAAS,UAAU;EACxD,MAAM;CACR;AACF;AAEA,eAAsB,oBAAoB,QAAQ,0BAA0B,GAAkC;CAC5G,MAAM,0BAA0B,MAAM,SAAS;CAC/C,MAAM,WAAW,MAAM,gBAAgB,KAAK;CAC5C,MAAM,iBAA2B,CAAC;CAClC,KAAK,MAAM,EAAE,MAAM,SAAS,QAAQ,cAAc,UAAU;EAC1D,MAAM,SAAS,KAAK,MAAM,WAAW,IAAI;EACzC,MAAM,UAAU,MAAM,cAAc,MAAM;EAC1C,IAAI,YAAY,KAAA,GAAW;EAC3B,IAAI,YAAY,WAAW,CAAC,OAAO,SAAS,OAAO,KAAK,CAAC,SAAS,OAAO,GAAG,MAAM,IAAI,2BAA2B,MAAM;EACvH,eAAe,KAAK,MAAM;CAC5B;CACA,KAAK,MAAM,UAAU,gBAAgB,MAAM,GAAG,MAAM;CACpD,IAAI;EACF,KAAK,MAAM,QAAQ,MAAM,SAAS,EAAA,CAAG,WAAW,GAAG,MAAM,MAAM,MAAM,SAAS;CAChF,SAAS,OAAO;EACd,IAAK,MAAgC,SAAS,UAAU,MAAM;CAChE;CACA,OAAO,eAAe,SAAS,IAAI,YAAY;AACjD"}