@norman-else/dsh-claude 0.1.26 → 0.1.27
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 +20 -3
- package/lib/client.js +417 -165
- package/lib/client.js.map +1 -1
- package/lib/{command-bridge-vyZoEeEO.mjs → command-bridge-BYj0VF4J.mjs} +2 -2
- package/lib/{command-bridge-vyZoEeEO.mjs.map → command-bridge-BYj0VF4J.mjs.map} +1 -1
- package/lib/{events-CY6V9oQ4.mjs → events-lDt9nTUw.mjs} +3 -2
- package/lib/events-lDt9nTUw.mjs.map +1 -0
- package/lib/index.d.mts +4 -0
- package/lib/index.mjs +194 -34
- package/lib/index.mjs.map +1 -1
- package/lib/{preset-installer-BMRaIHLu.mjs → preset-installer-CPOH9lAr.mjs} +2 -2
- package/lib/{preset-installer-BMRaIHLu.mjs.map → preset-installer-CPOH9lAr.mjs.map} +1 -1
- package/lib/preset-route.mjs +2 -2
- package/package.json +1 -1
- package/lib/events-CY6V9oQ4.mjs.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { l as redactText } from "./events-
|
|
1
|
+
import { l as redactText } from "./events-lDt9nTUw.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;
|
|
@@ -68,4 +68,4 @@ function projectClaudeCommands(catalog, target) {
|
|
|
68
68
|
//#endregion
|
|
69
69
|
export { projectClaudeCommands as n, CLAUDE_COMMANDS_SERVICE as t };
|
|
70
70
|
|
|
71
|
-
//# sourceMappingURL=command-bridge-
|
|
71
|
+
//# sourceMappingURL=command-bridge-BYj0VF4J.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-bridge-
|
|
1
|
+
{"version":3,"file":"command-bridge-BYj0VF4J.mjs","names":[],"sources":["../src/command-bridge.ts"],"sourcesContent":["import type { SlashCommand } from '@anthropic-ai/claude-agent-sdk'\nimport type { 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}\n\n/**\n * Agent-scope command-directory 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. The Host reads only the exact agent's effective names\n * for collision checks; Claude catalog entries are never registered there.\n */\nexport interface ClaudeAgentCommandService {\n list(agent: unknown): readonly Pick<CommandDescriptor, 'name'>[]\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 description: string\n hint?: string\n prefixed: boolean\n}\n\nfunction bounded(value: string, maxChars: number): string {\n return redactText(value, maxChars)\n}\n\nexport function projectClaudeCommands(\n catalog: readonly SlashCommand[],\n target: ClaudeCommandTarget,\n): readonly ClaudeCommandView[] {\n const reservedNames = new Set(target.list().map(command => command.name))\n // Client contributions are absent from the Host directory but share the\n // same slash menu, so reserve their public names explicitly.\n for (const name of CLIENT_CONTRIBUTION_NAMES) reservedNames.add(name)\n const assigned = new Set<string>()\n const views: ClaudeCommandView[] = []\n for (const command of catalog) {\n const names = [command.name, ...(command.aliases ?? [])]\n const commandBases = new Set<string>()\n for (const claudeName of names) {\n const base = registryName(claudeName)\n if (base === undefined || commandBases.has(base)) continue\n commandBases.add(base)\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 views.push({\n publicName,\n claudeName,\n description,\n ...(hint.length === 0 ? {} : { hint }),\n prefixed,\n })\n assigned.add(publicName)\n }\n }\n return views\n}\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;;;;;;;AAuBxE,MAAa,0BAA0B;AAgBvC,SAAS,QAAQ,OAAe,UAA0B;CACxD,OAAO,WAAW,OAAO,QAAQ;AACnC;AAEA,SAAgB,sBACd,SACA,QAC8B;CAC9B,MAAM,gBAAgB,IAAI,IAAI,OAAO,KAAK,CAAC,CAAC,KAAI,YAAW,QAAQ,IAAI,CAAC;CAGxE,KAAK,MAAM,QAAQ,2BAA2B,cAAc,IAAI,IAAI;CACpE,MAAM,2BAAW,IAAI,IAAY;CACjC,MAAM,QAA6B,CAAC;CACpC,KAAK,MAAM,WAAW,SAAS;EAC7B,MAAM,QAAQ,CAAC,QAAQ,MAAM,GAAI,QAAQ,WAAW,CAAC,CAAE;EACvD,MAAM,+BAAe,IAAI,IAAY;EACrC,KAAK,MAAM,cAAc,OAAO;GAC9B,MAAM,OAAO,aAAa,UAAU;GACpC,IAAI,SAAS,KAAA,KAAa,aAAa,IAAI,IAAI,GAAG;GAClD,aAAa,IAAI,IAAI;GACrB,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,KAAK;IACT;IACA;IACA;IACA,GAAI,KAAK,WAAW,IAAI,CAAC,IAAI,EAAE,KAAK;IACpC;GACF,CAAC;GACD,SAAS,IAAI,UAAU;EACzB;CACF;CACA,OAAO;AACT"}
|
|
@@ -9,6 +9,7 @@ const TASK_TOOL_NAMES = /* @__PURE__ */ new Set(["Task", "Agent"]);
|
|
|
9
9
|
const SDK_VERSION = "0.3.247";
|
|
10
10
|
const CLAUDE_DOCTOR_PATH = "/plugins/dsh-claude/doctor";
|
|
11
11
|
const CLAUDE_UPDATE_CHECK_PATH = "/plugins/dsh-claude/update/check";
|
|
12
|
+
const CLAUDE_USAGE_PATH = "/plugins/dsh-claude/usage";
|
|
12
13
|
const CLAUDE_UPDATE_PATH = "/plugins/dsh-claude/update";
|
|
13
14
|
const CLAUDE_PROJECTION_PATH = "/plugins/dsh-claude/projection";
|
|
14
15
|
const CLAUDE_GLOBAL_SETTINGS_PATH = "/plugins/dsh-claude/settings/global";
|
|
@@ -189,6 +190,6 @@ function latestClaudeSessionBinding(events) {
|
|
|
189
190
|
}
|
|
190
191
|
}
|
|
191
192
|
//#endregion
|
|
192
|
-
export { CLAUDE_REPOSITORY_SETUP_PATH as C, CLAUDE_UPDATE_PATH as D, CLAUDE_UPDATE_CHECK_PATH as E,
|
|
193
|
+
export { TASK_TOOL_NAMES as A, CLAUDE_REPOSITORY_SETUP_PATH as C, CLAUDE_UPDATE_PATH as D, CLAUDE_UPDATE_CHECK_PATH as E, CLAUDE_USAGE_PATH as O, CLAUDE_REPOSITORY_FILE_PATH as S, CLAUDE_REVIEW_COMMENT_PATH as T, CLAUDE_GLOBAL_SETTINGS_PATH as _, latestClaudeTasks as a, CLAUDE_REPOSITORY_ACTION_PATH as b, normalizeTasksEvent as c, CLAUDE_ACTIVITY_EVENT as d, CLAUDE_ASK_PATH as f, CLAUDE_DOCTOR_PATH as g, CLAUDE_CODE_PROVIDER_IDS as h, latestClaudeSessionBinding as i, SDK_VERSION as k, redactText as l, CLAUDE_CODE_PROVIDER as m, currentClaudeActivityCursor as n, normalizeActivity as o, CLAUDE_CODE_PRESET_ID as p, latestClaudeContextUsage as r, normalizeContextUsage as s, boundText as t, safeDetail as u, CLAUDE_JIRA_PATH as v, CLAUDE_REPOSITORY_STATUS_PATH as w, CLAUDE_REPOSITORY_FEEDBACK_PATH as x, CLAUDE_PROJECTION_PATH as y };
|
|
193
194
|
|
|
194
|
-
//# sourceMappingURL=events-
|
|
195
|
+
//# sourceMappingURL=events-lDt9nTUw.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events-lDt9nTUw.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.247'\nexport const CLAUDE_DOCTOR_PATH = '/plugins/dsh-claude/doctor'\nexport const CLAUDE_UPDATE_CHECK_PATH = '/plugins/dsh-claude/update/check'\nexport const CLAUDE_USAGE_PATH = '/plugins/dsh-claude/usage'\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'\nexport const CLAUDE_REPOSITORY_SETUP_PATH = '/plugins/dsh-claude/repository/setup'\nexport const CLAUDE_REPOSITORY_ACTION_PATH = '/plugins/dsh-claude/repository/action'\nexport const CLAUDE_REVIEW_COMMENT_PATH = '/plugins/dsh-claude/review-comments'\nexport const CLAUDE_REPOSITORY_FEEDBACK_PATH = '/plugins/dsh-claude/repository/feedback'\nexport const CLAUDE_REPOSITORY_STATUS_PATH = '/plugins/dsh-claude/repository/status'\nexport const CLAUDE_REPOSITORY_FILE_PATH = '/plugins/dsh-claude/repository/file'\nexport const CLAUDE_JIRA_PATH = '/plugins/dsh-claude/jira'\nexport const CLAUDE_ASK_PATH = '/plugins/dsh-claude/ask'\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 | 'text'\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 /** Redacted visible Claude prose used by the plugin-owned interleaved transcript. */\n text?: 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_TRANSCRIPT_TEXT_CHARS = 64_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.text !== undefined) normalized.text = redactText(activity.text, MAX_TRANSCRIPT_TEXT_CHARS)\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.d.mts
CHANGED
|
@@ -253,6 +253,10 @@ declare class ClaudeSupervisor {
|
|
|
253
253
|
supportedCommands(agent: Agent, model?: string): Promise<readonly SlashCommand[]>;
|
|
254
254
|
contextUsage(agent: Agent, model?: string): Promise<SDKControlGetContextUsageResponse>;
|
|
255
255
|
contextWindow(model: string): number | undefined;
|
|
256
|
+
/** Raw `/usage` payload, read over a session's existing process. Only the
|
|
257
|
+
* idle-time metadata bridge uses this; a user-triggered refresh runs
|
|
258
|
+
* probePlanUsage instead so it never waits on, or perturbs, a session. */
|
|
259
|
+
planUsage(agent: Agent, model?: string): Promise<unknown>;
|
|
256
260
|
runTurn(request: ClaudeTurnRequest): Promise<AsyncIterable<ClaudeTurnStreamEvent>>;
|
|
257
261
|
disposeSession(sessionId: string): Promise<void>;
|
|
258
262
|
dispose(): Promise<void>;
|
package/lib/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { C as CLAUDE_REPOSITORY_SETUP_PATH, D as CLAUDE_UPDATE_PATH, E as CLAUDE_UPDATE_CHECK_PATH, S as CLAUDE_REPOSITORY_FILE_PATH, T as CLAUDE_REVIEW_COMMENT_PATH, _ as CLAUDE_GLOBAL_SETTINGS_PATH, a as latestClaudeTasks, b as CLAUDE_REPOSITORY_ACTION_PATH, c as normalizeTasksEvent, d as CLAUDE_ACTIVITY_EVENT, f as CLAUDE_ASK_PATH, g as CLAUDE_DOCTOR_PATH, h as CLAUDE_CODE_PROVIDER_IDS, i as latestClaudeSessionBinding,
|
|
2
|
-
import { n as projectClaudeCommands, t as CLAUDE_COMMANDS_SERVICE } from "./command-bridge-
|
|
3
|
-
import { a as resolveClaudeExecutable, n as ensureManagedPreset, o as runClaudeDoctor, t as ManagedPresetConflictError } from "./preset-installer-
|
|
1
|
+
import { A as TASK_TOOL_NAMES, C as CLAUDE_REPOSITORY_SETUP_PATH, D as CLAUDE_UPDATE_PATH, E as CLAUDE_UPDATE_CHECK_PATH, O as CLAUDE_USAGE_PATH, S as CLAUDE_REPOSITORY_FILE_PATH, T as CLAUDE_REVIEW_COMMENT_PATH, _ as CLAUDE_GLOBAL_SETTINGS_PATH, a as latestClaudeTasks, b as CLAUDE_REPOSITORY_ACTION_PATH, c as normalizeTasksEvent, d as CLAUDE_ACTIVITY_EVENT, f as CLAUDE_ASK_PATH, g as CLAUDE_DOCTOR_PATH, h as CLAUDE_CODE_PROVIDER_IDS, i as latestClaudeSessionBinding, l as redactText, m as CLAUDE_CODE_PROVIDER, n as currentClaudeActivityCursor, o as normalizeActivity, p as CLAUDE_CODE_PRESET_ID, r as latestClaudeContextUsage, s as normalizeContextUsage, t as boundText, u as safeDetail, v as CLAUDE_JIRA_PATH, w as CLAUDE_REPOSITORY_STATUS_PATH, x as CLAUDE_REPOSITORY_FEEDBACK_PATH, y as CLAUDE_PROJECTION_PATH } from "./events-lDt9nTUw.mjs";
|
|
2
|
+
import { n as projectClaudeCommands, t as CLAUDE_COMMANDS_SERVICE } from "./command-bridge-BYj0VF4J.mjs";
|
|
3
|
+
import { a as resolveClaudeExecutable, n as ensureManagedPreset, o as runClaudeDoctor, t as ManagedPresetConflictError } from "./preset-installer-CPOH9lAr.mjs";
|
|
4
4
|
import z from "@deepseek-ai/schemastery";
|
|
5
5
|
import { createHash, randomUUID } from "node:crypto";
|
|
6
6
|
import { chmod, mkdir, opendir, readFile, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
@@ -26,7 +26,7 @@ function emptyProjection() {
|
|
|
26
26
|
activities: []
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
|
-
function record$
|
|
29
|
+
function record$11(value) {
|
|
30
30
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
31
31
|
}
|
|
32
32
|
function finiteInteger(value) {
|
|
@@ -36,7 +36,7 @@ function string$4(value, max) {
|
|
|
36
36
|
return typeof value === "string" && value.length > 0 && value.length <= max;
|
|
37
37
|
}
|
|
38
38
|
function binding(value) {
|
|
39
|
-
const input = record$
|
|
39
|
+
const input = record$11(value);
|
|
40
40
|
if (input === void 0 || !string$4(input.claudeSessionId, 512) || !string$4(input.sdkVersion, 128) || !string$4(input.cwd, 4096) || input.cliVersion !== void 0 && !string$4(input.cliVersion, 128)) return void 0;
|
|
41
41
|
return {
|
|
42
42
|
claudeSessionId: input.claudeSessionId,
|
|
@@ -66,22 +66,22 @@ const ACTIVITY_PHASES = /* @__PURE__ */ new Set([
|
|
|
66
66
|
"failed"
|
|
67
67
|
]);
|
|
68
68
|
function activity(value) {
|
|
69
|
-
const input = record$
|
|
69
|
+
const input = record$11(value);
|
|
70
70
|
if (input === void 0 || !finiteInteger(input.turn) || !finiteInteger(input.step) || !finiteInteger(input.ordinal) || typeof input.kind !== "string" || !ACTIVITY_KINDS.has(input.kind) || input.phase !== void 0 && (typeof input.phase !== "string" || !ACTIVITY_PHASES.has(input.phase))) return void 0;
|
|
71
71
|
return normalizeActivity(input);
|
|
72
72
|
}
|
|
73
73
|
function contextUsage(value) {
|
|
74
|
-
const input = record$
|
|
74
|
+
const input = record$11(value);
|
|
75
75
|
if (input === void 0 || !Array.isArray(input.categories)) return void 0;
|
|
76
76
|
return normalizeContextUsage(input);
|
|
77
77
|
}
|
|
78
78
|
function tasks(value) {
|
|
79
|
-
const input = record$
|
|
79
|
+
const input = record$11(value);
|
|
80
80
|
if (input === void 0 || !Array.isArray(input.tasks)) return void 0;
|
|
81
81
|
return normalizeTasksEvent(input.tasks);
|
|
82
82
|
}
|
|
83
83
|
function parseClaudeSidecar(value) {
|
|
84
|
-
const input = record$
|
|
84
|
+
const input = record$11(value);
|
|
85
85
|
if (input === void 0 || input.schemaVersion !== SIDECAR_SCHEMA_VERSION || !finiteInteger(input.revision) || !Array.isArray(input.activities) || input.activities.length > MAX_ACTIVITIES) throw new Error("dsh-claude: invalid sidecar document");
|
|
86
86
|
const activities = input.activities.map(activity);
|
|
87
87
|
if (activities.some((item) => item === void 0)) throw new Error("dsh-claude: invalid sidecar activity");
|
|
@@ -654,7 +654,7 @@ function createUserQuestionBridge(userQuestions, activeContext) {
|
|
|
654
654
|
}
|
|
655
655
|
//#endregion
|
|
656
656
|
//#region src/sdk-messages.ts
|
|
657
|
-
function record$
|
|
657
|
+
function record$10(value) {
|
|
658
658
|
return value !== null && typeof value === "object" ? value : void 0;
|
|
659
659
|
}
|
|
660
660
|
function string$3(value) {
|
|
@@ -669,7 +669,7 @@ function taskUsageOf(usage) {
|
|
|
669
669
|
return Object.keys(normalized).length === 0 ? void 0 : normalized;
|
|
670
670
|
}
|
|
671
671
|
function resultUsage(message) {
|
|
672
|
-
const usage = record$
|
|
672
|
+
const usage = record$10(message.usage);
|
|
673
673
|
const normalized = {};
|
|
674
674
|
if (usage !== void 0) {
|
|
675
675
|
if (typeof usage.input_tokens === "number") normalized.inputTokens = usage.input_tokens;
|
|
@@ -681,7 +681,7 @@ function resultUsage(message) {
|
|
|
681
681
|
return normalized;
|
|
682
682
|
}
|
|
683
683
|
function normalizeAssistant(message) {
|
|
684
|
-
const content = record$
|
|
684
|
+
const content = record$10(message.message)?.content;
|
|
685
685
|
if (!Array.isArray(content)) return [{
|
|
686
686
|
kind: "protocol-error",
|
|
687
687
|
title: "Malformed Claude assistant message",
|
|
@@ -690,7 +690,7 @@ function normalizeAssistant(message) {
|
|
|
690
690
|
const parentToolUseId = string$3(message.parent_tool_use_id);
|
|
691
691
|
const normalized = [];
|
|
692
692
|
for (const item of content) {
|
|
693
|
-
const block = record$
|
|
693
|
+
const block = record$10(item);
|
|
694
694
|
if (block === void 0) continue;
|
|
695
695
|
if (block.type === "text") {
|
|
696
696
|
const text = string$3(block.text);
|
|
@@ -723,7 +723,7 @@ function normalizeAssistant(message) {
|
|
|
723
723
|
}
|
|
724
724
|
function normalizeUser(message) {
|
|
725
725
|
if (message.isReplay === true) return [];
|
|
726
|
-
const content = record$
|
|
726
|
+
const content = record$10(message.message)?.content;
|
|
727
727
|
if (typeof content === "string") return [];
|
|
728
728
|
if (!Array.isArray(content)) return [{
|
|
729
729
|
kind: "protocol-error",
|
|
@@ -733,7 +733,7 @@ function normalizeUser(message) {
|
|
|
733
733
|
const parentToolUseId = string$3(message.parent_tool_use_id);
|
|
734
734
|
const normalized = [];
|
|
735
735
|
for (const item of content) {
|
|
736
|
-
const block = record$
|
|
736
|
+
const block = record$10(item);
|
|
737
737
|
if (block?.type !== "tool_result") continue;
|
|
738
738
|
const toolUseId = string$3(block.tool_use_id);
|
|
739
739
|
if (toolUseId === void 0) continue;
|
|
@@ -814,7 +814,7 @@ function normalizeSystem(message) {
|
|
|
814
814
|
const summary = string$3(message.summary);
|
|
815
815
|
const subagentType = string$3(message.subagent_type);
|
|
816
816
|
const lastToolName = string$3(message.last_tool_name);
|
|
817
|
-
const usage = taskUsageOf(record$
|
|
817
|
+
const usage = taskUsageOf(record$10(message.usage));
|
|
818
818
|
return [{
|
|
819
819
|
kind: "subagent",
|
|
820
820
|
title: summary ?? description ?? "Claude subagent update",
|
|
@@ -830,7 +830,7 @@ function normalizeSystem(message) {
|
|
|
830
830
|
}];
|
|
831
831
|
}
|
|
832
832
|
if (subtype === "task_updated") {
|
|
833
|
-
const patch = record$
|
|
833
|
+
const patch = record$10(message.patch);
|
|
834
834
|
const status = string$3(patch?.status);
|
|
835
835
|
const taskId = string$3(message.task_id);
|
|
836
836
|
const description = string$3(patch?.description);
|
|
@@ -853,7 +853,7 @@ function normalizeSystem(message) {
|
|
|
853
853
|
const taskId = string$3(message.task_id);
|
|
854
854
|
const summary = string$3(message.summary);
|
|
855
855
|
const taskStatus = failed ? "failed" : stopped ? "stopped" : "completed";
|
|
856
|
-
const usage = taskUsageOf(record$
|
|
856
|
+
const usage = taskUsageOf(record$10(message.usage));
|
|
857
857
|
return [{
|
|
858
858
|
kind: "subagent",
|
|
859
859
|
title: summary ?? taskId ?? "Claude subagent finished",
|
|
@@ -868,7 +868,7 @@ function normalizeSystem(message) {
|
|
|
868
868
|
if (subtype === "background_tasks_changed") return [{
|
|
869
869
|
kind: "background-tasks",
|
|
870
870
|
tasks: (Array.isArray(message.tasks) ? message.tasks : []).flatMap((item) => {
|
|
871
|
-
const entry = record$
|
|
871
|
+
const entry = record$10(item);
|
|
872
872
|
const taskId = string$3(entry?.task_id);
|
|
873
873
|
const description = string$3(entry?.description);
|
|
874
874
|
const taskType = string$3(entry?.task_type);
|
|
@@ -911,10 +911,10 @@ const RESULT_ERROR_SUBTYPES = /* @__PURE__ */ new Set([
|
|
|
911
911
|
function normalizeSdkMessage(message) {
|
|
912
912
|
const value = message;
|
|
913
913
|
if (value.type === "stream_event") {
|
|
914
|
-
const event = record$
|
|
914
|
+
const event = record$10(value.event);
|
|
915
915
|
const parentToolUseId = string$3(value.parent_tool_use_id);
|
|
916
916
|
if (event?.type === "content_block_delta") {
|
|
917
|
-
const delta = record$
|
|
917
|
+
const delta = record$10(event.delta);
|
|
918
918
|
if (delta?.type === "text_delta") {
|
|
919
919
|
const text = string$3(delta.text);
|
|
920
920
|
return text === void 0 ? [] : [{
|
|
@@ -949,7 +949,7 @@ function normalizeSdkMessage(message) {
|
|
|
949
949
|
const errors = Array.isArray(value.errors) ? value.errors.filter((item) => typeof item === "string") : void 0;
|
|
950
950
|
const terminalReason = string$3(value.terminal_reason);
|
|
951
951
|
const userMessageUuid = string$3(value.user_message_uuid);
|
|
952
|
-
const permissionDenials = Array.isArray(value.permission_denials) ? value.permission_denials.map((item) => record$
|
|
952
|
+
const permissionDenials = Array.isArray(value.permission_denials) ? value.permission_denials.map((item) => record$10(item)).filter((item) => item !== void 0).map((item) => {
|
|
953
953
|
const toolName = string$3(item.tool_name);
|
|
954
954
|
const toolUseId = string$3(item.tool_use_id);
|
|
955
955
|
return toolName === void 0 || toolUseId === void 0 ? void 0 : {
|
|
@@ -975,7 +975,7 @@ function normalizeSdkMessage(message) {
|
|
|
975
975
|
detail: value.error ?? value.output
|
|
976
976
|
}];
|
|
977
977
|
if (value.type === "rate_limit_event") {
|
|
978
|
-
const status = string$3(record$
|
|
978
|
+
const status = string$3(record$10(value.rate_limit_info)?.status);
|
|
979
979
|
return [{
|
|
980
980
|
kind: "status",
|
|
981
981
|
title: status !== void 0 && status !== "allowed" ? "Claude rate limit is blocking requests" : "Claude rate limit status changed",
|
|
@@ -989,6 +989,117 @@ function normalizeSdkMessage(message) {
|
|
|
989
989
|
}];
|
|
990
990
|
}
|
|
991
991
|
//#endregion
|
|
992
|
+
//#region src/plan-usage.ts
|
|
993
|
+
/** Claude.ai plan rate-limit windows behind the CLI's `/usage` panel.
|
|
994
|
+
*
|
|
995
|
+
* The Agent SDK exposes them through an experimental query method whose name
|
|
996
|
+
* advertises its own instability, so every read is guarded: a missing method,
|
|
997
|
+
* a shape change, or an API-key session all degrade to `available: false`
|
|
998
|
+
* instead of breaking the settings page. */
|
|
999
|
+
/** The SDK's own name for the unstable `/usage` control request; it is
|
|
1000
|
+
* documented to change when the API stabilizes, so it lives in one place. */
|
|
1001
|
+
const PLAN_USAGE_METHOD = "usage_EXPERIMENTAL_MAY_CHANGE_DO_NOT_RELY_ON_THIS_API_YET";
|
|
1002
|
+
/** A throwaway probe should not outlive a wedged control request. */
|
|
1003
|
+
const PLAN_USAGE_TIMEOUT_MS = 2e4;
|
|
1004
|
+
/** Invoke the experimental usage control request, or say why we cannot. */
|
|
1005
|
+
function readPlanUsageFrom(query) {
|
|
1006
|
+
const read = query[PLAN_USAGE_METHOD];
|
|
1007
|
+
if (typeof read !== "function") throw new Error("dsh-claude: this Claude Agent SDK build exposes no plan usage API");
|
|
1008
|
+
return read.call(query);
|
|
1009
|
+
}
|
|
1010
|
+
/** Fixed windows in display order; the SDK omits the ones a plan does not have. */
|
|
1011
|
+
const FIXED_WINDOWS = [
|
|
1012
|
+
"five_hour",
|
|
1013
|
+
"seven_day",
|
|
1014
|
+
"seven_day_opus",
|
|
1015
|
+
"seven_day_sonnet"
|
|
1016
|
+
];
|
|
1017
|
+
function record$9(value) {
|
|
1018
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
1019
|
+
}
|
|
1020
|
+
/** Utilization is documented as 0-100; clamp so a server glitch cannot render
|
|
1021
|
+
* a bar wider than its track or a negative width. */
|
|
1022
|
+
function utilization(value) {
|
|
1023
|
+
return typeof value === "number" && Number.isFinite(value) ? Math.min(100, Math.max(0, value)) : void 0;
|
|
1024
|
+
}
|
|
1025
|
+
function resetsAt(value) {
|
|
1026
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
1027
|
+
}
|
|
1028
|
+
function window(id, source, label) {
|
|
1029
|
+
const entry = record$9(source);
|
|
1030
|
+
if (entry === void 0) return void 0;
|
|
1031
|
+
const used = utilization(entry.utilization);
|
|
1032
|
+
const reset = resetsAt(entry.resets_at);
|
|
1033
|
+
if (used === void 0 && reset === void 0) return void 0;
|
|
1034
|
+
return {
|
|
1035
|
+
id,
|
|
1036
|
+
...label === void 0 ? {} : { label },
|
|
1037
|
+
...used === void 0 ? {} : { utilization: used },
|
|
1038
|
+
...reset === void 0 ? {} : { resetsAt: reset }
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
1041
|
+
/** Project the SDK's `/usage` response onto the windows the settings card shows. */
|
|
1042
|
+
function normalizePlanUsage(value, fetchedAt) {
|
|
1043
|
+
const response = record$9(value);
|
|
1044
|
+
const subscription = typeof response?.subscription_type === "string" ? response.subscription_type : void 0;
|
|
1045
|
+
const limits = record$9(response?.rate_limits);
|
|
1046
|
+
if (response?.rate_limits_available !== true || limits === void 0) return {
|
|
1047
|
+
available: false,
|
|
1048
|
+
...subscription === void 0 ? {} : { subscription },
|
|
1049
|
+
windows: [],
|
|
1050
|
+
fetchedAt
|
|
1051
|
+
};
|
|
1052
|
+
const windows = [...FIXED_WINDOWS.map((id) => window(id, limits[id])), ...(Array.isArray(limits.model_scoped) ? limits.model_scoped : []).map((entry, index) => {
|
|
1053
|
+
const name = record$9(entry)?.display_name;
|
|
1054
|
+
return window(`model:${typeof name === "string" ? name : index}`, entry, typeof name === "string" ? name : void 0);
|
|
1055
|
+
})].filter((entry) => entry !== void 0);
|
|
1056
|
+
return {
|
|
1057
|
+
available: windows.length > 0,
|
|
1058
|
+
...subscription === void 0 ? {} : { subscription },
|
|
1059
|
+
windows,
|
|
1060
|
+
fetchedAt
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
/** Read plan limits from a throwaway Claude process.
|
|
1064
|
+
*
|
|
1065
|
+
* Deliberately NOT routed through the supervisor. Borrowing a session's
|
|
1066
|
+
* process fails outright while that session is mid-turn, and for a session
|
|
1067
|
+
* with no process yet it would resume the whole conversation — and possibly
|
|
1068
|
+
* evict another idle session to make room — just to read three numbers. This
|
|
1069
|
+
* query carries no tools, no permission bridge, and no session binding: it
|
|
1070
|
+
* starts, answers one control request, and is killed. */
|
|
1071
|
+
async function probePlanUsage(executablePath, fetchedAt, factory = query) {
|
|
1072
|
+
const lifetime = new AbortController();
|
|
1073
|
+
const timer = setTimeout(() => lifetime.abort(), PLAN_USAGE_TIMEOUT_MS);
|
|
1074
|
+
timer.unref?.();
|
|
1075
|
+
const query$1 = factory({
|
|
1076
|
+
prompt: (async function* () {
|
|
1077
|
+
await new Promise(() => {});
|
|
1078
|
+
})(),
|
|
1079
|
+
options: {
|
|
1080
|
+
cwd: process.cwd(),
|
|
1081
|
+
abortController: lifetime,
|
|
1082
|
+
...executablePath.length === 0 ? {} : { pathToClaudeCodeExecutable: executablePath }
|
|
1083
|
+
}
|
|
1084
|
+
});
|
|
1085
|
+
try {
|
|
1086
|
+
(async () => {
|
|
1087
|
+
for await (const _ of query$1);
|
|
1088
|
+
})().catch(() => void 0);
|
|
1089
|
+
return normalizePlanUsage(await readPlanUsageFrom(query$1), fetchedAt);
|
|
1090
|
+
} finally {
|
|
1091
|
+
clearTimeout(timer);
|
|
1092
|
+
lifetime.abort();
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
let latest;
|
|
1096
|
+
function recordPlanUsage(report) {
|
|
1097
|
+
latest = report;
|
|
1098
|
+
}
|
|
1099
|
+
function latestPlanUsage() {
|
|
1100
|
+
return latest;
|
|
1101
|
+
}
|
|
1102
|
+
//#endregion
|
|
992
1103
|
//#region src/spawn.ts
|
|
993
1104
|
const CLAUDE_PROCESS_GRACE_MS = 2e3;
|
|
994
1105
|
const CLAUDE_STDERR_TAIL_BYTES = 32768;
|
|
@@ -1206,6 +1317,12 @@ var ClaudeSupervisor = class {
|
|
|
1206
1317
|
contextWindow(model) {
|
|
1207
1318
|
return this.#contextWindows.get(model);
|
|
1208
1319
|
}
|
|
1320
|
+
/** Raw `/usage` payload, read over a session's existing process. Only the
|
|
1321
|
+
* idle-time metadata bridge uses this; a user-triggered refresh runs
|
|
1322
|
+
* probePlanUsage instead so it never waits on, or perturbs, a session. */
|
|
1323
|
+
planUsage(agent, model = this.#config.defaultModel) {
|
|
1324
|
+
return this.#runMetadata(agent, model, (query) => readPlanUsageFrom(query));
|
|
1325
|
+
}
|
|
1209
1326
|
runTurn(request) {
|
|
1210
1327
|
const interruption = this.#interruptions.get(request.agent.id);
|
|
1211
1328
|
if (interruption !== void 0) return interruption.then(() => this.runTurn(request));
|
|
@@ -2385,7 +2502,7 @@ function json(res, status, value) {
|
|
|
2385
2502
|
//#region src/doctor-routes.ts
|
|
2386
2503
|
const CLAUDE_DOCTOR_PROBE_TIMEOUT_MS = 15e3;
|
|
2387
2504
|
const claudeBridgeDiagnostics = /* @__PURE__ */ new WeakMap();
|
|
2388
|
-
function safeMessage$
|
|
2505
|
+
function safeMessage$2(error) {
|
|
2389
2506
|
return redactText(error instanceof Error ? error.message : String(error), 1e3);
|
|
2390
2507
|
}
|
|
2391
2508
|
/** Live command-bridge diagnostics: which agents exist, their presets, and how
|
|
@@ -2401,14 +2518,14 @@ function commandDiagnostics(ctx) {
|
|
|
2401
2518
|
const preset = ctx.agentPresets.composedPreset(agent.ctx);
|
|
2402
2519
|
if (preset !== void 0) info.preset = preset;
|
|
2403
2520
|
} catch (error) {
|
|
2404
|
-
info.error = safeMessage$
|
|
2521
|
+
info.error = safeMessage$2(error);
|
|
2405
2522
|
}
|
|
2406
2523
|
try {
|
|
2407
2524
|
const list = ctx.commands.list(agent);
|
|
2408
2525
|
info.commandCount = list.length;
|
|
2409
2526
|
info.sample = list.slice(0, 10).map((command) => command.name);
|
|
2410
2527
|
} catch (error) {
|
|
2411
|
-
info.error = info.error === void 0 ? safeMessage$
|
|
2528
|
+
info.error = info.error === void 0 ? safeMessage$2(error) : `${info.error}; ${safeMessage$2(error)}`;
|
|
2412
2529
|
}
|
|
2413
2530
|
const bridge = claudeBridgeDiagnostics.get(agent);
|
|
2414
2531
|
if (bridge !== void 0) info.bridge = bridge;
|
|
@@ -2416,7 +2533,7 @@ function commandDiagnostics(ctx) {
|
|
|
2416
2533
|
})
|
|
2417
2534
|
};
|
|
2418
2535
|
} catch (error) {
|
|
2419
|
-
return { error: safeMessage$
|
|
2536
|
+
return { error: safeMessage$2(error) };
|
|
2420
2537
|
}
|
|
2421
2538
|
}
|
|
2422
2539
|
function registerClaudeDoctorRoutes(ctx, runtime, supervisor, config, resolutionError) {
|
|
@@ -2440,7 +2557,7 @@ function registerClaudeDoctorRoutes(ctx, runtime, supervisor, config, resolution
|
|
|
2440
2557
|
version: { status: "not-run" },
|
|
2441
2558
|
authentication: { status: "not-run" },
|
|
2442
2559
|
handshake: "not-run",
|
|
2443
|
-
message: safeMessage$
|
|
2560
|
+
message: safeMessage$2(resolutionError),
|
|
2444
2561
|
limits: {
|
|
2445
2562
|
idleTimeoutMs: config.idleTimeoutMs,
|
|
2446
2563
|
maxProcesses: config.maxProcesses
|
|
@@ -2470,7 +2587,7 @@ function registerClaudeDoctorRoutes(ctx, runtime, supervisor, config, resolution
|
|
|
2470
2587
|
commandBridge: commandDiagnostics(ctx)
|
|
2471
2588
|
});
|
|
2472
2589
|
} catch (error) {
|
|
2473
|
-
json(res, 500, { error: safeMessage$
|
|
2590
|
+
json(res, 500, { error: safeMessage$2(error) });
|
|
2474
2591
|
}
|
|
2475
2592
|
}
|
|
2476
2593
|
}), "dsh-claude: Doctor route");
|
|
@@ -3507,7 +3624,10 @@ var RepositorySetupService = class {
|
|
|
3507
3624
|
return resolve(root);
|
|
3508
3625
|
}
|
|
3509
3626
|
#git() {
|
|
3510
|
-
this.#gitPath ??= this.#runtime.resolveExecutable("git")
|
|
3627
|
+
this.#gitPath ??= this.#runtime.resolveExecutable("git", void 0, AbortSignal.timeout(GIT_TIMEOUT_MS$2)).catch((error) => {
|
|
3628
|
+
this.#gitPath = void 0;
|
|
3629
|
+
throw error;
|
|
3630
|
+
});
|
|
3511
3631
|
return this.#gitPath;
|
|
3512
3632
|
}
|
|
3513
3633
|
async #run(executable, args, cwd, timeoutMs = GIT_TIMEOUT_MS$2) {
|
|
@@ -5158,7 +5278,7 @@ const MAX_MANIFEST_BYTES = 262144;
|
|
|
5158
5278
|
const MAX_UPDATE_OUTPUT_BYTES = 32768;
|
|
5159
5279
|
const SEMVER = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
5160
5280
|
const PROFILE_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
5161
|
-
function safeMessage(error) {
|
|
5281
|
+
function safeMessage$1(error) {
|
|
5162
5282
|
return redactText(error instanceof Error ? error.message : String(error), 500);
|
|
5163
5283
|
}
|
|
5164
5284
|
async function readManifest(path) {
|
|
@@ -5320,7 +5440,7 @@ async function checkPluginUpdate(deps = {}) {
|
|
|
5320
5440
|
state: "error",
|
|
5321
5441
|
canUpdate: false,
|
|
5322
5442
|
restartRequired: false,
|
|
5323
|
-
message: safeMessage(error)
|
|
5443
|
+
message: safeMessage$1(error)
|
|
5324
5444
|
};
|
|
5325
5445
|
}
|
|
5326
5446
|
}
|
|
@@ -5367,7 +5487,7 @@ async function updatePlugin(deps = {}) {
|
|
|
5367
5487
|
const outcome = await handle.done;
|
|
5368
5488
|
if (outcome.exitCode !== 0) {
|
|
5369
5489
|
const detail = handle.collected.stderr?.readFrom(0).text ?? "";
|
|
5370
|
-
throw new Error(`DSH plugin update failed (${outcome.exitCode ?? outcome.signal ?? "unknown exit"}): ${safeMessage(detail)}`);
|
|
5490
|
+
throw new Error(`DSH plugin update failed (${outcome.exitCode ?? outcome.signal ?? "unknown exit"}): ${safeMessage$1(detail)}`);
|
|
5371
5491
|
}
|
|
5372
5492
|
await verifyInstalledVersion(installation, latest);
|
|
5373
5493
|
if (deps.requestRestart !== void 0) setTimeout(() => deps.requestRestart?.(), 100).unref?.();
|
|
@@ -5404,12 +5524,44 @@ function registerClaudeUpdateRoutes(ctx, runtime, deps = {}) {
|
|
|
5404
5524
|
try {
|
|
5405
5525
|
json(res, 200, await route.run());
|
|
5406
5526
|
} catch (error) {
|
|
5407
|
-
json(res, 500, { error: safeMessage(error) });
|
|
5527
|
+
json(res, 500, { error: safeMessage$1(error) });
|
|
5408
5528
|
}
|
|
5409
5529
|
}
|
|
5410
5530
|
}), `dsh-claude: ${route.method} ${route.path}`);
|
|
5411
5531
|
}
|
|
5412
5532
|
//#endregion
|
|
5533
|
+
//#region src/plan-usage-routes.ts
|
|
5534
|
+
const EMPTY = {
|
|
5535
|
+
available: false,
|
|
5536
|
+
windows: [],
|
|
5537
|
+
fetchedAt: 0
|
|
5538
|
+
};
|
|
5539
|
+
function safeMessage(error) {
|
|
5540
|
+
return redactText(error instanceof Error ? error.message : String(error), 1e3);
|
|
5541
|
+
}
|
|
5542
|
+
/** Plan usage: GET serves the value the metadata bridge last cached (free —
|
|
5543
|
+
* it rides an already-running session), POST reads fresh numbers from a
|
|
5544
|
+
* throwaway probe process so a refresh never depends on, or disturbs, a live
|
|
5545
|
+
* Claude session. */
|
|
5546
|
+
function registerPlanUsageRoute(ctx, probe, now = Date.now) {
|
|
5547
|
+
ctx.effect(() => ctx.webServer.register({
|
|
5548
|
+
kind: "exact",
|
|
5549
|
+
path: CLAUDE_USAGE_PATH,
|
|
5550
|
+
handler: async (req, res) => {
|
|
5551
|
+
if (req.method !== "GET" && req.method !== "POST") return json(res, 405, { error: "method not allowed" });
|
|
5552
|
+
if (!trustedRequest(req)) return json(res, 403, { error: "forbidden" });
|
|
5553
|
+
if (req.method === "GET") return json(res, 200, latestPlanUsage() ?? EMPTY);
|
|
5554
|
+
try {
|
|
5555
|
+
const report = await probe(now());
|
|
5556
|
+
recordPlanUsage(report);
|
|
5557
|
+
json(res, 200, report);
|
|
5558
|
+
} catch (error) {
|
|
5559
|
+
json(res, 500, { error: safeMessage(error) });
|
|
5560
|
+
}
|
|
5561
|
+
}
|
|
5562
|
+
}), "dsh-claude: plan usage route");
|
|
5563
|
+
}
|
|
5564
|
+
//#endregion
|
|
5413
5565
|
//#region src/global-settings.ts
|
|
5414
5566
|
const MAX_SETTINGS_BYTES = 262144;
|
|
5415
5567
|
const MAX_REQUEST_BYTES = 8192;
|
|
@@ -5793,6 +5945,13 @@ function mountClaudeMetadata(ctx, supervisor, agent, model, sidecar, publishComm
|
|
|
5793
5945
|
} catch (error) {
|
|
5794
5946
|
warn("context usage", error);
|
|
5795
5947
|
}
|
|
5948
|
+
if (stopped) return;
|
|
5949
|
+
try {
|
|
5950
|
+
const plan = await supervisor.planUsage(agent, model);
|
|
5951
|
+
if (!stopped) recordPlanUsage(normalizePlanUsage(plan, Date.now()));
|
|
5952
|
+
} catch (error) {
|
|
5953
|
+
warn("plan usage", error);
|
|
5954
|
+
}
|
|
5796
5955
|
});
|
|
5797
5956
|
};
|
|
5798
5957
|
return agent.ctx.effect(() => {
|
|
@@ -5965,6 +6124,7 @@ async function apply(ctx, config) {
|
|
|
5965
6124
|
return agent !== void 0 && webCtx.agentPresets.composedPreset(agent.ctx) === "claude";
|
|
5966
6125
|
};
|
|
5967
6126
|
registerReviewCommentRoute(webCtx, reviewComments, ownsClaudeSession);
|
|
6127
|
+
registerPlanUsageRoute(webCtx, (fetchedAt) => probePlanUsage(supervisorConfig.executablePath, fetchedAt));
|
|
5968
6128
|
registerClaudeProjectionRoute(webCtx, sidecar, ownsClaudeSession, (sessionId) => commandCatalogs.get(sessionId) ?? [], async (sessionId) => {
|
|
5969
6129
|
const agent = webCtx.agents.get(sessionId);
|
|
5970
6130
|
if (agent === void 0 || webCtx.agentPresets.composedPreset(agent.ctx) !== "claude") return void 0;
|