@norman-else/dsh-claude 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/bin.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { i as parseClaudeVersion, n as ensureManagedPreset, r as removeManagedPreset } from "./preset-installer-XBN9ywDk.mjs";
2
+ import { i as parseClaudeVersion, n as ensureManagedPreset, r as removeManagedPreset } from "./preset-installer-D8q3_-wJ.mjs";
3
3
  import { access } from "node:fs/promises";
4
4
  import { delimiter, isAbsolute, join } from "node:path";
5
5
  import { homedir } from "node:os";
package/lib/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { a as latestClaudeTasks, b as isClaudePresetId, c as normalizeTasksEvent, d as CLAUDE_ACTIVITY_EVENT, f as CLAUDE_CODE_PRESET_ID, g as CLAUDE_PROJECTION_PATH, h as CLAUDE_DOCTOR_PATH, i as latestClaudeSessionBinding, l as redactText, m as CLAUDE_CODE_PROVIDER_IDS, n as currentClaudeActivityCursor, o as normalizeActivity, p as CLAUDE_CODE_PROVIDER, r as latestClaudeContextUsage, s as normalizeContextUsage, t as boundText, u as safeDetail, y as TASK_TOOL_NAMES } from "./events-qlmU1KrH.mjs";
2
2
  import { a as ClaudeCommandBridge, i as CLAUDE_COMMANDS_SERVICE, r as dynamicPresenterDefinition, t as CLAUDE_PRESENTER_NAMES } from "./presenters-DbfG-9KQ.mjs";
3
- import { a as resolveClaudeExecutable, o as runClaudeDoctor, r as removeManagedPreset, t as ManagedPresetConflictError } from "./preset-installer-XBN9ywDk.mjs";
3
+ import { a as resolveClaudeExecutable, o as runClaudeDoctor, r as removeManagedPreset, t as ManagedPresetConflictError } from "./preset-installer-D8q3_-wJ.mjs";
4
4
  import z from "@deepseek-ai/schemastery";
5
5
  import { CallId, LlmAdapter, ReasoningEffortId, createToolResultMessage, createUserMessage } from "@deepseek-ai/dsh-llm";
6
6
  import { randomUUID } from "node:crypto";
@@ -172,7 +172,7 @@ var ManagedPresetConflictError = class extends Error {
172
172
  function defaultManagedPresetPaths(dshHome) {
173
173
  const packageRoot = fileURLToPath(new URL("../", import.meta.url));
174
174
  return {
175
- sourceDir: join(packageRoot, "preset"),
175
+ sourceDir: join(packageRoot, "legacy-preset"),
176
176
  targetDir: dshHome === void 0 ? dshHomePath(".agent-presets", CLAUDE_CODE_PRESET_ID) : join(dshHome, ".agent-presets", CLAUDE_CODE_PRESET_ID),
177
177
  legacyTargetDir: dshHome === void 0 ? dshHomePath(".agent-presets", LEGACY_CLAUDE_CODE_PRESET_ID) : join(dshHome, ".agent-presets", LEGACY_CLAUDE_CODE_PRESET_ID)
178
178
  };
@@ -289,4 +289,4 @@ async function removeManagedPreset(paths = defaultManagedPresetPaths()) {
289
289
  //#endregion
290
290
  export { resolveClaudeExecutable as a, parseClaudeVersion as i, ensureManagedPreset as n, runClaudeDoctor as o, removeManagedPreset as r, ManagedPresetConflictError as t };
291
291
 
292
- //# sourceMappingURL=preset-installer-XBN9ywDk.mjs.map
292
+ //# sourceMappingURL=preset-installer-D8q3_-wJ.mjs.map
@@ -0,0 +1 @@
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":"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"}
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.3",
4
+ "version": "0.1.4",
5
5
  "type": "module",
6
6
  "main": "lib/index.mjs",
7
7
  "types": "lib/index.d.mts",
@@ -32,6 +32,7 @@
32
32
  "files": [
33
33
  "lib",
34
34
  "preset",
35
+ "legacy-preset",
35
36
  "cordis.patch.yml",
36
37
  "README.md",
37
38
  "INSTALL.md",
@@ -0,0 +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
@@ -0,0 +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 +0,0 @@
1
- {"version":3,"file":"preset-installer-XBN9ywDk.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, '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":"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"}
File without changes
File without changes