dsh-wechat-remote 1.7.4 → 1.7.5

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.
@@ -1,1268 +1,1268 @@
1
- /* Generated from the shared plugin installation sources. */
2
-
3
- // src/install-control.ts
4
- import fs2 from "node:fs";
5
- import path5 from "node:path";
6
- import http from "node:http";
7
-
8
- // src/dsh-runtime.ts
9
- import { homedir } from "node:os";
10
- import path from "node:path";
11
- function adapterDshHome(environment = process.env, userHome = homedir()) {
12
- const configured = environment.DSH_HOME;
13
- const selected = configured && configured.trim() ? configured : path.join(userHome, ".dsh");
14
- const expanded = selected === "~" ? userHome : /^~[\\/]/.test(selected) ? path.join(userHome, selected.slice(2)) : selected;
15
- return path.resolve(expanded);
16
- }
17
- function validPort(value) {
18
- const parsed = typeof value === "number" ? value : typeof value === "string" && /^\d{1,5}$/.test(value) ? Number(value) : Number.NaN;
19
- return Number.isSafeInteger(parsed) && parsed >= 1 && parsed <= 65535 ? parsed : null;
20
- }
21
- function resolveDshWebRuntime(ctx, environment = process.env) {
22
- const webServer = ctx.get("webServer");
23
- const servicePort = validPort(webServer?.port);
24
- if (servicePort !== null) return { port: servicePort, source: "web-server" };
25
- const environmentPort = validPort(environment.DSH_PORT);
26
- if (environmentPort !== null) return { port: environmentPort, source: "environment" };
27
- return { port: 3080, source: "legacy-default" };
28
- }
29
-
30
- // src/agent-metadata.ts
31
- import { createHash, randomBytes as randomBytes2 } from "node:crypto";
32
- import { existsSync as existsSync3, readFileSync as readFileSync2, realpathSync } from "node:fs";
33
- import { homedir as homedir3, hostname } from "node:os";
34
- import path3 from "node:path";
35
- import { fileURLToPath } from "node:url";
36
-
37
- // src/secure-file.ts
38
- import { execFileSync } from "node:child_process";
39
- import {
40
- chmodSync,
41
- closeSync,
42
- existsSync,
43
- fsyncSync,
44
- mkdirSync,
45
- openSync,
46
- readFileSync,
47
- renameSync,
48
- rmSync,
49
- writeFileSync
50
- } from "node:fs";
51
- import { dirname } from "node:path";
52
- import { userInfo } from "node:os";
53
- import { randomBytes } from "node:crypto";
54
- function tightenPrivateFile(file) {
55
- try {
56
- chmodSync(file, 384);
57
- } catch {
58
- }
59
- if (process.platform !== "win32") return;
60
- try {
61
- execFileSync("icacls", [file, "/inheritance:r", "/grant:r", `${userInfo().username}:F`], {
62
- timeout: 5e3,
63
- windowsHide: true,
64
- stdio: "ignore"
65
- });
66
- } catch {
67
- }
68
- }
69
- function writePrivateJsonAtomic(file, value) {
70
- const parent = dirname(file);
71
- mkdirSync(parent, { recursive: true, mode: 448 });
72
- const temporary = `${file}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
73
- try {
74
- writeFileSync(temporary, `${JSON.stringify(value, null, 2)}
75
- `, {
76
- encoding: "utf8",
77
- mode: 384,
78
- flag: "wx"
79
- });
80
- const fd = openSync(temporary, "r+");
81
- try {
82
- try {
83
- fsyncSync(fd);
84
- } catch {
85
- }
86
- } finally {
87
- closeSync(fd);
88
- }
89
- tightenPrivateFile(temporary);
90
- renameSync(temporary, file);
91
- tightenPrivateFile(file);
92
- } finally {
93
- if (existsSync(temporary)) {
94
- try {
95
- rmSync(temporary, { force: true });
96
- } catch {
97
- }
98
- }
99
- }
100
- }
101
- function readPrivateJson(file) {
102
- const value = JSON.parse(readFileSync(file, "utf8"));
103
- tightenPrivateFile(file);
104
- return value;
105
- }
106
-
107
- // src/host-platform.ts
108
- import { execFile } from "node:child_process";
109
- import { existsSync as existsSync2 } from "node:fs";
110
- import { homedir as homedir2, networkInterfaces, platform } from "node:os";
111
- import path2 from "node:path";
112
- import { promisify } from "node:util";
113
- var execFileAsync = promisify(execFile);
114
- var BasePlatformAdapter = class {
115
- async isPotentiallyBlockingPath(_target, signal) {
116
- signal.throwIfAborted();
117
- return false;
118
- }
119
- lanIPv4() {
120
- return selectLanIPv4();
121
- }
122
- };
123
- var WindowsPlatformAdapter = class extends BasePlatformAdapter {
124
- descriptor = Object.freeze({
125
- kind: "windows",
126
- name: "Windows",
127
- pathStyle: "windows",
128
- directoryRootStyle: "drives"
129
- });
130
- rootsPromise;
131
- rootsByPath = /* @__PURE__ */ new Map();
132
- async filesystemRoots(signal) {
133
- if (!this.rootsPromise) {
134
- const workerSignal = new AbortController().signal;
135
- this.rootsPromise = enumerateWindowsRoots(workerSignal).then((roots) => {
136
- this.rootsByPath.clear();
137
- for (const root of roots) this.rootsByPath.set(root.path.toUpperCase(), root);
138
- return roots;
139
- }).catch((error) => {
140
- this.rootsPromise = void 0;
141
- throw error;
142
- });
143
- }
144
- return await raceSignal(this.rootsPromise, signal);
145
- }
146
- async isPotentiallyBlockingPath(target, signal) {
147
- signal.throwIfAborted();
148
- if (target.startsWith("\\\\")) return true;
149
- const match = /^([A-Za-z]):[\\/]/.exec(target);
150
- if (!match) return false;
151
- const rootPath = `${match[1].toUpperCase()}:\\`;
152
- let root = this.rootsByPath.get(rootPath);
153
- if (!root) {
154
- try {
155
- await this.filesystemRoots(signal);
156
- } catch (error) {
157
- signal.throwIfAborted();
158
- return true;
159
- }
160
- root = this.rootsByPath.get(rootPath);
161
- }
162
- return root?.kind === "network";
163
- }
164
- };
165
- var MacPlatformAdapter = class extends BasePlatformAdapter {
166
- descriptor = Object.freeze({
167
- kind: "macos",
168
- name: "macOS",
169
- pathStyle: "posix",
170
- directoryRootStyle: "filesystem"
171
- });
172
- async filesystemRoots(signal) {
173
- signal.throwIfAborted();
174
- const roots = [
175
- { name: "\u4E3B\u76EE\u5F55", path: homedir2(), kind: "home" },
176
- { name: "\u6839\u76EE\u5F55", path: "/", kind: "filesystem" }
177
- ];
178
- if (existsSync2("/Volumes")) roots.push({ name: "\u5377", path: "/Volumes", kind: "volume" });
179
- return roots;
180
- }
181
- async isPotentiallyBlockingPath(target, signal) {
182
- signal.throwIfAborted();
183
- const normalized = path2.resolve(target);
184
- return normalized.startsWith("/Volumes/");
185
- }
186
- };
187
- var LinuxPlatformAdapter = class extends BasePlatformAdapter {
188
- descriptor = Object.freeze({
189
- kind: "linux",
190
- name: "Linux",
191
- pathStyle: "posix",
192
- directoryRootStyle: "filesystem"
193
- });
194
- async filesystemRoots(signal) {
195
- signal.throwIfAborted();
196
- const roots = [
197
- { name: "\u4E3B\u76EE\u5F55", path: homedir2(), kind: "home" },
198
- { name: "\u6839\u76EE\u5F55", path: "/", kind: "filesystem" }
199
- ];
200
- for (const mountRoot of ["/mnt", "/media"]) {
201
- if (existsSync2(mountRoot)) roots.push({ name: path2.basename(mountRoot), path: mountRoot, kind: "volume" });
202
- }
203
- return roots;
204
- }
205
- async isPotentiallyBlockingPath(target, signal) {
206
- signal.throwIfAborted();
207
- const normalized = path2.resolve(target);
208
- return normalized.startsWith("/mnt/") || normalized.startsWith("/media/") || /\/run\/user\/\d+\/gvfs(?:\/|$)/.test(normalized);
209
- }
210
- };
211
- var UnknownPlatformAdapter = class extends BasePlatformAdapter {
212
- descriptor = Object.freeze({
213
- kind: "unknown",
214
- name: "Unknown",
215
- pathStyle: path2.sep === "\\" ? "windows" : "posix",
216
- directoryRootStyle: path2.sep === "\\" ? "drives" : "filesystem"
217
- });
218
- async filesystemRoots(signal) {
219
- signal.throwIfAborted();
220
- return [{ name: "\u4E3B\u76EE\u5F55", path: homedir2(), kind: "home" }];
221
- }
222
- };
223
- function buildAdapter() {
224
- switch (platform()) {
225
- case "win32":
226
- return new WindowsPlatformAdapter();
227
- case "darwin":
228
- return new MacPlatformAdapter();
229
- case "linux":
230
- return new LinuxPlatformAdapter();
231
- default:
232
- return new UnknownPlatformAdapter();
233
- }
234
- }
235
- var hostPlatform = buildAdapter();
236
- function hostPlatformDescriptor() {
237
- return hostPlatform.descriptor;
238
- }
239
- function selectLanIPv4(source = networkInterfaces()) {
240
- const candidates = [];
241
- for (const [name, addresses] of Object.entries(source)) {
242
- for (const item of addresses || []) {
243
- if (item.family !== "IPv4" || item.internal || !item.address) continue;
244
- candidates.push({ name, address: item.address });
245
- }
246
- }
247
- candidates.sort((left, right) => scoreAddress(right) - scoreAddress(left));
248
- return candidates[0]?.address || "127.0.0.1";
249
- }
250
- function scoreAddress(candidate) {
251
- const address = candidate.address;
252
- const name = candidate.name.toLowerCase();
253
- let score = 0;
254
- if (address.startsWith("192.168.")) score += 100;
255
- else if (address.startsWith("10.")) score += 90;
256
- else if (isPrivate172(address)) score += 80;
257
- else score += 10;
258
- if (/^(en0|en1|wi-?fi|wlan\d*|ethernet|以太网)/i.test(name)) score += 30;
259
- if (/(docker|veth|vmnet|virtualbox|hyper-v|wsl|loopback|utun|bridge|tunnel|vpn)/i.test(name)) score -= 80;
260
- if (address.startsWith("169.254.") || isBenchmarkAddress(address) || isCarrierGradeNat(address)) score -= 200;
261
- return score;
262
- }
263
- function isPrivate172(address) {
264
- const match = /^172\.(\d+)\./.exec(address);
265
- if (!match) return false;
266
- const second = Number(match[1]);
267
- return second >= 16 && second <= 31;
268
- }
269
- function isBenchmarkAddress(address) {
270
- const match = /^198\.(\d+)\./.exec(address);
271
- if (!match) return false;
272
- const second = Number(match[1]);
273
- return second === 18 || second === 19;
274
- }
275
- function isCarrierGradeNat(address) {
276
- const match = /^100\.(\d+)\./.exec(address);
277
- if (!match) return false;
278
- const second = Number(match[1]);
279
- return second >= 64 && second <= 127;
280
- }
281
- async function enumerateWindowsRoots(signal) {
282
- const script = [
283
- "[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)",
284
- "Get-PSDrive -PSProvider FileSystem | Where-Object { $_.Name -match '^[A-Za-z]$' -and $_.Root -match '^[A-Za-z]:\\\\$' } | ForEach-Object {",
285
- " [pscustomobject]@{ name = $_.Name.ToUpperInvariant(); path = $_.Root; displayRoot = $(if ($_.DisplayRoot) { [string]$_.DisplayRoot } else { $null }) }",
286
- "} | Sort-Object name | ConvertTo-Json -Compress"
287
- ].join("; ");
288
- const { stdout } = await execFileAsync(
289
- "powershell.exe",
290
- ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", script],
291
- { encoding: "utf8", windowsHide: true, timeout: 8e3, signal }
292
- );
293
- signal.throwIfAborted();
294
- const text = String(stdout).replace(/^\uFEFF/, "").trim();
295
- if (!text) return [];
296
- const decoded = JSON.parse(text);
297
- const rows = Array.isArray(decoded) ? decoded : [decoded];
298
- const unique = /* @__PURE__ */ new Map();
299
- for (const row of rows) {
300
- if (typeof row.name !== "string" || typeof row.path !== "string") continue;
301
- const name = row.name.toUpperCase();
302
- if (!/^[A-Z]$/.test(name) || !/^[A-Za-z]:[\\/]$/.test(row.path)) continue;
303
- const rootPath = `${name}:\\`;
304
- const displayRoot = typeof row.displayRoot === "string" && row.displayRoot.trim() ? row.displayRoot : void 0;
305
- unique.set(rootPath, {
306
- name: `${name}:`,
307
- path: rootPath,
308
- kind: displayRoot ? "network" : "local",
309
- ...displayRoot ? { displayRoot } : {}
310
- });
311
- }
312
- return [...unique.values()].sort((left, right) => left.path.localeCompare(right.path));
313
- }
314
- function raceSignal(promise, signal) {
315
- if (signal.aborted) return Promise.reject(signal.reason);
316
- return new Promise((resolve, reject) => {
317
- const aborted = () => reject(signal.reason);
318
- signal.addEventListener("abort", aborted, { once: true });
319
- promise.then(
320
- (value) => {
321
- signal.removeEventListener("abort", aborted);
322
- resolve(value);
323
- },
324
- (error) => {
325
- signal.removeEventListener("abort", aborted);
326
- reject(error);
327
- }
328
- );
329
- });
330
- }
331
-
332
- // src/agent-metadata.ts
333
- var ROOT = path3.join(adapterDshHome(), "harness-remote");
334
- var HOST_PATH = path3.join(ROOT, "host.json");
335
- var cachedDescriptor = null;
336
- var AGENT_CAPABILITIES = Object.freeze([
337
- Object.freeze({ id: "dsh.rpc", version: 1 }),
338
- Object.freeze({ id: "dsh.realtime", version: 1 }),
339
- Object.freeze({ id: "wechat.directory", version: 1 }),
340
- Object.freeze({ id: "wechat.history-window", version: 1 }),
341
- Object.freeze({ id: "wechat.attachment-object", version: 1 }),
342
- Object.freeze({ id: "harness.public-relay-e2ee", version: 1 }),
343
- Object.freeze({ id: "harness.lan-bootstrap", version: 1 }),
344
- Object.freeze({ id: "harness.host-platform", version: 1 }),
345
- Object.freeze({ id: "harness.oss-e2ee-objects", version: 1 }),
346
- Object.freeze({ id: "harness.secure-lan", version: 1 })
347
- ]);
348
- function stableId(file) {
349
- if (existsSync3(file)) {
350
- const stored = readPrivateJson(file);
351
- if (stored.version !== 1 || !/^[A-Za-z0-9_-]{20,64}$/.test(stored.id)) {
352
- throw new Error(`Harness Remote metadata is invalid: ${path3.basename(file)}`);
353
- }
354
- return stored.id;
355
- }
356
- const id = randomBytes2(18).toString("base64url");
357
- writePrivateJsonAtomic(file, { version: 1, id });
358
- return id;
359
- }
360
- function agentProfileScope() {
361
- return resolveAgentProfileScope(fileURLToPath(import.meta.url), process.argv, adapterDshHome());
362
- }
363
- function resolveAgentProfileScope(modulePath, argv, dshHome) {
364
- const valid = (value) => Boolean(value) && value.length <= 80 && !/[\\/\u0000-\u001f]/.test(value) && value !== "." && value !== ".." && value !== "node_modules";
365
- for (let index = 2; index < argv.length && argv[index] !== "--"; index++) {
366
- const arg = argv[index];
367
- const candidate = arg === "--profile" ? argv[index + 1] : arg.startsWith("--profile=") ? arg.slice("--profile=".length) : void 0;
368
- if (valid(candidate)) return candidate;
369
- }
370
- const relative = path3.relative(path3.join(dshHome, "profiles"), modulePath);
371
- const parts = relative.split(/[\\/]/);
372
- if (valid(parts[0]) && parts[1] === "node_modules") return parts[0];
373
- if (argv[2] === "web") return "web";
374
- return "default";
375
- }
376
- function instanceStorageKey(profileScope = agentProfileScope()) {
377
- return createHash("sha256").update(`deepseek-harness\0${profileScope}`).digest("hex").slice(0, 24);
378
- }
379
- function gateStatePathForProfile(profileScope, homeDirectory = homedir3(), dshHome = path3.join(homeDirectory, ".dsh")) {
380
- const normalized = profileScope.trim().toLowerCase();
381
- if (normalized === "web" || normalized === "default") {
382
- return path3.join(dshHome, "gate-wechat-state.json");
383
- }
384
- return path3.join(
385
- dshHome,
386
- "harness-remote",
387
- "instances",
388
- instanceStorageKey(profileScope),
389
- "gate-wechat-state.json"
390
- );
391
- }
392
- function defaultAgentIdentityPath() {
393
- const scope = agentProfileScope();
394
- if (scope === "web" || scope === "default") {
395
- return path3.join(adapterDshHome(), "harness-remote-public-identity.json");
396
- }
397
- return path3.join(ROOT, "instances", instanceStorageKey(scope), "identity.json");
398
- }
399
- function packageVersionFromAncestors(start) {
400
- let current = path3.resolve(start);
401
- for (let depth = 0; depth < 8; depth += 1) {
402
- const manifest = path3.join(current, "package.json");
403
- try {
404
- const value = JSON.parse(readFileSync2(manifest, "utf8"));
405
- if (value.name === "@deepseek-ai/dsh" && typeof value.version === "string" && value.version) {
406
- return value.version;
407
- }
408
- } catch {
409
- }
410
- const parent = path3.dirname(current);
411
- if (parent === current) break;
412
- current = parent;
413
- }
414
- return null;
415
- }
416
- function installedDshVersion() {
417
- const override = process.env.DSH_RUNTIME_VERSION;
418
- if (override && /^[A-Za-z0-9._+-]{1,64}$/.test(override)) return override;
419
- const argvEntry = process.argv[1];
420
- if (argvEntry) {
421
- let resolvedEntry = argvEntry;
422
- try {
423
- resolvedEntry = realpathSync(argvEntry);
424
- } catch {
425
- }
426
- const found = packageVersionFromAncestors(path3.dirname(resolvedEntry));
427
- if (found) return found;
428
- }
429
- for (const entry of String(process.env.PATH || "").split(path3.delimiter)) {
430
- if (!entry) continue;
431
- try {
432
- const command = path3.join(entry, process.platform === "win32" ? "dsh.cmd" : "dsh");
433
- const found = packageVersionFromAncestors(path3.dirname(realpathSync(command)));
434
- if (found) return found;
435
- } catch {
436
- }
437
- try {
438
- const value = JSON.parse(readFileSync2(
439
- path3.join(entry, "node_modules", "@deepseek-ai", "dsh", "package.json"),
440
- "utf8"
441
- ));
442
- if (value.name === "@deepseek-ai/dsh" && typeof value.version === "string" && value.version) {
443
- return value.version;
444
- }
445
- } catch {
446
- }
447
- }
448
- return "unknown";
449
- }
450
- function loadAgentDescriptor() {
451
- if (cachedDescriptor) return cachedDescriptor;
452
- const instancePath = path3.join(ROOT, "instances", instanceStorageKey(), "agent.json");
453
- cachedDescriptor = {
454
- schemaVersion: 1,
455
- hostId: stableId(HOST_PATH),
456
- agentInstanceId: stableId(instancePath),
457
- hostName: hostname(),
458
- agentKind: "deepseek-harness",
459
- agentName: "DeepSeek Harness",
460
- agentVersion: installedDshVersion(),
461
- hostPlatform: hostPlatformDescriptor(),
462
- capabilities: AGENT_CAPABILITIES
463
- };
464
- return cachedDescriptor;
465
- }
466
-
467
- // src/gate-ports.ts
468
- import { createHash as createHash2 } from "node:crypto";
469
- var LEGACY_PUBLIC_PORT = 3092;
470
- var LEGACY_LOCAL_PORT = 3093;
471
- var DYNAMIC_PORT_BASE = 32e3;
472
- var DYNAMIC_PORT_PAIRS = 4e3;
473
- function validPort2(value) {
474
- if (value === void 0 || value === "") return null;
475
- if (!/^\d{1,5}$/.test(value)) return null;
476
- const parsed = Number(value);
477
- return Number.isSafeInteger(parsed) && parsed >= 1 && parsed <= 65535 ? parsed : null;
478
- }
479
- function isLegacyProfile(profileScope) {
480
- const normalized = profileScope.trim().toLowerCase();
481
- return normalized === "web" || normalized === "default";
482
- }
483
- function deriveGatePorts(profileScope, agentInstanceId, environment = process.env) {
484
- const warnings = [];
485
- const legacy = isLegacyProfile(profileScope);
486
- const digest = createHash2("sha256").update(`harness-remote-gate-v1\0${profileScope}\0${agentInstanceId}`).digest();
487
- const pair = digest.readUInt32BE(0) % DYNAMIC_PORT_PAIRS;
488
- const derivedPublic = legacy ? LEGACY_PUBLIC_PORT : DYNAMIC_PORT_BASE + pair * 2;
489
- const derivedLocal = legacy ? LEGACY_LOCAL_PORT : derivedPublic + 1;
490
- const publicOverride = validPort2(environment.WECHAT_GATE_PORT);
491
- const localOverride = validPort2(environment.WECHAT_GATE_LOCAL_PORT);
492
- if (environment.WECHAT_GATE_PORT && publicOverride === null) {
493
- warnings.push(`\u5FFD\u7565\u65E0\u6548\u7684 WECHAT_GATE_PORT=${environment.WECHAT_GATE_PORT}`);
494
- }
495
- if (environment.WECHAT_GATE_LOCAL_PORT && localOverride === null) {
496
- warnings.push(`\u5FFD\u7565\u65E0\u6548\u7684 WECHAT_GATE_LOCAL_PORT=${environment.WECHAT_GATE_LOCAL_PORT}`);
497
- }
498
- const publicPort = publicOverride ?? derivedPublic;
499
- const localPort = localOverride ?? derivedLocal;
500
- if (publicPort === localPort) {
501
- warnings.push(`\u516C\u7F51\u95E8\u4E0E\u672C\u5730\u95E8\u90FD\u914D\u7F6E\u4E3A ${publicPort}\uFF1B\u5176\u4E2D\u4E00\u6247\u95E8\u5C06\u65E0\u6CD5\u76D1\u542C`);
502
- }
503
- return {
504
- profileScope,
505
- publicPort,
506
- localPort,
507
- source: publicOverride !== null || localOverride !== null ? "environment-override" : legacy ? "legacy-default" : "profile-derived",
508
- warnings
509
- };
510
- }
511
-
512
- // src/dsh-protocol-compat.ts
513
- import { setTimeout as delay } from "node:timers/promises";
514
-
515
- // src/dsh-session-address.ts
516
- function record(value) {
517
- return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
518
- }
519
- function unavailable(message, sessionId, reason) {
520
- throw Object.assign(new Error(message), {
521
- code: "adapter/session-address-unavailable",
522
- details: { sessionId, reason }
523
- });
524
- }
525
- async function resolveDshSessionAddress(gateway, sessionId, signal) {
526
- signal.throwIfAborted();
527
- if (!sessionId || sessionId.length > 256) unavailable("\u4F1A\u8BDD\u6807\u8BC6\u65E0\u6548", sessionId, "invalid-id");
528
- const listing = record(await gateway.invoke({
529
- namespace: "session",
530
- method: "list",
531
- args: { _request: {} },
532
- signal
533
- }));
534
- signal.throwIfAborted();
535
- if (!Array.isArray(listing?.items)) unavailable("DSH \u4F1A\u8BDD\u76EE\u5F55\u6682\u4E0D\u53EF\u7528", sessionId, "invalid-directory");
536
- const rows = listing.items.map(record).filter((row2) => row2?.sessionId === sessionId);
537
- if (rows.length !== 1) unavailable("\u4F1A\u8BDD\u4E0D\u5B58\u5728\u6216\u6682\u4E0D\u53EF\u8BFB\u53D6\uFF0C\u8BF7\u5237\u65B0\u4F1A\u8BDD\u5217\u8868", sessionId, "missing-session");
538
- const row = rows[0];
539
- if (row.origin !== "subagent") return { kind: "session", sessionId };
540
- const parentSessionId = row.parentSessionId;
541
- if (typeof parentSessionId !== "string" || !parentSessionId || parentSessionId === sessionId) {
542
- unavailable("\u5B50\u4EE3\u7406\u4F1A\u8BDD\u7F3A\u5C11\u6709\u6548\u7684\u7236\u4F1A\u8BDD\u6807\u8BC6", sessionId, "invalid-parent");
543
- }
544
- const catalog = record(await gateway.invoke({
545
- namespace: "subagents",
546
- method: "list",
547
- args: { parentSessionId },
548
- signal
549
- }));
550
- signal.throwIfAborted();
551
- if (!Array.isArray(catalog?.entries)) unavailable("DSH \u5B50\u4EE3\u7406\u76EE\u5F55\u6682\u4E0D\u53EF\u7528", sessionId, "invalid-catalog");
552
- const children = catalog.entries.map(record).filter((entry) => entry?.id === sessionId);
553
- if (children.length !== 1) unavailable("\u5B50\u4EE3\u7406\u4F1A\u8BDD\u6682\u4E0D\u53EF\u8BFB\u53D6\uFF0C\u8BF7\u5237\u65B0\u540E\u91CD\u8BD5", sessionId, "missing-child");
554
- const child = children[0];
555
- if (child.kind !== "child" || child.mode !== "one-shot" && child.mode !== "continuable") {
556
- unavailable(
557
- "DSH \u65E0\u6CD5\u8BC6\u522B\u6B64\u5B50\u4EE3\u7406\u4F1A\u8BDD\uFF0C\u8BF7\u5728\u7535\u8111\u7AEF\u68C0\u67E5\u8BE5\u4F1A\u8BDD",
558
- sessionId,
559
- typeof child.reason === "string" ? child.reason : "unsupported-child"
560
- );
561
- }
562
- return { kind: "subagent", parentSessionId, childSessionId: sessionId, mode: child.mode };
563
- }
564
-
565
- // src/session-presentation.ts
566
- var object = (v) => v && typeof v === "object" && !Array.isArray(v) ? v : {};
567
- function numbers(v, names) {
568
- return Object.fromEntries(names.flatMap(([from, to]) => typeof v[from] === "number" && Number.isFinite(v[from]) && Number(v[from]) >= 0 ? [[to, v[from]]] : []));
569
- }
570
- function presentationProjection(key, raw) {
571
- const base = object(raw), v = base.values ? object(base.values) : base;
572
- let name, value;
573
- switch (key) {
574
- case "sessionStats":
575
- name = "metrics";
576
- value = numbers(v, [["turns", "turns"], ["steps", "steps"], ["toolMs", "toolMs"], ["llmMs", "modelMs"], ["ttftMs", "firstTokenMs"], ["ttftSteps", "firstTokenSamples"], ["decodeMs", "decodeMs"], ["decodeTokens", "decodeTokens"]]);
577
- break;
578
- case "tokenUsage":
579
- name = "usage";
580
- value = numbers(v, [["uncachedInputTokens", "input"], ["outputTokens", "output"], ["cacheReadTokens", "cacheRead"], ["cacheWriteTokens", "cacheWrite"]]);
581
- break;
582
- case "contextPressure":
583
- name = "context";
584
- value = { estimated: true, ...numbers(v, [[v.projectedTokens === void 0 ? "pressureTokens" : "projectedTokens", "used"], ["contextWindow", "capacity"]]) };
585
- break;
586
- case "contextBreakdown":
587
- name = "composition";
588
- value = numbers(v, [["systemTokens", "system"], ["toolsTokens", "tools"], ["messageTokens", "messages"]]);
589
- break;
590
- case "plan":
591
- name = "planMode";
592
- value = { active: v.active === true, pending: v.pending === true };
593
- break;
594
- case "todos":
595
- name = "plan";
596
- value = Array.isArray(raw) ? { steps: raw.map((t) => ({ text: object(t).content, status: object(t).status })) } : null;
597
- break;
598
- case "goal": {
599
- name = "goal";
600
- const goal = object(base.goal);
601
- value = base.goal ? { id: goal.id, revision: goal.revision, text: goal.objective, status: goal.phase, rounds: base.roundsStarted, roundLimit: goal.maxGoalRounds, reason: object(goal.blockedReason).message } : null;
602
- break;
603
- }
604
- default:
605
- return null;
606
- }
607
- return { key: `agent.${name}.v1`, value };
608
- }
609
- function withPresentationProjections(block) {
610
- const source = object(block);
611
- if (!source.values) return block;
612
- const values = { ...object(source.values) };
613
- for (const [key, value] of Object.entries(values)) {
614
- const projected = presentationProjection(key, value);
615
- if (projected) values[projected.key] = projected.value;
616
- }
617
- return { ...source, values };
618
- }
619
-
620
- // src/dsh-protocol-compat.ts
621
- var SINGLE_REQUEST_METHODS = /* @__PURE__ */ new Set([
622
- "session.attachment",
623
- "session.cancel",
624
- "session.create",
625
- "session.fork",
626
- "session.openWorkspacePath",
627
- "session.rename",
628
- "session.search",
629
- "session.selectModel",
630
- "session.updateQueue",
631
- "workspace.archiveSession",
632
- "workspace.create",
633
- "workspace.delete",
634
- "workspace.insertBefore",
635
- "workspace.insertSessionBefore",
636
- "workspace.rename"
637
- ]);
638
- function recordOf(value) {
639
- return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
640
- }
641
- function present(value, key) {
642
- return Object.hasOwn(value, key) ? { [key]: value[key] } : {};
643
- }
644
- function requestInvocation(method, payload) {
645
- const [namespace, remoteMethod] = method.split(".", 2);
646
- return {
647
- kind: "invoke",
648
- namespace,
649
- method: remoteMethod,
650
- args: { request: payload }
651
- };
652
- }
653
- function planLegacyRpc(request) {
654
- const { method, payload } = request;
655
- if (method.includes("/")) {
656
- const split = method.indexOf("/");
657
- const namespace = method.slice(0, split);
658
- const remoteMethod = method.slice(split + 1);
659
- if (!namespace || !remoteMethod || remoteMethod.includes("/")) {
660
- throw new Error(`unsupported Remote endpoint: ${method}`);
661
- }
662
- const supplied = recordOf(payload.args);
663
- if (namespace === "commands" && remoteMethod === "execute") {
664
- const args = supplied ?? {};
665
- const line = typeof args.line === "string" ? args.line.trim() : "";
666
- if (/^\/permission(?:\s|$)/.test(line)) {
667
- const match = /^\/permission(?:[ \t]+([a-z][a-z0-9-]*))?[ \t]*$/.exec(line);
668
- if (!match || typeof args.agentId !== "string" || !args.agentId.trim() || ["images", "submittedAttachments"].some((key) => args[key] !== void 0 && (!Array.isArray(args[key]) || args[key].length))) {
669
- throw Object.assign(new Error("\u6743\u9650\u547D\u4EE4\u683C\u5F0F\u65E0\u6548\uFF1B\u672A\u66F4\u6539\u6743\u9650"), { code: "adapter/invalid-permission-command" });
670
- }
671
- return { kind: "permission-command", sessionId: args.agentId, line, nativeReceipt: true, ...match[1] ? { preset: match[1] } : {} };
672
- }
673
- const images = Array.isArray(args.images) ? args.images : [];
674
- return { kind: "invoke", namespace, method: remoteMethod, args: {
675
- ...args,
676
- images,
677
- submittedAttachments: args.submittedAttachments ?? images.map((image) => ({ ...recordOf(image), type: "image" }))
678
- } };
679
- }
680
- return { kind: "invoke", namespace, method: remoteMethod, args: supplied ?? {} };
681
- }
682
- if (method === "host.describe") return { kind: "host-describe" };
683
- if (method === "workspace.list") return { kind: "workspace-list" };
684
- if (method === "session.history") {
685
- return { kind: "session-history", request: payload };
686
- }
687
- if (method === "session.list") {
688
- return {
689
- kind: "invoke",
690
- namespace: "session",
691
- method: "list",
692
- args: { _request: payload }
693
- };
694
- }
695
- if (method === "session.models") {
696
- return { kind: "session-models", request: payload };
697
- }
698
- if (method === "session.prompt") {
699
- const parts = Array.isArray(payload.content) ? payload.content : [];
700
- const first = recordOf(parts[0]);
701
- const text = first?.type === "text" && typeof first.text === "string" ? first.text.trim() : "";
702
- if (/^\/permission(?:\s|$)/.test(text)) {
703
- const match = /^\/permission(?:[ \t]+([a-z][a-z0-9-]*))?[ \t]*$/.exec(text);
704
- if (!match || parts.length !== 1 || typeof payload.sessionId !== "string" || !payload.sessionId.trim()) {
705
- throw Object.assign(new Error("\u6743\u9650\u547D\u4EE4\u683C\u5F0F\u65E0\u6548\uFF1B\u672A\u53D1\u9001\u804A\u5929\u6D88\u606F\uFF0C\u4E5F\u672A\u66F4\u6539\u6743\u9650"), { code: "adapter/invalid-permission-command" });
706
- }
707
- return {
708
- kind: "permission-command",
709
- sessionId: payload.sessionId,
710
- line: text,
711
- ...match[1] ? { preset: match[1] } : {}
712
- };
713
- }
714
- return requestInvocation(method, {
715
- ...payload,
716
- requestId: typeof payload.requestId === "string" && payload.requestId ? payload.requestId : request.rpcId
717
- });
718
- }
719
- if (SINGLE_REQUEST_METHODS.has(method)) return requestInvocation(method, payload);
720
- if (method === "host.openPath") {
721
- return {
722
- kind: "invoke",
723
- namespace: "session",
724
- method: "openWorkspacePath",
725
- args: { request: payload }
726
- };
727
- }
728
- if (method === "agentPreset.list") {
729
- return { kind: "invoke", namespace: "agentPresets", method: "list", args: {} };
730
- }
731
- if (method === "agentPreset.select") {
732
- return {
733
- kind: "invoke",
734
- namespace: "agentPresets",
735
- method: "select",
736
- args: { agentId: payload.sessionId, agentPreset: payload.agentPreset },
737
- transform: (value) => ({ agentPreset: value })
738
- };
739
- }
740
- if (method === "settings.update") {
741
- return {
742
- kind: "invoke",
743
- namespace: "settings",
744
- method: "update",
745
- args: {
746
- ns: payload.ns,
747
- patch: payload.patch,
748
- ...present(payload, "expectedRevision")
749
- }
750
- };
751
- }
752
- if (method === "llm.providers") {
753
- return {
754
- kind: "invoke",
755
- namespace: "llm",
756
- method: "listConfigurableProviders",
757
- args: {},
758
- transform: (value) => ({ providers: Array.isArray(value) ? value : [] })
759
- };
760
- }
761
- if (method === "llm.models") {
762
- return {
763
- kind: "invoke",
764
- namespace: "session",
765
- method: "modelCatalog",
766
- args: {}
767
- };
768
- }
769
- if (method === "subagent.list") {
770
- return {
771
- kind: "invoke",
772
- namespace: "subagents",
773
- method: "list",
774
- args: { parentSessionId: payload.parentSessionId }
775
- };
776
- }
777
- if (method === "subagent.interrupt") {
778
- return {
779
- kind: "invoke",
780
- namespace: "subagents",
781
- method: "interruptByParent",
782
- args: {
783
- childSessionId: payload.childSessionId,
784
- parentSessionId: payload.parentSessionId,
785
- mode: payload.mode
786
- }
787
- };
788
- }
789
- if (method.startsWith("goal.")) {
790
- const remoteMethod = method.slice("goal.".length);
791
- const args = { agentId: payload.sessionId };
792
- const mutation = {
793
- ...present(payload, "objective"),
794
- ...present(payload, "maxGoalRounds")
795
- };
796
- if (remoteMethod === "create") args.request = mutation;
797
- else {
798
- args.ref = payload.ref;
799
- if (remoteMethod === "edit") args.request = mutation;
800
- }
801
- return {
802
- kind: "invoke",
803
- namespace: "goals",
804
- method: remoteMethod,
805
- args,
806
- transform: (value) => {
807
- if (remoteMethod === "create") return value;
808
- if (remoteMethod === "clear") return { cleared: true };
809
- const goal = recordOf(value);
810
- return { ref: { id: goal?.id, revision: goal?.revision } };
811
- }
812
- };
813
- }
814
- throw new Error(`unsupported legacy DSH RPC: ${method}`);
815
- }
816
- function resolveTypertGateway(ctx) {
817
- const candidate = ctx.get("typertGateway");
818
- if (!candidate || typeof candidate.invoke !== "function" || typeof candidate.stream !== "function") return null;
819
- return {
820
- wireStream: candidate.wireStream,
821
- invoke: (request) => candidate.invoke(request),
822
- stream: (request) => candidate.stream(request),
823
- commandAttachmentField: () => {
824
- const registry = ctx.get("typert");
825
- const descriptor = registry?.local?.get("commands/execute");
826
- if (descriptor) {
827
- const fields = descriptor.parameters?.map((parameter) => parameter.wire) || [];
828
- if (fields.includes("submittedAttachments") && !fields.includes("images")) return "submittedAttachments";
829
- if (fields.includes("images") && !fields.includes("submittedAttachments")) return "images";
830
- throw new Error("DSH \u547D\u4EE4\u9644\u4EF6\u53C2\u6570\u5DF2\u53D8\u66F4\uFF1B\u672A\u6267\u884C\u547D\u4EE4");
831
- }
832
- if (registry?.local?.hasSeen?.("commands/execute")) throw new Error("DSH \u547D\u4EE4\u63A5\u53E3\u6B63\u5728\u66F4\u65B0\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5");
833
- return ctx.get("fileUploads") ? "submittedAttachments" : "images";
834
- }
835
- };
836
- }
837
- function commandArguments(gateway, args) {
838
- const field = gateway.commandAttachmentField?.() || "images";
839
- const { images, submittedAttachments, ...rest } = args;
840
- const value = field === "submittedAttachments" ? submittedAttachments ?? (Array.isArray(images) ? images.map((image) => ({ ...recordOf(image), type: "image" })) : []) : images ?? [];
841
- if (field === "images" && Array.isArray(submittedAttachments) && submittedAttachments.some((part) => recordOf(part)?.type !== "image")) throw new Error("\u6B64\u7248\u672C DSH \u547D\u4EE4\u4E0D\u652F\u6301\u6587\u4EF6\u9644\u4EF6");
842
- return { ...rest, [field]: value };
843
- }
844
- function errorResult(error) {
845
- const value = recordOf(error);
846
- const code = typeof value?.code === "string" && value.code ? value.code : "adapter/internal";
847
- const message = error instanceof Error && error.message ? error.message : typeof value?.message === "string" && value.message ? value.message : String(error);
848
- return {
849
- ok: false,
850
- error: {
851
- code,
852
- message,
853
- ...value && Object.hasOwn(value, "details") ? { details: value.details } : {}
854
- }
855
- };
856
- }
857
- function historyEvents(records) {
858
- if (!Array.isArray(records)) return [];
859
- return records.flatMap((record2) => {
860
- const row = recordOf(record2);
861
- const event = recordOf(row?.event);
862
- if (row?.type === "event" && event) return [{ event }];
863
- if (row?.type !== "chunks" || !event) return [];
864
- return unpackChunkRow(event).map((item) => ({ event: item }));
865
- });
866
- }
867
- function unpackChunkRow(event) {
868
- const data = recordOf(event.data);
869
- if (!data || !Number.isSafeInteger(event.seq) || !Number.isSafeInteger(event.time)) return [];
870
- const type = event.type;
871
- const members = type === "chunkrow/tool-call-chunks" ? data.args : data.texts;
872
- if (!Array.isArray(members) || members.length === 0 || members.some((item) => typeof item !== "string")) return [];
873
- const gaps = Array.isArray(data.dt) ? data.dt : [];
874
- if (gaps.length !== members.length - 1 || gaps.some((gap) => !Number.isSafeInteger(gap))) return [];
875
- let time = Number(event.time);
876
- return members.map((member, index) => {
877
- if (index > 0) time += Number(gaps[index - 1]);
878
- let chunk;
879
- if (type === "chunkrow/text-chunks") {
880
- chunk = { type: "text-delta", index: data.index, text: member };
881
- } else if (type === "chunkrow/reasoning-chunks") {
882
- chunk = { type: "reasoning-delta", index: data.index, text: member };
883
- } else if (type === "chunkrow/tool-call-chunks") {
884
- chunk = {
885
- type: "tool-call-delta",
886
- index: data.index,
887
- id: data.id,
888
- ...present(data, "name"),
889
- argumentsDelta: member
890
- };
891
- } else return {};
892
- return {
893
- type: "assistant/chunk",
894
- seq: Number(event.seq) + index,
895
- time,
896
- data: { turn: data.turn, step: data.step, chunk }
897
- };
898
- }).filter((item) => typeof item.type === "string");
899
- }
900
- async function firstStreamFrame(gateway, namespace, method, args, signal) {
901
- const iterable = await gateway.stream({ namespace, method, args, signal });
902
- const iterator = iterable[Symbol.asyncIterator]();
903
- try {
904
- const first = await iterator.next();
905
- if (first.done) throw new Error(`${namespace}/${method} ended before its baseline`);
906
- return first.value;
907
- } finally {
908
- await iterator.return?.();
909
- }
910
- }
911
- async function historyValue(gateway, request, signal) {
912
- const sessionId = typeof request.sessionId === "string" ? request.sessionId : "";
913
- if (!sessionId) throw new Error("session.history requires sessionId");
914
- const maxMessages = Number.isSafeInteger(request.maxMessages) ? Math.max(1, Math.min(30, Number(request.maxMessages))) : 8;
915
- const address = await resolveDshSessionAddress(gateway, sessionId, signal);
916
- const first = recordOf(await firstStreamFrame(
917
- gateway,
918
- "session",
919
- "follow",
920
- { request: { address, maxMessages } },
921
- signal
922
- ));
923
- if (first?.type !== "snapshot" || !Number.isSafeInteger(first.cursor)) {
924
- throw new Error("session/follow returned an invalid opening snapshot");
925
- }
926
- if (Number.isSafeInteger(request.beforeSeq)) {
927
- const page = recordOf(await gateway.invoke({
928
- namespace: "session",
929
- method: "page",
930
- args: {
931
- request: {
932
- address,
933
- throughSeq: first.cursor,
934
- beforeSeq: request.beforeSeq,
935
- maxMessages
936
- }
937
- },
938
- signal
939
- })) ?? {};
940
- return {
941
- events: historyEvents(page.records),
942
- hasMore: page.hasMore === true
943
- };
944
- }
945
- return {
946
- events: historyEvents(first.records),
947
- hasMore: first.hasMore === true,
948
- projections: withPresentationProjections(first.projections),
949
- historyEndSeq: first.cursor
950
- };
951
- }
952
- async function workspaceValue(gateway, signal) {
953
- const frame = recordOf(await firstStreamFrame(
954
- gateway,
955
- "workspace",
956
- "follow",
957
- {},
958
- signal
959
- ));
960
- if (frame?.type !== "baseline" || !recordOf(frame.value)) {
961
- throw new Error("workspace/follow returned an invalid baseline");
962
- }
963
- return frame.value;
964
- }
965
- async function permissionCommandValue(gateway, plan, signal, readHistory, flushPermission) {
966
- signal.throwIfAborted();
967
- const command = recordOf(await gateway.invoke({
968
- namespace: "commands",
969
- method: "execute",
970
- args: commandArguments(gateway, { agentId: plan.sessionId, line: plan.line, images: [], submittedAttachments: [] }),
971
- signal
972
- }));
973
- const result = recordOf(command?.result);
974
- if (result?.kind !== "success") {
975
- throw Object.assign(new Error(typeof result?.text === "string" && result.text ? result.text : "DSH \u672A\u6210\u529F\u6267\u884C\u6743\u9650\u547D\u4EE4\uFF1B\u8BF7\u68C0\u67E5\u4E3B\u673A\u6743\u9650\u9884\u8BBE\u670D\u52A1"), { code: "adapter/command-failed" });
976
- }
977
- await flushPermission?.(plan.sessionId);
978
- signal.throwIfAborted();
979
- const history = recordOf(await readHistory(plan.sessionId, signal));
980
- const permissions = recordOf(recordOf(recordOf(history?.projections)?.values)?.permissions);
981
- const current = permissions?.currentValue;
982
- if (typeof current !== "string" || plan.preset && current !== plan.preset) {
983
- throw Object.assign(new Error("\u6743\u9650\u547D\u4EE4\u5DF2\u6267\u884C\uFF0C\u4F46\u672A\u786E\u8BA4\u76EE\u6807\u6743\u9650\uFF1B\u8BF7\u5237\u65B0\u4F1A\u8BDD\u6838\u5BF9\u5F53\u524D\u6743\u9650"), {
984
- code: "adapter/permission-not-applied"
985
- });
986
- }
987
- return plan.nativeReceipt ? command : { accepted: true, command: true, permission: current, commandId: command?.commandId };
988
- }
989
- async function sessionModelsValue(gateway, request, signal) {
990
- const sessionId = typeof request.sessionId === "string" ? request.sessionId : "";
991
- if (!sessionId) throw new Error("session.models requires sessionId");
992
- const address = await resolveDshSessionAddress(gateway, sessionId, signal);
993
- const [rawCatalog, rawSnapshot] = await Promise.all([
994
- gateway.invoke({ namespace: "session", method: "modelCatalog", args: {}, signal }),
995
- firstStreamFrame(gateway, "session", "follow", {
996
- request: { address, maxMessages: 1 }
997
- }, signal)
998
- ]);
999
- const catalog = recordOf(rawCatalog);
1000
- const snapshot = recordOf(rawSnapshot);
1001
- const projections = recordOf(snapshot?.projections);
1002
- const selection = recordOf(recordOf(projections?.values)?.modelSelection);
1003
- if (!catalog || snapshot?.type !== "snapshot" || !projections) {
1004
- throw new Error("DSH returned an invalid model catalog or Session snapshot");
1005
- }
1006
- const current = selection?.next ?? selection?.lastUsed ?? catalog.default;
1007
- if (!recordOf(current)) throw new Error("DSH returned no usable model selection");
1008
- return { ...catalog, current };
1009
- }
1010
- async function invokeLegacyRpc(gateway, request, options) {
1011
- let result;
1012
- try {
1013
- const plan = planLegacyRpc(request);
1014
- let value;
1015
- if (plan.kind === "host-describe") value = options.describeHost();
1016
- else if (plan.kind === "workspace-list") value = await workspaceValue(gateway, options.signal);
1017
- else if (plan.kind === "session-history") value = await historyValue(gateway, plan.request, options.signal);
1018
- else if (plan.kind === "session-models") value = await sessionModelsValue(gateway, plan.request, options.signal);
1019
- else if (plan.kind === "permission-command") value = await permissionCommandValue(
1020
- gateway,
1021
- plan,
1022
- options.signal,
1023
- (sessionId, signal) => historyValue(gateway, { sessionId, maxMessages: 1 }, signal),
1024
- options.flushPermission
1025
- );
1026
- else {
1027
- for (let attempt = 0; ; attempt++) {
1028
- try {
1029
- options.signal.throwIfAborted();
1030
- value = await gateway.invoke({
1031
- namespace: plan.namespace,
1032
- method: plan.method,
1033
- args: plan.namespace === "commands" && plan.method === "execute" ? commandArguments(gateway, plan.args) : plan.args,
1034
- signal: options.signal
1035
- });
1036
- break;
1037
- } catch (error) {
1038
- const failure = recordOf(error);
1039
- if (request.method !== "session.list" || attempt >= 2 || failure?.code !== "SESSION_QUERY_PERSISTENCE_FAILED" || typeof failure.message !== "string" || !/ENOENT.*scandir.*[\\/]\.dsh-mkdir-[^\\/]+$/.test(failure.message)) throw error;
1040
- await delay(25 * (attempt + 1), void 0, { signal: options.signal });
1041
- }
1042
- }
1043
- if (plan.transform) value = plan.transform(value);
1044
- }
1045
- result = { ok: true, value };
1046
- } catch (error) {
1047
- result = errorResult(error);
1048
- }
1049
- return { type: "server-response", rpcId: request.rpcId, result };
1050
- }
1051
-
1052
- // src/install-lifecycle.ts
1053
- import fs from "node:fs";
1054
- import path4 from "node:path";
1055
- import { execFileSync as execFileSync2, spawn } from "node:child_process";
1056
- var serviceName = (s) => /^[A-Za-z0-9_.@-]{1,180}$/.test(s);
1057
- function run(command, args) {
1058
- return execFileSync2(command, args, { encoding: "utf8", windowsHide: true, timeout: 15e3, maxBuffer: 1024 * 1024, stdio: ["ignore", "pipe", "pipe"] }).trim();
1059
- }
1060
- function validateManager(value) {
1061
- if (value.kind === "process") return;
1062
- if (value.kind === "systemd" && process.platform === "linux" && serviceName(value.unit) && value.unit.endsWith(".service")) return;
1063
- if (value.kind === "launchd" && process.platform === "darwin" && serviceName(value.label) && /^gui\/\d+$/.test(value.domain) && value.domain === `gui/${process.getuid?.()}` && path4.isAbsolute(value.plist) && fs.statSync(value.plist).isFile()) return;
1064
- throw new Error("\u65E0\u6CD5\u786E\u8BA4\u539F\u540E\u53F0\u670D\u52A1\uFF0C\u672A\u505C\u6B62 DSH\u3002");
1065
- }
1066
- function currentHostManager() {
1067
- if (process.env.PM2_HOME || process.env.NODE_APP_INSTANCE || process.env.KUBERNETES_SERVICE_HOST || process.env.container) throw new Error("\u6B64\u540E\u53F0\u7BA1\u7406\u65B9\u5F0F\u5C1A\u4E0D\u652F\u6301\u81EA\u52A8\u91CD\u542F\u3002");
1068
- if (process.platform === "linux") {
1069
- const group = fs.readFileSync("/proc/self/cgroup", "utf8");
1070
- const units = group.split(/[\n/]/).filter((s) => serviceName(s) && s.endsWith(".service"));
1071
- const unit = units.at(-1);
1072
- if (unit && !/^user@\d+\.service$/.test(unit)) {
1073
- const pid = Number(run("systemctl", ["--user", "show", unit, "--property=MainPID", "--value"]));
1074
- if (pid !== process.pid) throw new Error("DSH \u4E0D\u662F\u8BE5\u670D\u52A1\u7684\u72EC\u7ACB\u4E3B\u8FDB\u7A0B\uFF0C\u672A\u505C\u6B62\u540E\u53F0\u670D\u52A1\u3002");
1075
- return { kind: "systemd", unit };
1076
- }
1077
- if (process.env.INVOCATION_ID) throw new Error("\u65E0\u6CD5\u786E\u8BA4 systemd \u670D\u52A1\u5F52\u5C5E\uFF0C\u672A\u505C\u6B62 DSH\u3002");
1078
- }
1079
- if (process.platform === "darwin") {
1080
- const rows = run("/bin/launchctl", ["list"]).split("\n");
1081
- const row = rows.map((s) => s.trim().split(/\s+/)).find((parts) => parts[0] === String(process.pid));
1082
- if (row) {
1083
- const label = row[2], domain = `gui/${process.getuid?.()}`;
1084
- if (!serviceName(label)) throw new Error("\u540E\u53F0\u4EFB\u52A1\u540D\u79F0\u65E0\u6548\u3002");
1085
- const printed = run("/bin/launchctl", ["print", `${domain}/${label}`]);
1086
- const plist = /^\s*path = (.+\.plist)\s*$/m.exec(printed)?.[1];
1087
- if (!plist) throw new Error("\u540E\u53F0\u4EFB\u52A1\u7F3A\u5C11\u53EF\u6062\u590D\u7684\u542F\u52A8\u914D\u7F6E\u3002");
1088
- const result = { kind: "launchd", label, domain, plist };
1089
- validateManager(result);
1090
- return result;
1091
- }
1092
- if (process.env.LAUNCH_JOBKEY_LABEL) throw new Error("\u65E0\u6CD5\u786E\u8BA4 launchd \u670D\u52A1\u5F52\u5C5E\uFF0C\u672A\u505C\u6B62 DSH\u3002");
1093
- }
1094
- return { kind: "process" };
1095
- }
1096
- function startUpdateWorker(manager, directory, executable) {
1097
- validateManager(manager);
1098
- const id = path4.basename(directory);
1099
- if (!/^[a-f0-9]{32}$/.test(id)) throw new Error("\u65E0\u6548\u7684\u66F4\u65B0\u4EFB\u52A1");
1100
- const args = [path4.join(directory, "update-worker.js"), path4.join(directory, "job.json")];
1101
- if (manager.kind === "systemd") {
1102
- run("systemd-run", ["--user", "--quiet", "--collect", `--unit=dsh-wechat-update-${id}`, "--property=Type=exec", `--working-directory=${directory}`, executable, ...args]);
1103
- } else if (manager.kind === "launchd") {
1104
- run("/bin/launchctl", ["submit", "-l", `dsh.wechat.update.${id}`, "-o", path4.join(directory, "worker.log"), "-e", path4.join(directory, "worker.log"), "--", executable, ...args]);
1105
- } else {
1106
- const log = fs.openSync(path4.join(directory, "worker.log"), "a", 384);
1107
- const child = spawn(executable, args, { cwd: directory, env: process.env, windowsHide: true, detached: true, stdio: ["ignore", log, log] });
1108
- fs.closeSync(log);
1109
- child.unref();
1110
- child.on("error", () => {
1111
- });
1112
- if (!child.pid) throw new Error("\u65E0\u6CD5\u542F\u52A8\u66F4\u65B0\u8FDB\u7A0B");
1113
- }
1114
- }
1115
-
1116
- // src/install-control.ts
1117
- import { homedir as homedir4 } from "node:os";
1118
-
1119
- // src/install-capabilities.ts
1120
- function assertNativeUpdateCapabilities(context) {
1121
- const ctx = context.root || context;
1122
- const web = ctx.get("webServer");
1123
- const sessions = ctx.get("sessions");
1124
- const missing = [];
1125
- for (const name of ["on", "listeners", "removeAllListeners", "removeListener"]) {
1126
- if (typeof web?.server?.[name] !== "function") missing.push("webServer." + name);
1127
- }
1128
- for (const name of ["get", "list", "flush"]) {
1129
- if (typeof sessions?.[name] !== "function") missing.push("sessions." + name);
1130
- }
1131
- if (typeof ctx.fiber?.dispose !== "function") missing.push("lifecycle.dispose");
1132
- if (missing.length) throw new Error("\u5F53\u524D\u5BBF\u4E3B\u7F3A\u5C11\u5B89\u5168\u66F4\u65B0\u6240\u9700\u80FD\u529B\uFF0C\u672A\u505C\u6B62\u8282\u70B9\uFF1A" + missing.join(", "));
1133
- }
1134
-
1135
- // src/install-control.ts
1136
- async function quiesceNativeHost(ctx, read, disposing) {
1137
- const items = (await read("session.list")).items;
1138
- if (!Array.isArray(items) || items.some((s) => s.running !== false)) throw new Error("\u8BF7\u7B49\u5F85\u8FD0\u884C\u4E2D\u7684\u4F1A\u8BDD\u7ED3\u675F\u540E\u518D\u66F4\u65B0\u3002");
1139
- const sessions = ctx.get("sessions");
1140
- if (!sessions?.list || !sessions.flush) throw new Error("\u5F53\u524D DSH \u4E0D\u652F\u6301\u4FDD\u5B58\u68C0\u67E5\u3002");
1141
- for (const session of sessions.list()) if (!await sessions.flush(session)) throw new Error("\u4F1A\u8BDD\u4FDD\u5B58\u672A\u5B8C\u6210\u3002");
1142
- if ((await read("session.list")).items.some((s) => s.running !== false)) throw new Error("\u6709\u65B0\u4F1A\u8BDD\u5F00\u59CB\u8FD0\u884C\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002");
1143
- disposing();
1144
- await ctx.fiber.dispose();
1145
- }
1146
- async function createInstallControl(context, config) {
1147
- const ctx = context.root;
1148
- assertNativeUpdateCapabilities(ctx);
1149
- const home = adapterDshHome(), id = path5.basename(config.directory);
1150
- if (!/^[a-f0-9]{32}$/.test(id) || !/^[a-f0-9]{48}$/.test(config.token) || path5.dirname(config.directory) !== path5.join(home, "harness-remote-updates") || fs2.realpathSync(config.directory) !== config.directory) throw new Error("\u5B89\u88C5\u63A7\u5236\u8BF7\u6C42\u4E0D\u5C5E\u4E8E\u5F53\u524D DSH\u3002");
1151
- const scope = resolveAgentProfileScope("", process.argv, home), profile = path5.join(home, "profiles", scope);
1152
- const cli = fs2.realpathSync(process.argv[1]);
1153
- const manifest = JSON.parse(fs2.readFileSync(path5.resolve(cli, "../../package.json"), "utf8"));
1154
- if (manifest.name !== "@deepseek-ai/dsh" || process.execArgv.length || !process.argv.includes("web")) throw new Error("\u6B64 DSH \u542F\u52A8\u65B9\u5F0F\u5C1A\u4E0D\u652F\u6301\u81EA\u52A8\u66F4\u65B0\u3002");
1155
- const manager = currentHostManager(), webPort = resolveDshWebRuntime(ctx, process.env).port;
1156
- const ports = deriveGatePorts(scope, loadAgentDescriptor().agentInstanceId);
1157
- const nativeExit = ctx.get("appExit");
1158
- const gateway = resolveTypertGateway(ctx);
1159
- let launched = false, quiesced = false;
1160
- const read = async (method, payload = {}) => {
1161
- if (!["session.list", "session.history"].includes(method)) throw new Error("\u4E0D\u652F\u6301\u7684\u5B89\u88C5\u68C0\u67E5");
1162
- const request = { type: "client-request", rpcId: "installer-read", method, payload };
1163
- const response = gateway ? await invokeLegacyRpc(gateway, request, { signal: AbortSignal.timeout(1e4), describeHost: () => ({}) }) : await (await fetch(`http://127.0.0.1:${webPort}/api/${method}`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(request), signal: AbortSignal.timeout(1e4), redirect: "error" })).json();
1164
- if (!response.result?.ok) throw new Error("\u65E0\u6CD5\u9A8C\u8BC1 DSH \u4F1A\u8BDD\u72B6\u6001\uFF0C\u672A\u505C\u6B62\u8282\u70B9\u3002");
1165
- return response.result.value;
1166
- };
1167
- const version = () => {
1168
- try {
1169
- return JSON.parse(fs2.readFileSync(path5.join(profile, "node_modules/@harness-remote/dsh-wechat-remote/package.json"), "utf8")).version;
1170
- } catch {
1171
- return "0.0.0";
1172
- }
1173
- };
1174
- const server = http.createServer(async (req, res) => {
1175
- const json = (status, value) => {
1176
- res.writeHead(status, { "content-type": "application/json", "cache-control": "no-store" });
1177
- res.end(JSON.stringify(value));
1178
- };
1179
- if (!["127.0.0.1", "::ffff:127.0.0.1"].includes(req.socket.remoteAddress || "") || req.headers.host !== `127.0.0.1:${server.address().port}` || req.headers["x-forwarded-for"] || req.headers.authorization !== `Bearer ${config.token}` || req.method !== "POST") return json(403, {});
1180
- try {
1181
- let body = "";
1182
- for await (const chunk of req) {
1183
- body += chunk.toString();
1184
- if (body.length > 4096) throw new Error("\u8BF7\u6C42\u8FC7\u5927");
1185
- }
1186
- const input = body ? JSON.parse(body) : {};
1187
- if (req.url === "/describe") return json(200, {
1188
- pid: process.pid,
1189
- cli,
1190
- executable: process.execPath,
1191
- argv: process.argv.slice(1),
1192
- execArgv: process.execArgv,
1193
- cwd: process.cwd(),
1194
- home,
1195
- profile,
1196
- webPort,
1197
- stateFile: gateStatePathForProfile(scope, homedir4(), home),
1198
- identityFile: defaultAgentIdentityPath(),
1199
- gatePort: ports.publicPort,
1200
- localPort: ports.localPort,
1201
- manager,
1202
- dshVersion: manifest.version,
1203
- pluginVersion: version(),
1204
- platform: process.platform,
1205
- arch: process.arch,
1206
- quiesced
1207
- });
1208
- if (req.url === "/read" && !quiesced) return json(200, await read(input.method, input.payload));
1209
- if (req.url === "/launch" && !launched && !quiesced) {
1210
- const filename = path5.join(config.directory, "job.json");
1211
- const job = JSON.parse(fs2.readFileSync(filename, "utf8"));
1212
- if (job.id !== id || job.controlOrigin !== origin || job.statusToken !== config.token || job.parentPid !== process.pid || job.home !== home || job.profile !== profile || job.cli !== cli || job.pnpm !== config.pnpm) throw new Error("\u5B89\u88C5\u76EE\u6807\u53D1\u751F\u53D8\u5316");
1213
- startUpdateWorker(manager, config.directory, process.execPath);
1214
- launched = true;
1215
- return json(200, { started: true });
1216
- }
1217
- if (req.url === "/quiesce" && launched && !quiesced) {
1218
- await quiesceNativeHost(ctx, read, () => {
1219
- quiesced = true;
1220
- });
1221
- return json(200, { quiesced: true, pid: process.pid });
1222
- }
1223
- if (req.url === "/shutdown" && quiesced) {
1224
- json(200, { stopping: true });
1225
- setTimeout(() => {
1226
- close();
1227
- if (nativeExit) nativeExit(0);
1228
- else process.exit(0);
1229
- }, 50);
1230
- return;
1231
- }
1232
- if (req.url === "/close" && !quiesced) {
1233
- json(200, { closed: true });
1234
- close();
1235
- return;
1236
- }
1237
- json(409, { error: "\u5B89\u88C5\u72B6\u6001\u4E0D\u5141\u8BB8\u6B64\u64CD\u4F5C" });
1238
- } catch (error) {
1239
- json(409, { error: error instanceof Error ? error.message : "\u5B89\u88C5\u63A7\u5236\u5931\u8D25" });
1240
- }
1241
- });
1242
- await new Promise((resolve, reject) => {
1243
- server.once("error", reject);
1244
- server.listen(0, "127.0.0.1", resolve);
1245
- });
1246
- const origin = `http://127.0.0.1:${server.address().port}`;
1247
- const timer = setTimeout(() => {
1248
- if (!launched) close();
1249
- }, 12e4);
1250
- timer.unref();
1251
- function close() {
1252
- clearTimeout(timer);
1253
- server.closeAllConnections();
1254
- server.close();
1255
- }
1256
- writePrivateJsonAtomic(path5.join(config.directory, "control-ready.json"), { origin, pid: process.pid });
1257
- return { origin, close };
1258
- }
1259
- var apply = async (ctx, config) => {
1260
- await createInstallControl(ctx, config);
1261
- };
1262
- var inject = ["webServer", "sessions", "appExit"];
1263
- export {
1264
- apply,
1265
- createInstallControl,
1266
- inject,
1267
- quiesceNativeHost
1268
- };
1
+ /* Generated from the shared plugin installation sources. */
2
+
3
+ // src/install-control.ts
4
+ import fs2 from "node:fs";
5
+ import path5 from "node:path";
6
+ import http from "node:http";
7
+
8
+ // src/dsh-runtime.ts
9
+ import { homedir } from "node:os";
10
+ import path from "node:path";
11
+ function adapterDshHome(environment = process.env, userHome = homedir()) {
12
+ const configured = environment.DSH_HOME;
13
+ const selected = configured && configured.trim() ? configured : path.join(userHome, ".dsh");
14
+ const expanded = selected === "~" ? userHome : /^~[\\/]/.test(selected) ? path.join(userHome, selected.slice(2)) : selected;
15
+ return path.resolve(expanded);
16
+ }
17
+ function validPort(value) {
18
+ const parsed = typeof value === "number" ? value : typeof value === "string" && /^\d{1,5}$/.test(value) ? Number(value) : Number.NaN;
19
+ return Number.isSafeInteger(parsed) && parsed >= 1 && parsed <= 65535 ? parsed : null;
20
+ }
21
+ function resolveDshWebRuntime(ctx, environment = process.env) {
22
+ const webServer = ctx.get("webServer");
23
+ const servicePort = validPort(webServer?.port);
24
+ if (servicePort !== null) return { port: servicePort, source: "web-server" };
25
+ const environmentPort = validPort(environment.DSH_PORT);
26
+ if (environmentPort !== null) return { port: environmentPort, source: "environment" };
27
+ return { port: 3080, source: "legacy-default" };
28
+ }
29
+
30
+ // src/agent-metadata.ts
31
+ import { createHash, randomBytes as randomBytes2 } from "node:crypto";
32
+ import { existsSync as existsSync3, readFileSync as readFileSync2, realpathSync } from "node:fs";
33
+ import { homedir as homedir3, hostname } from "node:os";
34
+ import path3 from "node:path";
35
+ import { fileURLToPath } from "node:url";
36
+
37
+ // src/secure-file.ts
38
+ import { execFileSync } from "node:child_process";
39
+ import {
40
+ chmodSync,
41
+ closeSync,
42
+ existsSync,
43
+ fsyncSync,
44
+ mkdirSync,
45
+ openSync,
46
+ readFileSync,
47
+ renameSync,
48
+ rmSync,
49
+ writeFileSync
50
+ } from "node:fs";
51
+ import { dirname } from "node:path";
52
+ import { userInfo } from "node:os";
53
+ import { randomBytes } from "node:crypto";
54
+ function tightenPrivateFile(file) {
55
+ try {
56
+ chmodSync(file, 384);
57
+ } catch {
58
+ }
59
+ if (process.platform !== "win32") return;
60
+ try {
61
+ execFileSync("icacls", [file, "/inheritance:r", "/grant:r", `${userInfo().username}:F`], {
62
+ timeout: 5e3,
63
+ windowsHide: true,
64
+ stdio: "ignore"
65
+ });
66
+ } catch {
67
+ }
68
+ }
69
+ function writePrivateJsonAtomic(file, value) {
70
+ const parent = dirname(file);
71
+ mkdirSync(parent, { recursive: true, mode: 448 });
72
+ const temporary = `${file}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
73
+ try {
74
+ writeFileSync(temporary, `${JSON.stringify(value, null, 2)}
75
+ `, {
76
+ encoding: "utf8",
77
+ mode: 384,
78
+ flag: "wx"
79
+ });
80
+ const fd = openSync(temporary, "r+");
81
+ try {
82
+ try {
83
+ fsyncSync(fd);
84
+ } catch {
85
+ }
86
+ } finally {
87
+ closeSync(fd);
88
+ }
89
+ tightenPrivateFile(temporary);
90
+ renameSync(temporary, file);
91
+ tightenPrivateFile(file);
92
+ } finally {
93
+ if (existsSync(temporary)) {
94
+ try {
95
+ rmSync(temporary, { force: true });
96
+ } catch {
97
+ }
98
+ }
99
+ }
100
+ }
101
+ function readPrivateJson(file) {
102
+ const value = JSON.parse(readFileSync(file, "utf8"));
103
+ tightenPrivateFile(file);
104
+ return value;
105
+ }
106
+
107
+ // src/host-platform.ts
108
+ import { execFile } from "node:child_process";
109
+ import { existsSync as existsSync2 } from "node:fs";
110
+ import { homedir as homedir2, networkInterfaces, platform } from "node:os";
111
+ import path2 from "node:path";
112
+ import { promisify } from "node:util";
113
+ var execFileAsync = promisify(execFile);
114
+ var BasePlatformAdapter = class {
115
+ async isPotentiallyBlockingPath(_target, signal) {
116
+ signal.throwIfAborted();
117
+ return false;
118
+ }
119
+ lanIPv4() {
120
+ return selectLanIPv4();
121
+ }
122
+ };
123
+ var WindowsPlatformAdapter = class extends BasePlatformAdapter {
124
+ descriptor = Object.freeze({
125
+ kind: "windows",
126
+ name: "Windows",
127
+ pathStyle: "windows",
128
+ directoryRootStyle: "drives"
129
+ });
130
+ rootsPromise;
131
+ rootsByPath = /* @__PURE__ */ new Map();
132
+ async filesystemRoots(signal) {
133
+ if (!this.rootsPromise) {
134
+ const workerSignal = new AbortController().signal;
135
+ this.rootsPromise = enumerateWindowsRoots(workerSignal).then((roots) => {
136
+ this.rootsByPath.clear();
137
+ for (const root of roots) this.rootsByPath.set(root.path.toUpperCase(), root);
138
+ return roots;
139
+ }).catch((error) => {
140
+ this.rootsPromise = void 0;
141
+ throw error;
142
+ });
143
+ }
144
+ return await raceSignal(this.rootsPromise, signal);
145
+ }
146
+ async isPotentiallyBlockingPath(target, signal) {
147
+ signal.throwIfAborted();
148
+ if (target.startsWith("\\\\")) return true;
149
+ const match = /^([A-Za-z]):[\\/]/.exec(target);
150
+ if (!match) return false;
151
+ const rootPath = `${match[1].toUpperCase()}:\\`;
152
+ let root = this.rootsByPath.get(rootPath);
153
+ if (!root) {
154
+ try {
155
+ await this.filesystemRoots(signal);
156
+ } catch (error) {
157
+ signal.throwIfAborted();
158
+ return true;
159
+ }
160
+ root = this.rootsByPath.get(rootPath);
161
+ }
162
+ return root?.kind === "network";
163
+ }
164
+ };
165
+ var MacPlatformAdapter = class extends BasePlatformAdapter {
166
+ descriptor = Object.freeze({
167
+ kind: "macos",
168
+ name: "macOS",
169
+ pathStyle: "posix",
170
+ directoryRootStyle: "filesystem"
171
+ });
172
+ async filesystemRoots(signal) {
173
+ signal.throwIfAborted();
174
+ const roots = [
175
+ { name: "\u4E3B\u76EE\u5F55", path: homedir2(), kind: "home" },
176
+ { name: "\u6839\u76EE\u5F55", path: "/", kind: "filesystem" }
177
+ ];
178
+ if (existsSync2("/Volumes")) roots.push({ name: "\u5377", path: "/Volumes", kind: "volume" });
179
+ return roots;
180
+ }
181
+ async isPotentiallyBlockingPath(target, signal) {
182
+ signal.throwIfAborted();
183
+ const normalized = path2.resolve(target);
184
+ return normalized.startsWith("/Volumes/");
185
+ }
186
+ };
187
+ var LinuxPlatformAdapter = class extends BasePlatformAdapter {
188
+ descriptor = Object.freeze({
189
+ kind: "linux",
190
+ name: "Linux",
191
+ pathStyle: "posix",
192
+ directoryRootStyle: "filesystem"
193
+ });
194
+ async filesystemRoots(signal) {
195
+ signal.throwIfAborted();
196
+ const roots = [
197
+ { name: "\u4E3B\u76EE\u5F55", path: homedir2(), kind: "home" },
198
+ { name: "\u6839\u76EE\u5F55", path: "/", kind: "filesystem" }
199
+ ];
200
+ for (const mountRoot of ["/mnt", "/media"]) {
201
+ if (existsSync2(mountRoot)) roots.push({ name: path2.basename(mountRoot), path: mountRoot, kind: "volume" });
202
+ }
203
+ return roots;
204
+ }
205
+ async isPotentiallyBlockingPath(target, signal) {
206
+ signal.throwIfAborted();
207
+ const normalized = path2.resolve(target);
208
+ return normalized.startsWith("/mnt/") || normalized.startsWith("/media/") || /\/run\/user\/\d+\/gvfs(?:\/|$)/.test(normalized);
209
+ }
210
+ };
211
+ var UnknownPlatformAdapter = class extends BasePlatformAdapter {
212
+ descriptor = Object.freeze({
213
+ kind: "unknown",
214
+ name: "Unknown",
215
+ pathStyle: path2.sep === "\\" ? "windows" : "posix",
216
+ directoryRootStyle: path2.sep === "\\" ? "drives" : "filesystem"
217
+ });
218
+ async filesystemRoots(signal) {
219
+ signal.throwIfAborted();
220
+ return [{ name: "\u4E3B\u76EE\u5F55", path: homedir2(), kind: "home" }];
221
+ }
222
+ };
223
+ function buildAdapter() {
224
+ switch (platform()) {
225
+ case "win32":
226
+ return new WindowsPlatformAdapter();
227
+ case "darwin":
228
+ return new MacPlatformAdapter();
229
+ case "linux":
230
+ return new LinuxPlatformAdapter();
231
+ default:
232
+ return new UnknownPlatformAdapter();
233
+ }
234
+ }
235
+ var hostPlatform = buildAdapter();
236
+ function hostPlatformDescriptor() {
237
+ return hostPlatform.descriptor;
238
+ }
239
+ function selectLanIPv4(source = networkInterfaces()) {
240
+ const candidates = [];
241
+ for (const [name, addresses] of Object.entries(source)) {
242
+ for (const item of addresses || []) {
243
+ if (item.family !== "IPv4" || item.internal || !item.address) continue;
244
+ candidates.push({ name, address: item.address });
245
+ }
246
+ }
247
+ candidates.sort((left, right) => scoreAddress(right) - scoreAddress(left));
248
+ return candidates[0]?.address || "127.0.0.1";
249
+ }
250
+ function scoreAddress(candidate) {
251
+ const address = candidate.address;
252
+ const name = candidate.name.toLowerCase();
253
+ let score = 0;
254
+ if (address.startsWith("192.168.")) score += 100;
255
+ else if (address.startsWith("10.")) score += 90;
256
+ else if (isPrivate172(address)) score += 80;
257
+ else score += 10;
258
+ if (/^(en0|en1|wi-?fi|wlan\d*|ethernet|以太网)/i.test(name)) score += 30;
259
+ if (/(docker|veth|vmnet|virtualbox|hyper-v|wsl|loopback|utun|bridge|tunnel|vpn)/i.test(name)) score -= 80;
260
+ if (address.startsWith("169.254.") || isBenchmarkAddress(address) || isCarrierGradeNat(address)) score -= 200;
261
+ return score;
262
+ }
263
+ function isPrivate172(address) {
264
+ const match = /^172\.(\d+)\./.exec(address);
265
+ if (!match) return false;
266
+ const second = Number(match[1]);
267
+ return second >= 16 && second <= 31;
268
+ }
269
+ function isBenchmarkAddress(address) {
270
+ const match = /^198\.(\d+)\./.exec(address);
271
+ if (!match) return false;
272
+ const second = Number(match[1]);
273
+ return second === 18 || second === 19;
274
+ }
275
+ function isCarrierGradeNat(address) {
276
+ const match = /^100\.(\d+)\./.exec(address);
277
+ if (!match) return false;
278
+ const second = Number(match[1]);
279
+ return second >= 64 && second <= 127;
280
+ }
281
+ async function enumerateWindowsRoots(signal) {
282
+ const script = [
283
+ "[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)",
284
+ "Get-PSDrive -PSProvider FileSystem | Where-Object { $_.Name -match '^[A-Za-z]$' -and $_.Root -match '^[A-Za-z]:\\\\$' } | ForEach-Object {",
285
+ " [pscustomobject]@{ name = $_.Name.ToUpperInvariant(); path = $_.Root; displayRoot = $(if ($_.DisplayRoot) { [string]$_.DisplayRoot } else { $null }) }",
286
+ "} | Sort-Object name | ConvertTo-Json -Compress"
287
+ ].join("; ");
288
+ const { stdout } = await execFileAsync(
289
+ "powershell.exe",
290
+ ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", script],
291
+ { encoding: "utf8", windowsHide: true, timeout: 8e3, signal }
292
+ );
293
+ signal.throwIfAborted();
294
+ const text = String(stdout).replace(/^\uFEFF/, "").trim();
295
+ if (!text) return [];
296
+ const decoded = JSON.parse(text);
297
+ const rows = Array.isArray(decoded) ? decoded : [decoded];
298
+ const unique = /* @__PURE__ */ new Map();
299
+ for (const row of rows) {
300
+ if (typeof row.name !== "string" || typeof row.path !== "string") continue;
301
+ const name = row.name.toUpperCase();
302
+ if (!/^[A-Z]$/.test(name) || !/^[A-Za-z]:[\\/]$/.test(row.path)) continue;
303
+ const rootPath = `${name}:\\`;
304
+ const displayRoot = typeof row.displayRoot === "string" && row.displayRoot.trim() ? row.displayRoot : void 0;
305
+ unique.set(rootPath, {
306
+ name: `${name}:`,
307
+ path: rootPath,
308
+ kind: displayRoot ? "network" : "local",
309
+ ...displayRoot ? { displayRoot } : {}
310
+ });
311
+ }
312
+ return [...unique.values()].sort((left, right) => left.path.localeCompare(right.path));
313
+ }
314
+ function raceSignal(promise, signal) {
315
+ if (signal.aborted) return Promise.reject(signal.reason);
316
+ return new Promise((resolve, reject) => {
317
+ const aborted = () => reject(signal.reason);
318
+ signal.addEventListener("abort", aborted, { once: true });
319
+ promise.then(
320
+ (value) => {
321
+ signal.removeEventListener("abort", aborted);
322
+ resolve(value);
323
+ },
324
+ (error) => {
325
+ signal.removeEventListener("abort", aborted);
326
+ reject(error);
327
+ }
328
+ );
329
+ });
330
+ }
331
+
332
+ // src/agent-metadata.ts
333
+ var ROOT = path3.join(adapterDshHome(), "harness-remote");
334
+ var HOST_PATH = path3.join(ROOT, "host.json");
335
+ var cachedDescriptor = null;
336
+ var AGENT_CAPABILITIES = Object.freeze([
337
+ Object.freeze({ id: "dsh.rpc", version: 1 }),
338
+ Object.freeze({ id: "dsh.realtime", version: 1 }),
339
+ Object.freeze({ id: "wechat.directory", version: 1 }),
340
+ Object.freeze({ id: "wechat.history-window", version: 1 }),
341
+ Object.freeze({ id: "wechat.attachment-object", version: 1 }),
342
+ Object.freeze({ id: "harness.public-relay-e2ee", version: 1 }),
343
+ Object.freeze({ id: "harness.lan-bootstrap", version: 1 }),
344
+ Object.freeze({ id: "harness.host-platform", version: 1 }),
345
+ Object.freeze({ id: "harness.oss-e2ee-objects", version: 1 }),
346
+ Object.freeze({ id: "harness.secure-lan", version: 1 })
347
+ ]);
348
+ function stableId(file) {
349
+ if (existsSync3(file)) {
350
+ const stored = readPrivateJson(file);
351
+ if (stored.version !== 1 || !/^[A-Za-z0-9_-]{20,64}$/.test(stored.id)) {
352
+ throw new Error(`Harness Remote metadata is invalid: ${path3.basename(file)}`);
353
+ }
354
+ return stored.id;
355
+ }
356
+ const id = randomBytes2(18).toString("base64url");
357
+ writePrivateJsonAtomic(file, { version: 1, id });
358
+ return id;
359
+ }
360
+ function agentProfileScope() {
361
+ return resolveAgentProfileScope(fileURLToPath(import.meta.url), process.argv, adapterDshHome());
362
+ }
363
+ function resolveAgentProfileScope(modulePath, argv, dshHome) {
364
+ const valid = (value) => Boolean(value) && value.length <= 80 && !/[\\/\u0000-\u001f]/.test(value) && value !== "." && value !== ".." && value !== "node_modules";
365
+ for (let index = 2; index < argv.length && argv[index] !== "--"; index++) {
366
+ const arg = argv[index];
367
+ const candidate = arg === "--profile" ? argv[index + 1] : arg.startsWith("--profile=") ? arg.slice("--profile=".length) : void 0;
368
+ if (valid(candidate)) return candidate;
369
+ }
370
+ const relative = path3.relative(path3.join(dshHome, "profiles"), modulePath);
371
+ const parts = relative.split(/[\\/]/);
372
+ if (valid(parts[0]) && parts[1] === "node_modules") return parts[0];
373
+ if (argv[2] === "web") return "web";
374
+ return "default";
375
+ }
376
+ function instanceStorageKey(profileScope = agentProfileScope()) {
377
+ return createHash("sha256").update(`deepseek-harness\0${profileScope}`).digest("hex").slice(0, 24);
378
+ }
379
+ function gateStatePathForProfile(profileScope, homeDirectory = homedir3(), dshHome = path3.join(homeDirectory, ".dsh")) {
380
+ const normalized = profileScope.trim().toLowerCase();
381
+ if (normalized === "web" || normalized === "default") {
382
+ return path3.join(dshHome, "gate-wechat-state.json");
383
+ }
384
+ return path3.join(
385
+ dshHome,
386
+ "harness-remote",
387
+ "instances",
388
+ instanceStorageKey(profileScope),
389
+ "gate-wechat-state.json"
390
+ );
391
+ }
392
+ function defaultAgentIdentityPath() {
393
+ const scope = agentProfileScope();
394
+ if (scope === "web" || scope === "default") {
395
+ return path3.join(adapterDshHome(), "harness-remote-public-identity.json");
396
+ }
397
+ return path3.join(ROOT, "instances", instanceStorageKey(scope), "identity.json");
398
+ }
399
+ function packageVersionFromAncestors(start) {
400
+ let current = path3.resolve(start);
401
+ for (let depth = 0; depth < 8; depth += 1) {
402
+ const manifest = path3.join(current, "package.json");
403
+ try {
404
+ const value = JSON.parse(readFileSync2(manifest, "utf8"));
405
+ if (value.name === "@deepseek-ai/dsh" && typeof value.version === "string" && value.version) {
406
+ return value.version;
407
+ }
408
+ } catch {
409
+ }
410
+ const parent = path3.dirname(current);
411
+ if (parent === current) break;
412
+ current = parent;
413
+ }
414
+ return null;
415
+ }
416
+ function installedDshVersion() {
417
+ const override = process.env.DSH_RUNTIME_VERSION;
418
+ if (override && /^[A-Za-z0-9._+-]{1,64}$/.test(override)) return override;
419
+ const argvEntry = process.argv[1];
420
+ if (argvEntry) {
421
+ let resolvedEntry = argvEntry;
422
+ try {
423
+ resolvedEntry = realpathSync(argvEntry);
424
+ } catch {
425
+ }
426
+ const found = packageVersionFromAncestors(path3.dirname(resolvedEntry));
427
+ if (found) return found;
428
+ }
429
+ for (const entry of String(process.env.PATH || "").split(path3.delimiter)) {
430
+ if (!entry) continue;
431
+ try {
432
+ const command = path3.join(entry, process.platform === "win32" ? "dsh.cmd" : "dsh");
433
+ const found = packageVersionFromAncestors(path3.dirname(realpathSync(command)));
434
+ if (found) return found;
435
+ } catch {
436
+ }
437
+ try {
438
+ const value = JSON.parse(readFileSync2(
439
+ path3.join(entry, "node_modules", "@deepseek-ai", "dsh", "package.json"),
440
+ "utf8"
441
+ ));
442
+ if (value.name === "@deepseek-ai/dsh" && typeof value.version === "string" && value.version) {
443
+ return value.version;
444
+ }
445
+ } catch {
446
+ }
447
+ }
448
+ return "unknown";
449
+ }
450
+ function loadAgentDescriptor() {
451
+ if (cachedDescriptor) return cachedDescriptor;
452
+ const instancePath = path3.join(ROOT, "instances", instanceStorageKey(), "agent.json");
453
+ cachedDescriptor = {
454
+ schemaVersion: 1,
455
+ hostId: stableId(HOST_PATH),
456
+ agentInstanceId: stableId(instancePath),
457
+ hostName: hostname(),
458
+ agentKind: "deepseek-harness",
459
+ agentName: "DeepSeek Harness",
460
+ agentVersion: installedDshVersion(),
461
+ hostPlatform: hostPlatformDescriptor(),
462
+ capabilities: AGENT_CAPABILITIES
463
+ };
464
+ return cachedDescriptor;
465
+ }
466
+
467
+ // src/gate-ports.ts
468
+ import { createHash as createHash2 } from "node:crypto";
469
+ var LEGACY_PUBLIC_PORT = 3092;
470
+ var LEGACY_LOCAL_PORT = 3093;
471
+ var DYNAMIC_PORT_BASE = 32e3;
472
+ var DYNAMIC_PORT_PAIRS = 4e3;
473
+ function validPort2(value) {
474
+ if (value === void 0 || value === "") return null;
475
+ if (!/^\d{1,5}$/.test(value)) return null;
476
+ const parsed = Number(value);
477
+ return Number.isSafeInteger(parsed) && parsed >= 1 && parsed <= 65535 ? parsed : null;
478
+ }
479
+ function isLegacyProfile(profileScope) {
480
+ const normalized = profileScope.trim().toLowerCase();
481
+ return normalized === "web" || normalized === "default";
482
+ }
483
+ function deriveGatePorts(profileScope, agentInstanceId, environment = process.env) {
484
+ const warnings = [];
485
+ const legacy = isLegacyProfile(profileScope);
486
+ const digest = createHash2("sha256").update(`harness-remote-gate-v1\0${profileScope}\0${agentInstanceId}`).digest();
487
+ const pair = digest.readUInt32BE(0) % DYNAMIC_PORT_PAIRS;
488
+ const derivedPublic = legacy ? LEGACY_PUBLIC_PORT : DYNAMIC_PORT_BASE + pair * 2;
489
+ const derivedLocal = legacy ? LEGACY_LOCAL_PORT : derivedPublic + 1;
490
+ const publicOverride = validPort2(environment.WECHAT_GATE_PORT);
491
+ const localOverride = validPort2(environment.WECHAT_GATE_LOCAL_PORT);
492
+ if (environment.WECHAT_GATE_PORT && publicOverride === null) {
493
+ warnings.push(`\u5FFD\u7565\u65E0\u6548\u7684 WECHAT_GATE_PORT=${environment.WECHAT_GATE_PORT}`);
494
+ }
495
+ if (environment.WECHAT_GATE_LOCAL_PORT && localOverride === null) {
496
+ warnings.push(`\u5FFD\u7565\u65E0\u6548\u7684 WECHAT_GATE_LOCAL_PORT=${environment.WECHAT_GATE_LOCAL_PORT}`);
497
+ }
498
+ const publicPort = publicOverride ?? derivedPublic;
499
+ const localPort = localOverride ?? derivedLocal;
500
+ if (publicPort === localPort) {
501
+ warnings.push(`\u516C\u7F51\u95E8\u4E0E\u672C\u5730\u95E8\u90FD\u914D\u7F6E\u4E3A ${publicPort}\uFF1B\u5176\u4E2D\u4E00\u6247\u95E8\u5C06\u65E0\u6CD5\u76D1\u542C`);
502
+ }
503
+ return {
504
+ profileScope,
505
+ publicPort,
506
+ localPort,
507
+ source: publicOverride !== null || localOverride !== null ? "environment-override" : legacy ? "legacy-default" : "profile-derived",
508
+ warnings
509
+ };
510
+ }
511
+
512
+ // src/dsh-protocol-compat.ts
513
+ import { setTimeout as delay } from "node:timers/promises";
514
+
515
+ // src/dsh-session-address.ts
516
+ function record(value) {
517
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
518
+ }
519
+ function unavailable(message, sessionId, reason) {
520
+ throw Object.assign(new Error(message), {
521
+ code: "adapter/session-address-unavailable",
522
+ details: { sessionId, reason }
523
+ });
524
+ }
525
+ async function resolveDshSessionAddress(gateway, sessionId, signal) {
526
+ signal.throwIfAborted();
527
+ if (!sessionId || sessionId.length > 256) unavailable("\u4F1A\u8BDD\u6807\u8BC6\u65E0\u6548", sessionId, "invalid-id");
528
+ const listing = record(await gateway.invoke({
529
+ namespace: "session",
530
+ method: "list",
531
+ args: { _request: {} },
532
+ signal
533
+ }));
534
+ signal.throwIfAborted();
535
+ if (!Array.isArray(listing?.items)) unavailable("DSH \u4F1A\u8BDD\u76EE\u5F55\u6682\u4E0D\u53EF\u7528", sessionId, "invalid-directory");
536
+ const rows = listing.items.map(record).filter((row2) => row2?.sessionId === sessionId);
537
+ if (rows.length !== 1) unavailable("\u4F1A\u8BDD\u4E0D\u5B58\u5728\u6216\u6682\u4E0D\u53EF\u8BFB\u53D6\uFF0C\u8BF7\u5237\u65B0\u4F1A\u8BDD\u5217\u8868", sessionId, "missing-session");
538
+ const row = rows[0];
539
+ if (row.origin !== "subagent") return { kind: "session", sessionId };
540
+ const parentSessionId = row.parentSessionId;
541
+ if (typeof parentSessionId !== "string" || !parentSessionId || parentSessionId === sessionId) {
542
+ unavailable("\u5B50\u4EE3\u7406\u4F1A\u8BDD\u7F3A\u5C11\u6709\u6548\u7684\u7236\u4F1A\u8BDD\u6807\u8BC6", sessionId, "invalid-parent");
543
+ }
544
+ const catalog = record(await gateway.invoke({
545
+ namespace: "subagents",
546
+ method: "list",
547
+ args: { parentSessionId },
548
+ signal
549
+ }));
550
+ signal.throwIfAborted();
551
+ if (!Array.isArray(catalog?.entries)) unavailable("DSH \u5B50\u4EE3\u7406\u76EE\u5F55\u6682\u4E0D\u53EF\u7528", sessionId, "invalid-catalog");
552
+ const children = catalog.entries.map(record).filter((entry) => entry?.id === sessionId);
553
+ if (children.length !== 1) unavailable("\u5B50\u4EE3\u7406\u4F1A\u8BDD\u6682\u4E0D\u53EF\u8BFB\u53D6\uFF0C\u8BF7\u5237\u65B0\u540E\u91CD\u8BD5", sessionId, "missing-child");
554
+ const child = children[0];
555
+ if (child.kind !== "child" || child.mode !== "one-shot" && child.mode !== "continuable") {
556
+ unavailable(
557
+ "DSH \u65E0\u6CD5\u8BC6\u522B\u6B64\u5B50\u4EE3\u7406\u4F1A\u8BDD\uFF0C\u8BF7\u5728\u7535\u8111\u7AEF\u68C0\u67E5\u8BE5\u4F1A\u8BDD",
558
+ sessionId,
559
+ typeof child.reason === "string" ? child.reason : "unsupported-child"
560
+ );
561
+ }
562
+ return { kind: "subagent", parentSessionId, childSessionId: sessionId, mode: child.mode };
563
+ }
564
+
565
+ // src/session-presentation.ts
566
+ var object = (v) => v && typeof v === "object" && !Array.isArray(v) ? v : {};
567
+ function numbers(v, names) {
568
+ return Object.fromEntries(names.flatMap(([from, to]) => typeof v[from] === "number" && Number.isFinite(v[from]) && Number(v[from]) >= 0 ? [[to, v[from]]] : []));
569
+ }
570
+ function presentationProjection(key, raw) {
571
+ const base = object(raw), v = base.values ? object(base.values) : base;
572
+ let name, value;
573
+ switch (key) {
574
+ case "sessionStats":
575
+ name = "metrics";
576
+ value = numbers(v, [["turns", "turns"], ["steps", "steps"], ["toolMs", "toolMs"], ["llmMs", "modelMs"], ["ttftMs", "firstTokenMs"], ["ttftSteps", "firstTokenSamples"], ["decodeMs", "decodeMs"], ["decodeTokens", "decodeTokens"]]);
577
+ break;
578
+ case "tokenUsage":
579
+ name = "usage";
580
+ value = numbers(v, [["uncachedInputTokens", "input"], ["outputTokens", "output"], ["cacheReadTokens", "cacheRead"], ["cacheWriteTokens", "cacheWrite"]]);
581
+ break;
582
+ case "contextPressure":
583
+ name = "context";
584
+ value = { estimated: true, ...numbers(v, [[v.projectedTokens === void 0 ? "pressureTokens" : "projectedTokens", "used"], ["contextWindow", "capacity"]]) };
585
+ break;
586
+ case "contextBreakdown":
587
+ name = "composition";
588
+ value = numbers(v, [["systemTokens", "system"], ["toolsTokens", "tools"], ["messageTokens", "messages"]]);
589
+ break;
590
+ case "plan":
591
+ name = "planMode";
592
+ value = { active: v.active === true, pending: v.pending === true };
593
+ break;
594
+ case "todos":
595
+ name = "plan";
596
+ value = Array.isArray(raw) ? { steps: raw.map((t) => ({ text: object(t).content, status: object(t).status })) } : null;
597
+ break;
598
+ case "goal": {
599
+ name = "goal";
600
+ const goal = object(base.goal);
601
+ value = base.goal ? { id: goal.id, revision: goal.revision, text: goal.objective, status: goal.phase, rounds: base.roundsStarted, roundLimit: goal.maxGoalRounds, reason: object(goal.blockedReason).message } : null;
602
+ break;
603
+ }
604
+ default:
605
+ return null;
606
+ }
607
+ return { key: `agent.${name}.v1`, value };
608
+ }
609
+ function withPresentationProjections(block) {
610
+ const source = object(block);
611
+ if (!source.values) return block;
612
+ const values = { ...object(source.values) };
613
+ for (const [key, value] of Object.entries(values)) {
614
+ const projected = presentationProjection(key, value);
615
+ if (projected) values[projected.key] = projected.value;
616
+ }
617
+ return { ...source, values };
618
+ }
619
+
620
+ // src/dsh-protocol-compat.ts
621
+ var SINGLE_REQUEST_METHODS = /* @__PURE__ */ new Set([
622
+ "session.attachment",
623
+ "session.cancel",
624
+ "session.create",
625
+ "session.fork",
626
+ "session.openWorkspacePath",
627
+ "session.rename",
628
+ "session.search",
629
+ "session.selectModel",
630
+ "session.updateQueue",
631
+ "workspace.archiveSession",
632
+ "workspace.create",
633
+ "workspace.delete",
634
+ "workspace.insertBefore",
635
+ "workspace.insertSessionBefore",
636
+ "workspace.rename"
637
+ ]);
638
+ function recordOf(value) {
639
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
640
+ }
641
+ function present(value, key) {
642
+ return Object.hasOwn(value, key) ? { [key]: value[key] } : {};
643
+ }
644
+ function requestInvocation(method, payload) {
645
+ const [namespace, remoteMethod] = method.split(".", 2);
646
+ return {
647
+ kind: "invoke",
648
+ namespace,
649
+ method: remoteMethod,
650
+ args: { request: payload }
651
+ };
652
+ }
653
+ function planLegacyRpc(request) {
654
+ const { method, payload } = request;
655
+ if (method.includes("/")) {
656
+ const split = method.indexOf("/");
657
+ const namespace = method.slice(0, split);
658
+ const remoteMethod = method.slice(split + 1);
659
+ if (!namespace || !remoteMethod || remoteMethod.includes("/")) {
660
+ throw new Error(`unsupported Remote endpoint: ${method}`);
661
+ }
662
+ const supplied = recordOf(payload.args);
663
+ if (namespace === "commands" && remoteMethod === "execute") {
664
+ const args = supplied ?? {};
665
+ const line = typeof args.line === "string" ? args.line.trim() : "";
666
+ if (/^\/permission(?:\s|$)/.test(line)) {
667
+ const match = /^\/permission(?:[ \t]+([a-z][a-z0-9-]*))?[ \t]*$/.exec(line);
668
+ if (!match || typeof args.agentId !== "string" || !args.agentId.trim() || ["images", "submittedAttachments"].some((key) => args[key] !== void 0 && (!Array.isArray(args[key]) || args[key].length))) {
669
+ throw Object.assign(new Error("\u6743\u9650\u547D\u4EE4\u683C\u5F0F\u65E0\u6548\uFF1B\u672A\u66F4\u6539\u6743\u9650"), { code: "adapter/invalid-permission-command" });
670
+ }
671
+ return { kind: "permission-command", sessionId: args.agentId, line, nativeReceipt: true, ...match[1] ? { preset: match[1] } : {} };
672
+ }
673
+ const images = Array.isArray(args.images) ? args.images : [];
674
+ return { kind: "invoke", namespace, method: remoteMethod, args: {
675
+ ...args,
676
+ images,
677
+ submittedAttachments: args.submittedAttachments ?? images.map((image) => ({ ...recordOf(image), type: "image" }))
678
+ } };
679
+ }
680
+ return { kind: "invoke", namespace, method: remoteMethod, args: supplied ?? {} };
681
+ }
682
+ if (method === "host.describe") return { kind: "host-describe" };
683
+ if (method === "workspace.list") return { kind: "workspace-list" };
684
+ if (method === "session.history") {
685
+ return { kind: "session-history", request: payload };
686
+ }
687
+ if (method === "session.list") {
688
+ return {
689
+ kind: "invoke",
690
+ namespace: "session",
691
+ method: "list",
692
+ args: { _request: payload }
693
+ };
694
+ }
695
+ if (method === "session.models") {
696
+ return { kind: "session-models", request: payload };
697
+ }
698
+ if (method === "session.prompt") {
699
+ const parts = Array.isArray(payload.content) ? payload.content : [];
700
+ const first = recordOf(parts[0]);
701
+ const text = first?.type === "text" && typeof first.text === "string" ? first.text.trim() : "";
702
+ if (/^\/permission(?:\s|$)/.test(text)) {
703
+ const match = /^\/permission(?:[ \t]+([a-z][a-z0-9-]*))?[ \t]*$/.exec(text);
704
+ if (!match || parts.length !== 1 || typeof payload.sessionId !== "string" || !payload.sessionId.trim()) {
705
+ throw Object.assign(new Error("\u6743\u9650\u547D\u4EE4\u683C\u5F0F\u65E0\u6548\uFF1B\u672A\u53D1\u9001\u804A\u5929\u6D88\u606F\uFF0C\u4E5F\u672A\u66F4\u6539\u6743\u9650"), { code: "adapter/invalid-permission-command" });
706
+ }
707
+ return {
708
+ kind: "permission-command",
709
+ sessionId: payload.sessionId,
710
+ line: text,
711
+ ...match[1] ? { preset: match[1] } : {}
712
+ };
713
+ }
714
+ return requestInvocation(method, {
715
+ ...payload,
716
+ requestId: typeof payload.requestId === "string" && payload.requestId ? payload.requestId : request.rpcId
717
+ });
718
+ }
719
+ if (SINGLE_REQUEST_METHODS.has(method)) return requestInvocation(method, payload);
720
+ if (method === "host.openPath") {
721
+ return {
722
+ kind: "invoke",
723
+ namespace: "session",
724
+ method: "openWorkspacePath",
725
+ args: { request: payload }
726
+ };
727
+ }
728
+ if (method === "agentPreset.list") {
729
+ return { kind: "invoke", namespace: "agentPresets", method: "list", args: {} };
730
+ }
731
+ if (method === "agentPreset.select") {
732
+ return {
733
+ kind: "invoke",
734
+ namespace: "agentPresets",
735
+ method: "select",
736
+ args: { agentId: payload.sessionId, agentPreset: payload.agentPreset },
737
+ transform: (value) => ({ agentPreset: value })
738
+ };
739
+ }
740
+ if (method === "settings.update") {
741
+ return {
742
+ kind: "invoke",
743
+ namespace: "settings",
744
+ method: "update",
745
+ args: {
746
+ ns: payload.ns,
747
+ patch: payload.patch,
748
+ ...present(payload, "expectedRevision")
749
+ }
750
+ };
751
+ }
752
+ if (method === "llm.providers") {
753
+ return {
754
+ kind: "invoke",
755
+ namespace: "llm",
756
+ method: "listConfigurableProviders",
757
+ args: {},
758
+ transform: (value) => ({ providers: Array.isArray(value) ? value : [] })
759
+ };
760
+ }
761
+ if (method === "llm.models") {
762
+ return {
763
+ kind: "invoke",
764
+ namespace: "session",
765
+ method: "modelCatalog",
766
+ args: {}
767
+ };
768
+ }
769
+ if (method === "subagent.list") {
770
+ return {
771
+ kind: "invoke",
772
+ namespace: "subagents",
773
+ method: "list",
774
+ args: { parentSessionId: payload.parentSessionId }
775
+ };
776
+ }
777
+ if (method === "subagent.interrupt") {
778
+ return {
779
+ kind: "invoke",
780
+ namespace: "subagents",
781
+ method: "interruptByParent",
782
+ args: {
783
+ childSessionId: payload.childSessionId,
784
+ parentSessionId: payload.parentSessionId,
785
+ mode: payload.mode
786
+ }
787
+ };
788
+ }
789
+ if (method.startsWith("goal.")) {
790
+ const remoteMethod = method.slice("goal.".length);
791
+ const args = { agentId: payload.sessionId };
792
+ const mutation = {
793
+ ...present(payload, "objective"),
794
+ ...present(payload, "maxGoalRounds")
795
+ };
796
+ if (remoteMethod === "create") args.request = mutation;
797
+ else {
798
+ args.ref = payload.ref;
799
+ if (remoteMethod === "edit") args.request = mutation;
800
+ }
801
+ return {
802
+ kind: "invoke",
803
+ namespace: "goals",
804
+ method: remoteMethod,
805
+ args,
806
+ transform: (value) => {
807
+ if (remoteMethod === "create") return value;
808
+ if (remoteMethod === "clear") return { cleared: true };
809
+ const goal = recordOf(value);
810
+ return { ref: { id: goal?.id, revision: goal?.revision } };
811
+ }
812
+ };
813
+ }
814
+ throw new Error(`unsupported legacy DSH RPC: ${method}`);
815
+ }
816
+ function resolveTypertGateway(ctx) {
817
+ const candidate = ctx.get("typertGateway");
818
+ if (!candidate || typeof candidate.invoke !== "function" || typeof candidate.stream !== "function") return null;
819
+ return {
820
+ wireStream: candidate.wireStream,
821
+ invoke: (request) => candidate.invoke(request),
822
+ stream: (request) => candidate.stream(request),
823
+ commandAttachmentField: () => {
824
+ const registry = ctx.get("typert");
825
+ const descriptor = registry?.local?.get("commands/execute");
826
+ if (descriptor) {
827
+ const fields = descriptor.parameters?.map((parameter) => parameter.wire) || [];
828
+ if (fields.includes("submittedAttachments") && !fields.includes("images")) return "submittedAttachments";
829
+ if (fields.includes("images") && !fields.includes("submittedAttachments")) return "images";
830
+ throw new Error("DSH \u547D\u4EE4\u9644\u4EF6\u53C2\u6570\u5DF2\u53D8\u66F4\uFF1B\u672A\u6267\u884C\u547D\u4EE4");
831
+ }
832
+ if (registry?.local?.hasSeen?.("commands/execute")) throw new Error("DSH \u547D\u4EE4\u63A5\u53E3\u6B63\u5728\u66F4\u65B0\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5");
833
+ return ctx.get("fileUploads") ? "submittedAttachments" : "images";
834
+ }
835
+ };
836
+ }
837
+ function commandArguments(gateway, args) {
838
+ const field = gateway.commandAttachmentField?.() || "images";
839
+ const { images, submittedAttachments, ...rest } = args;
840
+ const value = field === "submittedAttachments" ? submittedAttachments ?? (Array.isArray(images) ? images.map((image) => ({ ...recordOf(image), type: "image" })) : []) : images ?? [];
841
+ if (field === "images" && Array.isArray(submittedAttachments) && submittedAttachments.some((part) => recordOf(part)?.type !== "image")) throw new Error("\u6B64\u7248\u672C DSH \u547D\u4EE4\u4E0D\u652F\u6301\u6587\u4EF6\u9644\u4EF6");
842
+ return { ...rest, [field]: value };
843
+ }
844
+ function errorResult(error) {
845
+ const value = recordOf(error);
846
+ const code = typeof value?.code === "string" && value.code ? value.code : "adapter/internal";
847
+ const message = error instanceof Error && error.message ? error.message : typeof value?.message === "string" && value.message ? value.message : String(error);
848
+ return {
849
+ ok: false,
850
+ error: {
851
+ code,
852
+ message,
853
+ ...value && Object.hasOwn(value, "details") ? { details: value.details } : {}
854
+ }
855
+ };
856
+ }
857
+ function historyEvents(records) {
858
+ if (!Array.isArray(records)) return [];
859
+ return records.flatMap((record2) => {
860
+ const row = recordOf(record2);
861
+ const event = recordOf(row?.event);
862
+ if (row?.type === "event" && event) return [{ event }];
863
+ if (row?.type !== "chunks" || !event) return [];
864
+ return unpackChunkRow(event).map((item) => ({ event: item }));
865
+ });
866
+ }
867
+ function unpackChunkRow(event) {
868
+ const data = recordOf(event.data);
869
+ if (!data || !Number.isSafeInteger(event.seq) || !Number.isSafeInteger(event.time)) return [];
870
+ const type = event.type;
871
+ const members = type === "chunkrow/tool-call-chunks" ? data.args : data.texts;
872
+ if (!Array.isArray(members) || members.length === 0 || members.some((item) => typeof item !== "string")) return [];
873
+ const gaps = Array.isArray(data.dt) ? data.dt : [];
874
+ if (gaps.length !== members.length - 1 || gaps.some((gap) => !Number.isSafeInteger(gap))) return [];
875
+ let time = Number(event.time);
876
+ return members.map((member, index) => {
877
+ if (index > 0) time += Number(gaps[index - 1]);
878
+ let chunk;
879
+ if (type === "chunkrow/text-chunks") {
880
+ chunk = { type: "text-delta", index: data.index, text: member };
881
+ } else if (type === "chunkrow/reasoning-chunks") {
882
+ chunk = { type: "reasoning-delta", index: data.index, text: member };
883
+ } else if (type === "chunkrow/tool-call-chunks") {
884
+ chunk = {
885
+ type: "tool-call-delta",
886
+ index: data.index,
887
+ id: data.id,
888
+ ...present(data, "name"),
889
+ argumentsDelta: member
890
+ };
891
+ } else return {};
892
+ return {
893
+ type: "assistant/chunk",
894
+ seq: Number(event.seq) + index,
895
+ time,
896
+ data: { turn: data.turn, step: data.step, chunk }
897
+ };
898
+ }).filter((item) => typeof item.type === "string");
899
+ }
900
+ async function firstStreamFrame(gateway, namespace, method, args, signal) {
901
+ const iterable = await gateway.stream({ namespace, method, args, signal });
902
+ const iterator = iterable[Symbol.asyncIterator]();
903
+ try {
904
+ const first = await iterator.next();
905
+ if (first.done) throw new Error(`${namespace}/${method} ended before its baseline`);
906
+ return first.value;
907
+ } finally {
908
+ await iterator.return?.();
909
+ }
910
+ }
911
+ async function historyValue(gateway, request, signal) {
912
+ const sessionId = typeof request.sessionId === "string" ? request.sessionId : "";
913
+ if (!sessionId) throw new Error("session.history requires sessionId");
914
+ const maxMessages = Number.isSafeInteger(request.maxMessages) ? Math.max(1, Math.min(30, Number(request.maxMessages))) : 8;
915
+ const address = await resolveDshSessionAddress(gateway, sessionId, signal);
916
+ const first = recordOf(await firstStreamFrame(
917
+ gateway,
918
+ "session",
919
+ "follow",
920
+ { request: { address, maxMessages } },
921
+ signal
922
+ ));
923
+ if (first?.type !== "snapshot" || !Number.isSafeInteger(first.cursor)) {
924
+ throw new Error("session/follow returned an invalid opening snapshot");
925
+ }
926
+ if (Number.isSafeInteger(request.beforeSeq)) {
927
+ const page = recordOf(await gateway.invoke({
928
+ namespace: "session",
929
+ method: "page",
930
+ args: {
931
+ request: {
932
+ address,
933
+ throughSeq: first.cursor,
934
+ beforeSeq: request.beforeSeq,
935
+ maxMessages
936
+ }
937
+ },
938
+ signal
939
+ })) ?? {};
940
+ return {
941
+ events: historyEvents(page.records),
942
+ hasMore: page.hasMore === true
943
+ };
944
+ }
945
+ return {
946
+ events: historyEvents(first.records),
947
+ hasMore: first.hasMore === true,
948
+ projections: withPresentationProjections(first.projections),
949
+ historyEndSeq: first.cursor
950
+ };
951
+ }
952
+ async function workspaceValue(gateway, signal) {
953
+ const frame = recordOf(await firstStreamFrame(
954
+ gateway,
955
+ "workspace",
956
+ "follow",
957
+ {},
958
+ signal
959
+ ));
960
+ if (frame?.type !== "baseline" || !recordOf(frame.value)) {
961
+ throw new Error("workspace/follow returned an invalid baseline");
962
+ }
963
+ return frame.value;
964
+ }
965
+ async function permissionCommandValue(gateway, plan, signal, readHistory, flushPermission) {
966
+ signal.throwIfAborted();
967
+ const command = recordOf(await gateway.invoke({
968
+ namespace: "commands",
969
+ method: "execute",
970
+ args: commandArguments(gateway, { agentId: plan.sessionId, line: plan.line, images: [], submittedAttachments: [] }),
971
+ signal
972
+ }));
973
+ const result = recordOf(command?.result);
974
+ if (result?.kind !== "success") {
975
+ throw Object.assign(new Error(typeof result?.text === "string" && result.text ? result.text : "DSH \u672A\u6210\u529F\u6267\u884C\u6743\u9650\u547D\u4EE4\uFF1B\u8BF7\u68C0\u67E5\u4E3B\u673A\u6743\u9650\u9884\u8BBE\u670D\u52A1"), { code: "adapter/command-failed" });
976
+ }
977
+ await flushPermission?.(plan.sessionId);
978
+ signal.throwIfAborted();
979
+ const history = recordOf(await readHistory(plan.sessionId, signal));
980
+ const permissions = recordOf(recordOf(recordOf(history?.projections)?.values)?.permissions);
981
+ const current = permissions?.currentValue;
982
+ if (typeof current !== "string" || plan.preset && current !== plan.preset) {
983
+ throw Object.assign(new Error("\u6743\u9650\u547D\u4EE4\u5DF2\u6267\u884C\uFF0C\u4F46\u672A\u786E\u8BA4\u76EE\u6807\u6743\u9650\uFF1B\u8BF7\u5237\u65B0\u4F1A\u8BDD\u6838\u5BF9\u5F53\u524D\u6743\u9650"), {
984
+ code: "adapter/permission-not-applied"
985
+ });
986
+ }
987
+ return plan.nativeReceipt ? command : { accepted: true, command: true, permission: current, commandId: command?.commandId };
988
+ }
989
+ async function sessionModelsValue(gateway, request, signal) {
990
+ const sessionId = typeof request.sessionId === "string" ? request.sessionId : "";
991
+ if (!sessionId) throw new Error("session.models requires sessionId");
992
+ const address = await resolveDshSessionAddress(gateway, sessionId, signal);
993
+ const [rawCatalog, rawSnapshot] = await Promise.all([
994
+ gateway.invoke({ namespace: "session", method: "modelCatalog", args: {}, signal }),
995
+ firstStreamFrame(gateway, "session", "follow", {
996
+ request: { address, maxMessages: 1 }
997
+ }, signal)
998
+ ]);
999
+ const catalog = recordOf(rawCatalog);
1000
+ const snapshot = recordOf(rawSnapshot);
1001
+ const projections = recordOf(snapshot?.projections);
1002
+ const selection = recordOf(recordOf(projections?.values)?.modelSelection);
1003
+ if (!catalog || snapshot?.type !== "snapshot" || !projections) {
1004
+ throw new Error("DSH returned an invalid model catalog or Session snapshot");
1005
+ }
1006
+ const current = selection?.next ?? selection?.lastUsed ?? catalog.default;
1007
+ if (!recordOf(current)) throw new Error("DSH returned no usable model selection");
1008
+ return { ...catalog, current };
1009
+ }
1010
+ async function invokeLegacyRpc(gateway, request, options) {
1011
+ let result;
1012
+ try {
1013
+ const plan = planLegacyRpc(request);
1014
+ let value;
1015
+ if (plan.kind === "host-describe") value = options.describeHost();
1016
+ else if (plan.kind === "workspace-list") value = await workspaceValue(gateway, options.signal);
1017
+ else if (plan.kind === "session-history") value = await historyValue(gateway, plan.request, options.signal);
1018
+ else if (plan.kind === "session-models") value = await sessionModelsValue(gateway, plan.request, options.signal);
1019
+ else if (plan.kind === "permission-command") value = await permissionCommandValue(
1020
+ gateway,
1021
+ plan,
1022
+ options.signal,
1023
+ (sessionId, signal) => historyValue(gateway, { sessionId, maxMessages: 1 }, signal),
1024
+ options.flushPermission
1025
+ );
1026
+ else {
1027
+ for (let attempt = 0; ; attempt++) {
1028
+ try {
1029
+ options.signal.throwIfAborted();
1030
+ value = await gateway.invoke({
1031
+ namespace: plan.namespace,
1032
+ method: plan.method,
1033
+ args: plan.namespace === "commands" && plan.method === "execute" ? commandArguments(gateway, plan.args) : plan.args,
1034
+ signal: options.signal
1035
+ });
1036
+ break;
1037
+ } catch (error) {
1038
+ const failure = recordOf(error);
1039
+ if (request.method !== "session.list" || attempt >= 2 || failure?.code !== "SESSION_QUERY_PERSISTENCE_FAILED" || typeof failure.message !== "string" || !/ENOENT.*scandir.*[\\/]\.dsh-mkdir-[^\\/]+$/.test(failure.message)) throw error;
1040
+ await delay(25 * (attempt + 1), void 0, { signal: options.signal });
1041
+ }
1042
+ }
1043
+ if (plan.transform) value = plan.transform(value);
1044
+ }
1045
+ result = { ok: true, value };
1046
+ } catch (error) {
1047
+ result = errorResult(error);
1048
+ }
1049
+ return { type: "server-response", rpcId: request.rpcId, result };
1050
+ }
1051
+
1052
+ // src/install-lifecycle.ts
1053
+ import fs from "node:fs";
1054
+ import path4 from "node:path";
1055
+ import { execFileSync as execFileSync2, spawn } from "node:child_process";
1056
+ var serviceName = (s) => /^[A-Za-z0-9_.@-]{1,180}$/.test(s);
1057
+ function run(command, args) {
1058
+ return execFileSync2(command, args, { encoding: "utf8", windowsHide: true, timeout: 15e3, maxBuffer: 1024 * 1024, stdio: ["ignore", "pipe", "pipe"] }).trim();
1059
+ }
1060
+ function validateManager(value) {
1061
+ if (value.kind === "process") return;
1062
+ if (value.kind === "systemd" && process.platform === "linux" && serviceName(value.unit) && value.unit.endsWith(".service")) return;
1063
+ if (value.kind === "launchd" && process.platform === "darwin" && serviceName(value.label) && /^gui\/\d+$/.test(value.domain) && value.domain === `gui/${process.getuid?.()}` && path4.isAbsolute(value.plist) && fs.statSync(value.plist).isFile()) return;
1064
+ throw new Error("\u65E0\u6CD5\u786E\u8BA4\u539F\u540E\u53F0\u670D\u52A1\uFF0C\u672A\u505C\u6B62 DSH\u3002");
1065
+ }
1066
+ function currentHostManager() {
1067
+ if (process.env.PM2_HOME || process.env.NODE_APP_INSTANCE || process.env.KUBERNETES_SERVICE_HOST || process.env.container) throw new Error("\u6B64\u540E\u53F0\u7BA1\u7406\u65B9\u5F0F\u5C1A\u4E0D\u652F\u6301\u81EA\u52A8\u91CD\u542F\u3002");
1068
+ if (process.platform === "linux") {
1069
+ const group = fs.readFileSync("/proc/self/cgroup", "utf8");
1070
+ const units = group.split(/[\n/]/).filter((s) => serviceName(s) && s.endsWith(".service"));
1071
+ const unit = units.at(-1);
1072
+ if (unit && !/^user@\d+\.service$/.test(unit)) {
1073
+ const pid = Number(run("systemctl", ["--user", "show", unit, "--property=MainPID", "--value"]));
1074
+ if (pid !== process.pid) throw new Error("DSH \u4E0D\u662F\u8BE5\u670D\u52A1\u7684\u72EC\u7ACB\u4E3B\u8FDB\u7A0B\uFF0C\u672A\u505C\u6B62\u540E\u53F0\u670D\u52A1\u3002");
1075
+ return { kind: "systemd", unit };
1076
+ }
1077
+ if (process.env.INVOCATION_ID) throw new Error("\u65E0\u6CD5\u786E\u8BA4 systemd \u670D\u52A1\u5F52\u5C5E\uFF0C\u672A\u505C\u6B62 DSH\u3002");
1078
+ }
1079
+ if (process.platform === "darwin") {
1080
+ const rows = run("/bin/launchctl", ["list"]).split("\n");
1081
+ const row = rows.map((s) => s.trim().split(/\s+/)).find((parts) => parts[0] === String(process.pid));
1082
+ if (row) {
1083
+ const label = row[2], domain = `gui/${process.getuid?.()}`;
1084
+ if (!serviceName(label)) throw new Error("\u540E\u53F0\u4EFB\u52A1\u540D\u79F0\u65E0\u6548\u3002");
1085
+ const printed = run("/bin/launchctl", ["print", `${domain}/${label}`]);
1086
+ const plist = /^\s*path = (.+\.plist)\s*$/m.exec(printed)?.[1];
1087
+ if (!plist) throw new Error("\u540E\u53F0\u4EFB\u52A1\u7F3A\u5C11\u53EF\u6062\u590D\u7684\u542F\u52A8\u914D\u7F6E\u3002");
1088
+ const result = { kind: "launchd", label, domain, plist };
1089
+ validateManager(result);
1090
+ return result;
1091
+ }
1092
+ if (process.env.LAUNCH_JOBKEY_LABEL) throw new Error("\u65E0\u6CD5\u786E\u8BA4 launchd \u670D\u52A1\u5F52\u5C5E\uFF0C\u672A\u505C\u6B62 DSH\u3002");
1093
+ }
1094
+ return { kind: "process" };
1095
+ }
1096
+ function startUpdateWorker(manager, directory, executable) {
1097
+ validateManager(manager);
1098
+ const id = path4.basename(directory);
1099
+ if (!/^[a-f0-9]{32}$/.test(id)) throw new Error("\u65E0\u6548\u7684\u66F4\u65B0\u4EFB\u52A1");
1100
+ const args = [path4.join(directory, "update-worker.js"), path4.join(directory, "job.json")];
1101
+ if (manager.kind === "systemd") {
1102
+ run("systemd-run", ["--user", "--quiet", "--collect", `--unit=dsh-wechat-update-${id}`, "--property=Type=exec", `--working-directory=${directory}`, executable, ...args]);
1103
+ } else if (manager.kind === "launchd") {
1104
+ run("/bin/launchctl", ["submit", "-l", `dsh.wechat.update.${id}`, "-o", path4.join(directory, "worker.log"), "-e", path4.join(directory, "worker.log"), "--", executable, ...args]);
1105
+ } else {
1106
+ const log = fs.openSync(path4.join(directory, "worker.log"), "a", 384);
1107
+ const child = spawn(executable, args, { cwd: directory, env: process.env, windowsHide: true, detached: true, stdio: ["ignore", log, log] });
1108
+ fs.closeSync(log);
1109
+ child.unref();
1110
+ child.on("error", () => {
1111
+ });
1112
+ if (!child.pid) throw new Error("\u65E0\u6CD5\u542F\u52A8\u66F4\u65B0\u8FDB\u7A0B");
1113
+ }
1114
+ }
1115
+
1116
+ // src/install-control.ts
1117
+ import { homedir as homedir4 } from "node:os";
1118
+
1119
+ // src/install-capabilities.ts
1120
+ function assertNativeUpdateCapabilities(context) {
1121
+ const ctx = context.root || context;
1122
+ const web = ctx.get("webServer");
1123
+ const sessions = ctx.get("sessions");
1124
+ const missing = [];
1125
+ for (const name of ["on", "listeners", "removeAllListeners", "removeListener"]) {
1126
+ if (typeof web?.server?.[name] !== "function") missing.push("webServer." + name);
1127
+ }
1128
+ for (const name of ["get", "list", "flush"]) {
1129
+ if (typeof sessions?.[name] !== "function") missing.push("sessions." + name);
1130
+ }
1131
+ if (typeof ctx.fiber?.dispose !== "function") missing.push("lifecycle.dispose");
1132
+ if (missing.length) throw new Error("\u5F53\u524D\u5BBF\u4E3B\u7F3A\u5C11\u5B89\u5168\u66F4\u65B0\u6240\u9700\u80FD\u529B\uFF0C\u672A\u505C\u6B62\u8282\u70B9\uFF1A" + missing.join(", "));
1133
+ }
1134
+
1135
+ // src/install-control.ts
1136
+ async function quiesceNativeHost(ctx, read, disposing) {
1137
+ const items = (await read("session.list")).items;
1138
+ if (!Array.isArray(items) || items.some((s) => s.running !== false)) throw new Error("\u8BF7\u7B49\u5F85\u8FD0\u884C\u4E2D\u7684\u4F1A\u8BDD\u7ED3\u675F\u540E\u518D\u66F4\u65B0\u3002");
1139
+ const sessions = ctx.get("sessions");
1140
+ if (!sessions?.list || !sessions.flush) throw new Error("\u5F53\u524D DSH \u4E0D\u652F\u6301\u4FDD\u5B58\u68C0\u67E5\u3002");
1141
+ for (const session of sessions.list()) if (!await sessions.flush(session)) throw new Error("\u4F1A\u8BDD\u4FDD\u5B58\u672A\u5B8C\u6210\u3002");
1142
+ if ((await read("session.list")).items.some((s) => s.running !== false)) throw new Error("\u6709\u65B0\u4F1A\u8BDD\u5F00\u59CB\u8FD0\u884C\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002");
1143
+ disposing();
1144
+ await ctx.fiber.dispose();
1145
+ }
1146
+ async function createInstallControl(context, config) {
1147
+ const ctx = context.root;
1148
+ assertNativeUpdateCapabilities(ctx);
1149
+ const home = adapterDshHome(), id = path5.basename(config.directory);
1150
+ if (!/^[a-f0-9]{32}$/.test(id) || !/^[a-f0-9]{48}$/.test(config.token) || path5.dirname(config.directory) !== path5.join(home, "harness-remote-updates") || fs2.realpathSync(config.directory) !== config.directory) throw new Error("\u5B89\u88C5\u63A7\u5236\u8BF7\u6C42\u4E0D\u5C5E\u4E8E\u5F53\u524D DSH\u3002");
1151
+ const scope = resolveAgentProfileScope("", process.argv, home), profile = path5.join(home, "profiles", scope);
1152
+ const cli = fs2.realpathSync(process.argv[1]);
1153
+ const manifest = JSON.parse(fs2.readFileSync(path5.resolve(cli, "../../package.json"), "utf8"));
1154
+ if (manifest.name !== "@deepseek-ai/dsh" || process.execArgv.length || !process.argv.includes("web")) throw new Error("\u6B64 DSH \u542F\u52A8\u65B9\u5F0F\u5C1A\u4E0D\u652F\u6301\u81EA\u52A8\u66F4\u65B0\u3002");
1155
+ const manager = currentHostManager(), webPort = resolveDshWebRuntime(ctx, process.env).port;
1156
+ const ports = deriveGatePorts(scope, loadAgentDescriptor().agentInstanceId);
1157
+ const nativeExit = ctx.get("appExit");
1158
+ const gateway = resolveTypertGateway(ctx);
1159
+ let launched = false, quiesced = false;
1160
+ const read = async (method, payload = {}) => {
1161
+ if (!["session.list", "session.history"].includes(method)) throw new Error("\u4E0D\u652F\u6301\u7684\u5B89\u88C5\u68C0\u67E5");
1162
+ const request = { type: "client-request", rpcId: "installer-read", method, payload };
1163
+ const response = gateway ? await invokeLegacyRpc(gateway, request, { signal: AbortSignal.timeout(1e4), describeHost: () => ({}) }) : await (await fetch(`http://127.0.0.1:${webPort}/api/${method}`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(request), signal: AbortSignal.timeout(1e4), redirect: "error" })).json();
1164
+ if (!response.result?.ok) throw new Error("\u65E0\u6CD5\u9A8C\u8BC1 DSH \u4F1A\u8BDD\u72B6\u6001\uFF0C\u672A\u505C\u6B62\u8282\u70B9\u3002");
1165
+ return response.result.value;
1166
+ };
1167
+ const version = () => {
1168
+ try {
1169
+ return JSON.parse(fs2.readFileSync(path5.join(profile, "node_modules/@harness-remote/dsh-wechat-remote/package.json"), "utf8")).version;
1170
+ } catch {
1171
+ return "0.0.0";
1172
+ }
1173
+ };
1174
+ const server = http.createServer(async (req, res) => {
1175
+ const json = (status, value) => {
1176
+ res.writeHead(status, { "content-type": "application/json", "cache-control": "no-store" });
1177
+ res.end(JSON.stringify(value));
1178
+ };
1179
+ if (!["127.0.0.1", "::ffff:127.0.0.1"].includes(req.socket.remoteAddress || "") || req.headers.host !== `127.0.0.1:${server.address().port}` || req.headers["x-forwarded-for"] || req.headers.authorization !== `Bearer ${config.token}` || req.method !== "POST") return json(403, {});
1180
+ try {
1181
+ let body = "";
1182
+ for await (const chunk of req) {
1183
+ body += chunk.toString();
1184
+ if (body.length > 4096) throw new Error("\u8BF7\u6C42\u8FC7\u5927");
1185
+ }
1186
+ const input = body ? JSON.parse(body) : {};
1187
+ if (req.url === "/describe") return json(200, {
1188
+ pid: process.pid,
1189
+ cli,
1190
+ executable: process.execPath,
1191
+ argv: process.argv.slice(1),
1192
+ execArgv: process.execArgv,
1193
+ cwd: process.cwd(),
1194
+ home,
1195
+ profile,
1196
+ webPort,
1197
+ stateFile: gateStatePathForProfile(scope, homedir4(), home),
1198
+ identityFile: defaultAgentIdentityPath(),
1199
+ gatePort: ports.publicPort,
1200
+ localPort: ports.localPort,
1201
+ manager,
1202
+ dshVersion: manifest.version,
1203
+ pluginVersion: version(),
1204
+ platform: process.platform,
1205
+ arch: process.arch,
1206
+ quiesced
1207
+ });
1208
+ if (req.url === "/read" && !quiesced) return json(200, await read(input.method, input.payload));
1209
+ if (req.url === "/launch" && !launched && !quiesced) {
1210
+ const filename = path5.join(config.directory, "job.json");
1211
+ const job = JSON.parse(fs2.readFileSync(filename, "utf8"));
1212
+ if (job.id !== id || job.controlOrigin !== origin || job.statusToken !== config.token || job.parentPid !== process.pid || job.home !== home || job.profile !== profile || job.cli !== cli || job.pnpm !== config.pnpm) throw new Error("\u5B89\u88C5\u76EE\u6807\u53D1\u751F\u53D8\u5316");
1213
+ startUpdateWorker(manager, config.directory, process.execPath);
1214
+ launched = true;
1215
+ return json(200, { started: true });
1216
+ }
1217
+ if (req.url === "/quiesce" && launched && !quiesced) {
1218
+ await quiesceNativeHost(ctx, read, () => {
1219
+ quiesced = true;
1220
+ });
1221
+ return json(200, { quiesced: true, pid: process.pid });
1222
+ }
1223
+ if (req.url === "/shutdown" && quiesced) {
1224
+ json(200, { stopping: true });
1225
+ setTimeout(() => {
1226
+ close();
1227
+ if (nativeExit) nativeExit(0);
1228
+ else process.exit(0);
1229
+ }, 50);
1230
+ return;
1231
+ }
1232
+ if (req.url === "/close" && !quiesced) {
1233
+ json(200, { closed: true });
1234
+ close();
1235
+ return;
1236
+ }
1237
+ json(409, { error: "\u5B89\u88C5\u72B6\u6001\u4E0D\u5141\u8BB8\u6B64\u64CD\u4F5C" });
1238
+ } catch (error) {
1239
+ json(409, { error: error instanceof Error ? error.message : "\u5B89\u88C5\u63A7\u5236\u5931\u8D25" });
1240
+ }
1241
+ });
1242
+ await new Promise((resolve, reject) => {
1243
+ server.once("error", reject);
1244
+ server.listen(0, "127.0.0.1", resolve);
1245
+ });
1246
+ const origin = `http://127.0.0.1:${server.address().port}`;
1247
+ const timer = setTimeout(() => {
1248
+ if (!launched) close();
1249
+ }, 12e4);
1250
+ timer.unref();
1251
+ function close() {
1252
+ clearTimeout(timer);
1253
+ server.closeAllConnections();
1254
+ server.close();
1255
+ }
1256
+ writePrivateJsonAtomic(path5.join(config.directory, "control-ready.json"), { origin, pid: process.pid });
1257
+ return { origin, close };
1258
+ }
1259
+ var apply = async (ctx, config) => {
1260
+ await createInstallControl(ctx, config);
1261
+ };
1262
+ var inject = ["webServer", "sessions", "appExit"];
1263
+ export {
1264
+ apply,
1265
+ createInstallControl,
1266
+ inject,
1267
+ quiesceNativeHost
1268
+ };