@norman-else/dsh-claude 0.1.9 → 0.1.11
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 +1 -1
- package/lib/client.d.ts +6 -0
- package/lib/client.js +302 -1
- package/lib/client.js.map +1 -1
- package/lib/{events-BVQkPmjg.mjs → events-6xTApIQw.mjs} +3 -2
- package/lib/events-6xTApIQw.mjs.map +1 -0
- package/lib/index.mjs +212 -13
- package/lib/index.mjs.map +1 -1
- package/lib/{presenters-CQ9dBVyY.mjs → presenters-BE6LPw2m.mjs} +5 -5
- package/lib/presenters-BE6LPw2m.mjs.map +1 -0
- package/lib/{preset-installer-DUmmDvBm.mjs → preset-installer-B0NrA4Hi.mjs} +2 -2
- package/lib/{preset-installer-DUmmDvBm.mjs.map → preset-installer-B0NrA4Hi.mjs.map} +1 -1
- package/lib/preset-route.mjs +2 -2
- package/package.json +1 -1
- package/lib/events-BVQkPmjg.mjs.map +0 -1
- package/lib/presenters-CQ9dBVyY.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events-6xTApIQw.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 CLAUDE_CODE_PROVIDER_IDS = [CLAUDE_CODE_PROVIDER] as const\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_UPDATE_CHECK_PATH = '/plugins/dsh-claude/update/check'\nexport const CLAUDE_UPDATE_PATH = '/plugins/dsh-claude/update'\nexport const CLAUDE_PROJECTION_PATH = '/plugins/dsh-claude/projection'\nexport const CLAUDE_GLOBAL_SETTINGS_PATH = '/plugins/dsh-claude/settings/global'\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 | 'question'\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":"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"}
|
package/lib/index.mjs
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { _ as
|
|
2
|
-
import { a as ClaudeCommandBridge, i as CLAUDE_COMMANDS_SERVICE, r as dynamicPresenterDefinition, t as CLAUDE_PRESENTER_NAMES } from "./presenters-
|
|
3
|
-
import { a as resolveClaudeExecutable, n as ensureManagedPreset, o as runClaudeDoctor, t as ManagedPresetConflictError } from "./preset-installer-
|
|
1
|
+
import { _ as CLAUDE_PROJECTION_PATH, a as latestClaudeTasks, c as normalizeTasksEvent, d as CLAUDE_ACTIVITY_EVENT, f as CLAUDE_CODE_PRESET_ID, g as CLAUDE_GLOBAL_SETTINGS_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, v as CLAUDE_UPDATE_CHECK_PATH, x as TASK_TOOL_NAMES, y as CLAUDE_UPDATE_PATH } from "./events-6xTApIQw.mjs";
|
|
2
|
+
import { a as ClaudeCommandBridge, i as CLAUDE_COMMANDS_SERVICE, r as dynamicPresenterDefinition, t as CLAUDE_PRESENTER_NAMES } from "./presenters-BE6LPw2m.mjs";
|
|
3
|
+
import { a as resolveClaudeExecutable, n as ensureManagedPreset, o as runClaudeDoctor, t as ManagedPresetConflictError } from "./preset-installer-B0NrA4Hi.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";
|
|
7
7
|
import { chmod, mkdir, opendir, readFile, realpath, rename, rm, writeFile } from "node:fs/promises";
|
|
8
|
-
import { dirname, join, resolve } from "node:path";
|
|
8
|
+
import { dirname, extname, join, resolve } from "node:path";
|
|
9
9
|
import { dshHomePath, resolveDshHome } from "@deepseek-ai/dsh-home-paths";
|
|
10
|
+
import { homedir } from "node:os";
|
|
10
11
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
11
12
|
import { EventEmitter } from "node:events";
|
|
12
13
|
import { DSH_ENV_PREFIX, SENSITIVE_ENV_PATTERN } from "@deepseek-ai/dsh-subprocess";
|
|
@@ -1344,6 +1345,7 @@ var ClaudeSupervisor = class {
|
|
|
1344
1345
|
}
|
|
1345
1346
|
async #handleMessage(entry, message) {
|
|
1346
1347
|
if (message.kind === "init") {
|
|
1348
|
+
const firstInitialization = !entry.initialized;
|
|
1347
1349
|
if (entry.expectedResume !== void 0 && message.sessionId !== entry.expectedResume) throw new ClaudeProtocolError(`Claude Code resumed unexpected session ${message.sessionId}; expected ${entry.expectedResume}`);
|
|
1348
1350
|
if (message.cwd !== entry.cwd) throw new ClaudeProtocolError(`Claude Code initialized in unexpected cwd ${message.cwd}; expected ${entry.cwd}`);
|
|
1349
1351
|
if (entry.initTimer !== void 0) {
|
|
@@ -1354,7 +1356,7 @@ var ClaudeSupervisor = class {
|
|
|
1354
1356
|
entry.claudeSessionId = message.sessionId;
|
|
1355
1357
|
entry.state = entry.active === void 0 ? "idle" : "running";
|
|
1356
1358
|
for (const waiter of entry.initWaiters.splice(0)) waiter(void 0);
|
|
1357
|
-
if (entry.tasks.size > 0) {
|
|
1359
|
+
if (firstInitialization && entry.tasks.size > 0) {
|
|
1358
1360
|
entry.tasks.clear();
|
|
1359
1361
|
await this.#flushTasksSnapshot(entry);
|
|
1360
1362
|
}
|
|
@@ -2457,6 +2459,11 @@ async function checkPluginUpdate(deps = {}) {
|
|
|
2457
2459
|
};
|
|
2458
2460
|
}
|
|
2459
2461
|
}
|
|
2462
|
+
async function verifyInstalledVersion(installation, expectedVersion) {
|
|
2463
|
+
if (dependencySpec(await readManifest(join(installation.profileDir, "package.json"))) !== expectedVersion) throw new Error("DSH plugin update completed without updating the profile dependency");
|
|
2464
|
+
const installedManifest = await readManifest(join(installation.profileDir, "node_modules", ...PLUGIN_PACKAGE_NAME.split("/"), "package.json"));
|
|
2465
|
+
if (installedManifest.name !== "@norman-else/dsh-claude" || installedManifest.version !== expectedVersion) throw new Error("DSH plugin update completed without installing the requested version");
|
|
2466
|
+
}
|
|
2460
2467
|
async function updatePlugin(deps = {}) {
|
|
2461
2468
|
const { version, installation } = await packageContext(deps);
|
|
2462
2469
|
if (installation === void 0 || installation.source !== "registry") throw new Error("Plugin update is unavailable for this installation");
|
|
@@ -2479,8 +2486,8 @@ async function updatePlugin(deps = {}) {
|
|
|
2479
2486
|
"plugin",
|
|
2480
2487
|
"--profile",
|
|
2481
2488
|
installation.profile,
|
|
2482
|
-
"
|
|
2483
|
-
PLUGIN_PACKAGE_NAME
|
|
2489
|
+
"add",
|
|
2490
|
+
`${PLUGIN_PACKAGE_NAME}@${latest}`
|
|
2484
2491
|
],
|
|
2485
2492
|
cwd: installation.profileDir,
|
|
2486
2493
|
env: {},
|
|
@@ -2497,14 +2504,16 @@ async function updatePlugin(deps = {}) {
|
|
|
2497
2504
|
const detail = handle.collected.stderr?.readFrom(0).text ?? "";
|
|
2498
2505
|
throw new Error(`DSH plugin update failed (${outcome.exitCode ?? outcome.signal ?? "unknown exit"}): ${safeMessage(detail)}`);
|
|
2499
2506
|
}
|
|
2507
|
+
await verifyInstalledVersion(installation, latest);
|
|
2508
|
+
if (deps.requestRestart !== void 0) setTimeout(() => deps.requestRestart?.(), 100).unref?.();
|
|
2500
2509
|
return {
|
|
2501
|
-
currentVersion:
|
|
2510
|
+
currentVersion: latest,
|
|
2502
2511
|
latestVersion: latest,
|
|
2503
2512
|
source: "registry",
|
|
2504
2513
|
state: "current",
|
|
2505
2514
|
canUpdate: false,
|
|
2506
|
-
restartRequired:
|
|
2507
|
-
message: "Update installed; restart DSH Desktop to load it"
|
|
2515
|
+
restartRequired: deps.requestRestart === void 0,
|
|
2516
|
+
message: deps.requestRestart === void 0 ? "Update installed; restart DSH Desktop to load it" : "Update installed; DSH Desktop is restarting to load it"
|
|
2508
2517
|
};
|
|
2509
2518
|
}
|
|
2510
2519
|
function registerClaudeUpdateRoutes(ctx, runtime, deps = {}) {
|
|
@@ -2536,6 +2545,194 @@ function registerClaudeUpdateRoutes(ctx, runtime, deps = {}) {
|
|
|
2536
2545
|
}), `dsh-claude: ${route.method} ${route.path}`);
|
|
2537
2546
|
}
|
|
2538
2547
|
//#endregion
|
|
2548
|
+
//#region src/global-settings.ts
|
|
2549
|
+
const MAX_SETTINGS_BYTES = 262144;
|
|
2550
|
+
const MAX_REQUEST_BYTES = 8192;
|
|
2551
|
+
const MAX_STYLE_BYTES = 65536;
|
|
2552
|
+
const MAX_STYLE_FILES = 256;
|
|
2553
|
+
const BUILTIN_OUTPUT_STYLES = [
|
|
2554
|
+
"Default",
|
|
2555
|
+
"Proactive",
|
|
2556
|
+
"Concise",
|
|
2557
|
+
"Explanatory",
|
|
2558
|
+
"Learning"
|
|
2559
|
+
];
|
|
2560
|
+
const STYLE_NAME = /^[\p{L}\p{N}][\p{L}\p{N} ._()\[\]-]{0,127}$/u;
|
|
2561
|
+
function pathsFor(deps) {
|
|
2562
|
+
const root = join(homedir(), ".claude");
|
|
2563
|
+
return {
|
|
2564
|
+
settingsFile: deps.paths?.settingsFile ?? join(root, "settings.json"),
|
|
2565
|
+
outputStylesDir: deps.paths?.outputStylesDir ?? join(root, "output-styles")
|
|
2566
|
+
};
|
|
2567
|
+
}
|
|
2568
|
+
function object(value) {
|
|
2569
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
2570
|
+
}
|
|
2571
|
+
async function readDocument(path) {
|
|
2572
|
+
try {
|
|
2573
|
+
const text = await readFile(path, "utf8");
|
|
2574
|
+
if (Buffer.byteLength(text) > MAX_SETTINGS_BYTES) throw new Error("Claude Code settings file is too large");
|
|
2575
|
+
const parsed = object(JSON.parse(text));
|
|
2576
|
+
if (parsed === void 0) throw new Error("Claude Code settings file must contain a JSON object");
|
|
2577
|
+
return parsed;
|
|
2578
|
+
} catch (error) {
|
|
2579
|
+
if (error.code === "ENOENT") return {};
|
|
2580
|
+
throw error;
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2583
|
+
function frontmatterName(text) {
|
|
2584
|
+
if (!text.startsWith("---\n") && !text.startsWith("---\r\n")) return void 0;
|
|
2585
|
+
const normalized = text.replaceAll("\r\n", "\n");
|
|
2586
|
+
const end = normalized.indexOf("\n---\n", 4);
|
|
2587
|
+
if (end < 0) return void 0;
|
|
2588
|
+
for (const line of normalized.slice(4, end).split("\n")) {
|
|
2589
|
+
const match = /^name:\s*(.+?)\s*$/.exec(line);
|
|
2590
|
+
if (match?.[1] === void 0) continue;
|
|
2591
|
+
const raw = match[1];
|
|
2592
|
+
const value = raw.startsWith("\"") && raw.endsWith("\"") || raw.startsWith("'") && raw.endsWith("'") ? raw.slice(1, -1) : raw;
|
|
2593
|
+
return STYLE_NAME.test(value) ? value : void 0;
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
async function userOutputStyleOptions(directory) {
|
|
2597
|
+
const options = [];
|
|
2598
|
+
let entries;
|
|
2599
|
+
try {
|
|
2600
|
+
entries = await opendir(directory);
|
|
2601
|
+
} catch (error) {
|
|
2602
|
+
if (error.code === "ENOENT") return options;
|
|
2603
|
+
throw error;
|
|
2604
|
+
}
|
|
2605
|
+
for await (const entry of entries) {
|
|
2606
|
+
if (options.length >= MAX_STYLE_FILES) break;
|
|
2607
|
+
if (!entry.isFile() || extname(entry.name).toLowerCase() !== ".md") continue;
|
|
2608
|
+
try {
|
|
2609
|
+
const text = await readFile(join(directory, entry.name), "utf8");
|
|
2610
|
+
if (Buffer.byteLength(text) > MAX_STYLE_BYTES) continue;
|
|
2611
|
+
const name = frontmatterName(text) ?? entry.name.slice(0, -3);
|
|
2612
|
+
if (STYLE_NAME.test(name)) options.push({
|
|
2613
|
+
value: name,
|
|
2614
|
+
label: name,
|
|
2615
|
+
source: "user"
|
|
2616
|
+
});
|
|
2617
|
+
} catch {}
|
|
2618
|
+
}
|
|
2619
|
+
return options;
|
|
2620
|
+
}
|
|
2621
|
+
const DESCRIPTORS = [{
|
|
2622
|
+
key: "outputStyle",
|
|
2623
|
+
kind: "select",
|
|
2624
|
+
effect: "new-session",
|
|
2625
|
+
async options(paths) {
|
|
2626
|
+
const builtIn = BUILTIN_OUTPUT_STYLES.map((value) => ({
|
|
2627
|
+
value,
|
|
2628
|
+
label: value,
|
|
2629
|
+
source: "built-in"
|
|
2630
|
+
}));
|
|
2631
|
+
const user = await userOutputStyleOptions(paths.outputStylesDir);
|
|
2632
|
+
const seen = new Set(builtIn.map((option) => option.value));
|
|
2633
|
+
return [...builtIn, ...user.filter((option) => !seen.has(option.value)).sort((a, b) => a.label.localeCompare(b.label))];
|
|
2634
|
+
},
|
|
2635
|
+
read(document, options) {
|
|
2636
|
+
const value = document.outputStyle;
|
|
2637
|
+
return typeof value === "string" && STYLE_NAME.test(value) ? value : "Default";
|
|
2638
|
+
},
|
|
2639
|
+
apply(document, value, options) {
|
|
2640
|
+
if (typeof value !== "string" || !options.some((option) => option.value === value)) throw new Error("Invalid value for global setting outputStyle");
|
|
2641
|
+
if (value === "Default") delete document.outputStyle;
|
|
2642
|
+
else document.outputStyle = value;
|
|
2643
|
+
}
|
|
2644
|
+
}];
|
|
2645
|
+
const DESCRIPTOR_BY_KEY = new Map(DESCRIPTORS.map((descriptor) => [descriptor.key, descriptor]));
|
|
2646
|
+
let pendingWrite = Promise.resolve();
|
|
2647
|
+
async function views(document, paths) {
|
|
2648
|
+
return { settings: await Promise.all(DESCRIPTORS.map(async (descriptor) => {
|
|
2649
|
+
const discovered = await descriptor.options(paths);
|
|
2650
|
+
const value = descriptor.read(document, discovered);
|
|
2651
|
+
const options = discovered.some((option) => option.value === value) ? discovered : [...discovered, {
|
|
2652
|
+
value,
|
|
2653
|
+
label: value,
|
|
2654
|
+
source: "configured"
|
|
2655
|
+
}];
|
|
2656
|
+
return {
|
|
2657
|
+
key: descriptor.key,
|
|
2658
|
+
kind: descriptor.kind,
|
|
2659
|
+
value,
|
|
2660
|
+
options,
|
|
2661
|
+
effect: descriptor.effect
|
|
2662
|
+
};
|
|
2663
|
+
})) };
|
|
2664
|
+
}
|
|
2665
|
+
async function readGlobalSettings(deps = {}) {
|
|
2666
|
+
const paths = pathsFor(deps);
|
|
2667
|
+
return views(await readDocument(paths.settingsFile), paths);
|
|
2668
|
+
}
|
|
2669
|
+
async function atomicWrite(path, document) {
|
|
2670
|
+
await mkdir(dirname(path), {
|
|
2671
|
+
recursive: true,
|
|
2672
|
+
mode: 448
|
|
2673
|
+
});
|
|
2674
|
+
const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`;
|
|
2675
|
+
try {
|
|
2676
|
+
await writeFile(temporary, `${JSON.stringify(document, null, 2)}\n`, {
|
|
2677
|
+
encoding: "utf8",
|
|
2678
|
+
mode: 384,
|
|
2679
|
+
flag: "wx"
|
|
2680
|
+
});
|
|
2681
|
+
await chmod(temporary, 384);
|
|
2682
|
+
await rename(temporary, path);
|
|
2683
|
+
await chmod(path, 384);
|
|
2684
|
+
} finally {
|
|
2685
|
+
await rm(temporary, { force: true }).catch(() => void 0);
|
|
2686
|
+
}
|
|
2687
|
+
}
|
|
2688
|
+
function updateGlobalSettings(changes, deps = {}) {
|
|
2689
|
+
const changeObject = object(changes);
|
|
2690
|
+
if (changeObject === void 0 || Object.keys(changeObject).length === 0) return Promise.reject(/* @__PURE__ */ new Error("Global settings changes must be a non-empty object"));
|
|
2691
|
+
for (const key of Object.keys(changeObject)) if (!DESCRIPTOR_BY_KEY.has(key)) return Promise.reject(/* @__PURE__ */ new Error(`Unsupported global setting: ${key}`));
|
|
2692
|
+
const paths = pathsFor(deps);
|
|
2693
|
+
const operation = pendingWrite.catch(() => void 0).then(async () => {
|
|
2694
|
+
const document = await readDocument(paths.settingsFile);
|
|
2695
|
+
for (const [key, value] of Object.entries(changeObject)) {
|
|
2696
|
+
const descriptor = DESCRIPTOR_BY_KEY.get(key);
|
|
2697
|
+
descriptor.apply(document, value, await descriptor.options(paths));
|
|
2698
|
+
}
|
|
2699
|
+
await atomicWrite(paths.settingsFile, document);
|
|
2700
|
+
return views(document, paths);
|
|
2701
|
+
});
|
|
2702
|
+
pendingWrite = operation;
|
|
2703
|
+
return operation;
|
|
2704
|
+
}
|
|
2705
|
+
async function requestJson(req) {
|
|
2706
|
+
const declared = Number(req.headers["content-length"] ?? 0);
|
|
2707
|
+
if (!Number.isFinite(declared) || declared < 0 || declared > MAX_REQUEST_BYTES) throw new Error("Request body is too large");
|
|
2708
|
+
const chunks = [];
|
|
2709
|
+
let bytes = 0;
|
|
2710
|
+
for await (const chunk of req) {
|
|
2711
|
+
const data = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
2712
|
+
bytes += data.byteLength;
|
|
2713
|
+
if (bytes > MAX_REQUEST_BYTES) throw new Error("Request body is too large");
|
|
2714
|
+
chunks.push(data);
|
|
2715
|
+
}
|
|
2716
|
+
const body = object(JSON.parse(Buffer.concat(chunks).toString("utf8")));
|
|
2717
|
+
if (body === void 0 || Object.keys(body).some((key) => key !== "changes")) throw new Error("Invalid global settings request");
|
|
2718
|
+
return body.changes;
|
|
2719
|
+
}
|
|
2720
|
+
function registerClaudeGlobalSettingsRoute(ctx, deps = {}) {
|
|
2721
|
+
ctx.effect(() => ctx.webServer.register({
|
|
2722
|
+
kind: "exact",
|
|
2723
|
+
path: CLAUDE_GLOBAL_SETTINGS_PATH,
|
|
2724
|
+
handler: async (req, res) => {
|
|
2725
|
+
if (req.method !== "GET" && req.method !== "PATCH") return json(res, 405, { error: "method not allowed" });
|
|
2726
|
+
if (!trustedRequest(req)) return json(res, 403, { error: "forbidden" });
|
|
2727
|
+
try {
|
|
2728
|
+
json(res, 200, req.method === "GET" ? await readGlobalSettings(deps) : await updateGlobalSettings(await requestJson(req), deps));
|
|
2729
|
+
} catch (error) {
|
|
2730
|
+
json(res, 400, { error: error instanceof Error ? error.message : "Invalid global settings request" });
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2733
|
+
}), "dsh-claude: global settings");
|
|
2734
|
+
}
|
|
2735
|
+
//#endregion
|
|
2539
2736
|
//#region src/index.ts
|
|
2540
2737
|
const name = "llm-claude";
|
|
2541
2738
|
const inject = [
|
|
@@ -2573,8 +2770,8 @@ function mountClaudeMetadata(ctx, supervisor, agent, model, sidecar, resolveComm
|
|
|
2573
2770
|
const bridge = new ClaudeCommandBridge({
|
|
2574
2771
|
list: () => scopedCommands().list(agent),
|
|
2575
2772
|
register: (definition) => scopedCommands().register(definition),
|
|
2576
|
-
forward: (line) => {
|
|
2577
|
-
|
|
2773
|
+
forward: (receivingAgent, line) => {
|
|
2774
|
+
receivingAgent.followup(createUserMessage({
|
|
2578
2775
|
content: [{
|
|
2579
2776
|
type: "text",
|
|
2580
2777
|
text: line
|
|
@@ -2748,7 +2945,9 @@ async function apply(ctx, config) {
|
|
|
2748
2945
|
ctx.effect(() => () => supervisor.dispose(), "dsh-claude: process supervisor");
|
|
2749
2946
|
ctx.inject(["webServer"], (webCtx) => {
|
|
2750
2947
|
registerClaudeDoctorRoutes(webCtx, webCtx.subprocess, supervisor, supervisorConfig, resolutionError);
|
|
2751
|
-
|
|
2948
|
+
const desktopActions = webCtx.get("desktopActions");
|
|
2949
|
+
registerClaudeUpdateRoutes(webCtx, webCtx.subprocess, { ...typeof desktopActions?.requestRestart === "function" ? { requestRestart: desktopActions.requestRestart.bind(desktopActions) } : {} });
|
|
2950
|
+
registerClaudeGlobalSettingsRoute(webCtx);
|
|
2752
2951
|
registerClaudeProjectionRoute(webCtx, sidecar, (sessionId) => {
|
|
2753
2952
|
const agent = webCtx.agents.get(sessionId);
|
|
2754
2953
|
return agent !== void 0 && webCtx.agentPresets.composedPreset(agent.ctx) === "claude";
|