@slayzone/computer 0.36.2 → 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 +1553 -589
- 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
|
}
|
|
@@ -4415,26 +4415,26 @@ var require_ignore = __commonJS({
|
|
|
4415
4415
|
const index = body.indexOf(SLASH);
|
|
4416
4416
|
return index < 0 || index === body.length - 1;
|
|
4417
4417
|
};
|
|
4418
|
-
var basenameOf = (
|
|
4419
|
-
const end =
|
|
4420
|
-
const index =
|
|
4418
|
+
var basenameOf = (path11) => {
|
|
4419
|
+
const end = path11.length - 1;
|
|
4420
|
+
const index = path11.lastIndexOf(
|
|
4421
4421
|
SLASH,
|
|
4422
|
-
|
|
4422
|
+
path11[end] === SLASH ? end - 1 : end
|
|
4423
4423
|
);
|
|
4424
|
-
return index < 0 ?
|
|
4424
|
+
return index < 0 ? path11 : path11.slice(index + 1);
|
|
4425
4425
|
};
|
|
4426
|
-
var parentOf = (
|
|
4427
|
-
if (
|
|
4428
|
-
const slices =
|
|
4426
|
+
var parentOf = (path11) => {
|
|
4427
|
+
if (path11.charCodeAt(0) === SLASH_CODE || path11.indexOf(DOUBLE_SLASH) >= 0) {
|
|
4428
|
+
const slices = path11.split(SLASH).filter(Boolean);
|
|
4429
4429
|
slices.pop();
|
|
4430
4430
|
return slices.length ? slices.join(SLASH) + SLASH : EMPTY;
|
|
4431
4431
|
}
|
|
4432
|
-
const end =
|
|
4433
|
-
const cut =
|
|
4432
|
+
const end = path11.length - 1;
|
|
4433
|
+
const cut = path11.lastIndexOf(
|
|
4434
4434
|
SLASH,
|
|
4435
|
-
|
|
4435
|
+
path11.charCodeAt(end) === SLASH_CODE ? end - 1 : end
|
|
4436
4436
|
);
|
|
4437
|
-
return cut < 0 ? EMPTY :
|
|
4437
|
+
return cut < 0 ? EMPTY : path11.slice(0, cut + 1);
|
|
4438
4438
|
};
|
|
4439
4439
|
var isString2 = (subject) => typeof subject === "string";
|
|
4440
4440
|
var checkPattern = (pattern) => pattern && isString2(pattern) && !REGEX_TEST_BLANK_LINE.test(pattern) && !REGEX_INVALID_TRAILING_BACKSLASH.test(pattern) && pattern.indexOf("#") !== 0;
|
|
@@ -4543,20 +4543,20 @@ var require_ignore = __commonJS({
|
|
|
4543
4543
|
// path matching.
|
|
4544
4544
|
// - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
|
|
4545
4545
|
// @returns {TestResult} true if a file is ignored
|
|
4546
|
-
test(
|
|
4546
|
+
test(path11, checkUnignored, mode) {
|
|
4547
4547
|
let ignored = false;
|
|
4548
4548
|
let unignored = false;
|
|
4549
4549
|
let matchedRule;
|
|
4550
4550
|
const rules = this._rules;
|
|
4551
4551
|
const { length } = rules;
|
|
4552
4552
|
const shortcut = this._basenameCount * 2 >= length;
|
|
4553
|
-
const basename5 = shortcut ? basenameOf(
|
|
4553
|
+
const basename5 = shortcut ? basenameOf(path11) : path11;
|
|
4554
4554
|
for (let index = 0; index < length; index++) {
|
|
4555
4555
|
const rule = rules[index];
|
|
4556
4556
|
const { negative } = rule;
|
|
4557
4557
|
const skip = unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored;
|
|
4558
4558
|
if (!skip && rule[mode].test(
|
|
4559
|
-
shortcut && rule._basenameOnly ? basename5 :
|
|
4559
|
+
shortcut && rule._basenameOnly ? basename5 : path11
|
|
4560
4560
|
)) {
|
|
4561
4561
|
ignored = !negative;
|
|
4562
4562
|
unignored = negative;
|
|
@@ -4576,17 +4576,17 @@ var require_ignore = __commonJS({
|
|
|
4576
4576
|
var throwError = (message, Ctor) => {
|
|
4577
4577
|
throw new Ctor(message);
|
|
4578
4578
|
};
|
|
4579
|
-
var checkPath = (
|
|
4580
|
-
if (!isString2(
|
|
4579
|
+
var checkPath = (path11, originalPath, doThrow) => {
|
|
4580
|
+
if (!isString2(path11)) {
|
|
4581
4581
|
return doThrow(
|
|
4582
4582
|
`path must be a string, but got \`${originalPath}\``,
|
|
4583
4583
|
TypeError
|
|
4584
4584
|
);
|
|
4585
4585
|
}
|
|
4586
|
-
if (!
|
|
4586
|
+
if (!path11) {
|
|
4587
4587
|
return doThrow(`path must not be empty`, TypeError);
|
|
4588
4588
|
}
|
|
4589
|
-
if (checkPath.isNotRelative(
|
|
4589
|
+
if (checkPath.isNotRelative(path11)) {
|
|
4590
4590
|
const r = "`path.relative()`d";
|
|
4591
4591
|
return doThrow(
|
|
4592
4592
|
`path should be a ${r} string, but got "${originalPath}"`,
|
|
@@ -4595,25 +4595,25 @@ var require_ignore = __commonJS({
|
|
|
4595
4595
|
}
|
|
4596
4596
|
return true;
|
|
4597
4597
|
};
|
|
4598
|
-
var isNotRelative = (
|
|
4599
|
-
const first =
|
|
4598
|
+
var isNotRelative = (path11) => {
|
|
4599
|
+
const first = path11.charCodeAt(0);
|
|
4600
4600
|
if (first === SLASH_CODE) {
|
|
4601
4601
|
return true;
|
|
4602
4602
|
}
|
|
4603
4603
|
if (first !== DOT_CODE) {
|
|
4604
4604
|
return false;
|
|
4605
4605
|
}
|
|
4606
|
-
if (
|
|
4606
|
+
if (path11.length === 1) {
|
|
4607
4607
|
return true;
|
|
4608
4608
|
}
|
|
4609
|
-
const second =
|
|
4609
|
+
const second = path11.charCodeAt(1);
|
|
4610
4610
|
if (second === SLASH_CODE) {
|
|
4611
4611
|
return true;
|
|
4612
4612
|
}
|
|
4613
4613
|
if (second !== DOT_CODE) {
|
|
4614
4614
|
return false;
|
|
4615
4615
|
}
|
|
4616
|
-
return
|
|
4616
|
+
return path11.length === 2 || path11.charCodeAt(2) === SLASH_CODE;
|
|
4617
4617
|
};
|
|
4618
4618
|
checkPath.isNotRelative = isNotRelative;
|
|
4619
4619
|
checkPath.convert = (p) => p;
|
|
@@ -4644,56 +4644,56 @@ var require_ignore = __commonJS({
|
|
|
4644
4644
|
}
|
|
4645
4645
|
// @returns {TestResult}
|
|
4646
4646
|
_test(originalPath, cache, checkUnignored) {
|
|
4647
|
-
const
|
|
4647
|
+
const path11 = originalPath && checkPath.convert(originalPath);
|
|
4648
4648
|
checkPath(
|
|
4649
|
-
|
|
4649
|
+
path11,
|
|
4650
4650
|
originalPath,
|
|
4651
4651
|
this._strictPathCheck ? throwError : RETURN_FALSE
|
|
4652
4652
|
);
|
|
4653
|
-
return this._t(
|
|
4653
|
+
return this._t(path11, cache, checkUnignored);
|
|
4654
4654
|
}
|
|
4655
|
-
checkIgnore(
|
|
4656
|
-
if (
|
|
4657
|
-
return this.test(
|
|
4655
|
+
checkIgnore(path11) {
|
|
4656
|
+
if (path11.charCodeAt(path11.length - 1) !== SLASH_CODE) {
|
|
4657
|
+
return this.test(path11);
|
|
4658
4658
|
}
|
|
4659
|
-
const parentPath = parentOf(
|
|
4659
|
+
const parentPath = parentOf(path11);
|
|
4660
4660
|
if (parentPath) {
|
|
4661
4661
|
const parent = this._t(parentPath, this._testCache, true);
|
|
4662
4662
|
if (parent.ignored) {
|
|
4663
4663
|
return parent;
|
|
4664
4664
|
}
|
|
4665
4665
|
}
|
|
4666
|
-
return this._rules.test(
|
|
4666
|
+
return this._rules.test(path11, false, MODE_CHECK_IGNORE);
|
|
4667
4667
|
}
|
|
4668
|
-
_t(
|
|
4669
|
-
if (
|
|
4670
|
-
return cache[
|
|
4668
|
+
_t(path11, cache, checkUnignored) {
|
|
4669
|
+
if (path11 in cache) {
|
|
4670
|
+
return cache[path11];
|
|
4671
4671
|
}
|
|
4672
|
-
const parentPath = parentOf(
|
|
4672
|
+
const parentPath = parentOf(path11);
|
|
4673
4673
|
const parent = parentPath ? this._t(parentPath, cache, checkUnignored) : UNDEFINED;
|
|
4674
|
-
return cache[
|
|
4674
|
+
return cache[path11] = parent && parent.ignored ? parent : this._rules.test(path11, checkUnignored, MODE_IGNORE);
|
|
4675
4675
|
}
|
|
4676
|
-
ignores(
|
|
4677
|
-
return this._test(
|
|
4676
|
+
ignores(path11) {
|
|
4677
|
+
return this._test(path11, this._ignoreCache, false).ignored;
|
|
4678
4678
|
}
|
|
4679
4679
|
createFilter() {
|
|
4680
|
-
return (
|
|
4680
|
+
return (path11) => !this.ignores(path11);
|
|
4681
4681
|
}
|
|
4682
4682
|
filter(paths) {
|
|
4683
4683
|
return makeArray(paths).filter(this.createFilter());
|
|
4684
4684
|
}
|
|
4685
4685
|
// @returns {TestResult}
|
|
4686
|
-
test(
|
|
4687
|
-
return this._test(
|
|
4686
|
+
test(path11) {
|
|
4687
|
+
return this._test(path11, this._testCache, true);
|
|
4688
4688
|
}
|
|
4689
4689
|
};
|
|
4690
4690
|
var factory = (options) => new Ignore(options);
|
|
4691
|
-
var isPathValid = (
|
|
4691
|
+
var isPathValid = (path11) => checkPath(path11 && checkPath.convert(path11), path11, RETURN_FALSE);
|
|
4692
4692
|
var setupWindows = () => {
|
|
4693
4693
|
const makePosix = (str2) => /^\\\\\?\\/.test(str2) || /["<>|\u0000-\u001F]+/u.test(str2) ? str2 : str2.replace(/\\/g, "/");
|
|
4694
4694
|
checkPath.convert = makePosix;
|
|
4695
4695
|
const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
4696
|
-
checkPath.isNotRelative = (
|
|
4696
|
+
checkPath.isNotRelative = (path11) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path11) || isNotRelative(path11);
|
|
4697
4697
|
};
|
|
4698
4698
|
if (
|
|
4699
4699
|
// Detect `process` so that it can run in browsers.
|
|
@@ -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,8 +5431,8 @@ var init_agent_hooks2 = __esm({
|
|
|
5431
5431
|
});
|
|
5432
5432
|
|
|
5433
5433
|
// src/bin.ts
|
|
5434
|
-
var
|
|
5435
|
-
var
|
|
5434
|
+
var import_node_os11 = require("node:os");
|
|
5435
|
+
var import_node_path18 = require("node:path");
|
|
5436
5436
|
|
|
5437
5437
|
// ../../shared/platform/src/config-prompt.ts
|
|
5438
5438
|
var import_promises = require("node:readline/promises");
|
|
@@ -5467,14 +5467,14 @@ function coerceComputerConfig(raw) {
|
|
|
5467
5467
|
}
|
|
5468
5468
|
return cfg;
|
|
5469
5469
|
}
|
|
5470
|
-
function readJsonObject(
|
|
5470
|
+
function readJsonObject(path11) {
|
|
5471
5471
|
let raw;
|
|
5472
5472
|
try {
|
|
5473
|
-
raw = (0, import_node_fs.readFileSync)(
|
|
5473
|
+
raw = (0, import_node_fs.readFileSync)(path11, "utf8");
|
|
5474
5474
|
} catch (err) {
|
|
5475
5475
|
if (err.code === "ENOENT") return null;
|
|
5476
5476
|
process.stderr.write(
|
|
5477
|
-
`[slayzone-config] cannot read ${
|
|
5477
|
+
`[slayzone-config] cannot read ${path11}: ${err instanceof Error ? err.message : String(err)} \u2014 using empty config
|
|
5478
5478
|
`
|
|
5479
5479
|
);
|
|
5480
5480
|
return null;
|
|
@@ -5484,61 +5484,61 @@ function readJsonObject(path9) {
|
|
|
5484
5484
|
parsed = JSON.parse(raw);
|
|
5485
5485
|
} catch (err) {
|
|
5486
5486
|
process.stderr.write(
|
|
5487
|
-
`[slayzone-config] ${
|
|
5487
|
+
`[slayzone-config] ${path11} is not valid JSON: ${err instanceof Error ? err.message : String(err)} \u2014 using empty config
|
|
5488
5488
|
`
|
|
5489
5489
|
);
|
|
5490
5490
|
return null;
|
|
5491
5491
|
}
|
|
5492
5492
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
5493
|
-
process.stderr.write(`[slayzone-config] ${
|
|
5493
|
+
process.stderr.write(`[slayzone-config] ${path11} is not a JSON object \u2014 using empty config
|
|
5494
5494
|
`);
|
|
5495
5495
|
return null;
|
|
5496
5496
|
}
|
|
5497
5497
|
return parsed;
|
|
5498
5498
|
}
|
|
5499
|
-
function writeJsonAtomic(
|
|
5500
|
-
const dir = (0, import_node_path.dirname)(
|
|
5499
|
+
function writeJsonAtomic(path11, data) {
|
|
5500
|
+
const dir = (0, import_node_path.dirname)(path11);
|
|
5501
5501
|
(0, import_node_fs.mkdirSync)(dir, { recursive: true, mode: 448 });
|
|
5502
|
-
const tmpPath = `${
|
|
5502
|
+
const tmpPath = `${path11}.tmp-${process.pid}-${Date.now()}`;
|
|
5503
5503
|
(0, import_node_fs.writeFileSync)(tmpPath, `${JSON.stringify(data, null, 2)}
|
|
5504
5504
|
`, { mode: 384 });
|
|
5505
5505
|
try {
|
|
5506
|
-
(0, import_node_fs.renameSync)(tmpPath,
|
|
5506
|
+
(0, import_node_fs.renameSync)(tmpPath, path11);
|
|
5507
5507
|
} catch (err) {
|
|
5508
5508
|
(0, import_node_fs.rmSync)(tmpPath, { force: true });
|
|
5509
5509
|
throw err;
|
|
5510
5510
|
}
|
|
5511
5511
|
}
|
|
5512
|
-
function loadComputerConfigFile(
|
|
5513
|
-
const raw = readJsonObject(
|
|
5512
|
+
function loadComputerConfigFile(path11 = getComputerConfigFilePath()) {
|
|
5513
|
+
const raw = readJsonObject(path11);
|
|
5514
5514
|
if (raw) return coerceComputerConfig(raw);
|
|
5515
|
-
const legacy = readJsonObject(legacyConfigFilePathFor(
|
|
5515
|
+
const legacy = readJsonObject(legacyConfigFilePathFor(path11));
|
|
5516
5516
|
return legacy ? coerceComputerConfig(legacy) : {};
|
|
5517
5517
|
}
|
|
5518
|
-
function ignoredComputerConfigKeys(
|
|
5519
|
-
const raw = readJsonObject(
|
|
5518
|
+
function ignoredComputerConfigKeys(path11 = getComputerConfigFilePath()) {
|
|
5519
|
+
const raw = readJsonObject(path11) ?? readJsonObject(legacyConfigFilePathFor(path11));
|
|
5520
5520
|
if (!raw) return [];
|
|
5521
5521
|
return IGNORED_COMPUTER_CONFIG_KEYS.filter((key) => raw[key] !== void 0);
|
|
5522
5522
|
}
|
|
5523
|
-
function saveComputerConfigFile(cfg,
|
|
5524
|
-
writeJsonAtomic(
|
|
5523
|
+
function saveComputerConfigFile(cfg, path11 = getComputerConfigFilePath()) {
|
|
5524
|
+
writeJsonAtomic(path11, cfg);
|
|
5525
5525
|
}
|
|
5526
|
-
function updateComputerConfigFile(patch,
|
|
5527
|
-
const current = readJsonObject(
|
|
5526
|
+
function updateComputerConfigFile(patch, path11 = getComputerConfigFilePath()) {
|
|
5527
|
+
const current = readJsonObject(path11);
|
|
5528
5528
|
const merged = current ? coerceComputerConfig(current) : {};
|
|
5529
5529
|
for (const [k, v] of Object.entries(patch)) {
|
|
5530
5530
|
if (v !== void 0) merged[k] = v;
|
|
5531
5531
|
}
|
|
5532
|
-
saveComputerConfigFile(merged,
|
|
5532
|
+
saveComputerConfigFile(merged, path11);
|
|
5533
5533
|
return merged;
|
|
5534
5534
|
}
|
|
5535
|
-
function updateJsonFile(patch,
|
|
5536
|
-
const current = readJsonObject(
|
|
5535
|
+
function updateJsonFile(patch, path11) {
|
|
5536
|
+
const current = readJsonObject(path11) ?? {};
|
|
5537
5537
|
const merged = { ...current };
|
|
5538
5538
|
for (const [k, v] of Object.entries(patch)) {
|
|
5539
5539
|
if (v !== void 0) merged[k] = v;
|
|
5540
5540
|
}
|
|
5541
|
-
writeJsonAtomic(
|
|
5541
|
+
writeJsonAtomic(path11, merged);
|
|
5542
5542
|
}
|
|
5543
5543
|
|
|
5544
5544
|
// ../../shared/platform/src/config-prompt.ts
|
|
@@ -5610,10 +5610,10 @@ function isLoopbackAuthority(addr) {
|
|
|
5610
5610
|
return false;
|
|
5611
5611
|
}
|
|
5612
5612
|
}
|
|
5613
|
-
function hubUrlFromAddr(addr, kind,
|
|
5613
|
+
function hubUrlFromAddr(addr, kind, path11 = "") {
|
|
5614
5614
|
const secure = !isLoopbackAuthority(addr);
|
|
5615
5615
|
const scheme = kind === "ws" ? secure ? "wss" : "ws" : secure ? "https" : "http";
|
|
5616
|
-
return `${scheme}://${addr}${
|
|
5616
|
+
return `${scheme}://${addr}${path11}`;
|
|
5617
5617
|
}
|
|
5618
5618
|
function hubAddrFromUrl(url2) {
|
|
5619
5619
|
let u;
|
|
@@ -6715,10 +6715,10 @@ function mergeDefs(...defs) {
|
|
|
6715
6715
|
function cloneDef(schema) {
|
|
6716
6716
|
return mergeDefs(schema._zod.def);
|
|
6717
6717
|
}
|
|
6718
|
-
function getElementAtPath(obj,
|
|
6719
|
-
if (!
|
|
6718
|
+
function getElementAtPath(obj, path11) {
|
|
6719
|
+
if (!path11)
|
|
6720
6720
|
return obj;
|
|
6721
|
-
return
|
|
6721
|
+
return path11.reduce((acc, key) => acc?.[key], obj);
|
|
6722
6722
|
}
|
|
6723
6723
|
function promiseAllObject(promisesObj) {
|
|
6724
6724
|
const keys = Object.keys(promisesObj);
|
|
@@ -7058,11 +7058,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
7058
7058
|
}
|
|
7059
7059
|
return false;
|
|
7060
7060
|
}
|
|
7061
|
-
function prefixIssues(
|
|
7061
|
+
function prefixIssues(path11, issues) {
|
|
7062
7062
|
return issues.map((iss) => {
|
|
7063
7063
|
var _a3;
|
|
7064
7064
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
7065
|
-
iss.path.unshift(
|
|
7065
|
+
iss.path.unshift(path11);
|
|
7066
7066
|
return iss;
|
|
7067
7067
|
});
|
|
7068
7068
|
}
|
|
@@ -7512,16 +7512,16 @@ function flattenError(error62, mapper = (issue2) => issue2.message) {
|
|
|
7512
7512
|
}
|
|
7513
7513
|
function formatError(error62, mapper = (issue2) => issue2.message) {
|
|
7514
7514
|
const fieldErrors = { _errors: [] };
|
|
7515
|
-
const processError = (error63,
|
|
7515
|
+
const processError = (error63, path11 = []) => {
|
|
7516
7516
|
for (const issue2 of error63.issues) {
|
|
7517
7517
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
7518
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
7518
|
+
issue2.errors.map((issues) => processError({ issues }, [...path11, ...issue2.path]));
|
|
7519
7519
|
} else if (issue2.code === "invalid_key") {
|
|
7520
|
-
processError({ issues: issue2.issues }, [...
|
|
7520
|
+
processError({ issues: issue2.issues }, [...path11, ...issue2.path]);
|
|
7521
7521
|
} else if (issue2.code === "invalid_element") {
|
|
7522
|
-
processError({ issues: issue2.issues }, [...
|
|
7522
|
+
processError({ issues: issue2.issues }, [...path11, ...issue2.path]);
|
|
7523
7523
|
} else {
|
|
7524
|
-
const fullpath = [...
|
|
7524
|
+
const fullpath = [...path11, ...issue2.path];
|
|
7525
7525
|
if (fullpath.length === 0) {
|
|
7526
7526
|
fieldErrors._errors.push(mapper(issue2));
|
|
7527
7527
|
} else {
|
|
@@ -7560,17 +7560,17 @@ function formatError(error62, mapper = (issue2) => issue2.message) {
|
|
|
7560
7560
|
}
|
|
7561
7561
|
function treeifyError(error62, mapper = (issue2) => issue2.message) {
|
|
7562
7562
|
const result = { errors: [] };
|
|
7563
|
-
const processError = (error63,
|
|
7563
|
+
const processError = (error63, path11 = []) => {
|
|
7564
7564
|
var _a3;
|
|
7565
7565
|
for (const issue2 of error63.issues) {
|
|
7566
7566
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
7567
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
7567
|
+
issue2.errors.map((issues) => processError({ issues }, [...path11, ...issue2.path]));
|
|
7568
7568
|
} else if (issue2.code === "invalid_key") {
|
|
7569
|
-
processError({ issues: issue2.issues }, [...
|
|
7569
|
+
processError({ issues: issue2.issues }, [...path11, ...issue2.path]);
|
|
7570
7570
|
} else if (issue2.code === "invalid_element") {
|
|
7571
|
-
processError({ issues: issue2.issues }, [...
|
|
7571
|
+
processError({ issues: issue2.issues }, [...path11, ...issue2.path]);
|
|
7572
7572
|
} else {
|
|
7573
|
-
const fullpath = [...
|
|
7573
|
+
const fullpath = [...path11, ...issue2.path];
|
|
7574
7574
|
if (fullpath.length === 0) {
|
|
7575
7575
|
result.errors.push(mapper(issue2));
|
|
7576
7576
|
continue;
|
|
@@ -7609,8 +7609,8 @@ function treeifyError(error62, mapper = (issue2) => issue2.message) {
|
|
|
7609
7609
|
}
|
|
7610
7610
|
function toDotPath(_path) {
|
|
7611
7611
|
const segs = [];
|
|
7612
|
-
const
|
|
7613
|
-
for (const seg of
|
|
7612
|
+
const path11 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
7613
|
+
for (const seg of path11) {
|
|
7614
7614
|
if (typeof seg === "number")
|
|
7615
7615
|
segs.push(`[${seg}]`);
|
|
7616
7616
|
else if (typeof seg === "symbol")
|
|
@@ -7896,8 +7896,8 @@ function emoji() {
|
|
|
7896
7896
|
}
|
|
7897
7897
|
var ipv4 = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/;
|
|
7898
7898
|
var ipv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/;
|
|
7899
|
-
var mac = (
|
|
7900
|
-
const escapedDelim = escapeRegex(
|
|
7899
|
+
var mac = (delimiter4) => {
|
|
7900
|
+
const escapedDelim = escapeRegex(delimiter4 ?? ":");
|
|
7901
7901
|
return new RegExp(`^(?:[0-9A-F]{2}${escapedDelim}){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}${escapedDelim}){5}[0-9a-f]{2}$`);
|
|
7902
7902
|
};
|
|
7903
7903
|
var cidrv4 = /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$/;
|
|
@@ -22585,14 +22585,14 @@ var nonoptionalProcessor = (schema, ctx, _json, params) => {
|
|
|
22585
22585
|
var UNREPRESENTABLE_DEFAULT = /* @__PURE__ */ Symbol();
|
|
22586
22586
|
function serializeDefaultValue(value, schema, ctx, json2, params) {
|
|
22587
22587
|
let unrepresentable = false;
|
|
22588
|
-
const
|
|
22588
|
+
const serialized2 = JSON.stringify(value, (_, val) => {
|
|
22589
22589
|
if (typeof val !== "bigint")
|
|
22590
22590
|
return val;
|
|
22591
22591
|
unrepresentable = true;
|
|
22592
22592
|
return null;
|
|
22593
22593
|
});
|
|
22594
22594
|
if (!unrepresentable)
|
|
22595
|
-
return JSON.parse(
|
|
22595
|
+
return JSON.parse(serialized2);
|
|
22596
22596
|
handleUnrepresentable(schema, ctx, json2, params, "BigInt defaults cannot be represented in JSON Schema");
|
|
22597
22597
|
return UNREPRESENTABLE_DEFAULT;
|
|
22598
22598
|
}
|
|
@@ -24712,13 +24712,13 @@ function resolveRef(ref, ctx) {
|
|
|
24712
24712
|
if (!ref.startsWith("#")) {
|
|
24713
24713
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
24714
24714
|
}
|
|
24715
|
-
const
|
|
24716
|
-
if (
|
|
24715
|
+
const path11 = ref.slice(1).split("/").filter(Boolean);
|
|
24716
|
+
if (path11.length === 0) {
|
|
24717
24717
|
return ctx.rootSchema;
|
|
24718
24718
|
}
|
|
24719
24719
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
24720
|
-
if (
|
|
24721
|
-
const key =
|
|
24720
|
+
if (path11[0] === defsKey) {
|
|
24721
|
+
const key = path11[1] === void 0 ? void 0 : decodeJSONPointerSegment(path11[1]);
|
|
24722
24722
|
if (!key || !ctx.defs[key]) {
|
|
24723
24723
|
throw new Error(`Reference not found: ${ref}`);
|
|
24724
24724
|
}
|
|
@@ -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,
|
|
@@ -25592,8 +25592,20 @@ var ComputerTransportErrorCodes = {
|
|
|
25592
25592
|
* transient — retrying the same request is refused again. The hub audits it as
|
|
25593
25593
|
* `denied` and the message says what to do on the device.
|
|
25594
25594
|
*/
|
|
25595
|
-
refusedByComputer: -32006
|
|
25595
|
+
refusedByComputer: -32006,
|
|
25596
|
+
/**
|
|
25597
|
+
* An agent for a project this computer holds no folder for. Not transient, and
|
|
25598
|
+
* not a policy refusal: the user picks the project's folder on this computer
|
|
25599
|
+
* (or the hub delivers it), then the same spawn works.
|
|
25600
|
+
*/
|
|
25601
|
+
projectNotPlaced: -32007
|
|
25596
25602
|
};
|
|
25603
|
+
function projectNotPlacedError(projectId) {
|
|
25604
|
+
return new RpcError(
|
|
25605
|
+
ComputerTransportErrorCodes.projectNotPlaced,
|
|
25606
|
+
`This computer has no folder for project ${projectId}. Pick the project's folder on this computer, then try again.`
|
|
25607
|
+
);
|
|
25608
|
+
}
|
|
25597
25609
|
var requestActorSchema = external_exports.object({
|
|
25598
25610
|
userId: external_exports.string().min(1).max(200).nullable(),
|
|
25599
25611
|
/** `trpc <path>` / `METHOD /path` the request entered the hub through. */
|
|
@@ -25659,12 +25671,14 @@ var ComputerNotificationMethods = {
|
|
|
25659
25671
|
};
|
|
25660
25672
|
var ptyDataParamsSchema = external_exports.object({
|
|
25661
25673
|
sessionId: external_exports.string().min(1),
|
|
25674
|
+
spawnId: external_exports.string().min(1).optional(),
|
|
25662
25675
|
/** Monotonic per-session sequence number, preserved end-to-end. */
|
|
25663
25676
|
seq: external_exports.number().int().nonnegative(),
|
|
25664
25677
|
data: external_exports.string()
|
|
25665
25678
|
});
|
|
25666
25679
|
var ptyExitParamsSchema = external_exports.object({
|
|
25667
25680
|
sessionId: external_exports.string().min(1),
|
|
25681
|
+
spawnId: external_exports.string().min(1).optional(),
|
|
25668
25682
|
exitCode: external_exports.number().int().nullable(),
|
|
25669
25683
|
signal: external_exports.string().nullable().optional(),
|
|
25670
25684
|
/**
|
|
@@ -25684,13 +25698,19 @@ var procDataParamsSchema = external_exports.object({
|
|
|
25684
25698
|
/** Monotonic per-session sequence number for `stdout`. Absent on stderr. */
|
|
25685
25699
|
seq: external_exports.number().int().nonnegative().optional(),
|
|
25686
25700
|
data: external_exports.string(),
|
|
25687
|
-
stream: external_exports.enum(["stdout", "stderr"]).optional()
|
|
25701
|
+
stream: external_exports.enum(["stdout", "stderr"]).optional(),
|
|
25702
|
+
/** The spawn attempt that produced this output; the hub drops a replaced attempt's. */
|
|
25703
|
+
spawnId: external_exports.string().min(1).optional()
|
|
25688
25704
|
});
|
|
25689
25705
|
var procExitParamsSchema = external_exports.object({
|
|
25690
25706
|
sessionId: external_exports.string().min(1),
|
|
25691
25707
|
exitCode: external_exports.number().int().nullable(),
|
|
25692
25708
|
signal: external_exports.string().nullable().optional(),
|
|
25693
|
-
reason: external_exports.string().min(1).optional()
|
|
25709
|
+
reason: external_exports.string().min(1).optional(),
|
|
25710
|
+
/** The spawn attempt that exited. */
|
|
25711
|
+
spawnId: external_exports.string().min(1).optional(),
|
|
25712
|
+
/** Why the process could not run (e.g. the OS refused to start it). */
|
|
25713
|
+
error: external_exports.string().optional()
|
|
25694
25714
|
});
|
|
25695
25715
|
var fsChangeParamsSchema = external_exports.object({
|
|
25696
25716
|
watchId: external_exports.string().min(1),
|
|
@@ -25717,6 +25737,8 @@ var HubToComputerMethods = {
|
|
|
25717
25737
|
ptyPrioritize: "pty.prioritize",
|
|
25718
25738
|
/** Which of claude's background jobs still have a live process under a session. */
|
|
25719
25739
|
ptyBackgroundJobs: "pty.backgroundJobs",
|
|
25740
|
+
/** Doctor's facts about this computer: node-pty install, the shell, agent binaries. */
|
|
25741
|
+
ptyDoctor: "pty.doctor",
|
|
25720
25742
|
// warm pool (pre-warmed agents live ON the computer — see server/warm-*)
|
|
25721
25743
|
ptyWarmSpawn: "pty.warmSpawn",
|
|
25722
25744
|
ptyWarmAdopt: "pty.warmAdopt",
|
|
@@ -25775,7 +25797,13 @@ var HubToComputerMethods = {
|
|
|
25775
25797
|
/** Edit the computer's own path-jail (Settings → Computers). */
|
|
25776
25798
|
computerSetAllowedRoots: "computer.setAllowedRoots",
|
|
25777
25799
|
/** Run `gh` on the computer (PR panel). One frame, not one per gh subcommand. */
|
|
25778
|
-
ghExec: "gh.exec"
|
|
25800
|
+
ghExec: "gh.exec",
|
|
25801
|
+
/**
|
|
25802
|
+
* Read the active branch of a Claude Code session file on the computer (a
|
|
25803
|
+
* resumed or handed-over conversation's history for the chat timeline). Only
|
|
25804
|
+
* that file: the path is built from the cwd and a uuid, nothing else.
|
|
25805
|
+
*/
|
|
25806
|
+
claudeSessionRead: "claudeSession.read"
|
|
25779
25807
|
};
|
|
25780
25808
|
var spawnIdField = external_exports.string().min(1).optional();
|
|
25781
25809
|
var exitedSessionSchema = external_exports.object({
|
|
@@ -25802,6 +25830,9 @@ var ptySpawnParamsSchema = external_exports.object({
|
|
|
25802
25830
|
startupTimeoutMs: external_exports.number().int().positive().optional(),
|
|
25803
25831
|
command: external_exports.string().min(1),
|
|
25804
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(),
|
|
25805
25836
|
/**
|
|
25806
25837
|
* Absolute cwd. Optional now: when absent the COMPUTER resolves it from
|
|
25807
25838
|
* `projectId` against its own `project.*` mapping, because the hub cannot —
|
|
@@ -25820,6 +25851,19 @@ var ptySpawnParamsSchema = external_exports.object({
|
|
|
25820
25851
|
}).refine((p) => p.cwd !== void 0 || p.projectId !== void 0, {
|
|
25821
25852
|
message: "pty.spawn needs either cwd or projectId"
|
|
25822
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
|
+
}
|
|
25823
25867
|
var ptySpawnResultSchema = external_exports.object({
|
|
25824
25868
|
pid: external_exports.number().int(),
|
|
25825
25869
|
/** The computer armed the requested `startupTimeoutMs`. Absent from a computer
|
|
@@ -25828,10 +25872,12 @@ var ptySpawnResultSchema = external_exports.object({
|
|
|
25828
25872
|
});
|
|
25829
25873
|
var ptyKillParamsSchema = external_exports.object({
|
|
25830
25874
|
sessionId: external_exports.string().min(1),
|
|
25875
|
+
spawnId: spawnIdField,
|
|
25831
25876
|
signal: external_exports.string().optional()
|
|
25832
25877
|
});
|
|
25833
25878
|
var ptyPrioritizeParamsSchema = external_exports.object({
|
|
25834
|
-
sessionId: external_exports.string().min(1)
|
|
25879
|
+
sessionId: external_exports.string().min(1),
|
|
25880
|
+
spawnId: spawnIdField
|
|
25835
25881
|
});
|
|
25836
25882
|
var ptyPrioritizeResultSchema = external_exports.object({
|
|
25837
25883
|
ok: external_exports.literal(true),
|
|
@@ -25839,6 +25885,7 @@ var ptyPrioritizeResultSchema = external_exports.object({
|
|
|
25839
25885
|
});
|
|
25840
25886
|
var ptyBackgroundJobsParamsSchema = external_exports.object({
|
|
25841
25887
|
sessionId: external_exports.string().min(1),
|
|
25888
|
+
spawnId: spawnIdField,
|
|
25842
25889
|
jobs: external_exports.array(
|
|
25843
25890
|
external_exports.object({
|
|
25844
25891
|
id: external_exports.string().min(1),
|
|
@@ -25852,17 +25899,33 @@ var ptyBackgroundJobsResultSchema = external_exports.object({
|
|
|
25852
25899
|
supported: external_exports.boolean(),
|
|
25853
25900
|
alive: external_exports.array(external_exports.string())
|
|
25854
25901
|
});
|
|
25902
|
+
var ptyDoctorParamsSchema = external_exports.object({
|
|
25903
|
+
shell: external_exports.string().min(1).max(4096),
|
|
25904
|
+
/** Plain command names only (`claude`, `cursor-agent`), never paths or syntax. */
|
|
25905
|
+
binaries: external_exports.array(
|
|
25906
|
+
external_exports.string().min(1).max(128).regex(/^[A-Za-z0-9][A-Za-z0-9._+-]*$/)
|
|
25907
|
+
).max(16)
|
|
25908
|
+
}).strict();
|
|
25909
|
+
var ptyDoctorResultSchema = external_exports.object({
|
|
25910
|
+
ok: external_exports.literal(true),
|
|
25911
|
+
problems: external_exports.array(external_exports.object({ check: external_exports.string(), detail: external_exports.string(), fix: external_exports.string() })),
|
|
25912
|
+
shellExists: external_exports.boolean(),
|
|
25913
|
+
binaries: external_exports.array(external_exports.object({ binary: external_exports.string(), path: external_exports.string().nullable() }))
|
|
25914
|
+
});
|
|
25855
25915
|
var ptyResizeParamsSchema = external_exports.object({
|
|
25856
25916
|
sessionId: external_exports.string().min(1),
|
|
25917
|
+
spawnId: spawnIdField,
|
|
25857
25918
|
cols: external_exports.number().int().positive(),
|
|
25858
25919
|
rows: external_exports.number().int().positive()
|
|
25859
25920
|
});
|
|
25860
25921
|
var ptyWriteParamsSchema = external_exports.object({
|
|
25861
25922
|
sessionId: external_exports.string().min(1),
|
|
25923
|
+
spawnId: spawnIdField,
|
|
25862
25924
|
data: external_exports.string()
|
|
25863
25925
|
});
|
|
25864
25926
|
var ptyGetBufferSinceParamsSchema = external_exports.object({
|
|
25865
25927
|
sessionId: external_exports.string().min(1),
|
|
25928
|
+
spawnId: spawnIdField,
|
|
25866
25929
|
/**
|
|
25867
25930
|
* Exclusive lower bound — frames with `seq > this` are replayed.
|
|
25868
25931
|
*
|
|
@@ -25904,6 +25967,7 @@ var ptyWarmSpawnParamsSchema = external_exports.object({
|
|
|
25904
25967
|
warmId: external_exports.string().min(1),
|
|
25905
25968
|
command: external_exports.string().min(1),
|
|
25906
25969
|
args: external_exports.array(external_exports.string()).optional(),
|
|
25970
|
+
computerShell: external_exports.boolean().optional(),
|
|
25907
25971
|
cwd: external_exports.string().min(1),
|
|
25908
25972
|
env: external_exports.record(external_exports.string(), external_exports.string()).optional(),
|
|
25909
25973
|
cols: external_exports.number().int().positive().optional(),
|
|
@@ -26005,6 +26069,22 @@ var ghExecResultSchema = external_exports.object({
|
|
|
26005
26069
|
stdout: external_exports.string(),
|
|
26006
26070
|
stderr: external_exports.string()
|
|
26007
26071
|
});
|
|
26072
|
+
var claudeSessionReadParamsSchema = external_exports.object({
|
|
26073
|
+
cwd: external_exports.string().min(1).optional(),
|
|
26074
|
+
projectId: external_exports.string().min(1).optional(),
|
|
26075
|
+
sessionId: external_exports.string().uuid(),
|
|
26076
|
+
/** Newest records to return at most. */
|
|
26077
|
+
maxRecords: external_exports.number().int().positive().max(5e3),
|
|
26078
|
+
/** Their total JSON size at most (large tool results are shortened first). */
|
|
26079
|
+
maxBytes: external_exports.number().int().positive().max(16 * 1024 * 1024)
|
|
26080
|
+
});
|
|
26081
|
+
var claudeSessionReadResultSchema = external_exports.object({
|
|
26082
|
+
/** False: no session file for that id in that cwd's project dir. */
|
|
26083
|
+
found: external_exports.boolean(),
|
|
26084
|
+
records: external_exports.array(external_exports.record(external_exports.string(), external_exports.unknown())),
|
|
26085
|
+
/** Older records of the branch that were not sent (the caps). */
|
|
26086
|
+
omitted: external_exports.number().int().nonnegative()
|
|
26087
|
+
});
|
|
26008
26088
|
var computerShutdownParamsSchema = external_exports.object({
|
|
26009
26089
|
reason: external_exports.string().optional(),
|
|
26010
26090
|
/** Grace period before the computer may hard-exit. */
|
|
@@ -26252,7 +26332,9 @@ var procSpawnParamsSchema = external_exports.object({
|
|
|
26252
26332
|
*/
|
|
26253
26333
|
shell: external_exports.boolean().optional(),
|
|
26254
26334
|
cwd: external_exports.string().min(1).optional(),
|
|
26255
|
-
env: external_exports.record(external_exports.string(), external_exports.string()).optional()
|
|
26335
|
+
env: external_exports.record(external_exports.string(), external_exports.string()).optional(),
|
|
26336
|
+
/** Agent-start admission lane (chat agents), as for `pty.spawn`. Absent: start at once. */
|
|
26337
|
+
admission: ptySpawnAdmissionSchema.optional()
|
|
26256
26338
|
});
|
|
26257
26339
|
var procSpawnResultSchema = external_exports.object({
|
|
26258
26340
|
/** `null` when the OS never assigned one (immediate spawn failure). The computer
|
|
@@ -26262,12 +26344,38 @@ var procSpawnResultSchema = external_exports.object({
|
|
|
26262
26344
|
});
|
|
26263
26345
|
var procKillParamsSchema = external_exports.object({
|
|
26264
26346
|
sessionId: external_exports.string().min(1),
|
|
26265
|
-
signal: external_exports.string().optional()
|
|
26347
|
+
signal: external_exports.string().optional(),
|
|
26348
|
+
/** Only kill this attempt: a kill meant for a replaced process must not hit its successor. */
|
|
26349
|
+
spawnId: spawnIdField
|
|
26266
26350
|
});
|
|
26267
26351
|
var procWriteParamsSchema = external_exports.object({
|
|
26268
26352
|
sessionId: external_exports.string().min(1),
|
|
26269
|
-
data: external_exports.string()
|
|
26353
|
+
data: external_exports.string(),
|
|
26354
|
+
/** Only write to this attempt; a replaced process is refused (`stale-spawn`). */
|
|
26355
|
+
spawnId: spawnIdField,
|
|
26356
|
+
/**
|
|
26357
|
+
* Idempotency key: a write the hub retries (its reply was lost) is written once.
|
|
26358
|
+
* The reply is sent only after the bytes reached the process's stdin, and a
|
|
26359
|
+
* write that cannot be delivered (unknown session, replaced attempt, broken pipe)
|
|
26360
|
+
* is refused rather than acked — so `{ok:true}` means delivered.
|
|
26361
|
+
*/
|
|
26362
|
+
writeId: external_exports.string().min(1).max(200).optional()
|
|
26270
26363
|
});
|
|
26364
|
+
var procWriteResultSchema = external_exports.object({
|
|
26365
|
+
ok: external_exports.literal(true),
|
|
26366
|
+
delivered: external_exports.literal(true).optional(),
|
|
26367
|
+
duplicate: external_exports.boolean().optional()
|
|
26368
|
+
});
|
|
26369
|
+
var ProcRefusalReasons = {
|
|
26370
|
+
/** `proc.spawn`: the command is not on the computer's PATH. `data.binary` names it. */
|
|
26371
|
+
binaryNotFound: "binary-not-found",
|
|
26372
|
+
/** `proc.write`: no process under this session id. */
|
|
26373
|
+
unknownSession: "unknown-session",
|
|
26374
|
+
/** `proc.write`: the session now runs a different spawn attempt. */
|
|
26375
|
+
staleSpawn: "stale-spawn",
|
|
26376
|
+
/** `proc.write`: the process's stdin is closed or broken. */
|
|
26377
|
+
stdinClosed: "stdin-closed"
|
|
26378
|
+
};
|
|
26271
26379
|
var procGetBufferSinceParamsSchema = external_exports.object({
|
|
26272
26380
|
sessionId: external_exports.string().min(1),
|
|
26273
26381
|
seq: external_exports.number().int()
|
|
@@ -26349,6 +26457,20 @@ function wsDataToText(data) {
|
|
|
26349
26457
|
return data.toString("utf8");
|
|
26350
26458
|
}
|
|
26351
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
|
+
|
|
26352
26474
|
// ../../domains/computer-transport/src/client/backoff.ts
|
|
26353
26475
|
var DEFAULT_BACKOFF = {
|
|
26354
26476
|
initialDelayMs: 100,
|
|
@@ -26842,6 +26964,64 @@ var HubDialer = class {
|
|
|
26842
26964
|
}
|
|
26843
26965
|
};
|
|
26844
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
|
+
|
|
26845
27025
|
// src/config.ts
|
|
26846
27026
|
var import_node_fs2 = require("node:fs");
|
|
26847
27027
|
var import_node_os2 = require("node:os");
|
|
@@ -26917,7 +27097,17 @@ var computerConfigSchema = external_exports.object({
|
|
|
26917
27097
|
* projects the user adds. No file key and no env var sets it, so nothing a hub
|
|
26918
27098
|
* can write turns it on.
|
|
26919
27099
|
*/
|
|
26920
|
-
hubMayEditRoots: external_exports.boolean().optional()
|
|
27100
|
+
hubMayEditRoots: external_exports.boolean().optional(),
|
|
27101
|
+
/**
|
|
27102
|
+
* A directory whose executables take precedence over PATH for every process this
|
|
27103
|
+
* computer starts — prepended in `buildSpawnEnv`, the one place both the pty and
|
|
27104
|
+
* the proc handler build a child's env. Exists so tests can swap an agent CLI for
|
|
27105
|
+
* a scripted fake (`@slayzone/test-utils/fake-claude`): a dir merely prepended to
|
|
27106
|
+
* the inherited PATH loses, because the computer adopts the login-shell PATH with
|
|
27107
|
+
* its entries FIRST at boot (login-shell-path.ts). Absolute: a relative dir would
|
|
27108
|
+
* resolve against each spawn's cwd. Env channel only (`ENV_VARS.binOverrideDir`).
|
|
27109
|
+
*/
|
|
27110
|
+
binOverrideDir: external_exports.string().min(1).refine((dir) => (0, import_node_path3.isAbsolute)(dir), { message: "must be an absolute path" }).optional()
|
|
26921
27111
|
});
|
|
26922
27112
|
var ENV_VARS = {
|
|
26923
27113
|
// Authority ONLY — `host[:port]`, no scheme, no path. The dial scheme
|
|
@@ -26936,7 +27126,9 @@ var ENV_VARS = {
|
|
|
26936
27126
|
// named its CONSUMER, which is what CLAUDE.md rule 2 forbids. `_TOKEN` suffix +
|
|
26937
27127
|
// `HUB_` family per rule 3, matching the domain term used everywhere else
|
|
26938
27128
|
// (`szjt1.`, the `join_tokens` table, mintJoinToken, POST /api/computers/join-token).
|
|
26939
|
-
joinToken: "SLAYZONE_HUB_JOIN_TOKEN"
|
|
27129
|
+
joinToken: "SLAYZONE_HUB_JOIN_TOKEN",
|
|
27130
|
+
// Read only by the computer (rule 2), a directory (rule 3). See `binOverrideDir`.
|
|
27131
|
+
binOverrideDir: "SLAYZONE_COMPUTER_BIN_OVERRIDE_DIR"
|
|
26940
27132
|
// No alias for the pre-rename `SLAYZONE_RUNNER_JOIN_TOKEN`: it never shipped.
|
|
26941
27133
|
// The published computer betas (0.36.0-beta.2/3) read `SLAYZONE_JOIN_TOKEN`, and
|
|
26942
27134
|
// the name it was renamed FROM only ever existed on unreleased main, so there is
|
|
@@ -26979,8 +27171,8 @@ var protectedPaths = [];
|
|
|
26979
27171
|
function setProtectedPaths(paths) {
|
|
26980
27172
|
protectedPaths = paths.map((p) => realpathBoundary(p));
|
|
26981
27173
|
}
|
|
26982
|
-
function isInside(
|
|
26983
|
-
return
|
|
27174
|
+
function isInside(path11, dir) {
|
|
27175
|
+
return path11 === dir || path11.startsWith(dir + import_node_path3.sep);
|
|
26984
27176
|
}
|
|
26985
27177
|
function isProtectedPath(absolute) {
|
|
26986
27178
|
return protectedPaths.some((guarded) => isInside(absolute, guarded));
|
|
@@ -27055,7 +27247,8 @@ function loadComputerConfig(env = process.env, shared = env === process.env && e
|
|
|
27055
27247
|
// <ROOT>/computer.config.json — base under env (spread after). The single config file.
|
|
27056
27248
|
...fromShared,
|
|
27057
27249
|
...hubUrlFromEnv !== void 0 ? { hubUrl: hubUrlFromEnv } : {},
|
|
27058
|
-
...envJoinToken !== void 0 ? { joinToken: envJoinToken } : {}
|
|
27250
|
+
...envJoinToken !== void 0 ? { joinToken: envJoinToken } : {},
|
|
27251
|
+
...env[ENV_VARS.binOverrideDir] !== void 0 ? { binOverrideDir: env[ENV_VARS.binOverrideDir] } : {}
|
|
27059
27252
|
};
|
|
27060
27253
|
const result = computerConfigSchema.safeParse(merged);
|
|
27061
27254
|
if (result.success) {
|
|
@@ -27350,8 +27543,10 @@ function assertHubLocalExecEnabled(refusal) {
|
|
|
27350
27543
|
|
|
27351
27544
|
// ../../shared/platform/src/env-manifest.ts
|
|
27352
27545
|
var ENV_MANIFEST = {
|
|
27353
|
-
// --- secret: credentials/tokens, never
|
|
27546
|
+
// --- secret: credentials/tokens, never inherited into a terminal ---
|
|
27354
27547
|
SLAYZONE_HUB_TOKEN: "secret",
|
|
27548
|
+
SLAYZONE_CLI_RELAY_TOKEN: "secret",
|
|
27549
|
+
// computer-issued capability, re-added per session
|
|
27355
27550
|
// HMAC signing key for ALL of hub-auth (better-auth's session/cookie signer AND
|
|
27356
27551
|
// the computer enroll/api-key credentials) — hence `HUB_AUTH`, not the former
|
|
27357
27552
|
// `HUB_COMPUTER_TRANSPORT_SECRET`, which named just one of its consumers. Rule 2
|
|
@@ -27386,6 +27581,8 @@ var ENV_MANIFEST = {
|
|
|
27386
27581
|
// name refuses to boot. Stripping both here keeps a terminal's dial address from
|
|
27387
27582
|
// ever being mistaken for another process's listen address.
|
|
27388
27583
|
SLAYZONE_HUB_ADDRESS: "infra",
|
|
27584
|
+
SLAYZONE_CLI_RELAY_ADDRESS: "infra",
|
|
27585
|
+
// computer loopback; re-added per session
|
|
27389
27586
|
SLAYZONE_HUB_LISTEN_ADDRESS: "infra",
|
|
27390
27587
|
// hub-only; where the hub listens
|
|
27391
27588
|
SLAYZONE_HUB_PUBLIC_ADDRESS: "infra",
|
|
@@ -27452,6 +27649,11 @@ var ENV_MANIFEST = {
|
|
|
27452
27649
|
// overrides below. `infra`: a spawned terminal must never inherit it, or an
|
|
27453
27650
|
// agent would resolve a different notify.sh than the one wired into its hooks.
|
|
27454
27651
|
SLAYZONE_MACHINE_DIR: "infra",
|
|
27652
|
+
// Computer-only: a directory whose executables take precedence over PATH for
|
|
27653
|
+
// every process the computer starts (spawn-env.ts). The e2e chat group puts its
|
|
27654
|
+
// fake `claude` there — a dir merely prepended to the inherited PATH loses to the
|
|
27655
|
+
// login-shell PATH the computer adopts at boot. Infra: an agent must not inherit it.
|
|
27656
|
+
SLAYZONE_COMPUTER_BIN_OVERRIDE_DIR: "infra",
|
|
27455
27657
|
// per-provider config/hook/plugin path overrides (test/sandbox redirects)
|
|
27456
27658
|
SLAYZONE_CLAUDE_SETTINGS_PATH: "infra",
|
|
27457
27659
|
SLAYZONE_GEMINI_SETTINGS_PATH: "infra",
|
|
@@ -27499,16 +27701,16 @@ var NON_PREFIXED_INFRA = /* @__PURE__ */ new Set([
|
|
|
27499
27701
|
function sanitizeSpawnEnv(base) {
|
|
27500
27702
|
const out = {};
|
|
27501
27703
|
for (const [key, value] of Object.entries(base)) {
|
|
27502
|
-
if (
|
|
27503
|
-
if (NON_PREFIXED_INFRA.has(key)) continue;
|
|
27504
|
-
if (key.startsWith("SLAYZONE_")) {
|
|
27505
|
-
if (ENV_MANIFEST[key] === "global") out[key] = value;
|
|
27506
|
-
continue;
|
|
27507
|
-
}
|
|
27508
|
-
out[key] = value;
|
|
27704
|
+
if (keptInSpawnEnv(key, value)) out[key] = value;
|
|
27509
27705
|
}
|
|
27510
27706
|
return out;
|
|
27511
27707
|
}
|
|
27708
|
+
function keptInSpawnEnv(key, value) {
|
|
27709
|
+
if (typeof value !== "string") return false;
|
|
27710
|
+
if (NON_PREFIXED_INFRA.has(key)) return false;
|
|
27711
|
+
if (key.startsWith("SLAYZONE_")) return ENV_MANIFEST[key] === "global";
|
|
27712
|
+
return true;
|
|
27713
|
+
}
|
|
27512
27714
|
|
|
27513
27715
|
// ../../shared/platform/src/index.ts
|
|
27514
27716
|
init_fs_utils();
|
|
@@ -27569,6 +27771,15 @@ function getShellStartupArgs(shellPath) {
|
|
|
27569
27771
|
}
|
|
27570
27772
|
return [];
|
|
27571
27773
|
}
|
|
27774
|
+
function quoteForShell(arg) {
|
|
27775
|
+
if ((0, import_os2.platform)() === "win32") {
|
|
27776
|
+
if (arg.length === 0) return '""';
|
|
27777
|
+
if (!/[\s"&|<>^%!]/.test(arg)) return arg;
|
|
27778
|
+
return `"${arg.replace(/"/g, '""')}"`;
|
|
27779
|
+
}
|
|
27780
|
+
if (arg.length === 0) return "''";
|
|
27781
|
+
return `'${arg.replace(/'/g, `'"'"'`)}'`;
|
|
27782
|
+
}
|
|
27572
27783
|
function buildShellInvocation(command) {
|
|
27573
27784
|
const shell = resolveUserShell();
|
|
27574
27785
|
if ((0, import_os2.platform)() === "win32") return { file: shell, args: ["/c", command] };
|
|
@@ -27645,6 +27856,16 @@ async function probeLoginShellPath(opts = {}) {
|
|
|
27645
27856
|
}
|
|
27646
27857
|
return null;
|
|
27647
27858
|
}
|
|
27859
|
+
var FIND_ON_PATH_TIMEOUT_MS = 3e3;
|
|
27860
|
+
function findOnPath(name, env) {
|
|
27861
|
+
const [file2, args] = (0, import_node_os3.platform)() === "win32" ? ["where", [name]] : ["/bin/sh", ["-c", `command -v ${quoteForShell(name)}`]];
|
|
27862
|
+
return new Promise((resolve9) => {
|
|
27863
|
+
(0, import_node_child_process2.execFile)(file2, args, { timeout: FIND_ON_PATH_TIMEOUT_MS, env }, (err, stdout2) => {
|
|
27864
|
+
const found = err ? "" : String(stdout2).trim().split("\n")[0]?.trim();
|
|
27865
|
+
resolve9(found ? found : null);
|
|
27866
|
+
});
|
|
27867
|
+
});
|
|
27868
|
+
}
|
|
27648
27869
|
|
|
27649
27870
|
// ../../shared/platform/src/slz-file-url.ts
|
|
27650
27871
|
var SLZ_FILE_HOST = "app";
|
|
@@ -27664,8 +27885,8 @@ async function adoptLoginShellPath(opts = {}) {
|
|
|
27664
27885
|
}
|
|
27665
27886
|
|
|
27666
27887
|
// src/main.ts
|
|
27667
|
-
var
|
|
27668
|
-
var
|
|
27888
|
+
var import_node_fs14 = require("node:fs");
|
|
27889
|
+
var import_node_path17 = require("node:path");
|
|
27669
27890
|
|
|
27670
27891
|
// src/request-log.ts
|
|
27671
27892
|
var import_node_fs4 = require("node:fs");
|
|
@@ -27969,6 +28190,155 @@ function createAgentHookServer(ctx) {
|
|
|
27969
28190
|
});
|
|
27970
28191
|
}
|
|
27971
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
|
+
|
|
27972
28342
|
// src/handlers/fs.ts
|
|
27973
28343
|
var import_node_fs7 = require("node:fs");
|
|
27974
28344
|
var import_promises7 = require("node:fs/promises");
|
|
@@ -28465,9 +28835,9 @@ var WORKSPACE_MAX_SEARCH_RESULTS = 2e3;
|
|
|
28465
28835
|
function createLocalWorkspaceFs() {
|
|
28466
28836
|
return refuseOnSwitchedOffHub(createUnguardedLocalWorkspaceFs());
|
|
28467
28837
|
}
|
|
28468
|
-
function refuseOnSwitchedOffHub(
|
|
28838
|
+
function refuseOnSwitchedOffHub(fs8) {
|
|
28469
28839
|
const guarded = {};
|
|
28470
|
-
for (const [op, fn] of Object.entries(
|
|
28840
|
+
for (const [op, fn] of Object.entries(fs8)) {
|
|
28471
28841
|
guarded[op] = (...args) => {
|
|
28472
28842
|
assertHubLocalExecEnabled({ op: `fs.${op}` });
|
|
28473
28843
|
return fn(...args);
|
|
@@ -28492,8 +28862,8 @@ function createUnguardedLocalWorkspaceFs() {
|
|
|
28492
28862
|
sep: import_node_path9.sep
|
|
28493
28863
|
};
|
|
28494
28864
|
},
|
|
28495
|
-
async listDir(
|
|
28496
|
-
const abs = import_node_fs6.realpathSync.native((0, import_node_path9.resolve)(
|
|
28865
|
+
async listDir(path11, options) {
|
|
28866
|
+
const abs = import_node_fs6.realpathSync.native((0, import_node_path9.resolve)(path11));
|
|
28497
28867
|
const dirents = await (0, import_promises6.readdir)(abs, { withFileTypes: true });
|
|
28498
28868
|
const entries = [];
|
|
28499
28869
|
for (const e of dirents) {
|
|
@@ -28525,13 +28895,13 @@ function createUnguardedLocalWorkspaceFs() {
|
|
|
28525
28895
|
const parent = (0, import_node_path9.dirname)(abs);
|
|
28526
28896
|
return { path: abs, parent: parent === abs ? null : parent, entries };
|
|
28527
28897
|
},
|
|
28528
|
-
async mkdir(
|
|
28529
|
-
const abs = (0, import_node_path9.resolve)(
|
|
28898
|
+
async mkdir(path11) {
|
|
28899
|
+
const abs = (0, import_node_path9.resolve)(path11);
|
|
28530
28900
|
await (0, import_promises6.mkdir)(abs, { recursive: true });
|
|
28531
28901
|
return { path: import_node_fs6.realpathSync.native(abs) };
|
|
28532
28902
|
},
|
|
28533
|
-
pathExists(
|
|
28534
|
-
return (0, import_node_fs6.existsSync)((0, import_node_path9.resolve)(
|
|
28903
|
+
pathExists(path11) {
|
|
28904
|
+
return (0, import_node_fs6.existsSync)((0, import_node_path9.resolve)(path11));
|
|
28535
28905
|
},
|
|
28536
28906
|
// A hub has no path-jail — `allowedRoots` is a computer concept. Saying so is
|
|
28537
28907
|
// better than accepting the edit and discarding it, which would leave the
|
|
@@ -28546,12 +28916,12 @@ function createUnguardedLocalWorkspaceFs() {
|
|
|
28546
28916
|
// the fork sidecar and tests, neither of which should be minting a new
|
|
28547
28917
|
// persistent state file for a mapping a real computer owns.
|
|
28548
28918
|
resolveProjectPath(projectId) {
|
|
28549
|
-
const
|
|
28550
|
-
if (!
|
|
28551
|
-
return { path:
|
|
28919
|
+
const path11 = localProjectPaths.get(projectId);
|
|
28920
|
+
if (!path11) return { path: null };
|
|
28921
|
+
return { path: path11, exists: (0, import_node_fs6.existsSync)(path11) };
|
|
28552
28922
|
},
|
|
28553
|
-
setProjectPath(projectId,
|
|
28554
|
-
const abs = import_node_fs6.realpathSync.native((0, import_node_path9.resolve)(
|
|
28923
|
+
setProjectPath(projectId, path11) {
|
|
28924
|
+
const abs = import_node_fs6.realpathSync.native((0, import_node_path9.resolve)(path11));
|
|
28555
28925
|
localProjectPaths.set(projectId, abs);
|
|
28556
28926
|
return { path: abs };
|
|
28557
28927
|
},
|
|
@@ -28591,12 +28961,12 @@ function createUnguardedLocalWorkspaceFs() {
|
|
|
28591
28961
|
function createLocalWorkspaceFsAdapters(local = createLocalWorkspaceFs()) {
|
|
28592
28962
|
return {
|
|
28593
28963
|
listRoots: async () => local.listRoots(),
|
|
28594
|
-
listDir: async (_r,
|
|
28595
|
-
mkdir: async (_r,
|
|
28596
|
-
pathExists: async (_r,
|
|
28964
|
+
listDir: async (_r, path11, options) => local.listDir(path11, options),
|
|
28965
|
+
mkdir: async (_r, path11) => local.mkdir(path11),
|
|
28966
|
+
pathExists: async (_r, path11) => local.pathExists(path11),
|
|
28597
28967
|
setAllowedRoots: async (_r, roots) => local.setAllowedRoots(roots),
|
|
28598
28968
|
resolveProjectPath: async (_r, projectId) => local.resolveProjectPath(projectId),
|
|
28599
|
-
setProjectPath: async (_r, projectId,
|
|
28969
|
+
setProjectPath: async (_r, projectId, path11) => local.setProjectPath(projectId, path11),
|
|
28600
28970
|
forgetProjectPath: async (_r, projectId) => local.forgetProjectPath(projectId),
|
|
28601
28971
|
readDir: async (_r, rootPath, dirPath) => local.readDir(rootPath, dirPath),
|
|
28602
28972
|
readFile: async (_r, rootPath, filePath, force) => local.readFile(rootPath, filePath, force),
|
|
@@ -28686,28 +29056,28 @@ var noop = () => {
|
|
|
28686
29056
|
var freezeIfAvailable = (obj) => {
|
|
28687
29057
|
if (Object.freeze) Object.freeze(obj);
|
|
28688
29058
|
};
|
|
28689
|
-
function createInnerProxy(callback,
|
|
29059
|
+
function createInnerProxy(callback, path11, memo2) {
|
|
28690
29060
|
var _memo$cacheKey;
|
|
28691
|
-
const cacheKey =
|
|
29061
|
+
const cacheKey = path11.join(".");
|
|
28692
29062
|
(_memo$cacheKey = memo2[cacheKey]) !== null && _memo$cacheKey !== void 0 || (memo2[cacheKey] = new Proxy(noop, {
|
|
28693
29063
|
get(_obj, key) {
|
|
28694
29064
|
if (typeof key !== "string" || key === "then") return;
|
|
28695
|
-
return createInnerProxy(callback, [...
|
|
29065
|
+
return createInnerProxy(callback, [...path11, key], memo2);
|
|
28696
29066
|
},
|
|
28697
29067
|
apply(_1, _2, args) {
|
|
28698
|
-
const lastOfPath =
|
|
28699
|
-
if (lastOfPath === "valueOf" || lastOfPath === "toString" || lastOfPath === "toJSON") return `tRPC.proxy(${
|
|
29068
|
+
const lastOfPath = path11[path11.length - 1];
|
|
29069
|
+
if (lastOfPath === "valueOf" || lastOfPath === "toString" || lastOfPath === "toJSON") return `tRPC.proxy(${path11.slice(0, -1).join(".")})`;
|
|
28700
29070
|
let opts = {
|
|
28701
29071
|
args,
|
|
28702
|
-
path:
|
|
29072
|
+
path: path11
|
|
28703
29073
|
};
|
|
28704
29074
|
if (lastOfPath === "call") opts = {
|
|
28705
29075
|
args: args.length >= 2 ? [args[1]] : [],
|
|
28706
|
-
path:
|
|
29076
|
+
path: path11.slice(0, -1)
|
|
28707
29077
|
};
|
|
28708
29078
|
else if (lastOfPath === "apply") opts = {
|
|
28709
29079
|
args: args.length >= 2 ? args[1] : [],
|
|
28710
|
-
path:
|
|
29080
|
+
path: path11.slice(0, -1)
|
|
28711
29081
|
};
|
|
28712
29082
|
freezeIfAvailable(opts.args);
|
|
28713
29083
|
freezeIfAvailable(opts.path);
|
|
@@ -28886,12 +29256,12 @@ function createRouterFactory(config2) {
|
|
|
28886
29256
|
})
|
|
28887
29257
|
};
|
|
28888
29258
|
}
|
|
28889
|
-
function step(from,
|
|
29259
|
+
function step(from, path11 = []) {
|
|
28890
29260
|
const aggregate = emptyObject();
|
|
28891
29261
|
for (const [key, item] of Object.entries(from !== null && from !== void 0 ? from : {})) {
|
|
28892
29262
|
if (isLazy(item)) {
|
|
28893
|
-
lazy3[[...
|
|
28894
|
-
path:
|
|
29263
|
+
lazy3[[...path11, key].join(".")] = createLazyLoader({
|
|
29264
|
+
path: path11,
|
|
28895
29265
|
ref: item,
|
|
28896
29266
|
key,
|
|
28897
29267
|
aggregate
|
|
@@ -28899,14 +29269,14 @@ function createRouterFactory(config2) {
|
|
|
28899
29269
|
continue;
|
|
28900
29270
|
}
|
|
28901
29271
|
if (isRouter(item)) {
|
|
28902
|
-
aggregate[key] = step(item._def.record, [...
|
|
29272
|
+
aggregate[key] = step(item._def.record, [...path11, key]);
|
|
28903
29273
|
continue;
|
|
28904
29274
|
}
|
|
28905
29275
|
if (!isProcedure(item)) {
|
|
28906
|
-
aggregate[key] = step(item, [...
|
|
29276
|
+
aggregate[key] = step(item, [...path11, key]);
|
|
28907
29277
|
continue;
|
|
28908
29278
|
}
|
|
28909
|
-
const newPath = [...
|
|
29279
|
+
const newPath = [...path11, key].join(".");
|
|
28910
29280
|
if (procedures[newPath]) throw new Error(`Duplicate key: ${newPath}`);
|
|
28911
29281
|
procedures[newPath] = item;
|
|
28912
29282
|
aggregate[key] = item;
|
|
@@ -28930,14 +29300,14 @@ function createRouterFactory(config2) {
|
|
|
28930
29300
|
function isProcedure(procedureOrRouter) {
|
|
28931
29301
|
return typeof procedureOrRouter === "function";
|
|
28932
29302
|
}
|
|
28933
|
-
async function getProcedureAtPath(router2,
|
|
29303
|
+
async function getProcedureAtPath(router2, path11) {
|
|
28934
29304
|
const { _def } = router2;
|
|
28935
|
-
let procedure = _def.procedures[
|
|
29305
|
+
let procedure = _def.procedures[path11];
|
|
28936
29306
|
while (!procedure) {
|
|
28937
|
-
const key = Object.keys(_def.lazy).find((key2) =>
|
|
29307
|
+
const key = Object.keys(_def.lazy).find((key2) => path11 === key2 || path11.startsWith(key2 + "."));
|
|
28938
29308
|
if (!key) return null;
|
|
28939
29309
|
await _def.lazy[key].load();
|
|
28940
|
-
procedure = _def.procedures[
|
|
29310
|
+
procedure = _def.procedures[path11];
|
|
28941
29311
|
}
|
|
28942
29312
|
return procedure;
|
|
28943
29313
|
}
|
|
@@ -28946,15 +29316,15 @@ function createCallerFactory() {
|
|
|
28946
29316
|
const { _def } = router2;
|
|
28947
29317
|
return function createCaller(ctxOrCallback, opts) {
|
|
28948
29318
|
return createRecursiveProxy(async (innerOpts) => {
|
|
28949
|
-
const { path:
|
|
28950
|
-
const fullPath =
|
|
28951
|
-
if (
|
|
29319
|
+
const { path: path11, args } = innerOpts;
|
|
29320
|
+
const fullPath = path11.join(".");
|
|
29321
|
+
if (path11.length === 1 && path11[0] === "_def") return _def;
|
|
28952
29322
|
const procedure = await getProcedureAtPath(router2, fullPath);
|
|
28953
29323
|
let ctx = void 0;
|
|
28954
29324
|
try {
|
|
28955
29325
|
if (!procedure) throw new TRPCError({
|
|
28956
29326
|
code: "NOT_FOUND",
|
|
28957
|
-
message: `No procedure found on path "${
|
|
29327
|
+
message: `No procedure found on path "${path11}"`
|
|
28958
29328
|
});
|
|
28959
29329
|
ctx = isFunction(ctxOrCallback) ? await Promise.resolve(ctxOrCallback()) : ctxOrCallback;
|
|
28960
29330
|
return await procedure({
|
|
@@ -29558,7 +29928,7 @@ var isURL = (payload) => payload instanceof URL;
|
|
|
29558
29928
|
|
|
29559
29929
|
// ../../../node_modules/superjson/dist/pathstringifier.js
|
|
29560
29930
|
var escapeKey = (key) => key.replace(/\\/g, "\\\\").replace(/\./g, "\\.");
|
|
29561
|
-
var stringifyPath = (
|
|
29931
|
+
var stringifyPath = (path11) => path11.map(String).map(escapeKey).join(".");
|
|
29562
29932
|
var parsePath = (string4, legacyPaths) => {
|
|
29563
29933
|
const result = [];
|
|
29564
29934
|
let segment = "";
|
|
@@ -29805,26 +30175,26 @@ var getNthKey = (value, n) => {
|
|
|
29805
30175
|
}
|
|
29806
30176
|
return keys.next().value;
|
|
29807
30177
|
};
|
|
29808
|
-
function validatePath(
|
|
29809
|
-
if (includes(
|
|
30178
|
+
function validatePath(path11) {
|
|
30179
|
+
if (includes(path11, "__proto__")) {
|
|
29810
30180
|
throw new Error("__proto__ is not allowed as a property");
|
|
29811
30181
|
}
|
|
29812
|
-
if (includes(
|
|
30182
|
+
if (includes(path11, "prototype")) {
|
|
29813
30183
|
throw new Error("prototype is not allowed as a property");
|
|
29814
30184
|
}
|
|
29815
|
-
if (includes(
|
|
30185
|
+
if (includes(path11, "constructor")) {
|
|
29816
30186
|
throw new Error("constructor is not allowed as a property");
|
|
29817
30187
|
}
|
|
29818
30188
|
}
|
|
29819
|
-
var getDeep = (object2,
|
|
29820
|
-
validatePath(
|
|
29821
|
-
for (let i = 0; i <
|
|
29822
|
-
const key =
|
|
30189
|
+
var getDeep = (object2, path11) => {
|
|
30190
|
+
validatePath(path11);
|
|
30191
|
+
for (let i = 0; i < path11.length; i++) {
|
|
30192
|
+
const key = path11[i];
|
|
29823
30193
|
if (isSet(object2)) {
|
|
29824
30194
|
object2 = getNthKey(object2, +key);
|
|
29825
30195
|
} else if (isMap(object2)) {
|
|
29826
30196
|
const row = +key;
|
|
29827
|
-
const type = +
|
|
30197
|
+
const type = +path11[++i] === 0 ? "key" : "value";
|
|
29828
30198
|
const keyOfRow = getNthKey(object2, row);
|
|
29829
30199
|
switch (type) {
|
|
29830
30200
|
case "key":
|
|
@@ -29840,14 +30210,14 @@ var getDeep = (object2, path9) => {
|
|
|
29840
30210
|
}
|
|
29841
30211
|
return object2;
|
|
29842
30212
|
};
|
|
29843
|
-
var setDeep = (object2,
|
|
29844
|
-
validatePath(
|
|
29845
|
-
if (
|
|
30213
|
+
var setDeep = (object2, path11, mapper) => {
|
|
30214
|
+
validatePath(path11);
|
|
30215
|
+
if (path11.length === 0) {
|
|
29846
30216
|
return mapper(object2);
|
|
29847
30217
|
}
|
|
29848
30218
|
let parent = object2;
|
|
29849
|
-
for (let i = 0; i <
|
|
29850
|
-
const key =
|
|
30219
|
+
for (let i = 0; i < path11.length - 1; i++) {
|
|
30220
|
+
const key = path11[i];
|
|
29851
30221
|
if (isArray(parent)) {
|
|
29852
30222
|
const index = +key;
|
|
29853
30223
|
parent = parent[index];
|
|
@@ -29857,12 +30227,12 @@ var setDeep = (object2, path9, mapper) => {
|
|
|
29857
30227
|
const row = +key;
|
|
29858
30228
|
parent = getNthKey(parent, row);
|
|
29859
30229
|
} else if (isMap(parent)) {
|
|
29860
|
-
const isEnd = i ===
|
|
30230
|
+
const isEnd = i === path11.length - 2;
|
|
29861
30231
|
if (isEnd) {
|
|
29862
30232
|
break;
|
|
29863
30233
|
}
|
|
29864
30234
|
const row = +key;
|
|
29865
|
-
const type = +
|
|
30235
|
+
const type = +path11[++i] === 0 ? "key" : "value";
|
|
29866
30236
|
const keyOfRow = getNthKey(parent, row);
|
|
29867
30237
|
switch (type) {
|
|
29868
30238
|
case "key":
|
|
@@ -29874,7 +30244,7 @@ var setDeep = (object2, path9, mapper) => {
|
|
|
29874
30244
|
}
|
|
29875
30245
|
}
|
|
29876
30246
|
}
|
|
29877
|
-
const lastKey =
|
|
30247
|
+
const lastKey = path11[path11.length - 1];
|
|
29878
30248
|
if (isArray(parent)) {
|
|
29879
30249
|
parent[+lastKey] = mapper(parent[+lastKey]);
|
|
29880
30250
|
} else if (isPlainObject2(parent)) {
|
|
@@ -29889,7 +30259,7 @@ var setDeep = (object2, path9, mapper) => {
|
|
|
29889
30259
|
}
|
|
29890
30260
|
}
|
|
29891
30261
|
if (isMap(parent)) {
|
|
29892
|
-
const row = +
|
|
30262
|
+
const row = +path11[path11.length - 2];
|
|
29893
30263
|
const keyToRow = getNthKey(parent, row);
|
|
29894
30264
|
const type = +lastKey === 0 ? "key" : "value";
|
|
29895
30265
|
switch (type) {
|
|
@@ -29936,16 +30306,16 @@ function traverse(tree, walker2, version2, origin = []) {
|
|
|
29936
30306
|
walker2(nodeValue, origin);
|
|
29937
30307
|
}
|
|
29938
30308
|
function applyValueAnnotations(plain, annotations, version2, superJson) {
|
|
29939
|
-
traverse(annotations, (type,
|
|
29940
|
-
plain = setDeep(plain,
|
|
30309
|
+
traverse(annotations, (type, path11) => {
|
|
30310
|
+
plain = setDeep(plain, path11, (v) => untransformValue(v, type, superJson));
|
|
29941
30311
|
}, version2);
|
|
29942
30312
|
return plain;
|
|
29943
30313
|
}
|
|
29944
30314
|
function applyReferentialEqualityAnnotations(plain, annotations, version2) {
|
|
29945
30315
|
const legacyPaths = enableLegacyPaths(version2);
|
|
29946
|
-
function apply(identicalPaths,
|
|
29947
|
-
const object2 = getDeep(plain, parsePath(
|
|
29948
|
-
identicalPaths.map((
|
|
30316
|
+
function apply(identicalPaths, path11) {
|
|
30317
|
+
const object2 = getDeep(plain, parsePath(path11, legacyPaths));
|
|
30318
|
+
identicalPaths.map((path12) => parsePath(path12, legacyPaths)).forEach((identicalObjectPath) => {
|
|
29949
30319
|
plain = setDeep(plain, identicalObjectPath, () => object2);
|
|
29950
30320
|
});
|
|
29951
30321
|
}
|
|
@@ -29963,12 +30333,12 @@ function applyReferentialEqualityAnnotations(plain, annotations, version2) {
|
|
|
29963
30333
|
return plain;
|
|
29964
30334
|
}
|
|
29965
30335
|
var isDeep = (object2, superJson) => isPlainObject2(object2) || isArray(object2) || isMap(object2) || isSet(object2) || isError(object2) || isInstanceOfRegisteredClass(object2, superJson);
|
|
29966
|
-
function addIdentity(object2,
|
|
30336
|
+
function addIdentity(object2, path11, identities) {
|
|
29967
30337
|
const existingSet = identities.get(object2);
|
|
29968
30338
|
if (existingSet) {
|
|
29969
|
-
existingSet.push(
|
|
30339
|
+
existingSet.push(path11);
|
|
29970
30340
|
} else {
|
|
29971
|
-
identities.set(object2, [
|
|
30341
|
+
identities.set(object2, [path11]);
|
|
29972
30342
|
}
|
|
29973
30343
|
}
|
|
29974
30344
|
function generateReferentialEqualityAnnotations(identitites, dedupe) {
|
|
@@ -29979,7 +30349,7 @@ function generateReferentialEqualityAnnotations(identitites, dedupe) {
|
|
|
29979
30349
|
return;
|
|
29980
30350
|
}
|
|
29981
30351
|
if (!dedupe) {
|
|
29982
|
-
paths = paths.map((
|
|
30352
|
+
paths = paths.map((path11) => path11.map(String)).sort((a, b) => a.length - b.length);
|
|
29983
30353
|
}
|
|
29984
30354
|
const [representativePath, ...identicalPaths] = paths;
|
|
29985
30355
|
if (representativePath.length === 0) {
|
|
@@ -29998,10 +30368,10 @@ function generateReferentialEqualityAnnotations(identitites, dedupe) {
|
|
|
29998
30368
|
return isEmptyObject(result) ? void 0 : result;
|
|
29999
30369
|
}
|
|
30000
30370
|
}
|
|
30001
|
-
var walker = (object2, identities, superJson, dedupe,
|
|
30371
|
+
var walker = (object2, identities, superJson, dedupe, path11 = [], objectsInThisPath = [], seenObjects = /* @__PURE__ */ new Map()) => {
|
|
30002
30372
|
const primitive = isPrimitive(object2);
|
|
30003
30373
|
if (!primitive) {
|
|
30004
|
-
addIdentity(object2,
|
|
30374
|
+
addIdentity(object2, path11, identities);
|
|
30005
30375
|
const seen = seenObjects.get(object2);
|
|
30006
30376
|
if (seen) {
|
|
30007
30377
|
return dedupe ? {
|
|
@@ -30035,7 +30405,7 @@ var walker = (object2, identities, superJson, dedupe, path9 = [], objectsInThisP
|
|
|
30035
30405
|
if (index === "__proto__" || index === "constructor" || index === "prototype") {
|
|
30036
30406
|
throw new Error(`Detected property ${index}. This is a prototype pollution risk, please remove it from your object.`);
|
|
30037
30407
|
}
|
|
30038
|
-
const recursiveResult = walker(value, identities, superJson, dedupe, [...
|
|
30408
|
+
const recursiveResult = walker(value, identities, superJson, dedupe, [...path11, index], [...objectsInThisPath, object2], seenObjects);
|
|
30039
30409
|
transformedValue[index] = recursiveResult.transformedValue;
|
|
30040
30410
|
if (isArray(recursiveResult.annotations)) {
|
|
30041
30411
|
innerAnnotations[escapeKey(index)] = recursiveResult.annotations;
|
|
@@ -30281,12 +30651,12 @@ function auditAdminDenied(opts) {
|
|
|
30281
30651
|
detail: { reason: "not_admin", code: ADMIN_ONLY, ...opts.detail }
|
|
30282
30652
|
});
|
|
30283
30653
|
}
|
|
30284
|
-
function adminOnlyTrpcError(
|
|
30285
|
-
auditAdminDenied({ targetKind: "procedure", targetId:
|
|
30654
|
+
function adminOnlyTrpcError(path11) {
|
|
30655
|
+
auditAdminDenied({ targetKind: "procedure", targetId: path11 });
|
|
30286
30656
|
return new TRPCError({
|
|
30287
30657
|
code: "FORBIDDEN",
|
|
30288
|
-
message: `${
|
|
30289
|
-
cause: new AdminOnlyError(
|
|
30658
|
+
message: `${path11} needs a hub admin account`,
|
|
30659
|
+
cause: new AdminOnlyError(path11)
|
|
30290
30660
|
});
|
|
30291
30661
|
}
|
|
30292
30662
|
|
|
@@ -30339,45 +30709,45 @@ var router = t.router;
|
|
|
30339
30709
|
var middleware = t.middleware;
|
|
30340
30710
|
var mergeRouters2 = t.mergeRouters;
|
|
30341
30711
|
var securityContext = t.middleware(
|
|
30342
|
-
({ ctx, next, path:
|
|
30343
|
-
{ actor: ctx.principal?.userId ?? null, ip: peerAddress(ctx.req), route: `trpc ${
|
|
30712
|
+
({ ctx, next, path: path11 }) => runWithSecurityContext(
|
|
30713
|
+
{ actor: ctx.principal?.userId ?? null, ip: peerAddress(ctx.req), route: `trpc ${path11}` },
|
|
30344
30714
|
() => next()
|
|
30345
30715
|
)
|
|
30346
30716
|
);
|
|
30347
|
-
function auditDenied(
|
|
30717
|
+
function auditDenied(path11, detail) {
|
|
30348
30718
|
securityEvent({
|
|
30349
30719
|
event: "authz.denied",
|
|
30350
30720
|
outcome: "denied",
|
|
30351
30721
|
targetKind: "procedure",
|
|
30352
|
-
targetId:
|
|
30722
|
+
targetId: path11,
|
|
30353
30723
|
detail
|
|
30354
30724
|
});
|
|
30355
30725
|
}
|
|
30356
|
-
var authGate2 = t.middleware(({ ctx, next, path:
|
|
30726
|
+
var authGate2 = t.middleware(({ ctx, next, path: path11 }) => {
|
|
30357
30727
|
if (getAuthGate() && !ctx.principal) {
|
|
30358
|
-
auditDenied(
|
|
30728
|
+
auditDenied(path11, { reason: "unauthenticated" });
|
|
30359
30729
|
throw new TRPCError({ code: "UNAUTHORIZED", message: "hub requires authentication" });
|
|
30360
30730
|
}
|
|
30361
30731
|
return next();
|
|
30362
30732
|
});
|
|
30363
|
-
var scopeGate = t.middleware(({ ctx, next, path:
|
|
30733
|
+
var scopeGate = t.middleware(({ ctx, next, path: path11 }) => {
|
|
30364
30734
|
if (!getAuthGate()) return next();
|
|
30365
30735
|
const scopes = ctx.principal?.scopes ?? [];
|
|
30366
|
-
if (!procedurePolicy().scopeAllows(scopes,
|
|
30367
|
-
auditDenied(
|
|
30368
|
-
throw new TRPCError({ code: "FORBIDDEN", message: `insufficient scope for ${
|
|
30736
|
+
if (!procedurePolicy().scopeAllows(scopes, path11)) {
|
|
30737
|
+
auditDenied(path11, { reason: "scope", scopes });
|
|
30738
|
+
throw new TRPCError({ code: "FORBIDDEN", message: `insufficient scope for ${path11}` });
|
|
30369
30739
|
}
|
|
30370
30740
|
return next();
|
|
30371
30741
|
});
|
|
30372
|
-
var adminGate = t.middleware(({ ctx, next, path:
|
|
30742
|
+
var adminGate = t.middleware(({ ctx, next, path: path11 }) => {
|
|
30373
30743
|
if (!getAuthGate()) return next();
|
|
30374
|
-
if (!roleAllows(procedurePolicy().adminClassOf(
|
|
30375
|
-
throw adminOnlyTrpcError(
|
|
30744
|
+
if (!roleAllows(procedurePolicy().adminClassOf(path11), ctx.principal?.role ?? "user")) {
|
|
30745
|
+
throw adminOnlyTrpcError(path11);
|
|
30376
30746
|
}
|
|
30377
30747
|
return next();
|
|
30378
30748
|
});
|
|
30379
|
-
var ownerGate = t.middleware(async ({ ctx, next, path:
|
|
30380
|
-
const filter = getAuthGate() ? await procedurePolicy().ownerCheck({ ctx, path:
|
|
30749
|
+
var ownerGate = t.middleware(async ({ ctx, next, path: path11, type, getRawInput }) => {
|
|
30750
|
+
const filter = getAuthGate() ? await procedurePolicy().ownerCheck({ ctx, path: path11, type, getRawInput }) : null;
|
|
30381
30751
|
const result = await next();
|
|
30382
30752
|
if (!filter || !result.ok) return result;
|
|
30383
30753
|
return { ...result, data: await filter(result.data) };
|
|
@@ -30405,8 +30775,8 @@ var unsafePathRefusal = t.middleware(async ({ next }) => {
|
|
|
30405
30775
|
return result;
|
|
30406
30776
|
});
|
|
30407
30777
|
var publicProcedure = t.procedure.use(securityContext).use(authGate2).use(adminGate).use(scopeGate).use(ownerGate).use(switchRefusal).use(unsafePathRefusal);
|
|
30408
|
-
var hubLocalProcedure = publicProcedure.use(({ next, path:
|
|
30409
|
-
assertHubLocalExecEnabled({ op:
|
|
30778
|
+
var hubLocalProcedure = publicProcedure.use(({ next, path: path11 }) => {
|
|
30779
|
+
assertHubLocalExecEnabled({ op: path11 });
|
|
30410
30780
|
return next();
|
|
30411
30781
|
});
|
|
30412
30782
|
var openProcedure = t.procedure.use(securityContext);
|
|
@@ -30496,8 +30866,8 @@ var fileEditorRouter = router({
|
|
|
30496
30866
|
* should only offer this when the workspace is co-resident — the caller decides.
|
|
30497
30867
|
*/
|
|
30498
30868
|
showInFinder: publicProcedure.input(external_exports.object({ rootPath: external_exports.string(), targetPath: external_exports.string() })).mutation(async ({ input: input2 }) => {
|
|
30499
|
-
const
|
|
30500
|
-
const abs = input2.targetPath ?
|
|
30869
|
+
const path11 = await import("node:path");
|
|
30870
|
+
const abs = input2.targetPath ? path11.resolve(input2.rootPath, input2.targetPath) : path11.resolve(input2.rootPath);
|
|
30501
30871
|
getFileEditorRouterDeps().shellShowItemInFolder(abs);
|
|
30502
30872
|
}),
|
|
30503
30873
|
/**
|
|
@@ -30549,9 +30919,9 @@ var workspaceRouter = router({
|
|
|
30549
30919
|
* surfaced as such rather than rendered as an empty directory.
|
|
30550
30920
|
*/
|
|
30551
30921
|
listRoots: publicProcedure.input(targetInput).query(async ({ input: input2 }) => {
|
|
30552
|
-
const { fs:
|
|
30922
|
+
const { fs: fs8, resolveComputer, isLocalComputer } = getWorkspaceDeps();
|
|
30553
30923
|
const computerId = await resolveComputer(input2);
|
|
30554
|
-
return { computerId, isLocal: isLocalComputer(computerId), ...await
|
|
30924
|
+
return { computerId, isLocal: isLocalComputer(computerId), ...await fs8.listRoots(computerId) };
|
|
30555
30925
|
}),
|
|
30556
30926
|
listDir: publicProcedure.input(
|
|
30557
30927
|
targetInput.extend({
|
|
@@ -30561,18 +30931,18 @@ var workspaceRouter = router({
|
|
|
30561
30931
|
includeHidden: external_exports.boolean().optional()
|
|
30562
30932
|
})
|
|
30563
30933
|
).query(async ({ input: input2 }) => {
|
|
30564
|
-
const { fs:
|
|
30934
|
+
const { fs: fs8, resolveComputer } = getWorkspaceDeps();
|
|
30565
30935
|
const computerId = await resolveComputer(input2);
|
|
30566
|
-
const res = await
|
|
30936
|
+
const res = await fs8.listDir(computerId, input2.path, {
|
|
30567
30937
|
dirsOnly: input2.dirsOnly,
|
|
30568
30938
|
includeHidden: input2.includeHidden
|
|
30569
30939
|
});
|
|
30570
30940
|
return { computerId, ...res };
|
|
30571
30941
|
}),
|
|
30572
30942
|
mkdir: publicProcedure.input(targetInput.extend({ path: external_exports.string().min(1) })).mutation(async ({ input: input2 }) => {
|
|
30573
|
-
const { fs:
|
|
30943
|
+
const { fs: fs8, resolveComputer } = getWorkspaceDeps();
|
|
30574
30944
|
const computerId = await resolveComputer(input2);
|
|
30575
|
-
const res = await
|
|
30945
|
+
const res = await fs8.mkdir(computerId, input2.path);
|
|
30576
30946
|
return { computerId, ...res };
|
|
30577
30947
|
}),
|
|
30578
30948
|
/**
|
|
@@ -30588,9 +30958,9 @@ var workspaceRouter = router({
|
|
|
30588
30958
|
* from the enforced one.
|
|
30589
30959
|
*/
|
|
30590
30960
|
setAllowedRoots: publicProcedure.input(targetInput.extend({ roots: external_exports.array(external_exports.string().min(1)) })).mutation(async ({ input: input2 }) => {
|
|
30591
|
-
const { fs:
|
|
30961
|
+
const { fs: fs8, resolveComputer } = getWorkspaceDeps();
|
|
30592
30962
|
const computerId = await resolveComputer(input2);
|
|
30593
|
-
return { computerId, ...await
|
|
30963
|
+
return { computerId, ...await fs8.setAllowedRoots(computerId, input2.roots) };
|
|
30594
30964
|
}),
|
|
30595
30965
|
/**
|
|
30596
30966
|
* Where does the target machine keep this project?
|
|
@@ -30601,13 +30971,13 @@ var workspaceRouter = router({
|
|
|
30601
30971
|
* is different again: recorded, but the folder is gone.
|
|
30602
30972
|
*/
|
|
30603
30973
|
resolveProjectPath: publicProcedure.input(targetInput.extend({ projectId: external_exports.string().min(1) })).query(async ({ input: input2 }) => {
|
|
30604
|
-
const { fs:
|
|
30974
|
+
const { fs: fs8, resolveComputer } = getWorkspaceDeps();
|
|
30605
30975
|
const computerId = await resolveComputer(input2);
|
|
30606
30976
|
try {
|
|
30607
30977
|
return {
|
|
30608
30978
|
computerId,
|
|
30609
30979
|
reachable: true,
|
|
30610
|
-
...await
|
|
30980
|
+
...await fs8.resolveProjectPath(computerId, input2.projectId)
|
|
30611
30981
|
};
|
|
30612
30982
|
} catch (err) {
|
|
30613
30983
|
return {
|
|
@@ -30626,18 +30996,18 @@ var workspaceRouter = router({
|
|
|
30626
30996
|
path: external_exports.string().min(1)
|
|
30627
30997
|
})
|
|
30628
30998
|
).mutation(async ({ input: input2 }) => {
|
|
30629
|
-
const { fs:
|
|
30999
|
+
const { fs: fs8, resolveComputer } = getWorkspaceDeps();
|
|
30630
31000
|
const computerId = await resolveComputer(input2);
|
|
30631
31001
|
return {
|
|
30632
31002
|
computerId,
|
|
30633
|
-
...await
|
|
31003
|
+
...await fs8.setProjectPath(computerId, input2.projectId, input2.path)
|
|
30634
31004
|
};
|
|
30635
31005
|
}),
|
|
30636
31006
|
/** Drop the mapping on that machine. The checkout itself is left alone. */
|
|
30637
31007
|
forgetProjectPath: publicProcedure.input(targetInput.extend({ projectId: external_exports.string().min(1) })).mutation(async ({ input: input2 }) => {
|
|
30638
|
-
const { fs:
|
|
31008
|
+
const { fs: fs8, resolveComputer } = getWorkspaceDeps();
|
|
30639
31009
|
const computerId = await resolveComputer(input2);
|
|
30640
|
-
await
|
|
31010
|
+
await fs8.forgetProjectPath(computerId, input2.projectId);
|
|
30641
31011
|
return { computerId, ok: true };
|
|
30642
31012
|
}),
|
|
30643
31013
|
/**
|
|
@@ -30649,10 +31019,10 @@ var workspaceRouter = router({
|
|
|
30649
31019
|
* "No repository path configured" for a path that was fine.
|
|
30650
31020
|
*/
|
|
30651
31021
|
pathExists: publicProcedure.input(targetInput.extend({ path: external_exports.string().min(1) })).query(async ({ input: input2 }) => {
|
|
30652
|
-
const { fs:
|
|
31022
|
+
const { fs: fs8, resolveComputer } = getWorkspaceDeps();
|
|
30653
31023
|
const computerId = await resolveComputer(input2);
|
|
30654
31024
|
try {
|
|
30655
|
-
return { computerId, exists: await
|
|
31025
|
+
return { computerId, exists: await fs8.pathExists(computerId, input2.path), reachable: true };
|
|
30656
31026
|
} catch (err) {
|
|
30657
31027
|
return {
|
|
30658
31028
|
computerId,
|
|
@@ -30676,13 +31046,13 @@ function createFsHandlers(ctx) {
|
|
|
30676
31046
|
};
|
|
30677
31047
|
const watches = /* @__PURE__ */ new Map();
|
|
30678
31048
|
function pathExists(rawParams) {
|
|
30679
|
-
const { path:
|
|
30680
|
-
const resolved = allow(
|
|
31049
|
+
const { path: path11 } = fsPathExistsParamsSchema.parse(rawParams);
|
|
31050
|
+
const resolved = allow(path11);
|
|
30681
31051
|
return { exists: (0, import_node_fs7.existsSync)(resolved) };
|
|
30682
31052
|
}
|
|
30683
31053
|
async function removeDir(rawParams) {
|
|
30684
|
-
const { path:
|
|
30685
|
-
const resolved = allow(
|
|
31054
|
+
const { path: path11 } = fsRemoveDirParamsSchema.parse(rawParams);
|
|
31055
|
+
const resolved = allow(path11, true);
|
|
30686
31056
|
await (0, import_promises7.rm)(resolved, { recursive: true, force: true });
|
|
30687
31057
|
ctx.log("fs removeDir", { path: resolved });
|
|
30688
31058
|
return { ok: true };
|
|
@@ -30745,8 +31115,8 @@ function createFsHandlers(ctx) {
|
|
|
30745
31115
|
return { path: abs, parent: isRoot ? null : (0, import_node_path10.dirname)(abs), entries };
|
|
30746
31116
|
}
|
|
30747
31117
|
async function mkdirHandler(rawParams) {
|
|
30748
|
-
const { path:
|
|
30749
|
-
const abs = allow(
|
|
31118
|
+
const { path: path11 } = fsMkdirParamsSchema.parse(rawParams);
|
|
31119
|
+
const abs = allow(path11);
|
|
30750
31120
|
await (0, import_promises7.mkdir)(abs, { recursive: true });
|
|
30751
31121
|
ctx.log("fs mkdir", { path: abs });
|
|
30752
31122
|
return { path: import_node_fs7.realpathSync.native(abs) };
|
|
@@ -30906,11 +31276,132 @@ function createGhHandlers(ctx) {
|
|
|
30906
31276
|
return { [HubToComputerMethods.ghExec]: ghExec };
|
|
30907
31277
|
}
|
|
30908
31278
|
|
|
31279
|
+
// src/handlers/claude-session.ts
|
|
31280
|
+
var import_node_fs8 = require("node:fs");
|
|
31281
|
+
var import_node_os7 = require("node:os");
|
|
31282
|
+
var import_node_path11 = require("node:path");
|
|
31283
|
+
var import_node_readline = require("node:readline");
|
|
31284
|
+
var LARGE_RECORD_BYTES = 32 * 1024;
|
|
31285
|
+
var KEEP_TOOL_TEXT = 8 * 1024;
|
|
31286
|
+
function encodeProjectDir(cwd) {
|
|
31287
|
+
return cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
31288
|
+
}
|
|
31289
|
+
function claudeConfigDir() {
|
|
31290
|
+
return process.env.CLAUDE_CONFIG_DIR || (0, import_node_path11.join)((0, import_node_os7.homedir)(), ".claude");
|
|
31291
|
+
}
|
|
31292
|
+
function sessionFile(cwd, sessionId) {
|
|
31293
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
31294
|
+
try {
|
|
31295
|
+
candidates.add((0, import_node_fs8.realpathSync)(cwd));
|
|
31296
|
+
} catch {
|
|
31297
|
+
}
|
|
31298
|
+
candidates.add(cwd);
|
|
31299
|
+
for (const dir of candidates) {
|
|
31300
|
+
const file2 = (0, import_node_path11.join)(claudeConfigDir(), "projects", encodeProjectDir(dir), `${sessionId}.jsonl`);
|
|
31301
|
+
if ((0, import_node_fs8.existsSync)(file2)) return file2;
|
|
31302
|
+
}
|
|
31303
|
+
return null;
|
|
31304
|
+
}
|
|
31305
|
+
async function* records(file2) {
|
|
31306
|
+
const input2 = (0, import_node_fs8.createReadStream)(file2, { encoding: "utf8" });
|
|
31307
|
+
const rl = (0, import_node_readline.createInterface)({ input: input2, crlfDelay: Infinity });
|
|
31308
|
+
try {
|
|
31309
|
+
for await (const line of rl) {
|
|
31310
|
+
const trimmed = line.trim();
|
|
31311
|
+
if (!trimmed) continue;
|
|
31312
|
+
try {
|
|
31313
|
+
const rec = JSON.parse(trimmed);
|
|
31314
|
+
if (rec && typeof rec === "object" && !Array.isArray(rec)) {
|
|
31315
|
+
yield rec;
|
|
31316
|
+
}
|
|
31317
|
+
} catch {
|
|
31318
|
+
}
|
|
31319
|
+
}
|
|
31320
|
+
} finally {
|
|
31321
|
+
rl.close();
|
|
31322
|
+
input2.destroy();
|
|
31323
|
+
}
|
|
31324
|
+
}
|
|
31325
|
+
function isContent(rec) {
|
|
31326
|
+
if (rec.isSidechain === true) return false;
|
|
31327
|
+
if (rec.type === "user" || rec.type === "assistant") return true;
|
|
31328
|
+
return rec.type === "system" && rec.subtype === "compact_boundary";
|
|
31329
|
+
}
|
|
31330
|
+
function shortened(rec) {
|
|
31331
|
+
if (JSON.stringify(rec).length <= LARGE_RECORD_BYTES) return rec;
|
|
31332
|
+
const message = rec.message;
|
|
31333
|
+
if (!message || !Array.isArray(message.content)) return rec;
|
|
31334
|
+
const cut = (text) => text.length > KEEP_TOOL_TEXT ? `${text.slice(0, KEEP_TOOL_TEXT)}
|
|
31335
|
+
\u2026 (shortened)` : text;
|
|
31336
|
+
const content = message.content.map((block) => {
|
|
31337
|
+
const b = block;
|
|
31338
|
+
if (b?.type !== "tool_result") return block;
|
|
31339
|
+
if (typeof b.content === "string") return { ...b, content: cut(b.content) };
|
|
31340
|
+
if (Array.isArray(b.content)) {
|
|
31341
|
+
return {
|
|
31342
|
+
...b,
|
|
31343
|
+
content: b.content.map((part) => {
|
|
31344
|
+
const p = part;
|
|
31345
|
+
return p?.type === "text" && typeof p.text === "string" ? { ...p, text: cut(p.text) } : part;
|
|
31346
|
+
})
|
|
31347
|
+
};
|
|
31348
|
+
}
|
|
31349
|
+
return block;
|
|
31350
|
+
});
|
|
31351
|
+
const { toolUseResult: _structured, ...rest } = rec;
|
|
31352
|
+
void _structured;
|
|
31353
|
+
return { ...rest, message: { ...message, content } };
|
|
31354
|
+
}
|
|
31355
|
+
function createClaudeSessionHandlers(ctx, getProjectPaths) {
|
|
31356
|
+
async function read(rawParams) {
|
|
31357
|
+
const params = claudeSessionReadParamsSchema.parse(rawParams);
|
|
31358
|
+
const resolved = params.cwd ?? (params.projectId ? getProjectPaths()?.get(params.projectId)?.path : void 0);
|
|
31359
|
+
if (!resolved) throw new Error("claudeSession.read: no cwd for this session");
|
|
31360
|
+
const cwd = assertPathAllowed(resolved, ctx.config.allowedRoots);
|
|
31361
|
+
const file2 = sessionFile(cwd, params.sessionId);
|
|
31362
|
+
if (!file2) return { found: false, records: [], omitted: 0 };
|
|
31363
|
+
const links = /* @__PURE__ */ new Map();
|
|
31364
|
+
let leaf = null;
|
|
31365
|
+
for await (const rec of records(file2)) {
|
|
31366
|
+
if (typeof rec.uuid !== "string") continue;
|
|
31367
|
+
const parent = typeof rec.parentUuid === "string" ? rec.parentUuid : typeof rec.logicalParentUuid === "string" ? rec.logicalParentUuid : null;
|
|
31368
|
+
const content = isContent(rec);
|
|
31369
|
+
links.set(rec.uuid, { parent, content });
|
|
31370
|
+
if (content && rec.type !== "system") leaf = rec.uuid;
|
|
31371
|
+
}
|
|
31372
|
+
const branch = [];
|
|
31373
|
+
const seen = /* @__PURE__ */ new Set();
|
|
31374
|
+
for (let at = leaf; at && !seen.has(at); at = links.get(at)?.parent ?? null) {
|
|
31375
|
+
seen.add(at);
|
|
31376
|
+
if (links.get(at)?.content) branch.push(at);
|
|
31377
|
+
}
|
|
31378
|
+
branch.reverse();
|
|
31379
|
+
const wanted = new Set(branch.slice(-params.maxRecords));
|
|
31380
|
+
const byUuid = /* @__PURE__ */ new Map();
|
|
31381
|
+
for await (const rec of records(file2)) {
|
|
31382
|
+
if (typeof rec.uuid === "string" && wanted.has(rec.uuid)) byUuid.set(rec.uuid, shortened(rec));
|
|
31383
|
+
}
|
|
31384
|
+
const picked = [];
|
|
31385
|
+
let bytes = 0;
|
|
31386
|
+
for (let i = branch.length - 1; i >= 0 && picked.length < params.maxRecords; i--) {
|
|
31387
|
+
const rec = byUuid.get(branch[i]);
|
|
31388
|
+
if (!rec) continue;
|
|
31389
|
+
const size = JSON.stringify(rec).length;
|
|
31390
|
+
if (bytes + size > params.maxBytes) break;
|
|
31391
|
+
bytes += size;
|
|
31392
|
+
picked.push(rec);
|
|
31393
|
+
}
|
|
31394
|
+
picked.reverse();
|
|
31395
|
+
return { found: true, records: picked, omitted: branch.length - picked.length };
|
|
31396
|
+
}
|
|
31397
|
+
return { [HubToComputerMethods.claudeSessionRead]: read };
|
|
31398
|
+
}
|
|
31399
|
+
|
|
30909
31400
|
// src/handlers/git.ts
|
|
30910
31401
|
var import_node_child_process7 = require("node:child_process");
|
|
30911
|
-
var
|
|
31402
|
+
var import_node_fs9 = require("node:fs");
|
|
30912
31403
|
var import_promises8 = require("node:fs/promises");
|
|
30913
|
-
var
|
|
31404
|
+
var import_node_path12 = require("node:path");
|
|
30914
31405
|
|
|
30915
31406
|
// src/handlers/exec.ts
|
|
30916
31407
|
var import_node_child_process6 = require("node:child_process");
|
|
@@ -30968,9 +31459,9 @@ var SETUP_SCRIPT_TIMEOUT_MS = 5 * 6e4;
|
|
|
30968
31459
|
function createGitHandlers(ctx) {
|
|
30969
31460
|
const allow = (candidate) => assertPathAllowed(candidate, ctx.config.allowedRoots);
|
|
30970
31461
|
async function isGitRepo(rawParams) {
|
|
30971
|
-
const { path:
|
|
30972
|
-
const repoPath = allow(
|
|
30973
|
-
if (!(0,
|
|
31462
|
+
const { path: path11 } = isGitRepoParams.parse(rawParams);
|
|
31463
|
+
const repoPath = allow(path11);
|
|
31464
|
+
if (!(0, import_node_fs9.existsSync)(repoPath)) return { isGitRepo: false };
|
|
30974
31465
|
const result = await execCapture("git", ["rev-parse", "--git-dir"], { cwd: repoPath });
|
|
30975
31466
|
if (result.spawnError) throw new Error(`Could not start git: ${result.spawnError}`);
|
|
30976
31467
|
return { isGitRepo: result.status === 0 };
|
|
@@ -31003,7 +31494,7 @@ function createGitHandlers(ctx) {
|
|
|
31003
31494
|
try {
|
|
31004
31495
|
await execGit(["worktree", "remove", worktreePath, "--force"], repoPath);
|
|
31005
31496
|
} catch (err) {
|
|
31006
|
-
if (!(0,
|
|
31497
|
+
if (!(0, import_node_fs9.existsSync)(worktreePath)) {
|
|
31007
31498
|
await execGit(["worktree", "prune"], repoPath);
|
|
31008
31499
|
} else {
|
|
31009
31500
|
throw err;
|
|
@@ -31031,13 +31522,13 @@ function createGitHandlers(ctx) {
|
|
|
31031
31522
|
const params = runWorktreeSetupScriptParams.parse(rawParams);
|
|
31032
31523
|
const worktreePath = allow(params.worktreePath);
|
|
31033
31524
|
const repoPath = allow(params.repoPath);
|
|
31034
|
-
const scriptPath = (0,
|
|
31035
|
-
if (!(0,
|
|
31525
|
+
const scriptPath = (0, import_node_path12.resolve)(worktreePath, SETUP_SCRIPT);
|
|
31526
|
+
if (!(0, import_node_fs9.existsSync)(scriptPath)) return Promise.resolve({ ran: false });
|
|
31036
31527
|
try {
|
|
31037
|
-
(0,
|
|
31528
|
+
(0, import_node_fs9.accessSync)(scriptPath, import_node_fs9.constants.X_OK);
|
|
31038
31529
|
} catch {
|
|
31039
31530
|
try {
|
|
31040
|
-
(0,
|
|
31531
|
+
(0, import_node_fs9.chmodSync)(scriptPath, 493);
|
|
31041
31532
|
} catch {
|
|
31042
31533
|
return Promise.resolve({ ran: false });
|
|
31043
31534
|
}
|
|
@@ -31088,19 +31579,19 @@ function createGitHandlers(ctx) {
|
|
|
31088
31579
|
} else {
|
|
31089
31580
|
relPaths = params.customPaths ?? [];
|
|
31090
31581
|
}
|
|
31091
|
-
const repoRoot = (0,
|
|
31092
|
-
const wtRoot = (0,
|
|
31582
|
+
const repoRoot = (0, import_node_path12.resolve)(repoPath);
|
|
31583
|
+
const wtRoot = (0, import_node_path12.resolve)(worktreePath);
|
|
31093
31584
|
let copied = 0;
|
|
31094
31585
|
for (const rel of relPaths) {
|
|
31095
|
-
const src = (0,
|
|
31096
|
-
const dest = (0,
|
|
31586
|
+
const src = (0, import_node_path12.resolve)(repoPath, rel);
|
|
31587
|
+
const dest = (0, import_node_path12.resolve)(worktreePath, rel);
|
|
31097
31588
|
if (!isInside2(src, repoRoot) || !isInside2(dest, wtRoot)) {
|
|
31098
31589
|
ctx.log("copyIgnoredFiles skipped traversal", { rel });
|
|
31099
31590
|
continue;
|
|
31100
31591
|
}
|
|
31101
|
-
if (!(0,
|
|
31592
|
+
if (!(0, import_node_fs9.existsSync)(src)) continue;
|
|
31102
31593
|
try {
|
|
31103
|
-
await (0, import_promises8.mkdir)((0,
|
|
31594
|
+
await (0, import_promises8.mkdir)((0, import_node_path12.dirname)(dest), { recursive: true });
|
|
31104
31595
|
await (0, import_promises8.cp)(src, dest, { recursive: true, verbatimSymlinks: true });
|
|
31105
31596
|
copied += 1;
|
|
31106
31597
|
} catch (err) {
|
|
@@ -31119,12 +31610,13 @@ function createGitHandlers(ctx) {
|
|
|
31119
31610
|
};
|
|
31120
31611
|
}
|
|
31121
31612
|
function isInside2(child, root) {
|
|
31122
|
-
const c = (0,
|
|
31123
|
-
return c === root || c.startsWith(root +
|
|
31613
|
+
const c = (0, import_node_path12.resolve)(child);
|
|
31614
|
+
return c === root || c.startsWith(root + import_node_path12.sep);
|
|
31124
31615
|
}
|
|
31125
31616
|
|
|
31126
31617
|
// src/handlers/proc.ts
|
|
31127
31618
|
var import_node_child_process8 = require("node:child_process");
|
|
31619
|
+
var import_node_string_decoder = require("node:string_decoder");
|
|
31128
31620
|
|
|
31129
31621
|
// src/exit-tombstones.ts
|
|
31130
31622
|
var TOMBSTONE_TTL_MS = 10 * 6e4;
|
|
@@ -31223,107 +31715,375 @@ var RingBuffer = class {
|
|
|
31223
31715
|
}
|
|
31224
31716
|
};
|
|
31225
31717
|
|
|
31226
|
-
// src/
|
|
31227
|
-
|
|
31228
|
-
|
|
31229
|
-
|
|
31230
|
-
|
|
31231
|
-
|
|
31232
|
-
}
|
|
31233
|
-
if (ctx.hubAddress) merged.SLAYZONE_HUB_ADDRESS = ctx.hubAddress;
|
|
31234
|
-
return merged;
|
|
31718
|
+
// src/spawn-admission.ts
|
|
31719
|
+
var import_node_os8 = require("node:os");
|
|
31720
|
+
var DEFAULT_START_CAP_MS = 5e3;
|
|
31721
|
+
var DEFAULT_WARM_QUIET_MS = 3e3;
|
|
31722
|
+
function defaultAdmissionConcurrency(cores = (0, import_node_os8.availableParallelism)()) {
|
|
31723
|
+
return Math.max(2, Math.min(4, Math.floor(cores / 2)));
|
|
31235
31724
|
}
|
|
31236
|
-
|
|
31237
|
-
|
|
31238
|
-
|
|
31239
|
-
|
|
31240
|
-
|
|
31241
|
-
|
|
31242
|
-
|
|
31243
|
-
|
|
31244
|
-
|
|
31245
|
-
|
|
31246
|
-
|
|
31247
|
-
|
|
31248
|
-
|
|
31249
|
-
|
|
31250
|
-
|
|
31251
|
-
|
|
31252
|
-
|
|
31253
|
-
|
|
31254
|
-
|
|
31255
|
-
|
|
31256
|
-
|
|
31257
|
-
|
|
31258
|
-
|
|
31259
|
-
|
|
31260
|
-
|
|
31261
|
-
|
|
31262
|
-
|
|
31263
|
-
|
|
31264
|
-
);
|
|
31725
|
+
var LANE_RANK = { foreground: 0, background: 1, warm: 2 };
|
|
31726
|
+
function createSpawnAdmission(opts = {}) {
|
|
31727
|
+
const concurrency = Math.max(1, opts.concurrency ?? defaultAdmissionConcurrency());
|
|
31728
|
+
const startCapMs = opts.startCapMs ?? DEFAULT_START_CAP_MS;
|
|
31729
|
+
const warmQuietMs = opts.warmQuietMs ?? DEFAULT_WARM_QUIET_MS;
|
|
31730
|
+
const now = opts.now ?? Date.now;
|
|
31731
|
+
const setTimer = opts.setTimer ?? ((fn, ms) => {
|
|
31732
|
+
const t2 = setTimeout(fn, ms);
|
|
31733
|
+
t2.unref?.();
|
|
31734
|
+
return t2;
|
|
31735
|
+
});
|
|
31736
|
+
const clearTimer = opts.clearTimer ?? ((h) => clearTimeout(h));
|
|
31737
|
+
const queue = [];
|
|
31738
|
+
const active = /* @__PURE__ */ new Set();
|
|
31739
|
+
let arrivals = 0;
|
|
31740
|
+
let promotions = 0;
|
|
31741
|
+
let lastAgentActivity = Number.NEGATIVE_INFINITY;
|
|
31742
|
+
let warmRetry = null;
|
|
31743
|
+
let pumping = false;
|
|
31744
|
+
let pumpAgain = false;
|
|
31745
|
+
const activeAgents = () => [...active].filter((s) => !s.warm).length;
|
|
31746
|
+
const activeWarm = () => [...active].filter((s) => s.warm).length;
|
|
31747
|
+
function next() {
|
|
31748
|
+
if (active.size >= concurrency || queue.length === 0) return null;
|
|
31749
|
+
let best = null;
|
|
31750
|
+
for (const t2 of queue) {
|
|
31751
|
+
if (!best || LANE_RANK[t2.lane] < LANE_RANK[best.lane] || t2.lane === best.lane && t2.order < best.order) {
|
|
31752
|
+
best = t2;
|
|
31265
31753
|
}
|
|
31266
31754
|
}
|
|
31267
|
-
|
|
31268
|
-
|
|
31269
|
-
const
|
|
31270
|
-
if (
|
|
31271
|
-
|
|
31272
|
-
|
|
31755
|
+
if (!best || best.lane !== "warm") return best;
|
|
31756
|
+
if (activeAgents() > 0 || activeWarm() > 0) return null;
|
|
31757
|
+
const quietLeft = lastAgentActivity + warmQuietMs - now();
|
|
31758
|
+
if (quietLeft > 0) {
|
|
31759
|
+
if (warmRetry === null) {
|
|
31760
|
+
warmRetry = setTimer(() => {
|
|
31761
|
+
warmRetry = null;
|
|
31762
|
+
pump();
|
|
31763
|
+
}, quietLeft);
|
|
31764
|
+
}
|
|
31765
|
+
return null;
|
|
31273
31766
|
}
|
|
31274
|
-
|
|
31275
|
-
|
|
31276
|
-
|
|
31277
|
-
|
|
31278
|
-
|
|
31279
|
-
|
|
31280
|
-
|
|
31281
|
-
|
|
31282
|
-
|
|
31283
|
-
|
|
31284
|
-
|
|
31285
|
-
|
|
31286
|
-
|
|
31287
|
-
buffer,
|
|
31288
|
-
...params.spawnId !== void 0 ? { spawnId: params.spawnId } : {}
|
|
31767
|
+
return best;
|
|
31768
|
+
}
|
|
31769
|
+
function admit(ticket) {
|
|
31770
|
+
queue.splice(queue.indexOf(ticket), 1);
|
|
31771
|
+
const warm = ticket.lane === "warm";
|
|
31772
|
+
const held = { warm, released: false, cap: null };
|
|
31773
|
+
const release = () => {
|
|
31774
|
+
if (held.released) return;
|
|
31775
|
+
held.released = true;
|
|
31776
|
+
clearTimer(held.cap);
|
|
31777
|
+
active.delete(held);
|
|
31778
|
+
if (!warm) lastAgentActivity = now();
|
|
31779
|
+
pump();
|
|
31289
31780
|
};
|
|
31290
|
-
|
|
31291
|
-
|
|
31292
|
-
|
|
31293
|
-
|
|
31294
|
-
|
|
31295
|
-
|
|
31296
|
-
|
|
31297
|
-
|
|
31298
|
-
|
|
31299
|
-
|
|
31300
|
-
|
|
31301
|
-
|
|
31302
|
-
|
|
31303
|
-
|
|
31304
|
-
|
|
31305
|
-
|
|
31781
|
+
held.cap = setTimer(release, startCapMs);
|
|
31782
|
+
active.add(held);
|
|
31783
|
+
try {
|
|
31784
|
+
ticket.start({ release });
|
|
31785
|
+
} catch {
|
|
31786
|
+
release();
|
|
31787
|
+
}
|
|
31788
|
+
}
|
|
31789
|
+
function pump() {
|
|
31790
|
+
if (pumping) {
|
|
31791
|
+
pumpAgain = true;
|
|
31792
|
+
return;
|
|
31793
|
+
}
|
|
31794
|
+
pumping = true;
|
|
31795
|
+
try {
|
|
31796
|
+
do {
|
|
31797
|
+
pumpAgain = false;
|
|
31798
|
+
for (let t2 = next(); t2; t2 = next()) admit(t2);
|
|
31799
|
+
} while (pumpAgain);
|
|
31800
|
+
} finally {
|
|
31801
|
+
pumping = false;
|
|
31802
|
+
}
|
|
31803
|
+
}
|
|
31804
|
+
return {
|
|
31805
|
+
enqueue(lane, start) {
|
|
31806
|
+
const ticket = { lane, order: ++arrivals, start };
|
|
31807
|
+
queue.push(ticket);
|
|
31808
|
+
if (lane !== "warm") lastAgentActivity = now();
|
|
31809
|
+
pump();
|
|
31810
|
+
return ticket;
|
|
31811
|
+
},
|
|
31812
|
+
cancel(ticket) {
|
|
31813
|
+
const i = queue.indexOf(ticket);
|
|
31814
|
+
if (i < 0) return false;
|
|
31815
|
+
queue.splice(i, 1);
|
|
31816
|
+
pump();
|
|
31817
|
+
return true;
|
|
31818
|
+
},
|
|
31819
|
+
prioritize(ticket) {
|
|
31820
|
+
const t2 = ticket;
|
|
31821
|
+
if (!queue.includes(t2)) return false;
|
|
31822
|
+
t2.lane = "foreground";
|
|
31823
|
+
t2.order = -++promotions;
|
|
31824
|
+
pump();
|
|
31825
|
+
return true;
|
|
31826
|
+
},
|
|
31827
|
+
isQueued(ticket) {
|
|
31828
|
+
return queue.includes(ticket);
|
|
31829
|
+
},
|
|
31830
|
+
stats() {
|
|
31831
|
+
return { queued: queue.length, active: active.size, concurrency };
|
|
31832
|
+
}
|
|
31833
|
+
};
|
|
31834
|
+
}
|
|
31835
|
+
|
|
31836
|
+
// src/handlers/resolve-command.ts
|
|
31837
|
+
var import_node_fs10 = require("node:fs");
|
|
31838
|
+
var import_node_path13 = require("node:path");
|
|
31839
|
+
function isExecutableFile(file2) {
|
|
31840
|
+
try {
|
|
31841
|
+
if (!(0, import_node_fs10.statSync)(file2).isFile()) return false;
|
|
31842
|
+
if (process.platform !== "win32") (0, import_node_fs10.accessSync)(file2, import_node_fs10.constants.X_OK);
|
|
31843
|
+
return true;
|
|
31844
|
+
} catch {
|
|
31845
|
+
return false;
|
|
31846
|
+
}
|
|
31847
|
+
}
|
|
31848
|
+
function resolveCommand(command, env, cwd) {
|
|
31849
|
+
const suffixes = process.platform === "win32" ? ["", ...(env.PATHEXT ?? process.env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";")] : [""];
|
|
31850
|
+
const hasDir = command.includes("/") || process.platform === "win32" && command.includes("\\");
|
|
31851
|
+
if (hasDir) {
|
|
31852
|
+
const base = (0, import_node_path13.isAbsolute)(command) ? command : (0, import_node_path13.join)(cwd ?? process.cwd(), command);
|
|
31853
|
+
return suffixes.map((s) => base + s).find(isExecutableFile) ?? null;
|
|
31854
|
+
}
|
|
31855
|
+
for (const dir of (env.PATH ?? env.Path ?? "").split(import_node_path13.delimiter)) {
|
|
31856
|
+
if (!dir) continue;
|
|
31857
|
+
for (const suffix of suffixes) {
|
|
31858
|
+
const candidate = (0, import_node_path13.join)(dir, command + suffix);
|
|
31859
|
+
if (isExecutableFile(candidate)) return candidate;
|
|
31860
|
+
}
|
|
31861
|
+
}
|
|
31862
|
+
return null;
|
|
31863
|
+
}
|
|
31864
|
+
|
|
31865
|
+
// src/handlers/spawn-env.ts
|
|
31866
|
+
var import_node_os9 = require("node:os");
|
|
31867
|
+
function buildSpawnEnv(ctx, overrides, capability) {
|
|
31868
|
+
const merged = overrides ? { ...sanitizeSpawnEnv(process.env), ...overrides } : sanitizeSpawnEnv(process.env);
|
|
31869
|
+
if (ctx.agentHookUrl) {
|
|
31870
|
+
merged.SLAYZONE_AGENT_HOOK_URL = ctx.agentHookUrl;
|
|
31871
|
+
delete merged.SLAYZONE_HUB_TOKEN;
|
|
31872
|
+
}
|
|
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
|
+
}
|
|
31881
|
+
if (ctx.config.binOverrideDir) {
|
|
31882
|
+
merged.PATH = mergePathLists(ctx.config.binOverrideDir, merged.PATH);
|
|
31883
|
+
}
|
|
31884
|
+
return merged;
|
|
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
|
+
}
|
|
31897
|
+
|
|
31898
|
+
// src/handlers/proc.ts
|
|
31899
|
+
var MAX_BUFFER_SIZE = 750 * 1024;
|
|
31900
|
+
var ProcMethods = {
|
|
31901
|
+
procSpawn: HubToComputerMethods.procSpawn,
|
|
31902
|
+
procKill: HubToComputerMethods.procKill,
|
|
31903
|
+
procWrite: HubToComputerMethods.procWrite,
|
|
31904
|
+
procGetBufferSince: HubToComputerMethods.procGetBufferSince,
|
|
31905
|
+
procList: HubToComputerMethods.procList
|
|
31906
|
+
};
|
|
31907
|
+
var ProcNotifications = {
|
|
31908
|
+
procData: ComputerNotificationMethods.procData,
|
|
31909
|
+
procExit: ComputerNotificationMethods.procExit
|
|
31910
|
+
};
|
|
31911
|
+
var WRITE_ID_MEMORY = 256;
|
|
31912
|
+
function refuse2(reason, message, data = {}) {
|
|
31913
|
+
return new RpcError(ComputerTransportErrorCodes.refusedByComputer, message, {
|
|
31914
|
+
reason,
|
|
31915
|
+
...data
|
|
31916
|
+
});
|
|
31917
|
+
}
|
|
31918
|
+
function createProcHandlers(ctx, getProjectPaths, options = {}) {
|
|
31919
|
+
const procs = /* @__PURE__ */ new Map();
|
|
31920
|
+
const tombstones = new ExitTombstones();
|
|
31921
|
+
const admission = options.admission ?? createSpawnAdmission();
|
|
31922
|
+
const queued = /* @__PURE__ */ new Map();
|
|
31923
|
+
function killTree(child, signal = "SIGTERM") {
|
|
31924
|
+
if (typeof child.pid === "number") killPidTree(child.pid, signal);
|
|
31925
|
+
}
|
|
31926
|
+
function releaseSlot(session) {
|
|
31927
|
+
const slot = session.slot;
|
|
31928
|
+
session.slot = null;
|
|
31929
|
+
slot?.release();
|
|
31930
|
+
}
|
|
31931
|
+
function procSpawn(rawParams) {
|
|
31932
|
+
const params = procSpawnParamsSchema.parse(rawParams);
|
|
31933
|
+
if (!params.admission) return startProc(params, null);
|
|
31934
|
+
const prior = queued.get(params.sessionId);
|
|
31935
|
+
if (prior && params.spawnId !== void 0 && prior.spawnId === params.spawnId) {
|
|
31936
|
+
return prior.promise;
|
|
31937
|
+
}
|
|
31938
|
+
let resolve9;
|
|
31939
|
+
let reject;
|
|
31940
|
+
const promise2 = new Promise((res, rej) => {
|
|
31941
|
+
resolve9 = res;
|
|
31942
|
+
reject = rej;
|
|
31943
|
+
});
|
|
31944
|
+
const item = {
|
|
31945
|
+
...params.spawnId !== void 0 ? { spawnId: params.spawnId } : {},
|
|
31946
|
+
ticket: void 0,
|
|
31947
|
+
promise: promise2,
|
|
31948
|
+
resolve: resolve9,
|
|
31949
|
+
reject
|
|
31306
31950
|
};
|
|
31307
|
-
|
|
31951
|
+
if (prior) {
|
|
31952
|
+
admission.cancel(prior.ticket);
|
|
31953
|
+
prior.resolve(promise2);
|
|
31954
|
+
}
|
|
31955
|
+
queued.set(params.sessionId, item);
|
|
31956
|
+
item.ticket = admission.enqueue(params.admission, (slot) => {
|
|
31957
|
+
if (queued.get(params.sessionId) !== item) {
|
|
31958
|
+
slot.release();
|
|
31959
|
+
return;
|
|
31960
|
+
}
|
|
31961
|
+
queued.delete(params.sessionId);
|
|
31962
|
+
try {
|
|
31963
|
+
item.resolve(startProc(params, slot));
|
|
31964
|
+
} catch (err) {
|
|
31965
|
+
slot.release();
|
|
31966
|
+
item.reject(err);
|
|
31967
|
+
}
|
|
31968
|
+
});
|
|
31969
|
+
return promise2;
|
|
31970
|
+
}
|
|
31971
|
+
function startProc(params, slot) {
|
|
31972
|
+
if (params.spawnId !== void 0) {
|
|
31973
|
+
const live = procs.get(params.sessionId);
|
|
31974
|
+
if (live && live.spawnId === params.spawnId) return { pid: live.child.pid ?? null };
|
|
31975
|
+
if (tombstones.ran(params.sessionId, params.spawnId)) {
|
|
31976
|
+
throw new Error(
|
|
31977
|
+
`proc.spawn ${params.sessionId}: attempt ${params.spawnId} already ran and exited \u2014 not starting it twice`
|
|
31978
|
+
);
|
|
31979
|
+
}
|
|
31980
|
+
}
|
|
31981
|
+
let resolved = params.cwd || void 0;
|
|
31982
|
+
if (!resolved && params.projectId) {
|
|
31983
|
+
resolved = getProjectPaths?.()?.get(params.projectId)?.path;
|
|
31984
|
+
if (!resolved) throw projectNotPlacedError(params.projectId);
|
|
31985
|
+
}
|
|
31986
|
+
const cwd = spawnCwd(resolved, ctx.config.allowedRoots);
|
|
31987
|
+
const existing = procs.get(params.sessionId);
|
|
31988
|
+
if (existing) {
|
|
31989
|
+
existing.revokeCli?.();
|
|
31990
|
+
killTree(existing.child);
|
|
31991
|
+
procs.delete(params.sessionId);
|
|
31992
|
+
}
|
|
31993
|
+
const capability = ctx.cliRelay?.issue();
|
|
31994
|
+
const env = buildSpawnEnv(ctx, params.env, capability);
|
|
31995
|
+
if (!params.shell && resolveCommand(params.command, env, cwd) === null) {
|
|
31996
|
+
capability?.revoke();
|
|
31997
|
+
throw refuse2(
|
|
31998
|
+
ProcRefusalReasons.binaryNotFound,
|
|
31999
|
+
`${params.command} was not found on this computer's PATH`,
|
|
32000
|
+
{ binary: params.command }
|
|
32001
|
+
);
|
|
32002
|
+
}
|
|
32003
|
+
const invocation = params.shell ? buildShellInvocation(params.command) : { file: params.command, args: params.args ?? [] };
|
|
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
|
+
}
|
|
32019
|
+
const buffer = new RingBuffer(MAX_BUFFER_SIZE);
|
|
32020
|
+
const session = {
|
|
32021
|
+
revokeCli: capability?.revoke,
|
|
32022
|
+
child,
|
|
32023
|
+
buffer,
|
|
32024
|
+
...params.spawnId !== void 0 ? { spawnId: params.spawnId } : {},
|
|
32025
|
+
stdinBroken: null,
|
|
32026
|
+
writeIds: [],
|
|
32027
|
+
slot
|
|
32028
|
+
};
|
|
32029
|
+
procs.set(params.sessionId, session);
|
|
32030
|
+
const tag = params.spawnId !== void 0 ? { spawnId: params.spawnId } : {};
|
|
32031
|
+
child.stdin?.on("error", (err) => {
|
|
32032
|
+
session.stdinBroken = err.message;
|
|
32033
|
+
ctx.log("proc stdin error", { sessionId: params.sessionId, error: err.message });
|
|
32034
|
+
});
|
|
32035
|
+
const settle2 = (payload) => {
|
|
32036
|
+
capability?.revoke();
|
|
32037
|
+
releaseSlot(session);
|
|
31308
32038
|
if (procs.get(params.sessionId) !== session) return;
|
|
31309
|
-
|
|
32039
|
+
procs.delete(params.sessionId);
|
|
32040
|
+
const delivered = ctx.dialer.notify(ProcNotifications.procExit, {
|
|
32041
|
+
sessionId: params.sessionId,
|
|
32042
|
+
...tag,
|
|
32043
|
+
...payload
|
|
32044
|
+
});
|
|
32045
|
+
tombstones.add(params.sessionId, {
|
|
32046
|
+
spawnId: session.spawnId,
|
|
32047
|
+
...typeof child.pid === "number" ? { pid: child.pid } : {},
|
|
32048
|
+
exitCode: payload.exitCode,
|
|
32049
|
+
signal: payload.signal,
|
|
32050
|
+
// The hub never got this exit, so it may be missing stdout too.
|
|
32051
|
+
...delivered ? {} : { buffer: session.buffer }
|
|
32052
|
+
});
|
|
32053
|
+
};
|
|
32054
|
+
const stdoutDecoder = new import_node_string_decoder.StringDecoder("utf8");
|
|
32055
|
+
const stderrDecoder = new import_node_string_decoder.StringDecoder("utf8");
|
|
32056
|
+
const emitStdout = (data) => {
|
|
32057
|
+
if (!data || procs.get(params.sessionId) !== session) return;
|
|
31310
32058
|
const seq = buffer.append(data);
|
|
31311
32059
|
ctx.dialer.notify(ProcNotifications.procData, {
|
|
31312
32060
|
sessionId: params.sessionId,
|
|
32061
|
+
...tag,
|
|
31313
32062
|
seq,
|
|
31314
32063
|
stream: "stdout",
|
|
31315
32064
|
data
|
|
31316
32065
|
});
|
|
31317
|
-
}
|
|
31318
|
-
|
|
31319
|
-
if (procs.get(params.sessionId) !== session) return;
|
|
32066
|
+
};
|
|
32067
|
+
const emitStderr = (data) => {
|
|
32068
|
+
if (!data || procs.get(params.sessionId) !== session) return;
|
|
31320
32069
|
ctx.dialer.notify(ProcNotifications.procData, {
|
|
31321
32070
|
sessionId: params.sessionId,
|
|
32071
|
+
...tag,
|
|
31322
32072
|
stream: "stderr",
|
|
31323
|
-
data
|
|
32073
|
+
data
|
|
31324
32074
|
});
|
|
32075
|
+
};
|
|
32076
|
+
child.stdout?.on("data", (d) => {
|
|
32077
|
+
releaseSlot(session);
|
|
32078
|
+
emitStdout(stdoutDecoder.write(d));
|
|
32079
|
+
});
|
|
32080
|
+
child.stderr?.on("data", (d) => {
|
|
32081
|
+
releaseSlot(session);
|
|
32082
|
+
emitStderr(stderrDecoder.write(d));
|
|
31325
32083
|
});
|
|
31326
32084
|
child.on("close", (code, signal) => {
|
|
32085
|
+
emitStdout(stdoutDecoder.end());
|
|
32086
|
+
emitStderr(stderrDecoder.end());
|
|
31327
32087
|
settle2({
|
|
31328
32088
|
exitCode: typeof code === "number" ? code : null,
|
|
31329
32089
|
signal: signal != null ? String(signal) : null
|
|
@@ -31346,6 +32106,10 @@ function createProcHandlers(ctx, getProjectPaths) {
|
|
|
31346
32106
|
ctx.log("proc kill on unknown session", { sessionId: params.sessionId });
|
|
31347
32107
|
return { ok: true };
|
|
31348
32108
|
}
|
|
32109
|
+
if (params.spawnId !== void 0 && session.spawnId !== params.spawnId) {
|
|
32110
|
+
ctx.log("proc kill for a replaced attempt", { sessionId: params.sessionId });
|
|
32111
|
+
return { ok: true };
|
|
32112
|
+
}
|
|
31349
32113
|
killTree(session.child, params.signal ?? "SIGTERM");
|
|
31350
32114
|
return { ok: true };
|
|
31351
32115
|
}
|
|
@@ -31354,14 +32118,55 @@ function createProcHandlers(ctx, getProjectPaths) {
|
|
|
31354
32118
|
const session = procs.get(params.sessionId);
|
|
31355
32119
|
if (!session) {
|
|
31356
32120
|
ctx.log("proc write on unknown session", { sessionId: params.sessionId });
|
|
31357
|
-
return
|
|
32121
|
+
return Promise.reject(
|
|
32122
|
+
refuse2(ProcRefusalReasons.unknownSession, `proc.write: no process ${params.sessionId}`)
|
|
32123
|
+
);
|
|
31358
32124
|
}
|
|
31359
|
-
|
|
31360
|
-
|
|
31361
|
-
|
|
31362
|
-
|
|
32125
|
+
if (params.spawnId !== void 0 && session.spawnId !== params.spawnId) {
|
|
32126
|
+
return Promise.reject(
|
|
32127
|
+
refuse2(
|
|
32128
|
+
ProcRefusalReasons.staleSpawn,
|
|
32129
|
+
`proc.write: ${params.sessionId} now runs a different process`
|
|
32130
|
+
)
|
|
32131
|
+
);
|
|
31363
32132
|
}
|
|
31364
|
-
|
|
32133
|
+
if (params.writeId !== void 0 && session.writeIds.includes(params.writeId)) {
|
|
32134
|
+
return Promise.resolve({ ok: true, delivered: true, duplicate: true });
|
|
32135
|
+
}
|
|
32136
|
+
const stdin2 = session.child.stdin;
|
|
32137
|
+
if (!stdin2 || session.stdinBroken !== null || stdin2.destroyed || stdin2.writableEnded) {
|
|
32138
|
+
return Promise.reject(
|
|
32139
|
+
refuse2(
|
|
32140
|
+
ProcRefusalReasons.stdinClosed,
|
|
32141
|
+
`proc.write: ${params.sessionId} stdin is closed${session.stdinBroken ? ` (${session.stdinBroken})` : ""}`
|
|
32142
|
+
)
|
|
32143
|
+
);
|
|
32144
|
+
}
|
|
32145
|
+
if (params.writeId !== void 0) {
|
|
32146
|
+
session.writeIds.push(params.writeId);
|
|
32147
|
+
if (session.writeIds.length > WRITE_ID_MEMORY) session.writeIds.shift();
|
|
32148
|
+
}
|
|
32149
|
+
return new Promise((resolve9, reject) => {
|
|
32150
|
+
const fail = (err) => {
|
|
32151
|
+
if (params.writeId !== void 0) {
|
|
32152
|
+
session.writeIds = session.writeIds.filter((id) => id !== params.writeId);
|
|
32153
|
+
}
|
|
32154
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
32155
|
+
session.stdinBroken ??= message;
|
|
32156
|
+
ctx.log("proc write failed", { sessionId: params.sessionId, error: message });
|
|
32157
|
+
reject(
|
|
32158
|
+
refuse2(ProcRefusalReasons.stdinClosed, `proc.write: ${params.sessionId}: ${message}`)
|
|
32159
|
+
);
|
|
32160
|
+
};
|
|
32161
|
+
try {
|
|
32162
|
+
stdin2.write(
|
|
32163
|
+
params.data,
|
|
32164
|
+
(err) => err ? fail(err) : resolve9({ ok: true, delivered: true })
|
|
32165
|
+
);
|
|
32166
|
+
} catch (err) {
|
|
32167
|
+
fail(err);
|
|
32168
|
+
}
|
|
32169
|
+
});
|
|
31365
32170
|
}
|
|
31366
32171
|
function procGetBufferSince(rawParams) {
|
|
31367
32172
|
const params = procGetBufferSinceParamsSchema.parse(rawParams);
|
|
@@ -31383,7 +32188,14 @@ function createProcHandlers(ctx, getProjectPaths) {
|
|
|
31383
32188
|
return { sessions, exited: tombstones.list() };
|
|
31384
32189
|
}
|
|
31385
32190
|
function disposeAll() {
|
|
32191
|
+
for (const item of queued.values()) {
|
|
32192
|
+
admission.cancel(item.ticket);
|
|
32193
|
+
item.reject(new Error("computer shutting down"));
|
|
32194
|
+
}
|
|
32195
|
+
queued.clear();
|
|
31386
32196
|
for (const [sessionId, session] of procs) {
|
|
32197
|
+
session.revokeCli?.();
|
|
32198
|
+
releaseSlot(session);
|
|
31387
32199
|
killTree(session.child);
|
|
31388
32200
|
ctx.log("proc disposed on shutdown", { sessionId });
|
|
31389
32201
|
}
|
|
@@ -31400,7 +32212,7 @@ function createProcHandlers(ctx, getProjectPaths) {
|
|
|
31400
32212
|
}
|
|
31401
32213
|
|
|
31402
32214
|
// src/handlers/project.ts
|
|
31403
|
-
var
|
|
32215
|
+
var import_node_fs11 = require("node:fs");
|
|
31404
32216
|
function createProjectHandlers(ctx, getStore) {
|
|
31405
32217
|
const allow = (candidate) => assertPathAllowed(candidate, ctx.config.allowedRoots);
|
|
31406
32218
|
const store = () => {
|
|
@@ -31412,11 +32224,11 @@ function createProjectHandlers(ctx, getStore) {
|
|
|
31412
32224
|
const { projectId } = projectResolvePathParamsSchema.parse(rawParams);
|
|
31413
32225
|
const entry = store().get(projectId);
|
|
31414
32226
|
if (!entry) return { path: null };
|
|
31415
|
-
return { path: entry.path, exists: (0,
|
|
32227
|
+
return { path: entry.path, exists: (0, import_node_fs11.existsSync)(entry.path) };
|
|
31416
32228
|
}
|
|
31417
32229
|
async function setPath(rawParams) {
|
|
31418
|
-
const { projectId, path:
|
|
31419
|
-
const resolved = allow(
|
|
32230
|
+
const { projectId, path: path11 } = projectSetPathParamsSchema.parse(rawParams);
|
|
32231
|
+
const resolved = allow(path11);
|
|
31420
32232
|
const entry = await store().set(projectId, resolved);
|
|
31421
32233
|
ctx.log("project path recorded", { projectId, path: entry.path });
|
|
31422
32234
|
return { path: entry.path };
|
|
@@ -31433,7 +32245,7 @@ function createProjectHandlers(ctx, getStore) {
|
|
|
31433
32245
|
projects: store().list().map((e) => ({
|
|
31434
32246
|
projectId: e.projectId,
|
|
31435
32247
|
path: e.path,
|
|
31436
|
-
exists: (0,
|
|
32248
|
+
exists: (0, import_node_fs11.existsSync)(e.path)
|
|
31437
32249
|
}))
|
|
31438
32250
|
};
|
|
31439
32251
|
}
|
|
@@ -31446,8 +32258,77 @@ function createProjectHandlers(ctx, getStore) {
|
|
|
31446
32258
|
}
|
|
31447
32259
|
|
|
31448
32260
|
// src/handlers/pty.ts
|
|
32261
|
+
var import_node_module = require("node:module");
|
|
32262
|
+
var path8 = __toESM(require("node:path"), 1);
|
|
31449
32263
|
var pty = __toESM(require("node-pty"), 1);
|
|
31450
32264
|
|
|
32265
|
+
// ../../shared/platform/src/node-pty-install.ts
|
|
32266
|
+
var import_node_fs12 = __toESM(require("node:fs"), 1);
|
|
32267
|
+
var import_node_path14 = __toESM(require("node:path"), 1);
|
|
32268
|
+
function pendingBuildsFrom(modulesYaml) {
|
|
32269
|
+
try {
|
|
32270
|
+
const parsed = JSON.parse(modulesYaml);
|
|
32271
|
+
const pending = parsed?.pendingBuilds;
|
|
32272
|
+
return Array.isArray(pending) && pending.every((p) => typeof p === "string") ? pending : null;
|
|
32273
|
+
} catch {
|
|
32274
|
+
return null;
|
|
32275
|
+
}
|
|
32276
|
+
}
|
|
32277
|
+
function spawnHelperCandidates(nodePtyDir2, platform3 = process.platform, arch = process.arch) {
|
|
32278
|
+
if (platform3 === "win32") return [];
|
|
32279
|
+
return ["build/Release", "build/Debug", `prebuilds/${platform3}-${arch}`].map(
|
|
32280
|
+
(dir) => import_node_path14.default.join(nodePtyDir2, dir, "spawn-helper")
|
|
32281
|
+
);
|
|
32282
|
+
}
|
|
32283
|
+
function unpackedAsarPath(file2) {
|
|
32284
|
+
return file2.replace(/app\.asar(?!\.unpacked)/, "app.asar.unpacked").replace(/node_modules\.asar(?!\.unpacked)/, "node_modules.asar.unpacked");
|
|
32285
|
+
}
|
|
32286
|
+
var shellQuote = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
|
|
32287
|
+
function installProblems(state) {
|
|
32288
|
+
const problems = [];
|
|
32289
|
+
if (state.repoRoot !== null && state.pendingBuilds && state.pendingBuilds.length > 0) {
|
|
32290
|
+
problems.push({
|
|
32291
|
+
check: "Install scripts ran",
|
|
32292
|
+
detail: `pnpm skipped the install scripts of: ${state.pendingBuilds.join(", ")}.`,
|
|
32293
|
+
fix: `cd ${shellQuote(state.repoRoot)} && pnpm rebuild --pending`
|
|
32294
|
+
});
|
|
32295
|
+
}
|
|
32296
|
+
const broken = state.helpers.filter((h) => !h.executable).map((h) => h.path);
|
|
32297
|
+
if (broken.length > 0) {
|
|
32298
|
+
problems.push({
|
|
32299
|
+
check: "spawn-helper executable",
|
|
32300
|
+
detail: `${broken.join(", ")} ${broken.length === 1 ? "is" : "are"} not executable, so every terminal fails to start ("posix_spawnp failed").`,
|
|
32301
|
+
fix: `chmod +x ${broken.map(shellQuote).join(" ")}`
|
|
32302
|
+
});
|
|
32303
|
+
}
|
|
32304
|
+
return problems;
|
|
32305
|
+
}
|
|
32306
|
+
function readNodePtyInstall(nodePtyDir2) {
|
|
32307
|
+
const nodeModules = import_node_path14.default.dirname(nodePtyDir2);
|
|
32308
|
+
let pendingBuilds = null;
|
|
32309
|
+
let repoRoot = null;
|
|
32310
|
+
try {
|
|
32311
|
+
pendingBuilds = pendingBuildsFrom(
|
|
32312
|
+
import_node_fs12.default.readFileSync(import_node_path14.default.join(nodeModules, ".modules.yaml"), "utf8")
|
|
32313
|
+
);
|
|
32314
|
+
repoRoot = import_node_path14.default.dirname(nodeModules);
|
|
32315
|
+
} catch {
|
|
32316
|
+
}
|
|
32317
|
+
const helpers = spawnHelperCandidates(nodePtyDir2).map(unpackedAsarPath).filter((p) => import_node_fs12.default.existsSync(p)).map((p) => ({ path: p, executable: isExecutable(p) }));
|
|
32318
|
+
return { repoRoot, pendingBuilds, helpers };
|
|
32319
|
+
}
|
|
32320
|
+
function nodePtyInstallProblems(nodePtyDir2) {
|
|
32321
|
+
return installProblems(readNodePtyInstall(nodePtyDir2));
|
|
32322
|
+
}
|
|
32323
|
+
function isExecutable(file2) {
|
|
32324
|
+
try {
|
|
32325
|
+
import_node_fs12.default.accessSync(file2, import_node_fs12.default.constants.X_OK);
|
|
32326
|
+
return true;
|
|
32327
|
+
} catch {
|
|
32328
|
+
return false;
|
|
32329
|
+
}
|
|
32330
|
+
}
|
|
32331
|
+
|
|
31451
32332
|
// ../../shared/platform/src/startup-deadline.ts
|
|
31452
32333
|
function armStartupDeadline({
|
|
31453
32334
|
timeoutMs,
|
|
@@ -31515,7 +32396,7 @@ function matchLiveJobs(jobs, descendants, stdoutByPid) {
|
|
|
31515
32396
|
const alive = [];
|
|
31516
32397
|
for (const job of jobs) {
|
|
31517
32398
|
const outputFile = `/tasks/${job.id}.output`;
|
|
31518
|
-
const byOutput = job.kind === "shell" && [...outputs].some((
|
|
32399
|
+
const byOutput = job.kind === "shell" && [...outputs].some((path11) => path11.endsWith(outputFile));
|
|
31519
32400
|
const evalArg = job.command ? `eval ${claudeEvalArg(job.command)}` : null;
|
|
31520
32401
|
const byCommand = evalArg !== null && descendants.some((d) => d.args.includes(evalArg));
|
|
31521
32402
|
if (byOutput || byCommand) alive.push(job.id);
|
|
@@ -31529,7 +32410,7 @@ async function readStdoutPaths(pids) {
|
|
|
31529
32410
|
await Promise.all(
|
|
31530
32411
|
pids.map(
|
|
31531
32412
|
(pid2) => (0, import_promises9.readlink)(`/proc/${pid2}/fd/1`).then(
|
|
31532
|
-
(
|
|
32413
|
+
(path11) => void out.set(pid2, path11),
|
|
31533
32414
|
() => void 0
|
|
31534
32415
|
)
|
|
31535
32416
|
)
|
|
@@ -31564,167 +32445,121 @@ async function probeBackgroundJobs(rootPid, jobs) {
|
|
|
31564
32445
|
return matchLiveJobs(jobs, descendants, stdoutByPid);
|
|
31565
32446
|
}
|
|
31566
32447
|
|
|
31567
|
-
// src/spawn-admission.ts
|
|
31568
|
-
var import_node_os7 = require("node:os");
|
|
31569
|
-
var DEFAULT_START_CAP_MS = 5e3;
|
|
31570
|
-
var DEFAULT_WARM_QUIET_MS = 3e3;
|
|
31571
|
-
function defaultAdmissionConcurrency(cores = (0, import_node_os7.availableParallelism)()) {
|
|
31572
|
-
return Math.max(2, Math.min(4, Math.floor(cores / 2)));
|
|
31573
|
-
}
|
|
31574
|
-
var LANE_RANK = { foreground: 0, background: 1, warm: 2 };
|
|
31575
|
-
function createSpawnAdmission(opts = {}) {
|
|
31576
|
-
const concurrency = Math.max(1, opts.concurrency ?? defaultAdmissionConcurrency());
|
|
31577
|
-
const startCapMs = opts.startCapMs ?? DEFAULT_START_CAP_MS;
|
|
31578
|
-
const warmQuietMs = opts.warmQuietMs ?? DEFAULT_WARM_QUIET_MS;
|
|
31579
|
-
const now = opts.now ?? Date.now;
|
|
31580
|
-
const setTimer = opts.setTimer ?? ((fn, ms) => {
|
|
31581
|
-
const t2 = setTimeout(fn, ms);
|
|
31582
|
-
t2.unref?.();
|
|
31583
|
-
return t2;
|
|
31584
|
-
});
|
|
31585
|
-
const clearTimer = opts.clearTimer ?? ((h) => clearTimeout(h));
|
|
31586
|
-
const queue = [];
|
|
31587
|
-
const active = /* @__PURE__ */ new Set();
|
|
31588
|
-
let arrivals = 0;
|
|
31589
|
-
let promotions = 0;
|
|
31590
|
-
let lastAgentActivity = Number.NEGATIVE_INFINITY;
|
|
31591
|
-
let warmRetry = null;
|
|
31592
|
-
let pumping = false;
|
|
31593
|
-
let pumpAgain = false;
|
|
31594
|
-
const activeAgents = () => [...active].filter((s) => !s.warm).length;
|
|
31595
|
-
const activeWarm = () => [...active].filter((s) => s.warm).length;
|
|
31596
|
-
function next() {
|
|
31597
|
-
if (active.size >= concurrency || queue.length === 0) return null;
|
|
31598
|
-
let best = null;
|
|
31599
|
-
for (const t2 of queue) {
|
|
31600
|
-
if (!best || LANE_RANK[t2.lane] < LANE_RANK[best.lane] || t2.lane === best.lane && t2.order < best.order) {
|
|
31601
|
-
best = t2;
|
|
31602
|
-
}
|
|
31603
|
-
}
|
|
31604
|
-
if (!best || best.lane !== "warm") return best;
|
|
31605
|
-
if (activeAgents() > 0 || activeWarm() > 0) return null;
|
|
31606
|
-
const quietLeft = lastAgentActivity + warmQuietMs - now();
|
|
31607
|
-
if (quietLeft > 0) {
|
|
31608
|
-
if (warmRetry === null) {
|
|
31609
|
-
warmRetry = setTimer(() => {
|
|
31610
|
-
warmRetry = null;
|
|
31611
|
-
pump();
|
|
31612
|
-
}, quietLeft);
|
|
31613
|
-
}
|
|
31614
|
-
return null;
|
|
31615
|
-
}
|
|
31616
|
-
return best;
|
|
31617
|
-
}
|
|
31618
|
-
function admit(ticket) {
|
|
31619
|
-
queue.splice(queue.indexOf(ticket), 1);
|
|
31620
|
-
const warm = ticket.lane === "warm";
|
|
31621
|
-
const held = { warm, released: false, cap: null };
|
|
31622
|
-
const release = () => {
|
|
31623
|
-
if (held.released) return;
|
|
31624
|
-
held.released = true;
|
|
31625
|
-
clearTimer(held.cap);
|
|
31626
|
-
active.delete(held);
|
|
31627
|
-
if (!warm) lastAgentActivity = now();
|
|
31628
|
-
pump();
|
|
31629
|
-
};
|
|
31630
|
-
held.cap = setTimer(release, startCapMs);
|
|
31631
|
-
active.add(held);
|
|
31632
|
-
try {
|
|
31633
|
-
ticket.start({ release });
|
|
31634
|
-
} catch {
|
|
31635
|
-
release();
|
|
31636
|
-
}
|
|
31637
|
-
}
|
|
31638
|
-
function pump() {
|
|
31639
|
-
if (pumping) {
|
|
31640
|
-
pumpAgain = true;
|
|
31641
|
-
return;
|
|
31642
|
-
}
|
|
31643
|
-
pumping = true;
|
|
31644
|
-
try {
|
|
31645
|
-
do {
|
|
31646
|
-
pumpAgain = false;
|
|
31647
|
-
for (let t2 = next(); t2; t2 = next()) admit(t2);
|
|
31648
|
-
} while (pumpAgain);
|
|
31649
|
-
} finally {
|
|
31650
|
-
pumping = false;
|
|
31651
|
-
}
|
|
31652
|
-
}
|
|
31653
|
-
return {
|
|
31654
|
-
enqueue(lane, start) {
|
|
31655
|
-
const ticket = { lane, order: ++arrivals, start };
|
|
31656
|
-
queue.push(ticket);
|
|
31657
|
-
if (lane !== "warm") lastAgentActivity = now();
|
|
31658
|
-
pump();
|
|
31659
|
-
return ticket;
|
|
31660
|
-
},
|
|
31661
|
-
cancel(ticket) {
|
|
31662
|
-
const i = queue.indexOf(ticket);
|
|
31663
|
-
if (i < 0) return false;
|
|
31664
|
-
queue.splice(i, 1);
|
|
31665
|
-
pump();
|
|
31666
|
-
return true;
|
|
31667
|
-
},
|
|
31668
|
-
prioritize(ticket) {
|
|
31669
|
-
const t2 = ticket;
|
|
31670
|
-
if (!queue.includes(t2)) return false;
|
|
31671
|
-
t2.lane = "foreground";
|
|
31672
|
-
t2.order = -++promotions;
|
|
31673
|
-
pump();
|
|
31674
|
-
return true;
|
|
31675
|
-
},
|
|
31676
|
-
isQueued(ticket) {
|
|
31677
|
-
return queue.includes(ticket);
|
|
31678
|
-
},
|
|
31679
|
-
stats() {
|
|
31680
|
-
return { queued: queue.length, active: active.size, concurrency };
|
|
31681
|
-
}
|
|
31682
|
-
};
|
|
31683
|
-
}
|
|
31684
|
-
|
|
31685
32448
|
// src/handlers/pty.ts
|
|
31686
32449
|
var MAX_BUFFER_SIZE2 = 750 * 1024;
|
|
31687
32450
|
var STARTUP_KILL_REAP_MS = 2e3;
|
|
32451
|
+
function nodePtyDir() {
|
|
32452
|
+
const resolve9 = typeof require === "function" ? require.resolve : (0, import_node_module.createRequire)(process.argv[1] ?? path8.join(process.cwd(), "index.js")).resolve;
|
|
32453
|
+
return path8.dirname(resolve9("node-pty/package.json"));
|
|
32454
|
+
}
|
|
32455
|
+
var installProblemsOnDisk = () => nodePtyInstallProblems(nodePtyDir());
|
|
31688
32456
|
function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
31689
32457
|
const sessions = /* @__PURE__ */ new Map();
|
|
31690
32458
|
const tombstones = new ExitTombstones();
|
|
31691
32459
|
const admission = options.admission ?? createSpawnAdmission();
|
|
32460
|
+
const spawnPty = options.spawnPty ?? pty.spawn;
|
|
32461
|
+
const installProblems2 = options.nodePtyInstallProblems ?? installProblemsOnDisk;
|
|
31692
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
|
+
}
|
|
31693
32529
|
function resolveCwd(opts) {
|
|
31694
32530
|
if (opts.cwd) return spawnCwd(opts.cwd, ctx.config.allowedRoots);
|
|
31695
32531
|
if (!opts.projectId) throw new Error("pty.spawn needs either cwd or projectId");
|
|
31696
32532
|
const entry = getProjectPaths?.()?.get(opts.projectId);
|
|
31697
|
-
if (!entry)
|
|
31698
|
-
throw new Error(
|
|
31699
|
-
`project ${opts.projectId} is not checked out on this computer \u2014 adopt a folder or enable remote git for it`
|
|
31700
|
-
);
|
|
31701
|
-
}
|
|
32533
|
+
if (!entry) throw projectNotPlacedError(opts.projectId);
|
|
31702
32534
|
return spawnCwd(entry.path, ctx.config.allowedRoots);
|
|
31703
32535
|
}
|
|
31704
32536
|
function spawnSession(id, opts) {
|
|
31705
32537
|
const cwd = resolveCwd(opts);
|
|
31706
|
-
|
|
31707
|
-
|
|
31708
|
-
|
|
31709
|
-
|
|
31710
|
-
|
|
31711
|
-
|
|
31712
|
-
|
|
31713
|
-
|
|
31714
|
-
|
|
32538
|
+
if (sessions.has(id)) throw new Error(`pty ${id}: old process still live`);
|
|
32539
|
+
const capability = ctx.cliRelay?.issue();
|
|
32540
|
+
let proc;
|
|
32541
|
+
try {
|
|
32542
|
+
proc = spawnPty(opts.command, opts.args ?? [], {
|
|
32543
|
+
name: "xterm-color",
|
|
32544
|
+
cwd,
|
|
32545
|
+
env: buildPtySpawnEnv(ctx, opts.env, capability),
|
|
32546
|
+
cols: opts.cols ?? 80,
|
|
32547
|
+
rows: opts.rows ?? 24
|
|
32548
|
+
});
|
|
32549
|
+
} catch (err) {
|
|
32550
|
+
capability?.revoke();
|
|
32551
|
+
throw explainSpawnFailure(err);
|
|
31715
32552
|
}
|
|
31716
|
-
const proc = pty.spawn(opts.command, opts.args ?? [], {
|
|
31717
|
-
name: "xterm-color",
|
|
31718
|
-
cwd,
|
|
31719
|
-
env: buildSpawnEnv(ctx, opts.env),
|
|
31720
|
-
cols: opts.cols ?? 80,
|
|
31721
|
-
rows: opts.rows ?? 24
|
|
31722
|
-
});
|
|
31723
32553
|
const buffer = new RingBuffer(MAX_BUFFER_SIZE2);
|
|
32554
|
+
let resolveExit;
|
|
32555
|
+
const exited = new Promise((resolve9) => resolveExit = resolve9);
|
|
31724
32556
|
const session = {
|
|
32557
|
+
revokeCli: capability?.revoke,
|
|
31725
32558
|
proc,
|
|
31726
32559
|
buffer,
|
|
31727
32560
|
id,
|
|
32561
|
+
exited,
|
|
32562
|
+
resolveExit,
|
|
31728
32563
|
...opts.warm ? { warm: opts.warm } : {},
|
|
31729
32564
|
...opts.spawnId !== void 0 ? { spawnId: opts.spawnId } : {}
|
|
31730
32565
|
};
|
|
@@ -31743,11 +32578,14 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
31743
32578
|
if (session.warm) return;
|
|
31744
32579
|
ctx.dialer.notify(ComputerNotificationMethods.ptyData, {
|
|
31745
32580
|
sessionId: session.id,
|
|
32581
|
+
...session.spawnId ? { spawnId: session.spawnId } : {},
|
|
31746
32582
|
seq,
|
|
31747
32583
|
data
|
|
31748
32584
|
});
|
|
31749
32585
|
});
|
|
31750
32586
|
proc.onExit(({ exitCode, signal }) => {
|
|
32587
|
+
capability?.revoke();
|
|
32588
|
+
session.resolveExit();
|
|
31751
32589
|
reap(
|
|
31752
32590
|
session,
|
|
31753
32591
|
typeof exitCode === "number" ? exitCode : null,
|
|
@@ -31756,11 +32594,33 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
31756
32594
|
});
|
|
31757
32595
|
return session;
|
|
31758
32596
|
}
|
|
32597
|
+
function explainSpawnFailure(err) {
|
|
32598
|
+
const original = err instanceof Error ? err : new Error(String(err));
|
|
32599
|
+
let problems;
|
|
32600
|
+
try {
|
|
32601
|
+
problems = installProblems2();
|
|
32602
|
+
} catch {
|
|
32603
|
+
return original;
|
|
32604
|
+
}
|
|
32605
|
+
if (problems.length === 0) return original;
|
|
32606
|
+
const why = problems.map((p) => `${p.detail} Fix: ${p.fix}`).join(" ");
|
|
32607
|
+
return new Error(`${original.message} ${why}`, { cause: original });
|
|
32608
|
+
}
|
|
31759
32609
|
function reap(session, exitCode, signal) {
|
|
31760
32610
|
if (sessions.get(session.id) !== session) return;
|
|
31761
32611
|
sessions.delete(session.id);
|
|
31762
32612
|
session.deadline?.cancel();
|
|
31763
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
|
+
}
|
|
31764
32624
|
if (session.warm) {
|
|
31765
32625
|
ctx.log("warm pty exited before adoption", { warmId: session.id });
|
|
31766
32626
|
return;
|
|
@@ -31768,6 +32628,7 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
31768
32628
|
const reason = session.exitReason;
|
|
31769
32629
|
const delivered = ctx.dialer.notify(ComputerNotificationMethods.ptyExit, {
|
|
31770
32630
|
sessionId: session.id,
|
|
32631
|
+
...session.spawnId ? { spawnId: session.spawnId } : {},
|
|
31771
32632
|
exitCode,
|
|
31772
32633
|
signal,
|
|
31773
32634
|
...reason ? { reason } : {}
|
|
@@ -31803,11 +32664,20 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
31803
32664
|
slot.release();
|
|
31804
32665
|
}
|
|
31805
32666
|
function startSession(params) {
|
|
31806
|
-
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
|
+
});
|
|
31807
32677
|
ctx.log("pty spawned", {
|
|
31808
32678
|
sessionId: params.sessionId,
|
|
31809
32679
|
pid: session.proc.pid,
|
|
31810
|
-
command:
|
|
32680
|
+
command: shell
|
|
31811
32681
|
});
|
|
31812
32682
|
return {
|
|
31813
32683
|
pid: session.proc.pid,
|
|
@@ -31859,16 +32729,26 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
31859
32729
|
}
|
|
31860
32730
|
const waitedMs = Date.now() - enqueuedAt;
|
|
31861
32731
|
if (waitedMs >= 1e3) ctx.log("agent start admitted after queueing", { id, lane, waitedMs });
|
|
31862
|
-
|
|
32732
|
+
Promise.resolve(reply).then(item.resolve, (err) => {
|
|
32733
|
+
slot.release();
|
|
32734
|
+
item.reject(err);
|
|
32735
|
+
});
|
|
31863
32736
|
});
|
|
31864
32737
|
return promise2;
|
|
31865
32738
|
}
|
|
31866
32739
|
function spawn6(rawParams) {
|
|
31867
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
|
+
}
|
|
31868
32745
|
if (params.spawnId !== void 0) {
|
|
31869
32746
|
const live = sessions.get(params.sessionId);
|
|
31870
32747
|
if (live && !live.warm && live.spawnId === params.spawnId) {
|
|
31871
|
-
return {
|
|
32748
|
+
return {
|
|
32749
|
+
pid: live.proc.pid,
|
|
32750
|
+
...live.deadline ? { startupDeadline: true } : {}
|
|
32751
|
+
};
|
|
31872
32752
|
}
|
|
31873
32753
|
if (tombstones.ran(params.sessionId, params.spawnId)) {
|
|
31874
32754
|
throw new Error(
|
|
@@ -31878,7 +32758,11 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
31878
32758
|
}
|
|
31879
32759
|
if (params.admission === void 0) {
|
|
31880
32760
|
const prior = queued.get(params.sessionId);
|
|
31881
|
-
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);
|
|
31882
32766
|
if (prior) {
|
|
31883
32767
|
queued.delete(params.sessionId);
|
|
31884
32768
|
admission.cancel(prior.ticket);
|
|
@@ -31886,11 +32770,15 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
31886
32770
|
}
|
|
31887
32771
|
return reply;
|
|
31888
32772
|
}
|
|
31889
|
-
return enqueueSpawn(params.sessionId, params.admission, params, (slot, item) => {
|
|
31890
|
-
const reply =
|
|
31891
|
-
|
|
31892
|
-
|
|
31893
|
-
|
|
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
|
+
});
|
|
31894
32782
|
});
|
|
31895
32783
|
const session = sessions.get(params.sessionId);
|
|
31896
32784
|
session.admissionSlot = slot;
|
|
@@ -31899,18 +32787,28 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
31899
32787
|
});
|
|
31900
32788
|
}
|
|
31901
32789
|
function startWarm(params, cols, rows) {
|
|
32790
|
+
const shell = params.computerShell ? resolveUserShell() : params.command;
|
|
31902
32791
|
const session = spawnSession(params.warmId, {
|
|
31903
32792
|
...params,
|
|
32793
|
+
command: shell,
|
|
32794
|
+
args: params.computerShell ? getShellStartupArgs(shell) : params.args,
|
|
31904
32795
|
...cols !== void 0 ? { cols } : {},
|
|
31905
32796
|
...rows !== void 0 ? { rows } : {},
|
|
31906
32797
|
warm: { cwd: params.cwd, startedAt: Date.now() }
|
|
31907
32798
|
});
|
|
31908
32799
|
if (params.postSpawnCommand) session.proc.write(`${params.postSpawnCommand}\r`);
|
|
31909
|
-
ctx.log("warm pty spawned", {
|
|
32800
|
+
ctx.log("warm pty spawned", {
|
|
32801
|
+
warmId: params.warmId,
|
|
32802
|
+
pid: session.proc.pid
|
|
32803
|
+
});
|
|
31910
32804
|
return session;
|
|
31911
32805
|
}
|
|
31912
32806
|
function warmSpawn(rawParams) {
|
|
31913
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;
|
|
31914
32812
|
return enqueueSpawn(params.warmId, "warm", params, (slot, item) => {
|
|
31915
32813
|
const session = startWarm(params, item.cols, item.rows);
|
|
31916
32814
|
session.admissionSlot = slot;
|
|
@@ -31929,20 +32827,49 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
31929
32827
|
function prioritize(rawParams) {
|
|
31930
32828
|
const params = ptyPrioritizeParamsSchema.parse(rawParams);
|
|
31931
32829
|
const item = queued.get(params.sessionId);
|
|
31932
|
-
return {
|
|
32830
|
+
return {
|
|
32831
|
+
ok: true,
|
|
32832
|
+
queued: item && (params.spawnId === void 0 || item.spawnId === params.spawnId) ? admission.prioritize(item.ticket) : false
|
|
32833
|
+
};
|
|
31933
32834
|
}
|
|
31934
32835
|
async function backgroundJobs(rawParams) {
|
|
31935
32836
|
const params = ptyBackgroundJobsParamsSchema.parse(rawParams);
|
|
31936
|
-
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;
|
|
31937
32839
|
if (pid === void 0) return { ok: true, supported: true, alive: [] };
|
|
31938
32840
|
const alive = await probeBackgroundJobs(pid, params.jobs);
|
|
31939
32841
|
return alive === null ? { ok: true, supported: false, alive: [] } : { ok: true, supported: true, alive };
|
|
31940
32842
|
}
|
|
32843
|
+
async function doctor(rawParams) {
|
|
32844
|
+
const params = ptyDoctorParamsSchema.parse(rawParams);
|
|
32845
|
+
await options.pathReady;
|
|
32846
|
+
const env = buildPtySpawnEnv(ctx);
|
|
32847
|
+
const binaries = await Promise.all(
|
|
32848
|
+
params.binaries.map(async (binary) => ({
|
|
32849
|
+
binary,
|
|
32850
|
+
path: await findOnPath(binary, env)
|
|
32851
|
+
}))
|
|
32852
|
+
);
|
|
32853
|
+
return {
|
|
32854
|
+
ok: true,
|
|
32855
|
+
problems: installProblems2(),
|
|
32856
|
+
shellExists: shellExists(resolveUserShell()),
|
|
32857
|
+
binaries
|
|
32858
|
+
};
|
|
32859
|
+
}
|
|
31941
32860
|
function dropQueued(reason) {
|
|
31942
32861
|
for (const id of [...queued.keys()]) cancelQueued(id, reason);
|
|
31943
32862
|
}
|
|
31944
32863
|
function warmAdopt(rawParams) {
|
|
31945
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
|
+
}
|
|
31946
32873
|
const session = sessions.get(params.warmId);
|
|
31947
32874
|
if (!session || !session.warm) {
|
|
31948
32875
|
throw new RpcError(
|
|
@@ -31952,16 +32879,15 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
31952
32879
|
}
|
|
31953
32880
|
const displaced = sessions.get(params.sessionId);
|
|
31954
32881
|
if (displaced) {
|
|
31955
|
-
|
|
31956
|
-
|
|
31957
|
-
|
|
31958
|
-
|
|
31959
|
-
displacedWarm: !!displaced.warm
|
|
31960
|
-
});
|
|
32882
|
+
throw new RpcError(
|
|
32883
|
+
ComputerTransportErrorCodes.unknownComputer,
|
|
32884
|
+
`session ${params.sessionId} already has live process ${displaced.proc.pid}`
|
|
32885
|
+
);
|
|
31961
32886
|
}
|
|
31962
32887
|
sessions.delete(params.warmId);
|
|
31963
32888
|
session.id = params.sessionId;
|
|
31964
32889
|
delete session.warm;
|
|
32890
|
+
session.spawnId = params.warmId;
|
|
31965
32891
|
sessions.set(params.sessionId, session);
|
|
31966
32892
|
releaseAdmission(session);
|
|
31967
32893
|
const data = session.buffer.toString();
|
|
@@ -31973,16 +32899,12 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
31973
32899
|
});
|
|
31974
32900
|
return { pid: session.proc.pid, data, seq };
|
|
31975
32901
|
}
|
|
31976
|
-
function warmKill(rawParams) {
|
|
32902
|
+
async function warmKill(rawParams) {
|
|
31977
32903
|
const params = ptyWarmKillParamsSchema.parse(rawParams);
|
|
31978
32904
|
cancelQueued(params.warmId, "killed");
|
|
31979
32905
|
const session = sessions.get(params.warmId);
|
|
31980
32906
|
if (!session) return { ok: true };
|
|
31981
|
-
|
|
31982
|
-
try {
|
|
31983
|
-
session.proc.kill();
|
|
31984
|
-
} catch {
|
|
31985
|
-
}
|
|
32907
|
+
await stopPty(session);
|
|
31986
32908
|
return { ok: true };
|
|
31987
32909
|
}
|
|
31988
32910
|
function warmList() {
|
|
@@ -31998,58 +32920,55 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
31998
32920
|
}
|
|
31999
32921
|
return { warms };
|
|
32000
32922
|
}
|
|
32001
|
-
function kill(rawParams) {
|
|
32923
|
+
async function kill(rawParams) {
|
|
32002
32924
|
const params = ptyKillParamsSchema.parse(rawParams);
|
|
32003
|
-
const
|
|
32004
|
-
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)) {
|
|
32005
32932
|
const delivered = ctx.dialer.notify(ComputerNotificationMethods.ptyExit, {
|
|
32006
32933
|
sessionId: params.sessionId,
|
|
32934
|
+
...pending.spawnId ? { spawnId: pending.spawnId } : {},
|
|
32007
32935
|
exitCode: null,
|
|
32008
32936
|
signal: params.signal ?? "SIGHUP",
|
|
32009
32937
|
reason: "killed-before-start"
|
|
32010
32938
|
});
|
|
32011
32939
|
tombstones.add(params.sessionId, {
|
|
32012
|
-
spawnId:
|
|
32940
|
+
spawnId: pending.spawnId,
|
|
32013
32941
|
exitCode: null,
|
|
32014
32942
|
signal: params.signal ?? "SIGHUP",
|
|
32015
32943
|
reason: "killed-before-start"
|
|
32016
32944
|
});
|
|
32017
32945
|
if (!delivered)
|
|
32018
|
-
ctx.log("kill of a queued start not delivered", {
|
|
32946
|
+
ctx.log("kill of a queued start not delivered", {
|
|
32947
|
+
sessionId: params.sessionId
|
|
32948
|
+
});
|
|
32019
32949
|
cancelQueued(params.sessionId, "killed");
|
|
32020
|
-
|
|
32021
|
-
if (superseded) {
|
|
32022
|
-
sessions.delete(params.sessionId);
|
|
32023
|
-
superseded.deadline?.cancel();
|
|
32024
|
-
releaseAdmission(superseded);
|
|
32025
|
-
try {
|
|
32026
|
-
superseded.proc.kill(params.signal);
|
|
32027
|
-
} catch {
|
|
32028
|
-
}
|
|
32029
|
-
}
|
|
32950
|
+
if (live && params.spawnId === void 0) await stopPty(live, params.signal);
|
|
32030
32951
|
return { ok: true };
|
|
32031
32952
|
}
|
|
32032
|
-
|
|
32033
|
-
if (!session) {
|
|
32953
|
+
if (!live || params.spawnId !== void 0 && live.spawnId !== params.spawnId) {
|
|
32034
32954
|
ctx.log("pty kill on unknown session", { sessionId: params.sessionId });
|
|
32035
32955
|
return { ok: true };
|
|
32036
32956
|
}
|
|
32037
|
-
|
|
32038
|
-
session.proc.kill(params.signal);
|
|
32039
|
-
} catch (err) {
|
|
32040
|
-
ctx.log("pty kill failed", { sessionId: params.sessionId, error: String(err) });
|
|
32041
|
-
}
|
|
32957
|
+
await stopPty(live, params.signal);
|
|
32042
32958
|
return { ok: true };
|
|
32043
32959
|
}
|
|
32044
32960
|
function resize(rawParams) {
|
|
32045
32961
|
const params = ptyResizeParamsSchema.parse(rawParams);
|
|
32046
32962
|
const waiting = queued.get(params.sessionId);
|
|
32047
32963
|
if (waiting) {
|
|
32964
|
+
if (params.spawnId !== void 0 && waiting.spawnId !== params.spawnId) return { ok: true };
|
|
32048
32965
|
waiting.cols = params.cols;
|
|
32049
32966
|
waiting.rows = params.rows;
|
|
32050
32967
|
return { ok: true };
|
|
32051
32968
|
}
|
|
32052
32969
|
const session = sessions.get(params.sessionId);
|
|
32970
|
+
if (session && params.spawnId !== void 0 && session.spawnId !== params.spawnId)
|
|
32971
|
+
return { ok: true };
|
|
32053
32972
|
if (!session) {
|
|
32054
32973
|
ctx.log("pty resize on unknown session", { sessionId: params.sessionId });
|
|
32055
32974
|
return { ok: true };
|
|
@@ -32061,10 +32980,13 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
32061
32980
|
const params = ptyWriteParamsSchema.parse(rawParams);
|
|
32062
32981
|
const waiting = queued.get(params.sessionId);
|
|
32063
32982
|
if (waiting) {
|
|
32983
|
+
if (params.spawnId !== void 0 && waiting.spawnId !== params.spawnId) return { ok: true };
|
|
32064
32984
|
waiting.writes.push(params.data);
|
|
32065
32985
|
return { ok: true };
|
|
32066
32986
|
}
|
|
32067
32987
|
const session = sessions.get(params.sessionId);
|
|
32988
|
+
if (session && params.spawnId !== void 0 && session.spawnId !== params.spawnId)
|
|
32989
|
+
return { ok: true };
|
|
32068
32990
|
if (!session) {
|
|
32069
32991
|
ctx.log("pty write on unknown session", { sessionId: params.sessionId });
|
|
32070
32992
|
return { ok: true };
|
|
@@ -32090,7 +33012,9 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
32090
33012
|
}
|
|
32091
33013
|
function getBufferSince(rawParams) {
|
|
32092
33014
|
const params = ptyGetBufferSinceParamsSchema.parse(rawParams);
|
|
32093
|
-
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;
|
|
32094
33018
|
if (!buffer) return { frames: [] };
|
|
32095
33019
|
const frames = buffer.getChunksSince(params.seq).map((c) => ({ seq: c.seq, data: c.data }));
|
|
32096
33020
|
return { frames };
|
|
@@ -32098,6 +33022,7 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
32098
33022
|
function disposeAll() {
|
|
32099
33023
|
dropQueued("computer shutting down");
|
|
32100
33024
|
for (const [sessionId, session] of sessions) {
|
|
33025
|
+
session.revokeCli?.();
|
|
32101
33026
|
session.deadline?.cancel();
|
|
32102
33027
|
releaseAdmission(session);
|
|
32103
33028
|
try {
|
|
@@ -32117,6 +33042,7 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
32117
33042
|
[HubToComputerMethods.ptyList]: list,
|
|
32118
33043
|
[HubToComputerMethods.ptyPrioritize]: prioritize,
|
|
32119
33044
|
[HubToComputerMethods.ptyBackgroundJobs]: backgroundJobs,
|
|
33045
|
+
[HubToComputerMethods.ptyDoctor]: doctor,
|
|
32120
33046
|
[HubToComputerMethods.ptyWarmSpawn]: warmSpawn,
|
|
32121
33047
|
[HubToComputerMethods.ptyWarmAdopt]: warmAdopt,
|
|
32122
33048
|
[HubToComputerMethods.ptyWarmKill]: warmKill,
|
|
@@ -32126,19 +33052,19 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
32126
33052
|
}
|
|
32127
33053
|
|
|
32128
33054
|
// src/handlers/computer-config.ts
|
|
32129
|
-
var
|
|
32130
|
-
var
|
|
33055
|
+
var import_node_fs13 = require("node:fs");
|
|
33056
|
+
var import_node_path15 = require("node:path");
|
|
32131
33057
|
function canonicalRoots(roots) {
|
|
32132
33058
|
const applied = [];
|
|
32133
33059
|
const rejected = [];
|
|
32134
33060
|
for (const root of roots) {
|
|
32135
|
-
if (!(0,
|
|
33061
|
+
if (!(0, import_node_path15.isAbsolute)(root)) {
|
|
32136
33062
|
rejected.push({ path: root, reason: "not an absolute path" });
|
|
32137
33063
|
continue;
|
|
32138
33064
|
}
|
|
32139
33065
|
let real;
|
|
32140
33066
|
try {
|
|
32141
|
-
real =
|
|
33067
|
+
real = import_node_fs13.realpathSync.native((0, import_node_path15.resolve)(root));
|
|
32142
33068
|
} catch {
|
|
32143
33069
|
rejected.push({ path: root, reason: "does not exist on this computer" });
|
|
32144
33070
|
continue;
|
|
@@ -32183,12 +33109,13 @@ function createComputerConfigHandlers(ctx) {
|
|
|
32183
33109
|
|
|
32184
33110
|
// src/project-paths.ts
|
|
32185
33111
|
var import_promises10 = require("node:fs/promises");
|
|
32186
|
-
var
|
|
32187
|
-
var
|
|
33112
|
+
var import_node_crypto4 = require("node:crypto");
|
|
33113
|
+
var import_node_os10 = require("node:os");
|
|
33114
|
+
var import_node_path16 = require("node:path");
|
|
32188
33115
|
function slayzoneRootDir3() {
|
|
32189
33116
|
if (process.env.SLAYZONE_ROOT) return process.env.SLAYZONE_ROOT;
|
|
32190
|
-
const home = process.env.HOME ?? process.env.USERPROFILE ?? (0,
|
|
32191
|
-
return (0,
|
|
33117
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? (0, import_node_os10.homedir)();
|
|
33118
|
+
return (0, import_node_path16.join)(home, ".slayzone");
|
|
32192
33119
|
}
|
|
32193
33120
|
var projectPathEntrySchema = external_exports.object({
|
|
32194
33121
|
/** Absolute, realpath-resolved at write time. */
|
|
@@ -32197,7 +33124,7 @@ var projectPathEntrySchema = external_exports.object({
|
|
|
32197
33124
|
});
|
|
32198
33125
|
var projectMapSchema = external_exports.record(external_exports.string(), projectPathEntrySchema);
|
|
32199
33126
|
function projectsFilePath(baseDir) {
|
|
32200
|
-
return (0,
|
|
33127
|
+
return (0, import_node_path16.join)(baseDir ?? slayzoneRootDir3(), "computer.projects.json");
|
|
32201
33128
|
}
|
|
32202
33129
|
var fileSchema = external_exports.record(external_exports.string(), projectMapSchema);
|
|
32203
33130
|
async function readFileMap(filePath) {
|
|
@@ -32228,8 +33155,8 @@ async function readFileMap(filePath) {
|
|
|
32228
33155
|
return fileSchema.parse(out);
|
|
32229
33156
|
}
|
|
32230
33157
|
async function writeFileMap(filePath, map2) {
|
|
32231
|
-
await (0, import_promises10.mkdir)((0,
|
|
32232
|
-
const tmpPath = `${filePath}.tmp-${process.pid}-${
|
|
33158
|
+
await (0, import_promises10.mkdir)((0, import_node_path16.dirname)(filePath), { recursive: true, mode: 448 });
|
|
33159
|
+
const tmpPath = `${filePath}.tmp-${process.pid}-${(0, import_node_crypto4.randomUUID)()}`;
|
|
32233
33160
|
await (0, import_promises10.writeFile)(tmpPath, `${JSON.stringify(map2, null, 2)}
|
|
32234
33161
|
`, { mode: 384 });
|
|
32235
33162
|
try {
|
|
@@ -32239,6 +33166,16 @@ async function writeFileMap(filePath, map2) {
|
|
|
32239
33166
|
throw err;
|
|
32240
33167
|
}
|
|
32241
33168
|
}
|
|
33169
|
+
var writeQueues = /* @__PURE__ */ new Map();
|
|
33170
|
+
function serialized(filePath, write) {
|
|
33171
|
+
const previous = writeQueues.get(filePath) ?? Promise.resolve();
|
|
33172
|
+
const next = previous.then(write, write);
|
|
33173
|
+
writeQueues.set(
|
|
33174
|
+
filePath,
|
|
33175
|
+
next.catch(() => void 0)
|
|
33176
|
+
);
|
|
33177
|
+
return next;
|
|
33178
|
+
}
|
|
32242
33179
|
async function createProjectPathStore(hubHost, options = {}) {
|
|
32243
33180
|
if (!hubHost.trim()) throw new Error(`invalid hub host for project map: '${hubHost}'`);
|
|
32244
33181
|
const filePath = projectsFilePath(options.baseDir);
|
|
@@ -32248,21 +33185,25 @@ async function createProjectPathStore(hubHost, options = {}) {
|
|
|
32248
33185
|
get(projectId) {
|
|
32249
33186
|
return cache[projectId] ?? null;
|
|
32250
33187
|
},
|
|
32251
|
-
|
|
32252
|
-
|
|
32253
|
-
|
|
32254
|
-
|
|
32255
|
-
|
|
32256
|
-
|
|
32257
|
-
|
|
33188
|
+
set(projectId, path11) {
|
|
33189
|
+
return serialized(filePath, async () => {
|
|
33190
|
+
const entry = { path: path11, updatedAt: Date.now() };
|
|
33191
|
+
const all = await readFileMap(filePath);
|
|
33192
|
+
all[hubHost] = { ...all[hubHost] ?? {}, [projectId]: entry };
|
|
33193
|
+
await writeFileMap(filePath, all);
|
|
33194
|
+
cache = all[hubHost];
|
|
33195
|
+
return entry;
|
|
33196
|
+
});
|
|
32258
33197
|
},
|
|
32259
|
-
|
|
32260
|
-
|
|
32261
|
-
|
|
32262
|
-
|
|
32263
|
-
|
|
32264
|
-
|
|
32265
|
-
|
|
33198
|
+
forget(projectId) {
|
|
33199
|
+
return serialized(filePath, async () => {
|
|
33200
|
+
const all = await readFileMap(filePath);
|
|
33201
|
+
const forHub = { ...all[hubHost] ?? {} };
|
|
33202
|
+
delete forHub[projectId];
|
|
33203
|
+
all[hubHost] = forHub;
|
|
33204
|
+
await writeFileMap(filePath, all);
|
|
33205
|
+
cache = forHub;
|
|
33206
|
+
});
|
|
32266
33207
|
},
|
|
32267
33208
|
list() {
|
|
32268
33209
|
return Object.entries(cache).map(([projectId, entry]) => ({ projectId, ...entry }));
|
|
@@ -32282,13 +33223,20 @@ function createHubRequestHandler(deps2) {
|
|
|
32282
33223
|
...deps2.agentHookUrl ? { agentHookUrl: deps2.agentHookUrl } : {},
|
|
32283
33224
|
...deps2.hubAddress ? { hubAddress: deps2.hubAddress } : {}
|
|
32284
33225
|
};
|
|
32285
|
-
const
|
|
33226
|
+
const cliReady = deps2.cliRelayReady?.then((relay) => {
|
|
33227
|
+
ctx.cliRelay = relay;
|
|
33228
|
+
});
|
|
33229
|
+
void cliReady?.catch(() => log("CLI relay is unavailable"));
|
|
33230
|
+
const pty2 = createPtyHandlers(ctx, deps2.getProjectPaths, {
|
|
33231
|
+
...deps2.admission ? { admission: deps2.admission } : {},
|
|
33232
|
+
...deps2.pathReady ? { pathReady: deps2.pathReady } : {}
|
|
33233
|
+
});
|
|
33234
|
+
const proc = createProcHandlers(
|
|
32286
33235
|
ctx,
|
|
32287
33236
|
deps2.getProjectPaths,
|
|
32288
33237
|
deps2.admission ? { admission: deps2.admission } : {}
|
|
32289
33238
|
);
|
|
32290
|
-
const
|
|
32291
|
-
const fs7 = createFsHandlers(ctx);
|
|
33239
|
+
const fs8 = createFsHandlers(ctx);
|
|
32292
33240
|
const shutdownHandler = async (params) => {
|
|
32293
33241
|
const parsed = computerShutdownParamsSchema.safeParse(params ?? {});
|
|
32294
33242
|
const reason = parsed.success ? parsed.data.reason ?? "hub-requested" : "hub-requested";
|
|
@@ -32300,9 +33248,10 @@ function createHubRequestHandler(deps2) {
|
|
|
32300
33248
|
...pty2.handlers,
|
|
32301
33249
|
...proc.handlers,
|
|
32302
33250
|
...createGitHandlers(ctx),
|
|
32303
|
-
...
|
|
33251
|
+
...fs8.handlers,
|
|
32304
33252
|
...createComputerConfigHandlers(ctx),
|
|
32305
33253
|
...createGhHandlers(ctx),
|
|
33254
|
+
...createClaudeSessionHandlers(ctx, deps2.getProjectPaths ?? (() => null)),
|
|
32306
33255
|
...createProjectHandlers(ctx, deps2.getProjectPaths ?? (() => null))
|
|
32307
33256
|
};
|
|
32308
33257
|
const hub = deps2.hubHost ?? "unknown";
|
|
@@ -32337,6 +33286,8 @@ function createHubRequestHandler(deps2) {
|
|
|
32337
33286
|
throw err;
|
|
32338
33287
|
}
|
|
32339
33288
|
try {
|
|
33289
|
+
if (method === HubToComputerMethods.ptySpawn || method === HubToComputerMethods.ptyWarmSpawn || method === HubToComputerMethods.procSpawn)
|
|
33290
|
+
await cliReady;
|
|
32340
33291
|
if (deps2.pathReady && !ptyMethods.has(method)) await deps2.pathReady;
|
|
32341
33292
|
const result = await entry(params);
|
|
32342
33293
|
record2("ok");
|
|
@@ -32360,7 +33311,7 @@ function createHubRequestHandler(deps2) {
|
|
|
32360
33311
|
const dispose = () => {
|
|
32361
33312
|
pty2.disposeAll();
|
|
32362
33313
|
proc.disposeAll();
|
|
32363
|
-
|
|
33314
|
+
fs8.disposeAll();
|
|
32364
33315
|
};
|
|
32365
33316
|
const setAgentHookUrl = (url2) => {
|
|
32366
33317
|
ctx.agentHookUrl = url2;
|
|
@@ -32389,14 +33340,14 @@ function startComputer(config2, deps2 = {}) {
|
|
|
32389
33340
|
});
|
|
32390
33341
|
});
|
|
32391
33342
|
let handle;
|
|
32392
|
-
const stateDir = deps2.stateDir ?? (0,
|
|
32393
|
-
const configFile = (0,
|
|
33343
|
+
const stateDir = deps2.stateDir ?? (0, import_node_path17.dirname)(getComputerConfigFilePath());
|
|
33344
|
+
const configFile = (0, import_node_path17.join)(stateDir, "computer.config.json");
|
|
32394
33345
|
setProtectedPaths([
|
|
32395
33346
|
configFile,
|
|
32396
33347
|
// The legacy file computer.config.json falls back to when absent.
|
|
32397
|
-
(0,
|
|
33348
|
+
(0, import_node_path17.join)(stateDir, "config.json"),
|
|
32398
33349
|
credentialsFilePath(stateDir),
|
|
32399
|
-
(0,
|
|
33350
|
+
(0, import_node_path17.join)(stateDir, REQUEST_LOG_DIR_NAME)
|
|
32400
33351
|
]);
|
|
32401
33352
|
const requestLog = createRequestLog({
|
|
32402
33353
|
stateDir,
|
|
@@ -32405,7 +33356,7 @@ function startComputer(config2, deps2 = {}) {
|
|
|
32405
33356
|
log("local request log", { file: requestLog.filePath });
|
|
32406
33357
|
const watchLocalRoots = !config2.hubMayEditRoots;
|
|
32407
33358
|
if (watchLocalRoots) {
|
|
32408
|
-
(0,
|
|
33359
|
+
(0, import_node_fs14.watchFile)(configFile, { interval: 2e3, persistent: false }, () => {
|
|
32409
33360
|
const declared = loadComputerConfigFile(configFile).allowedRoots;
|
|
32410
33361
|
const applied = reloadLocalRoots(config2, declared);
|
|
32411
33362
|
if (!applied) return;
|
|
@@ -32441,7 +33392,10 @@ function startComputer(config2, deps2 = {}) {
|
|
|
32441
33392
|
count: store.list().length
|
|
32442
33393
|
});
|
|
32443
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 }));
|
|
32444
33397
|
dispatch = createHubRequestHandler({
|
|
33398
|
+
cliRelayReady,
|
|
32445
33399
|
shutdown: (reason) => {
|
|
32446
33400
|
log("computer shutdown requested by hub", { hubUrl, reason });
|
|
32447
33401
|
void handle.stop().then(() => deps2.onShutdown?.(reason));
|
|
@@ -32462,6 +33416,11 @@ function startComputer(config2, deps2 = {}) {
|
|
|
32462
33416
|
({ computerId, mode }) => log("connected to hub", { hubUrl, computerId, mode })
|
|
32463
33417
|
);
|
|
32464
33418
|
dialer.events.on("disconnected", ({ reason }) => {
|
|
33419
|
+
void cliRelayReady.then(
|
|
33420
|
+
(relay) => relay.disconnect(),
|
|
33421
|
+
() => {
|
|
33422
|
+
}
|
|
33423
|
+
);
|
|
32465
33424
|
log("disconnected from hub", { hubUrl, reason });
|
|
32466
33425
|
dispatch.hubDisconnected(String(reason));
|
|
32467
33426
|
});
|
|
@@ -32490,6 +33449,11 @@ function startComputer(config2, deps2 = {}) {
|
|
|
32490
33449
|
dialer,
|
|
32491
33450
|
dispose: async () => {
|
|
32492
33451
|
dispatch.dispose();
|
|
33452
|
+
await cliRelayReady.then(
|
|
33453
|
+
(relay) => relay.close(),
|
|
33454
|
+
() => {
|
|
33455
|
+
}
|
|
33456
|
+
);
|
|
32493
33457
|
if (agentHookServer) await agentHookServer.close();
|
|
32494
33458
|
await dialer.stop();
|
|
32495
33459
|
}
|
|
@@ -32505,7 +33469,7 @@ function startComputer(config2, deps2 = {}) {
|
|
|
32505
33469
|
dialers: connections.map((c) => c.dialer),
|
|
32506
33470
|
stop: async () => {
|
|
32507
33471
|
hostIdStop?.();
|
|
32508
|
-
if (watchLocalRoots) (0,
|
|
33472
|
+
if (watchLocalRoots) (0, import_node_fs14.unwatchFile)(configFile);
|
|
32509
33473
|
await Promise.all(connections.map((c) => c.dispose()));
|
|
32510
33474
|
}
|
|
32511
33475
|
};
|
|
@@ -32547,7 +33511,7 @@ async function maybeInteractiveSetup() {
|
|
|
32547
33511
|
default: defaultRoot,
|
|
32548
33512
|
transform: (raw) => {
|
|
32549
33513
|
const roots = raw.split(",").map((r) => r.trim()).filter((r) => r.length > 0);
|
|
32550
|
-
return roots.length > 0 ? { config: roots, env: roots.join(
|
|
33514
|
+
return roots.length > 0 ? { config: roots, env: roots.join(import_node_path18.delimiter) } : null;
|
|
32551
33515
|
}
|
|
32552
33516
|
},
|
|
32553
33517
|
{
|
|
@@ -32559,7 +33523,7 @@ async function maybeInteractiveSetup() {
|
|
|
32559
33523
|
configKey: "computerName",
|
|
32560
33524
|
envKey: "SLAYZONE_COMPUTER_NAME",
|
|
32561
33525
|
label: "Computer display name",
|
|
32562
|
-
hint: `default: ${(0,
|
|
33526
|
+
hint: `default: ${(0, import_node_os11.hostname)()}`
|
|
32563
33527
|
}
|
|
32564
33528
|
]
|
|
32565
33529
|
});
|