agent-console-server 4.1.1 → 4.2.0

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,3275 +0,0 @@
1
- import { H as getSignedText, Ot as permissionModeSchema, U as serverMessageSchema, _n as SHELL_OUTPUT_LENGTH, dn as DEFAULT_HOSTNAME, fn as DEFAULT_PORT, gn as SCHEME_PAIRING_CODE_DURATION_MS, gt as todoStatusSchema, hn as PAIRING_HASH_PARAMETER, it as agentEventSchema, mn as PAIRING_CODE_PARAMETER, pn as HOST_SCHEME, r as handshakeMessageSchema, t as EphemeralAgentEventTypes, u as commandSchema, un as DEFAULT_APP_ORIGIN, y as driverCommandSchema } from "./EphemeralAgentEventTypes-Spjnk48Y.js";
2
- import { createRequire } from "node:module";
3
- import { homedir, hostname } from "node:os";
4
- import { ID_SEPARATOR, InvalidOperationError, Operation, RoutePath, SITE_NAME, createUniqueArraySchema, exhaustiveGuard, getOrCreate, getResult, getResultAsync, noop, withFinalizerAsync } from "@esposter/shared";
5
- import { basename, delimiter, dirname, isAbsolute, join, relative, resolve, sep, win32 } from "node:path";
6
- import { execFile, execFileSync, spawn } from "node:child_process";
7
- import { parseArgs, promisify } from "node:util";
8
- import { copyFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
9
- import { getAssetKeys, getRawAsset, isSea } from "node:sea";
10
- import { createHash, createPrivateKey, createPublicKey, generateKeyPairSync, randomBytes, sign, timingSafeEqual, verify } from "node:crypto";
11
- import { z } from "zod";
12
- import { once } from "node:events";
13
- import { WebSocket, WebSocketServer } from "ws";
14
- import { getSessionInfo, getSessionMessages, listSessions, query } from "@anthropic-ai/claude-agent-sdk";
15
- import { readFile, readdir, realpath, stat } from "node:fs/promises";
16
- import { createServer } from "node:http";
17
- import { setTimeout as setTimeout$1 } from "node:timers/promises";
18
- //#region src/services/cli/getReachableHostname.ts
19
- const getReachableHostname = (hostname$1) => hostname$1 === "0.0.0.0" ? hostname() : hostname$1;
20
- //#endregion
21
- //#region src/services/cli/hostArgs.ts
22
- const hostArgs = {
23
- hostname: {
24
- default: DEFAULT_HOSTNAME,
25
- description: "The address the host listens on",
26
- type: "string"
27
- },
28
- origin: {
29
- default: DEFAULT_APP_ORIGIN,
30
- description: "The app's origin the printed link opens",
31
- type: "string"
32
- },
33
- port: {
34
- default: String(DEFAULT_PORT),
35
- description: "The port the host listens on",
36
- type: "string"
37
- }
38
- };
39
- //#endregion
40
- //#region src/services/installer/constants.ts
41
- const HOST_EXECUTABLE_FILENAME = "agent-console-host.exe";
42
- const CLAUDE_CODE_EXECUTABLE_FILENAME = "claude.exe";
43
- const HOST_SCHEME_REGISTRY_KEY = win32.join("HKCU", "Software", "Classes", HOST_SCHEME);
44
- const HOST_DIRECTORY_ENVIRONMENT_VARIABLE = `${SITE_NAME.toUpperCase()}_HOST_DIRECTORY`;
45
- const HOST_INSTALL_DIRECTORY_SEGMENTS = [SITE_NAME, "Host"];
46
- const NODE_PTY_DIRECTORY_NAME = "node-pty";
47
- //#endregion
48
- //#region src/services/installer/getHostInstallDirectory.ts
49
- const getHostInstallDirectory = () => join(process.env.LOCALAPPDATA ?? join(homedir(), "AppData", "Local"), ...HOST_INSTALL_DIRECTORY_SEGMENTS);
50
- //#endregion
51
- //#region src/services/installer/checkIsHostInstalled.ts
52
- const checkIsHostInstalled = () => relative(getHostInstallDirectory(), dirname(process.execPath)) === "";
53
- //#endregion
54
- //#region ../shared-node/dist/execSync-CtM99iYq.js
55
- const withHiddenWindow = (parameters) => {
56
- const optionsIndex = Array.isArray(parameters[0]) || parameters[0] === void 0 && parameters.length > 1 ? 1 : 0;
57
- const options = parameters[optionsIndex];
58
- if (typeof options === "object" && options !== null) return parameters.toSpliced(optionsIndex, 1, {
59
- ...options,
60
- windowsHide: true
61
- });
62
- const isPlaceholder = options === void 0 && optionsIndex < parameters.length;
63
- return parameters.toSpliced(optionsIndex, Number(isPlaceholder), { windowsHide: true });
64
- };
65
- //#endregion
66
- //#region ../shared-node/dist/child-process.js
67
- const execFile$1 = ((file, ...parameters) => execFile(file, ...withHiddenWindow(parameters)));
68
- Object.defineProperty(execFile$1, promisify.custom, { value: (file, ...parameters) => promisify(execFile)(file, ...withHiddenWindow(parameters)) });
69
- const execFileSync$1 = ((file, ...parameters) => execFileSync(file, ...withHiddenWindow(parameters)));
70
- const spawn$1 = ((file, ...parameters) => spawn(file, ...withHiddenWindow(parameters)));
71
- //#endregion
72
- //#region src/services/installer/installHost.ts
73
- const installHost = () => {
74
- const installDirectory = getHostInstallDirectory();
75
- const executablePath = join(installDirectory, HOST_EXECUTABLE_FILENAME);
76
- mkdirSync(installDirectory, { recursive: true });
77
- copyFileSync(process.execPath, executablePath);
78
- writeFileSync(join(installDirectory, CLAUDE_CODE_EXECUTABLE_FILENAME), new Uint8Array(getRawAsset(CLAUDE_CODE_EXECUTABLE_FILENAME)));
79
- for (const assetKey of getAssetKeys().filter((key) => key.startsWith(`${NODE_PTY_DIRECTORY_NAME}/`))) {
80
- const assetPath = join(installDirectory, ...assetKey.split("/"));
81
- mkdirSync(dirname(assetPath), { recursive: true });
82
- writeFileSync(assetPath, new Uint8Array(getRawAsset(assetKey)));
83
- }
84
- for (const registryArguments of [
85
- [
86
- HOST_SCHEME_REGISTRY_KEY,
87
- "/ve",
88
- "/d",
89
- `URL:${SITE_NAME} Host`
90
- ],
91
- [
92
- HOST_SCHEME_REGISTRY_KEY,
93
- "/v",
94
- "URL Protocol",
95
- "/d",
96
- ""
97
- ],
98
- [
99
- win32.join(HOST_SCHEME_REGISTRY_KEY, "shell", "open", "command"),
100
- "/ve",
101
- "/d",
102
- `"${executablePath}" "%1"`
103
- ]
104
- ]) execFileSync$1("reg", [
105
- "add",
106
- ...registryArguments,
107
- "/f"
108
- ], { stdio: "ignore" });
109
- return installDirectory;
110
- };
111
- //#endregion
112
- //#region src/services/cli/installDownloadedHost.ts
113
- const installDownloadedHost = () => {
114
- if (!isSea() || checkIsHostInstalled()) return;
115
- getResult(() => installHost()).match((installDirectory) => {
116
- process.stdout.write(`Installed the host in ${installDirectory}. The page's Connect starts it from now on.\n\n`);
117
- }, (error) => {
118
- process.stderr.write(`Could not install the host: ${error.message}\nClose any other host window, then open this file again.\n`);
119
- process.exit(1);
120
- });
121
- };
122
- //#endregion
123
- //#region src/services/cli/writeLine.ts
124
- const writeLine = (line) => {
125
- process.stdout.write(`${line}\n`);
126
- };
127
- //#endregion
128
- //#region src/services/device/constants.ts
129
- const STATE_DIRECTORY_NAME = ".agent-console-server";
130
- const DEVICES_FILENAME = "devices.json";
131
- const HOST_KEY_FILENAME = "host-key.pem";
132
- const SECRET_BYTE_LENGTH = 32;
133
- const PRINTED_PAIRING_CODE_DURATION_MS = Temporal.Duration.from({ minutes: 10 }).total("milliseconds");
134
- //#endregion
135
- //#region src/services/device/getStateDirectory.ts
136
- const getStateDirectory = () => join(homedir(), STATE_DIRECTORY_NAME);
137
- //#endregion
138
- //#region src/services/device/writeStateFile.ts
139
- const writeStateFile = (stateDirectory, filename, content) => {
140
- const filePath = join(stateDirectory, filename);
141
- const temporaryFilePath = `${filePath}.${crypto.randomUUID()}.tmp`;
142
- mkdirSync(stateDirectory, { recursive: true });
143
- getResult(() => {
144
- writeFileSync(temporaryFilePath, content, {
145
- flag: "wx",
146
- mode: 384
147
- });
148
- renameSync(temporaryFilePath, filePath);
149
- }).match(noop, (error) => {
150
- getResult(() => {
151
- rmSync(temporaryFilePath, { force: true });
152
- }).match(noop, console.error);
153
- throw error;
154
- });
155
- };
156
- //#endregion
157
- //#region src/services/device/readHostKey.ts
158
- const readHostKey = (stateDirectory) => {
159
- const hostKeyPath = join(stateDirectory, HOST_KEY_FILENAME);
160
- if (existsSync(hostKeyPath)) return createPrivateKey(readFileSync(hostKeyPath, "utf8"));
161
- const { privateKey } = generateKeyPairSync("ed25519");
162
- writeStateFile(stateDirectory, HOST_KEY_FILENAME, privateKey.export({
163
- format: "pem",
164
- type: "pkcs8"
165
- }));
166
- return privateKey;
167
- };
168
- //#endregion
169
- //#region src/models/device/Device.ts
170
- const deviceSchema = z.object({
171
- createdAt: z.coerce.date(),
172
- credentialHash: z.string().min(1),
173
- id: z.string().min(1),
174
- name: z.string(),
175
- origin: z.string()
176
- });
177
- //#endregion
178
- //#region src/services/device/readDevices.ts
179
- const readDevices = (stateDirectory) => {
180
- const devicesPath = join(stateDirectory, DEVICES_FILENAME);
181
- if (existsSync(devicesPath)) return z.array(deviceSchema).parse(JSON.parse(readFileSync(devicesPath, "utf8")));
182
- else return [];
183
- };
184
- //#endregion
185
- //#region src/services/device/writeDevices.ts
186
- const writeDevices = (stateDirectory, devices) => {
187
- writeStateFile(stateDirectory, DEVICES_FILENAME, JSON.stringify(devices, void 0, 2));
188
- };
189
- //#endregion
190
- //#region src/services/handshake/checkIsSignatureValid.ts
191
- const checkIsSignatureValid = (key, purpose, port, nonce, signature) => verify(void 0, Buffer.from(getSignedText(purpose, port, nonce)), key, Buffer.from(signature, "base64url"));
192
- //#endregion
193
- //#region src/services/handshake/signNonce.ts
194
- const signNonce = (privateKey, purpose, port, nonce) => sign(void 0, Buffer.from(getSignedText(purpose, port, nonce)), privateKey).toString("base64url");
195
- //#endregion
196
- //#region src/services/server/constants.ts
197
- const HOST_PROBE_TIMEOUT_MS = 2e3;
198
- //#endregion
199
- //#region src/services/shared/readMessageText.ts
200
- const readMessageText = (data) => Buffer.isBuffer(data) ? data.toString() : Array.isArray(data) ? Buffer.concat(data).toString() : Buffer.from(data).toString();
201
- //#endregion
202
- //#region src/services/server/sendToRunningHost.ts
203
- const sendToRunningHost = async (hostname, port, hostKey, createMessage) => {
204
- const nonce = randomBytes(32).toString("base64url");
205
- const webSocket = new WebSocket(`ws://${hostname}:${port}`, { handshakeTimeout: HOST_PROBE_TIMEOUT_MS });
206
- const isHost = await getResultAsync(async () => {
207
- const signal = AbortSignal.timeout(HOST_PROBE_TIMEOUT_MS);
208
- await once(webSocket, "open", { signal });
209
- const pendingMessage = once(webSocket, "message", { signal });
210
- webSocket.send(JSON.stringify({
211
- nonce,
212
- type: "Challenge"
213
- }));
214
- const [data] = await pendingMessage;
215
- const serverMessage = serverMessageSchema.parse(JSON.parse(readMessageText(data)));
216
- if (serverMessage.type !== "Proof" || serverMessage.port !== port || !checkIsSignatureValid(createPublicKey(hostKey), "HostProof", port, nonce, serverMessage.signature)) return false;
217
- if (!createMessage) return true;
218
- const pendingClose = once(webSocket, "close", { signal });
219
- webSocket.send(JSON.stringify(createMessage(signNonce(hostKey, "OwnerProof", port, serverMessage.nonce))));
220
- await pendingClose;
221
- return true;
222
- }).match((isHolderHost) => isHolderHost, () => false);
223
- webSocket.terminate();
224
- return isHost;
225
- };
226
- //#endregion
227
- //#region src/services/device/runDevicesCommand.ts
228
- const runDevicesCommand = async ({ hostKey, hostname, port, revokeDeviceId, stateDirectory }, writeLine) => {
229
- const devices = readDevices(stateDirectory);
230
- if (!revokeDeviceId) {
231
- if (devices.length === 0) writeLine("No page is connected to this host.");
232
- for (const { createdAt, id, name, origin } of devices) writeLine(`${id} ${name} ${origin} since ${createdAt.toISOString()}`);
233
- return true;
234
- }
235
- if (!devices.some(({ id }) => id === revokeDeviceId)) {
236
- writeLine(`No device has the id ${revokeDeviceId}.`);
237
- return false;
238
- }
239
- writeDevices(stateDirectory, devices.filter(({ id }) => id !== revokeDeviceId));
240
- await sendToRunningHost(hostname, port, hostKey, (signature) => ({
241
- deviceId: revokeDeviceId,
242
- signature,
243
- type: "Revoke"
244
- }));
245
- writeLine(`Removed ${revokeDeviceId}. It has to connect again to use this host.`);
246
- return true;
247
- };
248
- //#endregion
249
- //#region ../../node_modules/.pnpm/citty@0.2.2/node_modules/citty/dist/_chunks/libs/scule.mjs
250
- const NUMBER_CHAR_RE = /\d/;
251
- const STR_SPLITTERS = [
252
- "-",
253
- "_",
254
- "/",
255
- "."
256
- ];
257
- function isUppercase(char = "") {
258
- if (NUMBER_CHAR_RE.test(char)) return;
259
- return char !== char.toLowerCase();
260
- }
261
- function splitByCase(str, separators) {
262
- const splitters = separators ?? STR_SPLITTERS;
263
- const parts = [];
264
- if (!str || typeof str !== "string") return parts;
265
- let buff = "";
266
- let previousUpper;
267
- let previousSplitter;
268
- for (const char of str) {
269
- const isSplitter = splitters.includes(char);
270
- if (isSplitter === true) {
271
- parts.push(buff);
272
- buff = "";
273
- previousUpper = void 0;
274
- continue;
275
- }
276
- const isUpper = isUppercase(char);
277
- if (previousSplitter === false) {
278
- if (previousUpper === false && isUpper === true) {
279
- parts.push(buff);
280
- buff = char;
281
- previousUpper = isUpper;
282
- continue;
283
- }
284
- if (previousUpper === true && isUpper === false && buff.length > 1) {
285
- const lastChar = buff.at(-1);
286
- parts.push(buff.slice(0, Math.max(0, buff.length - 1)));
287
- buff = lastChar + char;
288
- previousUpper = isUpper;
289
- continue;
290
- }
291
- }
292
- buff += char;
293
- previousUpper = isUpper;
294
- previousSplitter = isSplitter;
295
- }
296
- parts.push(buff);
297
- return parts;
298
- }
299
- function upperFirst(str) {
300
- return str ? str[0].toUpperCase() + str.slice(1) : "";
301
- }
302
- function lowerFirst(str) {
303
- return str ? str[0].toLowerCase() + str.slice(1) : "";
304
- }
305
- function pascalCase(str, opts) {
306
- return str ? (Array.isArray(str) ? str : splitByCase(str)).map((p) => upperFirst(opts?.normalize ? p.toLowerCase() : p)).join("") : "";
307
- }
308
- function camelCase(str, opts) {
309
- return lowerFirst(pascalCase(str || "", opts));
310
- }
311
- function kebabCase(str, joiner) {
312
- return str ? (Array.isArray(str) ? str : splitByCase(str)).map((p) => p.toLowerCase()).join(joiner ?? "-") : "";
313
- }
314
- function snakeCase(str) {
315
- return kebabCase(str || "", "_");
316
- }
317
- //#endregion
318
- //#region ../../node_modules/.pnpm/citty@0.2.2/node_modules/citty/dist/index.mjs
319
- function toArray(val) {
320
- if (Array.isArray(val)) return val;
321
- return val === void 0 ? [] : [val];
322
- }
323
- function formatLineColumns(lines, linePrefix = "") {
324
- const maxLength = [];
325
- for (const line of lines) for (const [i, element] of line.entries()) maxLength[i] = Math.max(maxLength[i] || 0, element.length);
326
- return lines.map((l) => l.map((c, i) => linePrefix + c[i === 0 ? "padStart" : "padEnd"](maxLength[i])).join(" ")).join("\n");
327
- }
328
- function resolveValue(input) {
329
- return typeof input === "function" ? input() : input;
330
- }
331
- var CLIError = class extends Error {
332
- code;
333
- constructor(message, code) {
334
- super(message);
335
- this.name = "CLIError";
336
- this.code = code;
337
- }
338
- };
339
- function parseRawArgs(args = [], opts = {}) {
340
- const booleans = new Set(opts.boolean || []);
341
- const strings = new Set(opts.string || []);
342
- const aliasMap = opts.alias || {};
343
- const defaults = opts.default || {};
344
- const aliasToMain = /* @__PURE__ */ new Map();
345
- const mainToAliases = /* @__PURE__ */ new Map();
346
- for (const [key, value] of Object.entries(aliasMap)) {
347
- const targets = value;
348
- for (const target of targets) {
349
- aliasToMain.set(key, target);
350
- if (!mainToAliases.has(target)) mainToAliases.set(target, []);
351
- mainToAliases.get(target).push(key);
352
- aliasToMain.set(target, key);
353
- if (!mainToAliases.has(key)) mainToAliases.set(key, []);
354
- mainToAliases.get(key).push(target);
355
- }
356
- }
357
- const options = {};
358
- function getType(name) {
359
- if (booleans.has(name)) return "boolean";
360
- const aliases = mainToAliases.get(name) || [];
361
- for (const alias of aliases) if (booleans.has(alias)) return "boolean";
362
- return "string";
363
- }
364
- function isStringType(name) {
365
- if (strings.has(name)) return true;
366
- const aliases = mainToAliases.get(name) || [];
367
- for (const alias of aliases) if (strings.has(alias)) return true;
368
- return false;
369
- }
370
- const allOptions = /* @__PURE__ */ new Set([
371
- ...booleans,
372
- ...strings,
373
- ...Object.keys(aliasMap),
374
- ...Object.values(aliasMap).flat(),
375
- ...Object.keys(defaults)
376
- ]);
377
- for (const name of allOptions) if (!options[name]) options[name] = {
378
- type: getType(name),
379
- default: defaults[name]
380
- };
381
- for (const [alias, main] of aliasToMain.entries()) if (alias.length === 1 && options[main] && !options[main].short) options[main].short = alias;
382
- const processedArgs = [];
383
- const negatedFlags = {};
384
- for (let i = 0; i < args.length; i++) {
385
- const arg = args[i];
386
- if (arg === "--") {
387
- processedArgs.push(...args.slice(i));
388
- break;
389
- }
390
- if (arg.startsWith("--no-")) {
391
- const flagName = arg.slice(5);
392
- negatedFlags[flagName] = true;
393
- continue;
394
- }
395
- processedArgs.push(arg);
396
- }
397
- let parsed;
398
- try {
399
- parsed = parseArgs({
400
- args: processedArgs,
401
- options: Object.keys(options).length > 0 ? options : void 0,
402
- allowPositionals: true,
403
- strict: false
404
- });
405
- } catch {
406
- parsed = {
407
- values: {},
408
- positionals: processedArgs
409
- };
410
- }
411
- const out = { _: [] };
412
- out._ = parsed.positionals;
413
- for (const [key, value] of Object.entries(parsed.values)) {
414
- let coerced = value;
415
- if (getType(key) === "boolean" && typeof value === "string") coerced = value !== "false";
416
- else if (isStringType(key) && typeof value === "boolean") coerced = "";
417
- out[key] = coerced;
418
- }
419
- for (const [name] of Object.entries(negatedFlags)) {
420
- out[name] = false;
421
- const mainName = aliasToMain.get(name);
422
- if (mainName) out[mainName] = false;
423
- const aliases = mainToAliases.get(name);
424
- if (aliases) for (const alias of aliases) out[alias] = false;
425
- }
426
- for (const [alias, main] of aliasToMain.entries()) {
427
- if (out[alias] !== void 0 && out[main] === void 0) out[main] = out[alias];
428
- if (out[main] !== void 0 && out[alias] === void 0) out[alias] = out[main];
429
- if (out[alias] !== out[main] && defaults[main] === out[main]) out[main] = out[alias];
430
- }
431
- return out;
432
- }
433
- const noColor = /* @__PURE__ */ (() => {
434
- const env = globalThis.process?.env ?? {};
435
- return env.NO_COLOR === "1" || env.TERM === "dumb" || env.TEST || env.CI;
436
- })();
437
- const _c = (c, r = 39) => (t) => noColor ? t : `\u001B[${c}m${t}\u001B[${r}m`;
438
- const bold = /* @__PURE__ */ _c(1, 22);
439
- const cyan = /* @__PURE__ */ _c(36);
440
- const gray = /* @__PURE__ */ _c(90);
441
- const underline = /* @__PURE__ */ _c(4, 24);
442
- function parseArgs$1(rawArgs, argsDef) {
443
- const parseOptions = {
444
- boolean: [],
445
- string: [],
446
- alias: {},
447
- default: {}
448
- };
449
- const args = resolveArgs(argsDef);
450
- for (const arg of args) {
451
- if (arg.type === "positional") continue;
452
- if (arg.type === "string" || arg.type === "enum") parseOptions.string.push(arg.name);
453
- else if (arg.type === "boolean") parseOptions.boolean.push(arg.name);
454
- if (arg.default !== void 0) parseOptions.default[arg.name] = arg.default;
455
- if (arg.alias) parseOptions.alias[arg.name] = arg.alias;
456
- const camelName = camelCase(arg.name);
457
- const kebabName = kebabCase(arg.name);
458
- if (camelName !== arg.name || kebabName !== arg.name) {
459
- const existingAliases = toArray(parseOptions.alias[arg.name] || []);
460
- if (camelName !== arg.name && !existingAliases.includes(camelName)) existingAliases.push(camelName);
461
- if (kebabName !== arg.name && !existingAliases.includes(kebabName)) existingAliases.push(kebabName);
462
- if (existingAliases.length > 0) parseOptions.alias[arg.name] = existingAliases;
463
- }
464
- }
465
- const parsed = parseRawArgs(rawArgs, parseOptions);
466
- const [ ...positionalArguments] = parsed._;
467
- const parsedArgsProxy = new Proxy(parsed, { get(target, prop) {
468
- return target[prop] ?? target[camelCase(prop)] ?? target[kebabCase(prop)];
469
- } });
470
- for (const [, arg] of args.entries()) if (arg.type === "positional") {
471
- const nextPositionalArgument = positionalArguments.shift();
472
- if (nextPositionalArgument !== void 0) parsedArgsProxy[arg.name] = nextPositionalArgument;
473
- else if (arg.default === void 0 && arg.required !== false) throw new CLIError(`Missing required positional argument: ${arg.name.toUpperCase()}`, "EARG");
474
- else parsedArgsProxy[arg.name] = arg.default;
475
- } else if (arg.type === "enum") {
476
- const argument = parsedArgsProxy[arg.name];
477
- const options = arg.options || [];
478
- if (argument !== void 0 && options.length > 0 && !options.includes(argument)) throw new CLIError(`Invalid value for argument: ${cyan(`--${arg.name}`)} (${cyan(argument)}). Expected one of: ${options.map((o) => cyan(o)).join(", ")}.`, "EARG");
479
- } else if (arg.required && parsedArgsProxy[arg.name] === void 0) throw new CLIError(`Missing required argument: --${arg.name}`, "EARG");
480
- return parsedArgsProxy;
481
- }
482
- function resolveArgs(argsDef) {
483
- const args = [];
484
- for (const [name, argDef] of Object.entries(argsDef || {})) args.push({
485
- ...argDef,
486
- name,
487
- alias: toArray(argDef.alias)
488
- });
489
- return args;
490
- }
491
- async function resolvePlugins(plugins) {
492
- return Promise.all(plugins.map((p) => resolveValue(p)));
493
- }
494
- function defineCommand(def) {
495
- return def;
496
- }
497
- async function runCommand(cmd, opts) {
498
- const cmdArgs = await resolveValue(cmd.args || {});
499
- const parsedArgs = parseArgs$1(opts.rawArgs, cmdArgs);
500
- const context = {
501
- rawArgs: opts.rawArgs,
502
- args: parsedArgs,
503
- data: opts.data,
504
- cmd
505
- };
506
- const plugins = await resolvePlugins(cmd.plugins ?? []);
507
- let result;
508
- let runError;
509
- try {
510
- for (const plugin of plugins) await plugin.setup?.(context);
511
- if (typeof cmd.setup === "function") await cmd.setup(context);
512
- const subCommands = await resolveValue(cmd.subCommands);
513
- if (subCommands && Object.keys(subCommands).length > 0) {
514
- const subCommandArgIndex = findSubCommandIndex(opts.rawArgs, cmdArgs);
515
- const explicitName = opts.rawArgs[subCommandArgIndex];
516
- if (explicitName) {
517
- const subCommand = await _findSubCommand(subCommands, explicitName);
518
- if (!subCommand) throw new CLIError(`Unknown command ${cyan(explicitName)}`, "E_UNKNOWN_COMMAND");
519
- await runCommand(subCommand, { rawArgs: opts.rawArgs.slice(subCommandArgIndex + 1) });
520
- } else {
521
- const defaultSubCommand = await resolveValue(cmd.default);
522
- if (defaultSubCommand) {
523
- if (cmd.run) throw new CLIError(`Cannot specify both 'run' and 'default' on the same command.`, "E_DEFAULT_CONFLICT");
524
- const subCommand = await _findSubCommand(subCommands, defaultSubCommand);
525
- if (!subCommand) throw new CLIError(`Default sub command ${cyan(defaultSubCommand)} not found in subCommands.`, "E_UNKNOWN_COMMAND");
526
- await runCommand(subCommand, { rawArgs: opts.rawArgs });
527
- } else if (!cmd.run) throw new CLIError(`No command specified.`, "E_NO_COMMAND");
528
- }
529
- }
530
- if (typeof cmd.run === "function") result = await cmd.run(context);
531
- } catch (error) {
532
- runError = error;
533
- }
534
- const cleanupErrors = [];
535
- if (typeof cmd.cleanup === "function") try {
536
- await cmd.cleanup(context);
537
- } catch (error) {
538
- cleanupErrors.push(error);
539
- }
540
- for (const plugin of [...plugins].reverse()) try {
541
- await plugin.cleanup?.(context);
542
- } catch (error) {
543
- cleanupErrors.push(error);
544
- }
545
- if (runError) throw runError;
546
- if (cleanupErrors.length === 1) throw cleanupErrors[0];
547
- if (cleanupErrors.length > 1) throw new Error("Multiple cleanup errors", { cause: cleanupErrors });
548
- return { result };
549
- }
550
- async function resolveSubCommand(cmd, rawArgs, parent) {
551
- const subCommands = await resolveValue(cmd.subCommands);
552
- if (subCommands && Object.keys(subCommands).length > 0) {
553
- const subCommandArgIndex = findSubCommandIndex(rawArgs, await resolveValue(cmd.args || {}));
554
- const subCommandName = rawArgs[subCommandArgIndex];
555
- const subCommand = await _findSubCommand(subCommands, subCommandName);
556
- if (subCommand) return resolveSubCommand(subCommand, rawArgs.slice(subCommandArgIndex + 1), cmd);
557
- }
558
- return [cmd, parent];
559
- }
560
- async function _findSubCommand(subCommands, name) {
561
- if (name in subCommands) return resolveValue(subCommands[name]);
562
- for (const sub of Object.values(subCommands)) {
563
- const resolved = await resolveValue(sub);
564
- const meta = await resolveValue(resolved?.meta);
565
- if (meta?.alias) {
566
- if (toArray(meta.alias).includes(name)) return resolved;
567
- }
568
- }
569
- }
570
- function findSubCommandIndex(rawArgs, argsDef) {
571
- for (let i = 0; i < rawArgs.length; i++) {
572
- const arg = rawArgs[i];
573
- if (arg === "--") return -1;
574
- if (arg.startsWith("-")) {
575
- if (!arg.includes("=") && _isValueFlag(arg, argsDef)) i++;
576
- continue;
577
- }
578
- return i;
579
- }
580
- return -1;
581
- }
582
- function _isValueFlag(flag, argsDef) {
583
- const name = flag.replace(/^-{1,2}/, "");
584
- const normalized = camelCase(name);
585
- for (const [key, def] of Object.entries(argsDef)) {
586
- if (def.type !== "string" && def.type !== "enum") continue;
587
- if (normalized === camelCase(key)) return true;
588
- if ((Array.isArray(def.alias) ? def.alias : def.alias ? [def.alias] : []).includes(name)) return true;
589
- }
590
- return false;
591
- }
592
- async function showUsage(cmd, parent) {
593
- try {
594
- console.log(await renderUsage(cmd, parent) + "\n");
595
- } catch (error) {
596
- console.error(error);
597
- }
598
- }
599
- const negativePrefixRe = /^no[-A-Z]/;
600
- async function renderUsage(cmd, parent) {
601
- const cmdMeta = await resolveValue(cmd.meta || {});
602
- const cmdArgs = resolveArgs(await resolveValue(cmd.args || {}));
603
- const parentMeta = await resolveValue(parent?.meta || {});
604
- const commandName = `${parentMeta.name ? `${parentMeta.name} ` : ""}` + (cmdMeta.name || process.argv[1]);
605
- const argLines = [];
606
- const posLines = [];
607
- const commandsLines = [];
608
- const usageLine = [];
609
- for (const arg of cmdArgs) if (arg.type === "positional") {
610
- const name = arg.name.toUpperCase();
611
- const isRequired = arg.required !== false && arg.default === void 0;
612
- posLines.push([cyan(name + renderValueHint(arg)), renderDescription(arg, isRequired)]);
613
- usageLine.push(isRequired ? `<${name}>` : `[${name}]`);
614
- } else {
615
- const isRequired = arg.required === true && arg.default === void 0;
616
- const argStr = [...(arg.alias || []).map((a) => `-${a}`), `--${arg.name}`].join(", ") + renderValueHint(arg);
617
- argLines.push([cyan(argStr), renderDescription(arg, isRequired)]);
618
- /**
619
- * print negative boolean arg variant usage when
620
- * - enabled by default or has `negativeDescription`
621
- * - not prefixed with `no-` or `no[A-Z]`
622
- */
623
- if (arg.type === "boolean" && (arg.default === true || arg.negativeDescription) && !negativePrefixRe.test(arg.name)) {
624
- const negativeArgStr = [...(arg.alias || []).map((a) => `--no-${a}`), `--no-${arg.name}`].join(", ");
625
- argLines.push([cyan(negativeArgStr), [arg.negativeDescription, isRequired ? gray("(Required)") : ""].filter(Boolean).join(" ")]);
626
- }
627
- if (isRequired) usageLine.push(`--${arg.name}` + renderValueHint(arg));
628
- }
629
- if (cmd.subCommands) {
630
- const commandNames = [];
631
- const subCommands = await resolveValue(cmd.subCommands);
632
- for (const [name, sub] of Object.entries(subCommands)) {
633
- const meta = await resolveValue((await resolveValue(sub))?.meta);
634
- if (meta?.hidden) continue;
635
- const aliases = toArray(meta?.alias);
636
- const label = [name, ...aliases].join(", ");
637
- commandsLines.push([cyan(label), meta?.description || ""]);
638
- commandNames.push(name, ...aliases);
639
- }
640
- usageLine.push(commandNames.join("|"));
641
- }
642
- const usageLines = [];
643
- const version = cmdMeta.version || parentMeta.version;
644
- usageLines.push(gray(`${cmdMeta.description} (${commandName + (version ? ` v${version}` : "")})`), "");
645
- const hasOptions = argLines.length > 0 || posLines.length > 0;
646
- usageLines.push(`${underline(bold("USAGE"))} ${cyan(`${commandName}${hasOptions ? " [OPTIONS]" : ""} ${usageLine.join(" ")}`)}`, "");
647
- if (posLines.length > 0) {
648
- usageLines.push(underline(bold("ARGUMENTS")), "");
649
- usageLines.push(formatLineColumns(posLines, " "));
650
- usageLines.push("");
651
- }
652
- if (argLines.length > 0) {
653
- usageLines.push(underline(bold("OPTIONS")), "");
654
- usageLines.push(formatLineColumns(argLines, " "));
655
- usageLines.push("");
656
- }
657
- if (commandsLines.length > 0) {
658
- usageLines.push(underline(bold("COMMANDS")), "");
659
- usageLines.push(formatLineColumns(commandsLines, " "));
660
- usageLines.push("", `Use ${cyan(`${commandName} <command> --help`)} for more information about a command.`);
661
- }
662
- return usageLines.filter((l) => typeof l === "string").join("\n");
663
- }
664
- function renderValueHint(arg) {
665
- const valueHint = arg.valueHint ? `=<${arg.valueHint}>` : "";
666
- const fallbackValueHint = valueHint || `=<${snakeCase(arg.name)}>`;
667
- if (!arg.type || arg.type === "positional" || arg.type === "boolean") return valueHint;
668
- if (arg.type === "enum" && arg.options?.length) return `=<${arg.options.join("|")}>`;
669
- return fallbackValueHint;
670
- }
671
- function renderDescription(arg, required) {
672
- const requiredHint = required ? gray("(Required)") : "";
673
- const defaultHint = arg.default === void 0 ? "" : gray(`(Default: ${arg.default})`);
674
- return [
675
- arg.description,
676
- requiredHint,
677
- defaultHint
678
- ].filter(Boolean).join(" ");
679
- }
680
- async function runMain(cmd, opts = {}) {
681
- const rawArgs = opts.rawArgs || process.argv.slice(2);
682
- const showUsage$1 = opts.showUsage || showUsage;
683
- try {
684
- const builtinFlags = await _resolveBuiltinFlags(cmd);
685
- if (builtinFlags.help.length > 0 && rawArgs.some((arg) => builtinFlags.help.includes(arg))) {
686
- await showUsage$1(...await resolveSubCommand(cmd, rawArgs));
687
- process.exit(0);
688
- } else if (rawArgs.length === 1 && builtinFlags.version.includes(rawArgs[0])) {
689
- const meta = typeof cmd.meta === "function" ? await cmd.meta() : await cmd.meta;
690
- if (!meta?.version) throw new CLIError("No version specified", "E_NO_VERSION");
691
- console.log(meta.version);
692
- } else await runCommand(cmd, { rawArgs });
693
- } catch (error) {
694
- if (error instanceof CLIError) {
695
- await showUsage$1(...await resolveSubCommand(cmd, rawArgs));
696
- console.error(error.message);
697
- } else console.error(error, "\n");
698
- process.exit(1);
699
- }
700
- }
701
- async function _resolveBuiltinFlags(cmd) {
702
- const argsDef = await resolveValue(cmd.args || {});
703
- const userNames = /* @__PURE__ */ new Set();
704
- const userAliases = /* @__PURE__ */ new Set();
705
- for (const [name, def] of Object.entries(argsDef)) {
706
- userNames.add(name);
707
- for (const alias of toArray(def.alias)) userAliases.add(alias);
708
- }
709
- return {
710
- help: _getBuiltinFlags("help", "h", userNames, userAliases),
711
- version: _getBuiltinFlags("version", "v", userNames, userAliases)
712
- };
713
- }
714
- function _getBuiltinFlags(long, short, userNames, userAliases) {
715
- if (userNames.has(long) || userAliases.has(long)) return [];
716
- if (userNames.has(short) || userAliases.has(short)) return [`--${long}`];
717
- return [`--${long}`, `-${short}`];
718
- }
719
- //#endregion
720
- //#region src/services/cli/commands/devicesCommand.ts
721
- const devicesCommand = defineCommand({
722
- args: {
723
- hostname: hostArgs.hostname,
724
- port: hostArgs.port,
725
- revoke: {
726
- default: "",
727
- description: "A paired page's id, to remove it",
728
- type: "string"
729
- }
730
- },
731
- meta: {
732
- description: "List the pages paired with this host, or remove one",
733
- name: "devices"
734
- },
735
- run: async ({ args }) => {
736
- installDownloadedHost();
737
- const stateDirectory = getStateDirectory();
738
- const isDone = await runDevicesCommand({
739
- hostKey: readHostKey(stateDirectory),
740
- hostname: getReachableHostname(args.hostname),
741
- port: Number(args.port),
742
- revokeDeviceId: args.revoke,
743
- stateDirectory
744
- }, writeLine);
745
- process.exit(isDone ? 0 : 1);
746
- }
747
- });
748
- //#endregion
749
- //#region src/services/drivers/claudeAgentSdk/getEventId.ts
750
- const getEventId = (sourceId, suffix) => `${sourceId}${ID_SEPARATOR}${suffix}`;
751
- //#endregion
752
- //#region src/services/drivers/claudeAgentSdk/toSessionClosedEvents.ts
753
- const toSessionClosedEvents = (message) => {
754
- const createdAt = /* @__PURE__ */ new Date();
755
- const closedId = getEventId(crypto.randomUUID(), "SessionState");
756
- return [...message ? [{
757
- createdAt,
758
- id: getEventId(closedId, "HostError"),
759
- message,
760
- type: "HostError"
761
- }] : [], {
762
- createdAt,
763
- id: closedId,
764
- state: "Closed",
765
- type: "SessionState"
766
- }];
767
- };
768
- //#endregion
769
- //#region src/services/drivers/claudeAgentSdk/closeOpenSession.ts
770
- const closeOpenSession = (sessionId, openSession, message, { emit, onSessionsChange, openSessionMap }) => {
771
- if (openSessionMap.get(sessionId) !== openSession) return;
772
- for (const pendingPermission of openSession.pendingPermissionMap.values()) pendingPermission.settle("Deny", "");
773
- emit(sessionId, toSessionClosedEvents(message));
774
- openSessionMap.delete(sessionId);
775
- onSessionsChange();
776
- };
777
- //#endregion
778
- //#region src/services/drivers/claudeAgentSdk/constants.ts
779
- const IGNORED_SDK_MESSAGE_TYPES = ["command_lifecycle"];
780
- const DEFAULT_DENY_MESSAGE = "The user denied this tool call.";
781
- const SETTING_SOURCES = [
782
- "user",
783
- "project",
784
- "local"
785
- ];
786
- const SESSION_LIST_LIMIT = 50;
787
- //#endregion
788
- //#region src/services/drivers/claudeAgentSdk/createInputQueue.ts
789
- const createInputQueue = () => {
790
- let items = [];
791
- let pendingReads = [];
792
- let isClosed = false;
793
- const next = () => {
794
- const [item, ...restItems] = items;
795
- if (item !== void 0) {
796
- items = restItems;
797
- return Promise.resolve({
798
- done: false,
799
- value: item
800
- });
801
- } else if (isClosed) return Promise.resolve({
802
- done: true,
803
- value: void 0
804
- });
805
- else return new Promise((resolve) => {
806
- pendingReads = [...pendingReads, resolve];
807
- });
808
- };
809
- return {
810
- close: () => {
811
- isClosed = true;
812
- const closedReads = pendingReads;
813
- pendingReads = [];
814
- for (const closedRead of closedReads) closedRead({
815
- done: true,
816
- value: void 0
817
- });
818
- },
819
- iterable: { [Symbol.asyncIterator]: () => ({ next }) },
820
- push: (item) => {
821
- const [pendingRead, ...restPendingReads] = pendingReads;
822
- if (pendingRead) {
823
- pendingReads = restPendingReads;
824
- pendingRead({
825
- done: false,
826
- value: item
827
- });
828
- } else items = [...items, item];
829
- }
830
- };
831
- };
832
- //#endregion
833
- //#region src/services/drivers/claudeAgentSdk/toPermissionRequestEvent.ts
834
- const toPermissionRequestEvent = (toolName, input, options, createdAt) => {
835
- const requestId = options.requestId ?? crypto.randomUUID();
836
- return {
837
- blockedPath: options.blockedPath ?? "",
838
- createdAt,
839
- decisionReason: options.decisionReason ?? "",
840
- hasSuggestions: Boolean(options.suggestions?.length),
841
- id: requestId,
842
- input,
843
- requestId,
844
- title: options.title ?? options.description ?? "",
845
- toolName,
846
- toolUseId: options.toolUseID ?? "",
847
- type: "PermissionRequest"
848
- };
849
- };
850
- //#endregion
851
- //#region src/services/drivers/claudeAgentSdk/toPermissionResult.ts
852
- const toPermissionResult = (input, suggestions, behavior, message) => {
853
- switch (behavior) {
854
- case "Allow": return {
855
- behavior: "allow",
856
- updatedInput: input
857
- };
858
- case "AllowAlways": return {
859
- behavior: "allow",
860
- updatedInput: input,
861
- updatedPermissions: suggestions
862
- };
863
- case "Deny": return {
864
- behavior: "deny",
865
- message: message || "The user denied this tool call."
866
- };
867
- default: return exhaustiveGuard(behavior);
868
- }
869
- };
870
- //#endregion
871
- //#region src/services/drivers/claudeAgentSdk/createPermissionBridge.ts
872
- const createPermissionBridge = (sessionId, pendingPermissionMap, emit) => (toolName, input, options) => new Promise((resolve) => {
873
- const requestEvent = toPermissionRequestEvent(toolName, input, options, /* @__PURE__ */ new Date());
874
- const { requestId } = requestEvent;
875
- const resolutionId = getEventId(requestId, "PermissionResolution");
876
- pendingPermissionMap.set(requestId, { settle: (behavior, message) => {
877
- if (!pendingPermissionMap.delete(requestId)) return;
878
- resolve(toPermissionResult(input, options.suggestions ?? [], behavior, message));
879
- const createdAt = /* @__PURE__ */ new Date();
880
- emit(sessionId, [{
881
- behavior,
882
- createdAt,
883
- id: resolutionId,
884
- requestId,
885
- type: "PermissionResolution"
886
- }, {
887
- createdAt,
888
- id: getEventId(resolutionId, "SessionState"),
889
- state: "Running",
890
- type: "SessionState"
891
- }]);
892
- } });
893
- options.signal.addEventListener("abort", () => {
894
- pendingPermissionMap.get(requestId)?.settle("Deny", "");
895
- }, { once: true });
896
- emit(sessionId, [requestEvent, {
897
- createdAt: requestEvent.createdAt,
898
- id: getEventId(requestId, "SessionState"),
899
- state: "RequiresAction",
900
- type: "SessionState"
901
- }]);
902
- });
903
- //#endregion
904
- //#region src/services/drivers/claudeAgentSdk/createFileOriginTracker.ts
905
- const fileEditResultSchema = z.object({
906
- filePath: z.string().min(1),
907
- originalFile: z.string().nullable(),
908
- type: z.enum(["create", "update"]).optional()
909
- });
910
- const createFileOriginTracker = () => {
911
- const changedFilePaths = /* @__PURE__ */ new Set();
912
- return { readToolResult: (event, toolUseResult) => {
913
- const fileEditResult = fileEditResultSchema.safeParse(toolUseResult);
914
- if (event.isError || !fileEditResult.success) return event;
915
- const { filePath, originalFile, type } = fileEditResult.data;
916
- if (changedFilePaths.has(filePath)) return event;
917
- changedFilePaths.add(filePath);
918
- const originalText = originalFile ?? (type === "create" ? "" : void 0);
919
- return originalText === void 0 ? event : {
920
- ...event,
921
- originalText
922
- };
923
- } };
924
- };
925
- //#endregion
926
- //#region src/services/drivers/claudeAgentSdk/createStreamTracker.ts
927
- const toTurnUsageEvent = (id, createdAt, outputTokens) => ({
928
- createdAt,
929
- id,
930
- outputTokens,
931
- type: "TurnUsage"
932
- });
933
- const createStreamTracker = () => {
934
- let messageId = "";
935
- let finishedOutputTokens = 0;
936
- return {
937
- readStreamEvent: ({ event, parent_tool_use_id, uuid }, createdAt) => {
938
- if (parent_tool_use_id) return [];
939
- switch (event.type) {
940
- case "content_block_delta":
941
- if (event.delta.type !== "text_delta" && event.delta.type !== "thinking_delta") return [];
942
- return [{
943
- blockId: getEventId(messageId, event.index),
944
- createdAt,
945
- id: uuid,
946
- isThinking: event.delta.type === "thinking_delta",
947
- text: event.delta.type === "text_delta" ? event.delta.text : event.delta.thinking,
948
- type: "StreamDelta"
949
- }];
950
- case "message_delta":
951
- finishedOutputTokens += event.usage.output_tokens;
952
- return [toTurnUsageEvent(uuid, createdAt, finishedOutputTokens)];
953
- case "message_start":
954
- ({id: messageId} = event.message);
955
- return [];
956
- default: return [];
957
- }
958
- },
959
- readThinkingTokens: ({ estimated_tokens, uuid }, createdAt) => [toTurnUsageEvent(uuid, createdAt, finishedOutputTokens + estimated_tokens)],
960
- reset: () => {
961
- finishedOutputTokens = 0;
962
- }
963
- };
964
- };
965
- //#endregion
966
- //#region src/models/claudeAgentSdk/TodoToolName.ts
967
- let TodoToolName = /* @__PURE__ */ function(TodoToolName) {
968
- TodoToolName["TaskCreate"] = "TaskCreate";
969
- TodoToolName["TaskUpdate"] = "TaskUpdate";
970
- TodoToolName["TodoWrite"] = "TodoWrite";
971
- return TodoToolName;
972
- }({});
973
- //#endregion
974
- //#region src/services/drivers/claudeAgentSdk/createTodoTracker.ts
975
- const todoWriteInputSchema = z.object({ todos: z.object({
976
- activeForm: z.string(),
977
- content: z.string(),
978
- status: todoStatusSchema
979
- }).array() });
980
- const taskCreateInputSchema = z.object({
981
- activeForm: z.string().optional(),
982
- subject: z.string()
983
- });
984
- const taskCreateResultSchema = z.object({ task: z.object({ id: z.string().min(1) }) });
985
- const taskUpdateInputSchema = z.object({
986
- activeForm: z.string().optional(),
987
- status: z.union([todoStatusSchema, z.literal("deleted")]).optional(),
988
- subject: z.string().optional(),
989
- taskId: z.string().min(1)
990
- });
991
- const createTodoTracker = () => {
992
- let todos = [];
993
- const pendingTodoMap = /* @__PURE__ */ new Map();
994
- const toTodoUpdateEvent = (sourceId, createdAt) => ({
995
- createdAt,
996
- id: getEventId(sourceId, "TodoUpdate"),
997
- todos,
998
- type: "TodoUpdate"
999
- });
1000
- return {
1001
- readToolResult: (event, toolUseResult) => {
1002
- const pendingTodo = pendingTodoMap.get(event.toolUseId);
1003
- if (!pendingTodo) return void 0;
1004
- pendingTodoMap.delete(event.toolUseId);
1005
- const taskCreateResult = taskCreateResultSchema.safeParse(toolUseResult);
1006
- if (!taskCreateResult.success || event.isError) return void 0;
1007
- todos = [...todos, {
1008
- ...pendingTodo,
1009
- id: taskCreateResult.data.task.id
1010
- }];
1011
- return toTodoUpdateEvent(event.id, event.createdAt);
1012
- },
1013
- readToolUse: (event) => {
1014
- if (event.parentToolUseId) return void 0;
1015
- switch (event.name) {
1016
- case "TaskCreate": {
1017
- const input = taskCreateInputSchema.safeParse(event.input);
1018
- if (!input.success) return void 0;
1019
- const { activeForm, subject } = input.data;
1020
- pendingTodoMap.set(event.toolUseId, {
1021
- activeForm: activeForm ?? subject,
1022
- content: subject,
1023
- status: "pending"
1024
- });
1025
- return;
1026
- }
1027
- case "TaskUpdate": {
1028
- const input = taskUpdateInputSchema.safeParse(event.input);
1029
- if (!input.success) return void 0;
1030
- const { activeForm, status, subject, taskId } = input.data;
1031
- todos = status === "deleted" ? todos.filter(({ id }) => id !== taskId) : todos.map((todo) => todo.id === taskId ? {
1032
- activeForm: activeForm ?? todo.activeForm,
1033
- content: subject ?? todo.content,
1034
- id: todo.id,
1035
- status: status ?? todo.status
1036
- } : todo);
1037
- return toTodoUpdateEvent(event.id, event.createdAt);
1038
- }
1039
- case "TodoWrite": {
1040
- const input = todoWriteInputSchema.safeParse(event.input);
1041
- if (!input.success) return void 0;
1042
- todos = input.data.todos.map(({ activeForm, content, status }, index) => ({
1043
- activeForm,
1044
- content,
1045
- id: String(index),
1046
- status
1047
- }));
1048
- return toTodoUpdateEvent(event.id, event.createdAt);
1049
- }
1050
- default: return;
1051
- }
1052
- }
1053
- };
1054
- };
1055
- //#endregion
1056
- //#region src/services/drivers/claudeAgentSdk/toUnknownEvent.ts
1057
- const toUnknownEvent = (id, sdkType, raw, createdAt) => ({
1058
- createdAt,
1059
- id,
1060
- raw,
1061
- sdkType,
1062
- type: "Unknown"
1063
- });
1064
- //#endregion
1065
- //#region src/services/drivers/claudeAgentSdk/mapAssistantContent.ts
1066
- const mapAssistantContent = (content, context) => content.map((block, index) => {
1067
- const id = getEventId(context.messageUuid, index);
1068
- switch (block.type) {
1069
- case "document":
1070
- case "image":
1071
- case "tool_result": return toUnknownEvent(id, block.type, JSON.stringify(block), context.createdAt);
1072
- case "other": return toUnknownEvent(id, block.blockType, block.raw, context.createdAt);
1073
- case "text": return {
1074
- ...context,
1075
- id,
1076
- text: block.text,
1077
- type: "AssistantMessage"
1078
- };
1079
- case "thinking": return {
1080
- ...context,
1081
- id,
1082
- thinking: block.thinking,
1083
- type: "Thinking"
1084
- };
1085
- case "tool_use": return {
1086
- ...context,
1087
- id,
1088
- input: block.input,
1089
- name: block.name,
1090
- toolUseId: block.id,
1091
- type: "ToolUse"
1092
- };
1093
- default: return exhaustiveGuard(block);
1094
- }
1095
- });
1096
- //#endregion
1097
- //#region src/services/drivers/claudeAgentSdk/mapHookMessage.ts
1098
- const HookPhaseMap = {
1099
- hook_progress: "Progress",
1100
- hook_response: "Response",
1101
- hook_started: "Started"
1102
- };
1103
- const mapHookMessage = (message, createdAt) => ({
1104
- createdAt,
1105
- exitCode: "exit_code" in message ? message.exit_code ?? 0 : 0,
1106
- hookEvent: message.hook_event,
1107
- hookId: message.hook_id,
1108
- hookName: message.hook_name,
1109
- id: message.uuid,
1110
- outcome: "outcome" in message ? message.outcome : "",
1111
- output: "output" in message ? message.output : "",
1112
- phase: HookPhaseMap[message.subtype],
1113
- stderr: "stderr" in message ? message.stderr : "",
1114
- stdout: "stdout" in message ? message.stdout : "",
1115
- type: "Hook"
1116
- });
1117
- //#endregion
1118
- //#region src/services/drivers/claudeAgentSdk/mapTaskMessage.ts
1119
- const SubagentStatusMap = {
1120
- completed: "Completed",
1121
- failed: "Failed",
1122
- killed: "Stopped",
1123
- paused: "Running",
1124
- pending: "Started",
1125
- running: "Running",
1126
- stopped: "Stopped"
1127
- };
1128
- const mapTaskMessage = (message, createdAt) => {
1129
- const base = {
1130
- createdAt,
1131
- description: "",
1132
- durationMs: 0,
1133
- id: message.uuid,
1134
- lastToolName: "",
1135
- subagentType: "",
1136
- summary: "",
1137
- taskId: message.task_id,
1138
- toolUseId: "tool_use_id" in message ? message.tool_use_id ?? "" : "",
1139
- toolUses: 0,
1140
- totalTokens: 0,
1141
- type: "Subagent"
1142
- };
1143
- switch (message.subtype) {
1144
- case "task_notification": return {
1145
- ...base,
1146
- durationMs: message.usage?.duration_ms ?? 0,
1147
- status: SubagentStatusMap[message.status],
1148
- summary: message.summary,
1149
- toolUses: message.usage?.tool_uses ?? 0,
1150
- totalTokens: message.usage?.total_tokens ?? 0
1151
- };
1152
- case "task_progress": return {
1153
- ...base,
1154
- description: message.description,
1155
- durationMs: message.usage.duration_ms,
1156
- lastToolName: message.last_tool_name ?? "",
1157
- status: "Running",
1158
- subagentType: message.subagent_type ?? "",
1159
- summary: message.summary ?? "",
1160
- toolUses: message.usage.tool_uses,
1161
- totalTokens: message.usage.total_tokens
1162
- };
1163
- case "task_started": return {
1164
- ...base,
1165
- description: message.description,
1166
- status: "Started",
1167
- subagentType: message.subagent_type ?? ""
1168
- };
1169
- case "task_updated": return {
1170
- ...base,
1171
- description: message.patch.description ?? "",
1172
- status: message.patch.status ? SubagentStatusMap[message.patch.status] : "Running",
1173
- summary: message.patch.error ?? ""
1174
- };
1175
- default: return exhaustiveGuard(message);
1176
- }
1177
- };
1178
- //#endregion
1179
- //#region src/services/drivers/claudeAgentSdk/mapSystemMessage.ts
1180
- const SessionStateMap = {
1181
- idle: "Idle",
1182
- requires_action: "RequiresAction",
1183
- running: "Running"
1184
- };
1185
- const mapSystemMessage = (message, createdAt) => {
1186
- switch (message.subtype) {
1187
- case "compact_boundary": return [{
1188
- createdAt,
1189
- id: message.uuid,
1190
- postTokens: message.compact_metadata.post_tokens ?? 0,
1191
- preTokens: message.compact_metadata.pre_tokens,
1192
- trigger: message.compact_metadata.trigger === "auto" ? "auto" : "manual",
1193
- type: "Compaction"
1194
- }];
1195
- case "hook_progress":
1196
- case "hook_response":
1197
- case "hook_started": return [mapHookMessage(message, createdAt)];
1198
- case "local_command_output": return [{
1199
- content: message.content,
1200
- createdAt,
1201
- id: message.uuid,
1202
- type: "CommandOutput"
1203
- }];
1204
- case "session_state_changed": return [{
1205
- createdAt,
1206
- id: message.uuid,
1207
- state: SessionStateMap[message.state],
1208
- type: "SessionState"
1209
- }];
1210
- case "task_notification":
1211
- case "task_progress":
1212
- case "task_started":
1213
- case "task_updated": return [mapTaskMessage(message, createdAt)];
1214
- default: return [toUnknownEvent(message.uuid, `system:${message.subtype}`, JSON.stringify(message), createdAt)];
1215
- }
1216
- };
1217
- //#endregion
1218
- //#region src/services/drivers/claudeAgentSdk/toToolResultText.ts
1219
- const toToolResultText = (content) => typeof content === "string" ? content : content.map((part) => part.text === void 0 ? `[${part.type}]` : part.text).join("\n");
1220
- //#endregion
1221
- //#region src/services/drivers/claudeAgentSdk/mapUserContent.ts
1222
- const mapUserContent = (content, context) => {
1223
- if (typeof content === "string") return [{
1224
- ...context,
1225
- attachmentCount: 0,
1226
- id: context.messageUuid,
1227
- text: content,
1228
- type: "UserMessage"
1229
- }];
1230
- const events = [];
1231
- const texts = [];
1232
- let attachmentCount = 0;
1233
- for (const [index, block] of content.entries()) {
1234
- const id = getEventId(context.messageUuid, index);
1235
- switch (block.type) {
1236
- case "document":
1237
- case "image":
1238
- attachmentCount++;
1239
- break;
1240
- case "other":
1241
- events.push(toUnknownEvent(id, block.blockType, block.raw, context.createdAt));
1242
- break;
1243
- case "text":
1244
- texts.push(block.text);
1245
- break;
1246
- case "thinking":
1247
- case "tool_use":
1248
- events.push(toUnknownEvent(id, block.type, JSON.stringify(block), context.createdAt));
1249
- break;
1250
- case "tool_result":
1251
- events.push({
1252
- content: toToolResultText(block.content),
1253
- createdAt: context.createdAt,
1254
- id,
1255
- isError: Boolean(block.is_error),
1256
- parentToolUseId: context.parentToolUseId,
1257
- toolUseId: block.tool_use_id,
1258
- type: "ToolResult"
1259
- });
1260
- break;
1261
- default: exhaustiveGuard(block);
1262
- }
1263
- }
1264
- return texts.length > 0 || attachmentCount > 0 ? [{
1265
- ...context,
1266
- attachmentCount,
1267
- id: context.messageUuid,
1268
- text: texts.join("\n\n"),
1269
- type: "UserMessage"
1270
- }, ...events] : events;
1271
- };
1272
- //#endregion
1273
- //#region src/services/drivers/claudeAgentSdk/parseMessageBody.ts
1274
- const documentBlockSchema = z.object({ type: z.literal("document") });
1275
- const imageBlockSchema = z.object({ type: z.literal("image") });
1276
- const textBlockSchema = z.object({
1277
- text: z.string(),
1278
- type: z.literal("text")
1279
- });
1280
- const thinkingBlockSchema = z.object({
1281
- thinking: z.string(),
1282
- type: z.literal("thinking")
1283
- });
1284
- const toolResultBlockSchema = z.object({
1285
- content: z.union([z.string(), z.looseObject({
1286
- text: z.string().optional(),
1287
- type: z.string()
1288
- }).array()]),
1289
- is_error: z.boolean().optional(),
1290
- tool_use_id: z.string().min(1),
1291
- type: z.literal("tool_result")
1292
- });
1293
- const toolUseBlockSchema = z.object({
1294
- id: z.string().min(1),
1295
- input: z.record(z.string(), z.unknown()),
1296
- name: z.string().min(1),
1297
- type: z.literal("tool_use")
1298
- });
1299
- const otherBlockSchema = z.looseObject({ type: z.string() }).transform((block) => ({
1300
- blockType: block.type,
1301
- raw: JSON.stringify(block),
1302
- type: "other"
1303
- }));
1304
- const contentBlockSchema = z.union([
1305
- documentBlockSchema,
1306
- imageBlockSchema,
1307
- textBlockSchema,
1308
- thinkingBlockSchema,
1309
- toolResultBlockSchema,
1310
- toolUseBlockSchema,
1311
- otherBlockSchema
1312
- ]);
1313
- const messageBodySchema = z.looseObject({ content: z.union([contentBlockSchema.array(), z.string()]) });
1314
- const parseMessageBody = (value) => {
1315
- const result = messageBodySchema.safeParse(value);
1316
- return result.success ? result.data : void 0;
1317
- };
1318
- //#endregion
1319
- //#region src/services/drivers/claudeAgentSdk/toSessionInitEvent.ts
1320
- const toSessionInitEvent = (message, permissionMode, createdAt) => ({
1321
- agents: [...new Set(message.agents)],
1322
- claudeCodeVersion: message.claude_code_version,
1323
- createdAt,
1324
- cwd: message.cwd,
1325
- id: message.uuid,
1326
- mcpServers: [...new Map(message.mcp_servers.map(({ name, status }) => [name, {
1327
- name,
1328
- status
1329
- }])).values()],
1330
- model: message.model,
1331
- outputStyle: message.output_style,
1332
- permissionMode,
1333
- plugins: [...new Set(message.plugins.map(({ name }) => name))],
1334
- skills: [...new Set(message.skills)],
1335
- slashCommands: [...new Set(message.slash_commands)],
1336
- tools: [...new Set(message.tools)],
1337
- type: "SessionInit"
1338
- });
1339
- //#endregion
1340
- //#region src/services/drivers/claudeAgentSdk/toTurnResultEvents.ts
1341
- const toTurnResultEvents = (message, createdAt) => [{
1342
- createdAt,
1343
- durationApiMs: message.duration_api_ms,
1344
- durationMs: message.duration_ms,
1345
- errors: message.subtype === "success" ? [] : message.errors,
1346
- id: message.uuid,
1347
- isError: message.is_error,
1348
- numTurns: message.num_turns,
1349
- result: message.subtype === "success" ? message.result : "",
1350
- subtype: message.subtype,
1351
- totalCostUsd: message.total_cost_usd,
1352
- type: "TurnResult",
1353
- usage: {
1354
- cacheCreationInputTokens: message.usage.cache_creation_input_tokens,
1355
- cacheReadInputTokens: message.usage.cache_read_input_tokens,
1356
- inputTokens: message.usage.input_tokens,
1357
- outputTokens: message.usage.output_tokens
1358
- }
1359
- }, {
1360
- createdAt,
1361
- id: getEventId(message.uuid, "SessionState"),
1362
- state: "Idle",
1363
- type: "SessionState"
1364
- }];
1365
- //#endregion
1366
- //#region src/services/drivers/claudeAgentSdk/createSdkMessageMapper.ts
1367
- const createSdkMessageMapper = () => {
1368
- const fileOriginTracker = createFileOriginTracker();
1369
- const streamTracker = createStreamTracker();
1370
- const todoTracker = createTodoTracker();
1371
- const settings = {
1372
- model: "",
1373
- permissionMode: "default"
1374
- };
1375
- let isCompacting = false;
1376
- const updateSettings = (id, createdAt, { model, permissionMode }) => {
1377
- if (model) settings.model = model;
1378
- const parsedPermissionMode = permissionModeSchema.safeParse(permissionMode);
1379
- if (parsedPermissionMode.success) settings.permissionMode = parsedPermissionMode.data;
1380
- return {
1381
- createdAt,
1382
- id,
1383
- ...settings,
1384
- type: "SessionSettings"
1385
- };
1386
- };
1387
- const mapContent = ({ body, error, messageType, messageUuid, parentToolUseId, toolUseResult }, createdAt) => {
1388
- const messageBody = parseMessageBody(body);
1389
- if (!messageBody) return [toUnknownEvent(messageUuid, messageType, JSON.stringify(body), createdAt)];
1390
- const context = {
1391
- createdAt,
1392
- messageUuid,
1393
- parentToolUseId
1394
- };
1395
- const { content } = messageBody;
1396
- const contentEvents = messageType === "assistant" ? mapAssistantContent(typeof content === "string" ? [{
1397
- text: content,
1398
- type: "text"
1399
- }] : content, context) : mapUserContent(content, context);
1400
- const events = [];
1401
- for (const event of contentEvents) if (event.type === "ToolUse") {
1402
- events.push(event);
1403
- const todoUpdateEvent = todoTracker.readToolUse(event);
1404
- if (todoUpdateEvent) events.push(todoUpdateEvent);
1405
- } else if (event.type === "ToolResult") {
1406
- events.push(fileOriginTracker.readToolResult(event, toolUseResult));
1407
- const todoUpdateEvent = todoTracker.readToolResult(event, toolUseResult);
1408
- if (todoUpdateEvent) events.push(todoUpdateEvent);
1409
- } else events.push(event);
1410
- if (error) events.push({
1411
- createdAt,
1412
- id: getEventId(messageUuid, "HostError"),
1413
- message: error,
1414
- type: "HostError"
1415
- });
1416
- return events;
1417
- };
1418
- const mapMessage = (message, createdAt) => {
1419
- const sdkType = message.type;
1420
- if (IGNORED_SDK_MESSAGE_TYPES.includes(sdkType)) return [];
1421
- switch (message.type) {
1422
- case "assistant": return mapContent({
1423
- body: message.message,
1424
- error: message.error ?? "",
1425
- messageType: message.type,
1426
- messageUuid: message.uuid,
1427
- parentToolUseId: message.parent_tool_use_id ?? "",
1428
- toolUseResult: void 0
1429
- }, createdAt);
1430
- case "rate_limit_event": {
1431
- const { rateLimitType, resetsAt, status, utilization } = message.rate_limit_info;
1432
- return [{
1433
- createdAt,
1434
- id: message.uuid,
1435
- rateLimitType: rateLimitType ?? "",
1436
- resetsAt: resetsAt === void 0 ? void 0 : new Date(Temporal.Duration.from({ seconds: resetsAt }).total("milliseconds")),
1437
- status,
1438
- type: "RateLimit",
1439
- utilization
1440
- }];
1441
- }
1442
- case "result":
1443
- streamTracker.reset();
1444
- return toTurnResultEvents(message, createdAt);
1445
- case "stream_event": return streamTracker.readStreamEvent(message, createdAt);
1446
- case "tool_progress": return [{
1447
- createdAt,
1448
- elapsedSeconds: message.elapsed_time_seconds,
1449
- id: message.uuid,
1450
- parentToolUseId: message.parent_tool_use_id ?? "",
1451
- toolUseId: message.tool_use_id,
1452
- type: "ToolProgress"
1453
- }];
1454
- case "user": return mapContent({
1455
- body: message.message,
1456
- error: "",
1457
- messageType: message.type,
1458
- messageUuid: message.uuid ?? crypto.randomUUID(),
1459
- parentToolUseId: message.parent_tool_use_id ?? "",
1460
- toolUseResult: message.tool_use_result
1461
- }, createdAt);
1462
- case "system": if (message.subtype === "init") {
1463
- const settingsEvent = updateSettings(getEventId(message.uuid, "SessionSettings"), createdAt, {
1464
- model: message.model,
1465
- permissionMode: message.permissionMode
1466
- });
1467
- return [toSessionInitEvent(message, settingsEvent.permissionMode, createdAt), settingsEvent];
1468
- } else if (message.subtype === "status") {
1469
- const wasCompacting = isCompacting;
1470
- isCompacting = message.status === "compacting";
1471
- const events = [];
1472
- if (isCompacting !== wasCompacting) events.push({
1473
- createdAt,
1474
- id: message.uuid,
1475
- state: isCompacting ? "Compacting" : "Running",
1476
- type: "SessionState"
1477
- });
1478
- if (message.permissionMode) events.push(updateSettings(getEventId(message.uuid, "SessionSettings"), createdAt, { permissionMode: message.permissionMode }));
1479
- return events;
1480
- } else if (message.subtype === "thinking_tokens") return streamTracker.readThinkingTokens(message, createdAt);
1481
- else return mapSystemMessage(message, createdAt);
1482
- default: return [toUnknownEvent(message.uuid, message.type, JSON.stringify(message), createdAt)];
1483
- }
1484
- };
1485
- return {
1486
- mapHistory: ({ message, parent_tool_use_id, type, uuid }, createdAt, toolUseResult) => type === "system" ? [toUnknownEvent(uuid, type, JSON.stringify(message), createdAt)] : mapContent({
1487
- body: message,
1488
- error: "",
1489
- messageType: type,
1490
- messageUuid: uuid,
1491
- parentToolUseId: parent_tool_use_id ?? "",
1492
- toolUseResult
1493
- }, createdAt),
1494
- mapMessage,
1495
- updateSettings
1496
- };
1497
- };
1498
- //#endregion
1499
- //#region src/services/drivers/claudeAgentSdk/readSessionHistory.ts
1500
- const readSessionHistory = async (sessionId, cwd, resumeAt) => {
1501
- const sessionMessages = await getSessionMessages(sessionId, { dir: cwd });
1502
- if (!resumeAt) return sessionMessages;
1503
- const resumeAtIndex = sessionMessages.findIndex(({ uuid }) => uuid === resumeAt);
1504
- if (resumeAtIndex === -1) throw new InvalidOperationError(Operation.Read, sessionId, `the transcript holds no message ${resumeAt}`);
1505
- return sessionMessages.slice(0, resumeAtIndex + 1);
1506
- };
1507
- //#endregion
1508
- //#region src/services/drivers/claudeAgentSdk/readToolUseResultMap.ts
1509
- const transcriptEntrySchema = z.object({
1510
- toolUseResult: z.unknown(),
1511
- uuid: z.string()
1512
- });
1513
- const readToolUseResultMap = async (sessionId) => {
1514
- const toolUseResultMap = /* @__PURE__ */ new Map();
1515
- const projectsDirectory = join(process.env.CLAUDE_CONFIG_DIR ?? join(homedir(), ".claude"), "projects");
1516
- if (!existsSync(projectsDirectory)) return toolUseResultMap;
1517
- const transcriptPath = (await readdir(projectsDirectory)).map((projectDirectory) => join(projectsDirectory, projectDirectory, `${sessionId}.jsonl`)).find((path) => existsSync(path));
1518
- if (!transcriptPath) return toolUseResultMap;
1519
- for (const line of (await readFile(transcriptPath, "utf8")).split("\n")) getResult(() => transcriptEntrySchema.parse(JSON.parse(line))).match(({ toolUseResult, uuid }) => {
1520
- if (toolUseResult !== void 0) toolUseResultMap.set(uuid, toolUseResult);
1521
- }, noop);
1522
- return toolUseResultMap;
1523
- };
1524
- //#endregion
1525
- //#region src/services/drivers/claudeAgentSdk/getSessionTitle.ts
1526
- const getSessionTitle = ({ customTitle, firstPrompt, summary }) => customTitle || summary || firstPrompt || "";
1527
- //#endregion
1528
- //#region src/services/drivers/claudeAgentSdk/toCapabilitiesEvent.ts
1529
- const toCapabilitiesEvent = (id, slashCommands, models, createdAt) => ({
1530
- commands: [...new Map(slashCommands.map(({ argumentHint, description, name }) => [name, {
1531
- argumentHint,
1532
- description,
1533
- name
1534
- }])).values()],
1535
- createdAt,
1536
- id,
1537
- models: [...new Map(models.map(({ description, displayName, value }) => [value, {
1538
- description,
1539
- displayName,
1540
- value
1541
- }])).values()],
1542
- type: "Capabilities"
1543
- });
1544
- //#endregion
1545
- //#region src/services/drivers/claudeAgentSdk/toContextUsageEvent.ts
1546
- const toContextUsageEvent = (id, { autoCompactThreshold, isAutoCompactEnabled, maxTokens, percentage, totalTokens }, createdAt) => ({
1547
- autoCompactThreshold: autoCompactThreshold ?? 0,
1548
- createdAt,
1549
- id,
1550
- isAutoCompactEnabled,
1551
- maxTokens,
1552
- percentage,
1553
- totalTokens,
1554
- type: "ContextUsage"
1555
- });
1556
- //#endregion
1557
- //#region src/services/drivers/claudeAgentSdk/watchSession.ts
1558
- const watchSession = async (sessionId, openSession, { emit, onSessionsChange, openSessionMap }) => {
1559
- const { cwd, mapper, query } = openSession;
1560
- const readContextUsage = () => getResultAsync(() => query.getContextUsage()).match((contextUsage) => {
1561
- emit(sessionId, [toContextUsageEvent(crypto.randomUUID(), contextUsage, /* @__PURE__ */ new Date())]);
1562
- }, console.error);
1563
- const readTitle = () => getResultAsync(() => getSessionInfo(sessionId, { dir: cwd })).match((sessionInfo) => {
1564
- if (!sessionInfo) return;
1565
- openSession.title = getSessionTitle(sessionInfo) || openSession.title;
1566
- onSessionsChange();
1567
- }, console.error);
1568
- await Promise.all([
1569
- getResultAsync(() => Promise.all([query.supportedCommands(), query.supportedModels()])).match(([slashCommands, models]) => {
1570
- emit(sessionId, [toCapabilitiesEvent(crypto.randomUUID(), slashCommands, models, /* @__PURE__ */ new Date())]);
1571
- }, console.error),
1572
- readContextUsage(),
1573
- getResultAsync(async () => {
1574
- for await (const message of query) {
1575
- emit(sessionId, mapper.mapMessage(message, /* @__PURE__ */ new Date()));
1576
- if (message.type === "result") await Promise.all([readContextUsage(), readTitle()]);
1577
- }
1578
- }).match(() => {
1579
- closeOpenSession(sessionId, openSession, "", {
1580
- emit,
1581
- onSessionsChange,
1582
- openSessionMap
1583
- });
1584
- }, (error) => {
1585
- closeOpenSession(sessionId, openSession, error.message, {
1586
- emit,
1587
- onSessionsChange,
1588
- openSessionMap
1589
- });
1590
- })
1591
- ]);
1592
- };
1593
- //#endregion
1594
- //#region src/services/installer/getClaudeCodeExecutablePath.ts
1595
- const getClaudeCodeExecutablePath = () => isSea() ? join(getHostInstallDirectory(), CLAUDE_CODE_EXECUTABLE_FILENAME) : void 0;
1596
- //#endregion
1597
- //#region src/services/drivers/claudeAgentSdk/createSessionOpener.ts
1598
- const createSessionOpener = ({ emit, onSessionOpen, onSessionsChange, openSessionMap, taskRegistry }) => async ({ cwd, isFork, resumeAt, resumeFrom, sessionId }) => {
1599
- const mapper = createSdkMessageMapper();
1600
- const history = resumeFrom ? await readSessionHistory(resumeFrom, cwd, resumeAt) : [];
1601
- const toolUseResultMap = resumeFrom ? await readToolUseResultMap(resumeFrom) : /* @__PURE__ */ new Map();
1602
- const input = createInputQueue();
1603
- const pendingPermissionMap = /* @__PURE__ */ new Map();
1604
- const claudeCodeExecutablePath = getClaudeCodeExecutablePath();
1605
- const sessionQuery = query({
1606
- options: {
1607
- allowDangerouslySkipPermissions: true,
1608
- canUseTool: createPermissionBridge(sessionId, pendingPermissionMap, emit),
1609
- cwd,
1610
- enableFileCheckpointing: true,
1611
- env: {
1612
- ...process.env,
1613
- CLAUDE_CODE_ENABLE_TODO_TOOLS: "true"
1614
- },
1615
- includeHookEvents: true,
1616
- includePartialMessages: true,
1617
- ...claudeCodeExecutablePath ? { pathToClaudeCodeExecutable: claudeCodeExecutablePath } : {},
1618
- settingSources: SETTING_SOURCES,
1619
- systemPrompt: {
1620
- preset: "claude_code",
1621
- type: "preset"
1622
- },
1623
- thinking: {
1624
- display: "summarized",
1625
- type: "adaptive"
1626
- },
1627
- ...resumeFrom ? { resume: resumeFrom } : {},
1628
- ...resumeAt ? { resumeSessionAt: resumeAt } : {},
1629
- ...!resumeFrom || isFork ? {
1630
- forkSession: isFork,
1631
- sessionId
1632
- } : {}
1633
- },
1634
- prompt: input.iterable
1635
- });
1636
- const openSession = {
1637
- cwd,
1638
- input,
1639
- lastActivityAt: /* @__PURE__ */ new Date(),
1640
- mapper,
1641
- pendingPermissionMap,
1642
- query: sessionQuery,
1643
- state: "Idle",
1644
- title: ""
1645
- };
1646
- onSessionOpen(sessionId);
1647
- openSessionMap.set(sessionId, openSession);
1648
- const createdAt = /* @__PURE__ */ new Date();
1649
- emit(sessionId, [...history.flatMap((sessionMessage) => mapper.mapHistory(sessionMessage, createdAt, toolUseResultMap.get(sessionMessage.uuid))), {
1650
- createdAt,
1651
- id: getEventId(crypto.randomUUID(), "SessionState"),
1652
- state: "Idle",
1653
- type: "SessionState"
1654
- }]);
1655
- onSessionsChange();
1656
- taskRegistry.run(() => watchSession(sessionId, openSession, {
1657
- emit,
1658
- onSessionsChange,
1659
- openSessionMap
1660
- }));
1661
- return sessionId;
1662
- };
1663
- //#endregion
1664
- //#region src/services/drivers/claudeAgentSdk/listSessionSummaries.ts
1665
- const listSessionSummaries = async (openSessionMap) => {
1666
- const sessionInfos = await listSessions({ limit: 50 });
1667
- const savedSessionIds = new Set(sessionInfos.map(({ sessionId }) => sessionId));
1668
- const unsavedSessions = [...openSessionMap].filter(([id]) => !savedSessionIds.has(id)).map(([id, { cwd, lastActivityAt, state, title }]) => ({
1669
- cwd,
1670
- id,
1671
- lastActivityAt,
1672
- state,
1673
- title
1674
- }));
1675
- const savedSessions = sessionInfos.map((sessionInfo) => {
1676
- const openSession = openSessionMap.get(sessionInfo.sessionId);
1677
- return {
1678
- cwd: sessionInfo.cwd ?? "",
1679
- id: sessionInfo.sessionId,
1680
- lastActivityAt: openSession?.lastActivityAt ?? new Date(sessionInfo.lastModified),
1681
- state: openSession?.state ?? "Closed",
1682
- title: openSession?.title || getSessionTitle(sessionInfo)
1683
- };
1684
- });
1685
- return [...unsavedSessions, ...savedSessions];
1686
- };
1687
- //#endregion
1688
- //#region src/services/drivers/claudeAgentSdk/readSessionCwd.ts
1689
- const readSessionCwd = async (sessionId) => {
1690
- const sessionInfo = await getSessionInfo(sessionId);
1691
- if (!sessionInfo?.cwd) throw new InvalidOperationError(Operation.Read, sessionId, "no session with this id on disk");
1692
- return sessionInfo.cwd;
1693
- };
1694
- //#endregion
1695
- //#region src/services/drivers/claudeAgentSdk/toContentBlockParam.ts
1696
- const toContentBlockParam = ({ data, mediaType, name }) => {
1697
- switch (mediaType) {
1698
- case "image/gif":
1699
- case "image/jpeg":
1700
- case "image/png":
1701
- case "image/webp": return {
1702
- source: {
1703
- data,
1704
- media_type: mediaType,
1705
- type: "base64"
1706
- },
1707
- type: "image"
1708
- };
1709
- case "application/pdf": return {
1710
- source: {
1711
- data,
1712
- media_type: mediaType,
1713
- type: "base64"
1714
- },
1715
- title: name,
1716
- type: "document"
1717
- };
1718
- case "text/plain": return {
1719
- source: {
1720
- data,
1721
- media_type: mediaType,
1722
- type: "text"
1723
- },
1724
- title: name,
1725
- type: "document"
1726
- };
1727
- default: return exhaustiveGuard(mediaType);
1728
- }
1729
- };
1730
- //#endregion
1731
- //#region src/services/shared/createTaskRegistry.ts
1732
- const createTaskRegistry = () => {
1733
- const pendingSet = /* @__PURE__ */ new Set();
1734
- return {
1735
- drain: async () => {
1736
- await Promise.all(pendingSet);
1737
- },
1738
- run: (task) => {
1739
- const pending = task().finally(() => {
1740
- pendingSet.delete(pending);
1741
- });
1742
- pendingSet.add(pending);
1743
- }
1744
- };
1745
- };
1746
- //#endregion
1747
- //#region src/services/drivers/claudeAgentSdk/createClaudeAgentSdkDriver.ts
1748
- const createClaudeAgentSdkDriver = ({ onEvents, onSessionOpen, onSessionsChange }) => {
1749
- const openSessionMap = /* @__PURE__ */ new Map();
1750
- const taskRegistry = createTaskRegistry();
1751
- const emit = (sessionId, events) => {
1752
- const openSession = openSessionMap.get(sessionId);
1753
- let isStateChanged = false;
1754
- if (openSession) {
1755
- openSession.lastActivityAt = /* @__PURE__ */ new Date();
1756
- for (const event of events) if (event.type === "SessionState" && event.state !== openSession.state) {
1757
- openSession.state = event.state;
1758
- isStateChanged = true;
1759
- }
1760
- }
1761
- onEvents(sessionId, events);
1762
- if (isStateChanged) onSessionsChange();
1763
- };
1764
- const getOpenSession = (sessionId) => {
1765
- const openSession = openSessionMap.get(sessionId);
1766
- if (!openSession) throw new InvalidOperationError(Operation.Read, sessionId, "the session is not open on this host");
1767
- return openSession;
1768
- };
1769
- const openSessionQuery = createSessionOpener({
1770
- emit,
1771
- onSessionOpen,
1772
- onSessionsChange,
1773
- openSessionMap,
1774
- taskRegistry
1775
- });
1776
- const closeSession = (sessionId) => {
1777
- const closingSession = getOpenSession(sessionId);
1778
- closeOpenSession(sessionId, closingSession, "", {
1779
- emit,
1780
- onSessionsChange,
1781
- openSessionMap
1782
- });
1783
- closingSession.input.close();
1784
- closingSession.query.close();
1785
- };
1786
- const prompt = (sessionId, text, attachments) => {
1787
- const openSession = getOpenSession(sessionId);
1788
- const uuid = crypto.randomUUID();
1789
- const createdAt = /* @__PURE__ */ new Date();
1790
- openSession.input.push({
1791
- message: {
1792
- content: [...attachments.map((attachment) => toContentBlockParam(attachment)), {
1793
- text,
1794
- type: "text"
1795
- }],
1796
- role: "user"
1797
- },
1798
- parent_tool_use_id: null,
1799
- type: "user",
1800
- uuid
1801
- });
1802
- openSession.title ||= text;
1803
- emit(sessionId, [{
1804
- attachmentCount: attachments.length,
1805
- createdAt,
1806
- id: uuid,
1807
- messageUuid: uuid,
1808
- parentToolUseId: "",
1809
- text,
1810
- type: "UserMessage"
1811
- }, {
1812
- createdAt,
1813
- id: getEventId(uuid, "SessionState"),
1814
- state: "Running",
1815
- type: "SessionState"
1816
- }]);
1817
- };
1818
- const resumeSession = async (sessionId) => {
1819
- if (openSessionMap.has(sessionId)) return sessionId;
1820
- const cwd = await readSessionCwd(sessionId);
1821
- return openSessionQuery({
1822
- cwd,
1823
- isFork: false,
1824
- resumeAt: "",
1825
- resumeFrom: sessionId,
1826
- sessionId
1827
- });
1828
- };
1829
- return {
1830
- backgroundTasks: async (sessionId) => {
1831
- await getOpenSession(sessionId).query.backgroundTasks();
1832
- },
1833
- close: async () => {
1834
- for (const sessionId of openSessionMap.keys()) closeSession(sessionId);
1835
- await taskRegistry.drain();
1836
- },
1837
- closeSession,
1838
- createSession: (cwd) => openSessionQuery({
1839
- cwd,
1840
- isFork: false,
1841
- resumeAt: "",
1842
- resumeFrom: "",
1843
- sessionId: crypto.randomUUID()
1844
- }),
1845
- forkSession: async (sessionId, messageUuid) => {
1846
- const cwd = await readSessionCwd(sessionId);
1847
- return openSessionQuery({
1848
- cwd,
1849
- isFork: true,
1850
- resumeAt: messageUuid,
1851
- resumeFrom: sessionId,
1852
- sessionId: crypto.randomUUID()
1853
- });
1854
- },
1855
- interrupt: async (sessionId) => {
1856
- await getOpenSession(sessionId).query.interrupt();
1857
- },
1858
- listSessions: () => listSessionSummaries(openSessionMap),
1859
- prompt,
1860
- resolvePermission: (sessionId, requestId, behavior, message) => {
1861
- getOpenSession(sessionId).pendingPermissionMap.get(requestId)?.settle(behavior, message);
1862
- },
1863
- resumeAt: async (sessionId, messageUuid) => {
1864
- const cwd = await readSessionCwd(sessionId);
1865
- if (openSessionMap.has(sessionId)) closeSession(sessionId);
1866
- return openSessionQuery({
1867
- cwd,
1868
- isFork: false,
1869
- resumeAt: messageUuid,
1870
- resumeFrom: sessionId,
1871
- sessionId
1872
- });
1873
- },
1874
- resumeSession,
1875
- rewindFiles: async (sessionId, messageUuid) => {
1876
- await resumeSession(sessionId);
1877
- const { query } = getOpenSession(sessionId);
1878
- const { canRewind, deletions, error, filesChanged, insertions } = await query.rewindFiles(messageUuid, { dryRun: true });
1879
- if (!canRewind) throw new InvalidOperationError(Operation.Update, sessionId, error ?? "nothing to rewind");
1880
- const rewindResult = await query.rewindFiles(messageUuid);
1881
- if (rewindResult.error) throw new InvalidOperationError(Operation.Update, sessionId, rewindResult.error);
1882
- emit(sessionId, [{
1883
- createdAt: /* @__PURE__ */ new Date(),
1884
- deletions: deletions ?? 0,
1885
- filePaths: filesChanged ?? [],
1886
- id: crypto.randomUUID(),
1887
- insertions: insertions ?? 0,
1888
- messageUuid,
1889
- type: "FileRewind"
1890
- }]);
1891
- },
1892
- runSlashCommand: (sessionId, name, commandArguments) => {
1893
- prompt(sessionId, commandArguments ? `/${name} ${commandArguments}` : `/${name}`, []);
1894
- },
1895
- setModel: async (sessionId, model) => {
1896
- const { mapper, query } = getOpenSession(sessionId);
1897
- await query.setModel(model);
1898
- emit(sessionId, [mapper.updateSettings(crypto.randomUUID(), /* @__PURE__ */ new Date(), { model })]);
1899
- },
1900
- setPermissionMode: async (sessionId, permissionMode) => {
1901
- const { mapper, query } = getOpenSession(sessionId);
1902
- await query.setPermissionMode(permissionMode);
1903
- emit(sessionId, [mapper.updateSettings(crypto.randomUUID(), /* @__PURE__ */ new Date(), { permissionMode })]);
1904
- },
1905
- stopTask: async (sessionId, taskId) => {
1906
- await getOpenSession(sessionId).query.stopTask(taskId);
1907
- }
1908
- };
1909
- };
1910
- //#endregion
1911
- //#region src/models/window/BaseChildMessage.ts
1912
- const createBaseChildMessageSchema = (typeSchema) => z.object({ type: typeSchema });
1913
- //#endregion
1914
- //#region src/models/window/ChildMessageType.ts
1915
- let ChildMessageType = /* @__PURE__ */ function(ChildMessageType) {
1916
- ChildMessageType["CommandError"] = "CommandError";
1917
- ChildMessageType["CommandResult"] = "CommandResult";
1918
- ChildMessageType["Events"] = "Events";
1919
- ChildMessageType["Rejoin"] = "Rejoin";
1920
- ChildMessageType["SessionOpen"] = "SessionOpen";
1921
- ChildMessageType["SessionsChange"] = "SessionsChange";
1922
- return ChildMessageType;
1923
- }({});
1924
- const childMessageTypeSchema = z.enum(ChildMessageType);
1925
- //#endregion
1926
- //#region src/models/window/ChildCommandErrorMessage.ts
1927
- const childCommandErrorMessageSchema = z.object({
1928
- ...createBaseChildMessageSchema(z.literal("CommandError")).shape,
1929
- commandId: z.string(),
1930
- message: z.string().min(1)
1931
- });
1932
- //#endregion
1933
- //#region src/models/window/ChildCommandResultMessage.ts
1934
- const childCommandResultMessageSchema = z.object({
1935
- ...createBaseChildMessageSchema(z.literal("CommandResult")).shape,
1936
- commandId: z.string().min(1),
1937
- sessionId: z.string()
1938
- });
1939
- //#endregion
1940
- //#region src/models/window/ChildEventsMessage.ts
1941
- const childEventsMessageSchema = z.object({
1942
- ...createBaseChildMessageSchema(z.literal("Events")).shape,
1943
- events: createUniqueArraySchema(agentEventSchema, "id"),
1944
- sessionId: z.string().min(1)
1945
- });
1946
- //#endregion
1947
- //#region src/models/window/RejoiningSession.ts
1948
- const rejoiningSessionSchema = z.object({
1949
- events: createUniqueArraySchema(agentEventSchema, "id"),
1950
- sessionId: z.string().min(1)
1951
- });
1952
- //#endregion
1953
- //#region src/models/window/ChildRejoinMessage.ts
1954
- const childRejoinMessageSchema = z.object({
1955
- ...createBaseChildMessageSchema(z.literal("Rejoin")).shape,
1956
- sessions: createUniqueArraySchema(rejoiningSessionSchema, "sessionId")
1957
- });
1958
- //#endregion
1959
- //#region src/models/window/ChildSessionOpenMessage.ts
1960
- const childSessionOpenMessageSchema = z.object({
1961
- ...createBaseChildMessageSchema(z.literal("SessionOpen")).shape,
1962
- sessionId: z.string().min(1)
1963
- });
1964
- //#endregion
1965
- //#region src/models/window/ChildSessionsChangeMessage.ts
1966
- const childSessionsChangeMessageSchema = z.object({ ...createBaseChildMessageSchema(z.literal("SessionsChange")).shape });
1967
- //#endregion
1968
- //#region src/models/window/ChildMessage.ts
1969
- const childMessageSchema = z.discriminatedUnion("type", [
1970
- childCommandErrorMessageSchema,
1971
- childCommandResultMessageSchema,
1972
- childEventsMessageSchema,
1973
- childRejoinMessageSchema,
1974
- childSessionOpenMessageSchema,
1975
- childSessionsChangeMessageSchema
1976
- ]);
1977
- //#endregion
1978
- //#region src/models/window/SessionWindowCloseCode.ts
1979
- let SessionWindowCloseCode = /* @__PURE__ */ function(SessionWindowCloseCode) {
1980
- SessionWindowCloseCode[SessionWindowCloseCode["SessionEnded"] = 4e3] = "SessionEnded";
1981
- return SessionWindowCloseCode;
1982
- }({});
1983
- //#endregion
1984
- //#region src/services/device/hashCredential.ts
1985
- const hashCredential = (credential) => createHash("sha256").update(credential).digest("base64url");
1986
- //#endregion
1987
- //#region src/services/drivers/window/constants.ts
1988
- const SESSION_SUBCOMMAND = "session";
1989
- const SESSION_SECRET_ENVIRONMENT_VARIABLE = `${SITE_NAME.toUpperCase()}_SESSION_SECRET`;
1990
- const SESSION_SECRET_BYTE_LENGTH = 32;
1991
- const SESSION_WINDOW_CONNECT_TIMEOUT_MS = Temporal.Duration.from({ seconds: 30 }).total("milliseconds");
1992
- const SESSION_WINDOWS_FILENAME = "session-windows.json";
1993
- const SESSION_WINDOW_REJOIN_DURATION_MS = Temporal.Duration.from({ minutes: 1 }).total("milliseconds");
1994
- const SESSION_WINDOW_RECONNECT_DELAY_MS = Temporal.Duration.from({ seconds: 1 }).total("milliseconds");
1995
- //#endregion
1996
- //#region src/models/window/SessionWindows.ts
1997
- const sessionWindowsSchema = z.object({
1998
- port: z.int().min(0),
1999
- secretHashes: z.array(z.string().min(1))
2000
- });
2001
- //#endregion
2002
- //#region src/services/drivers/window/readSessionWindows.ts
2003
- const readSessionWindows = (stateDirectory) => getResult(() => sessionWindowsSchema.parse(JSON.parse(readFileSync(join(stateDirectory, SESSION_WINDOWS_FILENAME), "utf8")))).unwrapOr({
2004
- port: 0,
2005
- secretHashes: []
2006
- });
2007
- //#endregion
2008
- //#region src/services/drivers/window/sendChildCommand.ts
2009
- const sendChildCommand = (child, command) => {
2010
- if (child.webSocket.readyState !== WebSocket.OPEN) throw new InvalidOperationError(Operation.Update, child.cwd, "the session's window closed");
2011
- const reply = Promise.withResolvers();
2012
- child.pendingReplyMap.set(command.id, reply);
2013
- child.webSocket.send(JSON.stringify(command));
2014
- return reply.promise;
2015
- };
2016
- //#endregion
2017
- //#region src/services/drivers/window/takeChildReply.ts
2018
- const takeChildReply = ({ pendingReplyMap }, commandId) => {
2019
- const reply = pendingReplyMap.get(commandId);
2020
- pendingReplyMap.delete(commandId);
2021
- return reply;
2022
- };
2023
- //#endregion
2024
- //#region src/services/server/checkIsTokenValid.ts
2025
- const checkIsTokenValid = (candidate, token) => {
2026
- if (!token) return false;
2027
- const candidateBuffer = Buffer.from(candidate);
2028
- const tokenBuffer = Buffer.from(token);
2029
- return candidateBuffer.length === tokenBuffer.length && timingSafeEqual(candidateBuffer, tokenBuffer);
2030
- };
2031
- //#endregion
2032
- //#region src/services/drivers/window/createWindowDriver.ts
2033
- const createWindowDriver = ({ onEvents, onSessionOpen, onSessionsChange }, { launchSessionWindow, stateDirectory, writeLine }) => {
2034
- const windowSessionMap = /* @__PURE__ */ new Map();
2035
- const childSet = /* @__PURE__ */ new Set();
2036
- const pendingLaunchMap = /* @__PURE__ */ new Map();
2037
- const taskRegistry = createTaskRegistry();
2038
- const webSocketServer = new WebSocketServer({ noServer: true });
2039
- const httpServer = createServer((_request, response) => {
2040
- response.writeHead(405).end();
2041
- });
2042
- const rejoinSecretHashSet = new Set(readSessionWindows(stateDirectory).secretHashes);
2043
- let port = 0;
2044
- const saveSessionWindows = () => {
2045
- const secretHashes = [...Array.from(childSet, ({ secretHash }) => secretHash), ...rejoinSecretHashSet];
2046
- getResult(() => {
2047
- writeStateFile(stateDirectory, SESSION_WINDOWS_FILENAME, JSON.stringify({
2048
- port,
2049
- secretHashes
2050
- }));
2051
- }).match(noop, console.error);
2052
- };
2053
- const rejoinTimeout = setTimeout(() => {
2054
- rejoinSecretHashSet.clear();
2055
- saveSessionWindows();
2056
- }, SESSION_WINDOW_REJOIN_DURATION_MS);
2057
- const getWindowSession = (sessionId) => {
2058
- const windowSession = windowSessionMap.get(sessionId);
2059
- if (!windowSession) throw new InvalidOperationError(Operation.Read, sessionId, "the session is not open on this host");
2060
- return windowSession;
2061
- };
2062
- const closeWindowSession = (sessionId, windowSession) => {
2063
- if (windowSessionMap.get(sessionId) !== windowSession) return;
2064
- windowSessionMap.delete(sessionId);
2065
- if (windowSession.state !== "Closed") onEvents(sessionId, toSessionClosedEvents(""));
2066
- writeLine(`A session in ${windowSession.child.cwd} closed, and its window with it.`);
2067
- onSessionsChange();
2068
- windowSession.child.webSocket.close(4e3);
2069
- };
2070
- const holdSession = (child, sessionId) => {
2071
- windowSessionMap.set(sessionId, {
2072
- child,
2073
- cwd: child.cwd,
2074
- lastActivityAt: /* @__PURE__ */ new Date(),
2075
- state: "Idle",
2076
- title: ""
2077
- });
2078
- onSessionOpen(sessionId);
2079
- };
2080
- const handleChildMessage = (child, childMessage) => {
2081
- switch (childMessage.type) {
2082
- case "CommandError": {
2083
- const reply = takeChildReply(child, childMessage.commandId);
2084
- if (reply) reply.reject(new Error(childMessage.message));
2085
- else console.error(childMessage.message);
2086
- return;
2087
- }
2088
- case "CommandResult":
2089
- takeChildReply(child, childMessage.commandId)?.resolve(childMessage.sessionId);
2090
- return;
2091
- case "Events": {
2092
- const { events, sessionId } = childMessage;
2093
- const windowSession = windowSessionMap.get(sessionId);
2094
- const isHeld = windowSession?.child === child;
2095
- if (isHeld) {
2096
- windowSession.lastActivityAt = /* @__PURE__ */ new Date();
2097
- for (const event of events) if (event.type === "SessionState") windowSession.state = event.state;
2098
- }
2099
- onEvents(sessionId, events);
2100
- if (isHeld && windowSession.state === "Closed") closeWindowSession(sessionId, windowSession);
2101
- return;
2102
- }
2103
- case "Rejoin":
2104
- for (const { events, sessionId } of childMessage.sessions) {
2105
- child.cwd = events.findLast((event) => event.type === "SessionInit")?.cwd ?? child.cwd;
2106
- holdSession(child, sessionId);
2107
- writeLine(`A session in ${child.cwd} came back from its window.`);
2108
- handleChildMessage(child, {
2109
- events,
2110
- sessionId,
2111
- type: "Events"
2112
- });
2113
- }
2114
- onSessionsChange();
2115
- return;
2116
- case "SessionOpen":
2117
- holdSession(child, childMessage.sessionId);
2118
- writeLine(`A session in ${child.cwd} opened in a window of its own.`);
2119
- return;
2120
- case "SessionsChange":
2121
- onSessionsChange();
2122
- return;
2123
- default: exhaustiveGuard(childMessage);
2124
- }
2125
- };
2126
- const receive = (child, data) => {
2127
- getResult(() => childMessageSchema.parse(JSON.parse(readMessageText(data)))).match((childMessage) => {
2128
- handleChildMessage(child, childMessage);
2129
- }, console.error);
2130
- };
2131
- const removeChild = (child) => {
2132
- childSet.delete(child);
2133
- saveSessionWindows();
2134
- for (const reply of child.pendingReplyMap.values()) reply.reject(new InvalidOperationError(Operation.Update, child.cwd, "the session's window closed"));
2135
- child.pendingReplyMap.clear();
2136
- for (const [sessionId, windowSession] of windowSessionMap) if (windowSession.child === child) closeWindowSession(sessionId, windowSession);
2137
- };
2138
- const addChild = (webSocket, cwd, secretHash) => {
2139
- const child = {
2140
- cwd,
2141
- pendingReplyMap: /* @__PURE__ */ new Map(),
2142
- secretHash,
2143
- webSocket
2144
- };
2145
- childSet.add(child);
2146
- saveSessionWindows();
2147
- webSocket.on("message", (data) => {
2148
- receive(child, data);
2149
- });
2150
- webSocket.on("close", () => {
2151
- removeChild(child);
2152
- });
2153
- return child;
2154
- };
2155
- httpServer.on("upgrade", (request, socket, head) => {
2156
- const secret = request.headers.authorization?.replace(/^Bearer /u, "") ?? "";
2157
- const pendingLaunchEntry = [...pendingLaunchMap].find(([launchSecret]) => checkIsTokenValid(secret, launchSecret));
2158
- if (pendingLaunchEntry) {
2159
- const [launchSecret, { connection, timeout }] = pendingLaunchEntry;
2160
- pendingLaunchMap.delete(launchSecret);
2161
- clearTimeout(timeout);
2162
- webSocketServer.handleUpgrade(request, socket, head, (webSocket) => {
2163
- connection.resolve(webSocket);
2164
- });
2165
- return;
2166
- }
2167
- const secretHash = hashCredential(secret);
2168
- if (!secret || !rejoinSecretHashSet.delete(secretHash)) {
2169
- socket.end("HTTP/1.1 401 Unauthorized\r\n\r\n");
2170
- return;
2171
- }
2172
- webSocketServer.handleUpgrade(request, socket, head, (webSocket) => {
2173
- addChild(webSocket, "", secretHash);
2174
- });
2175
- });
2176
- const listen = async () => {
2177
- httpServer.listen(0, DEFAULT_HOSTNAME);
2178
- await once(httpServer, "listening");
2179
- const address = httpServer.address();
2180
- port = typeof address === "object" && address ? address.port : 0;
2181
- saveSessionWindows();
2182
- };
2183
- const listening = listen();
2184
- const openWindow = async (cwd) => {
2185
- await listening;
2186
- const secret = randomBytes(32).toString("base64url");
2187
- const connection = Promise.withResolvers();
2188
- const timeout = setTimeout(() => {
2189
- pendingLaunchMap.delete(secret);
2190
- connection.reject(new InvalidOperationError(Operation.Create, cwd, "the session's window did not start"));
2191
- }, SESSION_WINDOW_CONNECT_TIMEOUT_MS);
2192
- pendingLaunchMap.set(secret, {
2193
- connection,
2194
- timeout
2195
- });
2196
- launchSessionWindow({
2197
- port,
2198
- secret
2199
- });
2200
- const webSocket = await connection.promise;
2201
- return addChild(webSocket, cwd, hashCredential(secret));
2202
- };
2203
- const openInWindow = async (cwd, command) => {
2204
- const child = await openWindow(cwd);
2205
- return getResultAsync(() => sendChildCommand(child, command)).match((sessionId) => sessionId, (error) => {
2206
- child.webSocket.close(4e3);
2207
- throw error;
2208
- });
2209
- };
2210
- const forward = (sessionId, command) => sendChildCommand(getWindowSession(sessionId).child, command);
2211
- const forwardInBackground = (sessionId, command) => {
2212
- const reply = forward(sessionId, command);
2213
- taskRegistry.run(() => getResultAsync(() => reply).match(noop, console.error));
2214
- };
2215
- const resumeSession = async (sessionId) => {
2216
- if (windowSessionMap.has(sessionId)) return sessionId;
2217
- return openInWindow(await readSessionCwd(sessionId), {
2218
- id: crypto.randomUUID(),
2219
- sessionId,
2220
- type: "Resume"
2221
- });
2222
- };
2223
- return {
2224
- backgroundTasks: async (sessionId) => {
2225
- await forward(sessionId, {
2226
- id: crypto.randomUUID(),
2227
- sessionId,
2228
- type: "BackgroundTasks"
2229
- });
2230
- },
2231
- close: async () => {
2232
- for (const { connection, timeout } of pendingLaunchMap.values()) {
2233
- clearTimeout(timeout);
2234
- connection.reject(new InvalidOperationError(Operation.Create, createWindowDriver.name, "the host is stopping"));
2235
- }
2236
- pendingLaunchMap.clear();
2237
- clearTimeout(rejoinTimeout);
2238
- rejoinSecretHashSet.clear();
2239
- const children = [...childSet];
2240
- for (const { webSocket } of children) webSocket.close(4e3);
2241
- await Promise.all(children.map(({ webSocket }) => once(webSocket, "close")));
2242
- await taskRegistry.drain();
2243
- await listening;
2244
- saveSessionWindows();
2245
- httpServer.close();
2246
- await once(httpServer, "close");
2247
- },
2248
- closeSession: (sessionId) => {
2249
- closeWindowSession(sessionId, getWindowSession(sessionId));
2250
- },
2251
- createSession: (cwd) => openInWindow(cwd, {
2252
- cwd,
2253
- id: crypto.randomUUID(),
2254
- type: "CreateSession"
2255
- }),
2256
- forkSession: async (sessionId, messageUuid) => openInWindow(await readSessionCwd(sessionId), {
2257
- id: crypto.randomUUID(),
2258
- messageUuid,
2259
- sessionId,
2260
- type: "Fork"
2261
- }),
2262
- interrupt: async (sessionId) => {
2263
- await forward(sessionId, {
2264
- id: crypto.randomUUID(),
2265
- sessionId,
2266
- type: "Interrupt"
2267
- });
2268
- },
2269
- listSessions: () => listSessionSummaries(windowSessionMap),
2270
- prompt: (sessionId, text, attachments) => {
2271
- forwardInBackground(sessionId, {
2272
- attachments,
2273
- id: crypto.randomUUID(),
2274
- sessionId,
2275
- text,
2276
- type: "Prompt"
2277
- });
2278
- },
2279
- resolvePermission: (sessionId, requestId, behavior, message) => {
2280
- forwardInBackground(sessionId, {
2281
- behavior,
2282
- id: crypto.randomUUID(),
2283
- message,
2284
- requestId,
2285
- sessionId,
2286
- type: "PermissionVerdict"
2287
- });
2288
- },
2289
- resumeAt: async (sessionId, messageUuid) => {
2290
- const cwd = await readSessionCwd(sessionId);
2291
- const windowSession = windowSessionMap.get(sessionId);
2292
- if (windowSession) closeWindowSession(sessionId, windowSession);
2293
- return openInWindow(cwd, {
2294
- id: crypto.randomUUID(),
2295
- messageUuid,
2296
- sessionId,
2297
- type: "ResumeAt"
2298
- });
2299
- },
2300
- resumeSession,
2301
- rewindFiles: async (sessionId, messageUuid) => {
2302
- await resumeSession(sessionId);
2303
- await forward(sessionId, {
2304
- id: crypto.randomUUID(),
2305
- messageUuid,
2306
- sessionId,
2307
- type: "RewindFiles"
2308
- });
2309
- },
2310
- runSlashCommand: (sessionId, name, commandArguments) => {
2311
- forwardInBackground(sessionId, {
2312
- arguments: commandArguments,
2313
- id: crypto.randomUUID(),
2314
- name,
2315
- sessionId,
2316
- type: "SlashCommand"
2317
- });
2318
- },
2319
- setModel: async (sessionId, model) => {
2320
- await forward(sessionId, {
2321
- id: crypto.randomUUID(),
2322
- model,
2323
- sessionId,
2324
- type: "SetModel"
2325
- });
2326
- },
2327
- setPermissionMode: async (sessionId, permissionMode) => {
2328
- await forward(sessionId, {
2329
- id: crypto.randomUUID(),
2330
- permissionMode,
2331
- sessionId,
2332
- type: "SetPermissionMode"
2333
- });
2334
- },
2335
- stopTask: async (sessionId, taskId) => {
2336
- await forward(sessionId, {
2337
- id: crypto.randomUUID(),
2338
- sessionId,
2339
- taskId,
2340
- type: "StopTask"
2341
- });
2342
- }
2343
- };
2344
- };
2345
- //#endregion
2346
- //#region src/services/drivers/window/launchSessionWindow.ts
2347
- const SESSION_COMMAND_ENVIRONMENT_VARIABLE_PREFIX = `${SITE_NAME.toUpperCase()}_SESSION_COMMAND_`;
2348
- const launchSessionWindow = ({ port, secret }) => {
2349
- const hostCommand = isSea() ? [process.execPath] : [process.execPath, process.argv[1] ?? ""];
2350
- const quotedHostCommand = hostCommand.map((_value, index) => `"%${SESSION_COMMAND_ENVIRONMENT_VARIABLE_PREFIX}${index}%"`).join(" ");
2351
- spawn$1("cmd.exe", [
2352
- "/d",
2353
- "/s",
2354
- "/c",
2355
- `"start "" ${quotedHostCommand} ${SESSION_SUBCOMMAND} --port ${port}"`
2356
- ], {
2357
- env: {
2358
- ...process.env,
2359
- ...Object.fromEntries(hostCommand.map((part, index) => [`${SESSION_COMMAND_ENVIRONMENT_VARIABLE_PREFIX}${index}`, part])),
2360
- [SESSION_SECRET_ENVIRONMENT_VARIABLE]: secret
2361
- },
2362
- stdio: "ignore",
2363
- windowsVerbatimArguments: true
2364
- }).on("error", console.error);
2365
- };
2366
- //#endregion
2367
- //#region src/services/device/findDevice.ts
2368
- const findDevice = (devices, credential) => {
2369
- const credentialHash = hashCredential(credential);
2370
- return devices.find((device) => checkIsTokenValid(credentialHash, device.credentialHash));
2371
- };
2372
- //#endregion
2373
- //#region src/services/device/getDeviceName.ts
2374
- const BrowserNames = [
2375
- [/Edg\//u, "Edge"],
2376
- [/Firefox\//u, "Firefox"],
2377
- [/Chrome\//u, "Chrome"],
2378
- [/Safari\//u, "Safari"]
2379
- ];
2380
- const SystemNames = [
2381
- [/Windows/u, "Windows"],
2382
- [/Android/u, "Android"],
2383
- [/iPhone|iPad/u, "iOS"],
2384
- [/Mac OS X/u, "macOS"],
2385
- [/Linux/u, "Linux"]
2386
- ];
2387
- const getDeviceName = (userAgent) => {
2388
- const browserName = BrowserNames.find(([pattern]) => pattern.test(userAgent))?.[1] ?? "A browser";
2389
- const systemName = SystemNames.find(([pattern]) => pattern.test(userAgent))?.[1];
2390
- return systemName ? `${browserName} on ${systemName}` : browserName;
2391
- };
2392
- //#endregion
2393
- //#region src/services/handshake/getPublicKeyText.ts
2394
- const getPublicKeyText = (privateKey) => createPublicKey(privateKey).export({ format: "jwk" }).x ?? "";
2395
- //#endregion
2396
- //#region src/services/server/answerHttpRequest.ts
2397
- const answerHttpRequest = ({ headers, method }, response) => {
2398
- if (method !== "OPTIONS") {
2399
- response.writeHead(405).end();
2400
- return;
2401
- }
2402
- response.setHeader("Access-Control-Allow-Origin", headers.origin ?? "*");
2403
- response.setHeader("Access-Control-Allow-Private-Network", "true");
2404
- response.setHeader("Access-Control-Allow-Methods", "GET");
2405
- response.setHeader("Access-Control-Allow-Headers", headers["access-control-request-headers"] ?? "*");
2406
- response.setHeader("Vary", "Origin");
2407
- response.writeHead(204).end();
2408
- };
2409
- //#endregion
2410
- //#region src/services/server/createEventLog.ts
2411
- const createEventLog = () => {
2412
- const sessionEventMap = /* @__PURE__ */ new Map();
2413
- return {
2414
- append: (sessionId, events) => {
2415
- const sessionEvents = getOrCreate(sessionEventMap, sessionId, () => ({
2416
- eventIds: /* @__PURE__ */ new Set(),
2417
- events: []
2418
- }));
2419
- const newEvents = [];
2420
- for (const event of events) {
2421
- if (EphemeralAgentEventTypes.includes(event.type)) {
2422
- newEvents.push(event);
2423
- continue;
2424
- }
2425
- if (sessionEvents.eventIds.has(event.id)) continue;
2426
- sessionEvents.eventIds.add(event.id);
2427
- sessionEvents.events.push(event);
2428
- newEvents.push(event);
2429
- }
2430
- return newEvents;
2431
- },
2432
- entries: () => Array.from(sessionEventMap, ([sessionId, { events }]) => [sessionId, events]),
2433
- reset: (sessionId) => {
2434
- sessionEventMap.delete(sessionId);
2435
- }
2436
- };
2437
- };
2438
- //#endregion
2439
- //#region src/services/server/createPairingCodes.ts
2440
- const createPairingCodes = () => {
2441
- const codeTimeoutMap = /* @__PURE__ */ new Map();
2442
- return {
2443
- add: (code, durationMs) => {
2444
- clearTimeout(codeTimeoutMap.get(code));
2445
- codeTimeoutMap.set(code, setTimeout(() => {
2446
- codeTimeoutMap.delete(code);
2447
- }, durationMs));
2448
- },
2449
- clear: () => {
2450
- for (const timeout of codeTimeoutMap.values()) clearTimeout(timeout);
2451
- codeTimeoutMap.clear();
2452
- },
2453
- take: (code) => {
2454
- const heldCode = [...codeTimeoutMap.keys()].find((candidate) => checkIsTokenValid(code, candidate));
2455
- if (heldCode === void 0) return false;
2456
- clearTimeout(codeTimeoutMap.get(heldCode));
2457
- codeTimeoutMap.delete(heldCode);
2458
- return true;
2459
- }
2460
- };
2461
- };
2462
- //#endregion
2463
- //#region src/services/server/handleCommand.ts
2464
- const handleCommand = async (driver, command) => {
2465
- switch (command.type) {
2466
- case "BackgroundTasks":
2467
- await driver.backgroundTasks(command.sessionId);
2468
- return "";
2469
- case "CloseSession":
2470
- driver.closeSession(command.sessionId);
2471
- return "";
2472
- case "CreateSession": return driver.createSession(command.cwd);
2473
- case "Fork": return driver.forkSession(command.sessionId, command.messageUuid);
2474
- case "Interrupt":
2475
- await driver.interrupt(command.sessionId);
2476
- return "";
2477
- case "ListSessions": return "";
2478
- case "PermissionVerdict":
2479
- driver.resolvePermission(command.sessionId, command.requestId, command.behavior, command.message);
2480
- return "";
2481
- case "Prompt":
2482
- driver.prompt(command.sessionId, command.text, command.attachments);
2483
- return "";
2484
- case "Resume": return driver.resumeSession(command.sessionId);
2485
- case "ResumeAt": return driver.resumeAt(command.sessionId, command.messageUuid);
2486
- case "RewindFiles":
2487
- await driver.rewindFiles(command.sessionId, command.messageUuid);
2488
- return "";
2489
- case "SetModel":
2490
- await driver.setModel(command.sessionId, command.model);
2491
- return "";
2492
- case "SetPermissionMode":
2493
- await driver.setPermissionMode(command.sessionId, command.permissionMode);
2494
- return "";
2495
- case "SlashCommand":
2496
- driver.runSlashCommand(command.sessionId, command.name, command.arguments);
2497
- return "";
2498
- case "StopTask":
2499
- await driver.stopTask(command.sessionId, command.taskId);
2500
- return "";
2501
- default: return exhaustiveGuard(command);
2502
- }
2503
- };
2504
- //#endregion
2505
- //#region src/services/files/getSessionCwd.ts
2506
- const getSessionCwd = async (driver, sessionId) => {
2507
- const session = (await driver.listSessions()).find(({ id }) => id === sessionId);
2508
- if (!session) throw new InvalidOperationError(Operation.Read, sessionId, "the session is not open on this host");
2509
- return session.cwd;
2510
- };
2511
- //#endregion
2512
- //#region src/services/files/constants.ts
2513
- const REPOSITORY_FILE_MAX_BYTE_LENGTH = 524288;
2514
- const REPOSITORY_WALK_IGNORED_NAMES = /* @__PURE__ */ new Set([".git", "node_modules"]);
2515
- //#endregion
2516
- //#region src/services/files/listRepositoryFiles.ts
2517
- const execFileAsync = promisify(execFile$1);
2518
- const listGitFiles = async (cwd) => {
2519
- const { stdout } = await execFileAsync("git", [
2520
- "ls-files",
2521
- "-co",
2522
- "--exclude-standard",
2523
- "-z"
2524
- ], { cwd });
2525
- return stdout.split("\0").filter(Boolean);
2526
- };
2527
- const walkFiles = async (root, directory) => {
2528
- const entries = await readdir(directory, { withFileTypes: true });
2529
- const files = entries.filter((entry) => entry.isFile()).map((entry) => relative(root, join(directory, entry.name)));
2530
- const subdirectories = entries.filter((entry) => entry.isDirectory() && !REPOSITORY_WALK_IGNORED_NAMES.has(entry.name));
2531
- const nested = await Promise.all(subdirectories.map((entry) => walkFiles(root, join(directory, entry.name))));
2532
- return [...files, ...nested.flat()];
2533
- };
2534
- const listRepositoryFiles = async (cwd) => {
2535
- return (await getResultAsync(() => listGitFiles(cwd)).unwrapOr(void 0) ?? await walkFiles(cwd, cwd)).map((file) => file.split(sep).join("/")).toSorted();
2536
- };
2537
- //#endregion
2538
- //#region src/services/files/resolveRepositoryPath.ts
2539
- const resolveRepositoryPath = async (cwd, path) => {
2540
- const root = await realpath(cwd);
2541
- const target = await realpath(resolve(root, path));
2542
- const relation = relative(root, target);
2543
- if (relation === ".." || relation.startsWith(`..${sep}`) || isAbsolute(relation)) throw new InvalidOperationError(Operation.Read, path, "the file is outside the session's working directory");
2544
- return target;
2545
- };
2546
- //#endregion
2547
- //#region src/services/files/readRepositoryFile.ts
2548
- const readRepositoryFile = async (cwd, path) => {
2549
- const target = await resolveRepositoryPath(cwd, path);
2550
- const { size } = await stat(target);
2551
- if (size > 524288) return {
2552
- content: "",
2553
- notice: "The file is too large to show."
2554
- };
2555
- const buffer = await readFile(target);
2556
- if (buffer.includes(0)) return {
2557
- content: "",
2558
- notice: "The file is not text, so it cannot be shown."
2559
- };
2560
- return {
2561
- content: buffer.toString("utf8"),
2562
- notice: ""
2563
- };
2564
- };
2565
- //#endregion
2566
- //#region src/services/server/handleFileCommand.ts
2567
- const handleFileCommand = async (driver, command) => {
2568
- const cwd = await getSessionCwd(driver, command.sessionId);
2569
- switch (command.type) {
2570
- case "ListFiles": return {
2571
- commandId: command.id,
2572
- files: await listRepositoryFiles(cwd),
2573
- type: "Files"
2574
- };
2575
- case "ReadFile": {
2576
- const file = await readRepositoryFile(cwd, command.path);
2577
- return {
2578
- commandId: command.id,
2579
- content: file.content,
2580
- notice: file.notice,
2581
- path: command.path,
2582
- type: "FileContent"
2583
- };
2584
- }
2585
- default: return exhaustiveGuard(command);
2586
- }
2587
- };
2588
- //#endregion
2589
- //#region src/services/server/handleShellCommand.ts
2590
- const handleShellCommand = async (shellRegistry, command) => {
2591
- switch (command.type) {
2592
- case "CloseShell":
2593
- shellRegistry.close(command.shellId);
2594
- return "";
2595
- case "OpenShell": return await shellRegistry.open(command.sessionId, {
2596
- cols: command.cols,
2597
- cwd: command.cwd,
2598
- rows: command.rows
2599
- });
2600
- case "ShellInput":
2601
- shellRegistry.write(command.shellId, command.data);
2602
- return "";
2603
- case "ShellResize":
2604
- shellRegistry.resize(command.shellId, command.cols, command.rows);
2605
- return "";
2606
- default: return exhaustiveGuard(command);
2607
- }
2608
- };
2609
- //#endregion
2610
- //#region src/services/server/sendServerMessage.ts
2611
- const sendServerMessage = (webSocket, message) => {
2612
- if (webSocket.readyState === WebSocket.OPEN) webSocket.send(JSON.stringify(message));
2613
- };
2614
- //#endregion
2615
- //#region src/services/shell/createShellRegistry.ts
2616
- const createShellRegistry = ({ onClose, onOutput, spawnShell }) => {
2617
- const shellMap = /* @__PURE__ */ new Map();
2618
- const pendingShells = /* @__PURE__ */ new Set();
2619
- const closedSessionIds = /* @__PURE__ */ new Set();
2620
- let isStopped = false;
2621
- const getTerminal = (shellId) => {
2622
- const shell = shellMap.get(shellId);
2623
- if (!shell) throw new InvalidOperationError(Operation.Read, shellId, "the shell is not running on this host");
2624
- return shell.terminal;
2625
- };
2626
- return {
2627
- close: (shellId) => {
2628
- getTerminal(shellId).kill();
2629
- },
2630
- closeAll: () => {
2631
- isStopped = true;
2632
- for (const pendingShell of pendingShells) pendingShell.isClosed = true;
2633
- for (const { terminal } of shellMap.values()) terminal.kill();
2634
- },
2635
- closeSession: (sessionId) => {
2636
- closedSessionIds.add(sessionId);
2637
- for (const pendingShell of pendingShells) if (pendingShell.sessionId === sessionId) pendingShell.isClosed = true;
2638
- for (const shell of shellMap.values()) if (shell.sessionId === sessionId) shell.terminal.kill();
2639
- },
2640
- entries: () => Array.from(shellMap, ([shellId, { output, sessionId }]) => ({
2641
- output,
2642
- sessionId,
2643
- shellId
2644
- })),
2645
- open: async (sessionId, options) => {
2646
- if (isStopped) throw new InvalidOperationError(Operation.Create, sessionId, "the host is stopping");
2647
- if (closedSessionIds.has(sessionId)) throw new InvalidOperationError(Operation.Create, sessionId, "the session is closed");
2648
- const pendingShell = {
2649
- isClosed: false,
2650
- sessionId
2651
- };
2652
- pendingShells.add(pendingShell);
2653
- const terminal = await withFinalizerAsync(() => spawnShell(options), () => {
2654
- pendingShells.delete(pendingShell);
2655
- });
2656
- if (pendingShell.isClosed) {
2657
- terminal.kill();
2658
- throw new InvalidOperationError(Operation.Create, sessionId, "the shell's session or host closed as it started");
2659
- }
2660
- const shellId = crypto.randomUUID();
2661
- const shell = {
2662
- output: "",
2663
- sessionId,
2664
- terminal
2665
- };
2666
- shellMap.set(shellId, shell);
2667
- terminal.onData((data) => {
2668
- shell.output = `${shell.output}${data}`.slice(-SHELL_OUTPUT_LENGTH);
2669
- onOutput(shellId, data);
2670
- });
2671
- terminal.onExit(() => {
2672
- shellMap.delete(shellId);
2673
- onClose(shellId);
2674
- });
2675
- return shellId;
2676
- },
2677
- openSession: (sessionId) => {
2678
- closedSessionIds.delete(sessionId);
2679
- },
2680
- resize: (shellId, cols, rows) => {
2681
- getTerminal(shellId).resize(cols, rows);
2682
- },
2683
- write: (shellId, data) => {
2684
- getTerminal(shellId).write(data);
2685
- }
2686
- };
2687
- };
2688
- //#endregion
2689
- //#region src/services/server/createAgentConsoleServer.ts
2690
- const createAgentConsoleServer = async ({ createDriver, hostKey, hostname, origin, port, spawnShell, stateDirectory, writeLine }) => {
2691
- const eventLog = createEventLog();
2692
- const taskRegistry = createTaskRegistry();
2693
- const pairingCodes = createPairingCodes();
2694
- const publicKey = createPublicKey(hostKey);
2695
- const publicKeyText = getPublicKeyText(hostKey);
2696
- const webSocketServer = new WebSocketServer({ noServer: true });
2697
- const socketDeviceIdMap = /* @__PURE__ */ new Map();
2698
- const broadcast = (message) => {
2699
- for (const webSocket of socketDeviceIdMap.keys()) sendServerMessage(webSocket, message);
2700
- };
2701
- let isRefreshing = false;
2702
- let isRefreshStale = false;
2703
- const refreshSessions = () => {
2704
- if (isRefreshing) {
2705
- isRefreshStale = true;
2706
- return;
2707
- }
2708
- isRefreshing = true;
2709
- taskRegistry.run(async () => {
2710
- do {
2711
- isRefreshStale = false;
2712
- await getResultAsync(() => driver.listSessions()).match((sessions) => {
2713
- broadcast({
2714
- sessions,
2715
- type: "Sessions"
2716
- });
2717
- }, console.error);
2718
- } while (isRefreshStale);
2719
- isRefreshing = false;
2720
- });
2721
- };
2722
- const shellRegistry = createShellRegistry({
2723
- onClose: (shellId) => {
2724
- broadcast({
2725
- shellId,
2726
- type: "ShellClosed"
2727
- });
2728
- },
2729
- onOutput: (shellId, data) => {
2730
- broadcast({
2731
- data,
2732
- shellId,
2733
- type: "ShellOutput"
2734
- });
2735
- },
2736
- spawnShell
2737
- });
2738
- const driver = createDriver({
2739
- onEvents: (sessionId, events) => {
2740
- const newEvents = eventLog.append(sessionId, events);
2741
- if (newEvents.length > 0) broadcast({
2742
- events: newEvents,
2743
- sessionId,
2744
- type: "Events"
2745
- });
2746
- if (events.some((event) => event.type === "SessionState" && event.state === "Closed")) shellRegistry.closeSession(sessionId);
2747
- },
2748
- onSessionOpen: (sessionId) => {
2749
- shellRegistry.openSession(sessionId);
2750
- eventLog.reset(sessionId);
2751
- broadcast({
2752
- sessionId,
2753
- type: "SessionReset"
2754
- });
2755
- },
2756
- onSessionsChange: () => {
2757
- refreshSessions();
2758
- }
2759
- });
2760
- const receive = async (webSocket, data) => {
2761
- const command = getResult(() => commandSchema.parse(JSON.parse(readMessageText(data)))).orTee((error) => {
2762
- sendServerMessage(webSocket, {
2763
- commandId: "",
2764
- message: error.message,
2765
- type: "CommandError"
2766
- });
2767
- }).unwrapOr(void 0);
2768
- if (!command) return;
2769
- const sendCommandError = (error) => {
2770
- sendServerMessage(webSocket, {
2771
- commandId: command.id,
2772
- message: error.message,
2773
- type: "CommandError"
2774
- });
2775
- };
2776
- switch (command.type) {
2777
- case "CloseShell":
2778
- case "OpenShell":
2779
- case "ShellInput":
2780
- case "ShellResize":
2781
- await getResultAsync(() => handleShellCommand(shellRegistry, command)).match((shellId) => {
2782
- if (command.type === "OpenShell") sendServerMessage(webSocket, {
2783
- commandId: command.id,
2784
- sessionId: command.sessionId,
2785
- shellId,
2786
- type: "ShellOpened"
2787
- });
2788
- }, sendCommandError);
2789
- return;
2790
- case "ListFiles":
2791
- case "ReadFile":
2792
- await getResultAsync(() => handleFileCommand(driver, command)).match((message) => {
2793
- sendServerMessage(webSocket, message);
2794
- }, sendCommandError);
2795
- return;
2796
- default: await getResultAsync(() => handleCommand(driver, command)).match((sessionId) => {
2797
- if (sessionId) sendServerMessage(webSocket, {
2798
- commandId: command.id,
2799
- sessionId,
2800
- type: "SessionOpened"
2801
- });
2802
- else if (command.type === "ListSessions") refreshSessions();
2803
- }, sendCommandError);
2804
- }
2805
- };
2806
- const admit = (webSocket, deviceId) => {
2807
- socketDeviceIdMap.set(webSocket, deviceId);
2808
- for (const [sessionId, events] of eventLog.entries()) sendServerMessage(webSocket, {
2809
- events,
2810
- sessionId,
2811
- type: "Events"
2812
- });
2813
- for (const { output, sessionId, shellId } of shellRegistry.entries()) {
2814
- sendServerMessage(webSocket, {
2815
- commandId: "",
2816
- sessionId,
2817
- shellId,
2818
- type: "ShellOpened"
2819
- });
2820
- if (output) sendServerMessage(webSocket, {
2821
- data: output,
2822
- shellId,
2823
- type: "ShellOutput"
2824
- });
2825
- }
2826
- refreshSessions();
2827
- };
2828
- const pair = (webSocket, request, code) => {
2829
- if (request.headers.origin !== origin || !pairingCodes.take(code)) {
2830
- webSocket.close(4403);
2831
- return;
2832
- }
2833
- const credential = randomBytes(32).toString("base64url");
2834
- const device = {
2835
- createdAt: /* @__PURE__ */ new Date(),
2836
- credentialHash: hashCredential(credential),
2837
- id: crypto.randomUUID(),
2838
- name: getDeviceName(request.headers["user-agent"] ?? ""),
2839
- origin
2840
- };
2841
- writeDevices(stateDirectory, [...readDevices(stateDirectory), device]);
2842
- writeLine(`${device.name} connected. To remove it: devices --revoke ${device.id}`);
2843
- sendServerMessage(webSocket, {
2844
- credential,
2845
- deviceId: device.id,
2846
- publicKey: publicKeyText,
2847
- type: "Paired"
2848
- });
2849
- admit(webSocket, device.id);
2850
- };
2851
- const receiveHandshake = (webSocket, request, hostNonce, handshakeMessage) => {
2852
- const localPort = request.socket.localPort ?? 0;
2853
- switch (handshakeMessage.type) {
2854
- case "Authenticate": {
2855
- const device = findDevice(readDevices(stateDirectory), handshakeMessage.credential);
2856
- if (!device) {
2857
- webSocket.close(4401);
2858
- return;
2859
- }
2860
- sendServerMessage(webSocket, { type: "Authenticated" });
2861
- admit(webSocket, device.id);
2862
- return;
2863
- }
2864
- case "Challenge":
2865
- sendServerMessage(webSocket, {
2866
- nonce: hostNonce,
2867
- port: localPort,
2868
- signature: signNonce(hostKey, "HostProof", localPort, handshakeMessage.nonce),
2869
- type: "Proof"
2870
- });
2871
- return;
2872
- case "HandOff":
2873
- case "Revoke":
2874
- if (!checkIsSignatureValid(publicKey, "OwnerProof", localPort, hostNonce, handshakeMessage.signature)) {
2875
- webSocket.close(4404);
2876
- return;
2877
- }
2878
- if (handshakeMessage.type === "HandOff") pairingCodes.add(handshakeMessage.code, SCHEME_PAIRING_CODE_DURATION_MS);
2879
- else {
2880
- writeDevices(stateDirectory, readDevices(stateDirectory).filter(({ id }) => id !== handshakeMessage.deviceId));
2881
- for (const [deviceWebSocket, deviceId] of socketDeviceIdMap) if (deviceId === handshakeMessage.deviceId) deviceWebSocket.close(4401);
2882
- writeLine("A device was removed, and its connection closed.");
2883
- }
2884
- webSocket.close();
2885
- return;
2886
- case "Pair":
2887
- pair(webSocket, request, handshakeMessage.code);
2888
- return;
2889
- default: exhaustiveGuard(handshakeMessage);
2890
- }
2891
- };
2892
- webSocketServer.on("connection", (webSocket, request) => {
2893
- const hostNonce = randomBytes(32).toString("base64url");
2894
- webSocket.on("message", (data) => {
2895
- if (socketDeviceIdMap.has(webSocket)) {
2896
- taskRegistry.run(() => receive(webSocket, data));
2897
- return;
2898
- }
2899
- getResult(() => {
2900
- receiveHandshake(webSocket, request, hostNonce, handshakeMessageSchema.parse(JSON.parse(readMessageText(data))));
2901
- }).match(noop, (error) => {
2902
- console.error(error);
2903
- webSocket.close(4400);
2904
- });
2905
- });
2906
- webSocket.on("close", () => {
2907
- socketDeviceIdMap.delete(webSocket);
2908
- });
2909
- });
2910
- const httpServer = createServer((request, response) => {
2911
- answerHttpRequest(request, response);
2912
- });
2913
- httpServer.on("upgrade", (request, socket, head) => {
2914
- if (request.headers.origin !== void 0 && request.headers.origin !== origin) {
2915
- socket.end("HTTP/1.1 403 Forbidden\r\n\r\n");
2916
- return;
2917
- }
2918
- webSocketServer.handleUpgrade(request, socket, head, (webSocket) => {
2919
- webSocketServer.emit("connection", webSocket, request);
2920
- });
2921
- });
2922
- httpServer.listen(port, hostname);
2923
- await once(httpServer, "listening");
2924
- const address = httpServer.address();
2925
- let closing;
2926
- const close = async () => {
2927
- broadcast({ type: "HostStopping" });
2928
- pairingCodes.clear();
2929
- shellRegistry.closeAll();
2930
- await driver.close();
2931
- for (const webSocket of webSocketServer.clients) webSocket.terminate();
2932
- webSocketServer.close();
2933
- httpServer.close();
2934
- await Promise.all([
2935
- taskRegistry.drain(),
2936
- once(webSocketServer, "close"),
2937
- once(httpServer, "close")
2938
- ]);
2939
- };
2940
- return {
2941
- addPairingCode: pairingCodes.add,
2942
- close: () => closing ??= close(),
2943
- port: typeof address === "object" && address ? address.port : port
2944
- };
2945
- };
2946
- //#endregion
2947
- //#region src/services/shell/getShellFile.ts
2948
- const POWERSHELL_FILE = "pwsh.exe";
2949
- const WINDOWS_POWERSHELL_FILE = "powershell.exe";
2950
- const getShellFile = () => {
2951
- if (process.platform !== "win32") return process.env.SHELL ?? "/bin/sh";
2952
- return (process.env.PATH ?? "").split(delimiter).filter(Boolean).some((pathDirectory) => existsSync(join(pathDirectory, POWERSHELL_FILE))) ? POWERSHELL_FILE : WINDOWS_POWERSHELL_FILE;
2953
- };
2954
- //#endregion
2955
- //#region src/services/shell/loadNodePty.ts
2956
- const loadNodePty = () => {
2957
- if (!isSea()) return import("node-pty");
2958
- const installDirectory = getHostInstallDirectory();
2959
- return Promise.resolve(createRequire(join(installDirectory, "package.json"))(join(installDirectory, NODE_PTY_DIRECTORY_NAME)));
2960
- };
2961
- //#endregion
2962
- //#region src/services/shell/spawnPtyShell.ts
2963
- const spawnPtyShell = async ({ cols, cwd, rows }) => {
2964
- const { spawn } = await getResultAsync(() => loadNodePty()).match((nodePty) => nodePty, () => {
2965
- throw new InvalidOperationError(Operation.Create, cwd, "this host has no terminal support, so no shell can start");
2966
- });
2967
- const env = Object.fromEntries(Object.entries(process.env).filter(([key]) => key.toLowerCase() !== "psmodulepath"));
2968
- return spawn(getShellFile(), [], {
2969
- cols,
2970
- cwd,
2971
- env,
2972
- name: "xterm-256color",
2973
- rows
2974
- });
2975
- };
2976
- //#endregion
2977
- //#region src/services/cli/serveHost.ts
2978
- const serveHost = async ({ hostname, origin, port, schemeLaunch }) => {
2979
- installDownloadedHost();
2980
- const stateDirectory = getStateDirectory();
2981
- const hostKey = readHostKey(stateDirectory);
2982
- const reachableHostname = getReachableHostname(hostname);
2983
- const server = await getResultAsync(() => createAgentConsoleServer({
2984
- createDriver: (callbacks) => process.platform === "win32" && hostname === "127.0.0.1" ? createWindowDriver(callbacks, {
2985
- launchSessionWindow,
2986
- stateDirectory,
2987
- writeLine
2988
- }) : createClaudeAgentSdkDriver(callbacks),
2989
- hostKey,
2990
- hostname,
2991
- origin,
2992
- port,
2993
- spawnShell: spawnPtyShell,
2994
- stateDirectory,
2995
- writeLine
2996
- })).match((agentConsoleServer) => agentConsoleServer, async (error) => {
2997
- if (!schemeLaunch || !("code" in error) || error.code !== "EADDRINUSE") throw error;
2998
- const { code } = schemeLaunch;
2999
- if (await sendToRunningHost(reachableHostname, port, hostKey, code ? (signature) => ({
3000
- code,
3001
- signature,
3002
- type: "HandOff"
3003
- }) : void 0)) {
3004
- process.stdout.write("The host is already running in another window.\n");
3005
- process.exit(0);
3006
- }
3007
- process.stderr.write(`Another program is using port ${port}, so the host cannot start. Close it and connect again.\n`);
3008
- process.exit(1);
3009
- });
3010
- if (schemeLaunch) {
3011
- if (schemeLaunch.code) server.addPairingCode(schemeLaunch.code, SCHEME_PAIRING_CODE_DURATION_MS);
3012
- process.stdout.write("The host is running. Keep this window open.\n\nTo stop, close this window.\n");
3013
- } else {
3014
- const code = randomBytes(32).toString("base64url");
3015
- server.addPairingCode(code, PRINTED_PAIRING_CODE_DURATION_MS);
3016
- const hostAddress = `ws://${reachableHostname}:${server.port}`;
3017
- const pairingUrl = `${origin}${RoutePath.Genshin}#${new URLSearchParams({
3018
- [PAIRING_CODE_PARAMETER]: code,
3019
- [PAIRING_HASH_PARAMETER]: hostAddress
3020
- })}`;
3021
- process.stdout.write(`The host is running. Keep this window open.\n\nTo connect, press Connect on the page, or hold Ctrl and click this link in the next ten minutes:\n ${pairingUrl}\n\nTo add this machine from another computer, enter its address and this code in the page's sessions tab:\n ${code}\n\nTo stop, close this window.\n`);
3022
- }
3023
- await Promise.race([once(process, "SIGINT"), once(process, "SIGHUP")]);
3024
- await server.close();
3025
- };
3026
- //#endregion
3027
- //#region src/services/cli/commands/serveCommand.ts
3028
- const serveCommand = defineCommand({
3029
- args: { ...hostArgs },
3030
- meta: {
3031
- description: "Start the host and print the link that pairs a page with it",
3032
- name: "serve"
3033
- },
3034
- run: ({ args }) => serveHost({
3035
- hostname: args.hostname,
3036
- origin: args.origin,
3037
- port: Number(args.port)
3038
- })
3039
- });
3040
- //#endregion
3041
- //#region src/services/drivers/window/formatSessionLogLine.ts
3042
- const formatSessionLogLine = (event) => {
3043
- switch (event.type) {
3044
- case "AssistantMessage": return event.parentToolUseId ? "" : `Claude: ${event.text}`;
3045
- case "HostError": return `Error: ${event.message}`;
3046
- case "PermissionRequest": return `Claude asks to use ${event.toolName}. Answer in the page.`;
3047
- case "ToolUse": return event.parentToolUseId ? "" : `Claude uses ${event.name}`;
3048
- case "UserMessage": return event.parentToolUseId ? "" : `You: ${event.text}`;
3049
- default: return "";
3050
- }
3051
- };
3052
- //#endregion
3053
- //#region src/services/drivers/window/serveSessionChild.ts
3054
- const serveSessionChild = async (connect, createDriver, writeLine, signal) => {
3055
- const taskRegistry = createTaskRegistry();
3056
- const sessionEventMap = /* @__PURE__ */ new Map();
3057
- let webSocket;
3058
- const send = (childMessage) => {
3059
- if (webSocket?.readyState === WebSocket.OPEN) webSocket.send(JSON.stringify(childMessage));
3060
- };
3061
- const driver = createDriver({
3062
- onEvents: (sessionId, events) => {
3063
- for (const event of events) {
3064
- const line = formatSessionLogLine(event);
3065
- if (line) writeLine(line);
3066
- }
3067
- getOrCreate(sessionEventMap, sessionId, () => []).push(...events.filter(({ type }) => !EphemeralAgentEventTypes.includes(type)));
3068
- if (events.some((event) => event.type === "SessionState" && event.state === "Closed")) sessionEventMap.delete(sessionId);
3069
- send({
3070
- events,
3071
- sessionId,
3072
- type: "Events"
3073
- });
3074
- },
3075
- onSessionOpen: (sessionId) => {
3076
- sessionEventMap.set(sessionId, []);
3077
- send({
3078
- sessionId,
3079
- type: "SessionOpen"
3080
- });
3081
- },
3082
- onSessionsChange: () => {
3083
- send({ type: "SessionsChange" });
3084
- }
3085
- });
3086
- const receive = async (text) => {
3087
- const command = getResult(() => driverCommandSchema.parse(JSON.parse(text))).orTee((error) => {
3088
- send({
3089
- commandId: "",
3090
- message: error.message,
3091
- type: "CommandError"
3092
- });
3093
- }).unwrapOr(void 0);
3094
- if (!command) return;
3095
- await getResultAsync(() => handleCommand(driver, command)).match((sessionId) => {
3096
- send({
3097
- commandId: command.id,
3098
- sessionId,
3099
- type: "CommandResult"
3100
- });
3101
- }, (error) => {
3102
- send({
3103
- commandId: command.id,
3104
- message: error.message,
3105
- type: "CommandError"
3106
- });
3107
- });
3108
- };
3109
- signal.addEventListener("abort", () => {
3110
- webSocket?.close();
3111
- });
3112
- let isRejoining = false;
3113
- while (!signal.aborted) {
3114
- const currentWebSocket = connect();
3115
- webSocket = currentWebSocket;
3116
- let isRefused = false;
3117
- currentWebSocket.on("message", (data) => {
3118
- taskRegistry.run(() => receive(readMessageText(data)));
3119
- });
3120
- currentWebSocket.on("error", noop);
3121
- currentWebSocket.on("unexpected-response", () => {
3122
- isRefused = true;
3123
- currentWebSocket.terminate();
3124
- });
3125
- if (isRejoining) currentWebSocket.once("open", () => {
3126
- writeLine("The host is back, and has this session again.");
3127
- send({
3128
- sessions: Array.from(sessionEventMap, ([sessionId, events]) => ({
3129
- events,
3130
- sessionId
3131
- })),
3132
- type: "Rejoin"
3133
- });
3134
- });
3135
- const [code] = await once(currentWebSocket, "close");
3136
- if (isRefused && isRejoining) writeLine("The host did not take this session back, so it ends here.");
3137
- if (signal.aborted || isRefused || code === 4e3 || sessionEventMap.size === 0) break;
3138
- if (!isRejoining) writeLine("The host went away. This session keeps running, and comes back when the host is started again.");
3139
- isRejoining = true;
3140
- await setTimeout$1(SESSION_WINDOW_RECONNECT_DELAY_MS);
3141
- }
3142
- await driver.close();
3143
- await taskRegistry.drain();
3144
- };
3145
- //#endregion
3146
- //#region src/services/drivers/window/runSessionChild.ts
3147
- const runSessionChild = async (port, writeLine) => {
3148
- const secret = process.env[SESSION_SECRET_ENVIRONMENT_VARIABLE] ?? "";
3149
- Reflect.deleteProperty(process.env, SESSION_SECRET_ENVIRONMENT_VARIABLE);
3150
- process.title = `${SITE_NAME} session`;
3151
- const stateDirectory = getStateDirectory();
3152
- let isFirstConnection = true;
3153
- const connect = () => {
3154
- const hostPort = isFirstConnection ? port : readSessionWindows(stateDirectory).port;
3155
- isFirstConnection = false;
3156
- return new WebSocket(`ws://${DEFAULT_HOSTNAME}:${hostPort}`, { headers: { authorization: `Bearer ${secret}` } });
3157
- };
3158
- const abortController = new AbortController();
3159
- const stop = () => {
3160
- abortController.abort();
3161
- };
3162
- process.once("SIGINT", stop);
3163
- process.once("SIGHUP", stop);
3164
- writeLine("This window shows one session as it runs. Type to it in the page.\nTo stop it, close this window.\n");
3165
- await serveSessionChild(connect, createClaudeAgentSdkDriver, writeLine, abortController.signal);
3166
- };
3167
- //#endregion
3168
- //#region src/services/cli/commands/sessionCommand.ts
3169
- const sessionCommand = defineCommand({
3170
- args: { port: {
3171
- description: "The port of the host that started this window",
3172
- required: true,
3173
- type: "string"
3174
- } },
3175
- meta: {
3176
- description: "Serve one session in a window of its own, for the host that started it",
3177
- name: SESSION_SUBCOMMAND
3178
- },
3179
- run: async ({ args }) => {
3180
- await runSessionChild(Number(args.port), writeLine);
3181
- process.exit(0);
3182
- }
3183
- });
3184
- //#endregion
3185
- //#region src/services/installer/uninstallHost.ts
3186
- const uninstallHost = () => {
3187
- execFileSync$1("reg", [
3188
- "delete",
3189
- HOST_SCHEME_REGISTRY_KEY,
3190
- "/f"
3191
- ], { stdio: "ignore" });
3192
- spawn$1("powershell.exe", [
3193
- "-NoProfile",
3194
- "-Command",
3195
- `Start-Sleep -Seconds 2; Remove-Item -LiteralPath $env:${HOST_DIRECTORY_ENVIRONMENT_VARIABLE} -Recurse -Force`
3196
- ], {
3197
- detached: true,
3198
- env: {
3199
- ...process.env,
3200
- [HOST_DIRECTORY_ENVIRONMENT_VARIABLE]: getHostInstallDirectory()
3201
- },
3202
- stdio: "ignore"
3203
- }).unref();
3204
- };
3205
- //#endregion
3206
- //#region src/services/cli/commands/uninstallCommand.ts
3207
- const uninstallCommand = defineCommand({
3208
- meta: {
3209
- description: "Remove the installed host, its link and its folder",
3210
- name: "uninstall"
3211
- },
3212
- run: () => {
3213
- uninstallHost();
3214
- process.stdout.write("The host is uninstalled, and its folder is removed once this window closes.\n");
3215
- process.exit(0);
3216
- }
3217
- });
3218
- //#endregion
3219
- //#region src/services/installer/checkIsSchemeLaunchTampered.ts
3220
- const checkIsSchemeLaunchTampered = (commandLineArguments) => commandLineArguments.length > 1 && commandLineArguments.some((commandLineArgument) => commandLineArgument.startsWith(`${HOST_SCHEME}:`));
3221
- //#endregion
3222
- //#region src/services/installer/getSchemeLaunch.ts
3223
- const getSchemeLaunch = (argument) => {
3224
- if (!URL.canParse(argument)) return void 0;
3225
- const url = new URL(argument);
3226
- if (url.protocol === `${HOST_SCHEME}:`) return { code: url.searchParams.get("code") ?? "" };
3227
- else return void 0;
3228
- };
3229
- //#endregion
3230
- //#region src/services/shell/constants.ts
3231
- const CONSOLE_LIST_AGENT_NAME = "conpty_console_list_agent";
3232
- //#endregion
3233
- //#region src/services/shell/runConsoleListAgent.ts
3234
- const runConsoleListAgent = (shellPid) => {
3235
- const installDirectory = getHostInstallDirectory();
3236
- const { getConsoleProcessList } = createRequire(join(installDirectory, "package.json"))(join(installDirectory, NODE_PTY_DIRECTORY_NAME, "prebuilds", "win32-x64", "conpty_console_list.node"));
3237
- process.send?.({ consoleProcessList: getConsoleProcessList(shellPid) });
3238
- };
3239
- //#endregion
3240
- //#region src/services/cli/commands/agentConsoleServerCommand.ts
3241
- const agentConsoleServerCommand = defineCommand({
3242
- default: "serve",
3243
- meta: {
3244
- description: "The agent console's host: Claude Code sessions for a paired page",
3245
- name: "agent-console-server"
3246
- },
3247
- setup: async ({ rawArgs }) => {
3248
- if (checkIsSchemeLaunchTampered(rawArgs)) {
3249
- process.stderr.write("This link tried to start the host with settings of its own, so it was not started.\n");
3250
- process.exit(1);
3251
- }
3252
- const [firstArgument = "", secondArgument = ""] = rawArgs;
3253
- if (basename(firstArgument) === "conpty_console_list_agent") {
3254
- runConsoleListAgent(Number(secondArgument));
3255
- process.exit(0);
3256
- }
3257
- const schemeLaunch = getSchemeLaunch(firstArgument);
3258
- if (!schemeLaunch) return;
3259
- await serveHost({
3260
- hostname: DEFAULT_HOSTNAME,
3261
- origin: DEFAULT_APP_ORIGIN,
3262
- port: DEFAULT_PORT,
3263
- schemeLaunch
3264
- });
3265
- process.exit(0);
3266
- },
3267
- subCommands: {
3268
- devices: devicesCommand,
3269
- serve: serveCommand,
3270
- [SESSION_SUBCOMMAND]: sessionCommand,
3271
- uninstall: uninstallCommand
3272
- }
3273
- });
3274
- //#endregion
3275
- export { childSessionOpenMessageSchema as $, signNonce as $t, createEventLog as A, mapAssistantContent as At, readSessionWindows as B, DEFAULT_DENY_MESSAGE as Bt, resolveRepositoryPath as C, hostArgs as Cn, toSessionInitEvent as Ct, getSessionCwd as D, mapSystemMessage as Dt, REPOSITORY_WALK_IGNORED_NAMES as E, toToolResultText as Et, launchSessionWindow as F, createFileOriginTracker as Ft, SESSION_WINDOWS_FILENAME as G, toSessionClosedEvents as Gt, SESSION_SECRET_BYTE_LENGTH as H, SESSION_LIST_LIMIT as Ht, createWindowDriver as I, createPermissionBridge as It, SESSION_WINDOW_REJOIN_DURATION_MS as J, runMain as Jt, SESSION_WINDOW_CONNECT_TIMEOUT_MS as K, getEventId as Kt, checkIsTokenValid as L, toPermissionResult as Lt, getPublicKeyText as M, createTodoTracker as Mt, getDeviceName as N, TodoToolName as Nt, handleCommand as O, mapTaskMessage as Ot, findDevice as P, createStreamTracker as Pt, childSessionsChangeMessageSchema as Q, HOST_PROBE_TIMEOUT_MS as Qt, takeChildReply as R, toPermissionRequestEvent as Rt, readRepositoryFile as S, NODE_PTY_DIRECTORY_NAME as Sn, toTurnResultEvents as St, REPOSITORY_FILE_MAX_BYTE_LENGTH as T, mapUserContent as Tt, SESSION_SECRET_ENVIRONMENT_VARIABLE as U, SETTING_SOURCES as Ut, sessionWindowsSchema as V, IGNORED_SDK_MESSAGE_TYPES as Vt, SESSION_SUBCOMMAND as W, closeOpenSession as Wt, SessionWindowCloseCode as X, sendToRunningHost as Xt, hashCredential as Y, runDevicesCommand as Yt, childMessageSchema as Z, readMessageText as Zt, createAgentConsoleServer as _, CLAUDE_CODE_EXECUTABLE_FILENAME as _n, toCapabilitiesEvent as _t, checkIsSchemeLaunchTampered as a, writeStateFile as an, ChildMessageType as at, handleShellCommand as b, HOST_INSTALL_DIRECTORY_SEGMENTS as bn, readSessionHistory as bt, sessionCommand as c, HOST_KEY_FILENAME as cn, createClaudeAgentSdkDriver as ct, formatSessionLogLine as d, STATE_DIRECTORY_NAME as dn, readSessionCwd as dt, checkIsSignatureValid as en, childRejoinMessageSchema as et, serveCommand as f, writeLine as fn, listSessionSummaries as ft, getShellFile as g, getHostInstallDirectory as gn, toContextUsageEvent as gt, loadNodePty as h, checkIsHostInstalled as hn, watchSession as ht, getSchemeLaunch as i, readHostKey as in, childCommandErrorMessageSchema as it, answerHttpRequest as j, toUnknownEvent as jt, createPairingCodes as k, mapHookMessage as kt, runSessionChild as l, PRINTED_PAIRING_CODE_DURATION_MS as ln, createTaskRegistry as lt, spawnPtyShell as m, installHost as mn, getClaudeCodeExecutablePath as mt, runConsoleListAgent as n, readDevices as nn, childEventsMessageSchema as nt, uninstallCommand as o, getStateDirectory as on, childMessageTypeSchema as ot, serveHost as p, installDownloadedHost as pn, createSessionOpener as pt, SESSION_WINDOW_RECONNECT_DELAY_MS as q, devicesCommand as qt, CONSOLE_LIST_AGENT_NAME as r, deviceSchema as rn, childCommandResultMessageSchema as rt, uninstallHost as s, DEVICES_FILENAME as sn, createBaseChildMessageSchema as st, agentConsoleServerCommand as t, writeDevices as tn, rejoiningSessionSchema as tt, serveSessionChild as u, SECRET_BYTE_LENGTH as un, toContentBlockParam as ut, createShellRegistry as v, HOST_DIRECTORY_ENVIRONMENT_VARIABLE as vn, getSessionTitle as vt, listRepositoryFiles as w, getReachableHostname as wn, parseMessageBody as wt, handleFileCommand as x, HOST_SCHEME_REGISTRY_KEY as xn, createSdkMessageMapper as xt, sendServerMessage as y, HOST_EXECUTABLE_FILENAME as yn, readToolUseResultMap as yt, sendChildCommand as z, createInputQueue as zt };