@norman-else/dsh-claude 0.1.33 → 0.1.34
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/README.md
CHANGED
|
@@ -66,17 +66,126 @@ Preset cleanup removes only installer-managed content and refuses to delete user
|
|
|
66
66
|
|
|
67
67
|
## 3. Features
|
|
68
68
|
|
|
69
|
+
### 3.1 Conversation
|
|
70
|
+
|
|
69
71
|
- **Native Claude Code conversations** — Runs Claude Code as the main agent in a normal DSH conversation instead of wrapping it as a tool or secondary chat.
|
|
70
72
|
- **Claude preset and model selection** — Adds a `Claude` Agent Preset and exposes Claude Code's `default`, `opus[1m]`, `fable`, `sonnet`, and `haiku` model choices.
|
|
71
73
|
- **Local Claude environment compatibility** — Preserves the user's existing Claude Code authentication, settings, `CLAUDE.md`, Skills, Hooks, Plugins, tools, and MCP configuration.
|
|
72
74
|
- **Real-time streaming and conversation continuity** — Streams Claude responses and tool activity into DSH while retaining multi-turn context and persisted Claude session resume.
|
|
73
75
|
- **DSH permissions and questions** — Routes Claude tool permission requests through DSH approvals and Claude clarification prompts through DSH's native question forms.
|
|
74
|
-
- **
|
|
76
|
+
- **Claude command bridge** — Publishes Claude Code's own command catalog into the DSH command palette, retrying with backoff while a fresh CLI finishes loading Skills and Plugins.
|
|
77
|
+
- **Message queue and steering** — Accepts further messages while a turn is running, and lets a queued message be edited, removed, or steered into the turn already in flight.
|
|
78
|
+
- **Rewind** — Drops a message and everything after it: Claude resumes from the kept turn's transcript anchor and genuinely forgets the discarded turns, the discarded rows are hidden from the append-only DSH log, and the original text returns to the composer for editing and resending.
|
|
79
|
+
- **Ask about a selection** — Answers a question about any selected text through a read-only side query limited to `Read`, `Grep`, and `Glob`, reusing the session's model and thinking mode, with the answer copyable or sendable into the main conversation.
|
|
75
80
|
- **Redacted activity timeline** — Displays thinking summaries, tool calls and results, permission events, questions, status changes, usage, errors, and subagent activity without persisting credentials.
|
|
76
81
|
- **Background task tracking** — Shows running and completed Claude subagents or background tasks with task status, recent tools, and expandable activity.
|
|
82
|
+
- **Context usage** — Tracks how much of the context window a session has consumed and surfaces it as a percentage in the conversation and on the session board.
|
|
83
|
+
- **Managed process lifecycle** — Keeps one live Claude process per active session, serializes turns, evicts idle processes, and handles Stop, cancellation, restart, and process-tree cleanup.
|
|
84
|
+
- **Bilingual interface** — Ships every user-facing string in both English and Chinese.
|
|
85
|
+
|
|
86
|
+
### 3.2 Repository, worktrees, and pull requests
|
|
87
|
+
|
|
77
88
|
- **Repository and worktree preparation** — Lets a user choose a branch before submitting, switch an eligible local branch, or create a dedicated Git worktree and DSH workspace while transferring the current draft and attachments, and removes a worktree's directory automatically once its workspace is deleted and the tree is clean.
|
|
89
|
+
- **Jira-driven sessions** — Connects to Jira Cloud and starts work from a ticket: the branch is named after the ticket key, the composer is seeded with the ticket brief, the ticket is assigned to the user once the worktree exists, and several tickets can be kicked off at once, each in its own worktree session.
|
|
78
90
|
- **Repository and pull request status** — Shows the current repository, branch, worktree state, changed-line counts, unpushed commits, GitHub pull request, checks, review state, merge state, and blocking Claude rate limits near the composer.
|
|
79
|
-
- **
|
|
80
|
-
- **
|
|
81
|
-
- **
|
|
91
|
+
- **Session board** — Summarizes every Claude session in one place with its run state, branch, pull request, context usage, auto-fix state, and whether it is waiting on an approval or an answer.
|
|
92
|
+
- **Branch diff viewer** — Provides an expandable or maximized branch diff with file statistics, expand-all and collapse-all, on-demand unmodified context, and comment-to-comment navigation.
|
|
93
|
+
- **Line-level review comments** — Records the user's own line or range comments against the diff and attaches them to the next Claude message.
|
|
94
|
+
- **GitHub review threads** — Reads, replies to, resolves, and unresolves pull request review threads inline, with `@` completion for repository members, bot authors marked as such, and a link back to the thread on GitHub.
|
|
95
|
+
- **Commit, push, merge, and pull request actions** — Supports Commit, Commit & Push, Push, draft pull request creation, and merging a pull request as a merge commit, squash, or rebase, with repository snapshot validation and optional Claude-generated commit messages.
|
|
96
|
+
- **Branch updates** — Updates the current branch from its base by rebase (pushed with `--force-with-lease`) or merge, and hands any resulting conflicts to Claude to resolve.
|
|
97
|
+
- **Pull request feedback handoff** — Expands failing check details and GitHub pull request comments and hands either of them to Claude as a fix request.
|
|
98
|
+
- **Auto fix** — Watches an open pull request for new review comments and failing checks, hands each new batch to Claude to fix, commit, and push, and keeps going until everything passes or the watcher is switched off.
|
|
99
|
+
- **Merged branch cleanup** — Removes the worktree and local branch, archives the workspace's sessions, and deletes the workspace; a plain checkout instead returns to the base branch and deletes the merged branch.
|
|
100
|
+
- **Open in an editor** — Opens the session's working directory in Cursor or IntelliJ IDEA from the session menu, trying each platform's launchers in turn and refusing paths the shell would re-interpret.
|
|
101
|
+
|
|
102
|
+
### 3.3 Diagnostics, settings, and updates
|
|
103
|
+
|
|
104
|
+
- **Claude Code settings and Doctor** — Adds a Settings panel for runtime diagnostics, supported Claude settings, output style, worktree branch prefix, process limits, and authentication and handshake status.
|
|
105
|
+
- **Plan usage** — Reports the signed-in subscription's utilization windows — five-hour, weekly across all models, and weekly per model — with reset countdowns, degrading to unavailable rather than failing on API-key, Bedrock, and Vertex sessions.
|
|
106
|
+
- **Plugin updates** — Checks npm for new releases and updates in place, only when the installation is uniquely identified; local development links are never replaced.
|
|
82
107
|
- **Managed preset compatibility** — Installs a guarded Claude preset whose route reuses the active profile package source, preserving discovery on DSH Desktop 2.0.4 without duplicate client-module Loaders or overwriting user changes.
|
|
108
|
+
|
|
109
|
+
## 4. Contributing
|
|
110
|
+
|
|
111
|
+
### 4.1 The two contribution types
|
|
112
|
+
|
|
113
|
+
Every contribution to this repository is exactly one of two types. There is no third type.
|
|
114
|
+
|
|
115
|
+
| Type | Means | Examples |
|
|
116
|
+
| --- | --- | --- |
|
|
117
|
+
| `feature` | Behavior that does not exist yet | A new composer action, a new Settings field, support for a new Claude Code capability, a documented behavior that was never written down |
|
|
118
|
+
| `fix` | Behavior that already exists but is wrong | A crashing slot entry, a wrong token count, a preset that stops being discovered after a Desktop upgrade, a README statement that no longer matches the code |
|
|
119
|
+
|
|
120
|
+
Refactors, dependency bumps, test-only changes, and documentation edits are not separate types. File them as the type that matches their purpose: something that is wrong today is a `fix`, something that does not exist today is a `feature`. If you cannot decide which one applies, that is a signal the issue is not scoped yet — open it as a question first and let the maintainer classify it.
|
|
121
|
+
|
|
122
|
+
### 4.2 Required flow
|
|
123
|
+
|
|
124
|
+
An issue always comes first. Pull requests that arrive without one are closed unreviewed, regardless of the quality of the code.
|
|
125
|
+
|
|
126
|
+
1. **Search existing issues.** If your problem or idea is already filed, comment there instead of opening a duplicate.
|
|
127
|
+
2. **Open an issue** at https://github.com/Norman-else/dsh-claude/issues/new/choose and pick the **Feature** or **Fix** form. Choosing the form is how you declare the type: it sets the `[feature]` / `[fix]` title prefix and the matching label for you. Every field listed in §4.3 is required by the form, so an issue that does not explain why it is needed or in what environment it happens cannot be submitted. Blank issues are disabled — if you genuinely cannot tell which type applies, use the "Not sure whether it is a feature or a fix" link on that page and let the maintainer classify it rather than guessing a type to get past the form.
|
|
128
|
+
3. **Wait for the issue to be accepted.** The maintainer confirms the type, the scope, and whether the change belongs in this plugin at all — several things that look like plugin bugs are DSH Host or Claude Code CLI behavior. Do not start implementation before this. Work done on a rejected issue cannot be merged.
|
|
129
|
+
4. **Branch from `master`** using `feature/<issue-number>-<short-slug>` or `fix/<issue-number>-<short-slug>`.
|
|
130
|
+
5. **Implement and verify** against the rules in §4.4.
|
|
131
|
+
6. **Open a pull request** that declares its type and links its issue (§4.5).
|
|
132
|
+
7. **Address review.** The maintainer reviews and approves; the maintainer merges. Contributors do not merge their own pull requests.
|
|
133
|
+
|
|
134
|
+
`master` is protected: direct pushes, force pushes, and branch deletion are blocked, and a pull request needs one approving review before it can merge. Pushing new commits to a pull request dismisses any existing approval, so expect to request review again after changes.
|
|
135
|
+
|
|
136
|
+
### 4.3 What the issue must contain
|
|
137
|
+
|
|
138
|
+
Both types require enough detail for someone else to reproduce your situation without asking you follow-up questions.
|
|
139
|
+
|
|
140
|
+
**A `feature` issue must state:**
|
|
141
|
+
|
|
142
|
+
- **Motivation** — what a DSH user cannot do today, and why the current workaround is not good enough.
|
|
143
|
+
- **Proposed behavior** — what should happen, described from the user's side, naming the surface it belongs to (composer, hero repository controls, diff panel, activity timeline, Settings panel, Agent Preset picker, …).
|
|
144
|
+
- **Scope and non-goals** — what this explicitly does not cover, so the pull request can be reviewed against a fixed boundary.
|
|
145
|
+
- **Affected layer** — plugin server (`src/`), client (`src/client/`), managed preset (`preset/`), or a combination.
|
|
146
|
+
- **Compatibility** — the DSH Desktop and DSH package line it targets, and the Claude Code CLI version it relies on. Say so if it depends on a Claude Code capability that older CLI versions do not have.
|
|
147
|
+
- **Alternatives considered** — including "do nothing", and why they were rejected.
|
|
148
|
+
- **Whether you intend to implement it** — so the maintainer knows whether to assign it to you or to schedule it.
|
|
149
|
+
|
|
150
|
+
**A `fix` issue must state:**
|
|
151
|
+
|
|
152
|
+
- **Expected behavior vs. actual behavior** — two separate sentences, not one combined complaint.
|
|
153
|
+
- **Reproduction steps** — numbered, minimal, and starting from a clean state. Say whether it reproduces every time or intermittently.
|
|
154
|
+
- **Environment** — plugin version (the `version` field in `package.json`, or the version DSH shows), DSH Desktop version, Claude Code CLI version, operating system, and Node.js version if you installed from source.
|
|
155
|
+
- **Evidence** — the relevant redacted log lines, activity-timeline excerpt, or screenshot. Boot and slot failures surface in the DSH log as `dsh-claude client [boot-check]` and `[slot-entry-crashed]`; include those lines when the plugin fails to load.
|
|
156
|
+
- **Regression range** — the last plugin or DSH Desktop version where it worked, if you know it.
|
|
157
|
+
|
|
158
|
+
**Redact before you post.** Never paste Claude credentials, API keys, session tokens, private repository contents, or customer data into an issue, a pull request, a test fixture, or a log excerpt. This applies to attachments and screenshots as well. The plugin never asks for or stores Claude credentials, and neither should its issue tracker.
|
|
159
|
+
|
|
160
|
+
### 4.4 Rules for the change itself
|
|
161
|
+
|
|
162
|
+
- **Stay out-of-tree.** Use only public DSH exports. Never patch the installed DSH checkout, and never depend on DSH internals that are not exported.
|
|
163
|
+
- **Respect the ownership split.** Claude Code owns its agent loop and tools; this plugin owns presentation, approval and question surfaces, and managed process lifetime. Changes that re-implement Claude Code behavior inside the plugin will be rejected.
|
|
164
|
+
- **Read the spec first** when you change runtime behavior: `docs/aegis/spec/2026-08-15-dsh-claude-spec.md` and the current plan under `docs/aegis/plan/` and `docs/aegis/plans/`. If you are reacting to a DSH Desktop upgrade, follow `docs/upgrading-dsh-desktop.md` — the Host ships no type declarations, so `pnpm typecheck` cannot see its API drift.
|
|
165
|
+
- **Never log, persist, render, or test with real credentials.** Redact before any durable event append.
|
|
166
|
+
- **Run `pnpm check`** — typecheck for both tsconfigs, the Vitest suite, and the build — and make sure it passes before you open the pull request. Do not claim a change works without it.
|
|
167
|
+
- **Cover behavior with tests** where the change is testable. A `fix` should come with a test that fails before the change and passes after it.
|
|
168
|
+
- **Keep one type per branch.** Do not mix a feature and a fix in the same pull request, even if you found them together — open two issues and two pull requests.
|
|
169
|
+
- **Leave releases alone.** Do not bump `version` in `package.json`, edit `scripts/publish.mjs`, or publish. Releases are maintainer-only via `pnpm release`.
|
|
170
|
+
- **Write commit subjects in the repository's existing style**: imperative mood, describing the intent rather than the mechanics, no `feat:` / `fix:` prefixes. See `git log` for the established pattern — for example, `Report the whole turn's output tokens, not the last call's`.
|
|
171
|
+
|
|
172
|
+
### 4.5 Pull request requirements
|
|
173
|
+
|
|
174
|
+
Open the pull request against `master` and include all of the following:
|
|
175
|
+
|
|
176
|
+
- **A `Type:` line as the first line of the description** — either `Type: feature` or `Type: fix`. This is how the contribution type is declared; a pull request without it is not reviewed.
|
|
177
|
+
- **A closing reference to its issue** — `Closes #<issue-number>`. A pull request that closes no issue does not get merged.
|
|
178
|
+
- **The matching label** — `feature` or `fix`, the same one carried by the issue.
|
|
179
|
+
- **What changed and how you verified it** — including the `pnpm check` result, and the manual verification you performed in DSH Desktop for anything that touches the UI or the process lifecycle.
|
|
180
|
+
- **A README update** when the change adds or alters user-visible behavior. New features belong in the Features list in §3.
|
|
181
|
+
|
|
182
|
+
The type is declared through the label and the `Type:` line rather than a title prefix so that squashed commit subjects stay in the repository's plain imperative style.
|
|
183
|
+
|
|
184
|
+
### 4.6 What gets rejected
|
|
185
|
+
|
|
186
|
+
- A pull request with no accepted issue behind it, or with no declared type.
|
|
187
|
+
- A feature and a fix bundled into one pull request.
|
|
188
|
+
- Changes that patch DSH, reach into non-public DSH APIs, or re-implement Claude Code's agent loop.
|
|
189
|
+
- A failing or unrun `pnpm check`.
|
|
190
|
+
- Anything that logs, persists, or renders credentials, including in tests and fixtures.
|
|
191
|
+
- Version bumps, release script edits, or publish attempts from a contribution branch.
|
package/lib/bin.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { i as parseClaudeVersion, n as ensureManagedPreset, r as removeManagedPreset } from "./preset-installer-
|
|
2
|
+
import { i as parseClaudeVersion, n as ensureManagedPreset, r as removeManagedPreset } from "./preset-installer-BY4846KK.mjs";
|
|
3
3
|
import { access } from "node:fs/promises";
|
|
4
4
|
import { delimiter, isAbsolute, join } from "node:path";
|
|
5
|
+
import { constants } from "node:fs";
|
|
5
6
|
import { homedir } from "node:os";
|
|
6
7
|
import { scrubbedParentEnv } from "@deepseek-ai/dsh-subprocess";
|
|
7
|
-
import { constants } from "node:fs";
|
|
8
8
|
import { spawnSync } from "node:child_process";
|
|
9
9
|
//#region src/bin.ts
|
|
10
10
|
function option(name) {
|
package/lib/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { A as CLAUDE_UPDATE_PATH, C as CLAUDE_REPOSITORY_FEEDBACK_PATH, D as CLAUDE_REVIEW_COMMENT_PATH, E as CLAUDE_REPOSITORY_STATUS_PATH, N as TASK_TOOL_NAMES, O as CLAUDE_REWIND_PATH, S as CLAUDE_REPOSITORY_ACTION_PATH, T as CLAUDE_REPOSITORY_SETUP_PATH, _ as CLAUDE_DOCTOR_PATH, a as latestClaudeTasks, b as CLAUDE_JIRA_PATH, c as normalizeTasksEvent, d as CLAUDE_ACTIVITY_EVENT, f as CLAUDE_ASK_PATH, g as CLAUDE_CODE_PROVIDER_IDS, h as CLAUDE_CODE_PROVIDER, i as latestClaudeSessionBinding, j as CLAUDE_USAGE_PATH, k as CLAUDE_UPDATE_CHECK_PATH, l as redactText, m as CLAUDE_CODE_PRESET_ID, n as currentClaudeActivityCursor, o as normalizeActivity, p as CLAUDE_CLIENT_DIAGNOSTICS_PATH, r as latestClaudeContextUsage, s as normalizeContextUsage, t as boundText, u as safeDetail, v as CLAUDE_EDITOR_OPEN_PATH, w as CLAUDE_REPOSITORY_FILE_PATH, x as CLAUDE_PROJECTION_PATH, y as CLAUDE_GLOBAL_SETTINGS_PATH } from "./events-DeSV0S1-.mjs";
|
|
2
2
|
import { n as projectClaudeCommands, t as CLAUDE_COMMANDS_SERVICE } from "./command-bridge-DXI6nWhB.mjs";
|
|
3
|
-
import { a as resolveClaudeExecutable, n as ensureManagedPreset, o as runClaudeDoctor, t as ManagedPresetConflictError } from "./preset-installer-
|
|
3
|
+
import { a as resolveClaudeExecutable, n as ensureManagedPreset, o as runClaudeDoctor, t as ManagedPresetConflictError } from "./preset-installer-BY4846KK.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";
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { l as redactText, m as CLAUDE_CODE_PRESET_ID } from "./events-DeSV0S1-.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
|
-
import { link, lstat, mkdir, readFile, readdir, rm, rmdir, writeFile } from "node:fs/promises";
|
|
3
|
+
import { access, link, lstat, mkdir, readFile, readdir, rm, rmdir, writeFile } from "node:fs/promises";
|
|
4
4
|
import { dirname, isAbsolute, join, win32 } from "node:path";
|
|
5
5
|
import { dshHomePath } from "@deepseek-ai/dsh-home-paths";
|
|
6
|
+
import { constants } from "node:fs";
|
|
6
7
|
import { homedir } from "node:os";
|
|
7
8
|
import { fileURLToPath } from "node:url";
|
|
8
9
|
//#region src/executable.ts
|
|
@@ -28,6 +29,37 @@ function fallbackCandidates() {
|
|
|
28
29
|
function abortError(error) {
|
|
29
30
|
return error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError");
|
|
30
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Prefer the package's own native binary over a Windows npm shim.
|
|
34
|
+
*
|
|
35
|
+
* A PATH lookup on Windows answers `claude.CMD`, and since the fix for
|
|
36
|
+
* CVE-2024-27980 Node refuses to spawn `.cmd` and `.bat` without
|
|
37
|
+
* `shell: true` — every probe and every session then fails with
|
|
38
|
+
* `spawn EINVAL`, reported to the user as an executable and authentication
|
|
39
|
+
* error with nothing pointing at the file extension. Auto-resolution
|
|
40
|
+
* therefore cannot work on Windows at all, and the user has to discover
|
|
41
|
+
* `executablePath` to get anywhere.
|
|
42
|
+
*
|
|
43
|
+
* npm installs the package beside the shim it writes, and
|
|
44
|
+
* `@anthropic-ai/claude-code` ships a native executable (its own manifest
|
|
45
|
+
* declares `"bin": { "claude": "bin/claude.exe" }`), which carries no such
|
|
46
|
+
* restriction. Falling back to the shim keeps a layout that does not match
|
|
47
|
+
* this assumption working exactly as before.
|
|
48
|
+
*
|
|
49
|
+
* @param path - the resolved candidate, possibly a Windows shim.
|
|
50
|
+
* @returns the native executable when one sits beside the shim, else `path`.
|
|
51
|
+
*/
|
|
52
|
+
async function preferNativeWindowsBinary(path) {
|
|
53
|
+
if (process.platform !== "win32") return path;
|
|
54
|
+
if (!/\.(?:cmd|bat)$/iu.test(path)) return path;
|
|
55
|
+
const native = win32.join(win32.dirname(path), "node_modules", "@anthropic-ai", "claude-code", "bin", "claude.exe");
|
|
56
|
+
try {
|
|
57
|
+
await access(native, constants.X_OK);
|
|
58
|
+
return native;
|
|
59
|
+
} catch {
|
|
60
|
+
return path;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
31
63
|
async function resolveClaudeExecutable(runtime, configuredPath, signal) {
|
|
32
64
|
const searched = [];
|
|
33
65
|
const candidates = configuredPath === void 0 ? ["claude", ...fallbackCandidates()] : [configuredPath];
|
|
@@ -38,7 +70,7 @@ async function resolveClaudeExecutable(runtime, configuredPath, signal) {
|
|
|
38
70
|
searched.push(candidate);
|
|
39
71
|
try {
|
|
40
72
|
return {
|
|
41
|
-
path: await runtime.resolveExecutable(candidate, void 0, signal),
|
|
73
|
+
path: await preferNativeWindowsBinary(await runtime.resolveExecutable(candidate, void 0, signal)),
|
|
42
74
|
searched
|
|
43
75
|
};
|
|
44
76
|
} catch (error) {
|
|
@@ -272,4 +304,4 @@ async function removeManagedPreset(paths = defaultManagedPresetPaths()) {
|
|
|
272
304
|
//#endregion
|
|
273
305
|
export { resolveClaudeExecutable as a, parseClaudeVersion as i, ensureManagedPreset as n, runClaudeDoctor as o, removeManagedPreset as r, ManagedPresetConflictError as t };
|
|
274
306
|
|
|
275
|
-
//# sourceMappingURL=preset-installer-
|
|
307
|
+
//# sourceMappingURL=preset-installer-BY4846KK.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preset-installer-BY4846KK.mjs","names":[],"sources":["../src/executable.ts","../src/preset-installer.ts"],"sourcesContent":["import { constants } from 'node:fs'\r\nimport { access } from 'node:fs/promises'\r\nimport { homedir } from 'node:os'\r\nimport { isAbsolute, join, win32 } from 'node:path'\r\nimport type { SubprocessHandle, SubprocessRuntime } from '@deepseek-ai/dsh-subprocess'\r\nimport { redactText } from './events.ts'\r\n\r\nconst VERSION_PATTERN = /(?:Claude Code\\s+)?v?(\\d+\\.\\d+\\.\\d+(?:[-+][\\w.-]+)?)/i\r\nconst MAX_PROBE_STDOUT = 64 * 1024\r\nconst MAX_PROBE_STDERR = 8 * 1024\r\n\r\nexport type ExecutableRuntime = Pick<SubprocessRuntime, 'resolveExecutable' | 'spawn'>\r\n\r\nexport class ClaudeExecutableNotFoundError extends Error {\r\n readonly searched: readonly string[]\r\n\r\n constructor(searched: readonly string[], options?: ErrorOptions) {\r\n super(`Claude Code executable not found. Searched: ${searched.join(', ')}`, options)\r\n this.name = 'ClaudeExecutableNotFoundError'\r\n this.searched = [...searched]\r\n }\r\n}\r\n\r\nexport interface ClaudeExecutableResolution {\r\n path: string\r\n searched: readonly string[]\r\n}\r\n\r\nexport interface ClaudeDoctorReport {\r\n executable: {\r\n status: 'found' | 'missing'\r\n path?: string\r\n searched: readonly string[]\r\n }\r\n version: {\r\n status: 'ok' | 'error' | 'not-run'\r\n value?: string\r\n message?: string\r\n }\r\n authentication: {\r\n status: 'signed-in' | 'signed-out' | 'unknown' | 'not-run'\r\n method?: string\r\n provider?: string\r\n subscription?: string\r\n message?: string\r\n }\r\n handshake: 'not-run' | 'ok' | 'error'\r\n}\r\n\r\nfunction fallbackCandidates(): string[] {\r\n if (process.platform !== 'darwin') return []\r\n return [\r\n join(homedir(), '.local', 'bin', 'claude'),\r\n '/opt/homebrew/bin/claude',\r\n '/usr/local/bin/claude',\r\n ]\r\n}\r\n\r\nfunction abortError(error: unknown): boolean {\r\n return error instanceof Error && (error.name === 'AbortError' || error.name === 'TimeoutError')\r\n}\r\n\r\n/**\r\n * Prefer the package's own native binary over a Windows npm shim.\r\n *\r\n * A PATH lookup on Windows answers `claude.CMD`, and since the fix for\r\n * CVE-2024-27980 Node refuses to spawn `.cmd` and `.bat` without\r\n * `shell: true` — every probe and every session then fails with\r\n * `spawn EINVAL`, reported to the user as an executable and authentication\r\n * error with nothing pointing at the file extension. Auto-resolution\r\n * therefore cannot work on Windows at all, and the user has to discover\r\n * `executablePath` to get anywhere.\r\n *\r\n * npm installs the package beside the shim it writes, and\r\n * `@anthropic-ai/claude-code` ships a native executable (its own manifest\r\n * declares `\"bin\": { \"claude\": \"bin/claude.exe\" }`), which carries no such\r\n * restriction. Falling back to the shim keeps a layout that does not match\r\n * this assumption working exactly as before.\r\n *\r\n * @param path - the resolved candidate, possibly a Windows shim.\r\n * @returns the native executable when one sits beside the shim, else `path`.\r\n */\r\nasync function preferNativeWindowsBinary(path: string): Promise<string> {\r\n if (process.platform !== 'win32') return path\r\n if (!/\\.(?:cmd|bat)$/iu.test(path)) return path\r\n const native = win32.join(\r\n win32.dirname(path),\r\n 'node_modules',\r\n '@anthropic-ai',\r\n 'claude-code',\r\n 'bin',\r\n 'claude.exe',\r\n )\r\n try {\r\n await access(native, constants.X_OK)\r\n return native\r\n } catch {\r\n return path\r\n }\r\n}\r\n\r\nexport async function resolveClaudeExecutable(\r\n runtime: ExecutableRuntime,\r\n configuredPath?: string,\r\n signal?: AbortSignal,\r\n): Promise<ClaudeExecutableResolution> {\r\n const searched: string[] = []\r\n const candidates = configuredPath === undefined\r\n ? ['claude', ...fallbackCandidates()]\r\n : [configuredPath]\r\n if (configuredPath !== undefined && !isAbsolute(configuredPath) && !win32.isAbsolute(configuredPath)) {\r\n throw new Error(`Claude Code executable path must be absolute: ${configuredPath}`)\r\n }\r\n\r\n let lastError: unknown\r\n for (const candidate of candidates) {\r\n if (searched.includes(candidate)) continue\r\n searched.push(candidate)\r\n try {\r\n const resolved = await runtime.resolveExecutable(candidate, undefined, signal)\r\n return { path: await preferNativeWindowsBinary(resolved), searched }\r\n } catch (error) {\r\n if (abortError(error) || signal?.aborted === true) throw error\r\n lastError = error\r\n }\r\n }\r\n throw new ClaudeExecutableNotFoundError(searched, lastError === undefined ? undefined : { cause: lastError })\r\n}\r\n\r\ninterface CollectedCommand {\r\n exitCode: number | null\r\n signal: NodeJS.Signals | null\r\n stdout: string\r\n stderr: string\r\n}\r\n\r\nasync function collect(handle: SubprocessHandle): Promise<CollectedCommand> {\r\n const outcome = await handle.done\r\n const stdout = handle.collected.stdout?.readFrom(0).text ?? ''\r\n const stderr = handle.collected.stderr?.readFrom(0).text ?? ''\r\n return { ...outcome, stdout, stderr }\r\n}\r\n\r\nasync function runProbe(\r\n runtime: ExecutableRuntime,\r\n executable: string,\r\n args: readonly string[],\r\n cwd: string,\r\n signal?: AbortSignal,\r\n): Promise<CollectedCommand> {\r\n return collect(runtime.spawn({\r\n argv: [executable, ...args],\r\n cwd,\r\n stdio: {\r\n stdin: 'ignore',\r\n stdout: { maxBytes: MAX_PROBE_STDOUT },\r\n stderr: { maxBytes: MAX_PROBE_STDERR },\r\n },\r\n graceMs: 2_000,\r\n ...(signal === undefined ? {} : { signal }),\r\n env: {},\r\n }))\r\n}\r\n\r\nexport function parseClaudeVersion(output: string): string | undefined {\r\n return VERSION_PATTERN.exec(output)?.[1]\r\n}\r\n\r\nexport async function probeClaudeVersion(\r\n runtime: ExecutableRuntime,\r\n executable: string,\r\n cwd: string,\r\n signal?: AbortSignal,\r\n): Promise<string> {\r\n const result = await runProbe(runtime, executable, ['--version'], cwd, signal)\r\n const version = parseClaudeVersion(`${result.stdout}\\n${result.stderr}`)\r\n if (result.exitCode !== 0 || version === undefined) {\r\n throw new Error(`Claude Code version probe failed (${result.exitCode ?? result.signal ?? 'unknown exit'})`)\r\n }\r\n return version\r\n}\r\n\r\nexport async function probeClaudeAuthentication(\r\n runtime: ExecutableRuntime,\r\n executable: string,\r\n cwd: string,\r\n signal?: AbortSignal,\r\n): Promise<ClaudeDoctorReport['authentication']> {\r\n const result = await runProbe(runtime, executable, ['auth', 'status', '--json'], cwd, signal)\r\n if (result.exitCode !== 0) {\r\n return { status: 'unknown', message: 'Claude authentication status command failed' }\r\n }\r\n try {\r\n const value = JSON.parse(result.stdout) as Record<string, unknown>\r\n const report: ClaudeDoctorReport['authentication'] = {\r\n status: value.loggedIn === true ? 'signed-in' : value.loggedIn === false ? 'signed-out' : 'unknown',\r\n }\r\n if (typeof value.authMethod === 'string') report.method = redactText(value.authMethod, 100)\r\n if (typeof value.apiProvider === 'string') report.provider = redactText(value.apiProvider, 100)\r\n if (typeof value.subscriptionType === 'string') report.subscription = redactText(value.subscriptionType, 100)\r\n return report\r\n } catch {\r\n return { status: 'unknown', message: 'Claude authentication status was not valid JSON' }\r\n }\r\n}\r\n\r\nexport async function runClaudeDoctor(\r\n runtime: ExecutableRuntime,\r\n options: { configuredPath?: string; cwd: string; signal?: AbortSignal },\r\n): Promise<ClaudeDoctorReport> {\r\n let resolution: ClaudeExecutableResolution\r\n try {\r\n resolution = await resolveClaudeExecutable(runtime, options.configuredPath, options.signal)\r\n } catch (error) {\r\n if (error instanceof ClaudeExecutableNotFoundError) {\r\n return {\r\n executable: { status: 'missing', searched: error.searched },\r\n version: { status: 'not-run' },\r\n authentication: { status: 'not-run' },\r\n handshake: 'not-run',\r\n }\r\n }\r\n throw error\r\n }\r\n\r\n const report: ClaudeDoctorReport = {\r\n executable: { status: 'found', path: resolution.path, searched: resolution.searched },\r\n version: { status: 'not-run' },\r\n authentication: { status: 'not-run' },\r\n handshake: 'not-run',\r\n }\r\n try {\r\n report.version = {\r\n status: 'ok',\r\n value: await probeClaudeVersion(runtime, resolution.path, options.cwd, options.signal),\r\n }\r\n } catch (error) {\r\n report.version = {\r\n status: 'error',\r\n message: error instanceof Error ? error.message : 'Version probe failed',\r\n }\r\n }\r\n try {\r\n report.authentication = await probeClaudeAuthentication(runtime, resolution.path, options.cwd, options.signal)\r\n } catch (error) {\r\n report.authentication = {\r\n status: 'unknown',\r\n message: error instanceof Error ? error.message : 'Authentication probe failed',\r\n }\r\n }\r\n return report\r\n}\r\n","import { randomUUID } from 'node:crypto'\r\nimport { link, lstat, mkdir, readFile, readdir, rm, rmdir, writeFile } from 'node:fs/promises'\r\nimport { dirname, join } from 'node:path'\r\nimport { fileURLToPath } from 'node:url'\r\nimport { dshHomePath } from '@deepseek-ai/dsh-home-paths'\r\nimport { CLAUDE_CODE_PRESET_ID } from './constants.ts'\r\n\r\nexport const MANAGED_PRESET_FILES = ['agent.cordis.yml', 'preset.yml'] as const\r\n\r\n/** Package specifier kept in the installed template. DSH Desktop 2.0.4 resolves\r\n * it through the active profile package factory, so the preset route and client\r\n * module share one Loader source. An absolute built entry would register a\r\n * second source and make the Desktop renderer reject the plugin graph. */\r\nconst PRESET_ROUTE_PACKAGE_SPECIFIER = '@norman-else/dsh-claude/preset-route'\r\n\r\nexport class ManagedPresetConflictError extends Error {\r\n readonly path: string\r\n\r\n constructor(path: string) {\r\n super(`dsh-claude: refusing to overwrite user-modified preset file ${path}`)\r\n this.name = 'ManagedPresetConflictError'\r\n this.path = path\r\n }\r\n}\r\n\r\nexport interface ManagedPresetPaths {\r\n sourceDir: string\r\n targetDir: string\r\n}\r\n\r\nexport function defaultManagedPresetPaths(dshHome?: string): ManagedPresetPaths {\r\n const packageRoot = fileURLToPath(new URL('../', import.meta.url))\r\n return {\r\n sourceDir: join(packageRoot, 'legacy-preset'),\r\n targetDir: dshHome === undefined\r\n ? dshHomePath('.agent-presets', CLAUDE_CODE_PRESET_ID)\r\n : join(dshHome, '.agent-presets', CLAUDE_CODE_PRESET_ID),\r\n }\r\n}\r\n\r\ninterface ManagedContent {\r\n file: string\r\n /** Content this installer version writes. */\r\n content: string\r\n /** Older installer-written contents that may be silently upgraded/removed. */\r\n legacy: readonly string[]\r\n /** Legacy detection for contents that predate the current template. */\r\n isLegacy(current: string): boolean\r\n}\r\n\r\nasync function managedContents(paths: ManagedPresetPaths): Promise<ManagedContent[]> {\r\n return await Promise.all(MANAGED_PRESET_FILES.map(async (file): Promise<ManagedContent> => {\r\n const source = await readFile(join(paths.sourceDir, file), 'utf8')\r\n const nameRow = `name: '${PRESET_ROUTE_PACKAGE_SPECIFIER}'`\r\n const legacyNameRow = `name: ${PRESET_ROUTE_PACKAGE_SPECIFIER}`\r\n if (file !== 'agent.cordis.yml' || !source.includes(nameRow)) {\r\n return { file, content: source, legacy: [], isLegacy: () => false }\r\n }\r\n return {\r\n file,\r\n content: source,\r\n legacy: [],\r\n // Earlier installers wrote either an unquoted specifier or an absolute\r\n // built-module path. Both are installer-owned and safe to converge to the\r\n // single profile package source used by Desktop 2.0.4.\r\n isLegacy: current =>\r\n current.includes('id: claude-code-route')\r\n && (current.includes(legacyNameRow)\r\n || current.includes('lib/preset-route.mjs')),\r\n }\r\n }))\r\n}\r\n\r\nasync function readIfPresent(path: string): Promise<string | undefined> {\r\n try {\r\n return await readFile(path, 'utf8')\r\n } catch (error) {\r\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined\r\n throw error\r\n }\r\n}\r\n\r\nasync function atomicWrite(path: string, content: string): Promise<boolean> {\r\n await mkdir(dirname(path), { recursive: true })\r\n const temporary = `${path}.${randomUUID()}.tmp`\r\n try {\r\n await writeFile(temporary, content, { encoding: 'utf8', mode: 0o600, flag: 'wx' })\r\n try {\r\n await link(temporary, path)\r\n return true\r\n } catch (error) {\r\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error\r\n if (await readIfPresent(path) === content) return false\r\n throw new ManagedPresetConflictError(path)\r\n }\r\n } finally {\r\n await rm(temporary, { force: true })\r\n }\r\n}\r\n\r\nexport async function ensureManagedPreset(paths = defaultManagedPresetPaths()): Promise<'installed' | 'unchanged'> {\r\n await assertSafeTargetDirectory(paths.targetDir)\r\n const expected = await managedContents(paths)\r\n let changed = false\r\n for (const { file, content, legacy, isLegacy } of expected) {\r\n const target = join(paths.targetDir, file)\r\n const current = await readIfPresent(target)\r\n if (current === content) continue\r\n if (current !== undefined) {\r\n // Upgrade installer-written legacy content in place; never touch user edits.\r\n if (!legacy.includes(current) && !isLegacy(current)) throw new ManagedPresetConflictError(target)\r\n await rm(target)\r\n }\r\n changed = await atomicWrite(target, content) || changed\r\n }\r\n return changed ? 'installed' : 'unchanged'\r\n}\r\n\r\n/** Reject a target directory that is a symlink (or occupies the path as a file)\r\n * so the managed preset never writes through an attacker-controlled link. */\r\nasync function assertSafeTargetDirectory(targetDir: string): Promise<void> {\r\n try {\r\n const stat = await lstat(targetDir)\r\n if (!stat.isDirectory() || stat.isSymbolicLink()) {\r\n throw new ManagedPresetConflictError(targetDir)\r\n }\r\n } catch (error) {\r\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return\r\n throw error\r\n }\r\n}\r\n\r\nexport async function removeManagedPreset(paths = defaultManagedPresetPaths()): Promise<'removed' | 'absent'> {\r\n await assertSafeTargetDirectory(paths.targetDir)\r\n const expected = await managedContents(paths)\r\n const managedTargets: string[] = []\r\n for (const { file, content, legacy, isLegacy } of expected) {\r\n const target = join(paths.targetDir, file)\r\n const current = await readIfPresent(target)\r\n if (current === undefined) continue\r\n if (current !== content && !legacy.includes(current) && !isLegacy(current)) throw new ManagedPresetConflictError(target)\r\n managedTargets.push(target)\r\n }\r\n for (const target of managedTargets) await rm(target)\r\n try {\r\n if ((await readdir(paths.targetDir)).length === 0) await rmdir(paths.targetDir)\r\n } catch (error) {\r\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\r\n }\r\n return managedTargets.length > 0 ? 'removed' : 'absent'\r\n}\r\n"],"mappings":"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"}
|
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.
|
|
4
|
+
"version": "0.1.34",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.mjs",
|
|
7
7
|
"types": "lib/index.d.mts",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"preset-installer-DbmlhBXI.mjs","names":[],"sources":["../src/executable.ts","../src/preset-installer.ts"],"sourcesContent":["import { homedir } from 'node:os'\r\nimport { isAbsolute, join, win32 } from 'node:path'\r\nimport type { SubprocessHandle, SubprocessRuntime } from '@deepseek-ai/dsh-subprocess'\r\nimport { redactText } from './events.ts'\r\n\r\nconst VERSION_PATTERN = /(?:Claude Code\\s+)?v?(\\d+\\.\\d+\\.\\d+(?:[-+][\\w.-]+)?)/i\r\nconst MAX_PROBE_STDOUT = 64 * 1024\r\nconst MAX_PROBE_STDERR = 8 * 1024\r\n\r\nexport type ExecutableRuntime = Pick<SubprocessRuntime, 'resolveExecutable' | 'spawn'>\r\n\r\nexport class ClaudeExecutableNotFoundError extends Error {\r\n readonly searched: readonly string[]\r\n\r\n constructor(searched: readonly string[], options?: ErrorOptions) {\r\n super(`Claude Code executable not found. Searched: ${searched.join(', ')}`, options)\r\n this.name = 'ClaudeExecutableNotFoundError'\r\n this.searched = [...searched]\r\n }\r\n}\r\n\r\nexport interface ClaudeExecutableResolution {\r\n path: string\r\n searched: readonly string[]\r\n}\r\n\r\nexport interface ClaudeDoctorReport {\r\n executable: {\r\n status: 'found' | 'missing'\r\n path?: string\r\n searched: readonly string[]\r\n }\r\n version: {\r\n status: 'ok' | 'error' | 'not-run'\r\n value?: string\r\n message?: string\r\n }\r\n authentication: {\r\n status: 'signed-in' | 'signed-out' | 'unknown' | 'not-run'\r\n method?: string\r\n provider?: string\r\n subscription?: string\r\n message?: string\r\n }\r\n handshake: 'not-run' | 'ok' | 'error'\r\n}\r\n\r\nfunction fallbackCandidates(): string[] {\r\n if (process.platform !== 'darwin') return []\r\n return [\r\n join(homedir(), '.local', 'bin', 'claude'),\r\n '/opt/homebrew/bin/claude',\r\n '/usr/local/bin/claude',\r\n ]\r\n}\r\n\r\nfunction abortError(error: unknown): boolean {\r\n return error instanceof Error && (error.name === 'AbortError' || error.name === 'TimeoutError')\r\n}\r\n\r\nexport async function resolveClaudeExecutable(\r\n runtime: ExecutableRuntime,\r\n configuredPath?: string,\r\n signal?: AbortSignal,\r\n): Promise<ClaudeExecutableResolution> {\r\n const searched: string[] = []\r\n const candidates = configuredPath === undefined\r\n ? ['claude', ...fallbackCandidates()]\r\n : [configuredPath]\r\n if (configuredPath !== undefined && !isAbsolute(configuredPath) && !win32.isAbsolute(configuredPath)) {\r\n throw new Error(`Claude Code executable path must be absolute: ${configuredPath}`)\r\n }\r\n\r\n let lastError: unknown\r\n for (const candidate of candidates) {\r\n if (searched.includes(candidate)) continue\r\n searched.push(candidate)\r\n try {\r\n const path = await runtime.resolveExecutable(candidate, undefined, signal)\r\n return { path, searched }\r\n } catch (error) {\r\n if (abortError(error) || signal?.aborted === true) throw error\r\n lastError = error\r\n }\r\n }\r\n throw new ClaudeExecutableNotFoundError(searched, lastError === undefined ? undefined : { cause: lastError })\r\n}\r\n\r\ninterface CollectedCommand {\r\n exitCode: number | null\r\n signal: NodeJS.Signals | null\r\n stdout: string\r\n stderr: string\r\n}\r\n\r\nasync function collect(handle: SubprocessHandle): Promise<CollectedCommand> {\r\n const outcome = await handle.done\r\n const stdout = handle.collected.stdout?.readFrom(0).text ?? ''\r\n const stderr = handle.collected.stderr?.readFrom(0).text ?? ''\r\n return { ...outcome, stdout, stderr }\r\n}\r\n\r\nasync function runProbe(\r\n runtime: ExecutableRuntime,\r\n executable: string,\r\n args: readonly string[],\r\n cwd: string,\r\n signal?: AbortSignal,\r\n): Promise<CollectedCommand> {\r\n return collect(runtime.spawn({\r\n argv: [executable, ...args],\r\n cwd,\r\n stdio: {\r\n stdin: 'ignore',\r\n stdout: { maxBytes: MAX_PROBE_STDOUT },\r\n stderr: { maxBytes: MAX_PROBE_STDERR },\r\n },\r\n graceMs: 2_000,\r\n ...(signal === undefined ? {} : { signal }),\r\n env: {},\r\n }))\r\n}\r\n\r\nexport function parseClaudeVersion(output: string): string | undefined {\r\n return VERSION_PATTERN.exec(output)?.[1]\r\n}\r\n\r\nexport async function probeClaudeVersion(\r\n runtime: ExecutableRuntime,\r\n executable: string,\r\n cwd: string,\r\n signal?: AbortSignal,\r\n): Promise<string> {\r\n const result = await runProbe(runtime, executable, ['--version'], cwd, signal)\r\n const version = parseClaudeVersion(`${result.stdout}\\n${result.stderr}`)\r\n if (result.exitCode !== 0 || version === undefined) {\r\n throw new Error(`Claude Code version probe failed (${result.exitCode ?? result.signal ?? 'unknown exit'})`)\r\n }\r\n return version\r\n}\r\n\r\nexport async function probeClaudeAuthentication(\r\n runtime: ExecutableRuntime,\r\n executable: string,\r\n cwd: string,\r\n signal?: AbortSignal,\r\n): Promise<ClaudeDoctorReport['authentication']> {\r\n const result = await runProbe(runtime, executable, ['auth', 'status', '--json'], cwd, signal)\r\n if (result.exitCode !== 0) {\r\n return { status: 'unknown', message: 'Claude authentication status command failed' }\r\n }\r\n try {\r\n const value = JSON.parse(result.stdout) as Record<string, unknown>\r\n const report: ClaudeDoctorReport['authentication'] = {\r\n status: value.loggedIn === true ? 'signed-in' : value.loggedIn === false ? 'signed-out' : 'unknown',\r\n }\r\n if (typeof value.authMethod === 'string') report.method = redactText(value.authMethod, 100)\r\n if (typeof value.apiProvider === 'string') report.provider = redactText(value.apiProvider, 100)\r\n if (typeof value.subscriptionType === 'string') report.subscription = redactText(value.subscriptionType, 100)\r\n return report\r\n } catch {\r\n return { status: 'unknown', message: 'Claude authentication status was not valid JSON' }\r\n }\r\n}\r\n\r\nexport async function runClaudeDoctor(\r\n runtime: ExecutableRuntime,\r\n options: { configuredPath?: string; cwd: string; signal?: AbortSignal },\r\n): Promise<ClaudeDoctorReport> {\r\n let resolution: ClaudeExecutableResolution\r\n try {\r\n resolution = await resolveClaudeExecutable(runtime, options.configuredPath, options.signal)\r\n } catch (error) {\r\n if (error instanceof ClaudeExecutableNotFoundError) {\r\n return {\r\n executable: { status: 'missing', searched: error.searched },\r\n version: { status: 'not-run' },\r\n authentication: { status: 'not-run' },\r\n handshake: 'not-run',\r\n }\r\n }\r\n throw error\r\n }\r\n\r\n const report: ClaudeDoctorReport = {\r\n executable: { status: 'found', path: resolution.path, searched: resolution.searched },\r\n version: { status: 'not-run' },\r\n authentication: { status: 'not-run' },\r\n handshake: 'not-run',\r\n }\r\n try {\r\n report.version = {\r\n status: 'ok',\r\n value: await probeClaudeVersion(runtime, resolution.path, options.cwd, options.signal),\r\n }\r\n } catch (error) {\r\n report.version = {\r\n status: 'error',\r\n message: error instanceof Error ? error.message : 'Version probe failed',\r\n }\r\n }\r\n try {\r\n report.authentication = await probeClaudeAuthentication(runtime, resolution.path, options.cwd, options.signal)\r\n } catch (error) {\r\n report.authentication = {\r\n status: 'unknown',\r\n message: error instanceof Error ? error.message : 'Authentication probe failed',\r\n }\r\n }\r\n return report\r\n}\r\n","import { randomUUID } from 'node:crypto'\r\nimport { link, lstat, mkdir, readFile, readdir, rm, rmdir, writeFile } from 'node:fs/promises'\r\nimport { dirname, join } from 'node:path'\r\nimport { fileURLToPath } from 'node:url'\r\nimport { dshHomePath } from '@deepseek-ai/dsh-home-paths'\r\nimport { CLAUDE_CODE_PRESET_ID } from './constants.ts'\r\n\r\nexport const MANAGED_PRESET_FILES = ['agent.cordis.yml', 'preset.yml'] as const\r\n\r\n/** Package specifier kept in the installed template. DSH Desktop 2.0.4 resolves\r\n * it through the active profile package factory, so the preset route and client\r\n * module share one Loader source. An absolute built entry would register a\r\n * second source and make the Desktop renderer reject the plugin graph. */\r\nconst PRESET_ROUTE_PACKAGE_SPECIFIER = '@norman-else/dsh-claude/preset-route'\r\n\r\nexport class ManagedPresetConflictError extends Error {\r\n readonly path: string\r\n\r\n constructor(path: string) {\r\n super(`dsh-claude: refusing to overwrite user-modified preset file ${path}`)\r\n this.name = 'ManagedPresetConflictError'\r\n this.path = path\r\n }\r\n}\r\n\r\nexport interface ManagedPresetPaths {\r\n sourceDir: string\r\n targetDir: string\r\n}\r\n\r\nexport function defaultManagedPresetPaths(dshHome?: string): ManagedPresetPaths {\r\n const packageRoot = fileURLToPath(new URL('../', import.meta.url))\r\n return {\r\n sourceDir: join(packageRoot, 'legacy-preset'),\r\n targetDir: dshHome === undefined\r\n ? dshHomePath('.agent-presets', CLAUDE_CODE_PRESET_ID)\r\n : join(dshHome, '.agent-presets', CLAUDE_CODE_PRESET_ID),\r\n }\r\n}\r\n\r\ninterface ManagedContent {\r\n file: string\r\n /** Content this installer version writes. */\r\n content: string\r\n /** Older installer-written contents that may be silently upgraded/removed. */\r\n legacy: readonly string[]\r\n /** Legacy detection for contents that predate the current template. */\r\n isLegacy(current: string): boolean\r\n}\r\n\r\nasync function managedContents(paths: ManagedPresetPaths): Promise<ManagedContent[]> {\r\n return await Promise.all(MANAGED_PRESET_FILES.map(async (file): Promise<ManagedContent> => {\r\n const source = await readFile(join(paths.sourceDir, file), 'utf8')\r\n const nameRow = `name: '${PRESET_ROUTE_PACKAGE_SPECIFIER}'`\r\n const legacyNameRow = `name: ${PRESET_ROUTE_PACKAGE_SPECIFIER}`\r\n if (file !== 'agent.cordis.yml' || !source.includes(nameRow)) {\r\n return { file, content: source, legacy: [], isLegacy: () => false }\r\n }\r\n return {\r\n file,\r\n content: source,\r\n legacy: [],\r\n // Earlier installers wrote either an unquoted specifier or an absolute\r\n // built-module path. Both are installer-owned and safe to converge to the\r\n // single profile package source used by Desktop 2.0.4.\r\n isLegacy: current =>\r\n current.includes('id: claude-code-route')\r\n && (current.includes(legacyNameRow)\r\n || current.includes('lib/preset-route.mjs')),\r\n }\r\n }))\r\n}\r\n\r\nasync function readIfPresent(path: string): Promise<string | undefined> {\r\n try {\r\n return await readFile(path, 'utf8')\r\n } catch (error) {\r\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined\r\n throw error\r\n }\r\n}\r\n\r\nasync function atomicWrite(path: string, content: string): Promise<boolean> {\r\n await mkdir(dirname(path), { recursive: true })\r\n const temporary = `${path}.${randomUUID()}.tmp`\r\n try {\r\n await writeFile(temporary, content, { encoding: 'utf8', mode: 0o600, flag: 'wx' })\r\n try {\r\n await link(temporary, path)\r\n return true\r\n } catch (error) {\r\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error\r\n if (await readIfPresent(path) === content) return false\r\n throw new ManagedPresetConflictError(path)\r\n }\r\n } finally {\r\n await rm(temporary, { force: true })\r\n }\r\n}\r\n\r\nexport async function ensureManagedPreset(paths = defaultManagedPresetPaths()): Promise<'installed' | 'unchanged'> {\r\n await assertSafeTargetDirectory(paths.targetDir)\r\n const expected = await managedContents(paths)\r\n let changed = false\r\n for (const { file, content, legacy, isLegacy } of expected) {\r\n const target = join(paths.targetDir, file)\r\n const current = await readIfPresent(target)\r\n if (current === content) continue\r\n if (current !== undefined) {\r\n // Upgrade installer-written legacy content in place; never touch user edits.\r\n if (!legacy.includes(current) && !isLegacy(current)) throw new ManagedPresetConflictError(target)\r\n await rm(target)\r\n }\r\n changed = await atomicWrite(target, content) || changed\r\n }\r\n return changed ? 'installed' : 'unchanged'\r\n}\r\n\r\n/** Reject a target directory that is a symlink (or occupies the path as a file)\r\n * so the managed preset never writes through an attacker-controlled link. */\r\nasync function assertSafeTargetDirectory(targetDir: string): Promise<void> {\r\n try {\r\n const stat = await lstat(targetDir)\r\n if (!stat.isDirectory() || stat.isSymbolicLink()) {\r\n throw new ManagedPresetConflictError(targetDir)\r\n }\r\n } catch (error) {\r\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return\r\n throw error\r\n }\r\n}\r\n\r\nexport async function removeManagedPreset(paths = defaultManagedPresetPaths()): Promise<'removed' | 'absent'> {\r\n await assertSafeTargetDirectory(paths.targetDir)\r\n const expected = await managedContents(paths)\r\n const managedTargets: string[] = []\r\n for (const { file, content, legacy, isLegacy } of expected) {\r\n const target = join(paths.targetDir, file)\r\n const current = await readIfPresent(target)\r\n if (current === undefined) continue\r\n if (current !== content && !legacy.includes(current) && !isLegacy(current)) throw new ManagedPresetConflictError(target)\r\n managedTargets.push(target)\r\n }\r\n for (const target of managedTargets) await rm(target)\r\n try {\r\n if ((await readdir(paths.targetDir)).length === 0) await rmdir(paths.targetDir)\r\n } catch (error) {\r\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\r\n }\r\n return managedTargets.length > 0 ? 'removed' : 'absent'\r\n}\r\n"],"mappings":"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"}
|