@slayzone/computer 0.36.0 → 0.36.1
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 +2129 -1052
- package/dist/bin.cjs.map +4 -4
- package/package.json +1 -1
package/dist/bin.cjs
CHANGED
|
@@ -6,11 +6,20 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __getProtoOf = Object.getPrototypeOf;
|
|
8
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
-
var __esm = (fn, res) => function __init() {
|
|
10
|
-
|
|
9
|
+
var __esm = (fn, res, err) => function __init() {
|
|
10
|
+
if (err) throw err[0];
|
|
11
|
+
try {
|
|
12
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
13
|
+
} catch (e) {
|
|
14
|
+
throw err = [e], e;
|
|
15
|
+
}
|
|
11
16
|
};
|
|
12
17
|
var __commonJS = (cb, mod) => function __require() {
|
|
13
|
-
|
|
18
|
+
try {
|
|
19
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
20
|
+
} catch (e) {
|
|
21
|
+
throw mod = 0, e;
|
|
22
|
+
}
|
|
14
23
|
};
|
|
15
24
|
var __export = (target, all) => {
|
|
16
25
|
for (var name in all)
|
|
@@ -98,10 +107,10 @@ var require_constants = __commonJS({
|
|
|
98
107
|
EMPTY_BUFFER: Buffer.alloc(0),
|
|
99
108
|
GUID: "258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
|
|
100
109
|
hasBlob,
|
|
101
|
-
kForOnEventAttribute: Symbol("kIsForOnEventAttribute"),
|
|
102
|
-
kListener: Symbol("kListener"),
|
|
103
|
-
kStatusCode: Symbol("status-code"),
|
|
104
|
-
kWebSocket: Symbol("websocket"),
|
|
110
|
+
kForOnEventAttribute: /* @__PURE__ */ Symbol("kIsForOnEventAttribute"),
|
|
111
|
+
kListener: /* @__PURE__ */ Symbol("kListener"),
|
|
112
|
+
kStatusCode: /* @__PURE__ */ Symbol("status-code"),
|
|
113
|
+
kWebSocket: /* @__PURE__ */ Symbol("websocket"),
|
|
105
114
|
NOOP: () => {
|
|
106
115
|
}
|
|
107
116
|
};
|
|
@@ -187,8 +196,8 @@ var require_buffer_util = __commonJS({
|
|
|
187
196
|
var require_limiter = __commonJS({
|
|
188
197
|
"../../../node_modules/ws/lib/limiter.js"(exports2, module2) {
|
|
189
198
|
"use strict";
|
|
190
|
-
var kDone = Symbol("kDone");
|
|
191
|
-
var kRun = Symbol("kRun");
|
|
199
|
+
var kDone = /* @__PURE__ */ Symbol("kDone");
|
|
200
|
+
var kRun = /* @__PURE__ */ Symbol("kRun");
|
|
192
201
|
var Limiter = class {
|
|
193
202
|
/**
|
|
194
203
|
* Creates a new `Limiter`.
|
|
@@ -243,11 +252,11 @@ var require_permessage_deflate = __commonJS({
|
|
|
243
252
|
var { kStatusCode } = require_constants();
|
|
244
253
|
var FastBuffer = Buffer[Symbol.species];
|
|
245
254
|
var TRAILER = Buffer.from([0, 0, 255, 255]);
|
|
246
|
-
var kPerMessageDeflate = Symbol("permessage-deflate");
|
|
247
|
-
var kTotalLength = Symbol("total-length");
|
|
248
|
-
var kCallback = Symbol("callback");
|
|
249
|
-
var kBuffers = Symbol("buffers");
|
|
250
|
-
var kError = Symbol("error");
|
|
255
|
+
var kPerMessageDeflate = /* @__PURE__ */ Symbol("permessage-deflate");
|
|
256
|
+
var kTotalLength = /* @__PURE__ */ Symbol("total-length");
|
|
257
|
+
var kCallback = /* @__PURE__ */ Symbol("callback");
|
|
258
|
+
var kBuffers = /* @__PURE__ */ Symbol("buffers");
|
|
259
|
+
var kError = /* @__PURE__ */ Symbol("error");
|
|
251
260
|
var zlibLimiter;
|
|
252
261
|
var PerMessageDeflate2 = class {
|
|
253
262
|
/**
|
|
@@ -1453,7 +1462,7 @@ var require_sender = __commonJS({
|
|
|
1453
1462
|
var { EMPTY_BUFFER, kWebSocket, NOOP } = require_constants();
|
|
1454
1463
|
var { isBlob, isValidStatusCode } = require_validation();
|
|
1455
1464
|
var { mask: applyMask, toBuffer } = require_buffer_util();
|
|
1456
|
-
var kByteLength = Symbol("kByteLength");
|
|
1465
|
+
var kByteLength = /* @__PURE__ */ Symbol("kByteLength");
|
|
1457
1466
|
var maskBuffer = Buffer.alloc(4);
|
|
1458
1467
|
var RANDOM_POOL_SIZE = 8 * 1024;
|
|
1459
1468
|
var randomPool;
|
|
@@ -1938,14 +1947,14 @@ var require_event_target = __commonJS({
|
|
|
1938
1947
|
"../../../node_modules/ws/lib/event-target.js"(exports2, module2) {
|
|
1939
1948
|
"use strict";
|
|
1940
1949
|
var { kForOnEventAttribute, kListener } = require_constants();
|
|
1941
|
-
var kCode = Symbol("kCode");
|
|
1942
|
-
var kData = Symbol("kData");
|
|
1943
|
-
var kError = Symbol("kError");
|
|
1944
|
-
var kMessage = Symbol("kMessage");
|
|
1945
|
-
var kReason = Symbol("kReason");
|
|
1946
|
-
var kTarget = Symbol("kTarget");
|
|
1947
|
-
var kType = Symbol("kType");
|
|
1948
|
-
var kWasClean = Symbol("kWasClean");
|
|
1950
|
+
var kCode = /* @__PURE__ */ Symbol("kCode");
|
|
1951
|
+
var kData = /* @__PURE__ */ Symbol("kData");
|
|
1952
|
+
var kError = /* @__PURE__ */ Symbol("kError");
|
|
1953
|
+
var kMessage = /* @__PURE__ */ Symbol("kMessage");
|
|
1954
|
+
var kReason = /* @__PURE__ */ Symbol("kReason");
|
|
1955
|
+
var kTarget = /* @__PURE__ */ Symbol("kTarget");
|
|
1956
|
+
var kType = /* @__PURE__ */ Symbol("kType");
|
|
1957
|
+
var kWasClean = /* @__PURE__ */ Symbol("kWasClean");
|
|
1949
1958
|
var Event = class {
|
|
1950
1959
|
/**
|
|
1951
1960
|
* Create a new `Event`.
|
|
@@ -2347,7 +2356,7 @@ var require_websocket = __commonJS({
|
|
|
2347
2356
|
} = require_event_target();
|
|
2348
2357
|
var { format, parse: parse3 } = require_extension();
|
|
2349
2358
|
var { toBuffer } = require_buffer_util();
|
|
2350
|
-
var kAborted = Symbol("kAborted");
|
|
2359
|
+
var kAborted = /* @__PURE__ */ Symbol("kAborted");
|
|
2351
2360
|
var protocolVersions = [8, 13];
|
|
2352
2361
|
var readyStates = ["CONNECTING", "OPEN", "CLOSING", "CLOSED"];
|
|
2353
2362
|
var subprotocolRegex = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
|
|
@@ -2380,11 +2389,23 @@ var require_websocket = __commonJS({
|
|
|
2380
2389
|
this._isServer = false;
|
|
2381
2390
|
this._redirects = 0;
|
|
2382
2391
|
if (protocols === void 0) {
|
|
2383
|
-
protocols
|
|
2392
|
+
if (!options || options.protocols === void 0) {
|
|
2393
|
+
protocols = [];
|
|
2394
|
+
} else if (Array.isArray(options.protocols)) {
|
|
2395
|
+
protocols = options.protocols;
|
|
2396
|
+
} else {
|
|
2397
|
+
protocols = [options.protocols];
|
|
2398
|
+
}
|
|
2384
2399
|
} else if (!Array.isArray(protocols)) {
|
|
2385
2400
|
if (typeof protocols === "object" && protocols !== null) {
|
|
2386
2401
|
options = protocols;
|
|
2387
|
-
protocols
|
|
2402
|
+
if (options.protocols === void 0) {
|
|
2403
|
+
protocols = [];
|
|
2404
|
+
} else if (Array.isArray(options.protocols)) {
|
|
2405
|
+
protocols = options.protocols;
|
|
2406
|
+
} else {
|
|
2407
|
+
protocols = [options.protocols];
|
|
2408
|
+
}
|
|
2388
2409
|
} else {
|
|
2389
2410
|
protocols = [protocols];
|
|
2390
2411
|
}
|
|
@@ -2581,7 +2602,6 @@ var require_websocket = __commonJS({
|
|
|
2581
2602
|
}
|
|
2582
2603
|
return;
|
|
2583
2604
|
}
|
|
2584
|
-
this._readyState = _WebSocket.CLOSING;
|
|
2585
2605
|
this._sender.close(code, data, !this._isServer, (err) => {
|
|
2586
2606
|
if (err) return;
|
|
2587
2607
|
this._closeFrameSent = true;
|
|
@@ -2589,6 +2609,7 @@ var require_websocket = __commonJS({
|
|
|
2589
2609
|
this._socket.end();
|
|
2590
2610
|
}
|
|
2591
2611
|
});
|
|
2612
|
+
this._readyState = _WebSocket.CLOSING;
|
|
2592
2613
|
setCloseTimer(this);
|
|
2593
2614
|
}
|
|
2594
2615
|
/**
|
|
@@ -2813,6 +2834,7 @@ var require_websocket = __commonJS({
|
|
|
2813
2834
|
socketPath: void 0,
|
|
2814
2835
|
hostname: void 0,
|
|
2815
2836
|
protocol: void 0,
|
|
2837
|
+
protocols: void 0,
|
|
2816
2838
|
timeout: void 0,
|
|
2817
2839
|
method: "GET",
|
|
2818
2840
|
host: void 0,
|
|
@@ -3755,61 +3777,347 @@ var require_websocket_server = __commonJS({
|
|
|
3755
3777
|
}
|
|
3756
3778
|
});
|
|
3757
3779
|
|
|
3758
|
-
//
|
|
3780
|
+
// ../../shared/platform/src/fs-utils.ts
|
|
3781
|
+
async function writeFileIfChanged(filePath, content, mode) {
|
|
3782
|
+
const target = await resolveTarget(filePath);
|
|
3783
|
+
const buf = Buffer.isBuffer(content) ? content : Buffer.from(content, "utf8");
|
|
3784
|
+
const existing = await safeReadFile(target);
|
|
3785
|
+
if (existing && existing.equals(buf)) return false;
|
|
3786
|
+
await import_promises3.default.mkdir(import_path2.default.dirname(target), { recursive: true });
|
|
3787
|
+
const tmp = `${target}.tmp.${import_crypto.default.randomBytes(6).toString("hex")}`;
|
|
3788
|
+
await import_promises3.default.writeFile(tmp, buf);
|
|
3789
|
+
if (mode != null && process.platform !== "win32") {
|
|
3790
|
+
await import_promises3.default.chmod(tmp, mode);
|
|
3791
|
+
}
|
|
3792
|
+
await import_promises3.default.rename(tmp, target);
|
|
3793
|
+
return true;
|
|
3794
|
+
}
|
|
3795
|
+
async function updateFileAtomically(filePath, merge2, opts = {}) {
|
|
3796
|
+
const { mode, retries = 5 } = opts;
|
|
3797
|
+
const target = await resolveTarget(filePath);
|
|
3798
|
+
await import_promises3.default.mkdir(import_path2.default.dirname(target), { recursive: true });
|
|
3799
|
+
const release = await acquireLock(`${target}.lock`);
|
|
3800
|
+
try {
|
|
3801
|
+
for (let attempt = 0; ; attempt++) {
|
|
3802
|
+
const baseline = await safeReadFile(target);
|
|
3803
|
+
const merged = await merge2(baseline);
|
|
3804
|
+
if (merged == null) return false;
|
|
3805
|
+
const buf = Buffer.isBuffer(merged) ? merged : Buffer.from(merged, "utf8");
|
|
3806
|
+
if (baseline && baseline.equals(buf)) return false;
|
|
3807
|
+
const tmp = `${target}.tmp.${import_crypto.default.randomBytes(6).toString("hex")}`;
|
|
3808
|
+
await import_promises3.default.writeFile(tmp, buf);
|
|
3809
|
+
if (mode != null && process.platform !== "win32") {
|
|
3810
|
+
await import_promises3.default.chmod(tmp, mode);
|
|
3811
|
+
}
|
|
3812
|
+
if (sameBytes(baseline, await safeReadFile(target))) {
|
|
3813
|
+
await import_promises3.default.rename(tmp, target);
|
|
3814
|
+
return true;
|
|
3815
|
+
}
|
|
3816
|
+
await import_promises3.default.rm(tmp, { force: true });
|
|
3817
|
+
if (attempt >= retries) {
|
|
3818
|
+
throw new Error(
|
|
3819
|
+
`[fs-utils] updateFileAtomically: ${target} kept changing underneath ${retries + 1} merge attempts \u2014 giving up rather than writing a stale merge`
|
|
3820
|
+
);
|
|
3821
|
+
}
|
|
3822
|
+
await sleep(2 ** attempt + Math.floor(Math.random() * 5));
|
|
3823
|
+
}
|
|
3824
|
+
} finally {
|
|
3825
|
+
await release();
|
|
3826
|
+
}
|
|
3827
|
+
}
|
|
3828
|
+
async function acquireLock(lockPath) {
|
|
3829
|
+
const deadline = Date.now() + LOCK_TIMEOUT_MS;
|
|
3830
|
+
for (; ; ) {
|
|
3831
|
+
try {
|
|
3832
|
+
const handle = await import_promises3.default.open(lockPath, "wx");
|
|
3833
|
+
await handle.writeFile(`${process.pid} ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
3834
|
+
`);
|
|
3835
|
+
await handle.close();
|
|
3836
|
+
return async () => {
|
|
3837
|
+
await import_promises3.default.rm(lockPath, { force: true });
|
|
3838
|
+
};
|
|
3839
|
+
} catch (err) {
|
|
3840
|
+
if (!isCode(err, "EEXIST")) throw err;
|
|
3841
|
+
const age = await lockAgeMs(lockPath);
|
|
3842
|
+
if (age === null) continue;
|
|
3843
|
+
if (age > LOCK_STALE_MS) {
|
|
3844
|
+
await import_promises3.default.rm(lockPath, { force: true });
|
|
3845
|
+
continue;
|
|
3846
|
+
}
|
|
3847
|
+
if (Date.now() > deadline) {
|
|
3848
|
+
throw new Error(`[fs-utils] timed out after ${LOCK_TIMEOUT_MS}ms waiting for ${lockPath}`);
|
|
3849
|
+
}
|
|
3850
|
+
await sleep(5 + Math.floor(Math.random() * 10));
|
|
3851
|
+
}
|
|
3852
|
+
}
|
|
3853
|
+
}
|
|
3854
|
+
async function lockAgeMs(lockPath) {
|
|
3855
|
+
try {
|
|
3856
|
+
return Date.now() - (await import_promises3.default.stat(lockPath)).mtimeMs;
|
|
3857
|
+
} catch (err) {
|
|
3858
|
+
if (isCode(err, "ENOENT")) return null;
|
|
3859
|
+
throw err;
|
|
3860
|
+
}
|
|
3861
|
+
}
|
|
3862
|
+
function sameBytes(a, b) {
|
|
3863
|
+
if (a === null || b === null) return a === b;
|
|
3864
|
+
return a.equals(b);
|
|
3865
|
+
}
|
|
3866
|
+
function sleep(ms) {
|
|
3867
|
+
return new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
3868
|
+
}
|
|
3869
|
+
async function safeReadFile(p) {
|
|
3870
|
+
try {
|
|
3871
|
+
return await import_promises3.default.readFile(p);
|
|
3872
|
+
} catch (err) {
|
|
3873
|
+
if (isENOENT(err)) return null;
|
|
3874
|
+
throw err;
|
|
3875
|
+
}
|
|
3876
|
+
}
|
|
3877
|
+
async function resolveTarget(p) {
|
|
3878
|
+
try {
|
|
3879
|
+
const stat2 = await import_promises3.default.lstat(p);
|
|
3880
|
+
if (stat2.isSymbolicLink()) return await import_promises3.default.realpath(p);
|
|
3881
|
+
} catch (err) {
|
|
3882
|
+
if (!isENOENT(err)) throw err;
|
|
3883
|
+
}
|
|
3884
|
+
return p;
|
|
3885
|
+
}
|
|
3886
|
+
function isENOENT(err) {
|
|
3887
|
+
return isCode(err, "ENOENT");
|
|
3888
|
+
}
|
|
3889
|
+
function isCode(err, code) {
|
|
3890
|
+
return typeof err === "object" && err != null && err.code === code;
|
|
3891
|
+
}
|
|
3892
|
+
var import_promises3, import_path2, import_crypto, LOCK_STALE_MS, LOCK_TIMEOUT_MS;
|
|
3893
|
+
var init_fs_utils = __esm({
|
|
3894
|
+
"../../shared/platform/src/fs-utils.ts"() {
|
|
3895
|
+
"use strict";
|
|
3896
|
+
import_promises3 = __toESM(require("fs/promises"), 1);
|
|
3897
|
+
import_path2 = __toESM(require("path"), 1);
|
|
3898
|
+
import_crypto = __toESM(require("crypto"), 1);
|
|
3899
|
+
LOCK_STALE_MS = 1e4;
|
|
3900
|
+
LOCK_TIMEOUT_MS = 5e3;
|
|
3901
|
+
}
|
|
3902
|
+
});
|
|
3903
|
+
|
|
3904
|
+
// ../../domains/file-editor/node_modules/ignore/index.js
|
|
3759
3905
|
var require_ignore = __commonJS({
|
|
3760
|
-
"
|
|
3906
|
+
"../../domains/file-editor/node_modules/ignore/index.js"(exports2, module2) {
|
|
3761
3907
|
function makeArray(subject) {
|
|
3762
3908
|
return Array.isArray(subject) ? subject : [subject];
|
|
3763
3909
|
}
|
|
3910
|
+
var UNDEFINED = void 0;
|
|
3764
3911
|
var EMPTY = "";
|
|
3765
3912
|
var SPACE = " ";
|
|
3766
3913
|
var ESCAPE = "\\";
|
|
3767
|
-
var
|
|
3914
|
+
var REGEX_LITERAL_SPECIAL = /[.*+?()[\]{}^$|\\/]/;
|
|
3915
|
+
var REGEX_TEST_BLANK_LINE = /^\uFEFF? *$/;
|
|
3768
3916
|
var REGEX_INVALID_TRAILING_BACKSLASH = /(?:[^\\]|^)\\$/;
|
|
3769
3917
|
var REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/;
|
|
3770
3918
|
var REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/;
|
|
3771
3919
|
var REGEX_SPLITALL_CRLF = /\r?\n/g;
|
|
3772
|
-
var
|
|
3920
|
+
var DOUBLE_SLASH = "//";
|
|
3921
|
+
var SLASH_CODE = 47;
|
|
3922
|
+
var DOT_CODE = 46;
|
|
3773
3923
|
var SLASH = "/";
|
|
3774
3924
|
var TMP_KEY_IGNORE = "node-ignore";
|
|
3775
3925
|
if (typeof Symbol !== "undefined") {
|
|
3776
|
-
TMP_KEY_IGNORE = Symbol.for("node-ignore");
|
|
3926
|
+
TMP_KEY_IGNORE = /* @__PURE__ */ Symbol.for("node-ignore");
|
|
3777
3927
|
}
|
|
3778
3928
|
var KEY_IGNORE = TMP_KEY_IGNORE;
|
|
3779
|
-
var define = (object2, key, value) =>
|
|
3780
|
-
|
|
3929
|
+
var define = (object2, key, value) => {
|
|
3930
|
+
Object.defineProperty(object2, key, { value });
|
|
3931
|
+
return value;
|
|
3932
|
+
};
|
|
3781
3933
|
var RETURN_FALSE = () => false;
|
|
3782
|
-
var sanitizeRange = (range) => range.replace(
|
|
3783
|
-
REGEX_REGEXP_RANGE,
|
|
3784
|
-
(match, from, to) => from.charCodeAt(0) <= to.charCodeAt(0) ? match : EMPTY
|
|
3785
|
-
);
|
|
3786
3934
|
var cleanRangeBackSlash = (slashes) => {
|
|
3787
3935
|
const { length } = slashes;
|
|
3788
3936
|
return slashes.slice(0, length - length % 2);
|
|
3789
3937
|
};
|
|
3938
|
+
var POSIX_CLASSES = {
|
|
3939
|
+
alnum: "0-9A-Za-z",
|
|
3940
|
+
alpha: "A-Za-z",
|
|
3941
|
+
blank: " \\t",
|
|
3942
|
+
cntrl: "\\x00-\\x1f\\x7f",
|
|
3943
|
+
digit: "0-9",
|
|
3944
|
+
graph: "!-.0-~",
|
|
3945
|
+
lower: "a-z",
|
|
3946
|
+
print: " -.0-~",
|
|
3947
|
+
punct: "!-.:-@\\[-`{-~",
|
|
3948
|
+
// git's `sane-ctype.h` classifies \v and \f as control, not space,
|
|
3949
|
+
// unlike C's `isspace`
|
|
3950
|
+
space: " \\t\\n\\r",
|
|
3951
|
+
upper: "A-Z",
|
|
3952
|
+
xdigit: "0-9A-Fa-f"
|
|
3953
|
+
};
|
|
3954
|
+
var CLASS_MEMBERS_TO_ESCAPE = "\\]^-[";
|
|
3955
|
+
var escapeMember = (char) => CLASS_MEMBERS_TO_ESCAPE.indexOf(char) < 0 ? char : ESCAPE + char;
|
|
3956
|
+
var NON_SLASH = "(?!\\/)";
|
|
3957
|
+
var classSource = (negated, body) => {
|
|
3958
|
+
if (negated) {
|
|
3959
|
+
return `[^\\/${body}]`;
|
|
3960
|
+
}
|
|
3961
|
+
const source = `[${body}]`;
|
|
3962
|
+
return new RegExp(source).test("/") ? NON_SLASH + source : source;
|
|
3963
|
+
};
|
|
3964
|
+
var scanBracket = (pattern, start) => {
|
|
3965
|
+
const { length } = pattern;
|
|
3966
|
+
let index = start + 1;
|
|
3967
|
+
let negated = EMPTY;
|
|
3968
|
+
const lead = pattern[index];
|
|
3969
|
+
if (lead === "!" || lead === "^") {
|
|
3970
|
+
negated = "^";
|
|
3971
|
+
index++;
|
|
3972
|
+
}
|
|
3973
|
+
let body = EMPTY;
|
|
3974
|
+
let prev = EMPTY;
|
|
3975
|
+
for (; ; ) {
|
|
3976
|
+
const char = pattern[index];
|
|
3977
|
+
if (char === UNDEFINED) {
|
|
3978
|
+
return null;
|
|
3979
|
+
}
|
|
3980
|
+
if (char === ESCAPE) {
|
|
3981
|
+
const escaped = pattern[index + 1];
|
|
3982
|
+
if (escaped === UNDEFINED) {
|
|
3983
|
+
return null;
|
|
3984
|
+
}
|
|
3985
|
+
body += escapeMember(escaped);
|
|
3986
|
+
prev = escaped;
|
|
3987
|
+
index++;
|
|
3988
|
+
} else if (char === "-" && prev && index + 1 < length && pattern[index + 1] !== "]") {
|
|
3989
|
+
index++;
|
|
3990
|
+
let to = pattern[index];
|
|
3991
|
+
if (to === ESCAPE) {
|
|
3992
|
+
to = pattern[index += 1];
|
|
3993
|
+
}
|
|
3994
|
+
if (prev <= to) {
|
|
3995
|
+
body += `-${escapeMember(to)}`;
|
|
3996
|
+
}
|
|
3997
|
+
prev = EMPTY;
|
|
3998
|
+
} else if (char === "[" && pattern[index + 1] === ":") {
|
|
3999
|
+
const nameStart = index + 2;
|
|
4000
|
+
let end = nameStart;
|
|
4001
|
+
while (end < length && pattern[end] !== "]") {
|
|
4002
|
+
end++;
|
|
4003
|
+
}
|
|
4004
|
+
if (end === length) {
|
|
4005
|
+
return null;
|
|
4006
|
+
}
|
|
4007
|
+
if (end > nameStart && pattern[end - 1] === ":") {
|
|
4008
|
+
const expanded = POSIX_CLASSES[pattern.slice(nameStart, end - 1)];
|
|
4009
|
+
if (expanded === UNDEFINED) {
|
|
4010
|
+
return null;
|
|
4011
|
+
}
|
|
4012
|
+
body += expanded;
|
|
4013
|
+
prev = EMPTY;
|
|
4014
|
+
index = end;
|
|
4015
|
+
} else {
|
|
4016
|
+
body += escapeMember("[");
|
|
4017
|
+
prev = "[";
|
|
4018
|
+
index = nameStart - 2;
|
|
4019
|
+
}
|
|
4020
|
+
} else {
|
|
4021
|
+
body += escapeMember(char);
|
|
4022
|
+
prev = char;
|
|
4023
|
+
}
|
|
4024
|
+
index++;
|
|
4025
|
+
if (pattern[index] === "]") {
|
|
4026
|
+
return {
|
|
4027
|
+
end: index,
|
|
4028
|
+
source: classSource(negated, body)
|
|
4029
|
+
};
|
|
4030
|
+
}
|
|
4031
|
+
}
|
|
4032
|
+
};
|
|
4033
|
+
var NEVER_MATCH = "[]";
|
|
4034
|
+
var PLACEHOLDER = "\0";
|
|
4035
|
+
var REGEX_RESTORE_PLACEHOLDER = new RegExp(
|
|
4036
|
+
`${PLACEHOLDER}(\\d+)${PLACEHOLDER}`,
|
|
4037
|
+
"g"
|
|
4038
|
+
);
|
|
4039
|
+
var TRAILING_WILDCARD = "\uE000";
|
|
4040
|
+
var extractBrackets = (pattern) => {
|
|
4041
|
+
const sources = [];
|
|
4042
|
+
const hold = (source) => `${PLACEHOLDER}${sources.push(source) - 1}${PLACEHOLDER}`;
|
|
4043
|
+
const { length } = pattern;
|
|
4044
|
+
let out = EMPTY;
|
|
4045
|
+
let index = 0;
|
|
4046
|
+
while (index < length) {
|
|
4047
|
+
const char = pattern[index];
|
|
4048
|
+
if (char === ESCAPE) {
|
|
4049
|
+
const escaped = pattern[index + 1];
|
|
4050
|
+
if (escaped === "*" || escaped === "[" || escaped === SPACE || escaped === ESCAPE) {
|
|
4051
|
+
out += pattern.slice(index, index + 2);
|
|
4052
|
+
} else {
|
|
4053
|
+
out += hold(
|
|
4054
|
+
REGEX_LITERAL_SPECIAL.test(escaped) ? ESCAPE + escaped : escaped
|
|
4055
|
+
);
|
|
4056
|
+
}
|
|
4057
|
+
index += 2;
|
|
4058
|
+
} else if (char === PLACEHOLDER) {
|
|
4059
|
+
out += hold(`[${PLACEHOLDER}]`);
|
|
4060
|
+
index++;
|
|
4061
|
+
} else if (char === "[") {
|
|
4062
|
+
const scanned = scanBracket(pattern, index);
|
|
4063
|
+
if (scanned === null) {
|
|
4064
|
+
out += hold(NEVER_MATCH);
|
|
4065
|
+
index = length;
|
|
4066
|
+
} else {
|
|
4067
|
+
out += hold(scanned.source);
|
|
4068
|
+
index = scanned.end + 1;
|
|
4069
|
+
}
|
|
4070
|
+
} else {
|
|
4071
|
+
out += char;
|
|
4072
|
+
index++;
|
|
4073
|
+
}
|
|
4074
|
+
}
|
|
4075
|
+
return {
|
|
4076
|
+
source: out,
|
|
4077
|
+
sources
|
|
4078
|
+
};
|
|
4079
|
+
};
|
|
4080
|
+
var DIRECT = null;
|
|
4081
|
+
var REGEX_INNER_SLASH = /\/(?!$)/;
|
|
3790
4082
|
var REPLACERS = [
|
|
3791
4083
|
[
|
|
3792
|
-
//
|
|
4084
|
+
// Remove BOM
|
|
3793
4085
|
// TODO:
|
|
3794
4086
|
// Other similar zero-width characters?
|
|
3795
4087
|
/^\uFEFF/,
|
|
4088
|
+
() => EMPTY,
|
|
4089
|
+
"\uFEFF"
|
|
4090
|
+
],
|
|
4091
|
+
[
|
|
4092
|
+
// A trailing line terminator, left on when a whole file's contents are
|
|
4093
|
+
// added as one pattern rather than split into lines. git never sees one
|
|
4094
|
+
// -- it reads a `.gitignore` line by line -- so it is not part of the
|
|
4095
|
+
// pattern and is dropped here, apart from the trailing-space trimming,
|
|
4096
|
+
// which follows git in touching spaces and nothing else.
|
|
4097
|
+
/[\r\n]+$/,
|
|
3796
4098
|
() => EMPTY
|
|
3797
4099
|
],
|
|
3798
4100
|
// > Trailing spaces are ignored unless they are quoted with backslash ("\")
|
|
3799
4101
|
[
|
|
4102
|
+
// Only spaces, never tabs or other whitespace: git trims a trailing run
|
|
4103
|
+
// of `' '` and nothing else (dir.c, `trim_trailing_spaces`, a single
|
|
4104
|
+
// `case ' '`), so a pattern ending in a tab keeps it as a literal.
|
|
3800
4105
|
// (a\ ) -> (a )
|
|
3801
4106
|
// (a ) -> (a)
|
|
3802
4107
|
// (a ) -> (a)
|
|
3803
4108
|
// (a \ ) -> (a )
|
|
3804
|
-
/((?:\\\\)*?)(
|
|
4109
|
+
/((?:\\\\)*?)(\\? +)$/,
|
|
3805
4110
|
(_, m1, m2) => m1 + (m2.indexOf("\\") === 0 ? SPACE : EMPTY)
|
|
3806
4111
|
],
|
|
3807
|
-
//
|
|
4112
|
+
// Replace (\ ) with ' '
|
|
4113
|
+
// Only a space: an escaped tab or other whitespace is already a literal by
|
|
4114
|
+
// the time it reaches here, and a bare tab must be left as one, not turned
|
|
4115
|
+
// into a space.
|
|
3808
4116
|
// (\ ) -> ' '
|
|
3809
4117
|
// (\\ ) -> '\\ '
|
|
3810
4118
|
// (\\\ ) -> '\\ '
|
|
3811
4119
|
[
|
|
3812
|
-
/(\\+?)
|
|
4120
|
+
/(\\+?) /g,
|
|
3813
4121
|
(_, m1) => {
|
|
3814
4122
|
const { length } = m1;
|
|
3815
4123
|
return m1.slice(0, length - length % 2) + SPACE;
|
|
@@ -3838,7 +4146,8 @@ var require_ignore = __commonJS({
|
|
|
3838
4146
|
[
|
|
3839
4147
|
// > a question mark (?) matches a single character
|
|
3840
4148
|
/(?!\\)\?/g,
|
|
3841
|
-
() => "[^/]"
|
|
4149
|
+
() => "[^/]",
|
|
4150
|
+
"?"
|
|
3842
4151
|
],
|
|
3843
4152
|
// leading slash
|
|
3844
4153
|
[
|
|
@@ -3846,12 +4155,14 @@ var require_ignore = __commonJS({
|
|
|
3846
4155
|
// > For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c".
|
|
3847
4156
|
// A leading slash matches the beginning of the pathname
|
|
3848
4157
|
/^\//,
|
|
3849
|
-
() => "^"
|
|
4158
|
+
() => "^",
|
|
4159
|
+
SLASH
|
|
3850
4160
|
],
|
|
3851
4161
|
// replace special metacharacter slash after the leading slash
|
|
3852
4162
|
[
|
|
3853
4163
|
/\//g,
|
|
3854
|
-
() => "\\/"
|
|
4164
|
+
() => "\\/",
|
|
4165
|
+
SLASH
|
|
3855
4166
|
],
|
|
3856
4167
|
[
|
|
3857
4168
|
// > A leading "**" followed by a slash means match in all directories.
|
|
@@ -3860,18 +4171,23 @@ var require_ignore = __commonJS({
|
|
|
3860
4171
|
// > "**/foo/bar" matches file or directory "bar" anywhere that is directly
|
|
3861
4172
|
// > under directory "foo".
|
|
3862
4173
|
// Notice that the '*'s have been replaced as '\\*'
|
|
3863
|
-
|
|
4174
|
+
/^\^*(?:\\\*\\\*\\\/)+/,
|
|
3864
4175
|
// '**/foo' <-> 'foo'
|
|
3865
|
-
() => "^(?:.*\\/)?"
|
|
4176
|
+
() => "^(?:.*\\/)?",
|
|
4177
|
+
"*"
|
|
3866
4178
|
],
|
|
3867
4179
|
// starting
|
|
3868
4180
|
[
|
|
3869
4181
|
// there will be no leading '/'
|
|
3870
4182
|
// (which has been replaced by section "leading slash")
|
|
3871
4183
|
// If starts with '**', adding a '^' to the regular expression also works
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
4184
|
+
DIRECT,
|
|
4185
|
+
(source, pattern) => {
|
|
4186
|
+
if (!source || source[0] === "^") {
|
|
4187
|
+
return source;
|
|
4188
|
+
}
|
|
4189
|
+
const anchor2 = !REGEX_INNER_SLASH.test(pattern) ? "(?:^|\\/)" : "^";
|
|
4190
|
+
return anchor2 + source;
|
|
3875
4191
|
}
|
|
3876
4192
|
],
|
|
3877
4193
|
// two globstars
|
|
@@ -3881,7 +4197,8 @@ var require_ignore = __commonJS({
|
|
|
3881
4197
|
// Zero, one or several directories
|
|
3882
4198
|
// should not use '*', or it will be replaced by the next replacer
|
|
3883
4199
|
// Check if it is not the last `'/**'`
|
|
3884
|
-
(_, index, str) => index + 6 < str.length ? "(?:\\/[^\\/]+)*" : "\\/.+"
|
|
4200
|
+
(_, index, str) => index + 6 < str.length ? str.slice(index + 6) === "\\/" ? "(?:\\/[^\\/]+)+" : "(?:\\/[^\\/]+)*" : "\\/.+",
|
|
4201
|
+
"*"
|
|
3885
4202
|
],
|
|
3886
4203
|
// normal intermediate wildcards
|
|
3887
4204
|
[
|
|
@@ -3896,32 +4213,56 @@ var require_ignore = __commonJS({
|
|
|
3896
4213
|
(_, p1, p2) => {
|
|
3897
4214
|
const unescaped = p2.replace(/\\\*/g, "[^\\/]*");
|
|
3898
4215
|
return p1 + unescaped;
|
|
3899
|
-
}
|
|
4216
|
+
},
|
|
4217
|
+
"*"
|
|
4218
|
+
],
|
|
4219
|
+
// trailing wildcard, held apart from a literal star
|
|
4220
|
+
[
|
|
4221
|
+
// The step above leaves a trailing `*` alone, so a single `\*` is all that
|
|
4222
|
+
// can be left at the end here. Whether it is a wildcard or a literal
|
|
4223
|
+
// turns on the backslashes the user put in front of it: the escaper has
|
|
4224
|
+
// since doubled every one, so what stands here is those `2N` doubled
|
|
4225
|
+
// backslashes and then the star's own escape. An even number of the
|
|
4226
|
+
// original `N` leaves the star unescaped -- a wildcard -- and an odd
|
|
4227
|
+
// number escapes it -- a literal. This runs while the two are still
|
|
4228
|
+
// distinct, before the unescape steps below collapse the literal onto
|
|
4229
|
+
// the very `\*` a wildcard leaves behind.
|
|
4230
|
+
/(^|[^\\])((?:\\\\)*)\\\*$/,
|
|
4231
|
+
(match, p1, p2) => (
|
|
4232
|
+
// `p2` holds the doubled user backslashes; half of them is `N`.
|
|
4233
|
+
p2.length / 2 % 2 === 0 ? p1 + p2 + TRAILING_WILDCARD : match
|
|
4234
|
+
),
|
|
4235
|
+
"*"
|
|
3900
4236
|
],
|
|
3901
4237
|
[
|
|
3902
4238
|
// unescape, revert step 3 except for back slash
|
|
3903
4239
|
// For example, if a user escape a '\\*',
|
|
3904
4240
|
// after step 3, the result will be '\\\\\\*'
|
|
3905
4241
|
/\\\\\\(?=[$.|*+(){^])/g,
|
|
3906
|
-
() => ESCAPE
|
|
4242
|
+
() => ESCAPE,
|
|
4243
|
+
ESCAPE + ESCAPE
|
|
3907
4244
|
],
|
|
3908
4245
|
[
|
|
3909
4246
|
// '\\\\' -> '\\'
|
|
3910
4247
|
/\\\\/g,
|
|
3911
|
-
() => ESCAPE
|
|
4248
|
+
() => ESCAPE,
|
|
4249
|
+
ESCAPE + ESCAPE
|
|
3912
4250
|
],
|
|
3913
4251
|
[
|
|
3914
|
-
//
|
|
3915
|
-
//
|
|
4252
|
+
// Every real bracket expression -- POSIX classes included -- has already
|
|
4253
|
+
// been held aside by `extractBrackets`, so the only `[` left in the
|
|
4254
|
+
// pattern is an escaped, literal one.
|
|
3916
4255
|
// `\` is escaped by step 3
|
|
3917
|
-
|
|
3918
|
-
|
|
4256
|
+
/\\\[([^\]/]*?)(\\*)($|\])/g,
|
|
4257
|
+
// '\\[bar]' -> '\\\\[bar\\]'
|
|
4258
|
+
(match, range, endEscape, close) => `\\[${range}${cleanRangeBackSlash(endEscape)}${close}`,
|
|
4259
|
+
"["
|
|
3919
4260
|
],
|
|
3920
4261
|
// ending
|
|
3921
4262
|
[
|
|
3922
4263
|
// 'js' will not match 'js.'
|
|
3923
4264
|
// 'ab' will not match 'abc'
|
|
3924
|
-
|
|
4265
|
+
DIRECT,
|
|
3925
4266
|
// WTF!
|
|
3926
4267
|
// https://git-scm.com/docs/gitignore
|
|
3927
4268
|
// changes in [2.22.1](https://git-scm.com/docs/gitignore/2.22.1)
|
|
@@ -3932,70 +4273,320 @@ var require_ignore = __commonJS({
|
|
|
3932
4273
|
// 'js*' will not match 'a.js'
|
|
3933
4274
|
// 'js/' will not match 'a.js'
|
|
3934
4275
|
// 'js' will match 'a.js' and 'a.js/'
|
|
3935
|
-
(
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
const prefix = p1 ? `${p1}[^/]+` : "[^/]*";
|
|
3942
|
-
return `${prefix}(?=$|\\/$)`;
|
|
4276
|
+
(source) => {
|
|
4277
|
+
const last = source[source.length - 1];
|
|
4278
|
+
if (!last || last === TRAILING_WILDCARD) {
|
|
4279
|
+
return source;
|
|
4280
|
+
}
|
|
4281
|
+
return last === SLASH ? `${source}$` : `${source}(?=$|\\/$)`;
|
|
3943
4282
|
}
|
|
3944
4283
|
]
|
|
3945
4284
|
];
|
|
3946
|
-
var
|
|
3947
|
-
var
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
)
|
|
3954
|
-
|
|
4285
|
+
var REGEX_REPLACE_TRAILING_WILDCARD = /(^|\\\/)?\uE000$/;
|
|
4286
|
+
var MODE_IGNORE = "regex";
|
|
4287
|
+
var MODE_CHECK_IGNORE = "checkRegex";
|
|
4288
|
+
var UNDERSCORE = "_";
|
|
4289
|
+
var TRAILING_WILD_CARD_REPLACERS = {
|
|
4290
|
+
[MODE_IGNORE](_, p1) {
|
|
4291
|
+
const prefix = p1 ? `${p1}[^/]+` : "[^/]*";
|
|
4292
|
+
return `${prefix}(?=$|\\/$)`;
|
|
4293
|
+
},
|
|
4294
|
+
[MODE_CHECK_IGNORE](_, p1) {
|
|
4295
|
+
const prefix = p1 ? `${p1}[^/]*` : "[^/]*";
|
|
4296
|
+
return `${prefix}(?=$|\\/$)`;
|
|
4297
|
+
}
|
|
4298
|
+
};
|
|
4299
|
+
var WILDCARD = "[^\\/]*";
|
|
4300
|
+
var separatorAfter = (run, at) => {
|
|
4301
|
+
let separator = EMPTY;
|
|
4302
|
+
for (let index = at + 1; index < run.length && !run[index].wildcard; index++) {
|
|
4303
|
+
separator += run[index].single;
|
|
4304
|
+
}
|
|
4305
|
+
return separator;
|
|
4306
|
+
};
|
|
4307
|
+
var pinWildcards = (source) => {
|
|
4308
|
+
if (source.indexOf(WILDCARD) < 0) {
|
|
4309
|
+
return source;
|
|
4310
|
+
}
|
|
4311
|
+
const tokens = [];
|
|
4312
|
+
const { length } = source;
|
|
4313
|
+
let index = 0;
|
|
4314
|
+
while (index < length) {
|
|
4315
|
+
const char = source[index];
|
|
4316
|
+
if (source.startsWith(WILDCARD, index)) {
|
|
4317
|
+
tokens.push({ wildcard: true });
|
|
4318
|
+
index += WILDCARD.length;
|
|
4319
|
+
} else if (char === "[") {
|
|
4320
|
+
let end = index + 1;
|
|
4321
|
+
if (source[end] === "^") {
|
|
4322
|
+
end++;
|
|
4323
|
+
}
|
|
4324
|
+
if (source[end] === "]") {
|
|
4325
|
+
end++;
|
|
4326
|
+
}
|
|
4327
|
+
while (end < length && source[end] !== "]") {
|
|
4328
|
+
end += source[end] === ESCAPE ? 2 : 1;
|
|
4329
|
+
}
|
|
4330
|
+
end++;
|
|
4331
|
+
tokens.push({ single: source.slice(index, end) });
|
|
4332
|
+
index = end;
|
|
4333
|
+
} else if (char === ESCAPE) {
|
|
4334
|
+
tokens.push({ single: source.slice(index, index + 2) });
|
|
4335
|
+
index += 2;
|
|
4336
|
+
} else if (char === "(") {
|
|
4337
|
+
let depth = 0;
|
|
4338
|
+
let end = index;
|
|
4339
|
+
do {
|
|
4340
|
+
if (source[end] === ESCAPE) {
|
|
4341
|
+
end++;
|
|
4342
|
+
} else if (source[end] === "(") {
|
|
4343
|
+
depth++;
|
|
4344
|
+
} else if (source[end] === ")") {
|
|
4345
|
+
depth--;
|
|
4346
|
+
}
|
|
4347
|
+
end++;
|
|
4348
|
+
} while (end < length && depth > 0);
|
|
4349
|
+
if ("*+?".indexOf(source[end]) >= 0) {
|
|
4350
|
+
end++;
|
|
4351
|
+
}
|
|
4352
|
+
tokens.push({ boundary: source.slice(index, end) });
|
|
4353
|
+
index = end;
|
|
4354
|
+
} else if (char === "^" || char === "$") {
|
|
4355
|
+
tokens.push({ boundary: char });
|
|
4356
|
+
index++;
|
|
4357
|
+
} else {
|
|
4358
|
+
tokens.push({ single: char });
|
|
4359
|
+
index++;
|
|
4360
|
+
}
|
|
4361
|
+
}
|
|
4362
|
+
let out = EMPTY;
|
|
4363
|
+
let run = [];
|
|
4364
|
+
const flush = () => {
|
|
4365
|
+
let lastWildcard;
|
|
4366
|
+
run.forEach((token, at) => {
|
|
4367
|
+
if (token.wildcard) {
|
|
4368
|
+
lastWildcard = at;
|
|
4369
|
+
}
|
|
4370
|
+
});
|
|
4371
|
+
run.forEach((token, at) => {
|
|
4372
|
+
if (!token.wildcard) {
|
|
4373
|
+
out += token.single;
|
|
4374
|
+
return;
|
|
4375
|
+
}
|
|
4376
|
+
out += at === lastWildcard ? WILDCARD : `(?:(?!${separatorAfter(run, at)})[^\\/])*`;
|
|
4377
|
+
});
|
|
4378
|
+
run = [];
|
|
4379
|
+
};
|
|
4380
|
+
tokens.forEach((token) => {
|
|
4381
|
+
if (token.boundary === void 0) {
|
|
4382
|
+
run.push(token);
|
|
4383
|
+
return;
|
|
4384
|
+
}
|
|
4385
|
+
flush();
|
|
4386
|
+
out += token.boundary;
|
|
4387
|
+
});
|
|
4388
|
+
flush();
|
|
4389
|
+
return out;
|
|
4390
|
+
};
|
|
4391
|
+
var makeRegexPrefix = (pattern) => {
|
|
4392
|
+
const { source, sources } = extractBrackets(pattern);
|
|
4393
|
+
const replaced = REPLACERS.reduce(
|
|
4394
|
+
// A pass whose matcher finds nothing hands back the very string it was
|
|
4395
|
+
// given, so asking first costs a search and saves a rewrite. Ten of the
|
|
4396
|
+
// fifteen passes never fire for a typical .gitignore line, and between
|
|
4397
|
+
// them they were 45% of this chain.
|
|
4398
|
+
(prev, [matcher, replacer, required2]) => {
|
|
4399
|
+
if (matcher === DIRECT) {
|
|
4400
|
+
return replacer(prev, pattern);
|
|
4401
|
+
}
|
|
4402
|
+
if (required2 !== UNDEFINED && prev.indexOf(required2) < 0) {
|
|
4403
|
+
return prev;
|
|
4404
|
+
}
|
|
4405
|
+
return matcher.test(prev) ? prev.replace(matcher, replacer.bind(pattern)) : prev;
|
|
4406
|
+
},
|
|
4407
|
+
source
|
|
4408
|
+
);
|
|
4409
|
+
return sources.length ? replaced.replace(
|
|
4410
|
+
REGEX_RESTORE_PLACEHOLDER,
|
|
4411
|
+
(match, index) => sources[index]
|
|
4412
|
+
) : replaced;
|
|
4413
|
+
};
|
|
4414
|
+
var matchesBasename = (body) => {
|
|
4415
|
+
const index = body.indexOf(SLASH);
|
|
4416
|
+
return index < 0 || index === body.length - 1;
|
|
4417
|
+
};
|
|
4418
|
+
var basenameOf = (path9) => {
|
|
4419
|
+
const end = path9.length - 1;
|
|
4420
|
+
const index = path9.lastIndexOf(
|
|
4421
|
+
SLASH,
|
|
4422
|
+
path9[end] === SLASH ? end - 1 : end
|
|
4423
|
+
);
|
|
4424
|
+
return index < 0 ? path9 : path9.slice(index + 1);
|
|
4425
|
+
};
|
|
4426
|
+
var parentOf = (path9) => {
|
|
4427
|
+
if (path9.charCodeAt(0) === SLASH_CODE || path9.indexOf(DOUBLE_SLASH) >= 0) {
|
|
4428
|
+
const slices = path9.split(SLASH).filter(Boolean);
|
|
4429
|
+
slices.pop();
|
|
4430
|
+
return slices.length ? slices.join(SLASH) + SLASH : EMPTY;
|
|
3955
4431
|
}
|
|
3956
|
-
|
|
4432
|
+
const end = path9.length - 1;
|
|
4433
|
+
const cut = path9.lastIndexOf(
|
|
4434
|
+
SLASH,
|
|
4435
|
+
path9.charCodeAt(end) === SLASH_CODE ? end - 1 : end
|
|
4436
|
+
);
|
|
4437
|
+
return cut < 0 ? EMPTY : path9.slice(0, cut + 1);
|
|
3957
4438
|
};
|
|
3958
4439
|
var isString = (subject) => typeof subject === "string";
|
|
3959
4440
|
var checkPattern = (pattern) => pattern && isString(pattern) && !REGEX_TEST_BLANK_LINE.test(pattern) && !REGEX_INVALID_TRAILING_BACKSLASH.test(pattern) && pattern.indexOf("#") !== 0;
|
|
3960
|
-
var splitPattern = (pattern) => pattern.split(REGEX_SPLITALL_CRLF);
|
|
4441
|
+
var splitPattern = (pattern) => pattern.split(REGEX_SPLITALL_CRLF).filter(Boolean);
|
|
3961
4442
|
var IgnoreRule = class {
|
|
3962
|
-
constructor(
|
|
3963
|
-
this.origin = origin;
|
|
4443
|
+
constructor(pattern, mark, body, ignoreCase, negative, prefix) {
|
|
3964
4444
|
this.pattern = pattern;
|
|
4445
|
+
this.mark = mark;
|
|
3965
4446
|
this.negative = negative;
|
|
3966
|
-
this
|
|
4447
|
+
define(this, "body", body);
|
|
4448
|
+
define(this, "ignoreCase", ignoreCase);
|
|
4449
|
+
define(this, "regexPrefix", prefix);
|
|
4450
|
+
}
|
|
4451
|
+
// Worked out on first use and kept behind an own property, the way `regex`
|
|
4452
|
+
// caches itself in `_regex`. Deciding it in the constructor instead would
|
|
4453
|
+
// add a fourth `defineProperty` to every rule ever built, which cost 4% of
|
|
4454
|
+
// every compile -- including the compiles of rules that are never matched
|
|
4455
|
+
// against anything.
|
|
4456
|
+
get _basenameOnly() {
|
|
4457
|
+
return define(this, "_basenameOnly", matchesBasename(this.body));
|
|
4458
|
+
}
|
|
4459
|
+
get regex() {
|
|
4460
|
+
const key = UNDERSCORE + MODE_IGNORE;
|
|
4461
|
+
if (this[key]) {
|
|
4462
|
+
return this[key];
|
|
4463
|
+
}
|
|
4464
|
+
return this._make(MODE_IGNORE, key);
|
|
4465
|
+
}
|
|
4466
|
+
get checkRegex() {
|
|
4467
|
+
const key = UNDERSCORE + MODE_CHECK_IGNORE;
|
|
4468
|
+
if (this[key]) {
|
|
4469
|
+
return this[key];
|
|
4470
|
+
}
|
|
4471
|
+
return this._make(MODE_CHECK_IGNORE, key);
|
|
4472
|
+
}
|
|
4473
|
+
_make(mode, key) {
|
|
4474
|
+
const str = pinWildcards(this.regexPrefix.replace(
|
|
4475
|
+
REGEX_REPLACE_TRAILING_WILDCARD,
|
|
4476
|
+
// It does not need to bind pattern
|
|
4477
|
+
TRAILING_WILD_CARD_REPLACERS[mode]
|
|
4478
|
+
));
|
|
4479
|
+
const regex = this.ignoreCase ? new RegExp(str, "i") : new RegExp(str);
|
|
4480
|
+
return define(this, key, regex);
|
|
3967
4481
|
}
|
|
3968
4482
|
};
|
|
3969
|
-
var createRule = (
|
|
3970
|
-
|
|
4483
|
+
var createRule = ({
|
|
4484
|
+
pattern,
|
|
4485
|
+
mark
|
|
4486
|
+
}, ignoreCase) => {
|
|
3971
4487
|
let negative = false;
|
|
3972
|
-
|
|
4488
|
+
let body = pattern;
|
|
4489
|
+
if (body.indexOf("!") === 0) {
|
|
3973
4490
|
negative = true;
|
|
3974
|
-
|
|
4491
|
+
body = body.substr(1);
|
|
3975
4492
|
}
|
|
3976
|
-
|
|
3977
|
-
const
|
|
4493
|
+
body = body.replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION, "!").replace(REGEX_REPLACE_LEADING_EXCAPED_HASH, "#");
|
|
4494
|
+
const regexPrefix = makeRegexPrefix(body);
|
|
3978
4495
|
return new IgnoreRule(
|
|
3979
|
-
origin,
|
|
3980
4496
|
pattern,
|
|
4497
|
+
mark,
|
|
4498
|
+
body,
|
|
4499
|
+
ignoreCase,
|
|
3981
4500
|
negative,
|
|
3982
|
-
|
|
4501
|
+
regexPrefix
|
|
3983
4502
|
);
|
|
3984
4503
|
};
|
|
4504
|
+
var RuleManager = class {
|
|
4505
|
+
constructor(ignoreCase) {
|
|
4506
|
+
this._ignoreCase = ignoreCase;
|
|
4507
|
+
this._rules = [];
|
|
4508
|
+
this._basenameCount = 0;
|
|
4509
|
+
}
|
|
4510
|
+
_add(pattern) {
|
|
4511
|
+
if (pattern && pattern[KEY_IGNORE]) {
|
|
4512
|
+
this._rules = this._rules.concat(pattern._rules._rules);
|
|
4513
|
+
this._basenameCount += pattern._rules._basenameCount;
|
|
4514
|
+
this._added = true;
|
|
4515
|
+
return;
|
|
4516
|
+
}
|
|
4517
|
+
if (isString(pattern)) {
|
|
4518
|
+
pattern = {
|
|
4519
|
+
pattern
|
|
4520
|
+
};
|
|
4521
|
+
}
|
|
4522
|
+
if (checkPattern(pattern.pattern)) {
|
|
4523
|
+
const rule = createRule(pattern, this._ignoreCase);
|
|
4524
|
+
this._added = true;
|
|
4525
|
+
this._rules.push(rule);
|
|
4526
|
+
if (matchesBasename(rule.body)) {
|
|
4527
|
+
this._basenameCount++;
|
|
4528
|
+
}
|
|
4529
|
+
}
|
|
4530
|
+
}
|
|
4531
|
+
// @param {Array<string> | string | Ignore} pattern
|
|
4532
|
+
add(pattern) {
|
|
4533
|
+
this._added = false;
|
|
4534
|
+
makeArray(
|
|
4535
|
+
isString(pattern) ? splitPattern(pattern) : pattern
|
|
4536
|
+
).forEach(this._add, this);
|
|
4537
|
+
return this._added;
|
|
4538
|
+
}
|
|
4539
|
+
// Test one single path without recursively checking parent directories
|
|
4540
|
+
//
|
|
4541
|
+
// - checkUnignored `boolean` whether should check if the path is unignored,
|
|
4542
|
+
// setting `checkUnignored` to `false` could reduce additional
|
|
4543
|
+
// path matching.
|
|
4544
|
+
// - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
|
|
4545
|
+
// @returns {TestResult} true if a file is ignored
|
|
4546
|
+
test(path9, checkUnignored, mode) {
|
|
4547
|
+
let ignored = false;
|
|
4548
|
+
let unignored = false;
|
|
4549
|
+
let matchedRule;
|
|
4550
|
+
const rules = this._rules;
|
|
4551
|
+
const { length } = rules;
|
|
4552
|
+
const shortcut = this._basenameCount * 2 >= length;
|
|
4553
|
+
const basename5 = shortcut ? basenameOf(path9) : path9;
|
|
4554
|
+
for (let index = 0; index < length; index++) {
|
|
4555
|
+
const rule = rules[index];
|
|
4556
|
+
const { negative } = rule;
|
|
4557
|
+
const skip = unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored;
|
|
4558
|
+
if (!skip && rule[mode].test(
|
|
4559
|
+
shortcut && rule._basenameOnly ? basename5 : path9
|
|
4560
|
+
)) {
|
|
4561
|
+
ignored = !negative;
|
|
4562
|
+
unignored = negative;
|
|
4563
|
+
matchedRule = negative ? UNDEFINED : rule;
|
|
4564
|
+
}
|
|
4565
|
+
}
|
|
4566
|
+
const ret = {
|
|
4567
|
+
ignored,
|
|
4568
|
+
unignored
|
|
4569
|
+
};
|
|
4570
|
+
if (matchedRule) {
|
|
4571
|
+
ret.rule = matchedRule;
|
|
4572
|
+
}
|
|
4573
|
+
return ret;
|
|
4574
|
+
}
|
|
4575
|
+
};
|
|
3985
4576
|
var throwError = (message, Ctor) => {
|
|
3986
4577
|
throw new Ctor(message);
|
|
3987
4578
|
};
|
|
3988
|
-
var checkPath = (
|
|
3989
|
-
if (!isString(
|
|
4579
|
+
var checkPath = (path9, originalPath, doThrow) => {
|
|
4580
|
+
if (!isString(path9)) {
|
|
3990
4581
|
return doThrow(
|
|
3991
4582
|
`path must be a string, but got \`${originalPath}\``,
|
|
3992
4583
|
TypeError
|
|
3993
4584
|
);
|
|
3994
4585
|
}
|
|
3995
|
-
if (!
|
|
4586
|
+
if (!path9) {
|
|
3996
4587
|
return doThrow(`path must not be empty`, TypeError);
|
|
3997
4588
|
}
|
|
3998
|
-
if (checkPath.isNotRelative(
|
|
4589
|
+
if (checkPath.isNotRelative(path9)) {
|
|
3999
4590
|
const r = "`path.relative()`d";
|
|
4000
4591
|
return doThrow(
|
|
4001
4592
|
`path should be a ${r} string, but got "${originalPath}"`,
|
|
@@ -4004,7 +4595,26 @@ var require_ignore = __commonJS({
|
|
|
4004
4595
|
}
|
|
4005
4596
|
return true;
|
|
4006
4597
|
};
|
|
4007
|
-
var isNotRelative = (
|
|
4598
|
+
var isNotRelative = (path9) => {
|
|
4599
|
+
const first = path9.charCodeAt(0);
|
|
4600
|
+
if (first === SLASH_CODE) {
|
|
4601
|
+
return true;
|
|
4602
|
+
}
|
|
4603
|
+
if (first !== DOT_CODE) {
|
|
4604
|
+
return false;
|
|
4605
|
+
}
|
|
4606
|
+
if (path9.length === 1) {
|
|
4607
|
+
return true;
|
|
4608
|
+
}
|
|
4609
|
+
const second = path9.charCodeAt(1);
|
|
4610
|
+
if (second === SLASH_CODE) {
|
|
4611
|
+
return true;
|
|
4612
|
+
}
|
|
4613
|
+
if (second !== DOT_CODE) {
|
|
4614
|
+
return false;
|
|
4615
|
+
}
|
|
4616
|
+
return path9.length === 2 || path9.charCodeAt(2) === SLASH_CODE;
|
|
4617
|
+
};
|
|
4008
4618
|
checkPath.isNotRelative = isNotRelative;
|
|
4009
4619
|
checkPath.convert = (p) => p;
|
|
4010
4620
|
var Ignore = class {
|
|
@@ -4014,255 +4624,87 @@ var require_ignore = __commonJS({
|
|
|
4014
4624
|
allowRelativePaths = false
|
|
4015
4625
|
} = {}) {
|
|
4016
4626
|
define(this, KEY_IGNORE, true);
|
|
4017
|
-
this._rules =
|
|
4018
|
-
this.
|
|
4019
|
-
this._allowRelativePaths = allowRelativePaths;
|
|
4627
|
+
this._rules = new RuleManager(ignoreCase);
|
|
4628
|
+
this._strictPathCheck = !allowRelativePaths;
|
|
4020
4629
|
this._initCache();
|
|
4021
4630
|
}
|
|
4022
4631
|
_initCache() {
|
|
4023
4632
|
this._ignoreCache = /* @__PURE__ */ Object.create(null);
|
|
4024
4633
|
this._testCache = /* @__PURE__ */ Object.create(null);
|
|
4025
4634
|
}
|
|
4026
|
-
|
|
4027
|
-
if (pattern
|
|
4028
|
-
this.
|
|
4029
|
-
this._added = true;
|
|
4030
|
-
return;
|
|
4031
|
-
}
|
|
4032
|
-
if (checkPattern(pattern)) {
|
|
4033
|
-
const rule = createRule(pattern, this._ignoreCase);
|
|
4034
|
-
this._added = true;
|
|
4035
|
-
this._rules.push(rule);
|
|
4635
|
+
add(pattern) {
|
|
4636
|
+
if (this._rules.add(pattern)) {
|
|
4637
|
+
this._initCache();
|
|
4036
4638
|
}
|
|
4037
|
-
|
|
4038
|
-
// @param {Array<string> | string | Ignore} pattern
|
|
4039
|
-
add(pattern) {
|
|
4040
|
-
this._added = false;
|
|
4041
|
-
makeArray(
|
|
4042
|
-
isString(pattern) ? splitPattern(pattern) : pattern
|
|
4043
|
-
).forEach(this._addPattern, this);
|
|
4044
|
-
if (this._added) {
|
|
4045
|
-
this._initCache();
|
|
4046
|
-
}
|
|
4047
|
-
return this;
|
|
4639
|
+
return this;
|
|
4048
4640
|
}
|
|
4049
4641
|
// legacy
|
|
4050
4642
|
addPattern(pattern) {
|
|
4051
4643
|
return this.add(pattern);
|
|
4052
4644
|
}
|
|
4053
|
-
// | ignored : unignored
|
|
4054
|
-
// negative | 0:0 | 0:1 | 1:0 | 1:1
|
|
4055
|
-
// -------- | ------- | ------- | ------- | --------
|
|
4056
|
-
// 0 | TEST | TEST | SKIP | X
|
|
4057
|
-
// 1 | TESTIF | SKIP | TEST | X
|
|
4058
|
-
// - SKIP: always skip
|
|
4059
|
-
// - TEST: always test
|
|
4060
|
-
// - TESTIF: only test if checkUnignored
|
|
4061
|
-
// - X: that never happen
|
|
4062
|
-
// @param {boolean} whether should check if the path is unignored,
|
|
4063
|
-
// setting `checkUnignored` to `false` could reduce additional
|
|
4064
|
-
// path matching.
|
|
4065
|
-
// @returns {TestResult} true if a file is ignored
|
|
4066
|
-
_testOne(path8, checkUnignored) {
|
|
4067
|
-
let ignored = false;
|
|
4068
|
-
let unignored = false;
|
|
4069
|
-
this._rules.forEach((rule) => {
|
|
4070
|
-
const { negative } = rule;
|
|
4071
|
-
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
|
|
4072
|
-
return;
|
|
4073
|
-
}
|
|
4074
|
-
const matched = rule.regex.test(path8);
|
|
4075
|
-
if (matched) {
|
|
4076
|
-
ignored = !negative;
|
|
4077
|
-
unignored = negative;
|
|
4078
|
-
}
|
|
4079
|
-
});
|
|
4080
|
-
return {
|
|
4081
|
-
ignored,
|
|
4082
|
-
unignored
|
|
4083
|
-
};
|
|
4084
|
-
}
|
|
4085
4645
|
// @returns {TestResult}
|
|
4086
|
-
_test(originalPath, cache, checkUnignored
|
|
4087
|
-
const
|
|
4646
|
+
_test(originalPath, cache, checkUnignored) {
|
|
4647
|
+
const path9 = originalPath && checkPath.convert(originalPath);
|
|
4088
4648
|
checkPath(
|
|
4089
|
-
|
|
4649
|
+
path9,
|
|
4090
4650
|
originalPath,
|
|
4091
|
-
this.
|
|
4651
|
+
this._strictPathCheck ? throwError : RETURN_FALSE
|
|
4092
4652
|
);
|
|
4093
|
-
return this._t(
|
|
4653
|
+
return this._t(path9, cache, checkUnignored);
|
|
4094
4654
|
}
|
|
4095
|
-
|
|
4096
|
-
if (
|
|
4097
|
-
return
|
|
4655
|
+
checkIgnore(path9) {
|
|
4656
|
+
if (path9.charCodeAt(path9.length - 1) !== SLASH_CODE) {
|
|
4657
|
+
return this.test(path9);
|
|
4098
4658
|
}
|
|
4099
|
-
|
|
4100
|
-
|
|
4659
|
+
const parentPath = parentOf(path9);
|
|
4660
|
+
if (parentPath) {
|
|
4661
|
+
const parent = this._t(parentPath, this._testCache, true);
|
|
4662
|
+
if (parent.ignored) {
|
|
4663
|
+
return parent;
|
|
4664
|
+
}
|
|
4101
4665
|
}
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
);
|
|
4112
|
-
return cache[path8] = parent.ignored ? parent : this._testOne(path8, checkUnignored);
|
|
4666
|
+
return this._rules.test(path9, false, MODE_CHECK_IGNORE);
|
|
4667
|
+
}
|
|
4668
|
+
_t(path9, cache, checkUnignored) {
|
|
4669
|
+
if (path9 in cache) {
|
|
4670
|
+
return cache[path9];
|
|
4671
|
+
}
|
|
4672
|
+
const parentPath = parentOf(path9);
|
|
4673
|
+
const parent = parentPath ? this._t(parentPath, cache, checkUnignored) : UNDEFINED;
|
|
4674
|
+
return cache[path9] = parent && parent.ignored ? parent : this._rules.test(path9, checkUnignored, MODE_IGNORE);
|
|
4113
4675
|
}
|
|
4114
|
-
ignores(
|
|
4115
|
-
return this._test(
|
|
4676
|
+
ignores(path9) {
|
|
4677
|
+
return this._test(path9, this._ignoreCache, false).ignored;
|
|
4116
4678
|
}
|
|
4117
4679
|
createFilter() {
|
|
4118
|
-
return (
|
|
4680
|
+
return (path9) => !this.ignores(path9);
|
|
4119
4681
|
}
|
|
4120
4682
|
filter(paths) {
|
|
4121
4683
|
return makeArray(paths).filter(this.createFilter());
|
|
4122
4684
|
}
|
|
4123
4685
|
// @returns {TestResult}
|
|
4124
|
-
test(
|
|
4125
|
-
return this._test(
|
|
4686
|
+
test(path9) {
|
|
4687
|
+
return this._test(path9, this._testCache, true);
|
|
4126
4688
|
}
|
|
4127
4689
|
};
|
|
4128
4690
|
var factory = (options) => new Ignore(options);
|
|
4129
|
-
var isPathValid = (
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4691
|
+
var isPathValid = (path9) => checkPath(path9 && checkPath.convert(path9), path9, RETURN_FALSE);
|
|
4692
|
+
var setupWindows = () => {
|
|
4693
|
+
const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
|
|
4694
|
+
checkPath.convert = makePosix;
|
|
4695
|
+
const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
4696
|
+
checkPath.isNotRelative = (path9) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path9) || isNotRelative(path9);
|
|
4697
|
+
};
|
|
4133
4698
|
if (
|
|
4134
4699
|
// Detect `process` so that it can run in browsers.
|
|
4135
|
-
typeof process !== "undefined" &&
|
|
4700
|
+
typeof process !== "undefined" && process.platform === "win32"
|
|
4136
4701
|
) {
|
|
4137
|
-
|
|
4138
|
-
checkPath.convert = makePosix;
|
|
4139
|
-
const REGIX_IS_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
4140
|
-
checkPath.isNotRelative = (path8) => REGIX_IS_WINDOWS_PATH_ABSOLUTE.test(path8) || isNotRelative(path8);
|
|
4141
|
-
}
|
|
4142
|
-
}
|
|
4143
|
-
});
|
|
4144
|
-
|
|
4145
|
-
// ../../shared/platform/src/fs-utils.ts
|
|
4146
|
-
async function writeFileIfChanged(filePath, content, mode) {
|
|
4147
|
-
const target = await resolveTarget(filePath);
|
|
4148
|
-
const buf = Buffer.isBuffer(content) ? content : Buffer.from(content, "utf8");
|
|
4149
|
-
const existing = await safeReadFile(target);
|
|
4150
|
-
if (existing && existing.equals(buf)) return false;
|
|
4151
|
-
await import_promises7.default.mkdir(import_path2.default.dirname(target), { recursive: true });
|
|
4152
|
-
const tmp = `${target}.tmp.${import_crypto.default.randomBytes(6).toString("hex")}`;
|
|
4153
|
-
await import_promises7.default.writeFile(tmp, buf);
|
|
4154
|
-
if (mode != null && process.platform !== "win32") {
|
|
4155
|
-
await import_promises7.default.chmod(tmp, mode);
|
|
4156
|
-
}
|
|
4157
|
-
await import_promises7.default.rename(tmp, target);
|
|
4158
|
-
return true;
|
|
4159
|
-
}
|
|
4160
|
-
async function updateFileAtomically(filePath, merge2, opts = {}) {
|
|
4161
|
-
const { mode, retries = 5 } = opts;
|
|
4162
|
-
const target = await resolveTarget(filePath);
|
|
4163
|
-
await import_promises7.default.mkdir(import_path2.default.dirname(target), { recursive: true });
|
|
4164
|
-
const release = await acquireLock(`${target}.lock`);
|
|
4165
|
-
try {
|
|
4166
|
-
for (let attempt = 0; ; attempt++) {
|
|
4167
|
-
const baseline = await safeReadFile(target);
|
|
4168
|
-
const merged = await merge2(baseline);
|
|
4169
|
-
if (merged == null) return false;
|
|
4170
|
-
const buf = Buffer.isBuffer(merged) ? merged : Buffer.from(merged, "utf8");
|
|
4171
|
-
if (baseline && baseline.equals(buf)) return false;
|
|
4172
|
-
const tmp = `${target}.tmp.${import_crypto.default.randomBytes(6).toString("hex")}`;
|
|
4173
|
-
await import_promises7.default.writeFile(tmp, buf);
|
|
4174
|
-
if (mode != null && process.platform !== "win32") {
|
|
4175
|
-
await import_promises7.default.chmod(tmp, mode);
|
|
4176
|
-
}
|
|
4177
|
-
if (sameBytes(baseline, await safeReadFile(target))) {
|
|
4178
|
-
await import_promises7.default.rename(tmp, target);
|
|
4179
|
-
return true;
|
|
4180
|
-
}
|
|
4181
|
-
await import_promises7.default.rm(tmp, { force: true });
|
|
4182
|
-
if (attempt >= retries) {
|
|
4183
|
-
throw new Error(
|
|
4184
|
-
`[fs-utils] updateFileAtomically: ${target} kept changing underneath ${retries + 1} merge attempts \u2014 giving up rather than writing a stale merge`
|
|
4185
|
-
);
|
|
4186
|
-
}
|
|
4187
|
-
await sleep(2 ** attempt + Math.floor(Math.random() * 5));
|
|
4188
|
-
}
|
|
4189
|
-
} finally {
|
|
4190
|
-
await release();
|
|
4191
|
-
}
|
|
4192
|
-
}
|
|
4193
|
-
async function acquireLock(lockPath) {
|
|
4194
|
-
const deadline = Date.now() + LOCK_TIMEOUT_MS;
|
|
4195
|
-
for (; ; ) {
|
|
4196
|
-
try {
|
|
4197
|
-
const handle = await import_promises7.default.open(lockPath, "wx");
|
|
4198
|
-
await handle.writeFile(`${process.pid} ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
4199
|
-
`);
|
|
4200
|
-
await handle.close();
|
|
4201
|
-
return async () => {
|
|
4202
|
-
await import_promises7.default.rm(lockPath, { force: true });
|
|
4203
|
-
};
|
|
4204
|
-
} catch (err) {
|
|
4205
|
-
if (!isCode(err, "EEXIST")) throw err;
|
|
4206
|
-
const age = await lockAgeMs(lockPath);
|
|
4207
|
-
if (age === null) continue;
|
|
4208
|
-
if (age > LOCK_STALE_MS) {
|
|
4209
|
-
await import_promises7.default.rm(lockPath, { force: true });
|
|
4210
|
-
continue;
|
|
4211
|
-
}
|
|
4212
|
-
if (Date.now() > deadline) {
|
|
4213
|
-
throw new Error(`[fs-utils] timed out after ${LOCK_TIMEOUT_MS}ms waiting for ${lockPath}`);
|
|
4214
|
-
}
|
|
4215
|
-
await sleep(5 + Math.floor(Math.random() * 10));
|
|
4702
|
+
setupWindows();
|
|
4216
4703
|
}
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
return Date.now() - (await import_promises7.default.stat(lockPath)).mtimeMs;
|
|
4222
|
-
} catch (err) {
|
|
4223
|
-
if (isCode(err, "ENOENT")) return null;
|
|
4224
|
-
throw err;
|
|
4225
|
-
}
|
|
4226
|
-
}
|
|
4227
|
-
function sameBytes(a, b) {
|
|
4228
|
-
if (a === null || b === null) return a === b;
|
|
4229
|
-
return a.equals(b);
|
|
4230
|
-
}
|
|
4231
|
-
function sleep(ms) {
|
|
4232
|
-
return new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
4233
|
-
}
|
|
4234
|
-
async function safeReadFile(p) {
|
|
4235
|
-
try {
|
|
4236
|
-
return await import_promises7.default.readFile(p);
|
|
4237
|
-
} catch (err) {
|
|
4238
|
-
if (isENOENT(err)) return null;
|
|
4239
|
-
throw err;
|
|
4240
|
-
}
|
|
4241
|
-
}
|
|
4242
|
-
async function resolveTarget(p) {
|
|
4243
|
-
try {
|
|
4244
|
-
const stat2 = await import_promises7.default.lstat(p);
|
|
4245
|
-
if (stat2.isSymbolicLink()) return await import_promises7.default.realpath(p);
|
|
4246
|
-
} catch (err) {
|
|
4247
|
-
if (!isENOENT(err)) throw err;
|
|
4248
|
-
}
|
|
4249
|
-
return p;
|
|
4250
|
-
}
|
|
4251
|
-
function isENOENT(err) {
|
|
4252
|
-
return isCode(err, "ENOENT");
|
|
4253
|
-
}
|
|
4254
|
-
function isCode(err, code) {
|
|
4255
|
-
return typeof err === "object" && err != null && err.code === code;
|
|
4256
|
-
}
|
|
4257
|
-
var import_promises7, import_path2, import_crypto, LOCK_STALE_MS, LOCK_TIMEOUT_MS;
|
|
4258
|
-
var init_fs_utils = __esm({
|
|
4259
|
-
"../../shared/platform/src/fs-utils.ts"() {
|
|
4260
|
-
"use strict";
|
|
4261
|
-
import_promises7 = __toESM(require("fs/promises"), 1);
|
|
4262
|
-
import_path2 = __toESM(require("path"), 1);
|
|
4263
|
-
import_crypto = __toESM(require("crypto"), 1);
|
|
4264
|
-
LOCK_STALE_MS = 1e4;
|
|
4265
|
-
LOCK_TIMEOUT_MS = 5e3;
|
|
4704
|
+
module2.exports = factory;
|
|
4705
|
+
factory.default = factory;
|
|
4706
|
+
module2.exports.isPathValid = isPathValid;
|
|
4707
|
+
define(module2.exports, /* @__PURE__ */ Symbol.for("setupWindows"), setupWindows);
|
|
4266
4708
|
}
|
|
4267
4709
|
});
|
|
4268
4710
|
|
|
@@ -4412,7 +4854,7 @@ var init_claude_hook_installer = __esm({
|
|
|
4412
4854
|
// ../../shared/platform/src/agent-hooks/gemini-hook-installer.ts
|
|
4413
4855
|
async function isGeminiInstalled() {
|
|
4414
4856
|
try {
|
|
4415
|
-
await
|
|
4857
|
+
await execFileAsync3("gemini", ["--version"], { timeout: 2e3 });
|
|
4416
4858
|
return true;
|
|
4417
4859
|
} catch {
|
|
4418
4860
|
return false;
|
|
@@ -4493,16 +4935,16 @@ function isManagedSlayzoneHook2(hook) {
|
|
|
4493
4935
|
const cmd = typeof h.command === "string" ? h.command : "";
|
|
4494
4936
|
return cmd.includes(".slayzone/hooks/notify.sh") || cmd.includes("/slayzone/hooks/notify.sh");
|
|
4495
4937
|
}
|
|
4496
|
-
var
|
|
4938
|
+
var import_node_child_process10, import_node_util2, execFileAsync3, MARKER_KEY2, GEMINI_HOOK_EVENTS, TOOL_MATCHED_EVENTS2;
|
|
4497
4939
|
var init_gemini_hook_installer = __esm({
|
|
4498
4940
|
"../../shared/platform/src/agent-hooks/gemini-hook-installer.ts"() {
|
|
4499
4941
|
"use strict";
|
|
4500
|
-
|
|
4501
|
-
|
|
4942
|
+
import_node_child_process10 = require("node:child_process");
|
|
4943
|
+
import_node_util2 = require("node:util");
|
|
4502
4944
|
init_dirs();
|
|
4503
4945
|
init_fs_utils();
|
|
4504
4946
|
init_hook_paths();
|
|
4505
|
-
|
|
4947
|
+
execFileAsync3 = (0, import_node_util2.promisify)(import_node_child_process10.execFile);
|
|
4506
4948
|
MARKER_KEY2 = "_slayzoneManaged";
|
|
4507
4949
|
GEMINI_HOOK_EVENTS = [
|
|
4508
4950
|
"SessionStart",
|
|
@@ -4601,9 +5043,9 @@ function isManagedSlayzoneHook3(hook) {
|
|
|
4601
5043
|
async function uninstallCodexWrapper(opts = {}) {
|
|
4602
5044
|
const target = opts.wrapperPath ?? import_path4.default.join(getSlayzoneBinDir(), "codex");
|
|
4603
5045
|
try {
|
|
4604
|
-
const content = await
|
|
5046
|
+
const content = await import_promises10.default.readFile(target, "utf8");
|
|
4605
5047
|
if (!content.includes("slayzone codex wrapper")) return false;
|
|
4606
|
-
await
|
|
5048
|
+
await import_promises10.default.unlink(target);
|
|
4607
5049
|
return true;
|
|
4608
5050
|
} catch (err) {
|
|
4609
5051
|
if (isENOENT2(err)) return false;
|
|
@@ -4638,11 +5080,11 @@ function isAtLeast(v, min) {
|
|
|
4638
5080
|
function isENOENT2(err) {
|
|
4639
5081
|
return typeof err === "object" && err != null && err.code === "ENOENT";
|
|
4640
5082
|
}
|
|
4641
|
-
var
|
|
5083
|
+
var import_promises10, import_path4, import_child_process2, import_util10, execP, MARKER_KEY3, CODEX_HOOK_EVENTS, TOOL_MATCHED_EVENTS3, MIN_CODEX_VERSION;
|
|
4642
5084
|
var init_codex_hook_installer = __esm({
|
|
4643
5085
|
"../../shared/platform/src/agent-hooks/codex-hook-installer.ts"() {
|
|
4644
5086
|
"use strict";
|
|
4645
|
-
|
|
5087
|
+
import_promises10 = __toESM(require("fs/promises"), 1);
|
|
4646
5088
|
import_path4 = __toESM(require("path"), 1);
|
|
4647
5089
|
import_child_process2 = require("child_process");
|
|
4648
5090
|
import_util10 = require("util");
|
|
@@ -4667,7 +5109,7 @@ var init_codex_hook_installer = __esm({
|
|
|
4667
5109
|
// ../../shared/platform/src/agent-hooks/antigravity-hook-installer.ts
|
|
4668
5110
|
async function isAntigravityInstalled() {
|
|
4669
5111
|
try {
|
|
4670
|
-
await
|
|
5112
|
+
await execFileAsync4("agy", ["--version"], { timeout: 2e3 });
|
|
4671
5113
|
return true;
|
|
4672
5114
|
} catch {
|
|
4673
5115
|
return false;
|
|
@@ -4729,16 +5171,16 @@ function buildEntry(event, scriptPath) {
|
|
|
4729
5171
|
if (TOOL_MATCHED_EVENTS4.has(event)) entry.matcher = "*";
|
|
4730
5172
|
return entry;
|
|
4731
5173
|
}
|
|
4732
|
-
var
|
|
5174
|
+
var import_node_child_process11, import_node_util3, execFileAsync4, SLAYZONE_HOOK_NAME, ANTIGRAVITY_HOOK_EVENTS, TOOL_MATCHED_EVENTS4;
|
|
4733
5175
|
var init_antigravity_hook_installer = __esm({
|
|
4734
5176
|
"../../shared/platform/src/agent-hooks/antigravity-hook-installer.ts"() {
|
|
4735
5177
|
"use strict";
|
|
4736
|
-
|
|
4737
|
-
|
|
5178
|
+
import_node_child_process11 = require("node:child_process");
|
|
5179
|
+
import_node_util3 = require("node:util");
|
|
4738
5180
|
init_dirs();
|
|
4739
5181
|
init_fs_utils();
|
|
4740
5182
|
init_hook_paths();
|
|
4741
|
-
|
|
5183
|
+
execFileAsync4 = (0, import_node_util3.promisify)(import_node_child_process11.execFile);
|
|
4742
5184
|
SLAYZONE_HOOK_NAME = "slayzone-notify";
|
|
4743
5185
|
ANTIGRAVITY_HOOK_EVENTS = ["PreInvocation", "PostToolUse", "Stop"];
|
|
4744
5186
|
TOOL_MATCHED_EVENTS4 = /* @__PURE__ */ new Set(["PostToolUse"]);
|
|
@@ -4778,10 +5220,10 @@ var init_agent_hooks = __esm({
|
|
|
4778
5220
|
}
|
|
4779
5221
|
});
|
|
4780
5222
|
|
|
4781
|
-
// raw-import:/tmp/slz-publish-wt.
|
|
5223
|
+
// raw-import:/tmp/slz-publish-wt.C8azA7/packages/shared/hooks/src/notify.sh
|
|
4782
5224
|
var notify_default;
|
|
4783
5225
|
var init_notify = __esm({
|
|
4784
|
-
"raw-import:/tmp/slz-publish-wt.
|
|
5226
|
+
"raw-import:/tmp/slz-publish-wt.C8azA7/packages/shared/hooks/src/notify.sh"() {
|
|
4785
5227
|
notify_default = `#!/bin/sh
|
|
4786
5228
|
# SlayZone agent lifecycle hook \u2014 BENIGN DUMB FORWARDER.
|
|
4787
5229
|
#
|
|
@@ -4943,10 +5385,10 @@ exit 0
|
|
|
4943
5385
|
}
|
|
4944
5386
|
});
|
|
4945
5387
|
|
|
4946
|
-
// raw-import:/tmp/slz-publish-wt.
|
|
5388
|
+
// raw-import:/tmp/slz-publish-wt.C8azA7/packages/shared/hooks/src/opencode-plugin.js
|
|
4947
5389
|
var opencode_plugin_default;
|
|
4948
5390
|
var init_opencode_plugin = __esm({
|
|
4949
|
-
"raw-import:/tmp/slz-publish-wt.
|
|
5391
|
+
"raw-import:/tmp/slz-publish-wt.C8azA7/packages/shared/hooks/src/opencode-plugin.js"() {
|
|
4950
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";
|
|
4951
5393
|
}
|
|
4952
5394
|
});
|
|
@@ -4989,8 +5431,8 @@ var init_agent_hooks2 = __esm({
|
|
|
4989
5431
|
});
|
|
4990
5432
|
|
|
4991
5433
|
// src/bin.ts
|
|
4992
|
-
var
|
|
4993
|
-
var
|
|
5434
|
+
var import_node_os8 = require("node:os");
|
|
5435
|
+
var import_node_path14 = require("node:path");
|
|
4994
5436
|
|
|
4995
5437
|
// ../../shared/platform/src/config-prompt.ts
|
|
4996
5438
|
var import_promises = require("node:readline/promises");
|
|
@@ -5025,14 +5467,14 @@ function coerceComputerConfig(raw) {
|
|
|
5025
5467
|
}
|
|
5026
5468
|
return cfg;
|
|
5027
5469
|
}
|
|
5028
|
-
function readJsonObject(
|
|
5470
|
+
function readJsonObject(path9) {
|
|
5029
5471
|
let raw;
|
|
5030
5472
|
try {
|
|
5031
|
-
raw = (0, import_node_fs.readFileSync)(
|
|
5473
|
+
raw = (0, import_node_fs.readFileSync)(path9, "utf8");
|
|
5032
5474
|
} catch (err) {
|
|
5033
5475
|
if (err.code === "ENOENT") return null;
|
|
5034
5476
|
process.stderr.write(
|
|
5035
|
-
`[slayzone-config] cannot read ${
|
|
5477
|
+
`[slayzone-config] cannot read ${path9}: ${err instanceof Error ? err.message : String(err)} \u2014 using empty config
|
|
5036
5478
|
`
|
|
5037
5479
|
);
|
|
5038
5480
|
return null;
|
|
@@ -5042,61 +5484,61 @@ function readJsonObject(path8) {
|
|
|
5042
5484
|
parsed = JSON.parse(raw);
|
|
5043
5485
|
} catch (err) {
|
|
5044
5486
|
process.stderr.write(
|
|
5045
|
-
`[slayzone-config] ${
|
|
5487
|
+
`[slayzone-config] ${path9} is not valid JSON: ${err instanceof Error ? err.message : String(err)} \u2014 using empty config
|
|
5046
5488
|
`
|
|
5047
5489
|
);
|
|
5048
5490
|
return null;
|
|
5049
5491
|
}
|
|
5050
5492
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
5051
|
-
process.stderr.write(`[slayzone-config] ${
|
|
5493
|
+
process.stderr.write(`[slayzone-config] ${path9} is not a JSON object \u2014 using empty config
|
|
5052
5494
|
`);
|
|
5053
5495
|
return null;
|
|
5054
5496
|
}
|
|
5055
5497
|
return parsed;
|
|
5056
5498
|
}
|
|
5057
|
-
function writeJsonAtomic(
|
|
5058
|
-
const dir = (0, import_node_path.dirname)(
|
|
5499
|
+
function writeJsonAtomic(path9, data) {
|
|
5500
|
+
const dir = (0, import_node_path.dirname)(path9);
|
|
5059
5501
|
(0, import_node_fs.mkdirSync)(dir, { recursive: true, mode: 448 });
|
|
5060
|
-
const tmpPath = `${
|
|
5502
|
+
const tmpPath = `${path9}.tmp-${process.pid}-${Date.now()}`;
|
|
5061
5503
|
(0, import_node_fs.writeFileSync)(tmpPath, `${JSON.stringify(data, null, 2)}
|
|
5062
5504
|
`, { mode: 384 });
|
|
5063
5505
|
try {
|
|
5064
|
-
(0, import_node_fs.renameSync)(tmpPath,
|
|
5506
|
+
(0, import_node_fs.renameSync)(tmpPath, path9);
|
|
5065
5507
|
} catch (err) {
|
|
5066
5508
|
(0, import_node_fs.rmSync)(tmpPath, { force: true });
|
|
5067
5509
|
throw err;
|
|
5068
5510
|
}
|
|
5069
5511
|
}
|
|
5070
|
-
function loadComputerConfigFile(
|
|
5071
|
-
const raw = readJsonObject(
|
|
5512
|
+
function loadComputerConfigFile(path9 = getComputerConfigFilePath()) {
|
|
5513
|
+
const raw = readJsonObject(path9);
|
|
5072
5514
|
if (raw) return coerceComputerConfig(raw);
|
|
5073
|
-
const legacy = readJsonObject(legacyConfigFilePathFor(
|
|
5515
|
+
const legacy = readJsonObject(legacyConfigFilePathFor(path9));
|
|
5074
5516
|
return legacy ? coerceComputerConfig(legacy) : {};
|
|
5075
5517
|
}
|
|
5076
|
-
function ignoredComputerConfigKeys(
|
|
5077
|
-
const raw = readJsonObject(
|
|
5518
|
+
function ignoredComputerConfigKeys(path9 = getComputerConfigFilePath()) {
|
|
5519
|
+
const raw = readJsonObject(path9) ?? readJsonObject(legacyConfigFilePathFor(path9));
|
|
5078
5520
|
if (!raw) return [];
|
|
5079
5521
|
return IGNORED_COMPUTER_CONFIG_KEYS.filter((key) => raw[key] !== void 0);
|
|
5080
5522
|
}
|
|
5081
|
-
function saveComputerConfigFile(cfg,
|
|
5082
|
-
writeJsonAtomic(
|
|
5523
|
+
function saveComputerConfigFile(cfg, path9 = getComputerConfigFilePath()) {
|
|
5524
|
+
writeJsonAtomic(path9, cfg);
|
|
5083
5525
|
}
|
|
5084
|
-
function updateComputerConfigFile(patch,
|
|
5085
|
-
const current = readJsonObject(
|
|
5526
|
+
function updateComputerConfigFile(patch, path9 = getComputerConfigFilePath()) {
|
|
5527
|
+
const current = readJsonObject(path9);
|
|
5086
5528
|
const merged = current ? coerceComputerConfig(current) : {};
|
|
5087
5529
|
for (const [k, v] of Object.entries(patch)) {
|
|
5088
5530
|
if (v !== void 0) merged[k] = v;
|
|
5089
5531
|
}
|
|
5090
|
-
saveComputerConfigFile(merged,
|
|
5532
|
+
saveComputerConfigFile(merged, path9);
|
|
5091
5533
|
return merged;
|
|
5092
5534
|
}
|
|
5093
|
-
function updateJsonFile(patch,
|
|
5094
|
-
const current = readJsonObject(
|
|
5535
|
+
function updateJsonFile(patch, path9) {
|
|
5536
|
+
const current = readJsonObject(path9) ?? {};
|
|
5095
5537
|
const merged = { ...current };
|
|
5096
5538
|
for (const [k, v] of Object.entries(patch)) {
|
|
5097
5539
|
if (v !== void 0) merged[k] = v;
|
|
5098
5540
|
}
|
|
5099
|
-
writeJsonAtomic(
|
|
5541
|
+
writeJsonAtomic(path9, merged);
|
|
5100
5542
|
}
|
|
5101
5543
|
|
|
5102
5544
|
// ../../shared/platform/src/config-prompt.ts
|
|
@@ -5168,10 +5610,10 @@ function isLoopbackAuthority(addr) {
|
|
|
5168
5610
|
return false;
|
|
5169
5611
|
}
|
|
5170
5612
|
}
|
|
5171
|
-
function hubUrlFromAddr(addr, kind,
|
|
5613
|
+
function hubUrlFromAddr(addr, kind, path9 = "") {
|
|
5172
5614
|
const secure = !isLoopbackAuthority(addr);
|
|
5173
5615
|
const scheme = kind === "ws" ? secure ? "wss" : "ws" : secure ? "https" : "http";
|
|
5174
|
-
return `${scheme}://${addr}${
|
|
5616
|
+
return `${scheme}://${addr}${path9}`;
|
|
5175
5617
|
}
|
|
5176
5618
|
function isLoopbackComputerUrl(url2) {
|
|
5177
5619
|
let host;
|
|
@@ -5183,6 +5625,68 @@ function isLoopbackComputerUrl(url2) {
|
|
|
5183
5625
|
return LOOPBACK_HOSTS.has(host) || host.startsWith("127.");
|
|
5184
5626
|
}
|
|
5185
5627
|
|
|
5628
|
+
// ../../shared/platform/src/parent-watch.ts
|
|
5629
|
+
var PARENT_POLL_INTERVAL_MS = 1e3;
|
|
5630
|
+
var SUPERVISED_STOP_DEADLINE_MS = 5e3;
|
|
5631
|
+
var SUPERVISED_STOP_SIGKILL_GRACE_MS = SUPERVISED_STOP_DEADLINE_MS + 1e3;
|
|
5632
|
+
function pidExists(pid) {
|
|
5633
|
+
try {
|
|
5634
|
+
process.kill(pid, 0);
|
|
5635
|
+
return true;
|
|
5636
|
+
} catch (err) {
|
|
5637
|
+
return err.code === "EPERM";
|
|
5638
|
+
}
|
|
5639
|
+
}
|
|
5640
|
+
function isBrokenPipe(err) {
|
|
5641
|
+
return err?.code === "EPIPE";
|
|
5642
|
+
}
|
|
5643
|
+
function watchParent(opts) {
|
|
5644
|
+
const stdin2 = opts.stdin ?? process.stdin;
|
|
5645
|
+
const outputs = opts.outputs ?? [process.stdout, process.stderr];
|
|
5646
|
+
const getPpid = opts.getPpid ?? (() => process.ppid);
|
|
5647
|
+
const isAlive = opts.isAlive ?? pidExists;
|
|
5648
|
+
const platform3 = opts.platform ?? process.platform;
|
|
5649
|
+
const parentPid = getPpid();
|
|
5650
|
+
let armed = true;
|
|
5651
|
+
let poll = null;
|
|
5652
|
+
const stop = () => {
|
|
5653
|
+
armed = false;
|
|
5654
|
+
if (poll) clearInterval(poll);
|
|
5655
|
+
poll = null;
|
|
5656
|
+
};
|
|
5657
|
+
const fire = (reason) => {
|
|
5658
|
+
if (!armed) return;
|
|
5659
|
+
stop();
|
|
5660
|
+
opts.onParentDeath(reason);
|
|
5661
|
+
};
|
|
5662
|
+
const onPipeClosed = () => fire("parent-pipe-closed");
|
|
5663
|
+
stdin2.on("end", onPipeClosed);
|
|
5664
|
+
stdin2.on("close", onPipeClosed);
|
|
5665
|
+
stdin2.on("error", onPipeClosed);
|
|
5666
|
+
stdin2.resume();
|
|
5667
|
+
stdin2.unref?.();
|
|
5668
|
+
for (const output2 of outputs) {
|
|
5669
|
+
output2.on("error", (err) => {
|
|
5670
|
+
if (!isBrokenPipe(err)) throw err;
|
|
5671
|
+
fire("parent-output-closed");
|
|
5672
|
+
});
|
|
5673
|
+
}
|
|
5674
|
+
const check2 = () => {
|
|
5675
|
+
const ppid = getPpid();
|
|
5676
|
+
if (ppid !== parentPid || platform3 !== "win32" && ppid === 1) {
|
|
5677
|
+
fire("parent-reparented");
|
|
5678
|
+
return;
|
|
5679
|
+
}
|
|
5680
|
+
if (!isAlive(parentPid)) fire("parent-exited");
|
|
5681
|
+
};
|
|
5682
|
+
check2();
|
|
5683
|
+
if (armed) {
|
|
5684
|
+
poll = setInterval(check2, opts.pollIntervalMs ?? PARENT_POLL_INTERVAL_MS);
|
|
5685
|
+
poll.unref();
|
|
5686
|
+
}
|
|
5687
|
+
return { stop };
|
|
5688
|
+
}
|
|
5689
|
+
|
|
5186
5690
|
// ../../domains/computer-transport/src/shared/rpc.ts
|
|
5187
5691
|
var RpcError = class _RpcError extends Error {
|
|
5188
5692
|
code;
|
|
@@ -5237,16 +5741,23 @@ var DuplexRpc = class {
|
|
|
5237
5741
|
* Issue a JSON-RPC request. Resolves with the `result` payload, rejects with
|
|
5238
5742
|
* `RpcError` on a JSON-RPC error reply, `RpcTimeoutError` on timeout, or
|
|
5239
5743
|
* `RpcDisposedError` on disposal. `timeoutMs <= 0` disables the timeout.
|
|
5744
|
+
*
|
|
5745
|
+
* `actor`, when given, rides as the envelope's top-level `actor` member (who
|
|
5746
|
+
* asked — hub → computer requests only; see `requestActorSchema`).
|
|
5240
5747
|
*/
|
|
5241
|
-
request(method, params, timeoutMs) {
|
|
5748
|
+
request(method, params, timeoutMs, actor) {
|
|
5242
5749
|
if (this.disposed) {
|
|
5243
5750
|
return Promise.reject(new RpcDisposedError(this.label));
|
|
5244
5751
|
}
|
|
5245
5752
|
const effectiveTimeout = timeoutMs ?? this.opts.defaultRequestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
5246
5753
|
const id = this.nextId++;
|
|
5247
|
-
const envelope = JSON.stringify(
|
|
5248
|
-
|
|
5249
|
-
|
|
5754
|
+
const envelope = JSON.stringify({
|
|
5755
|
+
jsonrpc: "2.0",
|
|
5756
|
+
id,
|
|
5757
|
+
method,
|
|
5758
|
+
...params === void 0 ? {} : { params },
|
|
5759
|
+
...actor === void 0 ? {} : { actor }
|
|
5760
|
+
});
|
|
5250
5761
|
return new Promise((resolve7, reject) => {
|
|
5251
5762
|
const timer = effectiveTimeout > 0 ? setTimeout(() => {
|
|
5252
5763
|
this.pending.delete(id);
|
|
@@ -5322,7 +5833,7 @@ var DuplexRpc = class {
|
|
|
5322
5833
|
if (typeof msg.method === "string") {
|
|
5323
5834
|
if (hasId) {
|
|
5324
5835
|
if (this.opts.onPeerRequest) {
|
|
5325
|
-
this.opts.onPeerRequest(msg.method, msg.params, rawId);
|
|
5836
|
+
this.opts.onPeerRequest(msg.method, msg.params, rawId, msg.actor);
|
|
5326
5837
|
} else {
|
|
5327
5838
|
this.respondError(
|
|
5328
5839
|
rawId,
|
|
@@ -6183,10 +6694,10 @@ function mergeDefs(...defs) {
|
|
|
6183
6694
|
function cloneDef(schema) {
|
|
6184
6695
|
return mergeDefs(schema._zod.def);
|
|
6185
6696
|
}
|
|
6186
|
-
function getElementAtPath(obj,
|
|
6187
|
-
if (!
|
|
6697
|
+
function getElementAtPath(obj, path9) {
|
|
6698
|
+
if (!path9)
|
|
6188
6699
|
return obj;
|
|
6189
|
-
return
|
|
6700
|
+
return path9.reduce((acc, key) => acc?.[key], obj);
|
|
6190
6701
|
}
|
|
6191
6702
|
function promiseAllObject(promisesObj) {
|
|
6192
6703
|
const keys = Object.keys(promisesObj);
|
|
@@ -6526,11 +7037,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
6526
7037
|
}
|
|
6527
7038
|
return false;
|
|
6528
7039
|
}
|
|
6529
|
-
function prefixIssues(
|
|
7040
|
+
function prefixIssues(path9, issues) {
|
|
6530
7041
|
return issues.map((iss) => {
|
|
6531
7042
|
var _a3;
|
|
6532
7043
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
6533
|
-
iss.path.unshift(
|
|
7044
|
+
iss.path.unshift(path9);
|
|
6534
7045
|
return iss;
|
|
6535
7046
|
});
|
|
6536
7047
|
}
|
|
@@ -6980,16 +7491,16 @@ function flattenError(error62, mapper = (issue2) => issue2.message) {
|
|
|
6980
7491
|
}
|
|
6981
7492
|
function formatError(error62, mapper = (issue2) => issue2.message) {
|
|
6982
7493
|
const fieldErrors = { _errors: [] };
|
|
6983
|
-
const processError = (error63,
|
|
7494
|
+
const processError = (error63, path9 = []) => {
|
|
6984
7495
|
for (const issue2 of error63.issues) {
|
|
6985
7496
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
6986
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
7497
|
+
issue2.errors.map((issues) => processError({ issues }, [...path9, ...issue2.path]));
|
|
6987
7498
|
} else if (issue2.code === "invalid_key") {
|
|
6988
|
-
processError({ issues: issue2.issues }, [...
|
|
7499
|
+
processError({ issues: issue2.issues }, [...path9, ...issue2.path]);
|
|
6989
7500
|
} else if (issue2.code === "invalid_element") {
|
|
6990
|
-
processError({ issues: issue2.issues }, [...
|
|
7501
|
+
processError({ issues: issue2.issues }, [...path9, ...issue2.path]);
|
|
6991
7502
|
} else {
|
|
6992
|
-
const fullpath = [...
|
|
7503
|
+
const fullpath = [...path9, ...issue2.path];
|
|
6993
7504
|
if (fullpath.length === 0) {
|
|
6994
7505
|
fieldErrors._errors.push(mapper(issue2));
|
|
6995
7506
|
} else {
|
|
@@ -7028,17 +7539,17 @@ function formatError(error62, mapper = (issue2) => issue2.message) {
|
|
|
7028
7539
|
}
|
|
7029
7540
|
function treeifyError(error62, mapper = (issue2) => issue2.message) {
|
|
7030
7541
|
const result = { errors: [] };
|
|
7031
|
-
const processError = (error63,
|
|
7542
|
+
const processError = (error63, path9 = []) => {
|
|
7032
7543
|
var _a3;
|
|
7033
7544
|
for (const issue2 of error63.issues) {
|
|
7034
7545
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
7035
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
7546
|
+
issue2.errors.map((issues) => processError({ issues }, [...path9, ...issue2.path]));
|
|
7036
7547
|
} else if (issue2.code === "invalid_key") {
|
|
7037
|
-
processError({ issues: issue2.issues }, [...
|
|
7548
|
+
processError({ issues: issue2.issues }, [...path9, ...issue2.path]);
|
|
7038
7549
|
} else if (issue2.code === "invalid_element") {
|
|
7039
|
-
processError({ issues: issue2.issues }, [...
|
|
7550
|
+
processError({ issues: issue2.issues }, [...path9, ...issue2.path]);
|
|
7040
7551
|
} else {
|
|
7041
|
-
const fullpath = [...
|
|
7552
|
+
const fullpath = [...path9, ...issue2.path];
|
|
7042
7553
|
if (fullpath.length === 0) {
|
|
7043
7554
|
result.errors.push(mapper(issue2));
|
|
7044
7555
|
continue;
|
|
@@ -7077,8 +7588,8 @@ function treeifyError(error62, mapper = (issue2) => issue2.message) {
|
|
|
7077
7588
|
}
|
|
7078
7589
|
function toDotPath(_path) {
|
|
7079
7590
|
const segs = [];
|
|
7080
|
-
const
|
|
7081
|
-
for (const seg of
|
|
7591
|
+
const path9 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
7592
|
+
for (const seg of path9) {
|
|
7082
7593
|
if (typeof seg === "number")
|
|
7083
7594
|
segs.push(`[${seg}]`);
|
|
7084
7595
|
else if (typeof seg === "symbol")
|
|
@@ -7364,8 +7875,8 @@ function emoji() {
|
|
|
7364
7875
|
}
|
|
7365
7876
|
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])$/;
|
|
7366
7877
|
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}|:))$/;
|
|
7367
|
-
var mac = (
|
|
7368
|
-
const escapedDelim = escapeRegex(
|
|
7878
|
+
var mac = (delimiter3) => {
|
|
7879
|
+
const escapedDelim = escapeRegex(delimiter3 ?? ":");
|
|
7369
7880
|
return new RegExp(`^(?:[0-9A-F]{2}${escapedDelim}){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}${escapedDelim}){5}[0-9a-f]{2}$`);
|
|
7370
7881
|
};
|
|
7371
7882
|
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])$/;
|
|
@@ -18269,8 +18780,8 @@ function registry() {
|
|
|
18269
18780
|
var globalRegistry = globalThis.__zod_globalRegistry;
|
|
18270
18781
|
|
|
18271
18782
|
// ../../../node_modules/zod/v4/core/compile.js
|
|
18272
|
-
var INVALID = Symbol.for("zod.compile.invalid");
|
|
18273
|
-
var FALLBACK_FLAG = Symbol.for("zod.compile.fallback");
|
|
18783
|
+
var INVALID = /* @__PURE__ */ Symbol.for("zod.compile.invalid");
|
|
18784
|
+
var FALLBACK_FLAG = /* @__PURE__ */ Symbol.for("zod.compile.fallback");
|
|
18274
18785
|
var ZodCompileAsyncError = class extends Error {
|
|
18275
18786
|
constructor(message = "z.compile does not support async refinements, transforms, or checks") {
|
|
18276
18787
|
super(message);
|
|
@@ -22050,7 +22561,7 @@ var nonoptionalProcessor = (schema, ctx, _json, params) => {
|
|
|
22050
22561
|
const seen = ctx.seen.get(schema);
|
|
22051
22562
|
seen.ref = def.innerType;
|
|
22052
22563
|
};
|
|
22053
|
-
var UNREPRESENTABLE_DEFAULT = Symbol();
|
|
22564
|
+
var UNREPRESENTABLE_DEFAULT = /* @__PURE__ */ Symbol();
|
|
22054
22565
|
function serializeDefaultValue(value, schema, ctx, json2, params) {
|
|
22055
22566
|
let unrepresentable = false;
|
|
22056
22567
|
const serialized = JSON.stringify(value, (_, val) => {
|
|
@@ -24180,13 +24691,13 @@ function resolveRef(ref, ctx) {
|
|
|
24180
24691
|
if (!ref.startsWith("#")) {
|
|
24181
24692
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
24182
24693
|
}
|
|
24183
|
-
const
|
|
24184
|
-
if (
|
|
24694
|
+
const path9 = ref.slice(1).split("/").filter(Boolean);
|
|
24695
|
+
if (path9.length === 0) {
|
|
24185
24696
|
return ctx.rootSchema;
|
|
24186
24697
|
}
|
|
24187
24698
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
24188
|
-
if (
|
|
24189
|
-
const key =
|
|
24699
|
+
if (path9[0] === defsKey) {
|
|
24700
|
+
const key = path9[1] === void 0 ? void 0 : decodeJSONPointerSegment(path9[1]);
|
|
24190
24701
|
if (!key || !ctx.defs[key]) {
|
|
24191
24702
|
throw new Error(`Reference not found: ${ref}`);
|
|
24192
24703
|
}
|
|
@@ -24814,7 +25325,7 @@ function fromJSONSchema(schema, params) {
|
|
|
24814
25325
|
}
|
|
24815
25326
|
|
|
24816
25327
|
// ../../../node_modules/zod/v4/core/visit.js
|
|
24817
|
-
var RESOLVING = Symbol("z.visit/resolving");
|
|
25328
|
+
var RESOLVING = /* @__PURE__ */ Symbol("z.visit/resolving");
|
|
24818
25329
|
function visit(schema, fnOrHandlers) {
|
|
24819
25330
|
const fn = typeof fnOrHandlers === "function" ? fnOrHandlers : (node2, rewritten) => {
|
|
24820
25331
|
const h = fnOrHandlers[node2._zod.def.type];
|
|
@@ -25037,7 +25548,7 @@ function date4(params) {
|
|
|
25037
25548
|
|
|
25038
25549
|
// ../../domains/computer-transport/src/shared/frames.ts
|
|
25039
25550
|
var COMPUTER_EXIT_NEEDS_RE_ENROLLMENT = 78;
|
|
25040
|
-
var COMPUTER_PROTOCOL_VERSION =
|
|
25551
|
+
var COMPUTER_PROTOCOL_VERSION = 3;
|
|
25041
25552
|
var ComputerTransportErrorCodes = {
|
|
25042
25553
|
/** Command is part of the contract but not implemented by this peer. */
|
|
25043
25554
|
unimplemented: -32001,
|
|
@@ -25053,8 +25564,20 @@ var ComputerTransportErrorCodes = {
|
|
|
25053
25564
|
* computer retries, and gets in once the incumbent's connection is gone.
|
|
25054
25565
|
* Older computers treat any unlisted code as transient too.
|
|
25055
25566
|
*/
|
|
25056
|
-
computerInUse: -32005
|
|
25567
|
+
computerInUse: -32005,
|
|
25568
|
+
/**
|
|
25569
|
+
* The computer refused the request by its own local policy (T-27): a hub-pushed
|
|
25570
|
+
* `allowedRoots` change, or a path / spawn cwd outside its `allowedRoots`. Not
|
|
25571
|
+
* transient — retrying the same request is refused again. The hub audits it as
|
|
25572
|
+
* `denied` and the message says what to do on the device.
|
|
25573
|
+
*/
|
|
25574
|
+
refusedByComputer: -32006
|
|
25057
25575
|
};
|
|
25576
|
+
var requestActorSchema = external_exports.object({
|
|
25577
|
+
userId: external_exports.string().min(1).max(200).nullable(),
|
|
25578
|
+
/** `trpc <path>` / `METHOD /path` the request entered the hub through. */
|
|
25579
|
+
route: external_exports.string().max(200).nullable()
|
|
25580
|
+
});
|
|
25058
25581
|
var ComputerToHubMethods = {
|
|
25059
25582
|
enroll: "enroll",
|
|
25060
25583
|
hello: "hello",
|
|
@@ -25078,15 +25601,25 @@ var enrollParamsSchema = external_exports.object({
|
|
|
25078
25601
|
});
|
|
25079
25602
|
var enrollResultSchema = external_exports.object({
|
|
25080
25603
|
computerId: external_exports.string().min(1),
|
|
25081
|
-
apiKey: external_exports.string().min(1)
|
|
25604
|
+
apiKey: external_exports.string().min(1),
|
|
25605
|
+
/** The hub's protocol version (v3+). Absent = an older hub; the computer refuses it. */
|
|
25606
|
+
protocolVersion: external_exports.number().int().positive().optional()
|
|
25082
25607
|
});
|
|
25083
25608
|
var helloParamsSchema = external_exports.object({
|
|
25084
25609
|
apiKey: external_exports.string().min(1),
|
|
25085
25610
|
epoch: epochField,
|
|
25086
|
-
hostId: hostIdField
|
|
25611
|
+
hostId: hostIdField,
|
|
25612
|
+
/**
|
|
25613
|
+
* v3+. Before v3 only `enroll` carried a version, so a computer enrolled long ago
|
|
25614
|
+
* reconnected on any protocol. Absent = a pre-v3 computer; a v3 hub refuses it
|
|
25615
|
+
* with `protocolMismatch`.
|
|
25616
|
+
*/
|
|
25617
|
+
protocolVersion: external_exports.number().int().positive().optional()
|
|
25087
25618
|
});
|
|
25088
25619
|
var helloResultSchema = external_exports.object({
|
|
25089
|
-
computerId: external_exports.string().min(1)
|
|
25620
|
+
computerId: external_exports.string().min(1),
|
|
25621
|
+
/** The hub's protocol version (v3+). Absent = an older hub; the computer refuses it. */
|
|
25622
|
+
protocolVersion: external_exports.number().int().positive().optional()
|
|
25090
25623
|
});
|
|
25091
25624
|
var heartbeatParamsSchema = external_exports.object({
|
|
25092
25625
|
/** Sender wall-clock ms, for skew diagnostics. */
|
|
@@ -25161,6 +25694,8 @@ var HubToComputerMethods = {
|
|
|
25161
25694
|
ptyList: "pty.list",
|
|
25162
25695
|
/** Move a session still waiting for an agent-start slot to the front (tab revealed). */
|
|
25163
25696
|
ptyPrioritize: "pty.prioritize",
|
|
25697
|
+
/** Which of claude's background jobs still have a live process under a session. */
|
|
25698
|
+
ptyBackgroundJobs: "pty.backgroundJobs",
|
|
25164
25699
|
// warm pool (pre-warmed agents live ON the computer — see server/warm-*)
|
|
25165
25700
|
ptyWarmSpawn: "pty.warmSpawn",
|
|
25166
25701
|
ptyWarmAdopt: "pty.warmAdopt",
|
|
@@ -25281,6 +25816,21 @@ var ptyPrioritizeResultSchema = external_exports.object({
|
|
|
25281
25816
|
ok: external_exports.literal(true),
|
|
25282
25817
|
queued: external_exports.boolean()
|
|
25283
25818
|
});
|
|
25819
|
+
var ptyBackgroundJobsParamsSchema = external_exports.object({
|
|
25820
|
+
sessionId: external_exports.string().min(1),
|
|
25821
|
+
jobs: external_exports.array(
|
|
25822
|
+
external_exports.object({
|
|
25823
|
+
id: external_exports.string().min(1),
|
|
25824
|
+
kind: external_exports.enum(["shell", "monitor"]),
|
|
25825
|
+
command: external_exports.string().nullable()
|
|
25826
|
+
})
|
|
25827
|
+
).max(256)
|
|
25828
|
+
});
|
|
25829
|
+
var ptyBackgroundJobsResultSchema = external_exports.object({
|
|
25830
|
+
ok: external_exports.literal(true),
|
|
25831
|
+
supported: external_exports.boolean(),
|
|
25832
|
+
alive: external_exports.array(external_exports.string())
|
|
25833
|
+
});
|
|
25284
25834
|
var ptyResizeParamsSchema = external_exports.object({
|
|
25285
25835
|
sessionId: external_exports.string().min(1),
|
|
25286
25836
|
cols: external_exports.number().int().positive(),
|
|
@@ -26007,7 +26557,7 @@ var HubDialer = class {
|
|
|
26007
26557
|
write: (line) => {
|
|
26008
26558
|
ws.send(line);
|
|
26009
26559
|
},
|
|
26010
|
-
onPeerRequest: (method, params, id) => this.handleHubRequest(conn, method, params, id),
|
|
26560
|
+
onPeerRequest: (method, params, id, actor) => this.handleHubRequest(conn, method, params, id, actor),
|
|
26011
26561
|
onNotification: (method, params) => this.events.emit("notification", { method, params }),
|
|
26012
26562
|
onParseError: (line, err) => {
|
|
26013
26563
|
this.log("computer dialer received malformed frame", { error: String(err), line });
|
|
@@ -26048,6 +26598,22 @@ var HubDialer = class {
|
|
|
26048
26598
|
});
|
|
26049
26599
|
return enrollResultSchema.parse(raw);
|
|
26050
26600
|
}
|
|
26601
|
+
/**
|
|
26602
|
+
* v3 (T-27): refuse a hub that does not echo our protocol version. Only a hub
|
|
26603
|
+
* older than v3 omits it, and such a hub sends no actor with its requests, so
|
|
26604
|
+
* nothing it asked for could be logged against a user. Fatal, like an enroll
|
|
26605
|
+
* mismatch: retrying the same hub cannot fix it; upgrading it does.
|
|
26606
|
+
*/
|
|
26607
|
+
hubSpeaksOurProtocol(conn, hubVersion) {
|
|
26608
|
+
if (hubVersion === COMPUTER_PROTOCOL_VERSION) return true;
|
|
26609
|
+
this.fatal(
|
|
26610
|
+
new Error(
|
|
26611
|
+
`hub speaks computer protocol ${hubVersion === void 0 ? "older than v3" : `v${hubVersion}`}, this computer speaks v${COMPUTER_PROTOCOL_VERSION} \u2014 upgrade the hub and this computer to the same SlayZone release`
|
|
26612
|
+
),
|
|
26613
|
+
conn
|
|
26614
|
+
);
|
|
26615
|
+
return false;
|
|
26616
|
+
}
|
|
26051
26617
|
async authenticate(conn) {
|
|
26052
26618
|
this.setState("authenticating");
|
|
26053
26619
|
try {
|
|
@@ -26057,14 +26623,20 @@ var HubDialer = class {
|
|
|
26057
26623
|
const raw = await conn.rpc.request(ComputerToHubMethods.hello, {
|
|
26058
26624
|
apiKey: stored.apiKey,
|
|
26059
26625
|
epoch: this.epoch,
|
|
26626
|
+
protocolVersion: COMPUTER_PROTOCOL_VERSION,
|
|
26060
26627
|
// Reconnect is the ONLY moment the hub can learn that this computer
|
|
26061
26628
|
// adopted a peer's host id while it was offline.
|
|
26062
26629
|
...this.opts.identity.hostId ? { hostId: this.opts.identity.hostId } : {}
|
|
26063
26630
|
});
|
|
26064
26631
|
const result2 = helloResultSchema.parse(raw);
|
|
26632
|
+
if (!this.hubSpeaksOurProtocol(conn, result2.protocolVersion)) return;
|
|
26065
26633
|
this.onAuthenticated(conn, result2.computerId, "hello");
|
|
26066
26634
|
return;
|
|
26067
26635
|
} catch (err) {
|
|
26636
|
+
if (err instanceof RpcError && err.code === ComputerTransportErrorCodes.protocolMismatch) {
|
|
26637
|
+
this.fatal(new Error(`hub refused reconnect: ${err.message}`), conn);
|
|
26638
|
+
return;
|
|
26639
|
+
}
|
|
26068
26640
|
if (!(err instanceof RpcError) || err.code !== ComputerTransportErrorCodes.unauthorized)
|
|
26069
26641
|
throw err;
|
|
26070
26642
|
if (!this.opts.joinToken) {
|
|
@@ -26137,6 +26709,7 @@ var HubDialer = class {
|
|
|
26137
26709
|
throw retryErr;
|
|
26138
26710
|
}
|
|
26139
26711
|
}
|
|
26712
|
+
if (!this.hubSpeaksOurProtocol(conn, result.protocolVersion)) return;
|
|
26140
26713
|
try {
|
|
26141
26714
|
await this.opts.credentialStore.save({
|
|
26142
26715
|
computerId: result.computerId,
|
|
@@ -26184,7 +26757,7 @@ var HubDialer = class {
|
|
|
26184
26757
|
}, intervalMs);
|
|
26185
26758
|
conn.heartbeatTimer.unref?.();
|
|
26186
26759
|
}
|
|
26187
|
-
handleHubRequest(conn, method, params, id) {
|
|
26760
|
+
handleHubRequest(conn, method, params, id, actor) {
|
|
26188
26761
|
if (method === HubToComputerMethods.ping) {
|
|
26189
26762
|
conn.rpc.respond(id, { ts: Date.now() });
|
|
26190
26763
|
return;
|
|
@@ -26200,7 +26773,7 @@ var HubDialer = class {
|
|
|
26200
26773
|
}
|
|
26201
26774
|
void (async () => {
|
|
26202
26775
|
try {
|
|
26203
|
-
const result = await handler(method, params);
|
|
26776
|
+
const result = await handler(method, params, actor);
|
|
26204
26777
|
conn.rpc.respond(id, result ?? null);
|
|
26205
26778
|
} catch (err) {
|
|
26206
26779
|
if (err instanceof RpcError) {
|
|
@@ -26311,7 +26884,19 @@ var computerConfigSchema = external_exports.object({
|
|
|
26311
26884
|
/** Filesystem roots this computer may operate under (fs./git. commands). */
|
|
26312
26885
|
allowedRoots: external_exports.array(external_exports.string().min(1)),
|
|
26313
26886
|
/** Capability tags advertised at enrollment. */
|
|
26314
|
-
capabilities: external_exports.array(external_exports.string().min(1))
|
|
26887
|
+
capabilities: external_exports.array(external_exports.string().min(1)),
|
|
26888
|
+
/**
|
|
26889
|
+
* May a hub change `allowedRoots` (`computer.setAllowedRoots`)? T-27: folder
|
|
26890
|
+
* limits are the device user's decision, so NO by default — set them on the
|
|
26891
|
+
* device (`slay computer roots`, or `computer.config.json`).
|
|
26892
|
+
*
|
|
26893
|
+
* Derived only, in {@link loadComputerConfig}: true for the desktop app's own
|
|
26894
|
+
* supervised computer when every hub it dials is on loopback — there the hub is
|
|
26895
|
+
* the local app (login off, same user, same machine), and it widens the jail for
|
|
26896
|
+
* projects the user adds. No file key and no env var sets it, so nothing a hub
|
|
26897
|
+
* can write turns it on.
|
|
26898
|
+
*/
|
|
26899
|
+
hubMayEditRoots: external_exports.boolean().optional()
|
|
26315
26900
|
});
|
|
26316
26901
|
var ENV_VARS = {
|
|
26317
26902
|
// Authority ONLY — `host[:port]`, no scheme, no path. The dial scheme
|
|
@@ -26363,13 +26948,36 @@ function realpathBoundary(target) {
|
|
|
26363
26948
|
}
|
|
26364
26949
|
}
|
|
26365
26950
|
}
|
|
26366
|
-
|
|
26951
|
+
var JailViolationError = class extends Error {
|
|
26952
|
+
constructor(message) {
|
|
26953
|
+
super(message);
|
|
26954
|
+
this.name = "JailViolationError";
|
|
26955
|
+
}
|
|
26956
|
+
};
|
|
26957
|
+
var protectedPaths = [];
|
|
26958
|
+
function setProtectedPaths(paths) {
|
|
26959
|
+
protectedPaths = paths.map((p) => realpathBoundary(p));
|
|
26960
|
+
}
|
|
26961
|
+
function isInside(path9, dir) {
|
|
26962
|
+
return path9 === dir || path9.startsWith(dir + import_node_path3.sep);
|
|
26963
|
+
}
|
|
26964
|
+
function isProtectedPath(absolute) {
|
|
26965
|
+
return protectedPaths.some((guarded) => isInside(absolute, guarded));
|
|
26966
|
+
}
|
|
26967
|
+
function assertPathAllowed(candidate, allowedRoots, opts = {}) {
|
|
26367
26968
|
if (allowedRoots.length === 0) {
|
|
26368
|
-
throw new
|
|
26969
|
+
throw new JailViolationError(
|
|
26369
26970
|
`computer has no allowedRoots configured; refusing filesystem access to '${candidate}'`
|
|
26370
26971
|
);
|
|
26371
26972
|
}
|
|
26372
26973
|
const resolved = realpathBoundary(candidate);
|
|
26974
|
+
for (const guarded of protectedPaths) {
|
|
26975
|
+
if (isInside(resolved, guarded) || opts.subtree && isInside(guarded, resolved)) {
|
|
26976
|
+
throw new JailViolationError(
|
|
26977
|
+
`path '${candidate}' holds this computer's own state (config, credentials or request log); refused`
|
|
26978
|
+
);
|
|
26979
|
+
}
|
|
26980
|
+
}
|
|
26373
26981
|
for (const root of allowedRoots) {
|
|
26374
26982
|
let realRoot;
|
|
26375
26983
|
try {
|
|
@@ -26377,14 +26985,24 @@ function assertPathAllowed(candidate, allowedRoots) {
|
|
|
26377
26985
|
} catch {
|
|
26378
26986
|
continue;
|
|
26379
26987
|
}
|
|
26380
|
-
if (resolved
|
|
26988
|
+
if (isInside(resolved, realRoot)) {
|
|
26381
26989
|
return resolved;
|
|
26382
26990
|
}
|
|
26383
26991
|
}
|
|
26384
|
-
throw new
|
|
26992
|
+
throw new JailViolationError(
|
|
26385
26993
|
`path '${candidate}' is outside the computer's allowedRoots [${allowedRoots.join(", ")}]`
|
|
26386
26994
|
);
|
|
26387
26995
|
}
|
|
26996
|
+
function spawnCwd(cwd, allowedRoots) {
|
|
26997
|
+
if (cwd !== void 0) return assertPathAllowed(cwd, allowedRoots);
|
|
26998
|
+
for (const root of allowedRoots) {
|
|
26999
|
+
try {
|
|
27000
|
+
return import_node_fs2.realpathSync.native((0, import_node_path3.resolve)(root));
|
|
27001
|
+
} catch {
|
|
27002
|
+
}
|
|
27003
|
+
}
|
|
27004
|
+
throw new JailViolationError("computer has no existing allowedRoots; refusing to start a process");
|
|
27005
|
+
}
|
|
26388
27006
|
function fromSharedConfig(shared) {
|
|
26389
27007
|
const out = {};
|
|
26390
27008
|
if (shared.hubUrl !== void 0) out.hubUrl = shared.hubUrl;
|
|
@@ -26419,6 +27037,9 @@ function loadComputerConfig(env = process.env, shared = env === process.env && e
|
|
|
26419
27037
|
...envJoinToken !== void 0 ? { joinToken: envJoinToken } : {}
|
|
26420
27038
|
};
|
|
26421
27039
|
const result = computerConfigSchema.safeParse(merged);
|
|
27040
|
+
if (result.success) {
|
|
27041
|
+
result.data.hubMayEditRoots = env.SLAYZONE_SUPERVISED === "1" && effectiveHubUrls(result.data).every((url2) => isLoopbackComputerUrl(url2));
|
|
27042
|
+
}
|
|
26422
27043
|
if (!result.success) {
|
|
26423
27044
|
const issues = result.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
|
|
26424
27045
|
throw new Error(
|
|
@@ -26461,71 +27082,587 @@ function effectiveHubUrls(config2) {
|
|
|
26461
27082
|
return [...new Set(all)];
|
|
26462
27083
|
}
|
|
26463
27084
|
|
|
26464
|
-
// src/
|
|
26465
|
-
var
|
|
26466
|
-
|
|
26467
|
-
|
|
26468
|
-
|
|
26469
|
-
|
|
26470
|
-
|
|
26471
|
-
|
|
26472
|
-
|
|
26473
|
-
|
|
26474
|
-
|
|
26475
|
-
|
|
26476
|
-
|
|
26477
|
-
|
|
26478
|
-
|
|
26479
|
-
|
|
26480
|
-
|
|
26481
|
-
|
|
26482
|
-
|
|
26483
|
-
|
|
26484
|
-
|
|
26485
|
-
|
|
26486
|
-
|
|
26487
|
-
|
|
26488
|
-
|
|
26489
|
-
|
|
26490
|
-
|
|
26491
|
-
|
|
26492
|
-
|
|
26493
|
-
|
|
26494
|
-
|
|
26495
|
-
|
|
26496
|
-
`,
|
|
26497
|
-
|
|
26498
|
-
|
|
26499
|
-
|
|
26500
|
-
|
|
26501
|
-
|
|
26502
|
-
|
|
26503
|
-
|
|
26504
|
-
|
|
26505
|
-
|
|
26506
|
-
|
|
26507
|
-
|
|
26508
|
-
|
|
26509
|
-
|
|
26510
|
-
|
|
26511
|
-
|
|
26512
|
-
|
|
26513
|
-
|
|
26514
|
-
|
|
26515
|
-
|
|
26516
|
-
|
|
26517
|
-
|
|
26518
|
-
|
|
26519
|
-
|
|
26520
|
-
|
|
26521
|
-
|
|
26522
|
-
|
|
27085
|
+
// src/login-shell-path.ts
|
|
27086
|
+
var import_node_path5 = require("node:path");
|
|
27087
|
+
|
|
27088
|
+
// ../../shared/platform/src/index.ts
|
|
27089
|
+
init_dirs();
|
|
27090
|
+
|
|
27091
|
+
// ../../shared/platform/src/paths.ts
|
|
27092
|
+
init_dirs();
|
|
27093
|
+
|
|
27094
|
+
// ../../shared/platform/src/hub-switches.ts
|
|
27095
|
+
var HUB_SWITCH_ENV = {
|
|
27096
|
+
requireLogin: "SLAYZONE_HUB_REQUIRE_LOGIN",
|
|
27097
|
+
enablePassword: "SLAYZONE_HUB_ENABLE_PASSWORD",
|
|
27098
|
+
enableSso: "SLAYZONE_HUB_ENABLE_SSO",
|
|
27099
|
+
enableProcesses: "SLAYZONE_HUB_ENABLE_PROCESSES",
|
|
27100
|
+
enableIntegrations: "SLAYZONE_HUB_ENABLE_INTEGRATIONS"
|
|
27101
|
+
};
|
|
27102
|
+
var HUB_SSO_ENV = {
|
|
27103
|
+
tenantId: "SLAYZONE_HUB_SSO_TENANT_ID",
|
|
27104
|
+
clientId: "SLAYZONE_HUB_SSO_CLIENT_ID",
|
|
27105
|
+
/** PEM certificate registered on the Entra app (its SHA-256 thumbprint goes in each assertion). Env only. */
|
|
27106
|
+
certPath: "SLAYZONE_HUB_SSO_CERT_PATH",
|
|
27107
|
+
/** PEM private key of that certificate (RSA, unencrypted). Env only. */
|
|
27108
|
+
keyPath: "SLAYZONE_HUB_SSO_KEY_PATH",
|
|
27109
|
+
/** `0|1`, default `1`: refuse a sign-in whose ID token's `amr` does not contain `mfa`. */
|
|
27110
|
+
requireMfa: "SLAYZONE_HUB_SSO_REQUIRE_MFA"
|
|
27111
|
+
};
|
|
27112
|
+
var RETIRED_HUB_SSO_CLIENT_SECRET_ENV = "SLAYZONE_HUB_SSO_CLIENT_SECRET";
|
|
27113
|
+
var HUB_ADDRESS_ENV = {
|
|
27114
|
+
/** Where the hub LISTENS. Renamed from `SLAYZONE_HUB_ADDRESS`, which now only
|
|
27115
|
+
* means "the hub to connect to" on a computer / `slay`. */
|
|
27116
|
+
listen: "SLAYZONE_HUB_LISTEN_ADDRESS",
|
|
27117
|
+
/** How OTHER machines reach the hub (`host[:port]`, no scheme). */
|
|
27118
|
+
public: "SLAYZONE_HUB_PUBLIC_ADDRESS",
|
|
27119
|
+
/** Signing key of hub-auth. */
|
|
27120
|
+
authSecret: "SLAYZONE_HUB_AUTH_SECRET"
|
|
27121
|
+
};
|
|
27122
|
+
var LEGACY_HUB_LISTEN_ENV = "SLAYZONE_HUB_ADDRESS";
|
|
27123
|
+
var HUB_SESSION_RETENTION_DAYS_ENV = "SLAYZONE_HUB_SESSION_RETENTION_DAYS";
|
|
27124
|
+
var HUB_ALLOWED_WEB_ORIGINS_ENV = "SLAYZONE_HUB_ALLOWED_WEB_ORIGINS";
|
|
27125
|
+
var HUB_TRUSTED_PROXIES_ENV = "SLAYZONE_HUB_TRUSTED_PROXIES";
|
|
27126
|
+
var HUB_CONVEX_ADDRESS_ENV = "SLAYZONE_HUB_CONVEX_ADDRESS";
|
|
27127
|
+
var HUB_PROCESS_ENV = [
|
|
27128
|
+
...Object.values(HUB_SWITCH_ENV),
|
|
27129
|
+
...Object.values(HUB_SSO_ENV),
|
|
27130
|
+
RETIRED_HUB_SSO_CLIENT_SECRET_ENV,
|
|
27131
|
+
...Object.values(HUB_ADDRESS_ENV),
|
|
27132
|
+
LEGACY_HUB_LISTEN_ENV,
|
|
27133
|
+
HUB_ALLOWED_WEB_ORIGINS_ENV,
|
|
27134
|
+
HUB_TRUSTED_PROXIES_ENV,
|
|
27135
|
+
HUB_SESSION_RETENTION_DAYS_ENV,
|
|
27136
|
+
HUB_CONVEX_ADDRESS_ENV
|
|
27137
|
+
];
|
|
27138
|
+
|
|
27139
|
+
// ../../shared/platform/src/security-log.ts
|
|
27140
|
+
var import_node_async_hooks = require("node:async_hooks");
|
|
27141
|
+
var LINE_SEPARATOR = String.fromCharCode(8232);
|
|
27142
|
+
var PARAGRAPH_SEPARATOR = String.fromCharCode(8233);
|
|
27143
|
+
var contextStore = new import_node_async_hooks.AsyncLocalStorage();
|
|
27144
|
+
|
|
27145
|
+
// ../../shared/platform/src/hub-switch-refusal.ts
|
|
27146
|
+
var MESSAGES = {
|
|
27147
|
+
HUB_PROCESSES_DISABLED: `This hub does not run processes, automation commands, terminals or agents itself (${HUB_SWITCH_ENV.enableProcesses}=0).`,
|
|
27148
|
+
HUB_INTEGRATIONS_DISABLED: `Integrations are switched off on this hub (${HUB_SWITCH_ENV.enableIntegrations}=0).`,
|
|
27149
|
+
NO_COMPUTER_CONNECTED: `Connect a computer: this hub does not read project files or run git, gh or other commands itself (${HUB_SWITCH_ENV.enableProcesses}=0).`,
|
|
27150
|
+
HUB_PASSWORD_DISABLED: `Password sign-in is switched off on this hub (${HUB_SWITCH_ENV.enablePassword}=0).`,
|
|
27151
|
+
HUB_CONVEX_NOT_CONFIGURED: `GitHub sign-in is off on this hub: no Convex deployment is configured (${HUB_CONVEX_ADDRESS_ENV}).`,
|
|
27152
|
+
HUB_CONVEX_MISMATCH: `This hub starts GitHub sign-in only against its own Convex deployment (${HUB_CONVEX_ADDRESS_ENV}); the request named another.`
|
|
27153
|
+
};
|
|
27154
|
+
var VARIABLES = {
|
|
27155
|
+
HUB_PROCESSES_DISABLED: HUB_SWITCH_ENV.enableProcesses,
|
|
27156
|
+
HUB_INTEGRATIONS_DISABLED: HUB_SWITCH_ENV.enableIntegrations,
|
|
27157
|
+
NO_COMPUTER_CONNECTED: HUB_SWITCH_ENV.enableProcesses,
|
|
27158
|
+
HUB_PASSWORD_DISABLED: HUB_SWITCH_ENV.enablePassword,
|
|
27159
|
+
HUB_CONVEX_NOT_CONFIGURED: HUB_CONVEX_ADDRESS_ENV,
|
|
27160
|
+
HUB_CONVEX_MISMATCH: HUB_CONVEX_ADDRESS_ENV
|
|
27161
|
+
};
|
|
27162
|
+
|
|
27163
|
+
// ../../shared/platform/src/env-manifest.ts
|
|
27164
|
+
var ENV_MANIFEST = {
|
|
27165
|
+
// --- secret: credentials/tokens, never in a terminal ---
|
|
27166
|
+
SLAYZONE_HUB_TOKEN: "secret",
|
|
27167
|
+
// HMAC signing key for ALL of hub-auth (better-auth's session/cookie signer AND
|
|
27168
|
+
// the computer enroll/api-key credentials) — hence `HUB_AUTH`, not the former
|
|
27169
|
+
// `HUB_COMPUTER_TRANSPORT_SECRET`, which named just one of its consumers. Rule 2
|
|
27170
|
+
// (name by what the value IS). `_SECRET` = a key you HOLD and sign with, vs
|
|
27171
|
+
// `_TOKEN` = a bearer you PRESENT (SLAYZONE_HUB_TOKEN above).
|
|
27172
|
+
SLAYZONE_HUB_AUTH_SECRET: "secret",
|
|
27173
|
+
// Hub-scoped by VALUE: the hub mints it, and it embeds the hub's url and a
|
|
27174
|
+
// secret verified against the hub's `join_tokens`
|
|
27175
|
+
// row. `mintJoinToken` binds it to NO computer (computer_id stays NULL until
|
|
27176
|
+
// redemption), so any computer can redeem any unused token — there is no computer in
|
|
27177
|
+
// the value to name. Rule 2 (name by what the value IS, not who reads it).
|
|
27178
|
+
SLAYZONE_HUB_JOIN_TOKEN: "secret",
|
|
27179
|
+
// Retired before it was ever read: Entra sign-in authenticates with a certificate
|
|
27180
|
+
// (SLAYZONE_HUB_SSO_CERT_PATH / _KEY_PATH below), and boot refuses a hub that still
|
|
27181
|
+
// sets this (B7). Still classified `secret` so a leftover value never reaches a
|
|
27182
|
+
// terminal or a log.
|
|
27183
|
+
SLAYZONE_HUB_SSO_CLIENT_SECRET: "secret",
|
|
27184
|
+
// Per-boot bearer the desktop's `/cap` listener requires on every connection
|
|
27185
|
+
// (desktop-bridge-server.ts `verifyClient`). Loopback is NOT trusted there —
|
|
27186
|
+
// the threat this closes is another loopback process finding the port — so
|
|
27187
|
+
// this is a real credential, `secret`-scoped like the two above it.
|
|
27188
|
+
SLAYZONE_DESKTOP_BRIDGE_TOKEN: "secret",
|
|
27189
|
+
// The pre-rename `SLAYZONE_RUNNER_JOIN_TOKEN` is deliberately UNLISTED: it is
|
|
27190
|
+
// retired, not aliased. It never shipped (the published computer betas read
|
|
27191
|
+
// `SLAYZONE_JOIN_TOKEN`), so no operator env carries it and there is nothing to
|
|
27192
|
+
// keep working. Unlisted ⇒ the fail-closed default strips it, same as every
|
|
27193
|
+
// other retired name in this rename sweep.
|
|
27194
|
+
// --- infra: SlayZone wiring a terminal must not reinterpret ---
|
|
27195
|
+
// The hub to CONNECT to, host[:port] (computer, `slay`); the scheme derives from
|
|
27196
|
+
// the host (loopback → http/ws, else https/wss). A hub no longer reads this name:
|
|
27197
|
+
// it LISTENS on SLAYZONE_HUB_LISTEN_ADDRESS, and a hub that still gets the old
|
|
27198
|
+
// name refuses to boot. Stripping both here keeps a terminal's dial address from
|
|
27199
|
+
// ever being mistaken for another process's listen address.
|
|
27200
|
+
SLAYZONE_HUB_ADDRESS: "infra",
|
|
27201
|
+
SLAYZONE_HUB_LISTEN_ADDRESS: "infra",
|
|
27202
|
+
// hub-only; where the hub listens
|
|
27203
|
+
SLAYZONE_HUB_PUBLIC_ADDRESS: "infra",
|
|
27204
|
+
// hub-only; how other machines reach it, written into join tokens
|
|
27205
|
+
// Hub-only; the front ends (IPs/CIDRs, comma-separated) whose X-Forwarded-For names
|
|
27206
|
+
// the client (client-address.ts). Not `_ADDRESS`: these are networks, not an authority.
|
|
27207
|
+
SLAYZONE_HUB_TRUSTED_PROXIES: "infra",
|
|
27208
|
+
// Per-hub switches (0|1) replacing SLAYZONE_MODE. Infra: they describe THIS hub
|
|
27209
|
+
// process, so a task terminal must not inherit them (a hub started from one would
|
|
27210
|
+
// otherwise pick up its parent's switches by accident).
|
|
27211
|
+
SLAYZONE_HUB_REQUIRE_LOGIN: "infra",
|
|
27212
|
+
SLAYZONE_HUB_ENABLE_PASSWORD: "infra",
|
|
27213
|
+
SLAYZONE_HUB_ENABLE_SSO: "infra",
|
|
27214
|
+
SLAYZONE_HUB_ENABLE_PROCESSES: "infra",
|
|
27215
|
+
SLAYZONE_HUB_ENABLE_INTEGRATIONS: "infra",
|
|
27216
|
+
SLAYZONE_HUB_SSO_TENANT_ID: "infra",
|
|
27217
|
+
SLAYZONE_HUB_SSO_CLIENT_ID: "infra",
|
|
27218
|
+
// Paths to the Entra client certificate and its private key, and the MFA switch.
|
|
27219
|
+
// Infra, not secret: a path is not the key (the file's own permissions protect it),
|
|
27220
|
+
// but a task terminal must still never inherit a hub's SSO wiring.
|
|
27221
|
+
SLAYZONE_HUB_SSO_CERT_PATH: "infra",
|
|
27222
|
+
SLAYZONE_HUB_SSO_KEY_PATH: "infra",
|
|
27223
|
+
SLAYZONE_HUB_SSO_REQUIRE_MFA: "infra",
|
|
27224
|
+
// Operator-facing hub name (`slay hub ls`/`stop` address a hub by it). Infra,
|
|
27225
|
+
// not global: it identifies THIS hub process, so inheriting it into a task
|
|
27226
|
+
// terminal — which may target a different hub entirely — would mislabel.
|
|
27227
|
+
SLAYZONE_HUB_NAME: "infra",
|
|
27228
|
+
// Days THIS hub keeps session content (T-23, hub session-retention.ts); 0 = off, the
|
|
27229
|
+
// default. Infra like the switches: a hub started from a task terminal must not
|
|
27230
|
+
// inherit its parent's period and start deleting.
|
|
27231
|
+
SLAYZONE_HUB_SESSION_RETENTION_DAYS: "infra",
|
|
27232
|
+
// The Convex deployment (host[:port]) the hub starts GitHub sign-in against — never
|
|
27233
|
+
// a client-named one (T-99). Hub process config, set by the desktop app for its hub.
|
|
27234
|
+
SLAYZONE_HUB_CONVEX_ADDRESS: "infra",
|
|
27235
|
+
SLAYZONE_DESKTOP_BRIDGE_ADDRESS: "infra",
|
|
27236
|
+
// sidecar→desktop capability bridge + REST proxy
|
|
27237
|
+
SLAYZONE_SUPERVISED: "infra",
|
|
27238
|
+
// "started by the desktop app" flag
|
|
27239
|
+
SLAYZONE_USER_DATA_DIR: "infra",
|
|
27240
|
+
// Playwright Electron userData redirect
|
|
27241
|
+
SLAYZONE_SIDECAR_HOT_RESTART: "infra",
|
|
27242
|
+
SLAYZONE_BOOT_LOG_PATH: "infra",
|
|
27243
|
+
SLAYZONE_DEBUG_BOOT: "infra",
|
|
27244
|
+
SLAYZONE_REGISTER_DEV_PROTOCOL: "infra",
|
|
27245
|
+
SLAYZONE_NONINTERACTIVE: "infra",
|
|
27246
|
+
// Historical opt-IN, now a NO-OP: e2e runs computer-ON by default, so the boot
|
|
27247
|
+
// gate no longer reads this. Kept manifested because specs still set it to
|
|
27248
|
+
// document "this spec wants a computer" (an unmanifested var would be stripped
|
|
27249
|
+
// by sanitizeSpawnEnv, fail-closed, and read as a silent typo).
|
|
27250
|
+
SLAYZONE_E2E_ALLOW_COMPUTER: "infra",
|
|
27251
|
+
// Opt-OUT, and the only lever that changes behavior now: set by the specs that
|
|
27252
|
+
// must boot computer-less (remote-mode, sidecar-crash, and the "hub accepts
|
|
27253
|
+
// computers with none spawned" contract test).
|
|
27254
|
+
SLAYZONE_E2E_NO_COMPUTER: "infra",
|
|
27255
|
+
SLAYZONE_E2E_INSTALL_HOOKS: "infra",
|
|
27256
|
+
// Redirects the MACHINE-WIDE ~/.slayzone (hooks/, bin/) — the files shared by
|
|
27257
|
+
// every SlayZone process on the box, which deliberately do NOT follow
|
|
27258
|
+
// SLAYZONE_ROOT. Sandbox redirect for tests, same role as the per-provider
|
|
27259
|
+
// overrides below. `infra`: a spawned terminal must never inherit it, or an
|
|
27260
|
+
// agent would resolve a different notify.sh than the one wired into its hooks.
|
|
27261
|
+
SLAYZONE_MACHINE_DIR: "infra",
|
|
27262
|
+
// per-provider config/hook/plugin path overrides (test/sandbox redirects)
|
|
27263
|
+
SLAYZONE_CLAUDE_SETTINGS_PATH: "infra",
|
|
27264
|
+
SLAYZONE_GEMINI_SETTINGS_PATH: "infra",
|
|
27265
|
+
SLAYZONE_CODEX_HOOKS_PATH: "infra",
|
|
27266
|
+
SLAYZONE_ANTIGRAVITY_HOOKS_PATH: "infra",
|
|
27267
|
+
SLAYZONE_OPENCODE_PLUGIN_PATH: "infra",
|
|
27268
|
+
// --- identity: per-task/agent; overlay re-adds the correct value per spawn ---
|
|
27269
|
+
SLAYZONE_TASK_ID: "identity",
|
|
27270
|
+
SLAYZONE_PROJECT_ID: "identity",
|
|
27271
|
+
// `SLAYZONE_SESSION_ID` (a warm agent's pre-task identity) is RETIRED and
|
|
27272
|
+
// deliberately unlisted: warm agents boot with SLAYZONE_TASK_ID now.
|
|
27273
|
+
SLAYZONE_AGENT_ID: "identity",
|
|
27274
|
+
SLAYZONE_AGENT_HOOK_URL: "identity",
|
|
27275
|
+
// Pairs with _HOOK_URL (where to post / who is posting). The pre-v4 name
|
|
27276
|
+
// `SLAYZONE_HOOK_CONTEXT` is RETIRED and deliberately unlisted: unlisted =
|
|
27277
|
+
// stripped (fail closed), and the old-name value an older release channel's app
|
|
27278
|
+
// supplies rides its OWN per-spawn overlay (applied after sanitizeSpawnEnv), so
|
|
27279
|
+
// notify.sh v4's fallback read still sees it. A manifest entry would only let a
|
|
27280
|
+
// stale inherited value survive.
|
|
27281
|
+
SLAYZONE_AGENT_HOOK_CONTEXT: "identity",
|
|
27282
|
+
// --- global: same app-wide, safe to inherit verbatim ---
|
|
27283
|
+
SLAYZONE_RELEASE_CHANNEL: "global",
|
|
27284
|
+
// attribution-only (hook envelope channel)
|
|
27285
|
+
// INSTALL IDENTITY — which SlayZone install this process tree belongs to, not
|
|
27286
|
+
// which task/session. A child MUST resolve the SAME install as its parent: the
|
|
27287
|
+
// `slay` CLI derives its DB from BOTH (getStorageDir() reads ROOT; the
|
|
27288
|
+
// filename is `slayzone{.dev}.sqlite` per DEV — apps/cli/src/db.ts getDbPath).
|
|
27289
|
+
// Strip either and a bare `slay` in a dev-app task terminal exits 1
|
|
27290
|
+
// ("Database not found … Re-run with --dev"), and under an isolated e2e ROOT a
|
|
27291
|
+
// child would escape the sandbox to the real ~/.slayzone. Neither is a
|
|
27292
|
+
// credential and neither can be reinterpreted as a different kind of target
|
|
27293
|
+
// (unlike a hub address), so both inherit verbatim.
|
|
27294
|
+
//
|
|
27295
|
+
// ROOT is NOT covered by the identity overlay: buildMcpEnv sets it only inside
|
|
27296
|
+
// setHookIdentity(), gated on a hook-capable agent mode — a plain `terminal`
|
|
27297
|
+
// PTY, a non-hook agent, or a pre-warmed pool spawn gets no overlay at all.
|
|
27298
|
+
SLAYZONE_ROOT: "global",
|
|
27299
|
+
SLAYZONE_DEV: "global"
|
|
27300
|
+
};
|
|
27301
|
+
var NON_PREFIXED_INFRA = /* @__PURE__ */ new Set([
|
|
27302
|
+
"ELECTRON_RUN_AS_NODE",
|
|
27303
|
+
"NODE_PATH",
|
|
27304
|
+
"PLAYWRIGHT"
|
|
27305
|
+
]);
|
|
27306
|
+
function sanitizeSpawnEnv(base) {
|
|
27307
|
+
const out = {};
|
|
27308
|
+
for (const [key, value] of Object.entries(base)) {
|
|
27309
|
+
if (typeof value !== "string") continue;
|
|
27310
|
+
if (NON_PREFIXED_INFRA.has(key)) continue;
|
|
27311
|
+
if (key.startsWith("SLAYZONE_")) {
|
|
27312
|
+
if (ENV_MANIFEST[key] === "global") out[key] = value;
|
|
27313
|
+
continue;
|
|
27314
|
+
}
|
|
27315
|
+
out[key] = value;
|
|
27316
|
+
}
|
|
27317
|
+
return out;
|
|
27318
|
+
}
|
|
27319
|
+
|
|
27320
|
+
// ../../shared/platform/src/index.ts
|
|
27321
|
+
init_fs_utils();
|
|
27322
|
+
|
|
27323
|
+
// ../../shared/platform/src/migrations.ts
|
|
27324
|
+
init_dirs();
|
|
27325
|
+
|
|
27326
|
+
// ../../shared/platform/src/cli-install.ts
|
|
27327
|
+
var import_child_process = require("child_process");
|
|
27328
|
+
var import_util9 = require("util");
|
|
27329
|
+
var execFileAsync = (0, import_util9.promisify)(import_child_process.execFile);
|
|
27330
|
+
var WINDOWS_SHIM = [
|
|
27331
|
+
"@echo off",
|
|
27332
|
+
`for /f "tokens=1 delims=." %%v in ('node -e "process.stdout.write(process.versions.node)"') do set NODE_MAJOR=%%v`,
|
|
27333
|
+
"if %NODE_MAJOR% LEQ 22 (",
|
|
27334
|
+
' node --experimental-sqlite --no-warnings "%~dp0slay.js" %*',
|
|
27335
|
+
") else (",
|
|
27336
|
+
' node --no-warnings "%~dp0slay.js" %*',
|
|
27337
|
+
")",
|
|
27338
|
+
""
|
|
27339
|
+
].join("\r\n");
|
|
27340
|
+
|
|
27341
|
+
// ../../shared/platform/src/shell.ts
|
|
27342
|
+
var import_node_fs3 = __toESM(require("node:fs"), 1);
|
|
27343
|
+
var import_os2 = require("os");
|
|
27344
|
+
var shellOverride = null;
|
|
27345
|
+
function shellExists(shellPath) {
|
|
27346
|
+
if ((0, import_os2.platform)() === "win32") return import_node_fs3.default.existsSync(shellPath);
|
|
27347
|
+
try {
|
|
27348
|
+
import_node_fs3.default.accessSync(shellPath, import_node_fs3.default.constants.X_OK);
|
|
27349
|
+
return true;
|
|
27350
|
+
} catch {
|
|
27351
|
+
return false;
|
|
27352
|
+
}
|
|
27353
|
+
}
|
|
27354
|
+
function defaultShellForPlatform() {
|
|
27355
|
+
if ((0, import_os2.platform)() === "win32") return process.env.COMSPEC || "cmd.exe";
|
|
27356
|
+
if ((0, import_os2.platform)() === "darwin") return "/bin/zsh";
|
|
27357
|
+
return "/bin/bash";
|
|
27358
|
+
}
|
|
27359
|
+
function resolveUserShell() {
|
|
27360
|
+
if (shellOverride && shellExists(shellOverride)) return shellOverride;
|
|
27361
|
+
const fromEnv = process.env.SHELL?.trim();
|
|
27362
|
+
if (fromEnv && shellExists(fromEnv)) return fromEnv;
|
|
27363
|
+
try {
|
|
27364
|
+
const fromUser = (0, import_os2.userInfo)().shell?.trim();
|
|
27365
|
+
if (fromUser && shellExists(fromUser)) return fromUser;
|
|
27366
|
+
} catch {
|
|
27367
|
+
}
|
|
27368
|
+
return defaultShellForPlatform();
|
|
27369
|
+
}
|
|
27370
|
+
function getShellStartupArgs(shellPath) {
|
|
27371
|
+
if ((0, import_os2.platform)() === "win32") return [];
|
|
27372
|
+
const shell = shellPath.toLowerCase();
|
|
27373
|
+
const name = shell.split("/").pop() ?? shell;
|
|
27374
|
+
if (name === "zsh" || name === "bash" || name === "fish") {
|
|
27375
|
+
return ["-i", "-l"];
|
|
27376
|
+
}
|
|
27377
|
+
return [];
|
|
27378
|
+
}
|
|
27379
|
+
function buildShellInvocation(command) {
|
|
27380
|
+
const shell = resolveUserShell();
|
|
27381
|
+
if ((0, import_os2.platform)() === "win32") return { file: shell, args: ["/c", command] };
|
|
27382
|
+
const isFish = shell.endsWith("/fish");
|
|
27383
|
+
return { file: shell, args: [...isFish ? ["-i", "-l"] : ["-l"], "-c", command] };
|
|
27384
|
+
}
|
|
27385
|
+
|
|
27386
|
+
// ../../shared/platform/src/process-tree.ts
|
|
27387
|
+
var import_node_child_process = require("node:child_process");
|
|
27388
|
+
var PROCESS_TREE_DETACHED = process.platform !== "win32";
|
|
27389
|
+
function killPidTree(pid, signal = "SIGTERM") {
|
|
27390
|
+
if (process.platform === "win32") {
|
|
27391
|
+
try {
|
|
27392
|
+
(0, import_node_child_process.execFileSync)("taskkill", ["/T", "/F", "/PID", String(pid)]);
|
|
27393
|
+
} catch {
|
|
27394
|
+
}
|
|
27395
|
+
} else {
|
|
27396
|
+
try {
|
|
27397
|
+
process.kill(-pid, signal);
|
|
27398
|
+
} catch {
|
|
27399
|
+
}
|
|
27400
|
+
}
|
|
27401
|
+
}
|
|
27402
|
+
|
|
27403
|
+
// ../../shared/platform/src/login-shell-path.ts
|
|
27404
|
+
var import_node_child_process2 = require("node:child_process");
|
|
27405
|
+
var import_node_os3 = require("node:os");
|
|
27406
|
+
var import_node_path4 = __toESM(require("node:path"), 1);
|
|
27407
|
+
var LOGIN_SHELL_PATH_MARKER = "__SLAYZONE_PATH__";
|
|
27408
|
+
var LOGIN_SHELL_PATH_PROBE_TIMEOUT_MS = 5e3;
|
|
27409
|
+
function loginShellPathProbes(shell) {
|
|
27410
|
+
if ((0, import_node_os3.platform)() === "win32") return [];
|
|
27411
|
+
const value = shell.endsWith("/fish") ? '(string join ":" $PATH)' : '"$PATH"';
|
|
27412
|
+
const cmd = `printf '\\n${LOGIN_SHELL_PATH_MARKER}%s\\n' ${value}`;
|
|
27413
|
+
return [
|
|
27414
|
+
// Interactive login first — sources .zshrc/.bashrc where pnpm/nvm add PATH
|
|
27415
|
+
{ file: shell, args: [...getShellStartupArgs(shell), "-c", cmd] },
|
|
27416
|
+
// Fallback: login only — sources .zprofile/.bash_profile
|
|
27417
|
+
{ file: shell, args: ["-l", "-c", cmd] }
|
|
27418
|
+
];
|
|
27419
|
+
}
|
|
27420
|
+
function parseLoginShellPathOutput(stdout2) {
|
|
27421
|
+
for (const line of stdout2.split(/\r?\n/).reverse()) {
|
|
27422
|
+
if (!line.startsWith(LOGIN_SHELL_PATH_MARKER)) continue;
|
|
27423
|
+
return line.slice(LOGIN_SHELL_PATH_MARKER.length).trim() || null;
|
|
27424
|
+
}
|
|
27425
|
+
return null;
|
|
27426
|
+
}
|
|
27427
|
+
function mergePathLists(primary, secondary) {
|
|
27428
|
+
const entries = /* @__PURE__ */ new Set();
|
|
27429
|
+
for (const entry of [
|
|
27430
|
+
...primary.split(import_node_path4.default.delimiter),
|
|
27431
|
+
...(secondary ?? "").split(import_node_path4.default.delimiter)
|
|
27432
|
+
]) {
|
|
27433
|
+
if (entry) entries.add(entry);
|
|
27434
|
+
}
|
|
27435
|
+
return [...entries].join(import_node_path4.default.delimiter);
|
|
27436
|
+
}
|
|
27437
|
+
function runProbe(probe, env) {
|
|
27438
|
+
return new Promise((resolve7) => {
|
|
27439
|
+
const child = (0, import_node_child_process2.execFile)(
|
|
27440
|
+
probe.file,
|
|
27441
|
+
probe.args,
|
|
27442
|
+
{ encoding: "utf-8", timeout: LOGIN_SHELL_PATH_PROBE_TIMEOUT_MS, env },
|
|
27443
|
+
(err, stdout2) => resolve7(err ? null : parseLoginShellPathOutput(stdout2))
|
|
27444
|
+
);
|
|
27445
|
+
child.stdin?.end();
|
|
27446
|
+
});
|
|
27447
|
+
}
|
|
27448
|
+
async function probeLoginShellPath(opts = {}) {
|
|
27449
|
+
for (const probe of loginShellPathProbes(opts.shell ?? resolveUserShell())) {
|
|
27450
|
+
const probed = await runProbe(probe, opts.env ?? process.env);
|
|
27451
|
+
if (probed) return probed;
|
|
27452
|
+
}
|
|
27453
|
+
return null;
|
|
27454
|
+
}
|
|
27455
|
+
|
|
27456
|
+
// ../../shared/platform/src/slz-file-url.ts
|
|
27457
|
+
var SLZ_FILE_HOST = "app";
|
|
27458
|
+
var SLZ_FILE_PREFIX = `slz-file://${SLZ_FILE_HOST}`;
|
|
27459
|
+
|
|
27460
|
+
// ../../shared/platform/src/ipc-dedup.ts
|
|
27461
|
+
var UNCHANGED = Object.freeze({ __ipc_unchanged__: true });
|
|
27462
|
+
|
|
27463
|
+
// src/login-shell-path.ts
|
|
27464
|
+
async function adoptLoginShellPath(opts = {}) {
|
|
27465
|
+
const env = opts.env ?? process.env;
|
|
27466
|
+
const probed = await probeLoginShellPath({ env, ...opts.shell ? { shell: opts.shell } : {} });
|
|
27467
|
+
if (!probed) return null;
|
|
27468
|
+
const before = new Set((env.PATH ?? "").split(import_node_path5.delimiter));
|
|
27469
|
+
env.PATH = mergePathLists(probed, env.PATH);
|
|
27470
|
+
return { added: env.PATH.split(import_node_path5.delimiter).filter((entry) => !before.has(entry)) };
|
|
27471
|
+
}
|
|
27472
|
+
|
|
27473
|
+
// src/main.ts
|
|
27474
|
+
var import_node_fs10 = require("node:fs");
|
|
27475
|
+
var import_node_path13 = require("node:path");
|
|
27476
|
+
|
|
27477
|
+
// src/request-log.ts
|
|
27478
|
+
var import_node_fs4 = require("node:fs");
|
|
27479
|
+
var import_node_path6 = require("node:path");
|
|
27480
|
+
var REQUEST_LOG_DIR_NAME = "request-log";
|
|
27481
|
+
var REQUEST_LOG_FILE_NAME = "requests.log";
|
|
27482
|
+
var DEFAULT_MAX_BYTES = 10 * 1024 * 1024;
|
|
27483
|
+
var DEFAULT_KEEP = 9;
|
|
27484
|
+
var MAX_STRING = 300;
|
|
27485
|
+
function clip(value) {
|
|
27486
|
+
if (typeof value !== "string") return void 0;
|
|
27487
|
+
return value.length > MAX_STRING ? `${value.slice(0, MAX_STRING)}\u2026` : value;
|
|
27488
|
+
}
|
|
27489
|
+
function programName(command) {
|
|
27490
|
+
if (typeof command !== "string") return void 0;
|
|
27491
|
+
const first = command.trim().split(/\s+/)[0] ?? "";
|
|
27492
|
+
const base = first.split(/[\\/]/).pop() ?? "";
|
|
27493
|
+
return /^[A-Za-z0-9._+-]{1,64}$/.test(base) ? base : void 0;
|
|
27494
|
+
}
|
|
27495
|
+
function ghSubcommand(args) {
|
|
27496
|
+
if (!Array.isArray(args)) return void 0;
|
|
27497
|
+
const words = [];
|
|
27498
|
+
for (const a of args.slice(0, 2)) {
|
|
27499
|
+
if (typeof a !== "string" || !/^[a-z][a-z-]{0,31}$/.test(a)) break;
|
|
27500
|
+
words.push(a);
|
|
27501
|
+
}
|
|
27502
|
+
return words.length > 0 ? words.join(" ") : void 0;
|
|
27503
|
+
}
|
|
27504
|
+
var TARGET_KEYS = [
|
|
27505
|
+
"sessionId",
|
|
27506
|
+
"warmId",
|
|
27507
|
+
"watchId",
|
|
27508
|
+
"projectId",
|
|
27509
|
+
"cwd",
|
|
27510
|
+
"path",
|
|
27511
|
+
"rootPath",
|
|
27512
|
+
"filePath",
|
|
27513
|
+
"dirPath",
|
|
27514
|
+
"targetPath",
|
|
27515
|
+
"oldPath",
|
|
27516
|
+
"newPath",
|
|
27517
|
+
"srcPath",
|
|
27518
|
+
"destPath",
|
|
27519
|
+
"absoluteSrc",
|
|
27520
|
+
"targetDir",
|
|
27521
|
+
"repoPath",
|
|
27522
|
+
"projectPath",
|
|
27523
|
+
"worktreePath",
|
|
27524
|
+
"branch",
|
|
27525
|
+
"signal"
|
|
27526
|
+
];
|
|
27527
|
+
function summarizeTarget(method, params) {
|
|
27528
|
+
if (params === null || typeof params !== "object") return {};
|
|
27529
|
+
const p = params;
|
|
27530
|
+
const out = {};
|
|
27531
|
+
for (const key of TARGET_KEYS) {
|
|
27532
|
+
const v = clip(p[key]);
|
|
27533
|
+
if (v !== void 0) out[key] = v;
|
|
27534
|
+
}
|
|
27535
|
+
const program = programName(p.command);
|
|
27536
|
+
if (program) out.program = program;
|
|
27537
|
+
if (p.shell === true) out.shell = true;
|
|
27538
|
+
if (method === "gh.exec") {
|
|
27539
|
+
const sub = ghSubcommand(p.args);
|
|
27540
|
+
if (sub) out.subcommand = sub;
|
|
27541
|
+
}
|
|
27542
|
+
if (Array.isArray(p.roots)) {
|
|
27543
|
+
out.roots = p.roots.slice(0, 20).map(clip).filter((r) => r !== void 0);
|
|
27544
|
+
}
|
|
27545
|
+
return out;
|
|
27546
|
+
}
|
|
27547
|
+
function createRequestLog(opts) {
|
|
27548
|
+
const dir = (0, import_node_path6.join)(opts.stateDir, REQUEST_LOG_DIR_NAME);
|
|
27549
|
+
const filePath = (0, import_node_path6.join)(dir, REQUEST_LOG_FILE_NAME);
|
|
27550
|
+
const maxBytes = opts.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
27551
|
+
const keep = Math.max(1, opts.keep ?? DEFAULT_KEEP);
|
|
27552
|
+
const now = opts.now ?? (() => /* @__PURE__ */ new Date());
|
|
27553
|
+
(0, import_node_fs4.mkdirSync)(dir, { recursive: true, mode: 448 });
|
|
27554
|
+
(0, import_node_fs4.chmodSync)(dir, 448);
|
|
27555
|
+
(0, import_node_fs4.closeSync)((0, import_node_fs4.openSync)(filePath, "a", 384));
|
|
27556
|
+
(0, import_node_fs4.chmodSync)(filePath, 384);
|
|
27557
|
+
let size = (0, import_node_fs4.statSync)(filePath).size;
|
|
27558
|
+
const rotate = () => {
|
|
27559
|
+
for (let i = keep - 1; i >= 1; i--) {
|
|
27560
|
+
try {
|
|
27561
|
+
(0, import_node_fs4.renameSync)(`${filePath}.${i}`, `${filePath}.${i + 1}`);
|
|
27562
|
+
} catch {
|
|
27563
|
+
}
|
|
27564
|
+
}
|
|
27565
|
+
(0, import_node_fs4.renameSync)(filePath, `${filePath}.1`);
|
|
27566
|
+
size = 0;
|
|
27567
|
+
};
|
|
27568
|
+
const append = (line) => {
|
|
27569
|
+
const fd = (0, import_node_fs4.openSync)(filePath, "a", 384);
|
|
27570
|
+
try {
|
|
27571
|
+
(0, import_node_fs4.writeSync)(fd, line);
|
|
27572
|
+
} finally {
|
|
27573
|
+
(0, import_node_fs4.closeSync)(fd);
|
|
27574
|
+
}
|
|
27575
|
+
size += Buffer.byteLength(line);
|
|
27576
|
+
};
|
|
27577
|
+
return {
|
|
27578
|
+
filePath,
|
|
27579
|
+
record(entry) {
|
|
27580
|
+
try {
|
|
27581
|
+
const line = `${JSON.stringify({
|
|
27582
|
+
ts: now().toISOString(),
|
|
27583
|
+
hub: entry.hub,
|
|
27584
|
+
method: entry.method,
|
|
27585
|
+
actor: entry.actor,
|
|
27586
|
+
target: summarizeTarget(entry.method, entry.params),
|
|
27587
|
+
result: entry.result,
|
|
27588
|
+
...entry.code !== void 0 ? { code: entry.code } : {},
|
|
27589
|
+
...entry.reason !== void 0 ? { reason: clip(entry.reason) } : {}
|
|
27590
|
+
})}
|
|
27591
|
+
`;
|
|
27592
|
+
if (size > 0 && size + Buffer.byteLength(line) > maxBytes) rotate();
|
|
27593
|
+
append(line);
|
|
27594
|
+
} catch (err) {
|
|
27595
|
+
opts.onError?.(err);
|
|
27596
|
+
}
|
|
27597
|
+
}
|
|
27598
|
+
};
|
|
27599
|
+
}
|
|
27600
|
+
|
|
27601
|
+
// src/host-id.ts
|
|
27602
|
+
var import_node_crypto2 = require("node:crypto");
|
|
27603
|
+
var import_promises4 = require("node:fs/promises");
|
|
27604
|
+
var import_node_os4 = require("node:os");
|
|
27605
|
+
var import_node_path7 = require("node:path");
|
|
27606
|
+
function slayzoneRootDir2() {
|
|
27607
|
+
if (process.env.SLAYZONE_ROOT) return process.env.SLAYZONE_ROOT;
|
|
27608
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? (0, import_node_os4.homedir)();
|
|
27609
|
+
return (0, import_node_path7.join)(home, ".slayzone");
|
|
27610
|
+
}
|
|
27611
|
+
function hostIdFilePath(baseDir) {
|
|
27612
|
+
return (0, import_node_path7.join)(baseDir ?? slayzoneRootDir2(), "computer.host.json");
|
|
27613
|
+
}
|
|
27614
|
+
function legacyHostIdFilePath(baseDir) {
|
|
27615
|
+
return (0, import_node_path7.join)(baseDir ?? slayzoneRootDir2(), "computer.host-id.json");
|
|
27616
|
+
}
|
|
27617
|
+
function sharedHostIdPath() {
|
|
27618
|
+
return process.platform === "win32" ? null : "/tmp/slayzone-host-id";
|
|
27619
|
+
}
|
|
27620
|
+
var hostIdFileSchema = external_exports.object({ hostId: external_exports.string().min(1), updatedAt: external_exports.number() });
|
|
27621
|
+
async function readDurable(filePath) {
|
|
27622
|
+
try {
|
|
27623
|
+
const parsed = hostIdFileSchema.safeParse(JSON.parse(await (0, import_promises4.readFile)(filePath, "utf-8")));
|
|
27624
|
+
return parsed.success ? parsed.data.hostId : null;
|
|
27625
|
+
} catch {
|
|
27626
|
+
return null;
|
|
27627
|
+
}
|
|
27628
|
+
}
|
|
27629
|
+
async function writeDurable(filePath, hostId) {
|
|
27630
|
+
await (0, import_promises4.mkdir)((0, import_node_path7.dirname)(filePath), { recursive: true, mode: 448 });
|
|
27631
|
+
const tmpPath = `${filePath}.tmp-${process.pid}`;
|
|
27632
|
+
await (0, import_promises4.writeFile)(tmpPath, `${JSON.stringify({ hostId, updatedAt: Date.now() }, null, 2)}
|
|
27633
|
+
`, {
|
|
27634
|
+
mode: 384
|
|
27635
|
+
});
|
|
27636
|
+
try {
|
|
27637
|
+
await (0, import_promises4.rename)(tmpPath, filePath);
|
|
27638
|
+
} catch (err) {
|
|
27639
|
+
await (0, import_promises4.rm)(tmpPath, { force: true });
|
|
27640
|
+
throw err;
|
|
27641
|
+
}
|
|
27642
|
+
}
|
|
27643
|
+
async function readShared(sharedPath) {
|
|
27644
|
+
try {
|
|
27645
|
+
const raw = (await (0, import_promises4.readFile)(sharedPath, "utf-8")).trim();
|
|
27646
|
+
return raw.length > 0 ? raw : null;
|
|
27647
|
+
} catch {
|
|
27648
|
+
return null;
|
|
27649
|
+
}
|
|
27650
|
+
}
|
|
27651
|
+
async function writeShared(sharedPath, hostId) {
|
|
27652
|
+
try {
|
|
27653
|
+
const fh2 = await (0, import_promises4.open)(sharedPath, import_promises4.constants.O_WRONLY | import_promises4.constants.O_CREAT | import_promises4.constants.O_EXCL);
|
|
27654
|
+
try {
|
|
27655
|
+
await fh2.chmod(438);
|
|
27656
|
+
await fh2.writeFile(`${hostId}
|
|
27657
|
+
`);
|
|
27658
|
+
} finally {
|
|
27659
|
+
await fh2.close();
|
|
26523
27660
|
}
|
|
26524
27661
|
return;
|
|
26525
27662
|
} catch (err) {
|
|
26526
27663
|
if (err.code !== "EEXIST") throw err;
|
|
26527
27664
|
}
|
|
26528
|
-
const fh = await (0,
|
|
27665
|
+
const fh = await (0, import_promises4.open)(sharedPath, import_promises4.constants.O_WRONLY | import_promises4.constants.O_TRUNC);
|
|
26529
27666
|
try {
|
|
26530
27667
|
await fh.writeFile(`${hostId}
|
|
26531
27668
|
`);
|
|
@@ -26640,50 +27777,50 @@ function createAgentHookServer(ctx) {
|
|
|
26640
27777
|
}
|
|
26641
27778
|
|
|
26642
27779
|
// src/handlers/fs.ts
|
|
26643
|
-
var
|
|
26644
|
-
var
|
|
26645
|
-
var
|
|
26646
|
-
var
|
|
27780
|
+
var import_node_fs6 = require("node:fs");
|
|
27781
|
+
var import_promises6 = require("node:fs/promises");
|
|
27782
|
+
var import_node_os5 = require("node:os");
|
|
27783
|
+
var import_node_path9 = require("node:path");
|
|
26647
27784
|
|
|
26648
27785
|
// ../../domains/file-editor/src/server/file-ops.ts
|
|
26649
|
-
var
|
|
26650
|
-
var
|
|
26651
|
-
var
|
|
27786
|
+
var fs3 = __toESM(require("node:fs"), 1);
|
|
27787
|
+
var path4 = __toESM(require("node:path"), 1);
|
|
27788
|
+
var import_node_child_process3 = require("node:child_process");
|
|
26652
27789
|
var import_ignore = __toESM(require_ignore(), 1);
|
|
26653
27790
|
var ALWAYS_IGNORED = /* @__PURE__ */ new Set([".git", ".DS_Store"]);
|
|
26654
27791
|
var MAX_FILE_SIZE = 1 * 1024 * 1024;
|
|
26655
27792
|
var FORCE_MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
26656
27793
|
function assertWithinRoot(root, target) {
|
|
26657
|
-
const resolved =
|
|
26658
|
-
if (!resolved.startsWith(
|
|
27794
|
+
const resolved = path4.resolve(root, target);
|
|
27795
|
+
if (!resolved.startsWith(path4.resolve(root) + path4.sep) && resolved !== path4.resolve(root)) {
|
|
26659
27796
|
throw new Error("Path traversal denied");
|
|
26660
27797
|
}
|
|
26661
27798
|
return resolved;
|
|
26662
27799
|
}
|
|
26663
27800
|
var ignoreCache = /* @__PURE__ */ new Map();
|
|
26664
27801
|
function getIgnoreFilter(rootPath) {
|
|
26665
|
-
const root =
|
|
26666
|
-
const gitignorePath =
|
|
27802
|
+
const root = path4.resolve(rootPath);
|
|
27803
|
+
const gitignorePath = path4.join(root, ".gitignore");
|
|
26667
27804
|
let mtime = 0;
|
|
26668
27805
|
try {
|
|
26669
|
-
mtime =
|
|
27806
|
+
mtime = fs3.statSync(gitignorePath).mtimeMs;
|
|
26670
27807
|
} catch {
|
|
26671
27808
|
}
|
|
26672
27809
|
const cached2 = ignoreCache.get(root);
|
|
26673
27810
|
if (cached2 && cached2.mtime === mtime) return cached2.ig;
|
|
26674
27811
|
const ig = (0, import_ignore.default)();
|
|
26675
27812
|
try {
|
|
26676
|
-
ig.add(
|
|
27813
|
+
ig.add(fs3.readFileSync(gitignorePath, "utf-8"));
|
|
26677
27814
|
} catch {
|
|
26678
27815
|
}
|
|
26679
27816
|
ignoreCache.set(root, { ig, mtime });
|
|
26680
27817
|
return ig;
|
|
26681
27818
|
}
|
|
26682
27819
|
function invalidateIgnoreCache(rootPath) {
|
|
26683
|
-
ignoreCache.delete(
|
|
27820
|
+
ignoreCache.delete(path4.resolve(rootPath));
|
|
26684
27821
|
}
|
|
26685
27822
|
function isIgnored(rootPath, relativePath, isDir) {
|
|
26686
|
-
if (ALWAYS_IGNORED.has(
|
|
27823
|
+
if (ALWAYS_IGNORED.has(path4.basename(relativePath))) return true;
|
|
26687
27824
|
const ig = getIgnoreFilter(rootPath);
|
|
26688
27825
|
return ig.ignores(isDir ? relativePath + "/" : relativePath);
|
|
26689
27826
|
}
|
|
@@ -26691,10 +27828,10 @@ function escapeRegExp(s) {
|
|
|
26691
27828
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
26692
27829
|
}
|
|
26693
27830
|
function readDir(rootPath, dirPath) {
|
|
26694
|
-
const abs = dirPath ? assertWithinRoot(rootPath, dirPath) :
|
|
27831
|
+
const abs = dirPath ? assertWithinRoot(rootPath, dirPath) : path4.resolve(rootPath);
|
|
26695
27832
|
let entries;
|
|
26696
27833
|
try {
|
|
26697
|
-
entries =
|
|
27834
|
+
entries = fs3.readdirSync(abs, { withFileTypes: true });
|
|
26698
27835
|
} catch (error62) {
|
|
26699
27836
|
if (error62?.code === "ENOENT") return [];
|
|
26700
27837
|
throw error62;
|
|
@@ -26705,7 +27842,7 @@ function readDir(rootPath, dirPath) {
|
|
|
26705
27842
|
let isDir = e.isDirectory();
|
|
26706
27843
|
if (symlink) {
|
|
26707
27844
|
try {
|
|
26708
|
-
isDir =
|
|
27845
|
+
isDir = fs3.statSync(path4.join(abs, e.name)).isDirectory();
|
|
26709
27846
|
} catch {
|
|
26710
27847
|
return null;
|
|
26711
27848
|
}
|
|
@@ -26725,23 +27862,23 @@ function readDir(rootPath, dirPath) {
|
|
|
26725
27862
|
}
|
|
26726
27863
|
function readFile3(rootPath, filePath, force) {
|
|
26727
27864
|
const abs = assertWithinRoot(rootPath, filePath);
|
|
26728
|
-
const stat2 =
|
|
27865
|
+
const stat2 = fs3.statSync(abs);
|
|
26729
27866
|
if (stat2.size > FORCE_MAX_FILE_SIZE) {
|
|
26730
27867
|
return { content: null, tooLarge: true, sizeBytes: stat2.size };
|
|
26731
27868
|
}
|
|
26732
27869
|
if (!force && stat2.size > MAX_FILE_SIZE) {
|
|
26733
27870
|
return { content: null, tooLarge: true, sizeBytes: stat2.size };
|
|
26734
27871
|
}
|
|
26735
|
-
return { content:
|
|
27872
|
+
return { content: fs3.readFileSync(abs, "utf-8") };
|
|
26736
27873
|
}
|
|
26737
27874
|
function listAllFiles(rootPath) {
|
|
26738
|
-
const root =
|
|
27875
|
+
const root = path4.resolve(rootPath);
|
|
26739
27876
|
const ig = getIgnoreFilter(rootPath);
|
|
26740
27877
|
const results = [];
|
|
26741
27878
|
function walk(dir, prefix) {
|
|
26742
27879
|
let entries;
|
|
26743
27880
|
try {
|
|
26744
|
-
entries =
|
|
27881
|
+
entries = fs3.readdirSync(dir, { withFileTypes: true });
|
|
26745
27882
|
} catch {
|
|
26746
27883
|
return;
|
|
26747
27884
|
}
|
|
@@ -26750,7 +27887,7 @@ function listAllFiles(rootPath) {
|
|
|
26750
27887
|
if (ALWAYS_IGNORED.has(e.name)) continue;
|
|
26751
27888
|
if (ig.ignores(e.isDirectory() ? relPath + "/" : relPath)) continue;
|
|
26752
27889
|
if (e.isDirectory()) {
|
|
26753
|
-
walk(
|
|
27890
|
+
walk(path4.join(dir, e.name), relPath);
|
|
26754
27891
|
} else {
|
|
26755
27892
|
results.push(relPath);
|
|
26756
27893
|
}
|
|
@@ -26761,65 +27898,65 @@ function listAllFiles(rootPath) {
|
|
|
26761
27898
|
}
|
|
26762
27899
|
function writeFile3(rootPath, filePath, content) {
|
|
26763
27900
|
const abs = assertWithinRoot(rootPath, filePath);
|
|
26764
|
-
|
|
26765
|
-
|
|
27901
|
+
fs3.mkdirSync(path4.dirname(abs), { recursive: true });
|
|
27902
|
+
fs3.writeFileSync(abs, content, "utf-8");
|
|
26766
27903
|
}
|
|
26767
27904
|
function createFile(rootPath, filePath) {
|
|
26768
27905
|
const abs = assertWithinRoot(rootPath, filePath);
|
|
26769
|
-
if (
|
|
26770
|
-
|
|
26771
|
-
|
|
27906
|
+
if (fs3.existsSync(abs)) throw new Error("File already exists");
|
|
27907
|
+
fs3.mkdirSync(path4.dirname(abs), { recursive: true });
|
|
27908
|
+
fs3.writeFileSync(abs, "", "utf-8");
|
|
26772
27909
|
}
|
|
26773
27910
|
function createDir(rootPath, dirPath) {
|
|
26774
27911
|
const abs = assertWithinRoot(rootPath, dirPath);
|
|
26775
|
-
|
|
27912
|
+
fs3.mkdirSync(abs, { recursive: true });
|
|
26776
27913
|
}
|
|
26777
27914
|
function renamePath(rootPath, oldPath, newPath) {
|
|
26778
27915
|
const absOld = assertWithinRoot(rootPath, oldPath);
|
|
26779
27916
|
const absNew = assertWithinRoot(rootPath, newPath);
|
|
26780
|
-
if (
|
|
26781
|
-
|
|
27917
|
+
if (fs3.existsSync(absNew)) throw new Error("Target already exists");
|
|
27918
|
+
fs3.renameSync(absOld, absNew);
|
|
26782
27919
|
}
|
|
26783
27920
|
function deletePath(rootPath, targetPath) {
|
|
26784
27921
|
const abs = assertWithinRoot(rootPath, targetPath);
|
|
26785
|
-
|
|
27922
|
+
fs3.rmSync(abs, { recursive: true });
|
|
26786
27923
|
}
|
|
26787
27924
|
function copyIn(rootPath, absoluteSrc, targetDir) {
|
|
26788
|
-
const srcResolved =
|
|
26789
|
-
if (!
|
|
27925
|
+
const srcResolved = path4.resolve(absoluteSrc);
|
|
27926
|
+
if (!fs3.existsSync(srcResolved) || !fs3.statSync(srcResolved).isFile()) {
|
|
26790
27927
|
throw new Error("Source is not a file");
|
|
26791
27928
|
}
|
|
26792
|
-
const ext =
|
|
26793
|
-
const stem =
|
|
26794
|
-
const baseName =
|
|
27929
|
+
const ext = path4.extname(srcResolved);
|
|
27930
|
+
const stem = path4.basename(srcResolved, ext);
|
|
27931
|
+
const baseName = path4.basename(srcResolved);
|
|
26795
27932
|
const dirRel = (targetDir ?? "").trim();
|
|
26796
27933
|
if (dirRel) assertWithinRoot(rootPath, dirRel);
|
|
26797
27934
|
let relPath = dirRel ? `${dirRel}/${baseName}` : baseName;
|
|
26798
27935
|
let dest = assertWithinRoot(rootPath, relPath);
|
|
26799
27936
|
let i = 1;
|
|
26800
|
-
while (
|
|
27937
|
+
while (fs3.existsSync(dest)) {
|
|
26801
27938
|
const candidate = `${stem} (${i})${ext}`;
|
|
26802
27939
|
relPath = dirRel ? `${dirRel}/${candidate}` : candidate;
|
|
26803
27940
|
dest = assertWithinRoot(rootPath, relPath);
|
|
26804
27941
|
i++;
|
|
26805
27942
|
}
|
|
26806
|
-
|
|
26807
|
-
|
|
27943
|
+
fs3.mkdirSync(path4.dirname(dest), { recursive: true });
|
|
27944
|
+
fs3.copyFileSync(srcResolved, dest);
|
|
26808
27945
|
return relPath;
|
|
26809
27946
|
}
|
|
26810
27947
|
function copy(rootPath, srcPath, destPath) {
|
|
26811
27948
|
const absSrc = assertWithinRoot(rootPath, srcPath);
|
|
26812
27949
|
const absDest = assertWithinRoot(rootPath, destPath);
|
|
26813
|
-
|
|
27950
|
+
fs3.cpSync(absSrc, absDest, { recursive: true });
|
|
26814
27951
|
}
|
|
26815
27952
|
function gitStatus(rootPath) {
|
|
26816
|
-
const root =
|
|
26817
|
-
if (!
|
|
27953
|
+
const root = path4.resolve(rootPath);
|
|
27954
|
+
if (!fs3.existsSync(path4.join(root, ".git"))) {
|
|
26818
27955
|
return Promise.resolve({ files: {}, isGitRepo: false });
|
|
26819
27956
|
}
|
|
26820
27957
|
return new Promise((resolve7) => {
|
|
26821
27958
|
const chunks = [];
|
|
26822
|
-
const proc = (0,
|
|
27959
|
+
const proc = (0, import_node_child_process3.spawn)("git", ["status", "--porcelain"], { cwd: root });
|
|
26823
27960
|
proc.stdout.on("data", (chunk) => chunks.push(chunk));
|
|
26824
27961
|
proc.on("error", () => resolve7({ files: {}, isGitRepo: false }));
|
|
26825
27962
|
proc.on("close", (code) => {
|
|
@@ -26857,7 +27994,7 @@ function gitStatus(rootPath) {
|
|
|
26857
27994
|
}
|
|
26858
27995
|
function searchFiles(rootPath, query, options) {
|
|
26859
27996
|
if (!query) return [];
|
|
26860
|
-
const root =
|
|
27997
|
+
const root = path4.resolve(rootPath);
|
|
26861
27998
|
const ig = getIgnoreFilter(rootPath);
|
|
26862
27999
|
const matchCase = options?.matchCase ?? false;
|
|
26863
28000
|
const useRegex = options?.regex ?? false;
|
|
@@ -26875,7 +28012,7 @@ function searchFiles(rootPath, query, options) {
|
|
|
26875
28012
|
if (totalMatches >= maxResults) return;
|
|
26876
28013
|
let entries;
|
|
26877
28014
|
try {
|
|
26878
|
-
entries =
|
|
28015
|
+
entries = fs3.readdirSync(dir, { withFileTypes: true });
|
|
26879
28016
|
} catch {
|
|
26880
28017
|
return;
|
|
26881
28018
|
}
|
|
@@ -26885,7 +28022,7 @@ function searchFiles(rootPath, query, options) {
|
|
|
26885
28022
|
if (ALWAYS_IGNORED.has(e.name)) continue;
|
|
26886
28023
|
if (ig.ignores(e.isDirectory() ? relPath + "/" : relPath)) continue;
|
|
26887
28024
|
if (e.isDirectory()) {
|
|
26888
|
-
walk(
|
|
28025
|
+
walk(path4.join(dir, e.name), relPath);
|
|
26889
28026
|
} else {
|
|
26890
28027
|
searchFile(root, relPath, pattern, results);
|
|
26891
28028
|
}
|
|
@@ -26893,17 +28030,17 @@ function searchFiles(rootPath, query, options) {
|
|
|
26893
28030
|
}
|
|
26894
28031
|
function searchFile(rootDir, relPath, re, out) {
|
|
26895
28032
|
if (totalMatches >= maxResults) return;
|
|
26896
|
-
const abs =
|
|
28033
|
+
const abs = path4.join(rootDir, relPath);
|
|
26897
28034
|
let stat2;
|
|
26898
28035
|
try {
|
|
26899
|
-
stat2 =
|
|
28036
|
+
stat2 = fs3.statSync(abs);
|
|
26900
28037
|
} catch {
|
|
26901
28038
|
return;
|
|
26902
28039
|
}
|
|
26903
28040
|
if (stat2.size > MAX_FILE_SIZE) return;
|
|
26904
28041
|
let content;
|
|
26905
28042
|
try {
|
|
26906
|
-
content =
|
|
28043
|
+
content = fs3.readFileSync(abs, "utf-8");
|
|
26907
28044
|
} catch {
|
|
26908
28045
|
return;
|
|
26909
28046
|
}
|
|
@@ -26928,11 +28065,11 @@ function searchFiles(rootPath, query, options) {
|
|
|
26928
28065
|
}
|
|
26929
28066
|
|
|
26930
28067
|
// ../../domains/file-editor/src/server/watcher.ts
|
|
26931
|
-
var
|
|
26932
|
-
var
|
|
28068
|
+
var fs4 = __toESM(require("node:fs"), 1);
|
|
28069
|
+
var path5 = __toESM(require("node:path"), 1);
|
|
26933
28070
|
var watchers = /* @__PURE__ */ new Map();
|
|
26934
28071
|
function subscribeFileWatcher(rootPath, listener) {
|
|
26935
|
-
const root =
|
|
28072
|
+
const root = path5.resolve(rootPath);
|
|
26936
28073
|
const existing = watchers.get(root);
|
|
26937
28074
|
if (existing) {
|
|
26938
28075
|
existing.listeners.add(listener);
|
|
@@ -26941,11 +28078,11 @@ function subscribeFileWatcher(rootPath, listener) {
|
|
|
26941
28078
|
const debounceMap = /* @__PURE__ */ new Map();
|
|
26942
28079
|
const listeners = /* @__PURE__ */ new Set([listener]);
|
|
26943
28080
|
try {
|
|
26944
|
-
const watcher =
|
|
28081
|
+
const watcher = fs4.watch(root, { recursive: true }, (_eventType, filename) => {
|
|
26945
28082
|
if (!filename) return;
|
|
26946
28083
|
const relPath = filename.replace(/\\/g, "/");
|
|
26947
28084
|
if (ignoredSafely(root, relPath)) return;
|
|
26948
|
-
if (
|
|
28085
|
+
if (path5.basename(relPath) === ".gitignore") {
|
|
26949
28086
|
invalidateIgnoreCache(root);
|
|
26950
28087
|
}
|
|
26951
28088
|
const prev = debounceMap.get(relPath);
|
|
@@ -26954,8 +28091,8 @@ function subscribeFileWatcher(rootPath, listener) {
|
|
|
26954
28091
|
relPath,
|
|
26955
28092
|
setTimeout(() => {
|
|
26956
28093
|
debounceMap.delete(relPath);
|
|
26957
|
-
const abs =
|
|
26958
|
-
const exists =
|
|
28094
|
+
const abs = path5.join(root, relPath);
|
|
28095
|
+
const exists = fs4.existsSync(abs);
|
|
26959
28096
|
const event = {
|
|
26960
28097
|
type: exists ? "changed" : "deleted",
|
|
26961
28098
|
root,
|
|
@@ -26994,10 +28131,10 @@ function unsubscribe(root, listener) {
|
|
|
26994
28131
|
}
|
|
26995
28132
|
|
|
26996
28133
|
// ../../domains/file-editor/src/server/repo-discovery.ts
|
|
26997
|
-
var
|
|
26998
|
-
var
|
|
26999
|
-
var
|
|
27000
|
-
var
|
|
28134
|
+
var import_node_child_process4 = require("node:child_process");
|
|
28135
|
+
var import_node_fs5 = require("node:fs");
|
|
28136
|
+
var import_promises5 = require("node:fs/promises");
|
|
28137
|
+
var import_node_path8 = __toESM(require("node:path"), 1);
|
|
27001
28138
|
var DEFAULT_REPO_SCAN_DEPTH = 3;
|
|
27002
28139
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
27003
28140
|
"node_modules",
|
|
@@ -27022,7 +28159,7 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
27022
28159
|
]);
|
|
27023
28160
|
function git(args, cwd) {
|
|
27024
28161
|
return new Promise((resolve7, reject) => {
|
|
27025
|
-
(0,
|
|
28162
|
+
(0, import_node_child_process4.execFile)("git", args, { cwd, maxBuffer: 8 * 1024 * 1024 }, (err, stdout2) => {
|
|
27026
28163
|
if (err) reject(err);
|
|
27027
28164
|
else resolve7(stdout2);
|
|
27028
28165
|
});
|
|
@@ -27042,17 +28179,17 @@ async function walkForGitRoots(root, maxDepth) {
|
|
|
27042
28179
|
if (depth > maxDepth) return;
|
|
27043
28180
|
let entries;
|
|
27044
28181
|
try {
|
|
27045
|
-
entries = await (0,
|
|
28182
|
+
entries = await (0, import_promises5.readdir)(dir);
|
|
27046
28183
|
} catch {
|
|
27047
28184
|
return;
|
|
27048
28185
|
}
|
|
27049
28186
|
await Promise.all(
|
|
27050
28187
|
entries.map(async (entry) => {
|
|
27051
28188
|
if (SKIP_DIRS.has(entry) || entry.startsWith(".")) return;
|
|
27052
|
-
const full =
|
|
28189
|
+
const full = import_node_path8.default.join(dir, entry);
|
|
27053
28190
|
let s;
|
|
27054
28191
|
try {
|
|
27055
|
-
s = await (0,
|
|
28192
|
+
s = await (0, import_promises5.lstat)(full);
|
|
27056
28193
|
} catch {
|
|
27057
28194
|
return;
|
|
27058
28195
|
}
|
|
@@ -27070,7 +28207,7 @@ async function walkForGitRoots(root, maxDepth) {
|
|
|
27070
28207
|
return found;
|
|
27071
28208
|
}
|
|
27072
28209
|
async function listSubmodules(repoPath) {
|
|
27073
|
-
if (!(0,
|
|
28210
|
+
if (!(0, import_node_fs5.existsSync)(import_node_path8.default.join(repoPath, ".gitmodules"))) return [];
|
|
27074
28211
|
let out;
|
|
27075
28212
|
try {
|
|
27076
28213
|
out = await git(["submodule", "status", "--recursive"], repoPath);
|
|
@@ -27081,13 +28218,13 @@ async function listSubmodules(repoPath) {
|
|
|
27081
28218
|
for (const line of out.split("\n")) {
|
|
27082
28219
|
if (!line.trim()) continue;
|
|
27083
28220
|
const m = line.match(/^[\s\-+U][0-9a-f]+\s+(\S+)/);
|
|
27084
|
-
if (m) subs.push(
|
|
28221
|
+
if (m) subs.push(import_node_path8.default.join(repoPath, m[1]));
|
|
27085
28222
|
}
|
|
27086
28223
|
return subs;
|
|
27087
28224
|
}
|
|
27088
28225
|
function nameFor(rootPath, repoPath) {
|
|
27089
|
-
if (repoPath === rootPath) return
|
|
27090
|
-
return
|
|
28226
|
+
if (repoPath === rootPath) return import_node_path8.default.basename(rootPath);
|
|
28227
|
+
return import_node_path8.default.relative(rootPath, repoPath) || import_node_path8.default.basename(repoPath);
|
|
27091
28228
|
}
|
|
27092
28229
|
async function discoverRepos(rootPath, maxDepth = DEFAULT_REPO_SCAN_DEPTH) {
|
|
27093
28230
|
const rootIsGit = await isGitRepoDir(rootPath);
|
|
@@ -27100,7 +28237,7 @@ async function discoverRepos(rootPath, maxDepth = DEFAULT_REPO_SCAN_DEPTH) {
|
|
|
27100
28237
|
name: nameFor(rootPath, repoPath),
|
|
27101
28238
|
kind: repoPath === rootPath ? "project-root" : "child-repo",
|
|
27102
28239
|
parentPath: null,
|
|
27103
|
-
hasGitmodules: (0,
|
|
28240
|
+
hasGitmodules: (0, import_node_fs5.existsSync)(import_node_path8.default.join(repoPath, ".gitmodules"))
|
|
27104
28241
|
});
|
|
27105
28242
|
for (const subPath of await listSubmodules(repoPath)) {
|
|
27106
28243
|
entries.push({
|
|
@@ -27108,7 +28245,7 @@ async function discoverRepos(rootPath, maxDepth = DEFAULT_REPO_SCAN_DEPTH) {
|
|
|
27108
28245
|
name: nameFor(rootPath, subPath),
|
|
27109
28246
|
kind: "submodule",
|
|
27110
28247
|
parentPath: repoPath,
|
|
27111
|
-
hasGitmodules: (0,
|
|
28248
|
+
hasGitmodules: (0, import_node_fs5.existsSync)(import_node_path8.default.join(subPath, ".gitmodules"))
|
|
27112
28249
|
});
|
|
27113
28250
|
}
|
|
27114
28251
|
})
|
|
@@ -27121,92 +28258,28 @@ async function discoverRepos(rootPath, maxDepth = DEFAULT_REPO_SCAN_DEPTH) {
|
|
|
27121
28258
|
if (a.parentPath !== null && b.parentPath === null) return 1;
|
|
27122
28259
|
return a.path.localeCompare(b.path);
|
|
27123
28260
|
});
|
|
27124
|
-
return entries;
|
|
27125
|
-
}
|
|
27126
|
-
|
|
27127
|
-
// ../../shared/platform/src/hub-switches.ts
|
|
27128
|
-
var HUB_SWITCH_ENV = {
|
|
27129
|
-
requireLogin: "SLAYZONE_HUB_REQUIRE_LOGIN",
|
|
27130
|
-
enablePassword: "SLAYZONE_HUB_ENABLE_PASSWORD",
|
|
27131
|
-
enableSso: "SLAYZONE_HUB_ENABLE_SSO",
|
|
27132
|
-
enableProcesses: "SLAYZONE_HUB_ENABLE_PROCESSES",
|
|
27133
|
-
enableIntegrations: "SLAYZONE_HUB_ENABLE_INTEGRATIONS"
|
|
27134
|
-
};
|
|
27135
|
-
var HUB_SSO_ENV = {
|
|
27136
|
-
tenantId: "SLAYZONE_HUB_SSO_TENANT_ID",
|
|
27137
|
-
clientId: "SLAYZONE_HUB_SSO_CLIENT_ID",
|
|
27138
|
-
/** PEM certificate registered on the Entra app (its SHA-256 thumbprint goes in each assertion). Env only. */
|
|
27139
|
-
certPath: "SLAYZONE_HUB_SSO_CERT_PATH",
|
|
27140
|
-
/** PEM private key of that certificate (RSA, unencrypted). Env only. */
|
|
27141
|
-
keyPath: "SLAYZONE_HUB_SSO_KEY_PATH",
|
|
27142
|
-
/** `0|1`, default `1`: refuse a sign-in whose ID token's `amr` does not contain `mfa`. */
|
|
27143
|
-
requireMfa: "SLAYZONE_HUB_SSO_REQUIRE_MFA"
|
|
27144
|
-
};
|
|
27145
|
-
var RETIRED_HUB_SSO_CLIENT_SECRET_ENV = "SLAYZONE_HUB_SSO_CLIENT_SECRET";
|
|
27146
|
-
var HUB_ADDRESS_ENV = {
|
|
27147
|
-
/** Where the hub LISTENS. Renamed from `SLAYZONE_HUB_ADDRESS`, which now only
|
|
27148
|
-
* means "the hub to connect to" on a computer / `slay`. */
|
|
27149
|
-
listen: "SLAYZONE_HUB_LISTEN_ADDRESS",
|
|
27150
|
-
/** How OTHER machines reach the hub (`host[:port]`, no scheme). */
|
|
27151
|
-
public: "SLAYZONE_HUB_PUBLIC_ADDRESS",
|
|
27152
|
-
/** Signing key of hub-auth. */
|
|
27153
|
-
authSecret: "SLAYZONE_HUB_AUTH_SECRET"
|
|
27154
|
-
};
|
|
27155
|
-
var LEGACY_HUB_LISTEN_ENV = "SLAYZONE_HUB_ADDRESS";
|
|
27156
|
-
var HUB_SESSION_RETENTION_DAYS_ENV = "SLAYZONE_HUB_SESSION_RETENTION_DAYS";
|
|
27157
|
-
var HUB_ALLOWED_WEB_ORIGINS_ENV = "SLAYZONE_HUB_ALLOWED_WEB_ORIGINS";
|
|
27158
|
-
var HUB_TRUSTED_PROXIES_ENV = "SLAYZONE_HUB_TRUSTED_PROXIES";
|
|
27159
|
-
var HUB_CONVEX_ADDRESS_ENV = "SLAYZONE_HUB_CONVEX_ADDRESS";
|
|
27160
|
-
var HUB_PROCESS_ENV = [
|
|
27161
|
-
...Object.values(HUB_SWITCH_ENV),
|
|
27162
|
-
...Object.values(HUB_SSO_ENV),
|
|
27163
|
-
RETIRED_HUB_SSO_CLIENT_SECRET_ENV,
|
|
27164
|
-
...Object.values(HUB_ADDRESS_ENV),
|
|
27165
|
-
LEGACY_HUB_LISTEN_ENV,
|
|
27166
|
-
HUB_ALLOWED_WEB_ORIGINS_ENV,
|
|
27167
|
-
HUB_TRUSTED_PROXIES_ENV,
|
|
27168
|
-
HUB_SESSION_RETENTION_DAYS_ENV,
|
|
27169
|
-
HUB_CONVEX_ADDRESS_ENV
|
|
27170
|
-
];
|
|
27171
|
-
|
|
27172
|
-
// ../../shared/platform/src/security-log.ts
|
|
27173
|
-
var import_node_async_hooks = require("node:async_hooks");
|
|
27174
|
-
var LINE_SEPARATOR = String.fromCharCode(8232);
|
|
27175
|
-
var PARAGRAPH_SEPARATOR = String.fromCharCode(8233);
|
|
27176
|
-
var contextStore = new import_node_async_hooks.AsyncLocalStorage();
|
|
27177
|
-
|
|
27178
|
-
// ../../shared/platform/src/hub-switch-refusal.ts
|
|
27179
|
-
var MESSAGES = {
|
|
27180
|
-
HUB_PROCESSES_DISABLED: `This hub does not run processes, automation commands, terminals or agents itself (${HUB_SWITCH_ENV.enableProcesses}=0).`,
|
|
27181
|
-
HUB_INTEGRATIONS_DISABLED: `Integrations are switched off on this hub (${HUB_SWITCH_ENV.enableIntegrations}=0).`,
|
|
27182
|
-
NO_COMPUTER_CONNECTED: `Connect a computer: this hub does not read project files or run git, gh or other commands itself (${HUB_SWITCH_ENV.enableProcesses}=0).`,
|
|
27183
|
-
HUB_PASSWORD_DISABLED: `Password sign-in is switched off on this hub (${HUB_SWITCH_ENV.enablePassword}=0).`,
|
|
27184
|
-
HUB_CONVEX_NOT_CONFIGURED: `GitHub sign-in is off on this hub: no Convex deployment is configured (${HUB_CONVEX_ADDRESS_ENV}).`,
|
|
27185
|
-
HUB_CONVEX_MISMATCH: `This hub starts GitHub sign-in only against its own Convex deployment (${HUB_CONVEX_ADDRESS_ENV}); the request named another.`
|
|
27186
|
-
};
|
|
27187
|
-
var VARIABLES = {
|
|
27188
|
-
HUB_PROCESSES_DISABLED: HUB_SWITCH_ENV.enableProcesses,
|
|
27189
|
-
HUB_INTEGRATIONS_DISABLED: HUB_SWITCH_ENV.enableIntegrations,
|
|
27190
|
-
NO_COMPUTER_CONNECTED: HUB_SWITCH_ENV.enableProcesses,
|
|
27191
|
-
HUB_PASSWORD_DISABLED: HUB_SWITCH_ENV.enablePassword,
|
|
27192
|
-
HUB_CONVEX_NOT_CONFIGURED: HUB_CONVEX_ADDRESS_ENV,
|
|
27193
|
-
HUB_CONVEX_MISMATCH: HUB_CONVEX_ADDRESS_ENV
|
|
27194
|
-
};
|
|
28261
|
+
return entries;
|
|
28262
|
+
}
|
|
27195
28263
|
|
|
27196
28264
|
// src/handlers/fs.ts
|
|
27197
28265
|
var BROWSE_ALWAYS_HIDDEN = /* @__PURE__ */ new Set([".git", ".DS_Store", "node_modules"]);
|
|
27198
28266
|
function createFsHandlers(ctx) {
|
|
27199
|
-
const allow = (candidate) => assertPathAllowed(candidate, ctx.config.allowedRoots);
|
|
28267
|
+
const allow = (candidate, subtree = false) => assertPathAllowed(candidate, ctx.config.allowedRoots, { subtree });
|
|
28268
|
+
const allowIn = (rootPath, rel, subtree = false) => {
|
|
28269
|
+
const root = allow(rootPath);
|
|
28270
|
+
allow((0, import_node_path9.join)(root, rel), subtree);
|
|
28271
|
+
return root;
|
|
28272
|
+
};
|
|
27200
28273
|
const watches = /* @__PURE__ */ new Map();
|
|
27201
28274
|
function pathExists(rawParams) {
|
|
27202
|
-
const { path:
|
|
27203
|
-
const resolved = allow(
|
|
27204
|
-
return { exists: (0,
|
|
28275
|
+
const { path: path9 } = fsPathExistsParamsSchema.parse(rawParams);
|
|
28276
|
+
const resolved = allow(path9);
|
|
28277
|
+
return { exists: (0, import_node_fs6.existsSync)(resolved) };
|
|
27205
28278
|
}
|
|
27206
28279
|
async function removeDir(rawParams) {
|
|
27207
|
-
const { path:
|
|
27208
|
-
const resolved = allow(
|
|
27209
|
-
await (0,
|
|
28280
|
+
const { path: path9 } = fsRemoveDirParamsSchema.parse(rawParams);
|
|
28281
|
+
const resolved = allow(path9, true);
|
|
28282
|
+
await (0, import_promises6.rm)(resolved, { recursive: true, force: true });
|
|
27210
28283
|
ctx.log("fs removeDir", { path: resolved });
|
|
27211
28284
|
return { ok: true };
|
|
27212
28285
|
}
|
|
@@ -27215,32 +28288,32 @@ function createFsHandlers(ctx) {
|
|
|
27215
28288
|
const resolvedRoots = [];
|
|
27216
28289
|
for (const root of ctx.config.allowedRoots) {
|
|
27217
28290
|
try {
|
|
27218
|
-
resolvedRoots.push(
|
|
28291
|
+
resolvedRoots.push(import_node_fs6.realpathSync.native((0, import_node_path9.resolve)(root)));
|
|
27219
28292
|
} catch {
|
|
27220
28293
|
}
|
|
27221
28294
|
}
|
|
27222
28295
|
let home = null;
|
|
27223
28296
|
try {
|
|
27224
|
-
home =
|
|
28297
|
+
home = import_node_fs6.realpathSync.native((0, import_node_os5.homedir)());
|
|
27225
28298
|
} catch {
|
|
27226
28299
|
home = null;
|
|
27227
28300
|
}
|
|
27228
|
-
return { roots: resolvedRoots, home, platform: process.platform, sep:
|
|
28301
|
+
return { roots: resolvedRoots, home, platform: process.platform, sep: import_node_path9.sep };
|
|
27229
28302
|
}
|
|
27230
28303
|
async function listDir(rawParams) {
|
|
27231
28304
|
const params = fsListDirParamsSchema.parse(rawParams);
|
|
27232
28305
|
const abs = allow(params.path);
|
|
27233
|
-
const dirents = await (0,
|
|
28306
|
+
const dirents = await (0, import_promises6.readdir)(abs, { withFileTypes: true });
|
|
27234
28307
|
const entries = [];
|
|
27235
28308
|
for (const e of dirents) {
|
|
27236
28309
|
if (BROWSE_ALWAYS_HIDDEN.has(e.name)) continue;
|
|
27237
28310
|
if (!params.includeHidden && e.name.startsWith(".")) continue;
|
|
27238
|
-
const full = (0,
|
|
28311
|
+
const full = (0, import_node_path9.join)(abs, e.name);
|
|
27239
28312
|
const symlink = e.isSymbolicLink();
|
|
27240
28313
|
let isDir = e.isDirectory();
|
|
27241
28314
|
if (symlink) {
|
|
27242
28315
|
try {
|
|
27243
|
-
isDir = (await (0,
|
|
28316
|
+
isDir = (await (0, import_promises6.stat)(full)).isDirectory();
|
|
27244
28317
|
} catch {
|
|
27245
28318
|
continue;
|
|
27246
28319
|
}
|
|
@@ -27251,7 +28324,7 @@ function createFsHandlers(ctx) {
|
|
|
27251
28324
|
path: full,
|
|
27252
28325
|
type: isDir ? "directory" : "file",
|
|
27253
28326
|
...symlink && { isSymlink: true },
|
|
27254
|
-
...isDir && (0,
|
|
28327
|
+
...isDir && (0, import_node_fs6.existsSync)((0, import_node_path9.join)(full, ".git")) && { isGitRepo: true }
|
|
27255
28328
|
});
|
|
27256
28329
|
}
|
|
27257
28330
|
entries.sort((a, b) => {
|
|
@@ -27260,19 +28333,19 @@ function createFsHandlers(ctx) {
|
|
|
27260
28333
|
});
|
|
27261
28334
|
const isRoot = ctx.config.allowedRoots.some((r) => {
|
|
27262
28335
|
try {
|
|
27263
|
-
return
|
|
28336
|
+
return import_node_fs6.realpathSync.native((0, import_node_path9.resolve)(r)) === abs;
|
|
27264
28337
|
} catch {
|
|
27265
28338
|
return false;
|
|
27266
28339
|
}
|
|
27267
28340
|
});
|
|
27268
|
-
return { path: abs, parent: isRoot ? null : (0,
|
|
28341
|
+
return { path: abs, parent: isRoot ? null : (0, import_node_path9.dirname)(abs), entries };
|
|
27269
28342
|
}
|
|
27270
28343
|
async function mkdirHandler(rawParams) {
|
|
27271
|
-
const { path:
|
|
27272
|
-
const abs = allow(
|
|
27273
|
-
await (0,
|
|
28344
|
+
const { path: path9 } = fsMkdirParamsSchema.parse(rawParams);
|
|
28345
|
+
const abs = allow(path9);
|
|
28346
|
+
await (0, import_promises6.mkdir)(abs, { recursive: true });
|
|
27274
28347
|
ctx.log("fs mkdir", { path: abs });
|
|
27275
|
-
return { path:
|
|
28348
|
+
return { path: import_node_fs6.realpathSync.native(abs) };
|
|
27276
28349
|
}
|
|
27277
28350
|
function readDirHandler(rawParams) {
|
|
27278
28351
|
const { rootPath, dirPath } = fsReadDirParamsSchema.parse(rawParams);
|
|
@@ -27280,312 +28353,166 @@ function createFsHandlers(ctx) {
|
|
|
27280
28353
|
}
|
|
27281
28354
|
function readFileHandler(rawParams) {
|
|
27282
28355
|
const { rootPath, filePath, force } = fsReadFileParamsSchema.parse(rawParams);
|
|
27283
|
-
return readFile3(
|
|
28356
|
+
return readFile3(allowIn(rootPath, filePath), filePath, force);
|
|
27284
28357
|
}
|
|
27285
28358
|
function writeFileHandler(rawParams) {
|
|
27286
28359
|
const { rootPath, filePath, content } = fsWriteFileParamsSchema.parse(rawParams);
|
|
27287
|
-
writeFile3(
|
|
28360
|
+
writeFile3(allowIn(rootPath, filePath), filePath, content);
|
|
27288
28361
|
return { ok: true };
|
|
27289
28362
|
}
|
|
27290
28363
|
function createFileHandler(rawParams) {
|
|
27291
28364
|
const { rootPath, filePath } = fsCreateFileParamsSchema.parse(rawParams);
|
|
27292
|
-
createFile(
|
|
28365
|
+
createFile(allowIn(rootPath, filePath), filePath);
|
|
27293
28366
|
return { ok: true };
|
|
27294
28367
|
}
|
|
27295
28368
|
function createDirHandler(rawParams) {
|
|
27296
28369
|
const { rootPath, dirPath } = fsCreateDirParamsSchema.parse(rawParams);
|
|
27297
|
-
createDir(
|
|
28370
|
+
createDir(allowIn(rootPath, dirPath), dirPath);
|
|
27298
28371
|
return { ok: true };
|
|
27299
28372
|
}
|
|
27300
28373
|
function renameHandler(rawParams) {
|
|
27301
28374
|
const { rootPath, oldPath, newPath } = fsRenameParamsSchema.parse(rawParams);
|
|
27302
|
-
|
|
28375
|
+
allowIn(rootPath, newPath, true);
|
|
28376
|
+
renamePath(allowIn(rootPath, oldPath, true), oldPath, newPath);
|
|
27303
28377
|
return { ok: true };
|
|
27304
28378
|
}
|
|
27305
28379
|
function deleteHandler(rawParams) {
|
|
27306
28380
|
const { rootPath, targetPath } = fsDeleteParamsSchema.parse(rawParams);
|
|
27307
|
-
deletePath(
|
|
28381
|
+
deletePath(allowIn(rootPath, targetPath, true), targetPath);
|
|
27308
28382
|
return { ok: true };
|
|
27309
28383
|
}
|
|
27310
28384
|
function copyInHandler(rawParams) {
|
|
27311
28385
|
const { rootPath, absoluteSrc, targetDir } = fsCopyInParamsSchema.parse(rawParams);
|
|
27312
28386
|
const src = allow(absoluteSrc);
|
|
27313
|
-
return { path: copyIn(
|
|
28387
|
+
return { path: copyIn(allowIn(rootPath, targetDir ?? ""), src, targetDir) };
|
|
27314
28388
|
}
|
|
27315
28389
|
function copyHandler(rawParams) {
|
|
27316
28390
|
const { rootPath, srcPath, destPath } = fsCopyParamsSchema.parse(rawParams);
|
|
27317
|
-
|
|
28391
|
+
allowIn(rootPath, srcPath);
|
|
28392
|
+
copy(allowIn(rootPath, destPath, true), srcPath, destPath);
|
|
27318
28393
|
return { ok: true };
|
|
27319
28394
|
}
|
|
27320
28395
|
async function gitStatusHandler(rawParams) {
|
|
27321
28396
|
const { rootPath } = fsGitStatusParamsSchema.parse(rawParams);
|
|
27322
28397
|
return gitStatus(allow(rootPath));
|
|
27323
28398
|
}
|
|
27324
|
-
function searchFilesHandler(rawParams) {
|
|
27325
|
-
const { rootPath, query, options } = fsSearchFilesParamsSchema.parse(rawParams);
|
|
27326
|
-
const maxResults = Math.min(options?.maxResults ?? FS_MAX_SEARCH_RESULTS, FS_MAX_SEARCH_RESULTS);
|
|
27327
|
-
const
|
|
27328
|
-
|
|
27329
|
-
|
|
27330
|
-
|
|
27331
|
-
|
|
27332
|
-
|
|
27333
|
-
|
|
27334
|
-
|
|
27335
|
-
|
|
27336
|
-
const
|
|
27337
|
-
|
|
27338
|
-
|
|
27339
|
-
|
|
27340
|
-
|
|
27341
|
-
|
|
27342
|
-
|
|
27343
|
-
|
|
27344
|
-
|
|
27345
|
-
|
|
27346
|
-
|
|
27347
|
-
|
|
27348
|
-
|
|
27349
|
-
|
|
27350
|
-
const
|
|
27351
|
-
|
|
27352
|
-
|
|
27353
|
-
|
|
27354
|
-
|
|
27355
|
-
|
|
27356
|
-
|
|
27357
|
-
|
|
27358
|
-
|
|
27359
|
-
|
|
27360
|
-
watches.
|
|
27361
|
-
|
|
27362
|
-
|
|
27363
|
-
|
|
27364
|
-
|
|
27365
|
-
[
|
|
27366
|
-
|
|
27367
|
-
|
|
27368
|
-
[HubToComputerMethods.fsMkdir]: mkdirHandler,
|
|
27369
|
-
[HubToComputerMethods.fsReadDir]: readDirHandler,
|
|
27370
|
-
[HubToComputerMethods.fsReadFile]: readFileHandler,
|
|
27371
|
-
[HubToComputerMethods.fsWriteFile]: writeFileHandler,
|
|
27372
|
-
[HubToComputerMethods.fsCreateFile]: createFileHandler,
|
|
27373
|
-
[HubToComputerMethods.fsCreateDir]: createDirHandler,
|
|
27374
|
-
[HubToComputerMethods.fsRename]: renameHandler,
|
|
27375
|
-
[HubToComputerMethods.fsDelete]: deleteHandler,
|
|
27376
|
-
[HubToComputerMethods.fsCopyIn]: copyInHandler,
|
|
27377
|
-
[HubToComputerMethods.fsCopy]: copyHandler,
|
|
27378
|
-
[HubToComputerMethods.fsGitStatus]: gitStatusHandler,
|
|
27379
|
-
[HubToComputerMethods.fsSearchFiles]: searchFilesHandler,
|
|
27380
|
-
[HubToComputerMethods.fsListAllFiles]: listAllFilesHandler,
|
|
27381
|
-
[HubToComputerMethods.fsDiscoverRepos]: discoverReposHandler,
|
|
27382
|
-
[HubToComputerMethods.fsWatchStart]: watchStart,
|
|
27383
|
-
[HubToComputerMethods.fsWatchStop]: watchStop
|
|
27384
|
-
};
|
|
27385
|
-
}
|
|
27386
|
-
|
|
27387
|
-
// src/handlers/gh.ts
|
|
27388
|
-
var import_node_child_process3 = require("node:child_process");
|
|
27389
|
-
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
27390
|
-
var MAX_OUTPUT_BYTES = 16 * 1024 * 1024;
|
|
27391
|
-
function createGhHandlers(ctx) {
|
|
27392
|
-
async function ghExec(rawParams) {
|
|
27393
|
-
const params = ghExecParamsSchema.parse(rawParams);
|
|
27394
|
-
const cwd = assertPathAllowed(params.cwd, ctx.config.allowedRoots);
|
|
27395
|
-
return new Promise((resolve7) => {
|
|
27396
|
-
const child = (0, import_node_child_process3.execFile)(
|
|
27397
|
-
"gh",
|
|
27398
|
-
params.args,
|
|
27399
|
-
{
|
|
27400
|
-
cwd,
|
|
27401
|
-
timeout: params.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
27402
|
-
maxBuffer: MAX_OUTPUT_BYTES,
|
|
27403
|
-
// No env overlay: gh must see this machine's own `GH_*`/`GITHUB_TOKEN`
|
|
27404
|
-
// and credential helpers. Injecting anything here would be SlayZone
|
|
27405
|
-
// taking custody of credentials it deliberately does not hold.
|
|
27406
|
-
env: process.env
|
|
27407
|
-
},
|
|
27408
|
-
(err, stdout2, stderr) => {
|
|
27409
|
-
const status = err && typeof err.code === "number" ? err.code : err ? null : 0;
|
|
27410
|
-
resolve7({ status, stdout: stdout2 ?? "", stderr: stderr ?? "" });
|
|
27411
|
-
}
|
|
27412
|
-
);
|
|
27413
|
-
if (params.stdin !== void 0) {
|
|
27414
|
-
child.stdin?.end(params.stdin);
|
|
27415
|
-
}
|
|
27416
|
-
});
|
|
27417
|
-
}
|
|
27418
|
-
return { [HubToComputerMethods.ghExec]: ghExec };
|
|
27419
|
-
}
|
|
27420
|
-
|
|
27421
|
-
// src/handlers/git.ts
|
|
27422
|
-
var import_node_child_process5 = require("node:child_process");
|
|
27423
|
-
var import_node_fs5 = require("node:fs");
|
|
27424
|
-
var import_promises6 = require("node:fs/promises");
|
|
27425
|
-
var import_node_path7 = require("node:path");
|
|
27426
|
-
|
|
27427
|
-
// ../../shared/platform/src/env-manifest.ts
|
|
27428
|
-
var ENV_MANIFEST = {
|
|
27429
|
-
// --- secret: credentials/tokens, never in a terminal ---
|
|
27430
|
-
SLAYZONE_HUB_TOKEN: "secret",
|
|
27431
|
-
// HMAC signing key for ALL of hub-auth (better-auth's session/cookie signer AND
|
|
27432
|
-
// the computer enroll/api-key credentials) — hence `HUB_AUTH`, not the former
|
|
27433
|
-
// `HUB_COMPUTER_TRANSPORT_SECRET`, which named just one of its consumers. Rule 2
|
|
27434
|
-
// (name by what the value IS). `_SECRET` = a key you HOLD and sign with, vs
|
|
27435
|
-
// `_TOKEN` = a bearer you PRESENT (SLAYZONE_HUB_TOKEN above).
|
|
27436
|
-
SLAYZONE_HUB_AUTH_SECRET: "secret",
|
|
27437
|
-
// Hub-scoped by VALUE: the hub mints it, and it embeds the hub's url and a
|
|
27438
|
-
// secret verified against the hub's `join_tokens`
|
|
27439
|
-
// row. `mintJoinToken` binds it to NO computer (computer_id stays NULL until
|
|
27440
|
-
// redemption), so any computer can redeem any unused token — there is no computer in
|
|
27441
|
-
// the value to name. Rule 2 (name by what the value IS, not who reads it).
|
|
27442
|
-
SLAYZONE_HUB_JOIN_TOKEN: "secret",
|
|
27443
|
-
// Retired before it was ever read: Entra sign-in authenticates with a certificate
|
|
27444
|
-
// (SLAYZONE_HUB_SSO_CERT_PATH / _KEY_PATH below), and boot refuses a hub that still
|
|
27445
|
-
// sets this (B7). Still classified `secret` so a leftover value never reaches a
|
|
27446
|
-
// terminal or a log.
|
|
27447
|
-
SLAYZONE_HUB_SSO_CLIENT_SECRET: "secret",
|
|
27448
|
-
// Per-boot bearer the desktop's `/cap` listener requires on every connection
|
|
27449
|
-
// (desktop-bridge-server.ts `verifyClient`). Loopback is NOT trusted there —
|
|
27450
|
-
// the threat this closes is another loopback process finding the port — so
|
|
27451
|
-
// this is a real credential, `secret`-scoped like the two above it.
|
|
27452
|
-
SLAYZONE_DESKTOP_BRIDGE_TOKEN: "secret",
|
|
27453
|
-
// The pre-rename `SLAYZONE_RUNNER_JOIN_TOKEN` is deliberately UNLISTED: it is
|
|
27454
|
-
// retired, not aliased. It never shipped (the published computer betas read
|
|
27455
|
-
// `SLAYZONE_JOIN_TOKEN`), so no operator env carries it and there is nothing to
|
|
27456
|
-
// keep working. Unlisted ⇒ the fail-closed default strips it, same as every
|
|
27457
|
-
// other retired name in this rename sweep.
|
|
27458
|
-
// --- infra: SlayZone wiring a terminal must not reinterpret ---
|
|
27459
|
-
// The hub to CONNECT to, host[:port] (computer, `slay`); the scheme derives from
|
|
27460
|
-
// the host (loopback → http/ws, else https/wss). A hub no longer reads this name:
|
|
27461
|
-
// it LISTENS on SLAYZONE_HUB_LISTEN_ADDRESS, and a hub that still gets the old
|
|
27462
|
-
// name refuses to boot. Stripping both here keeps a terminal's dial address from
|
|
27463
|
-
// ever being mistaken for another process's listen address.
|
|
27464
|
-
SLAYZONE_HUB_ADDRESS: "infra",
|
|
27465
|
-
SLAYZONE_HUB_LISTEN_ADDRESS: "infra",
|
|
27466
|
-
// hub-only; where the hub listens
|
|
27467
|
-
SLAYZONE_HUB_PUBLIC_ADDRESS: "infra",
|
|
27468
|
-
// hub-only; how other machines reach it, written into join tokens
|
|
27469
|
-
// Hub-only; the front ends (IPs/CIDRs, comma-separated) whose X-Forwarded-For names
|
|
27470
|
-
// the client (client-address.ts). Not `_ADDRESS`: these are networks, not an authority.
|
|
27471
|
-
SLAYZONE_HUB_TRUSTED_PROXIES: "infra",
|
|
27472
|
-
// Per-hub switches (0|1) replacing SLAYZONE_MODE. Infra: they describe THIS hub
|
|
27473
|
-
// process, so a task terminal must not inherit them (a hub started from one would
|
|
27474
|
-
// otherwise pick up its parent's switches by accident).
|
|
27475
|
-
SLAYZONE_HUB_REQUIRE_LOGIN: "infra",
|
|
27476
|
-
SLAYZONE_HUB_ENABLE_PASSWORD: "infra",
|
|
27477
|
-
SLAYZONE_HUB_ENABLE_SSO: "infra",
|
|
27478
|
-
SLAYZONE_HUB_ENABLE_PROCESSES: "infra",
|
|
27479
|
-
SLAYZONE_HUB_ENABLE_INTEGRATIONS: "infra",
|
|
27480
|
-
SLAYZONE_HUB_SSO_TENANT_ID: "infra",
|
|
27481
|
-
SLAYZONE_HUB_SSO_CLIENT_ID: "infra",
|
|
27482
|
-
// Paths to the Entra client certificate and its private key, and the MFA switch.
|
|
27483
|
-
// Infra, not secret: a path is not the key (the file's own permissions protect it),
|
|
27484
|
-
// but a task terminal must still never inherit a hub's SSO wiring.
|
|
27485
|
-
SLAYZONE_HUB_SSO_CERT_PATH: "infra",
|
|
27486
|
-
SLAYZONE_HUB_SSO_KEY_PATH: "infra",
|
|
27487
|
-
SLAYZONE_HUB_SSO_REQUIRE_MFA: "infra",
|
|
27488
|
-
// Operator-facing hub name (`slay hub ls`/`stop` address a hub by it). Infra,
|
|
27489
|
-
// not global: it identifies THIS hub process, so inheriting it into a task
|
|
27490
|
-
// terminal — which may target a different hub entirely — would mislabel.
|
|
27491
|
-
SLAYZONE_HUB_NAME: "infra",
|
|
27492
|
-
// Days THIS hub keeps session content (T-23, hub session-retention.ts); 0 = off, the
|
|
27493
|
-
// default. Infra like the switches: a hub started from a task terminal must not
|
|
27494
|
-
// inherit its parent's period and start deleting.
|
|
27495
|
-
SLAYZONE_HUB_SESSION_RETENTION_DAYS: "infra",
|
|
27496
|
-
// The Convex deployment (host[:port]) the hub starts GitHub sign-in against — never
|
|
27497
|
-
// a client-named one (T-99). Hub process config, set by the desktop app for its hub.
|
|
27498
|
-
SLAYZONE_HUB_CONVEX_ADDRESS: "infra",
|
|
27499
|
-
SLAYZONE_DESKTOP_BRIDGE_ADDRESS: "infra",
|
|
27500
|
-
// sidecar→desktop capability bridge + REST proxy
|
|
27501
|
-
SLAYZONE_SUPERVISED: "infra",
|
|
27502
|
-
// "started by the desktop app" flag
|
|
27503
|
-
SLAYZONE_USER_DATA_DIR: "infra",
|
|
27504
|
-
// Playwright Electron userData redirect
|
|
27505
|
-
SLAYZONE_SIDECAR_HOT_RESTART: "infra",
|
|
27506
|
-
SLAYZONE_BOOT_LOG_PATH: "infra",
|
|
27507
|
-
SLAYZONE_DEBUG_BOOT: "infra",
|
|
27508
|
-
SLAYZONE_REGISTER_DEV_PROTOCOL: "infra",
|
|
27509
|
-
SLAYZONE_NONINTERACTIVE: "infra",
|
|
27510
|
-
// Historical opt-IN, now a NO-OP: e2e runs computer-ON by default, so the boot
|
|
27511
|
-
// gate no longer reads this. Kept manifested because specs still set it to
|
|
27512
|
-
// document "this spec wants a computer" (an unmanifested var would be stripped
|
|
27513
|
-
// by sanitizeSpawnEnv, fail-closed, and read as a silent typo).
|
|
27514
|
-
SLAYZONE_E2E_ALLOW_COMPUTER: "infra",
|
|
27515
|
-
// Opt-OUT, and the only lever that changes behavior now: set by the specs that
|
|
27516
|
-
// must boot computer-less (remote-mode, sidecar-crash, and the "hub accepts
|
|
27517
|
-
// computers with none spawned" contract test).
|
|
27518
|
-
SLAYZONE_E2E_NO_COMPUTER: "infra",
|
|
27519
|
-
SLAYZONE_E2E_INSTALL_HOOKS: "infra",
|
|
27520
|
-
// Redirects the MACHINE-WIDE ~/.slayzone (hooks/, bin/) — the files shared by
|
|
27521
|
-
// every SlayZone process on the box, which deliberately do NOT follow
|
|
27522
|
-
// SLAYZONE_ROOT. Sandbox redirect for tests, same role as the per-provider
|
|
27523
|
-
// overrides below. `infra`: a spawned terminal must never inherit it, or an
|
|
27524
|
-
// agent would resolve a different notify.sh than the one wired into its hooks.
|
|
27525
|
-
SLAYZONE_MACHINE_DIR: "infra",
|
|
27526
|
-
// per-provider config/hook/plugin path overrides (test/sandbox redirects)
|
|
27527
|
-
SLAYZONE_CLAUDE_SETTINGS_PATH: "infra",
|
|
27528
|
-
SLAYZONE_GEMINI_SETTINGS_PATH: "infra",
|
|
27529
|
-
SLAYZONE_CODEX_HOOKS_PATH: "infra",
|
|
27530
|
-
SLAYZONE_ANTIGRAVITY_HOOKS_PATH: "infra",
|
|
27531
|
-
SLAYZONE_OPENCODE_PLUGIN_PATH: "infra",
|
|
27532
|
-
// --- identity: per-task/agent; overlay re-adds the correct value per spawn ---
|
|
27533
|
-
SLAYZONE_TASK_ID: "identity",
|
|
27534
|
-
SLAYZONE_PROJECT_ID: "identity",
|
|
27535
|
-
// `SLAYZONE_SESSION_ID` (a warm agent's pre-task identity) is RETIRED and
|
|
27536
|
-
// deliberately unlisted: warm agents boot with SLAYZONE_TASK_ID now.
|
|
27537
|
-
SLAYZONE_AGENT_ID: "identity",
|
|
27538
|
-
SLAYZONE_AGENT_HOOK_URL: "identity",
|
|
27539
|
-
// Pairs with _HOOK_URL (where to post / who is posting). The pre-v4 name
|
|
27540
|
-
// `SLAYZONE_HOOK_CONTEXT` is RETIRED and deliberately unlisted: unlisted =
|
|
27541
|
-
// stripped (fail closed), and the old-name value an older release channel's app
|
|
27542
|
-
// supplies rides its OWN per-spawn overlay (applied after sanitizeSpawnEnv), so
|
|
27543
|
-
// notify.sh v4's fallback read still sees it. A manifest entry would only let a
|
|
27544
|
-
// stale inherited value survive.
|
|
27545
|
-
SLAYZONE_AGENT_HOOK_CONTEXT: "identity",
|
|
27546
|
-
// --- global: same app-wide, safe to inherit verbatim ---
|
|
27547
|
-
SLAYZONE_RELEASE_CHANNEL: "global",
|
|
27548
|
-
// attribution-only (hook envelope channel)
|
|
27549
|
-
// INSTALL IDENTITY — which SlayZone install this process tree belongs to, not
|
|
27550
|
-
// which task/session. A child MUST resolve the SAME install as its parent: the
|
|
27551
|
-
// `slay` CLI derives its DB from BOTH (getStorageDir() reads ROOT; the
|
|
27552
|
-
// filename is `slayzone{.dev}.sqlite` per DEV — apps/cli/src/db.ts getDbPath).
|
|
27553
|
-
// Strip either and a bare `slay` in a dev-app task terminal exits 1
|
|
27554
|
-
// ("Database not found … Re-run with --dev"), and under an isolated e2e ROOT a
|
|
27555
|
-
// child would escape the sandbox to the real ~/.slayzone. Neither is a
|
|
27556
|
-
// credential and neither can be reinterpreted as a different kind of target
|
|
27557
|
-
// (unlike a hub address), so both inherit verbatim.
|
|
27558
|
-
//
|
|
27559
|
-
// ROOT is NOT covered by the identity overlay: buildMcpEnv sets it only inside
|
|
27560
|
-
// setHookIdentity(), gated on a hook-capable agent mode — a plain `terminal`
|
|
27561
|
-
// PTY, a non-hook agent, or a pre-warmed pool spawn gets no overlay at all.
|
|
27562
|
-
SLAYZONE_ROOT: "global",
|
|
27563
|
-
SLAYZONE_DEV: "global"
|
|
27564
|
-
};
|
|
27565
|
-
var NON_PREFIXED_INFRA = /* @__PURE__ */ new Set([
|
|
27566
|
-
"ELECTRON_RUN_AS_NODE",
|
|
27567
|
-
"NODE_PATH",
|
|
27568
|
-
"PLAYWRIGHT"
|
|
27569
|
-
]);
|
|
27570
|
-
function sanitizeSpawnEnv(base) {
|
|
27571
|
-
const out = {};
|
|
27572
|
-
for (const [key, value] of Object.entries(base)) {
|
|
27573
|
-
if (typeof value !== "string") continue;
|
|
27574
|
-
if (NON_PREFIXED_INFRA.has(key)) continue;
|
|
27575
|
-
if (key.startsWith("SLAYZONE_")) {
|
|
27576
|
-
if (ENV_MANIFEST[key] === "global") out[key] = value;
|
|
27577
|
-
continue;
|
|
28399
|
+
function searchFilesHandler(rawParams) {
|
|
28400
|
+
const { rootPath, query, options } = fsSearchFilesParamsSchema.parse(rawParams);
|
|
28401
|
+
const maxResults = Math.min(options?.maxResults ?? FS_MAX_SEARCH_RESULTS, FS_MAX_SEARCH_RESULTS);
|
|
28402
|
+
const root = allow(rootPath);
|
|
28403
|
+
const results = searchFiles(root, query, {
|
|
28404
|
+
...options,
|
|
28405
|
+
maxResults
|
|
28406
|
+
}).filter((r) => !isProtectedPath((0, import_node_path9.join)(root, r.path)));
|
|
28407
|
+
const total = results.reduce((n, r) => n + r.matches.length, 0);
|
|
28408
|
+
return { results, truncated: total >= maxResults };
|
|
28409
|
+
}
|
|
28410
|
+
function listAllFilesHandler(rawParams) {
|
|
28411
|
+
const { rootPath } = fsListAllFilesParamsSchema.parse(rawParams);
|
|
28412
|
+
const all = listAllFiles(allow(rootPath));
|
|
28413
|
+
return {
|
|
28414
|
+
files: all.slice(0, FS_MAX_LIST_ALL_FILES),
|
|
28415
|
+
truncated: all.length > FS_MAX_LIST_ALL_FILES
|
|
28416
|
+
};
|
|
28417
|
+
}
|
|
28418
|
+
async function discoverReposHandler(rawParams) {
|
|
28419
|
+
const { rootPath, maxDepth } = fsDiscoverReposParamsSchema.parse(rawParams);
|
|
28420
|
+
return { repos: await discoverRepos(allow(rootPath), maxDepth) };
|
|
28421
|
+
}
|
|
28422
|
+
function watchStart(rawParams) {
|
|
28423
|
+
const { watchId, rootPath } = fsWatchStartParamsSchema.parse(rawParams);
|
|
28424
|
+
watches.get(watchId)?.();
|
|
28425
|
+
const abs = allow(rootPath);
|
|
28426
|
+
const dispose = subscribeFileWatcher(abs, (e) => {
|
|
28427
|
+
const params = { watchId, type: e.type, relPath: e.relPath };
|
|
28428
|
+
ctx.dialer.notify(ComputerNotificationMethods.fsChange, params);
|
|
28429
|
+
});
|
|
28430
|
+
watches.set(watchId, dispose);
|
|
28431
|
+
return { ok: true };
|
|
28432
|
+
}
|
|
28433
|
+
function watchStop(rawParams) {
|
|
28434
|
+
const { watchId } = fsWatchStopParamsSchema.parse(rawParams);
|
|
28435
|
+
watches.get(watchId)?.();
|
|
28436
|
+
watches.delete(watchId);
|
|
28437
|
+
return { ok: true };
|
|
28438
|
+
}
|
|
28439
|
+
function disposeAll() {
|
|
28440
|
+
for (const [watchId, dispose] of watches) {
|
|
28441
|
+
dispose();
|
|
28442
|
+
ctx.log("fs watch disposed on shutdown", { watchId });
|
|
27578
28443
|
}
|
|
27579
|
-
|
|
28444
|
+
watches.clear();
|
|
27580
28445
|
}
|
|
27581
|
-
|
|
28446
|
+
const handlers = {
|
|
28447
|
+
[HubToComputerMethods.fsPathExists]: pathExists,
|
|
28448
|
+
[HubToComputerMethods.fsRemoveDir]: removeDir,
|
|
28449
|
+
[HubToComputerMethods.fsListRoots]: listRoots,
|
|
28450
|
+
[HubToComputerMethods.fsListDir]: listDir,
|
|
28451
|
+
[HubToComputerMethods.fsMkdir]: mkdirHandler,
|
|
28452
|
+
[HubToComputerMethods.fsReadDir]: readDirHandler,
|
|
28453
|
+
[HubToComputerMethods.fsReadFile]: readFileHandler,
|
|
28454
|
+
[HubToComputerMethods.fsWriteFile]: writeFileHandler,
|
|
28455
|
+
[HubToComputerMethods.fsCreateFile]: createFileHandler,
|
|
28456
|
+
[HubToComputerMethods.fsCreateDir]: createDirHandler,
|
|
28457
|
+
[HubToComputerMethods.fsRename]: renameHandler,
|
|
28458
|
+
[HubToComputerMethods.fsDelete]: deleteHandler,
|
|
28459
|
+
[HubToComputerMethods.fsCopyIn]: copyInHandler,
|
|
28460
|
+
[HubToComputerMethods.fsCopy]: copyHandler,
|
|
28461
|
+
[HubToComputerMethods.fsGitStatus]: gitStatusHandler,
|
|
28462
|
+
[HubToComputerMethods.fsSearchFiles]: searchFilesHandler,
|
|
28463
|
+
[HubToComputerMethods.fsListAllFiles]: listAllFilesHandler,
|
|
28464
|
+
[HubToComputerMethods.fsDiscoverRepos]: discoverReposHandler,
|
|
28465
|
+
[HubToComputerMethods.fsWatchStart]: watchStart,
|
|
28466
|
+
[HubToComputerMethods.fsWatchStop]: watchStop
|
|
28467
|
+
};
|
|
28468
|
+
return { handlers, disposeAll };
|
|
28469
|
+
}
|
|
28470
|
+
|
|
28471
|
+
// src/handlers/gh.ts
|
|
28472
|
+
var import_node_child_process5 = require("node:child_process");
|
|
28473
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
28474
|
+
var MAX_OUTPUT_BYTES = 16 * 1024 * 1024;
|
|
28475
|
+
function createGhHandlers(ctx) {
|
|
28476
|
+
async function ghExec(rawParams) {
|
|
28477
|
+
const params = ghExecParamsSchema.parse(rawParams);
|
|
28478
|
+
const cwd = assertPathAllowed(params.cwd, ctx.config.allowedRoots);
|
|
28479
|
+
return new Promise((resolve7) => {
|
|
28480
|
+
const child = (0, import_node_child_process5.execFile)(
|
|
28481
|
+
"gh",
|
|
28482
|
+
params.args,
|
|
28483
|
+
{
|
|
28484
|
+
cwd,
|
|
28485
|
+
timeout: params.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
28486
|
+
maxBuffer: MAX_OUTPUT_BYTES,
|
|
28487
|
+
// No env overlay: gh must see this machine's own `GH_*`/`GITHUB_TOKEN`
|
|
28488
|
+
// and credential helpers. Injecting anything here would be SlayZone
|
|
28489
|
+
// taking custody of credentials it deliberately does not hold.
|
|
28490
|
+
env: process.env
|
|
28491
|
+
},
|
|
28492
|
+
(err, stdout2, stderr) => {
|
|
28493
|
+
const status = err && typeof err.code === "number" ? err.code : err ? null : 0;
|
|
28494
|
+
resolve7({ status, stdout: stdout2 ?? "", stderr: stderr ?? "" });
|
|
28495
|
+
}
|
|
28496
|
+
);
|
|
28497
|
+
if (params.stdin !== void 0) {
|
|
28498
|
+
child.stdin?.end(params.stdin);
|
|
28499
|
+
}
|
|
28500
|
+
});
|
|
28501
|
+
}
|
|
28502
|
+
return { [HubToComputerMethods.ghExec]: ghExec };
|
|
27582
28503
|
}
|
|
27583
28504
|
|
|
28505
|
+
// src/handlers/git.ts
|
|
28506
|
+
var import_node_child_process7 = require("node:child_process");
|
|
28507
|
+
var import_node_fs7 = require("node:fs");
|
|
28508
|
+
var import_promises7 = require("node:fs/promises");
|
|
28509
|
+
var import_node_path10 = require("node:path");
|
|
28510
|
+
|
|
27584
28511
|
// src/handlers/exec.ts
|
|
27585
|
-
var
|
|
28512
|
+
var import_node_child_process6 = require("node:child_process");
|
|
27586
28513
|
function execCapture(command, args, opts = {}) {
|
|
27587
28514
|
return new Promise((resolve7) => {
|
|
27588
|
-
const child = (0,
|
|
28515
|
+
const child = (0, import_node_child_process6.spawn)(command, args, {
|
|
27589
28516
|
...opts.cwd ? { cwd: opts.cwd } : {},
|
|
27590
28517
|
...opts.env ? { env: opts.env } : {},
|
|
27591
28518
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -27604,7 +28531,7 @@ function execCapture(command, args, opts = {}) {
|
|
|
27604
28531
|
});
|
|
27605
28532
|
child.on("error", (err) => {
|
|
27606
28533
|
if (timer) clearTimeout(timer);
|
|
27607
|
-
resolve7({ stdout: "", stderr: err.message, status: 1 });
|
|
28534
|
+
resolve7({ stdout: "", stderr: err.message, status: 1, spawnError: err.message });
|
|
27608
28535
|
});
|
|
27609
28536
|
});
|
|
27610
28537
|
}
|
|
@@ -27637,14 +28564,12 @@ var SETUP_SCRIPT_TIMEOUT_MS = 5 * 6e4;
|
|
|
27637
28564
|
function createGitHandlers(ctx) {
|
|
27638
28565
|
const allow = (candidate) => assertPathAllowed(candidate, ctx.config.allowedRoots);
|
|
27639
28566
|
async function isGitRepo(rawParams) {
|
|
27640
|
-
const { path:
|
|
27641
|
-
const repoPath = allow(
|
|
27642
|
-
|
|
27643
|
-
|
|
27644
|
-
|
|
27645
|
-
|
|
27646
|
-
return { isGitRepo: false };
|
|
27647
|
-
}
|
|
28567
|
+
const { path: path9 } = isGitRepoParams.parse(rawParams);
|
|
28568
|
+
const repoPath = allow(path9);
|
|
28569
|
+
if (!(0, import_node_fs7.existsSync)(repoPath)) return { isGitRepo: false };
|
|
28570
|
+
const result = await execCapture("git", ["rev-parse", "--git-dir"], { cwd: repoPath });
|
|
28571
|
+
if (result.spawnError) throw new Error(`Could not start git: ${result.spawnError}`);
|
|
28572
|
+
return { isGitRepo: result.status === 0 };
|
|
27648
28573
|
}
|
|
27649
28574
|
async function getCurrentBranch(rawParams) {
|
|
27650
28575
|
const parsed = getCurrentBranchParams.parse(rawParams);
|
|
@@ -27674,7 +28599,7 @@ function createGitHandlers(ctx) {
|
|
|
27674
28599
|
try {
|
|
27675
28600
|
await execGit(["worktree", "remove", worktreePath, "--force"], repoPath);
|
|
27676
28601
|
} catch (err) {
|
|
27677
|
-
if (!(0,
|
|
28602
|
+
if (!(0, import_node_fs7.existsSync)(worktreePath)) {
|
|
27678
28603
|
await execGit(["worktree", "prune"], repoPath);
|
|
27679
28604
|
} else {
|
|
27680
28605
|
throw err;
|
|
@@ -27702,13 +28627,13 @@ function createGitHandlers(ctx) {
|
|
|
27702
28627
|
const params = runWorktreeSetupScriptParams.parse(rawParams);
|
|
27703
28628
|
const worktreePath = allow(params.worktreePath);
|
|
27704
28629
|
const repoPath = allow(params.repoPath);
|
|
27705
|
-
const scriptPath = (0,
|
|
27706
|
-
if (!(0,
|
|
28630
|
+
const scriptPath = (0, import_node_path10.resolve)(worktreePath, SETUP_SCRIPT);
|
|
28631
|
+
if (!(0, import_node_fs7.existsSync)(scriptPath)) return Promise.resolve({ ran: false });
|
|
27707
28632
|
try {
|
|
27708
|
-
(0,
|
|
28633
|
+
(0, import_node_fs7.accessSync)(scriptPath, import_node_fs7.constants.X_OK);
|
|
27709
28634
|
} catch {
|
|
27710
28635
|
try {
|
|
27711
|
-
(0,
|
|
28636
|
+
(0, import_node_fs7.chmodSync)(scriptPath, 493);
|
|
27712
28637
|
} catch {
|
|
27713
28638
|
return Promise.resolve({ ran: false });
|
|
27714
28639
|
}
|
|
@@ -27718,7 +28643,7 @@ function createGitHandlers(ctx) {
|
|
|
27718
28643
|
env.REPO_PATH = repoPath;
|
|
27719
28644
|
env.SOURCE_BRANCH = params.sourceBranch ?? "";
|
|
27720
28645
|
return new Promise((resolvePromise) => {
|
|
27721
|
-
const child = (0,
|
|
28646
|
+
const child = (0, import_node_child_process7.spawn)(scriptPath, [], {
|
|
27722
28647
|
cwd: worktreePath,
|
|
27723
28648
|
stdio: ["ignore", "pipe", "pipe"],
|
|
27724
28649
|
env
|
|
@@ -27759,20 +28684,20 @@ function createGitHandlers(ctx) {
|
|
|
27759
28684
|
} else {
|
|
27760
28685
|
relPaths = params.customPaths ?? [];
|
|
27761
28686
|
}
|
|
27762
|
-
const repoRoot = (0,
|
|
27763
|
-
const wtRoot = (0,
|
|
28687
|
+
const repoRoot = (0, import_node_path10.resolve)(repoPath);
|
|
28688
|
+
const wtRoot = (0, import_node_path10.resolve)(worktreePath);
|
|
27764
28689
|
let copied = 0;
|
|
27765
28690
|
for (const rel of relPaths) {
|
|
27766
|
-
const src = (0,
|
|
27767
|
-
const dest = (0,
|
|
27768
|
-
if (!
|
|
28691
|
+
const src = (0, import_node_path10.resolve)(repoPath, rel);
|
|
28692
|
+
const dest = (0, import_node_path10.resolve)(worktreePath, rel);
|
|
28693
|
+
if (!isInside2(src, repoRoot) || !isInside2(dest, wtRoot)) {
|
|
27769
28694
|
ctx.log("copyIgnoredFiles skipped traversal", { rel });
|
|
27770
28695
|
continue;
|
|
27771
28696
|
}
|
|
27772
|
-
if (!(0,
|
|
28697
|
+
if (!(0, import_node_fs7.existsSync)(src)) continue;
|
|
27773
28698
|
try {
|
|
27774
|
-
await (0,
|
|
27775
|
-
await (0,
|
|
28699
|
+
await (0, import_promises7.mkdir)((0, import_node_path10.dirname)(dest), { recursive: true });
|
|
28700
|
+
await (0, import_promises7.cp)(src, dest, { recursive: true, verbatimSymlinks: true });
|
|
27776
28701
|
copied += 1;
|
|
27777
28702
|
} catch (err) {
|
|
27778
28703
|
ctx.log("copyIgnoredFiles copy failed", { rel, error: String(err) });
|
|
@@ -27789,83 +28714,13 @@ function createGitHandlers(ctx) {
|
|
|
27789
28714
|
[GitMethods.copyIgnoredFiles]: copyIgnoredFiles
|
|
27790
28715
|
};
|
|
27791
28716
|
}
|
|
27792
|
-
function
|
|
27793
|
-
const c = (0,
|
|
27794
|
-
return c === root || c.startsWith(root +
|
|
28717
|
+
function isInside2(child, root) {
|
|
28718
|
+
const c = (0, import_node_path10.resolve)(child);
|
|
28719
|
+
return c === root || c.startsWith(root + import_node_path10.sep);
|
|
27795
28720
|
}
|
|
27796
28721
|
|
|
27797
28722
|
// src/handlers/proc.ts
|
|
27798
|
-
var
|
|
27799
|
-
|
|
27800
|
-
// ../../shared/platform/src/index.ts
|
|
27801
|
-
init_dirs();
|
|
27802
|
-
|
|
27803
|
-
// ../../shared/platform/src/paths.ts
|
|
27804
|
-
init_dirs();
|
|
27805
|
-
|
|
27806
|
-
// ../../shared/platform/src/index.ts
|
|
27807
|
-
init_fs_utils();
|
|
27808
|
-
|
|
27809
|
-
// ../../shared/platform/src/migrations.ts
|
|
27810
|
-
init_dirs();
|
|
27811
|
-
|
|
27812
|
-
// ../../shared/platform/src/cli-install.ts
|
|
27813
|
-
var import_child_process = require("child_process");
|
|
27814
|
-
var import_util9 = require("util");
|
|
27815
|
-
var execFileAsync = (0, import_util9.promisify)(import_child_process.execFile);
|
|
27816
|
-
var WINDOWS_SHIM = [
|
|
27817
|
-
"@echo off",
|
|
27818
|
-
`for /f "tokens=1 delims=." %%v in ('node -e "process.stdout.write(process.versions.node)"') do set NODE_MAJOR=%%v`,
|
|
27819
|
-
"if %NODE_MAJOR% LEQ 22 (",
|
|
27820
|
-
' node --experimental-sqlite --no-warnings "%~dp0slay.js" %*',
|
|
27821
|
-
") else (",
|
|
27822
|
-
' node --no-warnings "%~dp0slay.js" %*',
|
|
27823
|
-
")",
|
|
27824
|
-
""
|
|
27825
|
-
].join("\r\n");
|
|
27826
|
-
|
|
27827
|
-
// ../../shared/platform/src/shell.ts
|
|
27828
|
-
var import_node_fs6 = __toESM(require("node:fs"), 1);
|
|
27829
|
-
var import_os2 = require("os");
|
|
27830
|
-
var shellOverride = null;
|
|
27831
|
-
function shellExists(shellPath) {
|
|
27832
|
-
if ((0, import_os2.platform)() === "win32") return import_node_fs6.default.existsSync(shellPath);
|
|
27833
|
-
try {
|
|
27834
|
-
import_node_fs6.default.accessSync(shellPath, import_node_fs6.default.constants.X_OK);
|
|
27835
|
-
return true;
|
|
27836
|
-
} catch {
|
|
27837
|
-
return false;
|
|
27838
|
-
}
|
|
27839
|
-
}
|
|
27840
|
-
function defaultShellForPlatform() {
|
|
27841
|
-
if ((0, import_os2.platform)() === "win32") return process.env.COMSPEC || "cmd.exe";
|
|
27842
|
-
if ((0, import_os2.platform)() === "darwin") return "/bin/zsh";
|
|
27843
|
-
return "/bin/bash";
|
|
27844
|
-
}
|
|
27845
|
-
function resolveUserShell() {
|
|
27846
|
-
if (shellOverride && shellExists(shellOverride)) return shellOverride;
|
|
27847
|
-
const fromEnv = process.env.SHELL?.trim();
|
|
27848
|
-
if (fromEnv && shellExists(fromEnv)) return fromEnv;
|
|
27849
|
-
try {
|
|
27850
|
-
const fromUser = (0, import_os2.userInfo)().shell?.trim();
|
|
27851
|
-
if (fromUser && shellExists(fromUser)) return fromUser;
|
|
27852
|
-
} catch {
|
|
27853
|
-
}
|
|
27854
|
-
return defaultShellForPlatform();
|
|
27855
|
-
}
|
|
27856
|
-
function buildShellInvocation(command) {
|
|
27857
|
-
const shell = resolveUserShell();
|
|
27858
|
-
if ((0, import_os2.platform)() === "win32") return { file: shell, args: ["/c", command] };
|
|
27859
|
-
const isFish = shell.endsWith("/fish");
|
|
27860
|
-
return { file: shell, args: [...isFish ? ["-i", "-l"] : ["-l"], "-c", command] };
|
|
27861
|
-
}
|
|
27862
|
-
|
|
27863
|
-
// ../../shared/platform/src/slz-file-url.ts
|
|
27864
|
-
var SLZ_FILE_HOST = "app";
|
|
27865
|
-
var SLZ_FILE_PREFIX = `slz-file://${SLZ_FILE_HOST}`;
|
|
27866
|
-
|
|
27867
|
-
// ../../shared/platform/src/ipc-dedup.ts
|
|
27868
|
-
var UNCHANGED = Object.freeze({ __ipc_unchanged__: true });
|
|
28723
|
+
var import_node_child_process8 = require("node:child_process");
|
|
27869
28724
|
|
|
27870
28725
|
// src/exit-tombstones.ts
|
|
27871
28726
|
var TOMBSTONE_TTL_MS = 10 * 6e4;
|
|
@@ -27988,6 +28843,9 @@ function createProcHandlers(ctx, getProjectPaths) {
|
|
|
27988
28843
|
}
|
|
27989
28844
|
return merged;
|
|
27990
28845
|
}
|
|
28846
|
+
function killTree(child, signal = "SIGTERM") {
|
|
28847
|
+
if (typeof child.pid === "number") killPidTree(child.pid, signal);
|
|
28848
|
+
}
|
|
27991
28849
|
function procSpawn(rawParams) {
|
|
27992
28850
|
const params = procSpawnParamsSchema.parse(rawParams);
|
|
27993
28851
|
if (params.spawnId !== void 0) {
|
|
@@ -28000,21 +28858,21 @@ function createProcHandlers(ctx, getProjectPaths) {
|
|
|
28000
28858
|
}
|
|
28001
28859
|
}
|
|
28002
28860
|
const resolved = params.cwd ?? (params.projectId ? getProjectPaths?.()?.get(params.projectId)?.path : void 0);
|
|
28003
|
-
const cwd =
|
|
28861
|
+
const cwd = spawnCwd(resolved, ctx.config.allowedRoots);
|
|
28004
28862
|
const existing = procs.get(params.sessionId);
|
|
28005
28863
|
if (existing) {
|
|
28006
|
-
|
|
28007
|
-
existing.child.kill();
|
|
28008
|
-
} catch {
|
|
28009
|
-
}
|
|
28864
|
+
killTree(existing.child);
|
|
28010
28865
|
procs.delete(params.sessionId);
|
|
28011
28866
|
}
|
|
28012
28867
|
const invocation = params.shell ? buildShellInvocation(params.command) : { file: params.command, args: params.args ?? [] };
|
|
28013
|
-
const child = (0,
|
|
28868
|
+
const child = (0, import_node_child_process8.spawn)(invocation.file, invocation.args, {
|
|
28014
28869
|
...cwd ? { cwd } : {},
|
|
28015
28870
|
env: buildEnv(params.env),
|
|
28016
28871
|
// stdin PIPED (was 'ignore') so `proc.write` can reach the child.
|
|
28017
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
28872
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
28873
|
+
// Its own process group, so a kill reaches everything it started (`pnpm dev`
|
|
28874
|
+
// → node → …), not just the shell — see `killTree`.
|
|
28875
|
+
detached: PROCESS_TREE_DETACHED
|
|
28018
28876
|
});
|
|
28019
28877
|
const buffer = new RingBuffer(MAX_BUFFER_SIZE);
|
|
28020
28878
|
const session = {
|
|
@@ -28081,11 +28939,7 @@ function createProcHandlers(ctx, getProjectPaths) {
|
|
|
28081
28939
|
ctx.log("proc kill on unknown session", { sessionId: params.sessionId });
|
|
28082
28940
|
return { ok: true };
|
|
28083
28941
|
}
|
|
28084
|
-
|
|
28085
|
-
session.child.kill(params.signal ?? "SIGTERM");
|
|
28086
|
-
} catch (err) {
|
|
28087
|
-
ctx.log("proc kill failed", { sessionId: params.sessionId, error: String(err) });
|
|
28088
|
-
}
|
|
28942
|
+
killTree(session.child, params.signal ?? "SIGTERM");
|
|
28089
28943
|
return { ok: true };
|
|
28090
28944
|
}
|
|
28091
28945
|
function procWrite(rawParams) {
|
|
@@ -28123,10 +28977,7 @@ function createProcHandlers(ctx, getProjectPaths) {
|
|
|
28123
28977
|
}
|
|
28124
28978
|
function disposeAll() {
|
|
28125
28979
|
for (const [sessionId, session] of procs) {
|
|
28126
|
-
|
|
28127
|
-
session.child.kill();
|
|
28128
|
-
} catch {
|
|
28129
|
-
}
|
|
28980
|
+
killTree(session.child);
|
|
28130
28981
|
ctx.log("proc disposed on shutdown", { sessionId });
|
|
28131
28982
|
}
|
|
28132
28983
|
procs.clear();
|
|
@@ -28142,7 +28993,7 @@ function createProcHandlers(ctx, getProjectPaths) {
|
|
|
28142
28993
|
}
|
|
28143
28994
|
|
|
28144
28995
|
// src/handlers/project.ts
|
|
28145
|
-
var
|
|
28996
|
+
var import_node_fs8 = require("node:fs");
|
|
28146
28997
|
function createProjectHandlers(ctx, getStore) {
|
|
28147
28998
|
const allow = (candidate) => assertPathAllowed(candidate, ctx.config.allowedRoots);
|
|
28148
28999
|
const store = () => {
|
|
@@ -28154,11 +29005,11 @@ function createProjectHandlers(ctx, getStore) {
|
|
|
28154
29005
|
const { projectId } = projectResolvePathParamsSchema.parse(rawParams);
|
|
28155
29006
|
const entry = store().get(projectId);
|
|
28156
29007
|
if (!entry) return { path: null };
|
|
28157
|
-
return { path: entry.path, exists: (0,
|
|
29008
|
+
return { path: entry.path, exists: (0, import_node_fs8.existsSync)(entry.path) };
|
|
28158
29009
|
}
|
|
28159
29010
|
async function setPath(rawParams) {
|
|
28160
|
-
const { projectId, path:
|
|
28161
|
-
const resolved = allow(
|
|
29011
|
+
const { projectId, path: path9 } = projectSetPathParamsSchema.parse(rawParams);
|
|
29012
|
+
const resolved = allow(path9);
|
|
28162
29013
|
const entry = await store().set(projectId, resolved);
|
|
28163
29014
|
ctx.log("project path recorded", { projectId, path: entry.path });
|
|
28164
29015
|
return { path: entry.path };
|
|
@@ -28175,7 +29026,7 @@ function createProjectHandlers(ctx, getStore) {
|
|
|
28175
29026
|
projects: store().list().map((e) => ({
|
|
28176
29027
|
projectId: e.projectId,
|
|
28177
29028
|
path: e.path,
|
|
28178
|
-
exists: (0,
|
|
29029
|
+
exists: (0, import_node_fs8.existsSync)(e.path)
|
|
28179
29030
|
}))
|
|
28180
29031
|
};
|
|
28181
29032
|
}
|
|
@@ -28215,11 +29066,102 @@ function armStartupDeadline({
|
|
|
28215
29066
|
return { markOutput: stop, cancel: stop };
|
|
28216
29067
|
}
|
|
28217
29068
|
|
|
29069
|
+
// src/background-jobs.ts
|
|
29070
|
+
var import_node_child_process9 = require("node:child_process");
|
|
29071
|
+
var import_promises8 = require("node:fs/promises");
|
|
29072
|
+
var import_node_util = require("node:util");
|
|
29073
|
+
var execFileAsync2 = (0, import_node_util.promisify)(import_node_child_process9.execFile);
|
|
29074
|
+
var EXEC_TIMEOUT_MS = 5e3;
|
|
29075
|
+
function claudeEvalArg(cmd) {
|
|
29076
|
+
return `'${cmd.replace(/'/g, `'"'"'`)}'`;
|
|
29077
|
+
}
|
|
29078
|
+
function parsePsRows(out) {
|
|
29079
|
+
const rows = [];
|
|
29080
|
+
for (const line of out.split("\n")) {
|
|
29081
|
+
const m = /^\s*(\d+)\s+(\d+)\s(.*)$/.exec(line);
|
|
29082
|
+
if (m) rows.push({ pid: Number(m[1]), ppid: Number(m[2]), args: m[3].trim() });
|
|
29083
|
+
}
|
|
29084
|
+
return rows;
|
|
29085
|
+
}
|
|
29086
|
+
function descendantsOf(rootPid, rows) {
|
|
29087
|
+
const byParent = /* @__PURE__ */ new Map();
|
|
29088
|
+
for (const r of rows) {
|
|
29089
|
+
const list = byParent.get(r.ppid);
|
|
29090
|
+
if (list) list.push(r);
|
|
29091
|
+
else byParent.set(r.ppid, [r]);
|
|
29092
|
+
}
|
|
29093
|
+
const out = [];
|
|
29094
|
+
const queue = [rootPid];
|
|
29095
|
+
const seen = new Set(queue);
|
|
29096
|
+
while (queue.length > 0) {
|
|
29097
|
+
for (const child of byParent.get(queue.shift()) ?? []) {
|
|
29098
|
+
if (seen.has(child.pid)) continue;
|
|
29099
|
+
seen.add(child.pid);
|
|
29100
|
+
out.push(child);
|
|
29101
|
+
queue.push(child.pid);
|
|
29102
|
+
}
|
|
29103
|
+
}
|
|
29104
|
+
return out;
|
|
29105
|
+
}
|
|
29106
|
+
function matchLiveJobs(jobs, descendants, stdoutByPid) {
|
|
29107
|
+
const outputs = new Set(stdoutByPid.values());
|
|
29108
|
+
const alive = [];
|
|
29109
|
+
for (const job of jobs) {
|
|
29110
|
+
const outputFile = `/tasks/${job.id}.output`;
|
|
29111
|
+
const byOutput = job.kind === "shell" && [...outputs].some((path9) => path9.endsWith(outputFile));
|
|
29112
|
+
const evalArg = job.command ? `eval ${claudeEvalArg(job.command)}` : null;
|
|
29113
|
+
const byCommand = evalArg !== null && descendants.some((d) => d.args.includes(evalArg));
|
|
29114
|
+
if (byOutput || byCommand) alive.push(job.id);
|
|
29115
|
+
}
|
|
29116
|
+
return alive;
|
|
29117
|
+
}
|
|
29118
|
+
async function readStdoutPaths(pids) {
|
|
29119
|
+
const out = /* @__PURE__ */ new Map();
|
|
29120
|
+
if (pids.length === 0) return out;
|
|
29121
|
+
if (process.platform === "linux") {
|
|
29122
|
+
await Promise.all(
|
|
29123
|
+
pids.map(
|
|
29124
|
+
(pid2) => (0, import_promises8.readlink)(`/proc/${pid2}/fd/1`).then(
|
|
29125
|
+
(path9) => void out.set(pid2, path9),
|
|
29126
|
+
() => void 0
|
|
29127
|
+
)
|
|
29128
|
+
)
|
|
29129
|
+
);
|
|
29130
|
+
return out;
|
|
29131
|
+
}
|
|
29132
|
+
const res = await execFileAsync2(
|
|
29133
|
+
"lsof",
|
|
29134
|
+
["-b", "-n", "-P", "-w", "-a", "-p", pids.join(","), "-d", "1", "-F", "pn"],
|
|
29135
|
+
{
|
|
29136
|
+
timeout: EXEC_TIMEOUT_MS,
|
|
29137
|
+
maxBuffer: 4 * 1024 * 1024
|
|
29138
|
+
}
|
|
29139
|
+
).catch((err) => ({ stdout: err.stdout ?? "" }));
|
|
29140
|
+
let pid = 0;
|
|
29141
|
+
for (const line of res.stdout.split("\n")) {
|
|
29142
|
+
if (line.startsWith("p")) pid = Number(line.slice(1));
|
|
29143
|
+
else if (line.startsWith("n") && pid) out.set(pid, line.slice(1));
|
|
29144
|
+
}
|
|
29145
|
+
return out;
|
|
29146
|
+
}
|
|
29147
|
+
async function probeBackgroundJobs(rootPid, jobs) {
|
|
29148
|
+
if (process.platform === "win32") return null;
|
|
29149
|
+
if (jobs.length === 0) return [];
|
|
29150
|
+
const { stdout: stdout2 } = await execFileAsync2("ps", ["-A", "-ww", "-o", "pid=,ppid=,args="], {
|
|
29151
|
+
timeout: EXEC_TIMEOUT_MS,
|
|
29152
|
+
maxBuffer: 16 * 1024 * 1024
|
|
29153
|
+
});
|
|
29154
|
+
const descendants = descendantsOf(rootPid, parsePsRows(stdout2));
|
|
29155
|
+
const needStdout = jobs.some((j) => j.kind === "shell");
|
|
29156
|
+
const stdoutByPid = needStdout ? await readStdoutPaths(descendants.map((d) => d.pid)) : /* @__PURE__ */ new Map();
|
|
29157
|
+
return matchLiveJobs(jobs, descendants, stdoutByPid);
|
|
29158
|
+
}
|
|
29159
|
+
|
|
28218
29160
|
// src/spawn-admission.ts
|
|
28219
|
-
var
|
|
29161
|
+
var import_node_os6 = require("node:os");
|
|
28220
29162
|
var DEFAULT_START_CAP_MS = 5e3;
|
|
28221
29163
|
var DEFAULT_WARM_QUIET_MS = 3e3;
|
|
28222
|
-
function defaultAdmissionConcurrency(cores = (0,
|
|
29164
|
+
function defaultAdmissionConcurrency(cores = (0, import_node_os6.availableParallelism)()) {
|
|
28223
29165
|
return Math.max(2, Math.min(4, Math.floor(cores / 2)));
|
|
28224
29166
|
}
|
|
28225
29167
|
var LANE_RANK = { foreground: 0, background: 1, warm: 2 };
|
|
@@ -28342,7 +29284,7 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
28342
29284
|
const admission = options.admission ?? createSpawnAdmission();
|
|
28343
29285
|
const queued = /* @__PURE__ */ new Map();
|
|
28344
29286
|
function resolveCwd(opts) {
|
|
28345
|
-
if (opts.cwd) return opts.cwd;
|
|
29287
|
+
if (opts.cwd) return spawnCwd(opts.cwd, ctx.config.allowedRoots);
|
|
28346
29288
|
if (!opts.projectId) throw new Error("pty.spawn needs either cwd or projectId");
|
|
28347
29289
|
const entry = getProjectPaths?.()?.get(opts.projectId);
|
|
28348
29290
|
if (!entry) {
|
|
@@ -28350,7 +29292,7 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
28350
29292
|
`project ${opts.projectId} is not checked out on this computer \u2014 adopt a folder or enable remote git for it`
|
|
28351
29293
|
);
|
|
28352
29294
|
}
|
|
28353
|
-
return entry.path;
|
|
29295
|
+
return spawnCwd(entry.path, ctx.config.allowedRoots);
|
|
28354
29296
|
}
|
|
28355
29297
|
function buildEnv(overrides) {
|
|
28356
29298
|
const merged = overrides ? { ...sanitizeSpawnEnv(process.env), ...overrides } : sanitizeSpawnEnv(process.env);
|
|
@@ -28590,6 +29532,13 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
28590
29532
|
const item = queued.get(params.sessionId);
|
|
28591
29533
|
return { ok: true, queued: item ? admission.prioritize(item.ticket) : false };
|
|
28592
29534
|
}
|
|
29535
|
+
async function backgroundJobs(rawParams) {
|
|
29536
|
+
const params = ptyBackgroundJobsParamsSchema.parse(rawParams);
|
|
29537
|
+
const pid = sessions.get(params.sessionId)?.proc.pid;
|
|
29538
|
+
if (pid === void 0) return { ok: true, supported: true, alive: [] };
|
|
29539
|
+
const alive = await probeBackgroundJobs(pid, params.jobs);
|
|
29540
|
+
return alive === null ? { ok: true, supported: false, alive: [] } : { ok: true, supported: true, alive };
|
|
29541
|
+
}
|
|
28593
29542
|
function dropQueued(reason) {
|
|
28594
29543
|
for (const id of [...queued.keys()]) cancelQueued(id, reason);
|
|
28595
29544
|
}
|
|
@@ -28602,6 +29551,15 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
28602
29551
|
`no warm session ${params.warmId} to adopt`
|
|
28603
29552
|
);
|
|
28604
29553
|
}
|
|
29554
|
+
const displaced = sessions.get(params.sessionId);
|
|
29555
|
+
if (displaced) {
|
|
29556
|
+
ctx.log("warm adopt displaced a live session", {
|
|
29557
|
+
warmId: params.warmId,
|
|
29558
|
+
sessionId: params.sessionId,
|
|
29559
|
+
displacedPid: displaced.proc.pid,
|
|
29560
|
+
displacedWarm: !!displaced.warm
|
|
29561
|
+
});
|
|
29562
|
+
}
|
|
28605
29563
|
sessions.delete(params.warmId);
|
|
28606
29564
|
session.id = params.sessionId;
|
|
28607
29565
|
delete session.warm;
|
|
@@ -28759,6 +29717,7 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
28759
29717
|
[HubToComputerMethods.ptyGetBufferSince]: getBufferSince,
|
|
28760
29718
|
[HubToComputerMethods.ptyList]: list,
|
|
28761
29719
|
[HubToComputerMethods.ptyPrioritize]: prioritize,
|
|
29720
|
+
[HubToComputerMethods.ptyBackgroundJobs]: backgroundJobs,
|
|
28762
29721
|
[HubToComputerMethods.ptyWarmSpawn]: warmSpawn,
|
|
28763
29722
|
[HubToComputerMethods.ptyWarmAdopt]: warmAdopt,
|
|
28764
29723
|
[HubToComputerMethods.ptyWarmKill]: warmKill,
|
|
@@ -28768,27 +29727,46 @@ function createPtyHandlers(ctx, getProjectPaths, options = {}) {
|
|
|
28768
29727
|
}
|
|
28769
29728
|
|
|
28770
29729
|
// src/handlers/computer-config.ts
|
|
28771
|
-
var
|
|
28772
|
-
var
|
|
29730
|
+
var import_node_fs9 = require("node:fs");
|
|
29731
|
+
var import_node_path11 = require("node:path");
|
|
29732
|
+
function canonicalRoots(roots) {
|
|
29733
|
+
const applied = [];
|
|
29734
|
+
const rejected = [];
|
|
29735
|
+
for (const root of roots) {
|
|
29736
|
+
if (!(0, import_node_path11.isAbsolute)(root)) {
|
|
29737
|
+
rejected.push({ path: root, reason: "not an absolute path" });
|
|
29738
|
+
continue;
|
|
29739
|
+
}
|
|
29740
|
+
let real;
|
|
29741
|
+
try {
|
|
29742
|
+
real = import_node_fs9.realpathSync.native((0, import_node_path11.resolve)(root));
|
|
29743
|
+
} catch {
|
|
29744
|
+
rejected.push({ path: root, reason: "does not exist on this computer" });
|
|
29745
|
+
continue;
|
|
29746
|
+
}
|
|
29747
|
+
if (!applied.includes(real)) applied.push(real);
|
|
29748
|
+
}
|
|
29749
|
+
return { roots: applied, rejected };
|
|
29750
|
+
}
|
|
29751
|
+
function reloadLocalRoots(config2, declared) {
|
|
29752
|
+
if (!declared || declared.length === 0) return null;
|
|
29753
|
+
const { roots } = canonicalRoots(declared);
|
|
29754
|
+
if (roots.length === 0) return null;
|
|
29755
|
+
const same = roots.length === config2.allowedRoots.length && roots.every((r, i) => r === config2.allowedRoots[i]);
|
|
29756
|
+
if (same) return null;
|
|
29757
|
+
config2.allowedRoots = roots;
|
|
29758
|
+
return roots;
|
|
29759
|
+
}
|
|
28773
29760
|
function createComputerConfigHandlers(ctx) {
|
|
28774
29761
|
function setAllowedRoots(rawParams) {
|
|
28775
29762
|
const { roots } = computerSetAllowedRootsParamsSchema.parse(rawParams);
|
|
28776
|
-
|
|
28777
|
-
|
|
28778
|
-
|
|
28779
|
-
|
|
28780
|
-
|
|
28781
|
-
continue;
|
|
28782
|
-
}
|
|
28783
|
-
let real;
|
|
28784
|
-
try {
|
|
28785
|
-
real = import_node_fs8.realpathSync.native((0, import_node_path8.resolve)(root));
|
|
28786
|
-
} catch {
|
|
28787
|
-
rejected.push({ path: root, reason: "does not exist on this computer" });
|
|
28788
|
-
continue;
|
|
28789
|
-
}
|
|
28790
|
-
if (!applied.includes(real)) applied.push(real);
|
|
29763
|
+
if (!ctx.config.hubMayEditRoots) {
|
|
29764
|
+
throw new RpcError(
|
|
29765
|
+
ComputerTransportErrorCodes.refusedByComputer,
|
|
29766
|
+
`this computer's folder limits are set on the computer itself, not by a hub \u2014 on that machine run \`slay computer roots ${ctx.config.name} --add <dir>\` (or edit allowedRoots in its computer.config.json)`
|
|
29767
|
+
);
|
|
28791
29768
|
}
|
|
29769
|
+
const { roots: applied, rejected } = canonicalRoots(roots);
|
|
28792
29770
|
if (applied.length === 0) {
|
|
28793
29771
|
throw new Error(
|
|
28794
29772
|
rejected.length > 0 ? `no usable roots: ${rejected.map((r) => `${r.path} (${r.reason})`).join(", ")}` : "a computer needs at least one allowed root; revoke the computer to stop it entirely"
|
|
@@ -28805,13 +29783,13 @@ function createComputerConfigHandlers(ctx) {
|
|
|
28805
29783
|
}
|
|
28806
29784
|
|
|
28807
29785
|
// src/project-paths.ts
|
|
28808
|
-
var
|
|
28809
|
-
var
|
|
28810
|
-
var
|
|
29786
|
+
var import_promises9 = require("node:fs/promises");
|
|
29787
|
+
var import_node_os7 = require("node:os");
|
|
29788
|
+
var import_node_path12 = require("node:path");
|
|
28811
29789
|
function slayzoneRootDir3() {
|
|
28812
29790
|
if (process.env.SLAYZONE_ROOT) return process.env.SLAYZONE_ROOT;
|
|
28813
|
-
const home = process.env.HOME ?? process.env.USERPROFILE ?? (0,
|
|
28814
|
-
return (0,
|
|
29791
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? (0, import_node_os7.homedir)();
|
|
29792
|
+
return (0, import_node_path12.join)(home, ".slayzone");
|
|
28815
29793
|
}
|
|
28816
29794
|
var projectPathEntrySchema = external_exports.object({
|
|
28817
29795
|
/** Absolute, realpath-resolved at write time. */
|
|
@@ -28820,13 +29798,13 @@ var projectPathEntrySchema = external_exports.object({
|
|
|
28820
29798
|
});
|
|
28821
29799
|
var projectMapSchema = external_exports.record(external_exports.string(), projectPathEntrySchema);
|
|
28822
29800
|
function projectsFilePath(baseDir) {
|
|
28823
|
-
return (0,
|
|
29801
|
+
return (0, import_node_path12.join)(baseDir ?? slayzoneRootDir3(), "computer.projects.json");
|
|
28824
29802
|
}
|
|
28825
29803
|
var fileSchema = external_exports.record(external_exports.string(), projectMapSchema);
|
|
28826
29804
|
async function readFileMap(filePath) {
|
|
28827
29805
|
let raw;
|
|
28828
29806
|
try {
|
|
28829
|
-
raw = await (0,
|
|
29807
|
+
raw = await (0, import_promises9.readFile)(filePath, "utf8");
|
|
28830
29808
|
} catch (err) {
|
|
28831
29809
|
if (err.code === "ENOENT") return {};
|
|
28832
29810
|
throw err;
|
|
@@ -28851,14 +29829,14 @@ async function readFileMap(filePath) {
|
|
|
28851
29829
|
return fileSchema.parse(out);
|
|
28852
29830
|
}
|
|
28853
29831
|
async function writeFileMap(filePath, map2) {
|
|
28854
|
-
await (0,
|
|
29832
|
+
await (0, import_promises9.mkdir)((0, import_node_path12.dirname)(filePath), { recursive: true, mode: 448 });
|
|
28855
29833
|
const tmpPath = `${filePath}.tmp-${process.pid}-${Date.now()}`;
|
|
28856
|
-
await (0,
|
|
29834
|
+
await (0, import_promises9.writeFile)(tmpPath, `${JSON.stringify(map2, null, 2)}
|
|
28857
29835
|
`, { mode: 384 });
|
|
28858
29836
|
try {
|
|
28859
|
-
await (0,
|
|
29837
|
+
await (0, import_promises9.rename)(tmpPath, filePath);
|
|
28860
29838
|
} catch (err) {
|
|
28861
|
-
await (0,
|
|
29839
|
+
await (0, import_promises9.rm)(tmpPath, { force: true });
|
|
28862
29840
|
throw err;
|
|
28863
29841
|
}
|
|
28864
29842
|
}
|
|
@@ -28871,8 +29849,8 @@ async function createProjectPathStore(hubHost, options = {}) {
|
|
|
28871
29849
|
get(projectId) {
|
|
28872
29850
|
return cache[projectId] ?? null;
|
|
28873
29851
|
},
|
|
28874
|
-
async set(projectId,
|
|
28875
|
-
const entry = { path:
|
|
29852
|
+
async set(projectId, path9) {
|
|
29853
|
+
const entry = { path: path9, updatedAt: Date.now() };
|
|
28876
29854
|
const all = await readFileMap(filePath);
|
|
28877
29855
|
all[hubHost] = { ...all[hubHost] ?? {}, [projectId]: entry };
|
|
28878
29856
|
await writeFileMap(filePath, all);
|
|
@@ -28910,6 +29888,7 @@ function createHubRequestHandler(deps) {
|
|
|
28910
29888
|
deps.admission ? { admission: deps.admission } : {}
|
|
28911
29889
|
);
|
|
28912
29890
|
const proc = createProcHandlers(ctx, deps.getProjectPaths);
|
|
29891
|
+
const fs6 = createFsHandlers(ctx);
|
|
28913
29892
|
const shutdownHandler = async (params) => {
|
|
28914
29893
|
const parsed = computerShutdownParamsSchema.safeParse(params ?? {});
|
|
28915
29894
|
const reason = parsed.success ? parsed.data.reason ?? "hub-requested" : "hub-requested";
|
|
@@ -28921,21 +29900,67 @@ function createHubRequestHandler(deps) {
|
|
|
28921
29900
|
...pty2.handlers,
|
|
28922
29901
|
...proc.handlers,
|
|
28923
29902
|
...createGitHandlers(ctx),
|
|
28924
|
-
...
|
|
29903
|
+
...fs6.handlers,
|
|
28925
29904
|
...createComputerConfigHandlers(ctx),
|
|
28926
29905
|
...createGhHandlers(ctx),
|
|
28927
29906
|
...createProjectHandlers(ctx, deps.getProjectPaths ?? (() => null))
|
|
28928
29907
|
};
|
|
28929
|
-
const
|
|
29908
|
+
const hub = deps.hubHost ?? "unknown";
|
|
29909
|
+
const ptyMethods = new Set(Object.keys(pty2.handlers));
|
|
29910
|
+
const handle = async (method, params, rawActor) => {
|
|
29911
|
+
const parsedActor = requestActorSchema.safeParse(rawActor);
|
|
29912
|
+
const actor = parsedActor.success ? parsedActor.data : null;
|
|
29913
|
+
const record2 = (result, code, reason) => deps.requestLog?.record({
|
|
29914
|
+
hub,
|
|
29915
|
+
method,
|
|
29916
|
+
actor,
|
|
29917
|
+
params,
|
|
29918
|
+
result,
|
|
29919
|
+
...code !== void 0 ? { code } : {},
|
|
29920
|
+
...reason !== void 0 ? { reason } : {}
|
|
29921
|
+
});
|
|
29922
|
+
if (!actor) {
|
|
29923
|
+
const err = new RpcError(
|
|
29924
|
+
ComputerTransportErrorCodes.protocolMismatch,
|
|
29925
|
+
"hub sent no actor with this request (a hub older than computer protocol v3) \u2014 upgrade the hub to this computer's release"
|
|
29926
|
+
);
|
|
29927
|
+
record2("refused", err.code, err.message);
|
|
29928
|
+
throw err;
|
|
29929
|
+
}
|
|
28930
29930
|
const entry = table[method];
|
|
28931
29931
|
if (!entry) {
|
|
28932
|
-
|
|
29932
|
+
const err = new RpcError(
|
|
29933
|
+
ComputerTransportErrorCodes.unimplemented,
|
|
29934
|
+
`unimplemented: ${method}`
|
|
29935
|
+
);
|
|
29936
|
+
record2("error", err.code);
|
|
29937
|
+
throw err;
|
|
29938
|
+
}
|
|
29939
|
+
try {
|
|
29940
|
+
if (deps.pathReady && !ptyMethods.has(method)) await deps.pathReady;
|
|
29941
|
+
const result = await entry(params);
|
|
29942
|
+
record2("ok");
|
|
29943
|
+
return result;
|
|
29944
|
+
} catch (err) {
|
|
29945
|
+
if (err instanceof JailViolationError) {
|
|
29946
|
+
log("request refused by the path jail", { method, reason: err.message });
|
|
29947
|
+
const refused = new RpcError(ComputerTransportErrorCodes.refusedByComputer, err.message);
|
|
29948
|
+
record2("refused", refused.code, err.message);
|
|
29949
|
+
throw refused;
|
|
29950
|
+
}
|
|
29951
|
+
if (err instanceof RpcError && err.code === ComputerTransportErrorCodes.refusedByComputer) {
|
|
29952
|
+
log("request refused by local policy", { method, reason: err.message });
|
|
29953
|
+
record2("refused", err.code, err.message);
|
|
29954
|
+
throw err;
|
|
29955
|
+
}
|
|
29956
|
+
record2("error", err instanceof RpcError ? err.code : JSON_RPC_INTERNAL_ERROR);
|
|
29957
|
+
throw err;
|
|
28933
29958
|
}
|
|
28934
|
-
return entry(params);
|
|
28935
29959
|
};
|
|
28936
29960
|
const dispose = () => {
|
|
28937
29961
|
pty2.disposeAll();
|
|
28938
29962
|
proc.disposeAll();
|
|
29963
|
+
fs6.disposeAll();
|
|
28939
29964
|
};
|
|
28940
29965
|
const setAgentHookUrl = (url2) => {
|
|
28941
29966
|
ctx.agentHookUrl = url2;
|
|
@@ -28964,6 +29989,36 @@ function startComputer(config2, deps = {}) {
|
|
|
28964
29989
|
});
|
|
28965
29990
|
});
|
|
28966
29991
|
let handle;
|
|
29992
|
+
const stateDir = deps.stateDir ?? (0, import_node_path13.dirname)(getComputerConfigFilePath());
|
|
29993
|
+
const configFile = (0, import_node_path13.join)(stateDir, "computer.config.json");
|
|
29994
|
+
setProtectedPaths([
|
|
29995
|
+
configFile,
|
|
29996
|
+
// The legacy file computer.config.json falls back to when absent.
|
|
29997
|
+
(0, import_node_path13.join)(stateDir, "config.json"),
|
|
29998
|
+
credentialsFilePath(stateDir),
|
|
29999
|
+
(0, import_node_path13.join)(stateDir, REQUEST_LOG_DIR_NAME)
|
|
30000
|
+
]);
|
|
30001
|
+
const requestLog = createRequestLog({
|
|
30002
|
+
stateDir,
|
|
30003
|
+
onError: (err) => log("request log write failed", { error: String(err) })
|
|
30004
|
+
});
|
|
30005
|
+
log("local request log", { file: requestLog.filePath });
|
|
30006
|
+
const watchLocalRoots = !config2.hubMayEditRoots;
|
|
30007
|
+
if (watchLocalRoots) {
|
|
30008
|
+
(0, import_node_fs10.watchFile)(configFile, { interval: 2e3, persistent: false }, () => {
|
|
30009
|
+
const declared = loadComputerConfigFile(configFile).allowedRoots;
|
|
30010
|
+
const applied = reloadLocalRoots(config2, declared);
|
|
30011
|
+
if (!applied) return;
|
|
30012
|
+
log("allowedRoots reloaded from local config", { applied });
|
|
30013
|
+
requestLog.record({
|
|
30014
|
+
hub: "local",
|
|
30015
|
+
method: "local.allowedRoots",
|
|
30016
|
+
actor: null,
|
|
30017
|
+
params: { roots: applied },
|
|
30018
|
+
result: "ok"
|
|
30019
|
+
});
|
|
30020
|
+
});
|
|
30021
|
+
}
|
|
28967
30022
|
const admission = createSpawnAdmission();
|
|
28968
30023
|
const connectHub = (hubUrl, isPrimary) => {
|
|
28969
30024
|
const credentialStore = (isPrimary ? deps.credentialStore : void 0) ?? createFileCredentialStore(hubHostFromUrl(hubUrl));
|
|
@@ -28973,7 +30028,7 @@ function startComputer(config2, deps = {}) {
|
|
|
28973
30028
|
identity,
|
|
28974
30029
|
credentialStore,
|
|
28975
30030
|
...config2.joinToken && isPrimary ? { joinToken: config2.joinToken } : {},
|
|
28976
|
-
onHubRequest: (method, params) => dispatch.handle(method, params),
|
|
30031
|
+
onHubRequest: (method, params, actor) => dispatch.handle(method, params, actor),
|
|
28977
30032
|
log
|
|
28978
30033
|
});
|
|
28979
30034
|
let projectPaths = null;
|
|
@@ -28994,7 +30049,10 @@ function startComputer(config2, deps = {}) {
|
|
|
28994
30049
|
config: config2,
|
|
28995
30050
|
log,
|
|
28996
30051
|
getProjectPaths: () => projectPaths,
|
|
28997
|
-
admission
|
|
30052
|
+
admission,
|
|
30053
|
+
requestLog,
|
|
30054
|
+
hubHost: hubHostFromUrl(hubUrl),
|
|
30055
|
+
...deps.pathReady ? { pathReady: deps.pathReady } : {}
|
|
28998
30056
|
});
|
|
28999
30057
|
dialer.events.on(
|
|
29000
30058
|
"connected",
|
|
@@ -29044,6 +30102,7 @@ function startComputer(config2, deps = {}) {
|
|
|
29044
30102
|
dialers: connections.map((c) => c.dialer),
|
|
29045
30103
|
stop: async () => {
|
|
29046
30104
|
hostIdStop?.();
|
|
30105
|
+
if (watchLocalRoots) (0, import_node_fs10.unwatchFile)(configFile);
|
|
29047
30106
|
await Promise.all(connections.map((c) => c.dispose()));
|
|
29048
30107
|
}
|
|
29049
30108
|
};
|
|
@@ -29085,7 +30144,7 @@ async function maybeInteractiveSetup() {
|
|
|
29085
30144
|
default: defaultRoot,
|
|
29086
30145
|
transform: (raw) => {
|
|
29087
30146
|
const roots = raw.split(",").map((r) => r.trim()).filter((r) => r.length > 0);
|
|
29088
|
-
return roots.length > 0 ? { config: roots, env: roots.join(
|
|
30147
|
+
return roots.length > 0 ? { config: roots, env: roots.join(import_node_path14.delimiter) } : null;
|
|
29089
30148
|
}
|
|
29090
30149
|
},
|
|
29091
30150
|
{
|
|
@@ -29097,7 +30156,7 @@ async function maybeInteractiveSetup() {
|
|
|
29097
30156
|
configKey: "computerName",
|
|
29098
30157
|
envKey: "SLAYZONE_COMPUTER_NAME",
|
|
29099
30158
|
label: "Computer display name",
|
|
29100
|
-
hint: `default: ${(0,
|
|
30159
|
+
hint: `default: ${(0, import_node_os8.hostname)()}`
|
|
29101
30160
|
}
|
|
29102
30161
|
]
|
|
29103
30162
|
});
|
|
@@ -29109,7 +30168,8 @@ async function maybeInteractiveSetup() {
|
|
|
29109
30168
|
return out;
|
|
29110
30169
|
}
|
|
29111
30170
|
async function main() {
|
|
29112
|
-
|
|
30171
|
+
const supervised = process.env.SLAYZONE_SUPERVISED === "1";
|
|
30172
|
+
if (!supervised && !process.env.SLAYZONE_ROOT) {
|
|
29113
30173
|
process.env.SLAYZONE_ROOT = process.cwd();
|
|
29114
30174
|
}
|
|
29115
30175
|
const prompted = await maybeInteractiveSetup();
|
|
@@ -29139,12 +30199,18 @@ async function main() {
|
|
|
29139
30199
|
`);
|
|
29140
30200
|
};
|
|
29141
30201
|
for (const notice of ignoredCertPinNotices()) log(notice);
|
|
29142
|
-
if (
|
|
30202
|
+
if (!supervised) {
|
|
29143
30203
|
const { installAgentHooks: installAgentHooks2 } = await Promise.resolve().then(() => (init_agent_hooks2(), agent_hooks_exports));
|
|
29144
30204
|
await installAgentHooks2(log);
|
|
29145
30205
|
}
|
|
30206
|
+
const pathReady = adoptLoginShellPath().then(
|
|
30207
|
+
(adopted) => log(
|
|
30208
|
+
adopted ? `PATH: adopted login-shell PATH (+${adopted.added.length} entries)` : "PATH: no login-shell PATH probed \u2014 keeping the inherited one"
|
|
30209
|
+
)
|
|
30210
|
+
);
|
|
29146
30211
|
const handle = startComputer(config2, {
|
|
29147
30212
|
log,
|
|
30213
|
+
pathReady,
|
|
29148
30214
|
onShutdown: () => {
|
|
29149
30215
|
process.exitCode = 0;
|
|
29150
30216
|
}
|
|
@@ -29157,16 +30223,27 @@ async function main() {
|
|
|
29157
30223
|
if (fatal) process.exitCode = 1;
|
|
29158
30224
|
});
|
|
29159
30225
|
let stopping = false;
|
|
29160
|
-
const gracefulStop = (
|
|
30226
|
+
const gracefulStop = (why) => {
|
|
29161
30227
|
if (stopping) return;
|
|
29162
30228
|
stopping = true;
|
|
29163
|
-
log(
|
|
30229
|
+
log(why);
|
|
30230
|
+
if (supervised) {
|
|
30231
|
+
setTimeout(() => {
|
|
30232
|
+
log(`still running ${SUPERVISED_STOP_DEADLINE_MS}ms after stop began \u2014 forcing exit`);
|
|
30233
|
+
process.exit(1);
|
|
30234
|
+
}, SUPERVISED_STOP_DEADLINE_MS).unref();
|
|
30235
|
+
}
|
|
29164
30236
|
void handle.stop().then(() => {
|
|
29165
30237
|
process.exitCode ??= 0;
|
|
29166
30238
|
});
|
|
29167
30239
|
};
|
|
29168
|
-
process.on("SIGINT", () => gracefulStop("SIGINT"));
|
|
29169
|
-
process.on("SIGTERM", () => gracefulStop("SIGTERM"));
|
|
30240
|
+
process.on("SIGINT", () => gracefulStop("received SIGINT, stopping"));
|
|
30241
|
+
process.on("SIGTERM", () => gracefulStop("received SIGTERM, stopping"));
|
|
30242
|
+
if (supervised) {
|
|
30243
|
+
watchParent({
|
|
30244
|
+
onParentDeath: (reason) => gracefulStop(`parent app is gone (${reason}), stopping`)
|
|
30245
|
+
});
|
|
30246
|
+
}
|
|
29170
30247
|
}
|
|
29171
30248
|
void main();
|
|
29172
30249
|
//# sourceMappingURL=bin.cjs.map
|