@slayzone/computer 0.36.3 → 0.36.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.cjs +528 -100
- package/dist/bin.cjs.map +4 -4
- package/package.json +1 -1
package/dist/bin.cjs
CHANGED
|
@@ -1453,7 +1453,7 @@ var require_receiver = __commonJS({
|
|
|
1453
1453
|
var require_sender = __commonJS({
|
|
1454
1454
|
"../../../node_modules/ws/lib/sender.js"(exports2, module2) {
|
|
1455
1455
|
"use strict";
|
|
1456
|
-
var { Duplex } = require("stream");
|
|
1456
|
+
var { Duplex: Duplex2 } = require("stream");
|
|
1457
1457
|
var { randomFillSync } = require("crypto");
|
|
1458
1458
|
var {
|
|
1459
1459
|
types: { isUint8Array }
|
|
@@ -2330,11 +2330,11 @@ var require_websocket = __commonJS({
|
|
|
2330
2330
|
"use strict";
|
|
2331
2331
|
var EventEmitter = require("events");
|
|
2332
2332
|
var https = require("https");
|
|
2333
|
-
var
|
|
2333
|
+
var http3 = require("http");
|
|
2334
2334
|
var net = require("net");
|
|
2335
2335
|
var tls = require("tls");
|
|
2336
|
-
var { randomBytes, createHash } = require("crypto");
|
|
2337
|
-
var { Duplex, Readable } = require("stream");
|
|
2336
|
+
var { randomBytes: randomBytes2, createHash } = require("crypto");
|
|
2337
|
+
var { Duplex: Duplex2, Readable } = require("stream");
|
|
2338
2338
|
var { URL: URL2 } = require("url");
|
|
2339
2339
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
2340
2340
|
var Receiver2 = require_receiver();
|
|
@@ -2884,8 +2884,8 @@ var require_websocket = __commonJS({
|
|
|
2884
2884
|
}
|
|
2885
2885
|
}
|
|
2886
2886
|
const defaultPort = isSecure ? 443 : 80;
|
|
2887
|
-
const key =
|
|
2888
|
-
const request = isSecure ? https.request :
|
|
2887
|
+
const key = randomBytes2(16).toString("base64");
|
|
2888
|
+
const request = isSecure ? https.request : http3.request;
|
|
2889
2889
|
const protocolSet = /* @__PURE__ */ new Set();
|
|
2890
2890
|
let perMessageDeflate;
|
|
2891
2891
|
opts.createConnection = opts.createConnection || (isSecure ? tlsConnect : netConnect);
|
|
@@ -3238,7 +3238,7 @@ var require_stream = __commonJS({
|
|
|
3238
3238
|
"../../../node_modules/ws/lib/stream.js"(exports2, module2) {
|
|
3239
3239
|
"use strict";
|
|
3240
3240
|
var WebSocket2 = require_websocket();
|
|
3241
|
-
var { Duplex } = require("stream");
|
|
3241
|
+
var { Duplex: Duplex2 } = require("stream");
|
|
3242
3242
|
function emitClose(stream) {
|
|
3243
3243
|
stream.emit("close");
|
|
3244
3244
|
}
|
|
@@ -3256,7 +3256,7 @@ var require_stream = __commonJS({
|
|
|
3256
3256
|
}
|
|
3257
3257
|
function createWebSocketStream2(ws, options) {
|
|
3258
3258
|
let terminateOnDestroy = true;
|
|
3259
|
-
const duplex = new
|
|
3259
|
+
const duplex = new Duplex2({
|
|
3260
3260
|
...options,
|
|
3261
3261
|
autoDestroy: false,
|
|
3262
3262
|
emitClose: false,
|
|
@@ -3381,8 +3381,8 @@ var require_websocket_server = __commonJS({
|
|
|
3381
3381
|
"../../../node_modules/ws/lib/websocket-server.js"(exports2, module2) {
|
|
3382
3382
|
"use strict";
|
|
3383
3383
|
var EventEmitter = require("events");
|
|
3384
|
-
var
|
|
3385
|
-
var { Duplex } = require("stream");
|
|
3384
|
+
var http3 = require("http");
|
|
3385
|
+
var { Duplex: Duplex2 } = require("stream");
|
|
3386
3386
|
var { createHash } = require("crypto");
|
|
3387
3387
|
var extension2 = require_extension();
|
|
3388
3388
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -3462,8 +3462,8 @@ var require_websocket_server = __commonJS({
|
|
|
3462
3462
|
);
|
|
3463
3463
|
}
|
|
3464
3464
|
if (options.port != null) {
|
|
3465
|
-
this._server =
|
|
3466
|
-
const body =
|
|
3465
|
+
this._server = http3.createServer((req, res) => {
|
|
3466
|
+
const body = http3.STATUS_CODES[426];
|
|
3467
3467
|
res.writeHead(426, {
|
|
3468
3468
|
"Content-Length": body.length,
|
|
3469
3469
|
"Content-Type": "text/plain"
|
|
@@ -3752,7 +3752,7 @@ var require_websocket_server = __commonJS({
|
|
|
3752
3752
|
this.destroy();
|
|
3753
3753
|
}
|
|
3754
3754
|
function abortHandshake(socket, code, message, headers) {
|
|
3755
|
-
message = message ||
|
|
3755
|
+
message = message || http3.STATUS_CODES[code];
|
|
3756
3756
|
headers = {
|
|
3757
3757
|
Connection: "close",
|
|
3758
3758
|
"Content-Type": "text/html",
|
|
@@ -3761,7 +3761,7 @@ var require_websocket_server = __commonJS({
|
|
|
3761
3761
|
};
|
|
3762
3762
|
socket.once("finish", socket.destroy);
|
|
3763
3763
|
socket.end(
|
|
3764
|
-
`HTTP/1.1 ${code} ${
|
|
3764
|
+
`HTTP/1.1 ${code} ${http3.STATUS_CODES[code]}\r
|
|
3765
3765
|
` + Object.keys(headers).map((h) => `${h}: ${headers[h]}`).join("\r\n") + "\r\n\r\n" + message
|
|
3766
3766
|
);
|
|
3767
3767
|
}
|
|
@@ -5220,10 +5220,10 @@ var init_agent_hooks = __esm({
|
|
|
5220
5220
|
}
|
|
5221
5221
|
});
|
|
5222
5222
|
|
|
5223
|
-
// raw-import:/tmp/slz-publish-wt.
|
|
5223
|
+
// raw-import:/tmp/slz-publish-wt.a2Pyg4/packages/shared/hooks/src/notify.sh
|
|
5224
5224
|
var notify_default;
|
|
5225
5225
|
var init_notify = __esm({
|
|
5226
|
-
"raw-import:/tmp/slz-publish-wt.
|
|
5226
|
+
"raw-import:/tmp/slz-publish-wt.a2Pyg4/packages/shared/hooks/src/notify.sh"() {
|
|
5227
5227
|
notify_default = `#!/bin/sh
|
|
5228
5228
|
# SlayZone agent lifecycle hook \u2014 BENIGN DUMB FORWARDER.
|
|
5229
5229
|
#
|
|
@@ -5385,10 +5385,10 @@ exit 0
|
|
|
5385
5385
|
}
|
|
5386
5386
|
});
|
|
5387
5387
|
|
|
5388
|
-
// raw-import:/tmp/slz-publish-wt.
|
|
5388
|
+
// raw-import:/tmp/slz-publish-wt.a2Pyg4/packages/shared/hooks/src/opencode-plugin.js
|
|
5389
5389
|
var opencode_plugin_default;
|
|
5390
5390
|
var init_opencode_plugin = __esm({
|
|
5391
|
-
"raw-import:/tmp/slz-publish-wt.
|
|
5391
|
+
"raw-import:/tmp/slz-publish-wt.a2Pyg4/packages/shared/hooks/src/opencode-plugin.js"() {
|
|
5392
5392
|
opencode_plugin_default = "// SlayZone opencode plugin v1\n//\n// Forwards OpenCode SDK session lifecycle events to the SlayZone agent-hook\n// notify script over a local shell exec. Plugin is loaded by OpenCode from\n// ${XDG_CONFIG_HOME:-~/.config}/opencode/plugin/slayzone-notify.js\n//\n// The {{NOTIFY_PATH}} placeholder is substituted by the SlayZone installer\n// (packages/apps/app/src/main/agent-hooks/opencode-plugin-installer.ts) at\n// install time with the absolute path to `~/.slayzone/hooks/notify.sh`.\n//\n// Behavior:\n// - No-op when SLAYZONE_TASK_ID is unset (plugin only fires inside SlayZone PTYs).\n// - Subagent (child session) events are suppressed \u2014 oh-my-opencode + similar\n// tools spawn many child sessions and would otherwise flap Start/Stop.\n// - On client.session.list() error, sessions are assumed CHILD (safer to\n// suppress than emit a false-positive Start/Stop).\n// - Handles both modern (session.status w/ nested type) and legacy\n// (session.busy / session.idle) event shapes for forward compat.\n// - Singleton guard prevents double-load if OpenCode re-imports the plugin.\n\nexport const SlayzoneNotifyPlugin = async ({ $, client }) => {\n if (globalThis.__slayzoneOpencodePluginV1) return {}\n globalThis.__slayzoneOpencodePluginV1 = true\n if (!process?.env?.SLAYZONE_TASK_ID) return {}\n\n const notifyPath = '{{NOTIFY_PATH}}'\n let currentState = 'idle' // 'idle' | 'busy'\n let rootSessionID = null // first non-child session we see\n let stopSent = false // dedup Stop emits\n const childSessionCache = new Map() // sessionID -> isChild bool\n\n const notify = async (eventName) => {\n const payload = JSON.stringify({ hook_event_name: eventName })\n try {\n await $`bash ${notifyPath} ${payload}`\n } catch {\n // Hook failures must never bubble into the OpenCode TUI.\n }\n }\n\n const isChildSession = async (sessionID) => {\n if (!sessionID) return true\n if (!client?.session?.list) return true\n if (childSessionCache.has(sessionID)) return childSessionCache.get(sessionID)\n try {\n const sessions = await client.session.list()\n const session = sessions.data?.find((s) => s.id === sessionID)\n const isChild = !!session?.parentID\n childSessionCache.set(sessionID, isChild)\n return isChild\n } catch {\n // On error, assume CHILD \u2014 safer than emitting a false-positive Start/Stop.\n return true\n }\n }\n\n const handleBusy = async (sessionID) => {\n if (!rootSessionID) rootSessionID = sessionID\n if (sessionID !== rootSessionID) return\n if (currentState === 'idle') {\n currentState = 'busy'\n stopSent = false\n await notify('Start')\n }\n }\n\n const handleStop = async (sessionID) => {\n if (rootSessionID && sessionID !== rootSessionID) return\n if (currentState === 'busy' && !stopSent) {\n currentState = 'idle'\n stopSent = true\n await notify('Stop')\n rootSessionID = null // reset for next session\n }\n }\n\n return {\n event: async ({ event }) => {\n const sessionID = event.properties?.sessionID ?? event.properties?.info?.id ?? null\n\n if (event.type === 'session.created') {\n const isChild = Boolean(event.properties?.info?.parentID)\n if (sessionID) childSessionCache.set(sessionID, isChild)\n if (!isChild) await notify('SessionStart')\n return\n }\n\n if (event.type === 'session.deleted') {\n const cached = sessionID ? childSessionCache.get(sessionID) : undefined\n const isChild = cached !== undefined ? cached : await isChildSession(sessionID)\n if (!isChild) await notify('SessionEnd')\n if (sessionID) childSessionCache.delete(sessionID)\n return\n }\n\n if (await isChildSession(sessionID)) return\n\n if (event.type === 'session.status') {\n const status = event.properties?.status\n if (status?.type === 'busy') await handleBusy(sessionID)\n else if (status?.type === 'idle') await handleStop(sessionID)\n }\n // Backwards-compat: some OpenCode versions emit busy/idle as top-level events.\n if (event.type === 'session.busy') await handleBusy(sessionID)\n if (event.type === 'session.idle') await handleStop(sessionID)\n if (event.type === 'session.error') await handleStop(sessionID)\n },\n 'permission.ask': async (_permission, output) => {\n if (output.status === 'ask') await notify('PermissionRequest')\n }\n }\n}\n";
|
|
5393
5393
|
}
|
|
5394
5394
|
});
|
|
@@ -5431,7 +5431,7 @@ var init_agent_hooks2 = __esm({
|
|
|
5431
5431
|
});
|
|
5432
5432
|
|
|
5433
5433
|
// src/bin.ts
|
|
5434
|
-
var
|
|
5434
|
+
var import_node_os11 = require("node:os");
|
|
5435
5435
|
var import_node_path18 = require("node:path");
|
|
5436
5436
|
|
|
5437
5437
|
// ../../shared/platform/src/config-prompt.ts
|
|
@@ -25569,7 +25569,7 @@ function date4(params) {
|
|
|
25569
25569
|
|
|
25570
25570
|
// ../../domains/computer-transport/src/shared/frames.ts
|
|
25571
25571
|
var COMPUTER_EXIT_NEEDS_RE_ENROLLMENT = 78;
|
|
25572
|
-
var COMPUTER_PROTOCOL_VERSION =
|
|
25572
|
+
var COMPUTER_PROTOCOL_VERSION = 4;
|
|
25573
25573
|
var ComputerTransportErrorCodes = {
|
|
25574
25574
|
/** Command is part of the contract but not implemented by this peer. */
|
|
25575
25575
|
unimplemented: -32001,
|
|
@@ -25671,12 +25671,14 @@ var ComputerNotificationMethods = {
|
|
|
25671
25671
|
};
|
|
25672
25672
|
var ptyDataParamsSchema = external_exports.object({
|
|
25673
25673
|
sessionId: external_exports.string().min(1),
|
|
25674
|
+
spawnId: external_exports.string().min(1).optional(),
|
|
25674
25675
|
/** Monotonic per-session sequence number, preserved end-to-end. */
|
|
25675
25676
|
seq: external_exports.number().int().nonnegative(),
|
|
25676
25677
|
data: external_exports.string()
|
|
25677
25678
|
});
|
|
25678
25679
|
var ptyExitParamsSchema = external_exports.object({
|
|
25679
25680
|
sessionId: external_exports.string().min(1),
|
|
25681
|
+
spawnId: external_exports.string().min(1).optional(),
|
|
25680
25682
|
exitCode: external_exports.number().int().nullable(),
|
|
25681
25683
|
signal: external_exports.string().nullable().optional(),
|
|
25682
25684
|
/**
|
|
@@ -25828,6 +25830,9 @@ var ptySpawnParamsSchema = external_exports.object({
|
|
|
25828
25830
|
startupTimeoutMs: external_exports.number().int().positive().optional(),
|
|
25829
25831
|
command: external_exports.string().min(1),
|
|
25830
25832
|
args: external_exports.array(external_exports.string()).optional(),
|
|
25833
|
+
/** New computers resolve their own login shell; command/args remain for older peers. */
|
|
25834
|
+
computerShell: external_exports.boolean().optional(),
|
|
25835
|
+
computerShellCommand: external_exports.string().optional(),
|
|
25831
25836
|
/**
|
|
25832
25837
|
* Absolute cwd. Optional now: when absent the COMPUTER resolves it from
|
|
25833
25838
|
* `projectId` against its own `project.*` mapping, because the hub cannot —
|
|
@@ -25846,6 +25851,19 @@ var ptySpawnParamsSchema = external_exports.object({
|
|
|
25846
25851
|
}).refine((p) => p.cwd !== void 0 || p.projectId !== void 0, {
|
|
25847
25852
|
message: "pty.spawn needs either cwd or projectId"
|
|
25848
25853
|
});
|
|
25854
|
+
var HUB_PTY_ENV_KEYS = [
|
|
25855
|
+
"SLAYZONE_TASK_ID",
|
|
25856
|
+
"SLAYZONE_PROJECT_ID",
|
|
25857
|
+
"SLAYZONE_AGENT_ID",
|
|
25858
|
+
"SLAYZONE_AGENT_HOOK_CONTEXT"
|
|
25859
|
+
];
|
|
25860
|
+
function pickHubPtyEnv(env) {
|
|
25861
|
+
const picked = {};
|
|
25862
|
+
for (const key of HUB_PTY_ENV_KEYS) {
|
|
25863
|
+
if (env?.[key] !== void 0) picked[key] = env[key];
|
|
25864
|
+
}
|
|
25865
|
+
return picked;
|
|
25866
|
+
}
|
|
25849
25867
|
var ptySpawnResultSchema = external_exports.object({
|
|
25850
25868
|
pid: external_exports.number().int(),
|
|
25851
25869
|
/** The computer armed the requested `startupTimeoutMs`. Absent from a computer
|
|
@@ -25854,10 +25872,12 @@ var ptySpawnResultSchema = external_exports.object({
|
|
|
25854
25872
|
});
|
|
25855
25873
|
var ptyKillParamsSchema = external_exports.object({
|
|
25856
25874
|
sessionId: external_exports.string().min(1),
|
|
25875
|
+
spawnId: spawnIdField,
|
|
25857
25876
|
signal: external_exports.string().optional()
|
|
25858
25877
|
});
|
|
25859
25878
|
var ptyPrioritizeParamsSchema = external_exports.object({
|
|
25860
|
-
sessionId: external_exports.string().min(1)
|
|
25879
|
+
sessionId: external_exports.string().min(1),
|
|
25880
|
+
spawnId: spawnIdField
|
|
25861
25881
|
});
|
|
25862
25882
|
var ptyPrioritizeResultSchema = external_exports.object({
|
|
25863
25883
|
ok: external_exports.literal(true),
|
|
@@ -25865,6 +25885,7 @@ var ptyPrioritizeResultSchema = external_exports.object({
|
|
|
25865
25885
|
});
|
|
25866
25886
|
var ptyBackgroundJobsParamsSchema = external_exports.object({
|
|
25867
25887
|
sessionId: external_exports.string().min(1),
|
|
25888
|
+
spawnId: spawnIdField,
|
|
25868
25889
|
jobs: external_exports.array(
|
|
25869
25890
|
external_exports.object({
|
|
25870
25891
|
id: external_exports.string().min(1),
|
|
@@ -25893,15 +25914,18 @@ var ptyDoctorResultSchema = external_exports.object({
|
|
|
25893
25914
|
});
|
|
25894
25915
|
var ptyResizeParamsSchema = external_exports.object({
|
|
25895
25916
|
sessionId: external_exports.string().min(1),
|
|
25917
|
+
spawnId: spawnIdField,
|
|
25896
25918
|
cols: external_exports.number().int().positive(),
|
|
25897
25919
|
rows: external_exports.number().int().positive()
|
|
25898
25920
|
});
|
|
25899
25921
|
var ptyWriteParamsSchema = external_exports.object({
|
|
25900
25922
|
sessionId: external_exports.string().min(1),
|
|
25923
|
+
spawnId: spawnIdField,
|
|
25901
25924
|
data: external_exports.string()
|
|
25902
25925
|
});
|
|
25903
25926
|
var ptyGetBufferSinceParamsSchema = external_exports.object({
|
|
25904
25927
|
sessionId: external_exports.string().min(1),
|
|
25928
|
+
spawnId: spawnIdField,
|
|
25905
25929
|
/**
|
|
25906
25930
|
* Exclusive lower bound — frames with `seq > this` are replayed.
|
|
25907
25931
|
*
|
|
@@ -25943,6 +25967,7 @@ var ptyWarmSpawnParamsSchema = external_exports.object({
|
|
|
25943
25967
|
warmId: external_exports.string().min(1),
|
|
25944
25968
|
command: external_exports.string().min(1),
|
|
25945
25969
|
args: external_exports.array(external_exports.string()).optional(),
|
|
25970
|
+
computerShell: external_exports.boolean().optional(),
|
|
25946
25971
|
cwd: external_exports.string().min(1),
|
|
25947
25972
|
env: external_exports.record(external_exports.string(), external_exports.string()).optional(),
|
|
25948
25973
|
cols: external_exports.number().int().positive().optional(),
|
|
@@ -26432,6 +26457,20 @@ function wsDataToText(data) {
|
|
|
26432
26457
|
return data.toString("utf8");
|
|
26433
26458
|
}
|
|
26434
26459
|
|
|
26460
|
+
// ../../domains/computer-transport/src/shared/http-tunnel.ts
|
|
26461
|
+
var HTTP_TUNNEL_PREFIX = "cli.v1.";
|
|
26462
|
+
var HTTP_TUNNEL_CHUNK = 32 * 1024;
|
|
26463
|
+
var httpTunnelParamsSchema = external_exports.object({
|
|
26464
|
+
id: external_exports.string().min(1).max(100),
|
|
26465
|
+
data: external_exports.string().max(Math.ceil(HTTP_TUNNEL_CHUNK / 3) * 4).optional(),
|
|
26466
|
+
end: external_exports.boolean().optional()
|
|
26467
|
+
}).strict();
|
|
26468
|
+
var httpTunnelOpenResultSchema = external_exports.object({ id: external_exports.string().min(1).max(100) });
|
|
26469
|
+
var httpTunnelReadResultSchema = external_exports.object({
|
|
26470
|
+
data: external_exports.string().max(Math.ceil(HTTP_TUNNEL_CHUNK / 3) * 4),
|
|
26471
|
+
end: external_exports.boolean()
|
|
26472
|
+
});
|
|
26473
|
+
|
|
26435
26474
|
// ../../domains/computer-transport/src/client/backoff.ts
|
|
26436
26475
|
var DEFAULT_BACKOFF = {
|
|
26437
26476
|
initialDelayMs: 100,
|
|
@@ -26925,6 +26964,64 @@ var HubDialer = class {
|
|
|
26925
26964
|
}
|
|
26926
26965
|
};
|
|
26927
26966
|
|
|
26967
|
+
// ../../domains/computer-transport/src/client/http-tunnel.ts
|
|
26968
|
+
var import_node_stream = require("node:stream");
|
|
26969
|
+
async function openHttpTunnel(requester) {
|
|
26970
|
+
const { id } = httpTunnelOpenResultSchema.parse(
|
|
26971
|
+
await requester.request(`${HTTP_TUNNEL_PREFIX}open`)
|
|
26972
|
+
);
|
|
26973
|
+
let reading = false;
|
|
26974
|
+
const socket = new import_node_stream.Duplex({
|
|
26975
|
+
read() {
|
|
26976
|
+
if (reading) return;
|
|
26977
|
+
reading = true;
|
|
26978
|
+
void (async () => {
|
|
26979
|
+
try {
|
|
26980
|
+
while (!socket.destroyed) {
|
|
26981
|
+
const result = httpTunnelReadResultSchema.parse(
|
|
26982
|
+
await requester.request(`${HTTP_TUNNEL_PREFIX}read`, { id })
|
|
26983
|
+
);
|
|
26984
|
+
const more = result.data ? socket.push(Buffer.from(result.data, "base64")) : true;
|
|
26985
|
+
if (result.end) {
|
|
26986
|
+
socket.push(null);
|
|
26987
|
+
break;
|
|
26988
|
+
}
|
|
26989
|
+
if (!more) break;
|
|
26990
|
+
}
|
|
26991
|
+
} catch (error62) {
|
|
26992
|
+
socket.destroy(error62 instanceof Error ? error62 : new Error(String(error62)));
|
|
26993
|
+
} finally {
|
|
26994
|
+
reading = false;
|
|
26995
|
+
}
|
|
26996
|
+
})();
|
|
26997
|
+
},
|
|
26998
|
+
write(chunk, _encoding, done) {
|
|
26999
|
+
void (async () => {
|
|
27000
|
+
for (let offset = 0; offset < chunk.length; offset += HTTP_TUNNEL_CHUNK) {
|
|
27001
|
+
await requester.request(`${HTTP_TUNNEL_PREFIX}write`, {
|
|
27002
|
+
id,
|
|
27003
|
+
data: chunk.subarray(offset, offset + HTTP_TUNNEL_CHUNK).toString("base64")
|
|
27004
|
+
});
|
|
27005
|
+
}
|
|
27006
|
+
})().then(() => done(), done);
|
|
27007
|
+
},
|
|
27008
|
+
final(done) {
|
|
27009
|
+
void requester.request(`${HTTP_TUNNEL_PREFIX}write`, { id, end: true }).then(() => done(), done);
|
|
27010
|
+
},
|
|
27011
|
+
destroy(error62, done) {
|
|
27012
|
+
void requester.request(`${HTTP_TUNNEL_PREFIX}close`, { id }).catch(() => {
|
|
27013
|
+
});
|
|
27014
|
+
done(error62);
|
|
27015
|
+
}
|
|
27016
|
+
});
|
|
27017
|
+
Object.assign(socket, {
|
|
27018
|
+
setTimeout: () => socket,
|
|
27019
|
+
setNoDelay: () => socket,
|
|
27020
|
+
setKeepAlive: () => socket
|
|
27021
|
+
});
|
|
27022
|
+
return socket;
|
|
27023
|
+
}
|
|
27024
|
+
|
|
26928
27025
|
// src/config.ts
|
|
26929
27026
|
var import_node_fs2 = require("node:fs");
|
|
26930
27027
|
var import_node_os2 = require("node:os");
|
|
@@ -27446,8 +27543,10 @@ function assertHubLocalExecEnabled(refusal) {
|
|
|
27446
27543
|
|
|
27447
27544
|
// ../../shared/platform/src/env-manifest.ts
|
|
27448
27545
|
var ENV_MANIFEST = {
|
|
27449
|
-
// --- secret: credentials/tokens, never
|
|
27546
|
+
// --- secret: credentials/tokens, never inherited into a terminal ---
|
|
27450
27547
|
SLAYZONE_HUB_TOKEN: "secret",
|
|
27548
|
+
SLAYZONE_CLI_RELAY_TOKEN: "secret",
|
|
27549
|
+
// computer-issued capability, re-added per session
|
|
27451
27550
|
// HMAC signing key for ALL of hub-auth (better-auth's session/cookie signer AND
|
|
27452
27551
|
// the computer enroll/api-key credentials) — hence `HUB_AUTH`, not the former
|
|
27453
27552
|
// `HUB_COMPUTER_TRANSPORT_SECRET`, which named just one of its consumers. Rule 2
|
|
@@ -27482,6 +27581,8 @@ var ENV_MANIFEST = {
|
|
|
27482
27581
|
// name refuses to boot. Stripping both here keeps a terminal's dial address from
|
|
27483
27582
|
// ever being mistaken for another process's listen address.
|
|
27484
27583
|
SLAYZONE_HUB_ADDRESS: "infra",
|
|
27584
|
+
SLAYZONE_CLI_RELAY_ADDRESS: "infra",
|
|
27585
|
+
// computer loopback; re-added per session
|
|
27485
27586
|
SLAYZONE_HUB_LISTEN_ADDRESS: "infra",
|
|
27486
27587
|
// hub-only; where the hub listens
|
|
27487
27588
|
SLAYZONE_HUB_PUBLIC_ADDRESS: "infra",
|
|
@@ -28089,6 +28190,155 @@ function createAgentHookServer(ctx) {
|
|
|
28089
28190
|
});
|
|
28090
28191
|
}
|
|
28091
28192
|
|
|
28193
|
+
// src/cli-relay.ts
|
|
28194
|
+
var import_node_http2 = __toESM(require("node:http"), 1);
|
|
28195
|
+
var import_node_crypto3 = require("node:crypto");
|
|
28196
|
+
function responseHeaders(headers) {
|
|
28197
|
+
const excluded = /* @__PURE__ */ new Set([
|
|
28198
|
+
"connection",
|
|
28199
|
+
"keep-alive",
|
|
28200
|
+
"proxy-authenticate",
|
|
28201
|
+
"proxy-authorization",
|
|
28202
|
+
"proxy-connection",
|
|
28203
|
+
"te",
|
|
28204
|
+
"trailer",
|
|
28205
|
+
"transfer-encoding",
|
|
28206
|
+
"upgrade",
|
|
28207
|
+
...(headers.connection ?? "").split(",").map((name) => name.trim().toLowerCase())
|
|
28208
|
+
]);
|
|
28209
|
+
return Object.fromEntries(Object.entries(headers).filter(([name]) => !excluded.has(name)));
|
|
28210
|
+
}
|
|
28211
|
+
async function createComputerCliRelay(requester) {
|
|
28212
|
+
const tokens = /* @__PURE__ */ new Map();
|
|
28213
|
+
const responses = /* @__PURE__ */ new Set();
|
|
28214
|
+
const active = /* @__PURE__ */ new Set();
|
|
28215
|
+
const server = import_node_http2.default.createServer((req, res) => {
|
|
28216
|
+
const token = req.headers.authorization?.replace(/^Bearer /, "") ?? "";
|
|
28217
|
+
const requests = tokens.get(token);
|
|
28218
|
+
if (!requests) {
|
|
28219
|
+
res.writeHead(401, { "content-type": "application/json" }).end(
|
|
28220
|
+
JSON.stringify({
|
|
28221
|
+
error: "CLI relay capability is missing or expired. Open a new task terminal."
|
|
28222
|
+
})
|
|
28223
|
+
);
|
|
28224
|
+
return;
|
|
28225
|
+
}
|
|
28226
|
+
if (responses.size >= 32) {
|
|
28227
|
+
res.writeHead(429).end("Too many CLI relay requests");
|
|
28228
|
+
return;
|
|
28229
|
+
}
|
|
28230
|
+
let upstream;
|
|
28231
|
+
let receivedResponse = false;
|
|
28232
|
+
let cancelled = false;
|
|
28233
|
+
responses.add(res);
|
|
28234
|
+
requests.add(res);
|
|
28235
|
+
res.on("close", () => {
|
|
28236
|
+
responses.delete(res);
|
|
28237
|
+
requests.delete(res);
|
|
28238
|
+
cancelled = true;
|
|
28239
|
+
upstream?.destroy();
|
|
28240
|
+
});
|
|
28241
|
+
void openHttpTunnel(requester).then((socket) => {
|
|
28242
|
+
if (cancelled || !tokens.has(token)) {
|
|
28243
|
+
socket.destroy();
|
|
28244
|
+
return;
|
|
28245
|
+
}
|
|
28246
|
+
const headers = { connection: "close" };
|
|
28247
|
+
for (const name of ["content-type", "content-length", "accept", "last-event-id"]) {
|
|
28248
|
+
if (req.headers[name] !== void 0) headers[name] = req.headers[name];
|
|
28249
|
+
}
|
|
28250
|
+
const agent = new import_node_http2.default.Agent({ keepAlive: false });
|
|
28251
|
+
agent.createConnection = () => socket;
|
|
28252
|
+
upstream = import_node_http2.default.request(
|
|
28253
|
+
{
|
|
28254
|
+
host: "relay",
|
|
28255
|
+
path: req.url,
|
|
28256
|
+
method: req.method,
|
|
28257
|
+
headers,
|
|
28258
|
+
agent
|
|
28259
|
+
},
|
|
28260
|
+
(response) => {
|
|
28261
|
+
receivedResponse = true;
|
|
28262
|
+
const stopUpload = () => {
|
|
28263
|
+
req.unpipe(upstream);
|
|
28264
|
+
req.resume();
|
|
28265
|
+
};
|
|
28266
|
+
if ((response.statusCode ?? 502) >= 400) stopUpload();
|
|
28267
|
+
response.on("end", stopUpload);
|
|
28268
|
+
res.writeHead(response.statusCode ?? 502, responseHeaders(response.headers));
|
|
28269
|
+
response.on("error", () => res.destroy());
|
|
28270
|
+
response.pipe(res);
|
|
28271
|
+
}
|
|
28272
|
+
);
|
|
28273
|
+
active.add(upstream);
|
|
28274
|
+
const current = upstream;
|
|
28275
|
+
upstream.on("close", () => {
|
|
28276
|
+
active.delete(current);
|
|
28277
|
+
agent.destroy();
|
|
28278
|
+
});
|
|
28279
|
+
upstream.on("error", () => {
|
|
28280
|
+
if (receivedResponse) return;
|
|
28281
|
+
if (res.headersSent) res.destroy();
|
|
28282
|
+
else
|
|
28283
|
+
res.writeHead(503, { "content-type": "application/json" }).end(
|
|
28284
|
+
JSON.stringify({
|
|
28285
|
+
error: "CLI relay lost its hub connection. Check the computer connection, then retry."
|
|
28286
|
+
})
|
|
28287
|
+
);
|
|
28288
|
+
});
|
|
28289
|
+
req.on("aborted", () => upstream?.destroy());
|
|
28290
|
+
req.pipe(upstream);
|
|
28291
|
+
}).catch(() => {
|
|
28292
|
+
if (!res.headersSent)
|
|
28293
|
+
res.writeHead(503, { "content-type": "application/json" }).end(
|
|
28294
|
+
JSON.stringify({
|
|
28295
|
+
error: "CLI relay unavailable. Check the computer connection and update hub and computer together."
|
|
28296
|
+
})
|
|
28297
|
+
);
|
|
28298
|
+
});
|
|
28299
|
+
});
|
|
28300
|
+
await new Promise((resolve9, reject) => {
|
|
28301
|
+
server.once("error", reject);
|
|
28302
|
+
server.listen(0, "127.0.0.1", resolve9);
|
|
28303
|
+
});
|
|
28304
|
+
const address = server.address();
|
|
28305
|
+
if (!address || typeof address === "string") throw new Error("CLI relay failed to bind");
|
|
28306
|
+
const disconnect = () => {
|
|
28307
|
+
for (const response of responses) {
|
|
28308
|
+
if (response.headersSent) response.destroy();
|
|
28309
|
+
else
|
|
28310
|
+
response.writeHead(503, { "content-type": "application/json" }).end(
|
|
28311
|
+
JSON.stringify({
|
|
28312
|
+
error: "CLI relay lost its hub connection. Check the computer connection, then retry."
|
|
28313
|
+
})
|
|
28314
|
+
);
|
|
28315
|
+
}
|
|
28316
|
+
for (const request of active) request.destroy(new Error("hub disconnected"));
|
|
28317
|
+
};
|
|
28318
|
+
return {
|
|
28319
|
+
url: `http://127.0.0.1:${address.port}`,
|
|
28320
|
+
issue() {
|
|
28321
|
+
const token = (0, import_node_crypto3.randomBytes)(32).toString("hex");
|
|
28322
|
+
const requests = /* @__PURE__ */ new Set();
|
|
28323
|
+
tokens.set(token, requests);
|
|
28324
|
+
return {
|
|
28325
|
+
token,
|
|
28326
|
+
revoke() {
|
|
28327
|
+
tokens.delete(token);
|
|
28328
|
+
for (const response of requests) response.destroy();
|
|
28329
|
+
}
|
|
28330
|
+
};
|
|
28331
|
+
},
|
|
28332
|
+
disconnect,
|
|
28333
|
+
async close() {
|
|
28334
|
+
tokens.clear();
|
|
28335
|
+
disconnect();
|
|
28336
|
+
server.closeAllConnections();
|
|
28337
|
+
await new Promise((resolve9) => server.close(() => resolve9()));
|
|
28338
|
+
}
|
|
28339
|
+
};
|
|
28340
|
+
}
|
|
28341
|
+
|
|
28092
28342
|
// src/handlers/fs.ts
|
|
28093
28343
|
var import_node_fs7 = require("node:fs");
|
|
28094
28344
|
var import_promises7 = require("node:fs/promises");
|
|
@@ -31613,18 +31863,37 @@ function resolveCommand(command, env, cwd) {
|
|
|
31613
31863
|
}
|
|
31614
31864
|
|
|
31615
31865
|
// src/handlers/spawn-env.ts
|
|
31616
|
-
|
|
31866
|
+
var import_node_os9 = require("node:os");
|
|
31867
|
+
function buildSpawnEnv(ctx, overrides, capability) {
|
|
31617
31868
|
const merged = overrides ? { ...sanitizeSpawnEnv(process.env), ...overrides } : sanitizeSpawnEnv(process.env);
|
|
31618
31869
|
if (ctx.agentHookUrl) {
|
|
31619
31870
|
merged.SLAYZONE_AGENT_HOOK_URL = ctx.agentHookUrl;
|
|
31620
31871
|
delete merged.SLAYZONE_HUB_TOKEN;
|
|
31621
31872
|
}
|
|
31622
31873
|
if (ctx.hubAddress) merged.SLAYZONE_HUB_ADDRESS = ctx.hubAddress;
|
|
31874
|
+
delete merged.SLAYZONE_CLI_RELAY_ADDRESS;
|
|
31875
|
+
delete merged.SLAYZONE_CLI_RELAY_TOKEN;
|
|
31876
|
+
if (capability && ctx.cliRelay) {
|
|
31877
|
+
delete merged.SLAYZONE_HUB_TOKEN;
|
|
31878
|
+
merged.SLAYZONE_CLI_RELAY_ADDRESS = new URL(ctx.cliRelay.url).host;
|
|
31879
|
+
merged.SLAYZONE_CLI_RELAY_TOKEN = capability.token;
|
|
31880
|
+
}
|
|
31623
31881
|
if (ctx.config.binOverrideDir) {
|
|
31624
31882
|
merged.PATH = mergePathLists(ctx.config.binOverrideDir, merged.PATH);
|
|
31625
31883
|
}
|
|
31626
31884
|
return merged;
|
|
31627
31885
|
}
|
|
31886
|
+
function buildPtySpawnEnv(ctx, hubEnv, capability) {
|
|
31887
|
+
const env = buildSpawnEnv(ctx, pickHubPtyEnv(hubEnv), capability);
|
|
31888
|
+
const account = (0, import_node_os9.userInfo)();
|
|
31889
|
+
env.HOME = account.homedir;
|
|
31890
|
+
env.USER = account.username;
|
|
31891
|
+
env.LOGNAME = account.username;
|
|
31892
|
+
env.SHELL = resolveUserShell();
|
|
31893
|
+
env.TERM = "xterm-256color";
|
|
31894
|
+
env.COLORTERM = "truecolor";
|
|
31895
|
+
return env;
|
|
31896
|
+
}
|
|
31628
31897
|
|
|
31629
31898
|
// src/handlers/proc.ts
|
|
31630
31899
|
var MAX_BUFFER_SIZE = 750 * 1024;
|
|
@@ -31717,11 +31986,14 @@ function createProcHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
31717
31986
|
const cwd = spawnCwd(resolved, ctx.config.allowedRoots);
|
|
31718
31987
|
const existing = procs.get(params.sessionId);
|
|
31719
31988
|
if (existing) {
|
|
31989
|
+
existing.revokeCli?.();
|
|
31720
31990
|
killTree(existing.child);
|
|
31721
31991
|
procs.delete(params.sessionId);
|
|
31722
31992
|
}
|
|
31723
|
-
const
|
|
31993
|
+
const capability = ctx.cliRelay?.issue();
|
|
31994
|
+
const env = buildSpawnEnv(ctx, params.env, capability);
|
|
31724
31995
|
if (!params.shell && resolveCommand(params.command, env, cwd) === null) {
|
|
31996
|
+
capability?.revoke();
|
|
31725
31997
|
throw refuse2(
|
|
31726
31998
|
ProcRefusalReasons.binaryNotFound,
|
|
31727
31999
|
`${params.command} was not found on this computer's PATH`,
|
|
@@ -31729,17 +32001,24 @@ function createProcHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
31729
32001
|
);
|
|
31730
32002
|
}
|
|
31731
32003
|
const invocation = params.shell ? buildShellInvocation(params.command) : { file: params.command, args: params.args ?? [] };
|
|
31732
|
-
|
|
31733
|
-
|
|
31734
|
-
|
|
31735
|
-
|
|
31736
|
-
|
|
31737
|
-
|
|
31738
|
-
|
|
31739
|
-
|
|
31740
|
-
|
|
32004
|
+
let child;
|
|
32005
|
+
try {
|
|
32006
|
+
child = (0, import_node_child_process8.spawn)(invocation.file, invocation.args, {
|
|
32007
|
+
...cwd ? { cwd } : {},
|
|
32008
|
+
env,
|
|
32009
|
+
// stdin PIPED (was 'ignore') so `proc.write` can reach the child.
|
|
32010
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
32011
|
+
// Its own process group, so a kill reaches everything it started (`pnpm dev`
|
|
32012
|
+
// → node → …), not just the shell — see `killTree`.
|
|
32013
|
+
detached: PROCESS_TREE_DETACHED
|
|
32014
|
+
});
|
|
32015
|
+
} catch (error62) {
|
|
32016
|
+
capability?.revoke();
|
|
32017
|
+
throw error62;
|
|
32018
|
+
}
|
|
31741
32019
|
const buffer = new RingBuffer(MAX_BUFFER_SIZE);
|
|
31742
32020
|
const session = {
|
|
32021
|
+
revokeCli: capability?.revoke,
|
|
31743
32022
|
child,
|
|
31744
32023
|
buffer,
|
|
31745
32024
|
...params.spawnId !== void 0 ? { spawnId: params.spawnId } : {},
|
|
@@ -31754,6 +32033,7 @@ function createProcHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
31754
32033
|
ctx.log("proc stdin error", { sessionId: params.sessionId, error: err.message });
|
|
31755
32034
|
});
|
|
31756
32035
|
const settle2 = (payload) => {
|
|
32036
|
+
capability?.revoke();
|
|
31757
32037
|
releaseSlot(session);
|
|
31758
32038
|
if (procs.get(params.sessionId) !== session) return;
|
|
31759
32039
|
procs.delete(params.sessionId);
|
|
@@ -31914,6 +32194,7 @@ function createProcHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
31914
32194
|
}
|
|
31915
32195
|
queued.clear();
|
|
31916
32196
|
for (const [sessionId, session] of procs) {
|
|
32197
|
+
session.revokeCli?.();
|
|
31917
32198
|
releaseSlot(session);
|
|
31918
32199
|
killTree(session.child);
|
|
31919
32200
|
ctx.log("proc disposed on shutdown", { sessionId });
|
|
@@ -32179,6 +32460,72 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
32179
32460
|
const spawnPty = options.spawnPty ?? pty.spawn;
|
|
32180
32461
|
const installProblems2 = options.nodePtyInstallProblems ?? installProblemsOnDisk;
|
|
32181
32462
|
const queued = /* @__PURE__ */ new Map();
|
|
32463
|
+
const starts = /* @__PURE__ */ new Map();
|
|
32464
|
+
function serializeStart(id, spawnId, run) {
|
|
32465
|
+
const prior = starts.get(id)?.promise ?? Promise.resolve();
|
|
32466
|
+
const record2 = { spawnId, promise: void 0, cancelled: false };
|
|
32467
|
+
const assertActive = () => {
|
|
32468
|
+
if (record2.cancelled) throw new Error(`pty ${id}: spawn killed before start`);
|
|
32469
|
+
};
|
|
32470
|
+
const current = prior.catch(() => {
|
|
32471
|
+
}).then(() => {
|
|
32472
|
+
assertActive();
|
|
32473
|
+
return run(assertActive);
|
|
32474
|
+
});
|
|
32475
|
+
record2.promise = current;
|
|
32476
|
+
starts.set(id, record2);
|
|
32477
|
+
void current.finally(() => {
|
|
32478
|
+
if (starts.get(id)?.promise === current) starts.delete(id);
|
|
32479
|
+
}).catch(() => {
|
|
32480
|
+
});
|
|
32481
|
+
return current;
|
|
32482
|
+
}
|
|
32483
|
+
async function waitForExit(session, timeoutMs) {
|
|
32484
|
+
let timer;
|
|
32485
|
+
try {
|
|
32486
|
+
return await Promise.race([
|
|
32487
|
+
session.exited.then(() => true),
|
|
32488
|
+
new Promise((resolve9) => {
|
|
32489
|
+
timer = setTimeout(() => resolve9(false), timeoutMs);
|
|
32490
|
+
timer.unref?.();
|
|
32491
|
+
})
|
|
32492
|
+
]);
|
|
32493
|
+
} finally {
|
|
32494
|
+
if (timer) clearTimeout(timer);
|
|
32495
|
+
}
|
|
32496
|
+
}
|
|
32497
|
+
function stopPty(session, signal) {
|
|
32498
|
+
if (session.stopping) return session.stopping;
|
|
32499
|
+
session.stopping = (async () => {
|
|
32500
|
+
try {
|
|
32501
|
+
session.proc.kill(signal);
|
|
32502
|
+
} catch {
|
|
32503
|
+
}
|
|
32504
|
+
if (await waitForExit(session, 2e3)) return;
|
|
32505
|
+
try {
|
|
32506
|
+
session.proc.kill("SIGKILL");
|
|
32507
|
+
} catch {
|
|
32508
|
+
}
|
|
32509
|
+
if (!await waitForExit(session, 2e3)) {
|
|
32510
|
+
throw new Error(`pty ${session.id}: process ${session.proc.pid} did not exit`);
|
|
32511
|
+
}
|
|
32512
|
+
})();
|
|
32513
|
+
void session.stopping.catch(() => {
|
|
32514
|
+
session.stopping = void 0;
|
|
32515
|
+
});
|
|
32516
|
+
return session.stopping;
|
|
32517
|
+
}
|
|
32518
|
+
async function stopBeforeReplacement(id) {
|
|
32519
|
+
const old = sessions.get(id);
|
|
32520
|
+
if (!old) return;
|
|
32521
|
+
old.replacing = true;
|
|
32522
|
+
try {
|
|
32523
|
+
await stopPty(old);
|
|
32524
|
+
} catch (err) {
|
|
32525
|
+
old.replacing = false;
|
|
32526
|
+
throw err;
|
|
32527
|
+
}
|
|
32528
|
+
}
|
|
32182
32529
|
function resolveCwd(opts) {
|
|
32183
32530
|
if (opts.cwd) return spawnCwd(opts.cwd, ctx.config.allowedRoots);
|
|
32184
32531
|
if (!opts.projectId) throw new Error("pty.spawn needs either cwd or projectId");
|
|
@@ -32188,33 +32535,31 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
32188
32535
|
}
|
|
32189
32536
|
function spawnSession(id, opts) {
|
|
32190
32537
|
const cwd = resolveCwd(opts);
|
|
32191
|
-
|
|
32192
|
-
|
|
32193
|
-
existing.deadline?.cancel();
|
|
32194
|
-
releaseAdmission(existing);
|
|
32195
|
-
try {
|
|
32196
|
-
existing.proc.kill();
|
|
32197
|
-
} catch {
|
|
32198
|
-
}
|
|
32199
|
-
sessions.delete(id);
|
|
32200
|
-
}
|
|
32538
|
+
if (sessions.has(id)) throw new Error(`pty ${id}: old process still live`);
|
|
32539
|
+
const capability = ctx.cliRelay?.issue();
|
|
32201
32540
|
let proc;
|
|
32202
32541
|
try {
|
|
32203
32542
|
proc = spawnPty(opts.command, opts.args ?? [], {
|
|
32204
32543
|
name: "xterm-color",
|
|
32205
32544
|
cwd,
|
|
32206
|
-
env:
|
|
32545
|
+
env: buildPtySpawnEnv(ctx, opts.env, capability),
|
|
32207
32546
|
cols: opts.cols ?? 80,
|
|
32208
32547
|
rows: opts.rows ?? 24
|
|
32209
32548
|
});
|
|
32210
32549
|
} catch (err) {
|
|
32550
|
+
capability?.revoke();
|
|
32211
32551
|
throw explainSpawnFailure(err);
|
|
32212
32552
|
}
|
|
32213
32553
|
const buffer = new RingBuffer(MAX_BUFFER_SIZE2);
|
|
32554
|
+
let resolveExit;
|
|
32555
|
+
const exited = new Promise((resolve9) => resolveExit = resolve9);
|
|
32214
32556
|
const session = {
|
|
32557
|
+
revokeCli: capability?.revoke,
|
|
32215
32558
|
proc,
|
|
32216
32559
|
buffer,
|
|
32217
32560
|
id,
|
|
32561
|
+
exited,
|
|
32562
|
+
resolveExit,
|
|
32218
32563
|
...opts.warm ? { warm: opts.warm } : {},
|
|
32219
32564
|
...opts.spawnId !== void 0 ? { spawnId: opts.spawnId } : {}
|
|
32220
32565
|
};
|
|
@@ -32233,11 +32578,14 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
32233
32578
|
if (session.warm) return;
|
|
32234
32579
|
ctx.dialer.notify(ComputerNotificationMethods.ptyData, {
|
|
32235
32580
|
sessionId: session.id,
|
|
32581
|
+
...session.spawnId ? { spawnId: session.spawnId } : {},
|
|
32236
32582
|
seq,
|
|
32237
32583
|
data
|
|
32238
32584
|
});
|
|
32239
32585
|
});
|
|
32240
32586
|
proc.onExit(({ exitCode, signal }) => {
|
|
32587
|
+
capability?.revoke();
|
|
32588
|
+
session.resolveExit();
|
|
32241
32589
|
reap(
|
|
32242
32590
|
session,
|
|
32243
32591
|
typeof exitCode === "number" ? exitCode : null,
|
|
@@ -32263,6 +32611,16 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
32263
32611
|
sessions.delete(session.id);
|
|
32264
32612
|
session.deadline?.cancel();
|
|
32265
32613
|
releaseAdmission(session);
|
|
32614
|
+
if (session.replacing) {
|
|
32615
|
+
tombstones.add(session.id, {
|
|
32616
|
+
spawnId: session.spawnId,
|
|
32617
|
+
pid: session.proc.pid,
|
|
32618
|
+
exitCode,
|
|
32619
|
+
signal,
|
|
32620
|
+
reason: "replaced"
|
|
32621
|
+
});
|
|
32622
|
+
return;
|
|
32623
|
+
}
|
|
32266
32624
|
if (session.warm) {
|
|
32267
32625
|
ctx.log("warm pty exited before adoption", { warmId: session.id });
|
|
32268
32626
|
return;
|
|
@@ -32270,6 +32628,7 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
32270
32628
|
const reason = session.exitReason;
|
|
32271
32629
|
const delivered = ctx.dialer.notify(ComputerNotificationMethods.ptyExit, {
|
|
32272
32630
|
sessionId: session.id,
|
|
32631
|
+
...session.spawnId ? { spawnId: session.spawnId } : {},
|
|
32273
32632
|
exitCode,
|
|
32274
32633
|
signal,
|
|
32275
32634
|
...reason ? { reason } : {}
|
|
@@ -32305,11 +32664,20 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
32305
32664
|
slot.release();
|
|
32306
32665
|
}
|
|
32307
32666
|
function startSession(params) {
|
|
32308
|
-
const
|
|
32667
|
+
const shell = params.computerShell ? resolveUserShell() : params.command;
|
|
32668
|
+
const args = params.computerShell ? [
|
|
32669
|
+
...getShellStartupArgs(shell),
|
|
32670
|
+
...params.computerShellCommand ? ["-c", params.computerShellCommand] : []
|
|
32671
|
+
] : params.args;
|
|
32672
|
+
const session = spawnSession(params.sessionId, {
|
|
32673
|
+
...params,
|
|
32674
|
+
command: shell,
|
|
32675
|
+
args
|
|
32676
|
+
});
|
|
32309
32677
|
ctx.log("pty spawned", {
|
|
32310
32678
|
sessionId: params.sessionId,
|
|
32311
32679
|
pid: session.proc.pid,
|
|
32312
|
-
command:
|
|
32680
|
+
command: shell
|
|
32313
32681
|
});
|
|
32314
32682
|
return {
|
|
32315
32683
|
pid: session.proc.pid,
|
|
@@ -32361,16 +32729,26 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
32361
32729
|
}
|
|
32362
32730
|
const waitedMs = Date.now() - enqueuedAt;
|
|
32363
32731
|
if (waitedMs >= 1e3) ctx.log("agent start admitted after queueing", { id, lane, waitedMs });
|
|
32364
|
-
|
|
32732
|
+
Promise.resolve(reply).then(item.resolve, (err) => {
|
|
32733
|
+
slot.release();
|
|
32734
|
+
item.reject(err);
|
|
32735
|
+
});
|
|
32365
32736
|
});
|
|
32366
32737
|
return promise2;
|
|
32367
32738
|
}
|
|
32368
32739
|
function spawn6(rawParams) {
|
|
32369
32740
|
const params = ptySpawnParamsSchema.parse(rawParams);
|
|
32741
|
+
const starting = starts.get(params.sessionId);
|
|
32742
|
+
if (starting && params.spawnId !== void 0 && starting.spawnId === params.spawnId) {
|
|
32743
|
+
return starting.promise;
|
|
32744
|
+
}
|
|
32370
32745
|
if (params.spawnId !== void 0) {
|
|
32371
32746
|
const live = sessions.get(params.sessionId);
|
|
32372
32747
|
if (live && !live.warm && live.spawnId === params.spawnId) {
|
|
32373
|
-
return {
|
|
32748
|
+
return {
|
|
32749
|
+
pid: live.proc.pid,
|
|
32750
|
+
...live.deadline ? { startupDeadline: true } : {}
|
|
32751
|
+
};
|
|
32374
32752
|
}
|
|
32375
32753
|
if (tombstones.ran(params.sessionId, params.spawnId)) {
|
|
32376
32754
|
throw new Error(
|
|
@@ -32380,7 +32758,11 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
32380
32758
|
}
|
|
32381
32759
|
if (params.admission === void 0) {
|
|
32382
32760
|
const prior = queued.get(params.sessionId);
|
|
32383
|
-
const reply =
|
|
32761
|
+
const reply = sessions.has(params.sessionId) || starts.has(params.sessionId) ? serializeStart(params.sessionId, params.spawnId, async (assertActive) => {
|
|
32762
|
+
await stopBeforeReplacement(params.sessionId);
|
|
32763
|
+
assertActive();
|
|
32764
|
+
return startSession(params);
|
|
32765
|
+
}) : startSession(params);
|
|
32384
32766
|
if (prior) {
|
|
32385
32767
|
queued.delete(params.sessionId);
|
|
32386
32768
|
admission.cancel(prior.ticket);
|
|
@@ -32388,11 +32770,15 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
32388
32770
|
}
|
|
32389
32771
|
return reply;
|
|
32390
32772
|
}
|
|
32391
|
-
return enqueueSpawn(params.sessionId, params.admission, params, (slot, item) => {
|
|
32392
|
-
const reply =
|
|
32393
|
-
|
|
32394
|
-
|
|
32395
|
-
|
|
32773
|
+
return enqueueSpawn(params.sessionId, params.admission, params, async (slot, item) => {
|
|
32774
|
+
const reply = await serializeStart(params.sessionId, params.spawnId, async (assertActive) => {
|
|
32775
|
+
await stopBeforeReplacement(params.sessionId);
|
|
32776
|
+
assertActive();
|
|
32777
|
+
return startSession({
|
|
32778
|
+
...params,
|
|
32779
|
+
...item.cols !== void 0 ? { cols: item.cols } : {},
|
|
32780
|
+
...item.rows !== void 0 ? { rows: item.rows } : {}
|
|
32781
|
+
});
|
|
32396
32782
|
});
|
|
32397
32783
|
const session = sessions.get(params.sessionId);
|
|
32398
32784
|
session.admissionSlot = slot;
|
|
@@ -32401,18 +32787,28 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
32401
32787
|
});
|
|
32402
32788
|
}
|
|
32403
32789
|
function startWarm(params, cols, rows) {
|
|
32790
|
+
const shell = params.computerShell ? resolveUserShell() : params.command;
|
|
32404
32791
|
const session = spawnSession(params.warmId, {
|
|
32405
32792
|
...params,
|
|
32793
|
+
command: shell,
|
|
32794
|
+
args: params.computerShell ? getShellStartupArgs(shell) : params.args,
|
|
32406
32795
|
...cols !== void 0 ? { cols } : {},
|
|
32407
32796
|
...rows !== void 0 ? { rows } : {},
|
|
32408
32797
|
warm: { cwd: params.cwd, startedAt: Date.now() }
|
|
32409
32798
|
});
|
|
32410
32799
|
if (params.postSpawnCommand) session.proc.write(`${params.postSpawnCommand}\r`);
|
|
32411
|
-
ctx.log("warm pty spawned", {
|
|
32800
|
+
ctx.log("warm pty spawned", {
|
|
32801
|
+
warmId: params.warmId,
|
|
32802
|
+
pid: session.proc.pid
|
|
32803
|
+
});
|
|
32412
32804
|
return session;
|
|
32413
32805
|
}
|
|
32414
32806
|
function warmSpawn(rawParams) {
|
|
32415
32807
|
const params = ptyWarmSpawnParamsSchema.parse(rawParams);
|
|
32808
|
+
const live = sessions.get(params.warmId);
|
|
32809
|
+
if (live?.warm) return Promise.resolve({ pid: live.proc.pid });
|
|
32810
|
+
const waiting = queued.get(params.warmId);
|
|
32811
|
+
if (waiting) return waiting.promise;
|
|
32416
32812
|
return enqueueSpawn(params.warmId, "warm", params, (slot, item) => {
|
|
32417
32813
|
const session = startWarm(params, item.cols, item.rows);
|
|
32418
32814
|
session.admissionSlot = slot;
|
|
@@ -32431,11 +32827,15 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
32431
32827
|
function prioritize(rawParams) {
|
|
32432
32828
|
const params = ptyPrioritizeParamsSchema.parse(rawParams);
|
|
32433
32829
|
const item = queued.get(params.sessionId);
|
|
32434
|
-
return {
|
|
32830
|
+
return {
|
|
32831
|
+
ok: true,
|
|
32832
|
+
queued: item && (params.spawnId === void 0 || item.spawnId === params.spawnId) ? admission.prioritize(item.ticket) : false
|
|
32833
|
+
};
|
|
32435
32834
|
}
|
|
32436
32835
|
async function backgroundJobs(rawParams) {
|
|
32437
32836
|
const params = ptyBackgroundJobsParamsSchema.parse(rawParams);
|
|
32438
|
-
const
|
|
32837
|
+
const session = sessions.get(params.sessionId);
|
|
32838
|
+
const pid = session && (params.spawnId === void 0 || session.spawnId === params.spawnId) ? session.proc.pid : void 0;
|
|
32439
32839
|
if (pid === void 0) return { ok: true, supported: true, alive: [] };
|
|
32440
32840
|
const alive = await probeBackgroundJobs(pid, params.jobs);
|
|
32441
32841
|
return alive === null ? { ok: true, supported: false, alive: [] } : { ok: true, supported: true, alive };
|
|
@@ -32443,14 +32843,17 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
32443
32843
|
async function doctor(rawParams) {
|
|
32444
32844
|
const params = ptyDoctorParamsSchema.parse(rawParams);
|
|
32445
32845
|
await options.pathReady;
|
|
32446
|
-
const env =
|
|
32846
|
+
const env = buildPtySpawnEnv(ctx);
|
|
32447
32847
|
const binaries = await Promise.all(
|
|
32448
|
-
params.binaries.map(async (binary) => ({
|
|
32848
|
+
params.binaries.map(async (binary) => ({
|
|
32849
|
+
binary,
|
|
32850
|
+
path: await findOnPath(binary, env)
|
|
32851
|
+
}))
|
|
32449
32852
|
);
|
|
32450
32853
|
return {
|
|
32451
32854
|
ok: true,
|
|
32452
32855
|
problems: installProblems2(),
|
|
32453
|
-
shellExists: shellExists(
|
|
32856
|
+
shellExists: shellExists(resolveUserShell()),
|
|
32454
32857
|
binaries
|
|
32455
32858
|
};
|
|
32456
32859
|
}
|
|
@@ -32459,6 +32862,14 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
32459
32862
|
}
|
|
32460
32863
|
function warmAdopt(rawParams) {
|
|
32461
32864
|
const params = ptyWarmAdoptParamsSchema.parse(rawParams);
|
|
32865
|
+
const adopted2 = sessions.get(params.sessionId);
|
|
32866
|
+
if (adopted2?.spawnId === params.warmId) {
|
|
32867
|
+
return {
|
|
32868
|
+
pid: adopted2.proc.pid,
|
|
32869
|
+
data: adopted2.buffer.toString(),
|
|
32870
|
+
seq: adopted2.buffer.getCurrentSeq()
|
|
32871
|
+
};
|
|
32872
|
+
}
|
|
32462
32873
|
const session = sessions.get(params.warmId);
|
|
32463
32874
|
if (!session || !session.warm) {
|
|
32464
32875
|
throw new RpcError(
|
|
@@ -32468,16 +32879,15 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
32468
32879
|
}
|
|
32469
32880
|
const displaced = sessions.get(params.sessionId);
|
|
32470
32881
|
if (displaced) {
|
|
32471
|
-
|
|
32472
|
-
|
|
32473
|
-
|
|
32474
|
-
|
|
32475
|
-
displacedWarm: !!displaced.warm
|
|
32476
|
-
});
|
|
32882
|
+
throw new RpcError(
|
|
32883
|
+
ComputerTransportErrorCodes.unknownComputer,
|
|
32884
|
+
`session ${params.sessionId} already has live process ${displaced.proc.pid}`
|
|
32885
|
+
);
|
|
32477
32886
|
}
|
|
32478
32887
|
sessions.delete(params.warmId);
|
|
32479
32888
|
session.id = params.sessionId;
|
|
32480
32889
|
delete session.warm;
|
|
32890
|
+
session.spawnId = params.warmId;
|
|
32481
32891
|
sessions.set(params.sessionId, session);
|
|
32482
32892
|
releaseAdmission(session);
|
|
32483
32893
|
const data = session.buffer.toString();
|
|
@@ -32489,16 +32899,12 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
32489
32899
|
});
|
|
32490
32900
|
return { pid: session.proc.pid, data, seq };
|
|
32491
32901
|
}
|
|
32492
|
-
function warmKill(rawParams) {
|
|
32902
|
+
async function warmKill(rawParams) {
|
|
32493
32903
|
const params = ptyWarmKillParamsSchema.parse(rawParams);
|
|
32494
32904
|
cancelQueued(params.warmId, "killed");
|
|
32495
32905
|
const session = sessions.get(params.warmId);
|
|
32496
32906
|
if (!session) return { ok: true };
|
|
32497
|
-
|
|
32498
|
-
try {
|
|
32499
|
-
session.proc.kill();
|
|
32500
|
-
} catch {
|
|
32501
|
-
}
|
|
32907
|
+
await stopPty(session);
|
|
32502
32908
|
return { ok: true };
|
|
32503
32909
|
}
|
|
32504
32910
|
function warmList() {
|
|
@@ -32514,58 +32920,55 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
32514
32920
|
}
|
|
32515
32921
|
return { warms };
|
|
32516
32922
|
}
|
|
32517
|
-
function kill(rawParams) {
|
|
32923
|
+
async function kill(rawParams) {
|
|
32518
32924
|
const params = ptyKillParamsSchema.parse(rawParams);
|
|
32519
|
-
const
|
|
32520
|
-
if (
|
|
32925
|
+
const starting = starts.get(params.sessionId);
|
|
32926
|
+
if (starting && (params.spawnId === void 0 || starting.spawnId === params.spawnId)) {
|
|
32927
|
+
starting.cancelled = true;
|
|
32928
|
+
}
|
|
32929
|
+
const live = sessions.get(params.sessionId);
|
|
32930
|
+
const pending = queued.get(params.sessionId);
|
|
32931
|
+
if (pending && (params.spawnId === void 0 || pending.spawnId === params.spawnId)) {
|
|
32521
32932
|
const delivered = ctx.dialer.notify(ComputerNotificationMethods.ptyExit, {
|
|
32522
32933
|
sessionId: params.sessionId,
|
|
32934
|
+
...pending.spawnId ? { spawnId: pending.spawnId } : {},
|
|
32523
32935
|
exitCode: null,
|
|
32524
32936
|
signal: params.signal ?? "SIGHUP",
|
|
32525
32937
|
reason: "killed-before-start"
|
|
32526
32938
|
});
|
|
32527
32939
|
tombstones.add(params.sessionId, {
|
|
32528
|
-
spawnId:
|
|
32940
|
+
spawnId: pending.spawnId,
|
|
32529
32941
|
exitCode: null,
|
|
32530
32942
|
signal: params.signal ?? "SIGHUP",
|
|
32531
32943
|
reason: "killed-before-start"
|
|
32532
32944
|
});
|
|
32533
32945
|
if (!delivered)
|
|
32534
|
-
ctx.log("kill of a queued start not delivered", {
|
|
32946
|
+
ctx.log("kill of a queued start not delivered", {
|
|
32947
|
+
sessionId: params.sessionId
|
|
32948
|
+
});
|
|
32535
32949
|
cancelQueued(params.sessionId, "killed");
|
|
32536
|
-
|
|
32537
|
-
if (superseded) {
|
|
32538
|
-
sessions.delete(params.sessionId);
|
|
32539
|
-
superseded.deadline?.cancel();
|
|
32540
|
-
releaseAdmission(superseded);
|
|
32541
|
-
try {
|
|
32542
|
-
superseded.proc.kill(params.signal);
|
|
32543
|
-
} catch {
|
|
32544
|
-
}
|
|
32545
|
-
}
|
|
32950
|
+
if (live && params.spawnId === void 0) await stopPty(live, params.signal);
|
|
32546
32951
|
return { ok: true };
|
|
32547
32952
|
}
|
|
32548
|
-
|
|
32549
|
-
if (!session) {
|
|
32953
|
+
if (!live || params.spawnId !== void 0 && live.spawnId !== params.spawnId) {
|
|
32550
32954
|
ctx.log("pty kill on unknown session", { sessionId: params.sessionId });
|
|
32551
32955
|
return { ok: true };
|
|
32552
32956
|
}
|
|
32553
|
-
|
|
32554
|
-
session.proc.kill(params.signal);
|
|
32555
|
-
} catch (err) {
|
|
32556
|
-
ctx.log("pty kill failed", { sessionId: params.sessionId, error: String(err) });
|
|
32557
|
-
}
|
|
32957
|
+
await stopPty(live, params.signal);
|
|
32558
32958
|
return { ok: true };
|
|
32559
32959
|
}
|
|
32560
32960
|
function resize(rawParams) {
|
|
32561
32961
|
const params = ptyResizeParamsSchema.parse(rawParams);
|
|
32562
32962
|
const waiting = queued.get(params.sessionId);
|
|
32563
32963
|
if (waiting) {
|
|
32964
|
+
if (params.spawnId !== void 0 && waiting.spawnId !== params.spawnId) return { ok: true };
|
|
32564
32965
|
waiting.cols = params.cols;
|
|
32565
32966
|
waiting.rows = params.rows;
|
|
32566
32967
|
return { ok: true };
|
|
32567
32968
|
}
|
|
32568
32969
|
const session = sessions.get(params.sessionId);
|
|
32970
|
+
if (session && params.spawnId !== void 0 && session.spawnId !== params.spawnId)
|
|
32971
|
+
return { ok: true };
|
|
32569
32972
|
if (!session) {
|
|
32570
32973
|
ctx.log("pty resize on unknown session", { sessionId: params.sessionId });
|
|
32571
32974
|
return { ok: true };
|
|
@@ -32577,10 +32980,13 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
32577
32980
|
const params = ptyWriteParamsSchema.parse(rawParams);
|
|
32578
32981
|
const waiting = queued.get(params.sessionId);
|
|
32579
32982
|
if (waiting) {
|
|
32983
|
+
if (params.spawnId !== void 0 && waiting.spawnId !== params.spawnId) return { ok: true };
|
|
32580
32984
|
waiting.writes.push(params.data);
|
|
32581
32985
|
return { ok: true };
|
|
32582
32986
|
}
|
|
32583
32987
|
const session = sessions.get(params.sessionId);
|
|
32988
|
+
if (session && params.spawnId !== void 0 && session.spawnId !== params.spawnId)
|
|
32989
|
+
return { ok: true };
|
|
32584
32990
|
if (!session) {
|
|
32585
32991
|
ctx.log("pty write on unknown session", { sessionId: params.sessionId });
|
|
32586
32992
|
return { ok: true };
|
|
@@ -32606,7 +33012,9 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
32606
33012
|
}
|
|
32607
33013
|
function getBufferSince(rawParams) {
|
|
32608
33014
|
const params = ptyGetBufferSinceParamsSchema.parse(rawParams);
|
|
32609
|
-
const
|
|
33015
|
+
const live = sessions.get(params.sessionId);
|
|
33016
|
+
const exited = tombstones.get(params.sessionId);
|
|
33017
|
+
const buffer = live && (params.spawnId === void 0 || live.spawnId === params.spawnId) ? live.buffer : exited && (params.spawnId === void 0 || exited.spawnId === params.spawnId) ? exited.buffer : void 0;
|
|
32610
33018
|
if (!buffer) return { frames: [] };
|
|
32611
33019
|
const frames = buffer.getChunksSince(params.seq).map((c) => ({ seq: c.seq, data: c.data }));
|
|
32612
33020
|
return { frames };
|
|
@@ -32614,6 +33022,7 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
32614
33022
|
function disposeAll() {
|
|
32615
33023
|
dropQueued("computer shutting down");
|
|
32616
33024
|
for (const [sessionId, session] of sessions) {
|
|
33025
|
+
session.revokeCli?.();
|
|
32617
33026
|
session.deadline?.cancel();
|
|
32618
33027
|
releaseAdmission(session);
|
|
32619
33028
|
try {
|
|
@@ -32700,12 +33109,12 @@ function createComputerConfigHandlers(ctx) {
|
|
|
32700
33109
|
|
|
32701
33110
|
// src/project-paths.ts
|
|
32702
33111
|
var import_promises10 = require("node:fs/promises");
|
|
32703
|
-
var
|
|
32704
|
-
var
|
|
33112
|
+
var import_node_crypto4 = require("node:crypto");
|
|
33113
|
+
var import_node_os10 = require("node:os");
|
|
32705
33114
|
var import_node_path16 = require("node:path");
|
|
32706
33115
|
function slayzoneRootDir3() {
|
|
32707
33116
|
if (process.env.SLAYZONE_ROOT) return process.env.SLAYZONE_ROOT;
|
|
32708
|
-
const home = process.env.HOME ?? process.env.USERPROFILE ?? (0,
|
|
33117
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? (0, import_node_os10.homedir)();
|
|
32709
33118
|
return (0, import_node_path16.join)(home, ".slayzone");
|
|
32710
33119
|
}
|
|
32711
33120
|
var projectPathEntrySchema = external_exports.object({
|
|
@@ -32747,7 +33156,7 @@ async function readFileMap(filePath) {
|
|
|
32747
33156
|
}
|
|
32748
33157
|
async function writeFileMap(filePath, map2) {
|
|
32749
33158
|
await (0, import_promises10.mkdir)((0, import_node_path16.dirname)(filePath), { recursive: true, mode: 448 });
|
|
32750
|
-
const tmpPath = `${filePath}.tmp-${process.pid}-${(0,
|
|
33159
|
+
const tmpPath = `${filePath}.tmp-${process.pid}-${(0, import_node_crypto4.randomUUID)()}`;
|
|
32751
33160
|
await (0, import_promises10.writeFile)(tmpPath, `${JSON.stringify(map2, null, 2)}
|
|
32752
33161
|
`, { mode: 384 });
|
|
32753
33162
|
try {
|
|
@@ -32814,6 +33223,10 @@ function createHubRequestHandler(deps2) {
|
|
|
32814
33223
|
...deps2.agentHookUrl ? { agentHookUrl: deps2.agentHookUrl } : {},
|
|
32815
33224
|
...deps2.hubAddress ? { hubAddress: deps2.hubAddress } : {}
|
|
32816
33225
|
};
|
|
33226
|
+
const cliReady = deps2.cliRelayReady?.then((relay) => {
|
|
33227
|
+
ctx.cliRelay = relay;
|
|
33228
|
+
});
|
|
33229
|
+
void cliReady?.catch(() => log("CLI relay is unavailable"));
|
|
32817
33230
|
const pty2 = createPtyHandlers(ctx, deps2.getProjectPaths, {
|
|
32818
33231
|
...deps2.admission ? { admission: deps2.admission } : {},
|
|
32819
33232
|
...deps2.pathReady ? { pathReady: deps2.pathReady } : {}
|
|
@@ -32873,6 +33286,8 @@ function createHubRequestHandler(deps2) {
|
|
|
32873
33286
|
throw err;
|
|
32874
33287
|
}
|
|
32875
33288
|
try {
|
|
33289
|
+
if (method === HubToComputerMethods.ptySpawn || method === HubToComputerMethods.ptyWarmSpawn || method === HubToComputerMethods.procSpawn)
|
|
33290
|
+
await cliReady;
|
|
32876
33291
|
if (deps2.pathReady && !ptyMethods.has(method)) await deps2.pathReady;
|
|
32877
33292
|
const result = await entry(params);
|
|
32878
33293
|
record2("ok");
|
|
@@ -32977,7 +33392,10 @@ function startComputer(config2, deps2 = {}) {
|
|
|
32977
33392
|
count: store.list().length
|
|
32978
33393
|
});
|
|
32979
33394
|
}).catch((err) => log("project path map failed to load", { hubUrl, error: String(err) }));
|
|
33395
|
+
const cliRelayReady = createComputerCliRelay(dialer);
|
|
33396
|
+
void cliRelayReady.catch(() => log("CLI relay failed to start", { hubUrl }));
|
|
32980
33397
|
dispatch = createHubRequestHandler({
|
|
33398
|
+
cliRelayReady,
|
|
32981
33399
|
shutdown: (reason) => {
|
|
32982
33400
|
log("computer shutdown requested by hub", { hubUrl, reason });
|
|
32983
33401
|
void handle.stop().then(() => deps2.onShutdown?.(reason));
|
|
@@ -32998,6 +33416,11 @@ function startComputer(config2, deps2 = {}) {
|
|
|
32998
33416
|
({ computerId, mode }) => log("connected to hub", { hubUrl, computerId, mode })
|
|
32999
33417
|
);
|
|
33000
33418
|
dialer.events.on("disconnected", ({ reason }) => {
|
|
33419
|
+
void cliRelayReady.then(
|
|
33420
|
+
(relay) => relay.disconnect(),
|
|
33421
|
+
() => {
|
|
33422
|
+
}
|
|
33423
|
+
);
|
|
33001
33424
|
log("disconnected from hub", { hubUrl, reason });
|
|
33002
33425
|
dispatch.hubDisconnected(String(reason));
|
|
33003
33426
|
});
|
|
@@ -33026,6 +33449,11 @@ function startComputer(config2, deps2 = {}) {
|
|
|
33026
33449
|
dialer,
|
|
33027
33450
|
dispose: async () => {
|
|
33028
33451
|
dispatch.dispose();
|
|
33452
|
+
await cliRelayReady.then(
|
|
33453
|
+
(relay) => relay.close(),
|
|
33454
|
+
() => {
|
|
33455
|
+
}
|
|
33456
|
+
);
|
|
33029
33457
|
if (agentHookServer) await agentHookServer.close();
|
|
33030
33458
|
await dialer.stop();
|
|
33031
33459
|
}
|
|
@@ -33095,7 +33523,7 @@ async function maybeInteractiveSetup() {
|
|
|
33095
33523
|
configKey: "computerName",
|
|
33096
33524
|
envKey: "SLAYZONE_COMPUTER_NAME",
|
|
33097
33525
|
label: "Computer display name",
|
|
33098
|
-
hint: `default: ${(0,
|
|
33526
|
+
hint: `default: ${(0, import_node_os11.hostname)()}`
|
|
33099
33527
|
}
|
|
33100
33528
|
]
|
|
33101
33529
|
});
|