@norman-else/dsh-claude 0.1.46 → 0.1.47
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 +1 -2
- package/lib/bin.mjs +1 -1
- package/lib/client.d.ts +2 -10
- package/lib/client.js +451 -912
- package/lib/client.js.map +1 -1
- package/lib/{events-oovRTmX7.mjs → events-B498uofA.mjs} +2 -3
- package/lib/events-B498uofA.mjs.map +1 -0
- package/lib/index.d.mts +0 -1
- package/lib/index.mjs +131 -221
- package/lib/index.mjs.map +1 -1
- package/lib/{presenters-BVWj7u0a.mjs → presenters-CRFcjLSC.mjs} +2 -2
- package/lib/{presenters-BVWj7u0a.mjs.map → presenters-CRFcjLSC.mjs.map} +1 -1
- package/lib/{preset-installer-CuosP3sA.mjs → preset-installer-C4TTNV9L.mjs} +2 -2
- package/lib/{preset-installer-CuosP3sA.mjs.map → preset-installer-C4TTNV9L.mjs.map} +1 -1
- package/lib/preset-route.mjs +2 -2
- package/package.json +121 -96
- package/lib/events-oovRTmX7.mjs.map +0 -1
package/lib/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
import { a as projectClaudeCommands, i as CLAUDE_COMMANDS_SERVICE, r as dynamicPresenterDefinition, t as CLAUDE_PRESENTER_NAMES } from "./presenters-
|
|
3
|
-
import { a as resolveClaudeExecutable, n as ensureManagedPreset, o as runClaudeDoctor, t as ManagedPresetConflictError } from "./preset-installer-
|
|
1
|
+
import { A as CLAUDE_REPOSITORY_FILE_PATH, C as CLAUDE_PROMPTS_PATH, D as CLAUDE_RENDER_MODES, E as CLAUDE_PROSE_MODES, F as CLAUDE_UPDATE_CHECK_PATH, H as isClaudeAlertMode, I as CLAUDE_UPDATE_PATH, L as CLAUDE_USAGE_PATH, M as CLAUDE_REPOSITORY_STATUS_PATH, N as CLAUDE_REVIEW_COMMENT_PATH, O as CLAUDE_REPOSITORY_ACTION_PATH, P as CLAUDE_REWIND_PATH, R as DEFAULT_CLAUDE_PROSE_MODE, S as CLAUDE_PROJECTION_PATH, T as CLAUDE_PROMPT_REFINE_PATH, U as isClaudeProseMode, V as TASK_TOOL_NAMES, W as isClaudeRenderMode, _ as CLAUDE_CODE_PROVIDER_IDS, a as latestClaudeTasks, b as CLAUDE_JIRA_PATH, c as normalizeTasksEvent, d as CLAUDE_ACTIVITY_EVENT, f as CLAUDE_ALERT_MODES, g as CLAUDE_CODE_PROVIDER, h as CLAUDE_CODE_PRESET_ID, i as latestClaudeSessionBinding, j as CLAUDE_REPOSITORY_SETUP_PATH, k as CLAUDE_REPOSITORY_FEEDBACK_PATH, l as redactText, m as CLAUDE_CLIENT_DIAGNOSTICS_PATH, n as currentClaudeActivityCursor, o as normalizeActivity, p as CLAUDE_ASK_PATH, r as latestClaudeContextUsage, s as normalizeContextUsage, t as boundText, u as safeDetail, v as CLAUDE_DOCTOR_PATH, w as CLAUDE_PROMPT_NAME_PATH, x as CLAUDE_PLAN_FEEDBACK_PATH, y as CLAUDE_GLOBAL_SETTINGS_PATH, z as DEFAULT_CLAUDE_RENDER_MODE } from "./events-B498uofA.mjs";
|
|
2
|
+
import { a as projectClaudeCommands, i as CLAUDE_COMMANDS_SERVICE, r as dynamicPresenterDefinition, t as CLAUDE_PRESENTER_NAMES } from "./presenters-CRFcjLSC.mjs";
|
|
3
|
+
import { a as resolveClaudeExecutable, n as ensureManagedPreset, o as runClaudeDoctor, t as ManagedPresetConflictError } from "./preset-installer-C4TTNV9L.mjs";
|
|
4
4
|
import z from "@deepseek-ai/schemastery";
|
|
5
5
|
import { createHash, randomUUID } from "node:crypto";
|
|
6
6
|
import { chmod, mkdir, opendir, readFile, readdir, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
@@ -1810,7 +1810,7 @@ function createManagedClaudeSpawner(runtime, executablePath, observe) {
|
|
|
1810
1810
|
* honours `.gitignore`, so build output and `node_modules` are neither
|
|
1811
1811
|
* captured nor removed.
|
|
1812
1812
|
*/
|
|
1813
|
-
const MAX_OUTPUT_BYTES$
|
|
1813
|
+
const MAX_OUTPUT_BYTES$4 = 65536;
|
|
1814
1814
|
/** `add -A` walks the whole checkout; a large repository needs more than the
|
|
1815
1815
|
* status probes' five seconds, and this sits in front of every turn. */
|
|
1816
1816
|
const GIT_TIMEOUT_MS$4 = 3e4;
|
|
@@ -1835,8 +1835,8 @@ async function run$1(runtime, git, args, cwd, env = {}) {
|
|
|
1835
1835
|
cwd,
|
|
1836
1836
|
stdio: {
|
|
1837
1837
|
stdin: "ignore",
|
|
1838
|
-
stdout: { maxBytes: MAX_OUTPUT_BYTES$
|
|
1839
|
-
stderr: { maxBytes: MAX_OUTPUT_BYTES$
|
|
1838
|
+
stdout: { maxBytes: MAX_OUTPUT_BYTES$4 },
|
|
1839
|
+
stderr: { maxBytes: MAX_OUTPUT_BYTES$4 }
|
|
1840
1840
|
},
|
|
1841
1841
|
graceMs: 1e3,
|
|
1842
1842
|
signal: AbortSignal.timeout(GIT_TIMEOUT_MS$4),
|
|
@@ -1923,6 +1923,18 @@ const CLAUDE_MODE_BY_SANDBOX = {
|
|
|
1923
1923
|
"workspace-write": "acceptEdits",
|
|
1924
1924
|
"danger-full-access": "bypassPermissions"
|
|
1925
1925
|
};
|
|
1926
|
+
/** Appended to the Claude Code system prompt on every session.
|
|
1927
|
+
*
|
|
1928
|
+
* The plan panel opens on exactly one signal: an `ExitPlanMode` call, whose
|
|
1929
|
+
* argument is the plan (see `planText` in permission.ts). Claude Code's own
|
|
1930
|
+
* plan mode says to make that call once the plan file is written, but
|
|
1931
|
+
* user-installed skills routinely say the opposite — "confirm the design in
|
|
1932
|
+
* prose before proceeding" — and a turn that ends on that question hands DSH
|
|
1933
|
+
* nothing to show: no record, no button, no panel, until the user types
|
|
1934
|
+
* "continue" and Claude makes the call it skipped. This line settles the
|
|
1935
|
+
* conflict in favour of the handoff, so the plan reaches the panel the moment
|
|
1936
|
+
* it is done rather than one message later. */
|
|
1937
|
+
const PLAN_MODE_HANDOFF_PROMPT = "When you are in plan mode and the plan is written, end the turn by calling ExitPlanMode with the plan. Do not end a plan-mode turn by asking the user for confirmation in prose: the user reads and approves the plan through ExitPlanMode, and a turn that stops short of that call shows them nothing.";
|
|
1926
1938
|
/** Fold DSH's native access selector into Claude Code's closest permission mode. */
|
|
1927
1939
|
function claudePermissionMode(events) {
|
|
1928
1940
|
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
@@ -2072,8 +2084,7 @@ var ClaudeSupervisor = class {
|
|
|
2072
2084
|
cwd: entry.cwd,
|
|
2073
2085
|
model: entry.model,
|
|
2074
2086
|
...entry.thinkingMode === void 0 ? {} : { thinkingMode: entry.thinkingMode },
|
|
2075
|
-
lastUsedAt: entry.lastUsedAt
|
|
2076
|
-
...entry.process === void 0 ? {} : { pid: entry.process.handle.pid }
|
|
2087
|
+
lastUsedAt: entry.lastUsedAt
|
|
2077
2088
|
}));
|
|
2078
2089
|
}
|
|
2079
2090
|
supportedCommands(agent, model = this.#config.defaultModel) {
|
|
@@ -2596,7 +2607,8 @@ var ClaudeSupervisor = class {
|
|
|
2596
2607
|
],
|
|
2597
2608
|
systemPrompt: {
|
|
2598
2609
|
type: "preset",
|
|
2599
|
-
preset: "claude_code"
|
|
2610
|
+
preset: "claude_code",
|
|
2611
|
+
append: PLAN_MODE_HANDOFF_PROMPT
|
|
2600
2612
|
},
|
|
2601
2613
|
tools: {
|
|
2602
2614
|
type: "preset",
|
|
@@ -4198,7 +4210,7 @@ function registerClaudeDoctorRoutes(ctx, runtime, supervisor, config, resolution
|
|
|
4198
4210
|
}
|
|
4199
4211
|
//#endregion
|
|
4200
4212
|
//#region src/projection-routes.ts
|
|
4201
|
-
const MAX_SESSION_ID_CHARS$
|
|
4213
|
+
const MAX_SESSION_ID_CHARS$6 = 1024;
|
|
4202
4214
|
/** Slow-moving metadata refresh and stream heartbeat cadence; deliberately off
|
|
4203
4215
|
* the transcript hot path so git/gh latency never delays visible text. */
|
|
4204
4216
|
const META_REFRESH_MS = 5e3;
|
|
@@ -4206,7 +4218,7 @@ const META_REFRESH_MS = 5e3;
|
|
|
4206
4218
|
* so this cannot collide with one. */
|
|
4207
4219
|
const MULTI_SEGMENT = "multi";
|
|
4208
4220
|
function validSessionId(value) {
|
|
4209
|
-
return value.length > 0 && value.length <= MAX_SESSION_ID_CHARS$
|
|
4221
|
+
return value.length > 0 && value.length <= MAX_SESSION_ID_CHARS$6;
|
|
4210
4222
|
}
|
|
4211
4223
|
function targetFromUrl(url) {
|
|
4212
4224
|
const prefix = `${CLAUDE_PROJECTION_PATH}/`;
|
|
@@ -4536,8 +4548,13 @@ async function diffFuncnameArgs() {
|
|
|
4536
4548
|
}
|
|
4537
4549
|
//#endregion
|
|
4538
4550
|
//#region src/repository-status.ts
|
|
4539
|
-
const MAX_OUTPUT_BYTES$
|
|
4551
|
+
const MAX_OUTPUT_BYTES$3 = 65536;
|
|
4552
|
+
/** What the diff panel receives: whole files packed under this budget. */
|
|
4540
4553
|
const MAX_DIFF_BYTES = 262144;
|
|
4554
|
+
/** What git may hand back before packing; beyond this the diff is truncated. */
|
|
4555
|
+
const MAX_RAW_DIFF_BYTES = 8388608;
|
|
4556
|
+
/** One file's share of the panel; a lockfile or bundle past this is elided by name. */
|
|
4557
|
+
const MAX_FILE_PATCH_BYTES = 65536;
|
|
4541
4558
|
const MAX_FILE_BYTES = 8388608;
|
|
4542
4559
|
const MAX_UNTRACKED_DIFFS = 50;
|
|
4543
4560
|
const GIT_TIMEOUT_MS$3 = 5e3;
|
|
@@ -4557,7 +4574,7 @@ async function collect$3(handle) {
|
|
|
4557
4574
|
lossy: stdout?.lossy === true
|
|
4558
4575
|
};
|
|
4559
4576
|
}
|
|
4560
|
-
async function run(runtime, executable, args, cwd, timeoutMs, maxBytes = MAX_OUTPUT_BYTES$
|
|
4577
|
+
async function run(runtime, executable, args, cwd, timeoutMs, maxBytes = MAX_OUTPUT_BYTES$3) {
|
|
4561
4578
|
const signal = AbortSignal.timeout(timeoutMs);
|
|
4562
4579
|
return collect$3(runtime.spawn({
|
|
4563
4580
|
argv: [executable, ...args],
|
|
@@ -4565,7 +4582,7 @@ async function run(runtime, executable, args, cwd, timeoutMs, maxBytes = MAX_OUT
|
|
|
4565
4582
|
stdio: {
|
|
4566
4583
|
stdin: "ignore",
|
|
4567
4584
|
stdout: { maxBytes },
|
|
4568
|
-
stderr: { maxBytes: MAX_OUTPUT_BYTES$
|
|
4585
|
+
stderr: { maxBytes: MAX_OUTPUT_BYTES$3 }
|
|
4569
4586
|
},
|
|
4570
4587
|
graceMs: 1e3,
|
|
4571
4588
|
signal,
|
|
@@ -4644,6 +4661,31 @@ async function detectRepositoryOperation(gitDir) {
|
|
|
4644
4661
|
};
|
|
4645
4662
|
}
|
|
4646
4663
|
}
|
|
4664
|
+
/**
|
|
4665
|
+
* Pack a unified diff into a byte budget one whole file at a time.
|
|
4666
|
+
*
|
|
4667
|
+
* A single oversized file (a lockfile, a bundle) used to void the entire
|
|
4668
|
+
* patch, which read as "no changes" in the panel. Files are kept in git's
|
|
4669
|
+
* order while they fit; a file past its own cap or past what is left of the
|
|
4670
|
+
* budget is skipped and named, so the panel still shows every other change.
|
|
4671
|
+
*/
|
|
4672
|
+
function packPatchByFile(patch, budget = MAX_DIFF_BYTES, perFile = MAX_FILE_PATCH_BYTES) {
|
|
4673
|
+
let packed = "";
|
|
4674
|
+
const elided = [];
|
|
4675
|
+
for (const section of patch.split(/(?=^diff --git )/mu)) {
|
|
4676
|
+
if (section.length === 0) continue;
|
|
4677
|
+
if (section.length <= perFile && packed.length + section.length <= budget) {
|
|
4678
|
+
packed += section;
|
|
4679
|
+
continue;
|
|
4680
|
+
}
|
|
4681
|
+
const header = /^diff --git a\/(?<a>.*?) b\/(?<b>.*)$/mu.exec(section);
|
|
4682
|
+
elided.push(header?.groups?.["b"] ?? header?.groups?.["a"] ?? "?");
|
|
4683
|
+
}
|
|
4684
|
+
return {
|
|
4685
|
+
patch: packed,
|
|
4686
|
+
elided
|
|
4687
|
+
};
|
|
4688
|
+
}
|
|
4647
4689
|
function parseDiffNumstat(value) {
|
|
4648
4690
|
let additions = 0;
|
|
4649
4691
|
let deletions = 0;
|
|
@@ -4956,19 +4998,20 @@ var RepositoryStatusService = class {
|
|
|
4956
4998
|
"--unified=3",
|
|
4957
4999
|
base,
|
|
4958
5000
|
"--"
|
|
4959
|
-
], cwd, GIT_TIMEOUT_MS$3,
|
|
5001
|
+
], cwd, GIT_TIMEOUT_MS$3, MAX_RAW_DIFF_BYTES);
|
|
4960
5002
|
if (patch.exitCode !== 0) return {
|
|
4961
5003
|
...summary,
|
|
4962
5004
|
truncated: true
|
|
4963
5005
|
};
|
|
4964
5006
|
const untracked = await this.#untrackedDiff(cwd, git, signal);
|
|
4965
|
-
const
|
|
5007
|
+
const packed = packPatchByFile(`${patch.stdout}${untracked.patch}`);
|
|
4966
5008
|
return {
|
|
4967
5009
|
additions: summary.additions + untracked.additions,
|
|
4968
5010
|
deletions: summary.deletions,
|
|
4969
5011
|
files: summary.files + untracked.files,
|
|
4970
|
-
...patch.lossy ? {} : { patch:
|
|
4971
|
-
|
|
5012
|
+
...patch.lossy ? {} : { patch: packed.patch },
|
|
5013
|
+
...packed.elided.length === 0 ? {} : { elided: packed.elided },
|
|
5014
|
+
truncated: patch.lossy || untracked.truncated
|
|
4972
5015
|
};
|
|
4973
5016
|
} catch {
|
|
4974
5017
|
return;
|
|
@@ -5132,7 +5175,7 @@ async function summarizeBranchSlug(executablePath, intent, factory = query) {
|
|
|
5132
5175
|
}
|
|
5133
5176
|
//#endregion
|
|
5134
5177
|
//#region src/repository-setup.ts
|
|
5135
|
-
const MAX_OUTPUT_BYTES$
|
|
5178
|
+
const MAX_OUTPUT_BYTES$2 = 131072;
|
|
5136
5179
|
const GIT_TIMEOUT_MS$2 = 1e4;
|
|
5137
5180
|
const GIT_FETCH_TIMEOUT_MS = 6e4;
|
|
5138
5181
|
const MAX_PATH_CHARS$2 = 4096;
|
|
@@ -5652,8 +5695,8 @@ var RepositorySetupService = class {
|
|
|
5652
5695
|
cwd,
|
|
5653
5696
|
stdio: {
|
|
5654
5697
|
stdin: "ignore",
|
|
5655
|
-
stdout: { maxBytes: MAX_OUTPUT_BYTES$
|
|
5656
|
-
stderr: { maxBytes: MAX_OUTPUT_BYTES$
|
|
5698
|
+
stdout: { maxBytes: MAX_OUTPUT_BYTES$2 },
|
|
5699
|
+
stderr: { maxBytes: MAX_OUTPUT_BYTES$2 }
|
|
5657
5700
|
},
|
|
5658
5701
|
graceMs: 1e3,
|
|
5659
5702
|
signal: AbortSignal.timeout(timeoutMs),
|
|
@@ -5694,7 +5737,7 @@ var RepositorySetupService = class {
|
|
|
5694
5737
|
};
|
|
5695
5738
|
//#endregion
|
|
5696
5739
|
//#region src/repository-actions.ts
|
|
5697
|
-
const MAX_OUTPUT_BYTES$
|
|
5740
|
+
const MAX_OUTPUT_BYTES$1 = 262144;
|
|
5698
5741
|
const MAX_PATCH_CHARS = 65536;
|
|
5699
5742
|
const MAX_MESSAGE_CHARS = 512;
|
|
5700
5743
|
const MAX_PR_TEXT_CHARS = 8192;
|
|
@@ -6120,7 +6163,7 @@ var RepositoryActionService = class {
|
|
|
6120
6163
|
"--",
|
|
6121
6164
|
":(exclude)WARP.md",
|
|
6122
6165
|
":(exclude)**/WARP.md"
|
|
6123
|
-
], root, GIT_TIMEOUT_MS$1, MAX_OUTPUT_BYTES$
|
|
6166
|
+
], root, GIT_TIMEOUT_MS$1, MAX_OUTPUT_BYTES$1),
|
|
6124
6167
|
this.#run(git, [
|
|
6125
6168
|
...funcname,
|
|
6126
6169
|
"diff",
|
|
@@ -6130,7 +6173,7 @@ var RepositoryActionService = class {
|
|
|
6130
6173
|
"--",
|
|
6131
6174
|
":(exclude)WARP.md",
|
|
6132
6175
|
":(exclude)**/WARP.md"
|
|
6133
|
-
], root, GIT_TIMEOUT_MS$1, MAX_OUTPUT_BYTES$
|
|
6176
|
+
], root, GIT_TIMEOUT_MS$1, MAX_OUTPUT_BYTES$1)
|
|
6134
6177
|
]);
|
|
6135
6178
|
if (branchResult.exitCode !== 0) throw new RepositoryActionError("detached-head", "A detached HEAD cannot be committed from this panel.");
|
|
6136
6179
|
if (headResult.exitCode !== 0 || statusResult.exitCode !== 0 || statusResult.lossy) throw new RepositoryActionError("repository-unavailable", "Repository state is unavailable.");
|
|
@@ -6224,14 +6267,14 @@ var RepositoryActionService = class {
|
|
|
6224
6267
|
if (result.exitCode !== 0 || result.lossy) throw new RepositoryActionError(code, message);
|
|
6225
6268
|
return result;
|
|
6226
6269
|
}
|
|
6227
|
-
#run(executable, args, cwd, timeoutMs, maxBytes = MAX_OUTPUT_BYTES$
|
|
6270
|
+
#run(executable, args, cwd, timeoutMs, maxBytes = MAX_OUTPUT_BYTES$1) {
|
|
6228
6271
|
return collect$1(this.#runtime.spawn({
|
|
6229
6272
|
argv: [executable, ...args],
|
|
6230
6273
|
cwd,
|
|
6231
6274
|
stdio: {
|
|
6232
6275
|
stdin: "ignore",
|
|
6233
6276
|
stdout: { maxBytes },
|
|
6234
|
-
stderr: { maxBytes: MAX_OUTPUT_BYTES$
|
|
6277
|
+
stderr: { maxBytes: MAX_OUTPUT_BYTES$1 }
|
|
6235
6278
|
},
|
|
6236
6279
|
graceMs: 1e3,
|
|
6237
6280
|
signal: AbortSignal.timeout(timeoutMs),
|
|
@@ -6372,7 +6415,7 @@ function registerRepositorySetupRoute(ctx, service, sweep, cleaned) {
|
|
|
6372
6415
|
//#endregion
|
|
6373
6416
|
//#region src/repository-action-routes.ts
|
|
6374
6417
|
const MAX_BODY_BYTES$6 = 16384;
|
|
6375
|
-
const MAX_SESSION_ID_CHARS$
|
|
6418
|
+
const MAX_SESSION_ID_CHARS$5 = 1024;
|
|
6376
6419
|
const MAX_ROOT_CHARS = 4096;
|
|
6377
6420
|
const ACTIONS = /* @__PURE__ */ new Set([
|
|
6378
6421
|
"commit",
|
|
@@ -6409,7 +6452,7 @@ async function readJson$5(io) {
|
|
|
6409
6452
|
}
|
|
6410
6453
|
function sessionId$1(url) {
|
|
6411
6454
|
const value = url.searchParams.get("sessionId");
|
|
6412
|
-
if (value === null || value.length === 0 || value.length > MAX_SESSION_ID_CHARS$
|
|
6455
|
+
if (value === null || value.length === 0 || value.length > MAX_SESSION_ID_CHARS$5) throw new RepositoryActionError("invalid-session", "The session is invalid.");
|
|
6413
6456
|
return value;
|
|
6414
6457
|
}
|
|
6415
6458
|
function requestedRoot(url) {
|
|
@@ -6528,187 +6571,6 @@ function registerRepositoryActionRoute(ctx, service, cwdForSession) {
|
|
|
6528
6571
|
});
|
|
6529
6572
|
}
|
|
6530
6573
|
//#endregion
|
|
6531
|
-
//#region src/editor-open.ts
|
|
6532
|
-
const MAX_OUTPUT_BYTES$1 = 8192;
|
|
6533
|
-
/** Long enough for a launcher shim to fail loudly, short enough that the
|
|
6534
|
-
* request returns while the IDE is still booting. On Windows and Linux the
|
|
6535
|
-
* IDE binary IS the launched process, so still running past this is success. */
|
|
6536
|
-
const SETTLE_MS = 1500;
|
|
6537
|
-
const EDITOR_IDS = /* @__PURE__ */ new Set(["cursor", "idea"]);
|
|
6538
|
-
/** Launch commands tried in order, first success wins. macOS keeps `open -a`
|
|
6539
|
-
* behind the CLI shim because both shims are opt-in installs there, while
|
|
6540
|
-
* `open -a` finds the bundle wherever Toolbox or the DMG dropped it. */
|
|
6541
|
-
const LAUNCHERS = {
|
|
6542
|
-
cursor: {
|
|
6543
|
-
darwin: [["cursor"], [
|
|
6544
|
-
"open",
|
|
6545
|
-
"-a",
|
|
6546
|
-
"Cursor"
|
|
6547
|
-
]],
|
|
6548
|
-
win32: [["cursor"]],
|
|
6549
|
-
linux: [["cursor"]]
|
|
6550
|
-
},
|
|
6551
|
-
idea: {
|
|
6552
|
-
darwin: [
|
|
6553
|
-
["idea"],
|
|
6554
|
-
[
|
|
6555
|
-
"open",
|
|
6556
|
-
"-a",
|
|
6557
|
-
"IntelliJ IDEA"
|
|
6558
|
-
],
|
|
6559
|
-
[
|
|
6560
|
-
"open",
|
|
6561
|
-
"-a",
|
|
6562
|
-
"IntelliJ IDEA CE"
|
|
6563
|
-
]
|
|
6564
|
-
],
|
|
6565
|
-
win32: [["idea"], ["idea64.exe"]],
|
|
6566
|
-
linux: [["idea"], ["idea.sh"]]
|
|
6567
|
-
}
|
|
6568
|
-
};
|
|
6569
|
-
/** cmd.exe re-interprets these, and a mis-parsed path opens the wrong project
|
|
6570
|
-
* rather than failing. Refuse instead of guessing. */
|
|
6571
|
-
const WINDOWS_UNSAFE = /["&|<>^%]/u;
|
|
6572
|
-
var EditorOpenError = class extends Error {
|
|
6573
|
-
code;
|
|
6574
|
-
constructor(code, message) {
|
|
6575
|
-
super(message);
|
|
6576
|
-
this.name = "EditorOpenError";
|
|
6577
|
-
this.code = code;
|
|
6578
|
-
}
|
|
6579
|
-
};
|
|
6580
|
-
/** Open a session's working directory in a desktop editor. */
|
|
6581
|
-
var EditorOpenService = class {
|
|
6582
|
-
#runtime;
|
|
6583
|
-
#platform;
|
|
6584
|
-
#settleMs;
|
|
6585
|
-
constructor(runtime, platform = process.platform, settleMs = SETTLE_MS) {
|
|
6586
|
-
this.#runtime = runtime;
|
|
6587
|
-
this.#platform = platform;
|
|
6588
|
-
this.#settleMs = settleMs;
|
|
6589
|
-
}
|
|
6590
|
-
async open(cwd, editor) {
|
|
6591
|
-
if (this.#platform === "win32" && WINDOWS_UNSAFE.test(cwd)) throw new EditorOpenError("unsupported-path", "The project path cannot be opened through the Windows shell.");
|
|
6592
|
-
const candidates = LAUNCHERS[editor][this.#platform] ?? LAUNCHERS[editor].linux ?? [];
|
|
6593
|
-
let found = false;
|
|
6594
|
-
for (const candidate of candidates) {
|
|
6595
|
-
const argv = await this.#argv(candidate, cwd);
|
|
6596
|
-
if (argv === void 0) continue;
|
|
6597
|
-
found = true;
|
|
6598
|
-
if (await this.#launch(argv, cwd)) return;
|
|
6599
|
-
}
|
|
6600
|
-
throw found ? new EditorOpenError("launch-failed", "The editor refused to open the project.") : new EditorOpenError("editor-unavailable", "The editor was not found on PATH.");
|
|
6601
|
-
}
|
|
6602
|
-
async #argv(candidate, cwd) {
|
|
6603
|
-
const [program, ...rest] = candidate;
|
|
6604
|
-
if (program === void 0) return void 0;
|
|
6605
|
-
if (this.#platform === "win32") return [
|
|
6606
|
-
"cmd.exe",
|
|
6607
|
-
"/d",
|
|
6608
|
-
"/s",
|
|
6609
|
-
"/c",
|
|
6610
|
-
program,
|
|
6611
|
-
...rest,
|
|
6612
|
-
cwd
|
|
6613
|
-
];
|
|
6614
|
-
try {
|
|
6615
|
-
return [
|
|
6616
|
-
await this.#runtime.resolveExecutable(program),
|
|
6617
|
-
...rest,
|
|
6618
|
-
cwd
|
|
6619
|
-
];
|
|
6620
|
-
} catch {
|
|
6621
|
-
return;
|
|
6622
|
-
}
|
|
6623
|
-
}
|
|
6624
|
-
/** True once the editor is launched: either the shim exited cleanly or the
|
|
6625
|
-
* process is still alive past the settle window. */
|
|
6626
|
-
async #launch(argv, cwd) {
|
|
6627
|
-
let handle;
|
|
6628
|
-
try {
|
|
6629
|
-
handle = this.#runtime.spawn({
|
|
6630
|
-
argv,
|
|
6631
|
-
cwd,
|
|
6632
|
-
stdio: {
|
|
6633
|
-
stdin: "ignore",
|
|
6634
|
-
stdout: { maxBytes: MAX_OUTPUT_BYTES$1 },
|
|
6635
|
-
stderr: { maxBytes: MAX_OUTPUT_BYTES$1 }
|
|
6636
|
-
},
|
|
6637
|
-
graceMs: 1e3,
|
|
6638
|
-
env: {}
|
|
6639
|
-
});
|
|
6640
|
-
} catch {
|
|
6641
|
-
return false;
|
|
6642
|
-
}
|
|
6643
|
-
return await Promise.race([handle.done.then((outcome) => outcome.exitCode === 0), new Promise((resolve) => {
|
|
6644
|
-
setTimeout(() => resolve(true), this.#settleMs).unref?.();
|
|
6645
|
-
})]);
|
|
6646
|
-
}
|
|
6647
|
-
};
|
|
6648
|
-
//#endregion
|
|
6649
|
-
//#region src/editor-open-routes.ts
|
|
6650
|
-
const MAX_SESSION_ID_CHARS$5 = 1024;
|
|
6651
|
-
/** Open the session's working directory in a desktop editor. Query-only: the
|
|
6652
|
-
* request carries two enum-ish values, so there is no body to parse. */
|
|
6653
|
-
function registerEditorOpenRoute(ctx, service, cwdForSession) {
|
|
6654
|
-
registerPluginRoute(ctx, {
|
|
6655
|
-
mode: "unary",
|
|
6656
|
-
kind: "exact",
|
|
6657
|
-
path: CLAUDE_EDITOR_OPEN_PATH,
|
|
6658
|
-
methods: ["POST"],
|
|
6659
|
-
budget: "fast",
|
|
6660
|
-
handler: async (io) => {
|
|
6661
|
-
const params = io.url.searchParams;
|
|
6662
|
-
const id = params.get("sessionId");
|
|
6663
|
-
const editor = params.get("editor");
|
|
6664
|
-
if (id === null || id.length === 0 || id.length > MAX_SESSION_ID_CHARS$5) return {
|
|
6665
|
-
status: 400,
|
|
6666
|
-
value: {
|
|
6667
|
-
error: "invalid-session",
|
|
6668
|
-
message: "The session is invalid."
|
|
6669
|
-
}
|
|
6670
|
-
};
|
|
6671
|
-
if (editor === null || !EDITOR_IDS.has(editor)) return {
|
|
6672
|
-
status: 400,
|
|
6673
|
-
value: {
|
|
6674
|
-
error: "invalid-editor",
|
|
6675
|
-
message: "The editor is invalid."
|
|
6676
|
-
}
|
|
6677
|
-
};
|
|
6678
|
-
const cwd = cwdForSession(id);
|
|
6679
|
-
if (cwd === void 0) return {
|
|
6680
|
-
status: 409,
|
|
6681
|
-
value: {
|
|
6682
|
-
error: "session-unavailable",
|
|
6683
|
-
message: "The Claude session is unavailable."
|
|
6684
|
-
}
|
|
6685
|
-
};
|
|
6686
|
-
try {
|
|
6687
|
-
await service.open(cwd, editor);
|
|
6688
|
-
return {
|
|
6689
|
-
status: 200,
|
|
6690
|
-
value: { opened: true }
|
|
6691
|
-
};
|
|
6692
|
-
} catch (error) {
|
|
6693
|
-
if (error instanceof EditorOpenError) return {
|
|
6694
|
-
status: 409,
|
|
6695
|
-
value: {
|
|
6696
|
-
error: error.code,
|
|
6697
|
-
message: error.message
|
|
6698
|
-
}
|
|
6699
|
-
};
|
|
6700
|
-
return {
|
|
6701
|
-
status: 500,
|
|
6702
|
-
value: {
|
|
6703
|
-
error: "editor-open-unavailable",
|
|
6704
|
-
message: "The editor could not be launched."
|
|
6705
|
-
}
|
|
6706
|
-
};
|
|
6707
|
-
}
|
|
6708
|
-
}
|
|
6709
|
-
});
|
|
6710
|
-
}
|
|
6711
|
-
//#endregion
|
|
6712
6574
|
//#region src/prompts.ts
|
|
6713
6575
|
const MAX_PROMPT_FILES = 256;
|
|
6714
6576
|
const MAX_PROMPT_BYTES = 16384;
|
|
@@ -8967,6 +8829,54 @@ function registerClaudeUpdateRoutes(ctx, runtime, deps = {}) {
|
|
|
8967
8829
|
});
|
|
8968
8830
|
}
|
|
8969
8831
|
//#endregion
|
|
8832
|
+
//#region src/windows-job-runner.ts
|
|
8833
|
+
const RUN_AS_NODE = "ELECTRON_RUN_AS_NODE";
|
|
8834
|
+
/**
|
|
8835
|
+
* Make the Host's Windows Job runner start as Node when the Host itself is an
|
|
8836
|
+
* Electron utility process.
|
|
8837
|
+
*
|
|
8838
|
+
* Since DSH Desktop 2.0.7 the Host runs in `utilityProcess.fork` and, on
|
|
8839
|
+
* Windows, `subprocess-local` launches every ordinary target through a helper
|
|
8840
|
+
* it spawns as `[process.execPath, runner.js]`. Inside Electron that path is
|
|
8841
|
+
* `DSH Desktop.exe`, and the fork carries no `ELECTRON_RUN_AS_NODE`, so the
|
|
8842
|
+
* helper starts as a second GUI instance, yields to the running one, and exits
|
|
8843
|
+
* 0 without ever reporting -- the Host then fails the spawn with "Windows Job
|
|
8844
|
+
* runner exited with exit code 0 before proving its managed range empty".
|
|
8845
|
+
*
|
|
8846
|
+
* The runner's environment is read from `process.env` synchronously inside
|
|
8847
|
+
* `spawn()`, so the flag is set for exactly that call and removed after. The
|
|
8848
|
+
* target's own environment is built the same way, so the spec tells the Host
|
|
8849
|
+
* to drop the flag there: a target must not inherit a variable that turns any
|
|
8850
|
+
* Electron app it launches into a bare Node process.
|
|
8851
|
+
*/
|
|
8852
|
+
function withElectronNodeRunner(runtime, options = {}) {
|
|
8853
|
+
const platform = options.platform ?? process.platform;
|
|
8854
|
+
const electron = options.electron ?? process.versions.electron !== void 0;
|
|
8855
|
+
if (platform !== "win32" || !electron) return runtime;
|
|
8856
|
+
return new Proxy(runtime, { get(target, property, receiver) {
|
|
8857
|
+
if (property !== "spawn") {
|
|
8858
|
+
const value = Reflect.get(target, property, receiver);
|
|
8859
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
8860
|
+
}
|
|
8861
|
+
return (spec) => {
|
|
8862
|
+
const withoutFlag = {
|
|
8863
|
+
...spec,
|
|
8864
|
+
env: {
|
|
8865
|
+
...spec.env,
|
|
8866
|
+
[RUN_AS_NODE]: void 0
|
|
8867
|
+
}
|
|
8868
|
+
};
|
|
8869
|
+
if (process.env[RUN_AS_NODE] !== void 0) return target.spawn(withoutFlag);
|
|
8870
|
+
process.env[RUN_AS_NODE] = "1";
|
|
8871
|
+
try {
|
|
8872
|
+
return target.spawn(withoutFlag);
|
|
8873
|
+
} finally {
|
|
8874
|
+
delete process.env[RUN_AS_NODE];
|
|
8875
|
+
}
|
|
8876
|
+
};
|
|
8877
|
+
} });
|
|
8878
|
+
}
|
|
8879
|
+
//#endregion
|
|
8970
8880
|
//#region src/plan-usage-routes.ts
|
|
8971
8881
|
const EMPTY = {
|
|
8972
8882
|
available: false,
|
|
@@ -9514,6 +9424,7 @@ async function installManagedPresetCompatibility(logger, install = ensureManaged
|
|
|
9514
9424
|
}
|
|
9515
9425
|
}
|
|
9516
9426
|
async function apply(ctx, config) {
|
|
9427
|
+
const subprocess = withElectronNodeRunner(ctx.subprocess);
|
|
9517
9428
|
await installManagedPresetCompatibility(ctx.logger);
|
|
9518
9429
|
const defaultLimits = {
|
|
9519
9430
|
idleTimeoutMs: config.idleTimeoutMs ?? 18e5,
|
|
@@ -9531,15 +9442,15 @@ async function apply(ctx, config) {
|
|
|
9531
9442
|
};
|
|
9532
9443
|
await applySettingsOverrides();
|
|
9533
9444
|
const sidecar = new ClaudeSidecarRepository();
|
|
9534
|
-
const repositoryStatus = new RepositoryStatusService(
|
|
9535
|
-
const repositorySetup = new RepositorySetupService(
|
|
9445
|
+
const repositoryStatus = new RepositoryStatusService(subprocess);
|
|
9446
|
+
const repositorySetup = new RepositorySetupService(subprocess, {
|
|
9536
9447
|
branchPrefix: () => readWorktreeBranchPrefix(),
|
|
9537
9448
|
summarizeBranch: (intent) => summarizeBranchSlug(supervisorConfig.executablePath, intent)
|
|
9538
9449
|
});
|
|
9539
9450
|
const reviewComments = new ReviewCommentStore();
|
|
9540
9451
|
const commandCatalogs = /* @__PURE__ */ new Map();
|
|
9541
9452
|
const supervisor = new ClaudeSupervisor({
|
|
9542
|
-
runtime:
|
|
9453
|
+
runtime: subprocess,
|
|
9543
9454
|
approval: ctx.approval,
|
|
9544
9455
|
userQuestions: ctx.userQuestions,
|
|
9545
9456
|
config: supervisorConfig,
|
|
@@ -9548,7 +9459,7 @@ async function apply(ctx, config) {
|
|
|
9548
9459
|
});
|
|
9549
9460
|
let resolutionError;
|
|
9550
9461
|
try {
|
|
9551
|
-
supervisorConfig.executablePath = (await resolveClaudeExecutable(
|
|
9462
|
+
supervisorConfig.executablePath = (await resolveClaudeExecutable(subprocess, config.executablePath === void 0 || config.executablePath.length === 0 ? void 0 : config.executablePath)).path;
|
|
9552
9463
|
ctx.llm.registerAdapter([...CLAUDE_CODE_PROVIDER_IDS], createClaudeCodeAdapter(supervisor, ctx.agents, ctx.attachments, (agent) => ctx.agentPresets.composedPreset(agent.ctx), (sessionId) => reviewComments.drain(sessionId), () => readRenderMode(), (request) => summarizeSessionTitle(supervisorConfig.executablePath, request), () => probeClaudeModels(supervisorConfig.executablePath)));
|
|
9553
9464
|
ctx.effect(() => {
|
|
9554
9465
|
const mounted = /* @__PURE__ */ new Map();
|
|
@@ -9648,9 +9559,9 @@ async function apply(ctx, config) {
|
|
|
9648
9559
|
ctx.effect(() => () => repositoryStatus.dispose(), "dsh-claude: repository status cache");
|
|
9649
9560
|
ctx.inject(["webServer"], (webCtx) => {
|
|
9650
9561
|
registerClaudeClientDiagnosticsRoute(webCtx);
|
|
9651
|
-
registerClaudeDoctorRoutes(webCtx,
|
|
9562
|
+
registerClaudeDoctorRoutes(webCtx, subprocess, supervisor, supervisorConfig, resolutionError);
|
|
9652
9563
|
const desktopActions = webCtx.get("desktopActions");
|
|
9653
|
-
registerClaudeUpdateRoutes(webCtx,
|
|
9564
|
+
registerClaudeUpdateRoutes(webCtx, subprocess, { ...typeof desktopActions?.requestRestart === "function" ? { requestRestart: desktopActions.requestRestart.bind(desktopActions) } : {} });
|
|
9654
9565
|
registerClaudeGlobalSettingsRoute(webCtx, {
|
|
9655
9566
|
defaultLimits,
|
|
9656
9567
|
onUpdated: async () => {
|
|
@@ -9662,7 +9573,7 @@ async function apply(ctx, config) {
|
|
|
9662
9573
|
registerRepositoryStatusRoute(webCtx, repositoryStatus);
|
|
9663
9574
|
registerRepositoryFileRoute(webCtx, repositoryStatus);
|
|
9664
9575
|
registerJiraRoute(webCtx, new JiraService());
|
|
9665
|
-
const repositoryActions = new RepositoryActionService(
|
|
9576
|
+
const repositoryActions = new RepositoryActionService(subprocess, supervisorConfig.executablePath, (cwd) => repositoryStatus.invalidate(cwd));
|
|
9666
9577
|
/** Roots the latest projection probe vouched for, per session: the only
|
|
9667
9578
|
* checkouts a route may act on besides the session's own. */
|
|
9668
9579
|
const extraRoots = /* @__PURE__ */ new Map();
|
|
@@ -9682,13 +9593,12 @@ async function apply(ctx, config) {
|
|
|
9682
9593
|
return statuses;
|
|
9683
9594
|
};
|
|
9684
9595
|
registerRepositoryActionRoute(webCtx, repositoryActions, cwdForClaudeSession);
|
|
9685
|
-
registerEditorOpenRoute(webCtx, new EditorOpenService(webCtx.subprocess), cwdForClaudeSession);
|
|
9686
9596
|
registerClaudePromptsRoute(webCtx);
|
|
9687
|
-
const promptAssist = new PromptAssistService(
|
|
9597
|
+
const promptAssist = new PromptAssistService(subprocess, () => supervisorConfig.executablePath);
|
|
9688
9598
|
registerClaudePromptNameRoute(webCtx, promptAssist);
|
|
9689
9599
|
registerClaudePromptRefineRoute(webCtx, promptAssist);
|
|
9690
|
-
registerPullRequestFeedbackRoute(webCtx, new PullRequestFeedbackService(
|
|
9691
|
-
registerAskRoute(webCtx, new AskService(
|
|
9600
|
+
registerPullRequestFeedbackRoute(webCtx, new PullRequestFeedbackService(subprocess), cwdForClaudeSession);
|
|
9601
|
+
registerAskRoute(webCtx, new AskService(subprocess, supervisorConfig.executablePath), cwdForClaudeSession, (sessionId) => {
|
|
9692
9602
|
const snapshot = supervisor.snapshots().find((item) => item.sessionId === sessionId);
|
|
9693
9603
|
return snapshot === void 0 ? void 0 : {
|
|
9694
9604
|
model: claudeModelValue(snapshot.model),
|
|
@@ -9710,7 +9620,7 @@ async function apply(ctx, config) {
|
|
|
9710
9620
|
reset: (sessionId) => supervisor.disposeSession(sessionId),
|
|
9711
9621
|
restoreFiles: async (sessionId, tree) => {
|
|
9712
9622
|
const cwd = webCtx.agents.get(sessionId)?.session.header.cwd;
|
|
9713
|
-
return cwd === void 0 ? false : restoreWorktreeTree(
|
|
9623
|
+
return cwd === void 0 ? false : restoreWorktreeTree(subprocess, cwd, tree);
|
|
9714
9624
|
}
|
|
9715
9625
|
});
|
|
9716
9626
|
registerPlanUsageRoute(webCtx, (fetchedAt) => probePlanUsage(supervisorConfig.executablePath, fetchedAt));
|