@slayzone/computer 0.36.0-beta.18 → 0.36.0-beta.19

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 CHANGED
@@ -85,103 +85,6 @@ var init_dirs = __esm({
85
85
  }
86
86
  });
87
87
 
88
- // ../../../node_modules/ws/lib/stream.js
89
- var require_stream = __commonJS({
90
- "../../../node_modules/ws/lib/stream.js"(exports2, module2) {
91
- "use strict";
92
- var { Duplex } = require("stream");
93
- function emitClose(stream) {
94
- stream.emit("close");
95
- }
96
- function duplexOnEnd() {
97
- if (!this.destroyed && this._writableState.finished) {
98
- this.destroy();
99
- }
100
- }
101
- function duplexOnError(err) {
102
- this.removeListener("error", duplexOnError);
103
- this.destroy();
104
- if (this.listenerCount("error") === 0) {
105
- this.emit("error", err);
106
- }
107
- }
108
- function createWebSocketStream2(ws, options) {
109
- let terminateOnDestroy = true;
110
- const duplex = new Duplex({
111
- ...options,
112
- autoDestroy: false,
113
- emitClose: false,
114
- objectMode: false,
115
- writableObjectMode: false
116
- });
117
- ws.on("message", function message(msg, isBinary) {
118
- const data = !isBinary && duplex._readableState.objectMode ? msg.toString() : msg;
119
- if (!duplex.push(data)) ws.pause();
120
- });
121
- ws.once("error", function error48(err) {
122
- if (duplex.destroyed) return;
123
- terminateOnDestroy = false;
124
- duplex.destroy(err);
125
- });
126
- ws.once("close", function close() {
127
- if (duplex.destroyed) return;
128
- duplex.push(null);
129
- });
130
- duplex._destroy = function(err, callback) {
131
- if (ws.readyState === ws.CLOSED) {
132
- callback(err);
133
- process.nextTick(emitClose, duplex);
134
- return;
135
- }
136
- let called = false;
137
- ws.once("error", function error48(err2) {
138
- called = true;
139
- callback(err2);
140
- });
141
- ws.once("close", function close() {
142
- if (!called) callback(err);
143
- process.nextTick(emitClose, duplex);
144
- });
145
- if (terminateOnDestroy) ws.terminate();
146
- };
147
- duplex._final = function(callback) {
148
- if (ws.readyState === ws.CONNECTING) {
149
- ws.once("open", function open2() {
150
- duplex._final(callback);
151
- });
152
- return;
153
- }
154
- if (ws._socket === null) return;
155
- if (ws._socket._writableState.finished) {
156
- callback();
157
- if (duplex._readableState.endEmitted) duplex.destroy();
158
- } else {
159
- ws._socket.once("finish", function finish() {
160
- callback();
161
- });
162
- ws.close();
163
- }
164
- };
165
- duplex._read = function() {
166
- if (ws.isPaused) ws.resume();
167
- };
168
- duplex._write = function(chunk, encoding, callback) {
169
- if (ws.readyState === ws.CONNECTING) {
170
- ws.once("open", function open2() {
171
- duplex._write(chunk, encoding, callback);
172
- });
173
- return;
174
- }
175
- ws.send(chunk, callback);
176
- };
177
- duplex.on("end", duplexOnEnd);
178
- duplex.on("error", duplexOnError);
179
- return duplex;
180
- }
181
- module2.exports = createWebSocketStream2;
182
- }
183
- });
184
-
185
88
  // ../../../node_modules/ws/lib/constants.js
186
89
  var require_constants = __commonJS({
187
90
  "../../../node_modules/ws/lib/constants.js"(exports2, module2) {
@@ -191,6 +94,7 @@ var require_constants = __commonJS({
191
94
  if (hasBlob) BINARY_TYPES.push("blob");
192
95
  module2.exports = {
193
96
  BINARY_TYPES,
97
+ CLOSE_TIMEOUT: 3e4,
194
98
  EMPTY_BUFFER: Buffer.alloc(0),
195
99
  GUID: "258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
196
100
  hasBlob,
@@ -345,7 +249,7 @@ var require_permessage_deflate = __commonJS({
345
249
  var kBuffers = Symbol("buffers");
346
250
  var kError = Symbol("error");
347
251
  var zlibLimiter;
348
- var PerMessageDeflate = class {
252
+ var PerMessageDeflate2 = class {
349
253
  /**
350
254
  * Creates a PerMessageDeflate instance.
351
255
  *
@@ -356,6 +260,9 @@ var require_permessage_deflate = __commonJS({
356
260
  * acknowledge disabling of client context takeover
357
261
  * @param {Number} [options.concurrencyLimit=10] The number of concurrent
358
262
  * calls to zlib
263
+ * @param {Boolean} [options.isServer=false] Create the instance in either
264
+ * server or client mode
265
+ * @param {Number} [options.maxPayload=0] The maximum allowed message length
359
266
  * @param {(Boolean|Number)} [options.serverMaxWindowBits] Request/confirm the
360
267
  * use of a custom server window size
361
268
  * @param {Boolean} [options.serverNoContextTakeover=false] Request/accept
@@ -366,15 +273,12 @@ var require_permessage_deflate = __commonJS({
366
273
  * deflate
367
274
  * @param {Object} [options.zlibInflateOptions] Options to pass to zlib on
368
275
  * inflate
369
- * @param {Boolean} [isServer=false] Create the instance in either server or
370
- * client mode
371
- * @param {Number} [maxPayload=0] The maximum allowed message length
372
276
  */
373
- constructor(options, isServer, maxPayload) {
374
- this._maxPayload = maxPayload | 0;
277
+ constructor(options) {
375
278
  this._options = options || {};
376
279
  this._threshold = this._options.threshold !== void 0 ? this._options.threshold : 1024;
377
- this._isServer = !!isServer;
280
+ this._maxPayload = this._options.maxPayload | 0;
281
+ this._isServer = !!this._options.isServer;
378
282
  this._deflate = null;
379
283
  this._inflate = null;
380
284
  this.params = null;
@@ -458,7 +362,7 @@ var require_permessage_deflate = __commonJS({
458
362
  acceptAsServer(offers) {
459
363
  const opts = this._options;
460
364
  const accepted = offers.find((params) => {
461
- if (opts.serverNoContextTakeover === false && params.server_no_context_takeover || params.server_max_window_bits && (opts.serverMaxWindowBits === false || typeof opts.serverMaxWindowBits === "number" && opts.serverMaxWindowBits > params.server_max_window_bits) || typeof opts.clientMaxWindowBits === "number" && !params.client_max_window_bits) {
365
+ if (opts.serverNoContextTakeover === false && params.server_no_context_takeover || params.server_max_window_bits && (opts.serverMaxWindowBits === false || typeof opts.serverMaxWindowBits === "number" && opts.serverMaxWindowBits > params.server_max_window_bits) || typeof opts.clientMaxWindowBits === "number" && (typeof params.client_max_window_bits === "number" ? opts.clientMaxWindowBits > params.client_max_window_bits : !params.client_max_window_bits)) {
462
366
  return false;
463
367
  }
464
368
  return true;
@@ -683,7 +587,7 @@ var require_permessage_deflate = __commonJS({
683
587
  });
684
588
  }
685
589
  };
686
- module2.exports = PerMessageDeflate;
590
+ module2.exports = PerMessageDeflate2;
687
591
  function deflateOnData(chunk) {
688
592
  this[kBuffers].push(chunk);
689
593
  this[kTotalLength] += chunk.length;
@@ -702,6 +606,10 @@ var require_permessage_deflate = __commonJS({
702
606
  }
703
607
  function inflateOnError(err) {
704
608
  this[kPerMessageDeflate]._inflate = null;
609
+ if (this[kError]) {
610
+ this[kCallback](this[kError]);
611
+ return;
612
+ }
705
613
  err[kStatusCode] = 1007;
706
614
  this[kCallback](err);
707
615
  }
@@ -914,7 +822,7 @@ var require_receiver = __commonJS({
914
822
  "../../../node_modules/ws/lib/receiver.js"(exports2, module2) {
915
823
  "use strict";
916
824
  var { Writable } = require("stream");
917
- var PerMessageDeflate = require_permessage_deflate();
825
+ var PerMessageDeflate2 = require_permessage_deflate();
918
826
  var {
919
827
  BINARY_TYPES,
920
828
  EMPTY_BUFFER,
@@ -944,6 +852,10 @@ var require_receiver = __commonJS({
944
852
  * extensions
945
853
  * @param {Boolean} [options.isServer=false] Specifies whether to operate in
946
854
  * client or server mode
855
+ * @param {Number} [options.maxBufferedChunks=0] The maximum number of
856
+ * buffered data chunks
857
+ * @param {Number} [options.maxFragments=0] The maximum number of message
858
+ * fragments
947
859
  * @param {Number} [options.maxPayload=0] The maximum allowed message length
948
860
  * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
949
861
  * not to skip UTF-8 validation for text and close messages
@@ -954,6 +866,8 @@ var require_receiver = __commonJS({
954
866
  this._binaryType = options.binaryType || BINARY_TYPES[0];
955
867
  this._extensions = options.extensions || {};
956
868
  this._isServer = !!options.isServer;
869
+ this._maxBufferedChunks = options.maxBufferedChunks | 0;
870
+ this._maxFragments = options.maxFragments | 0;
957
871
  this._maxPayload = options.maxPayload | 0;
958
872
  this._skipUTF8Validation = !!options.skipUTF8Validation;
959
873
  this[kWebSocket] = void 0;
@@ -968,6 +882,7 @@ var require_receiver = __commonJS({
968
882
  this._opcode = 0;
969
883
  this._totalPayloadLength = 0;
970
884
  this._messageLength = 0;
885
+ this._numFragments = 0;
971
886
  this._fragments = [];
972
887
  this._errored = false;
973
888
  this._loop = false;
@@ -983,6 +898,18 @@ var require_receiver = __commonJS({
983
898
  */
984
899
  _write(chunk, encoding, cb) {
985
900
  if (this._opcode === 8 && this._state == GET_INFO) return cb();
901
+ if (this._maxBufferedChunks > 0 && this._buffers.length >= this._maxBufferedChunks) {
902
+ cb(
903
+ this.createError(
904
+ RangeError,
905
+ "Too many buffered chunks",
906
+ false,
907
+ 1008,
908
+ "WS_ERR_TOO_MANY_BUFFERED_PARTS"
909
+ )
910
+ );
911
+ return;
912
+ }
986
913
  this._bufferedBytes += chunk.length;
987
914
  this._buffers.push(chunk);
988
915
  this.startLoop(cb);
@@ -1081,7 +1008,7 @@ var require_receiver = __commonJS({
1081
1008
  return;
1082
1009
  }
1083
1010
  const compressed = (buf[0] & 64) === 64;
1084
- if (compressed && !this._extensions[PerMessageDeflate.extensionName]) {
1011
+ if (compressed && !this._extensions[PerMessageDeflate2.extensionName]) {
1085
1012
  const error48 = this.createError(
1086
1013
  RangeError,
1087
1014
  "RSV1 must be clear",
@@ -1306,6 +1233,17 @@ var require_receiver = __commonJS({
1306
1233
  this.controlMessage(data, cb);
1307
1234
  return;
1308
1235
  }
1236
+ if (this._maxFragments > 0 && ++this._numFragments > this._maxFragments) {
1237
+ const error48 = this.createError(
1238
+ RangeError,
1239
+ "Too many message fragments",
1240
+ false,
1241
+ 1008,
1242
+ "WS_ERR_TOO_MANY_BUFFERED_PARTS"
1243
+ );
1244
+ cb(error48);
1245
+ return;
1246
+ }
1309
1247
  if (this._compressed) {
1310
1248
  this._state = INFLATING;
1311
1249
  this.decompress(data, cb);
@@ -1325,7 +1263,7 @@ var require_receiver = __commonJS({
1325
1263
  * @private
1326
1264
  */
1327
1265
  decompress(data, cb) {
1328
- const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
1266
+ const perMessageDeflate = this._extensions[PerMessageDeflate2.extensionName];
1329
1267
  perMessageDeflate.decompress(data, this._fin, (err, buf) => {
1330
1268
  if (err) return cb(err);
1331
1269
  if (buf.length) {
@@ -1363,6 +1301,7 @@ var require_receiver = __commonJS({
1363
1301
  this._totalPayloadLength = 0;
1364
1302
  this._messageLength = 0;
1365
1303
  this._fragmented = 0;
1304
+ this._numFragments = 0;
1366
1305
  this._fragments = [];
1367
1306
  if (this._opcode === 2) {
1368
1307
  let data;
@@ -1507,7 +1446,10 @@ var require_sender = __commonJS({
1507
1446
  "use strict";
1508
1447
  var { Duplex } = require("stream");
1509
1448
  var { randomFillSync } = require("crypto");
1510
- var PerMessageDeflate = require_permessage_deflate();
1449
+ var {
1450
+ types: { isUint8Array }
1451
+ } = require("util");
1452
+ var PerMessageDeflate2 = require_permessage_deflate();
1511
1453
  var { EMPTY_BUFFER, kWebSocket, NOOP } = require_constants();
1512
1454
  var { isBlob, isValidStatusCode } = require_validation();
1513
1455
  var { mask: applyMask, toBuffer } = require_buffer_util();
@@ -1660,8 +1602,10 @@ var require_sender = __commonJS({
1660
1602
  buf.writeUInt16BE(code, 0);
1661
1603
  if (typeof data === "string") {
1662
1604
  buf.write(data, 2);
1663
- } else {
1605
+ } else if (isUint8Array(data)) {
1664
1606
  buf.set(data, 2);
1607
+ } else {
1608
+ throw new TypeError("Second argument must be a string or a Uint8Array");
1665
1609
  }
1666
1610
  }
1667
1611
  const options = {
@@ -1791,7 +1735,7 @@ var require_sender = __commonJS({
1791
1735
  * @public
1792
1736
  */
1793
1737
  send(data, options, cb) {
1794
- const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
1738
+ const perMessageDeflate = this._extensions[PerMessageDeflate2.extensionName];
1795
1739
  let opcode = options.binary ? 2 : 1;
1796
1740
  let rsv1 = options.compress;
1797
1741
  let byteLength;
@@ -1915,7 +1859,7 @@ var require_sender = __commonJS({
1915
1859
  this.sendFrame(_Sender.frame(data, options), cb);
1916
1860
  return;
1917
1861
  }
1918
- const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
1862
+ const perMessageDeflate = this._extensions[PerMessageDeflate2.extensionName];
1919
1863
  this._bufferedBytes += options[kByteLength];
1920
1864
  this._state = DEFLATING;
1921
1865
  perMessageDeflate.compress(data, options.fin, (_, buf) => {
@@ -1958,7 +1902,7 @@ var require_sender = __commonJS({
1958
1902
  /**
1959
1903
  * Sends a frame.
1960
1904
  *
1961
- * @param {Buffer[]} list The frame to send
1905
+ * @param {(Buffer | String)[]} list The frame to send
1962
1906
  * @param {Function} [cb] Callback
1963
1907
  * @private
1964
1908
  */
@@ -2353,11 +2297,11 @@ var require_extension = __commonJS({
2353
2297
  return offers;
2354
2298
  }
2355
2299
  function format(extensions) {
2356
- return Object.keys(extensions).map((extension) => {
2357
- let configurations = extensions[extension];
2300
+ return Object.keys(extensions).map((extension2) => {
2301
+ let configurations = extensions[extension2];
2358
2302
  if (!Array.isArray(configurations)) configurations = [configurations];
2359
2303
  return configurations.map((params) => {
2360
- return [extension].concat(
2304
+ return [extension2].concat(
2361
2305
  Object.keys(params).map((k) => {
2362
2306
  let values = params[k];
2363
2307
  if (!Array.isArray(values)) values = [values];
@@ -2380,15 +2324,16 @@ var require_websocket = __commonJS({
2380
2324
  var http2 = require("http");
2381
2325
  var net = require("net");
2382
2326
  var tls = require("tls");
2383
- var { randomBytes, createHash: createHash2 } = require("crypto");
2327
+ var { randomBytes, createHash } = require("crypto");
2384
2328
  var { Duplex, Readable } = require("stream");
2385
2329
  var { URL: URL2 } = require("url");
2386
- var PerMessageDeflate = require_permessage_deflate();
2330
+ var PerMessageDeflate2 = require_permessage_deflate();
2387
2331
  var Receiver2 = require_receiver();
2388
2332
  var Sender2 = require_sender();
2389
2333
  var { isBlob } = require_validation();
2390
2334
  var {
2391
2335
  BINARY_TYPES,
2336
+ CLOSE_TIMEOUT,
2392
2337
  EMPTY_BUFFER,
2393
2338
  GUID,
2394
2339
  kForOnEventAttribute,
@@ -2402,7 +2347,6 @@ var require_websocket = __commonJS({
2402
2347
  } = require_event_target();
2403
2348
  var { format, parse: parse3 } = require_extension();
2404
2349
  var { toBuffer } = require_buffer_util();
2405
- var closeTimeout = 30 * 1e3;
2406
2350
  var kAborted = Symbol("kAborted");
2407
2351
  var protocolVersions = [8, 13];
2408
2352
  var readyStates = ["CONNECTING", "OPEN", "CLOSING", "CLOSED"];
@@ -2448,6 +2392,7 @@ var require_websocket = __commonJS({
2448
2392
  initAsClient(this, address, protocols, options);
2449
2393
  } else {
2450
2394
  this._autoPong = options.autoPong;
2395
+ this._closeTimeout = options.closeTimeout;
2451
2396
  this._isServer = true;
2452
2397
  }
2453
2398
  }
@@ -2541,6 +2486,10 @@ var require_websocket = __commonJS({
2541
2486
  * multiple times in the same tick
2542
2487
  * @param {Function} [options.generateMask] The function used to generate the
2543
2488
  * masking key
2489
+ * @param {Number} [options.maxBufferedChunks=0] The maximum number of
2490
+ * buffered data chunks
2491
+ * @param {Number} [options.maxFragments=0] The maximum number of message
2492
+ * fragments
2544
2493
  * @param {Number} [options.maxPayload=0] The maximum allowed message size
2545
2494
  * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
2546
2495
  * not to skip UTF-8 validation for text and close messages
@@ -2552,6 +2501,8 @@ var require_websocket = __commonJS({
2552
2501
  binaryType: this.binaryType,
2553
2502
  extensions: this._extensions,
2554
2503
  isServer: this._isServer,
2504
+ maxBufferedChunks: options.maxBufferedChunks,
2505
+ maxFragments: options.maxFragments,
2555
2506
  maxPayload: options.maxPayload,
2556
2507
  skipUTF8Validation: options.skipUTF8Validation
2557
2508
  });
@@ -2590,8 +2541,8 @@ var require_websocket = __commonJS({
2590
2541
  this.emit("close", this._closeCode, this._closeMessage);
2591
2542
  return;
2592
2543
  }
2593
- if (this._extensions[PerMessageDeflate.extensionName]) {
2594
- this._extensions[PerMessageDeflate.extensionName].cleanup();
2544
+ if (this._extensions[PerMessageDeflate2.extensionName]) {
2545
+ this._extensions[PerMessageDeflate2.extensionName].cleanup();
2595
2546
  }
2596
2547
  this._receiver.removeAllListeners();
2597
2548
  this._readyState = _WebSocket.CLOSED;
@@ -2753,7 +2704,7 @@ var require_websocket = __commonJS({
2753
2704
  fin: true,
2754
2705
  ...options
2755
2706
  };
2756
- if (!this._extensions[PerMessageDeflate.extensionName]) {
2707
+ if (!this._extensions[PerMessageDeflate2.extensionName]) {
2757
2708
  opts.compress = false;
2758
2709
  }
2759
2710
  this._sender.send(data || EMPTY_BUFFER, opts, cb);
@@ -2849,7 +2800,10 @@ var require_websocket = __commonJS({
2849
2800
  const opts = {
2850
2801
  allowSynchronousEvents: true,
2851
2802
  autoPong: true,
2803
+ closeTimeout: CLOSE_TIMEOUT,
2852
2804
  protocolVersion: protocolVersions[1],
2805
+ maxBufferedChunks: 256 * 1024,
2806
+ maxFragments: 16 * 1024,
2853
2807
  maxPayload: 100 * 1024 * 1024,
2854
2808
  skipUTF8Validation: false,
2855
2809
  perMessageDeflate: true,
@@ -2866,6 +2820,7 @@ var require_websocket = __commonJS({
2866
2820
  port: void 0
2867
2821
  };
2868
2822
  websocket._autoPong = opts.autoPong;
2823
+ websocket._closeTimeout = opts.closeTimeout;
2869
2824
  if (!protocolVersions.includes(opts.protocolVersion)) {
2870
2825
  throw new RangeError(
2871
2826
  `Unsupported protocol version: ${opts.protocolVersion} (supported versions: ${protocolVersions.join(", ")})`
@@ -2877,7 +2832,7 @@ var require_websocket = __commonJS({
2877
2832
  } else {
2878
2833
  try {
2879
2834
  parsedUrl = new URL2(address);
2880
- } catch (e) {
2835
+ } catch {
2881
2836
  throw new SyntaxError(`Invalid URL: ${address}`);
2882
2837
  }
2883
2838
  }
@@ -2891,7 +2846,7 @@ var require_websocket = __commonJS({
2891
2846
  const isIpcUrl = parsedUrl.protocol === "ws+unix:";
2892
2847
  let invalidUrlMessage;
2893
2848
  if (parsedUrl.protocol !== "ws:" && !isSecure && !isIpcUrl) {
2894
- invalidUrlMessage = `The URL's protocol must be one of "ws:", "wss:", "http:", "https", or "ws+unix:"`;
2849
+ invalidUrlMessage = `The URL's protocol must be one of "ws:", "wss:", "http:", "https:", or "ws+unix:"`;
2895
2850
  } else if (isIpcUrl && !parsedUrl.pathname) {
2896
2851
  invalidUrlMessage = "The URL's pathname is empty";
2897
2852
  } else if (parsedUrl.hash) {
@@ -2925,13 +2880,13 @@ var require_websocket = __commonJS({
2925
2880
  opts.path = parsedUrl.pathname + parsedUrl.search;
2926
2881
  opts.timeout = opts.handshakeTimeout;
2927
2882
  if (opts.perMessageDeflate) {
2928
- perMessageDeflate = new PerMessageDeflate(
2929
- opts.perMessageDeflate !== true ? opts.perMessageDeflate : {},
2930
- false,
2931
- opts.maxPayload
2932
- );
2883
+ perMessageDeflate = new PerMessageDeflate2({
2884
+ ...opts.perMessageDeflate,
2885
+ isServer: false,
2886
+ maxPayload: opts.maxPayload
2887
+ });
2933
2888
  opts.headers["Sec-WebSocket-Extensions"] = format({
2934
- [PerMessageDeflate.extensionName]: perMessageDeflate.offer()
2889
+ [PerMessageDeflate2.extensionName]: perMessageDeflate.offer()
2935
2890
  });
2936
2891
  }
2937
2892
  if (protocols.length) {
@@ -3037,7 +2992,7 @@ var require_websocket = __commonJS({
3037
2992
  abortHandshake(websocket, socket, "Invalid Upgrade header");
3038
2993
  return;
3039
2994
  }
3040
- const digest = createHash2("sha1").update(key + GUID).digest("base64");
2995
+ const digest = createHash("sha1").update(key + GUID).digest("base64");
3041
2996
  if (res.headers["sec-websocket-accept"] !== digest) {
3042
2997
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
3043
2998
  return;
@@ -3074,23 +3029,25 @@ var require_websocket = __commonJS({
3074
3029
  return;
3075
3030
  }
3076
3031
  const extensionNames = Object.keys(extensions);
3077
- if (extensionNames.length !== 1 || extensionNames[0] !== PerMessageDeflate.extensionName) {
3032
+ if (extensionNames.length !== 1 || extensionNames[0] !== PerMessageDeflate2.extensionName) {
3078
3033
  const message = "Server indicated an extension that was not requested";
3079
3034
  abortHandshake(websocket, socket, message);
3080
3035
  return;
3081
3036
  }
3082
3037
  try {
3083
- perMessageDeflate.accept(extensions[PerMessageDeflate.extensionName]);
3038
+ perMessageDeflate.accept(extensions[PerMessageDeflate2.extensionName]);
3084
3039
  } catch (err) {
3085
3040
  const message = "Invalid Sec-WebSocket-Extensions header";
3086
3041
  abortHandshake(websocket, socket, message);
3087
3042
  return;
3088
3043
  }
3089
- websocket._extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
3044
+ websocket._extensions[PerMessageDeflate2.extensionName] = perMessageDeflate;
3090
3045
  }
3091
3046
  websocket.setSocket(socket, head, {
3092
3047
  allowSynchronousEvents: opts.allowSynchronousEvents,
3093
3048
  generateMask: opts.generateMask,
3049
+ maxBufferedChunks: opts.maxBufferedChunks,
3050
+ maxFragments: opts.maxFragments,
3094
3051
  maxPayload: opts.maxPayload,
3095
3052
  skipUTF8Validation: opts.skipUTF8Validation
3096
3053
  });
@@ -3208,7 +3165,7 @@ var require_websocket = __commonJS({
3208
3165
  function setCloseTimer(websocket) {
3209
3166
  websocket._closeTimer = setTimeout(
3210
3167
  websocket._socket.destroy.bind(websocket._socket),
3211
- closeTimeout
3168
+ websocket._closeTimeout
3212
3169
  );
3213
3170
  }
3214
3171
  function socketOnClose() {
@@ -3217,8 +3174,8 @@ var require_websocket = __commonJS({
3217
3174
  this.removeListener("data", socketOnData);
3218
3175
  this.removeListener("end", socketOnEnd);
3219
3176
  websocket._readyState = WebSocket2.CLOSING;
3220
- let chunk;
3221
- if (!this._readableState.endEmitted && !websocket._closeFrameReceived && !websocket._receiver._writableState.errorEmitted && (chunk = websocket._socket.read()) !== null) {
3177
+ if (!this._readableState.endEmitted && !websocket._closeFrameReceived && !websocket._receiver._writableState.errorEmitted && this._readableState.length !== 0) {
3178
+ const chunk = this.read(this._readableState.length);
3222
3179
  websocket._receiver.write(chunk);
3223
3180
  }
3224
3181
  websocket._receiver.end();
@@ -3254,6 +3211,104 @@ var require_websocket = __commonJS({
3254
3211
  }
3255
3212
  });
3256
3213
 
3214
+ // ../../../node_modules/ws/lib/stream.js
3215
+ var require_stream = __commonJS({
3216
+ "../../../node_modules/ws/lib/stream.js"(exports2, module2) {
3217
+ "use strict";
3218
+ var WebSocket2 = require_websocket();
3219
+ var { Duplex } = require("stream");
3220
+ function emitClose(stream) {
3221
+ stream.emit("close");
3222
+ }
3223
+ function duplexOnEnd() {
3224
+ if (!this.destroyed && this._writableState.finished) {
3225
+ this.destroy();
3226
+ }
3227
+ }
3228
+ function duplexOnError(err) {
3229
+ this.removeListener("error", duplexOnError);
3230
+ this.destroy();
3231
+ if (this.listenerCount("error") === 0) {
3232
+ this.emit("error", err);
3233
+ }
3234
+ }
3235
+ function createWebSocketStream2(ws, options) {
3236
+ let terminateOnDestroy = true;
3237
+ const duplex = new Duplex({
3238
+ ...options,
3239
+ autoDestroy: false,
3240
+ emitClose: false,
3241
+ objectMode: false,
3242
+ writableObjectMode: false
3243
+ });
3244
+ ws.on("message", function message(msg, isBinary) {
3245
+ const data = !isBinary && duplex._readableState.objectMode ? msg.toString() : msg;
3246
+ if (!duplex.push(data)) ws.pause();
3247
+ });
3248
+ ws.once("error", function error48(err) {
3249
+ if (duplex.destroyed) return;
3250
+ terminateOnDestroy = false;
3251
+ duplex.destroy(err);
3252
+ });
3253
+ ws.once("close", function close() {
3254
+ if (duplex.destroyed) return;
3255
+ duplex.push(null);
3256
+ });
3257
+ duplex._destroy = function(err, callback) {
3258
+ if (ws.readyState === ws.CLOSED) {
3259
+ callback(err);
3260
+ process.nextTick(emitClose, duplex);
3261
+ return;
3262
+ }
3263
+ let called = false;
3264
+ ws.once("error", function error48(err2) {
3265
+ called = true;
3266
+ callback(err2);
3267
+ });
3268
+ ws.once("close", function close() {
3269
+ if (!called) callback(err);
3270
+ process.nextTick(emitClose, duplex);
3271
+ });
3272
+ if (terminateOnDestroy) ws.terminate();
3273
+ };
3274
+ duplex._final = function(callback) {
3275
+ if (ws.readyState === ws.CONNECTING) {
3276
+ ws.once("open", function open2() {
3277
+ duplex._final(callback);
3278
+ });
3279
+ return;
3280
+ }
3281
+ if (ws._socket === null) return;
3282
+ if (ws._socket._writableState.finished) {
3283
+ callback();
3284
+ if (duplex._readableState.endEmitted) duplex.destroy();
3285
+ } else {
3286
+ ws._socket.once("finish", function finish() {
3287
+ callback();
3288
+ });
3289
+ ws.close();
3290
+ }
3291
+ };
3292
+ duplex._read = function() {
3293
+ if (ws.isPaused) ws.resume();
3294
+ };
3295
+ duplex._write = function(chunk, encoding, callback) {
3296
+ if (ws.readyState === ws.CONNECTING) {
3297
+ ws.once("open", function open2() {
3298
+ duplex._write(chunk, encoding, callback);
3299
+ });
3300
+ return;
3301
+ }
3302
+ ws.send(chunk, callback);
3303
+ };
3304
+ duplex.on("end", duplexOnEnd);
3305
+ duplex.on("error", duplexOnError);
3306
+ return duplex;
3307
+ }
3308
+ module2.exports = createWebSocketStream2;
3309
+ }
3310
+ });
3311
+
3257
3312
  // ../../../node_modules/ws/lib/subprotocol.js
3258
3313
  var require_subprotocol = __commonJS({
3259
3314
  "../../../node_modules/ws/lib/subprotocol.js"(exports2, module2) {
@@ -3306,12 +3361,12 @@ var require_websocket_server = __commonJS({
3306
3361
  var EventEmitter = require("events");
3307
3362
  var http2 = require("http");
3308
3363
  var { Duplex } = require("stream");
3309
- var { createHash: createHash2 } = require("crypto");
3310
- var extension = require_extension();
3311
- var PerMessageDeflate = require_permessage_deflate();
3312
- var subprotocol = require_subprotocol();
3364
+ var { createHash } = require("crypto");
3365
+ var extension2 = require_extension();
3366
+ var PerMessageDeflate2 = require_permessage_deflate();
3367
+ var subprotocol2 = require_subprotocol();
3313
3368
  var WebSocket2 = require_websocket();
3314
- var { GUID, kWebSocket } = require_constants();
3369
+ var { CLOSE_TIMEOUT, GUID, kWebSocket } = require_constants();
3315
3370
  var keyRegex = /^[+/0-9A-Za-z]{22}==$/;
3316
3371
  var RUNNING = 0;
3317
3372
  var CLOSING = 1;
@@ -3330,8 +3385,15 @@ var require_websocket_server = __commonJS({
3330
3385
  * pending connections
3331
3386
  * @param {Boolean} [options.clientTracking=true] Specifies whether or not to
3332
3387
  * track clients
3388
+ * @param {Number} [options.closeTimeout=30000] Duration in milliseconds to
3389
+ * wait for the closing handshake to finish after `websocket.close()` is
3390
+ * called
3333
3391
  * @param {Function} [options.handleProtocols] A hook to handle protocols
3334
3392
  * @param {String} [options.host] The hostname where to bind the server
3393
+ * @param {Number} [options.maxBufferedChunks=262144] The maximum number of
3394
+ * buffered data chunks
3395
+ * @param {Number} [options.maxFragments=16384] The maximum number of message
3396
+ * fragments
3335
3397
  * @param {Number} [options.maxPayload=104857600] The maximum allowed message
3336
3398
  * size
3337
3399
  * @param {Boolean} [options.noServer=false] Enable no server mode
@@ -3353,11 +3415,14 @@ var require_websocket_server = __commonJS({
3353
3415
  options = {
3354
3416
  allowSynchronousEvents: true,
3355
3417
  autoPong: true,
3418
+ maxBufferedChunks: 256 * 1024,
3419
+ maxFragments: 16 * 1024,
3356
3420
  maxPayload: 100 * 1024 * 1024,
3357
3421
  skipUTF8Validation: false,
3358
3422
  perMessageDeflate: false,
3359
3423
  handleProtocols: null,
3360
3424
  clientTracking: true,
3425
+ closeTimeout: CLOSE_TIMEOUT,
3361
3426
  verifyClient: null,
3362
3427
  noServer: false,
3363
3428
  backlog: null,
@@ -3513,9 +3578,11 @@ var require_websocket_server = __commonJS({
3513
3578
  abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
3514
3579
  return;
3515
3580
  }
3516
- if (version2 !== 8 && version2 !== 13) {
3581
+ if (version2 !== 13 && version2 !== 8) {
3517
3582
  const message = "Missing or invalid Sec-WebSocket-Version header";
3518
- abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
3583
+ abortHandshakeOrEmitwsClientError(this, req, socket, 400, message, {
3584
+ "Sec-WebSocket-Version": "13, 8"
3585
+ });
3519
3586
  return;
3520
3587
  }
3521
3588
  if (!this.shouldHandle(req)) {
@@ -3526,7 +3593,7 @@ var require_websocket_server = __commonJS({
3526
3593
  let protocols = /* @__PURE__ */ new Set();
3527
3594
  if (secWebSocketProtocol !== void 0) {
3528
3595
  try {
3529
- protocols = subprotocol.parse(secWebSocketProtocol);
3596
+ protocols = subprotocol2.parse(secWebSocketProtocol);
3530
3597
  } catch (err) {
3531
3598
  const message = "Invalid Sec-WebSocket-Protocol header";
3532
3599
  abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
@@ -3536,16 +3603,16 @@ var require_websocket_server = __commonJS({
3536
3603
  const secWebSocketExtensions = req.headers["sec-websocket-extensions"];
3537
3604
  const extensions = {};
3538
3605
  if (this.options.perMessageDeflate && secWebSocketExtensions !== void 0) {
3539
- const perMessageDeflate = new PerMessageDeflate(
3540
- this.options.perMessageDeflate,
3541
- true,
3542
- this.options.maxPayload
3543
- );
3606
+ const perMessageDeflate = new PerMessageDeflate2({
3607
+ ...this.options.perMessageDeflate,
3608
+ isServer: true,
3609
+ maxPayload: this.options.maxPayload
3610
+ });
3544
3611
  try {
3545
- const offers = extension.parse(secWebSocketExtensions);
3546
- if (offers[PerMessageDeflate.extensionName]) {
3547
- perMessageDeflate.accept(offers[PerMessageDeflate.extensionName]);
3548
- extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
3612
+ const offers = extension2.parse(secWebSocketExtensions);
3613
+ if (offers[PerMessageDeflate2.extensionName]) {
3614
+ perMessageDeflate.accept(offers[PerMessageDeflate2.extensionName]);
3615
+ extensions[PerMessageDeflate2.extensionName] = perMessageDeflate;
3549
3616
  }
3550
3617
  } catch (err) {
3551
3618
  const message = "Invalid or unacceptable Sec-WebSocket-Extensions header";
@@ -3601,7 +3668,7 @@ var require_websocket_server = __commonJS({
3601
3668
  );
3602
3669
  }
3603
3670
  if (this._state > RUNNING) return abortHandshake(socket, 503);
3604
- const digest = createHash2("sha1").update(key + GUID).digest("base64");
3671
+ const digest = createHash("sha1").update(key + GUID).digest("base64");
3605
3672
  const headers = [
3606
3673
  "HTTP/1.1 101 Switching Protocols",
3607
3674
  "Upgrade: websocket",
@@ -3616,10 +3683,10 @@ var require_websocket_server = __commonJS({
3616
3683
  ws._protocol = protocol;
3617
3684
  }
3618
3685
  }
3619
- if (extensions[PerMessageDeflate.extensionName]) {
3620
- const params = extensions[PerMessageDeflate.extensionName].params;
3621
- const value = extension.format({
3622
- [PerMessageDeflate.extensionName]: [params]
3686
+ if (extensions[PerMessageDeflate2.extensionName]) {
3687
+ const params = extensions[PerMessageDeflate2.extensionName].params;
3688
+ const value = extension2.format({
3689
+ [PerMessageDeflate2.extensionName]: [params]
3623
3690
  });
3624
3691
  headers.push(`Sec-WebSocket-Extensions: ${value}`);
3625
3692
  ws._extensions = extensions;
@@ -3629,6 +3696,8 @@ var require_websocket_server = __commonJS({
3629
3696
  socket.removeListener("error", socketOnError);
3630
3697
  ws.setSocket(socket, head, {
3631
3698
  allowSynchronousEvents: this.options.allowSynchronousEvents,
3699
+ maxBufferedChunks: this.options.maxBufferedChunks,
3700
+ maxFragments: this.options.maxFragments,
3632
3701
  maxPayload: this.options.maxPayload,
3633
3702
  skipUTF8Validation: this.options.skipUTF8Validation
3634
3703
  });
@@ -3674,13 +3743,13 @@ var require_websocket_server = __commonJS({
3674
3743
  ` + Object.keys(headers).map((h) => `${h}: ${headers[h]}`).join("\r\n") + "\r\n\r\n" + message
3675
3744
  );
3676
3745
  }
3677
- function abortHandshakeOrEmitwsClientError(server, req, socket, code, message) {
3746
+ function abortHandshakeOrEmitwsClientError(server, req, socket, code, message, headers) {
3678
3747
  if (server.listenerCount("wsClientError")) {
3679
3748
  const err = new Error(message);
3680
3749
  Error.captureStackTrace(err, abortHandshakeOrEmitwsClientError);
3681
3750
  server.emit("wsClientError", err, socket, req);
3682
3751
  } else {
3683
- abortHandshake(socket, code, message);
3752
+ abortHandshake(socket, code, message, headers);
3684
3753
  }
3685
3754
  }
3686
3755
  }
@@ -4709,10 +4778,10 @@ var init_agent_hooks = __esm({
4709
4778
  }
4710
4779
  });
4711
4780
 
4712
- // raw-import:/tmp/slz-publish-wt.2QxCLO/packages/shared/hooks/src/notify.sh
4781
+ // raw-import:/tmp/slz-publish-wt.A7COkR/packages/shared/hooks/src/notify.sh
4713
4782
  var notify_default;
4714
4783
  var init_notify = __esm({
4715
- "raw-import:/tmp/slz-publish-wt.2QxCLO/packages/shared/hooks/src/notify.sh"() {
4784
+ "raw-import:/tmp/slz-publish-wt.A7COkR/packages/shared/hooks/src/notify.sh"() {
4716
4785
  notify_default = `#!/bin/sh
4717
4786
  # SlayZone agent lifecycle hook \u2014 BENIGN DUMB FORWARDER.
4718
4787
  #
@@ -4874,10 +4943,10 @@ exit 0
4874
4943
  }
4875
4944
  });
4876
4945
 
4877
- // raw-import:/tmp/slz-publish-wt.2QxCLO/packages/shared/hooks/src/opencode-plugin.js
4946
+ // raw-import:/tmp/slz-publish-wt.A7COkR/packages/shared/hooks/src/opencode-plugin.js
4878
4947
  var opencode_plugin_default;
4879
4948
  var init_opencode_plugin = __esm({
4880
- "raw-import:/tmp/slz-publish-wt.2QxCLO/packages/shared/hooks/src/opencode-plugin.js"() {
4949
+ "raw-import:/tmp/slz-publish-wt.A7COkR/packages/shared/hooks/src/opencode-plugin.js"() {
4881
4950
  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";
4882
4951
  }
4883
4952
  });
@@ -4937,6 +5006,7 @@ init_dirs();
4937
5006
  var DEFAULT_LOCAL_COMPUTER_NAME = "local-computer";
4938
5007
 
4939
5008
  // ../../shared/platform/src/slayzone-config.ts
5009
+ var IGNORED_COMPUTER_CONFIG_KEYS = ["pinnedCertSha256"];
4940
5010
  function getComputerConfigFilePath() {
4941
5011
  return (0, import_node_path.join)(getSlayzoneHomeDir(), "computer.config.json");
4942
5012
  }
@@ -4953,8 +5023,6 @@ function coerceComputerConfig(raw) {
4953
5023
  const roots = raw.allowedRoots.filter((r) => typeof r === "string" && r.length > 0);
4954
5024
  if (roots.length > 0) cfg.allowedRoots = roots;
4955
5025
  }
4956
- if (typeof raw.pinnedCertSha256 === "string" && raw.pinnedCertSha256.length > 0)
4957
- cfg.pinnedCertSha256 = raw.pinnedCertSha256;
4958
5026
  return cfg;
4959
5027
  }
4960
5028
  function readJsonObject(path8) {
@@ -5005,6 +5073,11 @@ function loadComputerConfigFile(path8 = getComputerConfigFilePath()) {
5005
5073
  const legacy = readJsonObject(legacyConfigFilePathFor(path8));
5006
5074
  return legacy ? coerceComputerConfig(legacy) : {};
5007
5075
  }
5076
+ function ignoredComputerConfigKeys(path8 = getComputerConfigFilePath()) {
5077
+ const raw = readJsonObject(path8) ?? readJsonObject(legacyConfigFilePathFor(path8));
5078
+ if (!raw) return [];
5079
+ return IGNORED_COMPUTER_CONFIG_KEYS.filter((key) => raw[key] !== void 0);
5080
+ }
5008
5081
  function saveComputerConfigFile(cfg, path8 = getComputerConfigFilePath()) {
5009
5082
  writeJsonAtomic(path8, cfg);
5010
5083
  }
@@ -19435,7 +19508,11 @@ var gitCreateWorktreeParamsSchema = external_exports.object({
19435
19508
  });
19436
19509
  var gitRemoveWorktreeParamsSchema = external_exports.object({
19437
19510
  projectPath: external_exports.string().min(1),
19438
- worktreePath: external_exports.string().min(1)
19511
+ worktreePath: external_exports.string().min(1),
19512
+ /** Also force-delete the worktree's branch (`git branch -D`). OFF unless asked: the
19513
+ * hub's archive / delete / purge paths remove only the worktree, and unmerged,
19514
+ * unpushed commits on the branch would otherwise be left dangling. */
19515
+ deleteBranch: external_exports.boolean().optional()
19439
19516
  });
19440
19517
  var gitRemoveWorktreeResultSchema = external_exports.object({
19441
19518
  branchDeleted: external_exports.boolean().optional(),
@@ -19690,23 +19767,6 @@ var procListResultSchema = external_exports.object({
19690
19767
  )
19691
19768
  });
19692
19769
 
19693
- // ../../domains/computer-transport/src/shared/pinning.ts
19694
- var import_node_crypto = require("node:crypto");
19695
- var FINGERPRINT_RE = /^[0-9a-f]{64}$/;
19696
- function normalizeCertSha256Fingerprint(input) {
19697
- const normalized = input.replace(/[:\s]/g, "").toLowerCase();
19698
- if (!FINGERPRINT_RE.test(normalized)) {
19699
- throw new Error(`invalid sha256 certificate fingerprint: '${input}'`);
19700
- }
19701
- return normalized;
19702
- }
19703
- function certSha256FingerprintFromDer(der) {
19704
- return (0, import_node_crypto.createHash)("sha256").update(der).digest("hex");
19705
- }
19706
- function certMatchesFingerprint(expected, presentedDer) {
19707
- return normalizeCertSha256Fingerprint(expected) === certSha256FingerprintFromDer(presentedDer);
19708
- }
19709
-
19710
19770
  // ../../domains/computer-transport/src/shared/events.ts
19711
19771
  var TypedEventEmitter = class {
19712
19772
  listeners = /* @__PURE__ */ new Map();
@@ -19789,9 +19849,7 @@ function slayzoneRootDir() {
19789
19849
  }
19790
19850
  var storedComputerCredentialsSchema = external_exports.object({
19791
19851
  computerId: external_exports.string().min(1),
19792
- apiKey: external_exports.string().min(1),
19793
- /** Pin recorded at enroll time (lowercase hex sha256 of the hub leaf DER). */
19794
- pinnedFingerprint: external_exports.string().optional()
19852
+ apiKey: external_exports.string().min(1)
19795
19853
  });
19796
19854
  var credentialsMapSchema = external_exports.record(external_exports.string(), storedComputerCredentialsSchema);
19797
19855
  function hubHostFromUrl(url2) {
@@ -19869,25 +19927,27 @@ function createFileCredentialStore(hubHost, options = {}) {
19869
19927
  }
19870
19928
 
19871
19929
  // ../../domains/computer-transport/src/client/hub-dialer.ts
19872
- var import_node_crypto2 = require("node:crypto");
19930
+ var import_node_crypto = require("node:crypto");
19873
19931
 
19874
19932
  // ../../../node_modules/ws/wrapper.mjs
19875
19933
  var import_stream = __toESM(require_stream(), 1);
19934
+ var import_extension = __toESM(require_extension(), 1);
19935
+ var import_permessage_deflate = __toESM(require_permessage_deflate(), 1);
19876
19936
  var import_receiver = __toESM(require_receiver(), 1);
19877
19937
  var import_sender = __toESM(require_sender(), 1);
19938
+ var import_subprotocol = __toESM(require_subprotocol(), 1);
19878
19939
  var import_websocket = __toESM(require_websocket(), 1);
19879
19940
  var import_websocket_server = __toESM(require_websocket_server(), 1);
19880
19941
  var wrapper_default = import_websocket.default;
19881
19942
 
19882
19943
  // ../../domains/computer-transport/src/client/hub-dialer.ts
19883
- var PROCESS_EPOCH = (0, import_node_crypto2.randomUUID)();
19944
+ var PROCESS_EPOCH = (0, import_node_crypto.randomUUID)();
19884
19945
  var DEFAULT_HEARTBEAT_INTERVAL_MS = 15e3;
19885
19946
  var DEFAULT_HEARTBEAT_TIMEOUT_MS = 1e4;
19886
19947
  var HubDialer = class {
19887
19948
  events;
19888
19949
  opts;
19889
19950
  log;
19890
- pinnedFingerprint;
19891
19951
  stateValue = "stopped";
19892
19952
  current = null;
19893
19953
  retryTimer = null;
@@ -19911,14 +19971,6 @@ var HubDialer = class {
19911
19971
  if (protocol !== "ws:" && protocol !== "wss:") {
19912
19972
  throw new Error(`hub url must be ws:// or wss://, got '${opts.url}'`);
19913
19973
  }
19914
- if (opts.pinnedCertSha256 !== void 0) {
19915
- if (protocol !== "wss:") {
19916
- throw new Error("pinnedCertSha256 requires a wss:// hub url");
19917
- }
19918
- this.pinnedFingerprint = normalizeCertSha256Fingerprint(opts.pinnedCertSha256);
19919
- } else {
19920
- this.pinnedFingerprint = null;
19921
- }
19922
19974
  }
19923
19975
  get state() {
19924
19976
  return this.stateValue;
@@ -19981,55 +20033,11 @@ var HubDialer = class {
19981
20033
  this.stateValue = state;
19982
20034
  this.events.emit("state-change", { state });
19983
20035
  }
19984
- buildWsOptions() {
19985
- if (!this.pinnedFingerprint) return {};
19986
- const expected = this.pinnedFingerprint;
19987
- const tlsOptions = {
19988
- // The pin *replaces* CA-chain trust — hubs present self-signed certs.
19989
- // NOTE: with rejectUnauthorized:false node records a checkServerIdentity
19990
- // failure in socket.authorizationError but does NOT abort the handshake,
19991
- // so the pin is ENFORCED in verifyPinnedCert() (ws 'upgrade' event) —
19992
- // this override only makes the failure observable early.
19993
- rejectUnauthorized: false,
19994
- checkServerIdentity: (_host, cert) => {
19995
- if (!cert?.raw) return new Error("hub presented no certificate");
19996
- if (!certMatchesFingerprint(expected, cert.raw)) {
19997
- const actual = certSha256FingerprintFromDer(cert.raw);
19998
- return new Error(
19999
- `hub certificate fingerprint mismatch: expected ${expected}, got ${actual}`
20000
- );
20001
- }
20002
- return void 0;
20003
- }
20004
- };
20005
- return tlsOptions;
20006
- }
20007
- /**
20008
- * Hard pin enforcement. Runs on the ws 'upgrade' event — after the TLS
20009
- * handshake and the (secret-free) HTTP upgrade request, but before 'open',
20010
- * so no computer frame (hello/enroll credentials) is ever sent to an unpinned
20011
- * peer. Returns false and tears the socket down on mismatch.
20012
- */
20013
- verifyPinnedCert(ws, tlsSocket) {
20014
- const expected = this.pinnedFingerprint;
20015
- if (!expected) return true;
20016
- const cert = typeof tlsSocket.getPeerCertificate === "function" ? tlsSocket.getPeerCertificate() : null;
20017
- const raw = cert && cert.raw ? cert.raw : null;
20018
- if (raw && certMatchesFingerprint(expected, raw)) return true;
20019
- const actual = raw ? certSha256FingerprintFromDer(raw) : "no certificate";
20020
- const error48 = new Error(
20021
- `hub certificate fingerprint mismatch: expected ${expected}, got ${actual}`
20022
- );
20023
- this.log("computer dialer rejected hub certificate", { error: error48.message });
20024
- this.events.emit("error", { error: error48, fatal: false });
20025
- ws.terminate();
20026
- return false;
20027
- }
20028
20036
  connect() {
20029
20037
  this.retryTimer = null;
20030
20038
  this.triedTokenRefresh = false;
20031
20039
  this.setState("connecting");
20032
- const ws = new wrapper_default(this.opts.url, this.buildWsOptions());
20040
+ const ws = new wrapper_default(this.opts.url);
20033
20041
  const conn = {
20034
20042
  ws,
20035
20043
  rpc: null,
@@ -20050,11 +20058,6 @@ var HubDialer = class {
20050
20058
  }
20051
20059
  });
20052
20060
  this.current = conn;
20053
- if (this.pinnedFingerprint) {
20054
- ws.on("upgrade", (response) => {
20055
- this.verifyPinnedCert(ws, response.socket);
20056
- });
20057
- }
20058
20061
  ws.on("open", () => {
20059
20062
  if (this.current === conn && !this.stopping) void this.authenticate(conn);
20060
20063
  });
@@ -20181,8 +20184,7 @@ var HubDialer = class {
20181
20184
  try {
20182
20185
  await this.opts.credentialStore.save({
20183
20186
  computerId: result.computerId,
20184
- apiKey: result.apiKey,
20185
- ...this.pinnedFingerprint ? { pinnedFingerprint: this.pinnedFingerprint } : {}
20187
+ apiKey: result.apiKey
20186
20188
  });
20187
20189
  } catch (err) {
20188
20190
  const error48 = new Error(
@@ -20297,7 +20299,8 @@ var import_node_path3 = require("node:path");
20297
20299
 
20298
20300
  // ../../shared/platform/src/join-token.ts
20299
20301
  var JOIN_TOKEN_PREFIX = "szjt1";
20300
- function decodeJoinToken(token) {
20302
+ var JOIN_TOKEN_MAX_TTL_MS = 60 * 6e4;
20303
+ function parseJoinTokenBody(token) {
20301
20304
  const dot = token.indexOf(".");
20302
20305
  if (dot === -1 || token.slice(0, dot) !== JOIN_TOKEN_PREFIX) return null;
20303
20306
  const body = token.slice(dot + 1);
@@ -20305,15 +20308,23 @@ function decodeJoinToken(token) {
20305
20308
  try {
20306
20309
  const parsed = JSON.parse(Buffer.from(body, "base64url").toString("utf8"));
20307
20310
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
20308
- const { hubUrl, certFingerprint, secret } = parsed;
20309
- if (typeof hubUrl !== "string" || typeof certFingerprint !== "string" || typeof secret !== "string") {
20310
- return null;
20311
- }
20312
- return { hubUrl, certFingerprint, secret };
20311
+ return parsed;
20313
20312
  } catch {
20314
20313
  return null;
20315
20314
  }
20316
20315
  }
20316
+ function decodeJoinToken(token) {
20317
+ const parsed = parseJoinTokenBody(token);
20318
+ if (!parsed) return null;
20319
+ const { hubUrl, secret } = parsed;
20320
+ if (typeof hubUrl !== "string" || typeof secret !== "string") return null;
20321
+ return { hubUrl, secret };
20322
+ }
20323
+ function joinTokenCarriesCertPin(token) {
20324
+ if (!decodeJoinToken(token)) return false;
20325
+ const pin = parseJoinTokenBody(token)?.certFingerprint;
20326
+ return typeof pin === "string" && pin !== "";
20327
+ }
20317
20328
 
20318
20329
  // src/config.ts
20319
20330
  var computerConfigSchema = external_exports.object({
@@ -20344,9 +20355,7 @@ var computerConfigSchema = external_exports.object({
20344
20355
  /** Filesystem roots this computer may operate under (fs./git. commands). */
20345
20356
  allowedRoots: external_exports.array(external_exports.string().min(1)),
20346
20357
  /** Capability tags advertised at enrollment. */
20347
- capabilities: external_exports.array(external_exports.string().min(1)),
20348
- /** sha256 pin of the hub TLS leaf cert (lowercase hex; colons tolerated). */
20349
- pinnedCertSha256: external_exports.string().min(1).optional()
20358
+ capabilities: external_exports.array(external_exports.string().min(1))
20350
20359
  });
20351
20360
  var ENV_VARS = {
20352
20361
  // Authority ONLY — `host[:port]`, no scheme, no path. The dial scheme
@@ -20358,8 +20367,8 @@ var ENV_VARS = {
20358
20367
  // into terminals, so outside the collision).
20359
20368
  hubAddress: "SLAYZONE_HUB_ADDRESS",
20360
20369
  // HUB_-prefixed because the token is hub-scoped BY VALUE, not by reader: the hub
20361
- // mints it and it embeds the hub's `wss://…/computers` url, the hub's TLS cert
20362
- // fingerprint, and a secret checked against the hub's `join_tokens` row.
20370
+ // mints it and it embeds the hub's `wss://…/computers` url and a secret checked
20371
+ // against the hub's `join_tokens` row.
20363
20372
  // `mintJoinToken` binds it to NO computer (computer_id is NULL until redemption), so
20364
20373
  // any computer can redeem any unused token — the old `SLAYZONE_RUNNER_JOIN_TOKEN`
20365
20374
  // named its CONSUMER, which is what CLAUDE.md rule 2 forbids. `_TOKEN` suffix +
@@ -20377,9 +20386,8 @@ var ENV_VARS = {
20377
20386
  // <ROOT>/computer.config.json `allowedRoots` (+ the ROOT default in bin.ts). The
20378
20387
  // computer NAME likewise has no env channel — it derives from SUPERVISED (→
20379
20388
  // DEFAULT_LOCAL_COMPUTER_NAME) or computer.config.json `computerName`, else the hostname.
20380
- // The cert pin has NO env channel either: it arrives via the join token (auto
20381
- // path) or <ROOT>/computer.config.json `pinnedCertSha256` (explicit standalone path).
20382
- // Never a bare env var — a fingerprint is never fished out of ambient env.
20389
+ // There is no certificate pin on any channel: a wss:// hub's front end is verified
20390
+ // the normal way (see ignoredCertPinNotices).
20383
20391
  };
20384
20392
  var DEFAULT_CAPABILITIES = ["pty", "git", "fs", "proc"];
20385
20393
  function realpathBoundary(target) {
@@ -20428,7 +20436,6 @@ function fromSharedConfig(shared) {
20428
20436
  if (shared.joinToken !== void 0) out.joinToken = shared.joinToken;
20429
20437
  if (shared.computerName !== void 0) out.name = shared.computerName;
20430
20438
  if (shared.allowedRoots !== void 0) out.allowedRoots = shared.allowedRoots;
20431
- if (shared.pinnedCertSha256 !== void 0) out.pinnedCertSha256 = shared.pinnedCertSha256;
20432
20439
  return out;
20433
20440
  }
20434
20441
  function loadComputerConfig(env = process.env, shared = env === process.env && env.SLAYZONE_SUPERVISED !== "1" ? loadComputerConfigFile() : {}) {
@@ -20449,15 +20456,7 @@ function loadComputerConfig(env = process.env, shared = env === process.env && e
20449
20456
  // fallback in bin.ts. No env channel either way.
20450
20457
  allowedRoots: env.SLAYZONE_SUPERVISED === "1" ? [(0, import_node_os2.homedir)()] : [],
20451
20458
  capabilities: [...DEFAULT_CAPABILITIES],
20452
- ...fromToken ? {
20453
- hubUrl: fromToken.hubUrl,
20454
- // A token minted by the current hub carries an EMPTY fingerprint: the hub
20455
- // no longer presents a certificate of its own, so there is nothing to pin
20456
- // and the front end's certificate is verified the normal way. An empty
20457
- // string must read as "no pin", never reach the schema (min(1)) and fail
20458
- // the whole config. A token from an older hub still carries a real pin.
20459
- ...fromToken.certFingerprint !== "" ? { pinnedCertSha256: fromToken.certFingerprint } : {}
20460
- } : {},
20459
+ ...fromToken ? { hubUrl: fromToken.hubUrl } : {},
20461
20460
  // <ROOT>/computer.config.json — base under env (spread after). The single config file.
20462
20461
  ...fromShared,
20463
20462
  ...hubUrlFromEnv !== void 0 ? { hubUrl: hubUrlFromEnv } : {},
@@ -20470,12 +20469,6 @@ function loadComputerConfig(env = process.env, shared = env === process.env && e
20470
20469
  `invalid computer configuration (${issues}). Set ${ENV_VARS.hubAddress} (and ${ENV_VARS.joinToken} for first contact) or a <ROOT>/computer.config.json.`
20471
20470
  );
20472
20471
  }
20473
- const explicitPin = fromShared.pinnedCertSha256;
20474
- if (explicitPin !== void 0 && urlProtocol(result.data.hubUrl) === "ws:") {
20475
- throw new Error(
20476
- `config pinnedCertSha256 requires a wss:// hub url; got '${result.data.hubUrl}'. Pinning has no effect without TLS \u2014 use a wss:// url or drop the pin.`
20477
- );
20478
- }
20479
20472
  for (const url2 of effectiveHubUrls(result.data)) {
20480
20473
  if (urlProtocol(url2) === "ws:" && !isLoopbackComputerUrl(url2)) {
20481
20474
  throw new Error(
@@ -20485,6 +20478,21 @@ function loadComputerConfig(env = process.env, shared = env === process.env && e
20485
20478
  }
20486
20479
  return result.data;
20487
20480
  }
20481
+ function ignoredCertPinNotices(env = process.env, shared = env === process.env && env.SLAYZONE_SUPERVISED !== "1" ? loadComputerConfigFile() : {}, ignoredFileKeys = env === process.env && env.SLAYZONE_SUPERVISED !== "1" ? ignoredComputerConfigKeys() : []) {
20482
+ const notices = [];
20483
+ const joinToken = env[ENV_VARS.joinToken] ?? shared.joinToken;
20484
+ if (joinToken && joinTokenCarriesCertPin(joinToken)) {
20485
+ notices.push(
20486
+ "ignoring the certificate pin in the join token (minted by an older hub): the hub's certificate is verified against the trusted CAs instead"
20487
+ );
20488
+ }
20489
+ if (ignoredFileKeys.includes("pinnedCertSha256")) {
20490
+ notices.push(
20491
+ "ignoring 'pinnedCertSha256' in computer.config.json: certificate pinning was removed and the hub's certificate is verified against the trusted CAs instead \u2014 delete the key"
20492
+ );
20493
+ }
20494
+ return notices;
20495
+ }
20488
20496
  function urlProtocol(url2) {
20489
20497
  try {
20490
20498
  return new URL(url2).protocol;
@@ -20498,7 +20506,7 @@ function effectiveHubUrls(config2) {
20498
20506
  }
20499
20507
 
20500
20508
  // src/host-id.ts
20501
- var import_node_crypto3 = require("node:crypto");
20509
+ var import_node_crypto2 = require("node:crypto");
20502
20510
  var import_promises3 = require("node:fs/promises");
20503
20511
  var import_node_os3 = require("node:os");
20504
20512
  var import_node_path4 = require("node:path");
@@ -20508,6 +20516,9 @@ function slayzoneRootDir2() {
20508
20516
  return (0, import_node_path4.join)(home, ".slayzone");
20509
20517
  }
20510
20518
  function hostIdFilePath(baseDir) {
20519
+ return (0, import_node_path4.join)(baseDir ?? slayzoneRootDir2(), "computer.host.json");
20520
+ }
20521
+ function legacyHostIdFilePath(baseDir) {
20511
20522
  return (0, import_node_path4.join)(baseDir ?? slayzoneRootDir2(), "computer.host-id.json");
20512
20523
  }
20513
20524
  function sharedHostIdPath() {
@@ -20571,13 +20582,20 @@ async function reconcileHostId(options = {}) {
20571
20582
  const sharedPath = options.sharedPath === void 0 ? sharedHostIdPath() : options.sharedPath;
20572
20583
  const log = options.log ?? (() => void 0);
20573
20584
  let durable = await readDurable(filePath);
20585
+ if (!durable) {
20586
+ const legacy = await readDurable(legacyHostIdFilePath(options.baseDir));
20587
+ if (legacy) {
20588
+ durable = legacy;
20589
+ await writeDurable(filePath, durable);
20590
+ }
20591
+ }
20574
20592
  const shared = sharedPath ? await readShared(sharedPath) : null;
20575
20593
  if (shared && durable && shared !== durable) {
20576
20594
  log("host id adopted from the shared file", { from: durable, to: shared });
20577
20595
  durable = shared;
20578
20596
  await writeDurable(filePath, durable);
20579
20597
  } else if (!durable) {
20580
- durable = shared ?? (0, import_node_crypto3.randomUUID)();
20598
+ durable = shared ?? (0, import_node_crypto2.randomUUID)();
20581
20599
  await writeDurable(filePath, durable);
20582
20600
  }
20583
20601
  if (sharedPath && shared !== durable) {
@@ -20970,7 +20988,7 @@ function subscribeFileWatcher(rootPath, listener) {
20970
20988
  const watcher = fs2.watch(root, { recursive: true }, (_eventType, filename) => {
20971
20989
  if (!filename) return;
20972
20990
  const relPath = filename.replace(/\\/g, "/");
20973
- if (isIgnored(root, relPath, false)) return;
20991
+ if (ignoredSafely(root, relPath)) return;
20974
20992
  if (path3.basename(relPath) === ".gitignore") {
20975
20993
  invalidateIgnoreCache(root);
20976
20994
  }
@@ -20991,6 +21009,9 @@ function subscribeFileWatcher(rootPath, listener) {
20991
21009
  }, 100)
20992
21010
  );
20993
21011
  });
21012
+ watcher.on("error", (err) => {
21013
+ console.warn(`[file-editor] watcher on ${root}: ${err.message}`);
21014
+ });
20994
21015
  watchers.set(root, { watcher, listeners, debounceMap });
20995
21016
  return () => unsubscribe(root, listener);
20996
21017
  } catch {
@@ -20998,6 +21019,13 @@ function subscribeFileWatcher(rootPath, listener) {
20998
21019
  };
20999
21020
  }
21000
21021
  }
21022
+ function ignoredSafely(root, relPath) {
21023
+ try {
21024
+ return isIgnored(root, relPath, false);
21025
+ } catch {
21026
+ return false;
21027
+ }
21028
+ }
21001
21029
  function unsubscribe(root, listener) {
21002
21030
  const entry = watchers.get(root);
21003
21031
  if (!entry) return;
@@ -21140,6 +21168,75 @@ async function discoverRepos(rootPath, maxDepth = DEFAULT_REPO_SCAN_DEPTH) {
21140
21168
  return entries;
21141
21169
  }
21142
21170
 
21171
+ // ../../shared/platform/src/hub-switches.ts
21172
+ var HUB_SWITCH_ENV = {
21173
+ requireLogin: "SLAYZONE_HUB_REQUIRE_LOGIN",
21174
+ enablePassword: "SLAYZONE_HUB_ENABLE_PASSWORD",
21175
+ enableSso: "SLAYZONE_HUB_ENABLE_SSO",
21176
+ enableProcesses: "SLAYZONE_HUB_ENABLE_PROCESSES",
21177
+ enableIntegrations: "SLAYZONE_HUB_ENABLE_INTEGRATIONS"
21178
+ };
21179
+ var HUB_SSO_ENV = {
21180
+ tenantId: "SLAYZONE_HUB_SSO_TENANT_ID",
21181
+ clientId: "SLAYZONE_HUB_SSO_CLIENT_ID",
21182
+ /** PEM certificate registered on the Entra app (its SHA-256 thumbprint goes in each assertion). Env only. */
21183
+ certPath: "SLAYZONE_HUB_SSO_CERT_PATH",
21184
+ /** PEM private key of that certificate (RSA, unencrypted). Env only. */
21185
+ keyPath: "SLAYZONE_HUB_SSO_KEY_PATH",
21186
+ /** `0|1`, default `1`: refuse a sign-in whose ID token's `amr` does not contain `mfa`. */
21187
+ requireMfa: "SLAYZONE_HUB_SSO_REQUIRE_MFA"
21188
+ };
21189
+ var RETIRED_HUB_SSO_CLIENT_SECRET_ENV = "SLAYZONE_HUB_SSO_CLIENT_SECRET";
21190
+ var HUB_ADDRESS_ENV = {
21191
+ /** Where the hub LISTENS. Renamed from `SLAYZONE_HUB_ADDRESS`, which now only
21192
+ * means "the hub to connect to" on a computer / `slay`. */
21193
+ listen: "SLAYZONE_HUB_LISTEN_ADDRESS",
21194
+ /** How OTHER machines reach the hub (`host[:port]`, no scheme). */
21195
+ public: "SLAYZONE_HUB_PUBLIC_ADDRESS",
21196
+ /** Signing key of hub-auth. */
21197
+ authSecret: "SLAYZONE_HUB_AUTH_SECRET"
21198
+ };
21199
+ var LEGACY_HUB_LISTEN_ENV = "SLAYZONE_HUB_ADDRESS";
21200
+ var HUB_SESSION_RETENTION_DAYS_ENV = "SLAYZONE_HUB_SESSION_RETENTION_DAYS";
21201
+ var HUB_ALLOWED_WEB_ORIGINS_ENV = "SLAYZONE_HUB_ALLOWED_WEB_ORIGINS";
21202
+ var HUB_TRUSTED_PROXIES_ENV = "SLAYZONE_HUB_TRUSTED_PROXIES";
21203
+ var HUB_CONVEX_ADDRESS_ENV = "SLAYZONE_HUB_CONVEX_ADDRESS";
21204
+ var HUB_PROCESS_ENV = [
21205
+ ...Object.values(HUB_SWITCH_ENV),
21206
+ ...Object.values(HUB_SSO_ENV),
21207
+ RETIRED_HUB_SSO_CLIENT_SECRET_ENV,
21208
+ ...Object.values(HUB_ADDRESS_ENV),
21209
+ LEGACY_HUB_LISTEN_ENV,
21210
+ HUB_ALLOWED_WEB_ORIGINS_ENV,
21211
+ HUB_TRUSTED_PROXIES_ENV,
21212
+ HUB_SESSION_RETENTION_DAYS_ENV,
21213
+ HUB_CONVEX_ADDRESS_ENV
21214
+ ];
21215
+
21216
+ // ../../shared/platform/src/security-log.ts
21217
+ var import_node_async_hooks = require("node:async_hooks");
21218
+ var LINE_SEPARATOR = String.fromCharCode(8232);
21219
+ var PARAGRAPH_SEPARATOR = String.fromCharCode(8233);
21220
+ var contextStore = new import_node_async_hooks.AsyncLocalStorage();
21221
+
21222
+ // ../../shared/platform/src/hub-switch-refusal.ts
21223
+ var MESSAGES = {
21224
+ HUB_PROCESSES_DISABLED: `This hub does not run processes, automation commands, terminals or agents itself (${HUB_SWITCH_ENV.enableProcesses}=0).`,
21225
+ HUB_INTEGRATIONS_DISABLED: `Integrations are switched off on this hub (${HUB_SWITCH_ENV.enableIntegrations}=0).`,
21226
+ 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).`,
21227
+ HUB_PASSWORD_DISABLED: `Password sign-in is switched off on this hub (${HUB_SWITCH_ENV.enablePassword}=0).`,
21228
+ HUB_CONVEX_NOT_CONFIGURED: `GitHub sign-in is off on this hub: no Convex deployment is configured (${HUB_CONVEX_ADDRESS_ENV}).`,
21229
+ HUB_CONVEX_MISMATCH: `This hub starts GitHub sign-in only against its own Convex deployment (${HUB_CONVEX_ADDRESS_ENV}); the request named another.`
21230
+ };
21231
+ var VARIABLES = {
21232
+ HUB_PROCESSES_DISABLED: HUB_SWITCH_ENV.enableProcesses,
21233
+ HUB_INTEGRATIONS_DISABLED: HUB_SWITCH_ENV.enableIntegrations,
21234
+ NO_COMPUTER_CONNECTED: HUB_SWITCH_ENV.enableProcesses,
21235
+ HUB_PASSWORD_DISABLED: HUB_SWITCH_ENV.enablePassword,
21236
+ HUB_CONVEX_NOT_CONFIGURED: HUB_CONVEX_ADDRESS_ENV,
21237
+ HUB_CONVEX_MISMATCH: HUB_CONVEX_ADDRESS_ENV
21238
+ };
21239
+
21143
21240
  // src/handlers/fs.ts
21144
21241
  var BROWSE_ALWAYS_HIDDEN = /* @__PURE__ */ new Set([".git", ".DS_Store", "node_modules"]);
21145
21242
  function createFsHandlers(ctx) {
@@ -21381,14 +21478,16 @@ var ENV_MANIFEST = {
21381
21478
  // (name by what the value IS). `_SECRET` = a key you HOLD and sign with, vs
21382
21479
  // `_TOKEN` = a bearer you PRESENT (SLAYZONE_HUB_TOKEN above).
21383
21480
  SLAYZONE_HUB_AUTH_SECRET: "secret",
21384
- // Hub-scoped by VALUE: the hub mints it, and it embeds the hub's url, the hub's
21385
- // TLS cert fingerprint, and a secret verified against the hub's `join_tokens`
21481
+ // Hub-scoped by VALUE: the hub mints it, and it embeds the hub's url and a
21482
+ // secret verified against the hub's `join_tokens`
21386
21483
  // row. `mintJoinToken` binds it to NO computer (computer_id stays NULL until
21387
21484
  // redemption), so any computer can redeem any unused token — there is no computer in
21388
21485
  // the value to name. Rule 2 (name by what the value IS, not who reads it).
21389
21486
  SLAYZONE_HUB_JOIN_TOKEN: "secret",
21390
- // Entra OIDC client secret (SSO). Env only — never read from hub.config.json,
21391
- // never logged, never in a terminal.
21487
+ // Retired before it was ever read: Entra sign-in authenticates with a certificate
21488
+ // (SLAYZONE_HUB_SSO_CERT_PATH / _KEY_PATH below), and boot refuses a hub that still
21489
+ // sets this (B7). Still classified `secret` so a leftover value never reaches a
21490
+ // terminal or a log.
21392
21491
  SLAYZONE_HUB_SSO_CLIENT_SECRET: "secret",
21393
21492
  // Per-boot bearer the desktop's `/cap` listener requires on every connection
21394
21493
  // (desktop-bridge-server.ts `verifyClient`). Loopback is NOT trusted there —
@@ -21411,6 +21510,9 @@ var ENV_MANIFEST = {
21411
21510
  // hub-only; where the hub listens
21412
21511
  SLAYZONE_HUB_PUBLIC_ADDRESS: "infra",
21413
21512
  // hub-only; how other machines reach it, written into join tokens
21513
+ // Hub-only; the front ends (IPs/CIDRs, comma-separated) whose X-Forwarded-For names
21514
+ // the client (client-address.ts). Not `_ADDRESS`: these are networks, not an authority.
21515
+ SLAYZONE_HUB_TRUSTED_PROXIES: "infra",
21414
21516
  // Per-hub switches (0|1) replacing SLAYZONE_MODE. Infra: they describe THIS hub
21415
21517
  // process, so a task terminal must not inherit them (a hub started from one would
21416
21518
  // otherwise pick up its parent's switches by accident).
@@ -21421,10 +21523,23 @@ var ENV_MANIFEST = {
21421
21523
  SLAYZONE_HUB_ENABLE_INTEGRATIONS: "infra",
21422
21524
  SLAYZONE_HUB_SSO_TENANT_ID: "infra",
21423
21525
  SLAYZONE_HUB_SSO_CLIENT_ID: "infra",
21526
+ // Paths to the Entra client certificate and its private key, and the MFA switch.
21527
+ // Infra, not secret: a path is not the key (the file's own permissions protect it),
21528
+ // but a task terminal must still never inherit a hub's SSO wiring.
21529
+ SLAYZONE_HUB_SSO_CERT_PATH: "infra",
21530
+ SLAYZONE_HUB_SSO_KEY_PATH: "infra",
21531
+ SLAYZONE_HUB_SSO_REQUIRE_MFA: "infra",
21424
21532
  // Operator-facing hub name (`slay hub ls`/`stop` address a hub by it). Infra,
21425
21533
  // not global: it identifies THIS hub process, so inheriting it into a task
21426
21534
  // terminal — which may target a different hub entirely — would mislabel.
21427
21535
  SLAYZONE_HUB_NAME: "infra",
21536
+ // Days THIS hub keeps session content (T-23, hub session-retention.ts); 0 = off, the
21537
+ // default. Infra like the switches: a hub started from a task terminal must not
21538
+ // inherit its parent's period and start deleting.
21539
+ SLAYZONE_HUB_SESSION_RETENTION_DAYS: "infra",
21540
+ // The Convex deployment (host[:port]) the hub starts GitHub sign-in against — never
21541
+ // a client-named one (T-99). Hub process config, set by the desktop app for its hub.
21542
+ SLAYZONE_HUB_CONVEX_ADDRESS: "infra",
21428
21543
  SLAYZONE_DESKTOP_BRIDGE_ADDRESS: "infra",
21429
21544
  // sidecar→desktop capability bridge + REST proxy
21430
21545
  SLAYZONE_SUPERVISED: "infra",
@@ -21461,7 +21576,8 @@ var ENV_MANIFEST = {
21461
21576
  // --- identity: per-task/agent; overlay re-adds the correct value per spawn ---
21462
21577
  SLAYZONE_TASK_ID: "identity",
21463
21578
  SLAYZONE_PROJECT_ID: "identity",
21464
- SLAYZONE_SESSION_ID: "identity",
21579
+ // `SLAYZONE_SESSION_ID` (a warm agent's pre-task identity) is RETIRED and
21580
+ // deliberately unlisted: warm agents boot with SLAYZONE_TASK_ID now.
21465
21581
  SLAYZONE_AGENT_ID: "identity",
21466
21582
  SLAYZONE_AGENT_HOOK_URL: "identity",
21467
21583
  // Pairs with _HOOK_URL (where to post / who is posting). The pre-v4 name
@@ -21608,6 +21724,7 @@ function createGitHandlers(ctx) {
21608
21724
  throw err;
21609
21725
  }
21610
21726
  }
21727
+ if (params.deleteBranch !== true) return {};
21611
21728
  if (removedBranch === null) return {};
21612
21729
  const branch = removedBranch.replace(/^refs\/heads\//, "").trim();
21613
21730
  if (!branch) return {};
@@ -21730,39 +21847,6 @@ init_dirs();
21730
21847
  // ../../shared/platform/src/paths.ts
21731
21848
  init_dirs();
21732
21849
 
21733
- // ../../shared/platform/src/hub-switches.ts
21734
- var HUB_SWITCH_ENV = {
21735
- requireLogin: "SLAYZONE_HUB_REQUIRE_LOGIN",
21736
- enablePassword: "SLAYZONE_HUB_ENABLE_PASSWORD",
21737
- enableSso: "SLAYZONE_HUB_ENABLE_SSO",
21738
- enableProcesses: "SLAYZONE_HUB_ENABLE_PROCESSES",
21739
- enableIntegrations: "SLAYZONE_HUB_ENABLE_INTEGRATIONS"
21740
- };
21741
- var HUB_SSO_ENV = {
21742
- tenantId: "SLAYZONE_HUB_SSO_TENANT_ID",
21743
- clientId: "SLAYZONE_HUB_SSO_CLIENT_ID",
21744
- /** Secret — env only, never read from `hub.config.json`, never logged. */
21745
- clientSecret: "SLAYZONE_HUB_SSO_CLIENT_SECRET"
21746
- };
21747
- var HUB_ADDRESS_ENV = {
21748
- /** Where the hub LISTENS. Renamed from `SLAYZONE_HUB_ADDRESS`, which now only
21749
- * means "the hub to connect to" on a computer / `slay`. */
21750
- listen: "SLAYZONE_HUB_LISTEN_ADDRESS",
21751
- /** How OTHER machines reach the hub (`host[:port]`, no scheme). */
21752
- public: "SLAYZONE_HUB_PUBLIC_ADDRESS",
21753
- /** Signing key of hub-auth. */
21754
- authSecret: "SLAYZONE_HUB_AUTH_SECRET"
21755
- };
21756
- var LEGACY_HUB_LISTEN_ENV = "SLAYZONE_HUB_ADDRESS";
21757
- var HUB_ALLOWED_WEB_ORIGINS_ENV = "SLAYZONE_HUB_ALLOWED_WEB_ORIGINS";
21758
- var HUB_PROCESS_ENV = [
21759
- ...Object.values(HUB_SWITCH_ENV),
21760
- ...Object.values(HUB_SSO_ENV),
21761
- ...Object.values(HUB_ADDRESS_ENV),
21762
- LEGACY_HUB_LISTEN_ENV,
21763
- HUB_ALLOWED_WEB_ORIGINS_ENV
21764
- ];
21765
-
21766
21850
  // ../../shared/platform/src/index.ts
21767
21851
  init_fs_utils();
21768
21852
 
@@ -21773,6 +21857,16 @@ init_dirs();
21773
21857
  var import_child_process = require("child_process");
21774
21858
  var import_util3 = require("util");
21775
21859
  var execFileAsync = (0, import_util3.promisify)(import_child_process.execFile);
21860
+ var WINDOWS_SHIM = [
21861
+ "@echo off",
21862
+ `for /f "tokens=1 delims=." %%v in ('node -e "process.stdout.write(process.versions.node)"') do set NODE_MAJOR=%%v`,
21863
+ "if %NODE_MAJOR% LEQ 22 (",
21864
+ ' node --experimental-sqlite --no-warnings "%~dp0slay.js" %*',
21865
+ ") else (",
21866
+ ' node --no-warnings "%~dp0slay.js" %*',
21867
+ ")",
21868
+ ""
21869
+ ].join("\r\n");
21776
21870
 
21777
21871
  // ../../shared/platform/src/shell.ts
21778
21872
  var import_node_fs6 = __toESM(require("node:fs"), 1);
@@ -21814,15 +21908,6 @@ function buildShellInvocation(command) {
21814
21908
  var SLZ_FILE_HOST = "app";
21815
21909
  var SLZ_FILE_PREFIX = `slz-file://${SLZ_FILE_HOST}`;
21816
21910
 
21817
- // ../../shared/platform/src/html-theme-css.ts
21818
- var FORCED_HTML_THEME_CSS = {
21819
- dark: [
21820
- "html{filter:invert(90%) hue-rotate(180deg)!important}",
21821
- "img,video,canvas,svg,iframe{filter:invert(90%) hue-rotate(180deg)!important}"
21822
- ].join(""),
21823
- light: ":root{color-scheme:light!important}"
21824
- };
21825
-
21826
21911
  // ../../shared/platform/src/ipc-dedup.ts
21827
21912
  var UNCHANGED = Object.freeze({ __ipc_unchanged__: true });
21828
21913
 
@@ -22484,29 +22569,12 @@ function startComputer(config2, deps = {}) {
22484
22569
  let handle;
22485
22570
  const connectHub = (hubUrl, isPrimary) => {
22486
22571
  const credentialStore = (isPrimary ? deps.credentialStore : void 0) ?? createFileCredentialStore(hubHostFromUrl(hubUrl));
22487
- const isSecureUrl = (() => {
22488
- try {
22489
- return new URL(hubUrl).protocol === "wss:";
22490
- } catch {
22491
- return false;
22492
- }
22493
- })();
22494
- const pinnedCertSha256 = config2.pinnedCertSha256 && isSecureUrl ? config2.pinnedCertSha256 : void 0;
22495
- if (config2.pinnedCertSha256 && !isSecureUrl) {
22496
- log(
22497
- "ignoring token-decoded cert fingerprint on a non-wss hub url (pinning requires wss://)",
22498
- {
22499
- hubUrl
22500
- }
22501
- );
22502
- }
22503
22572
  let dispatch;
22504
22573
  const dialer = new HubDialer({
22505
22574
  url: hubUrl,
22506
22575
  identity,
22507
22576
  credentialStore,
22508
22577
  ...config2.joinToken && isPrimary ? { joinToken: config2.joinToken } : {},
22509
- ...pinnedCertSha256 ? { pinnedCertSha256 } : {},
22510
22578
  onHubRequest: (method, params) => dispatch.handle(method, params),
22511
22579
  log
22512
22580
  });
@@ -22671,6 +22739,7 @@ async function main() {
22671
22739
  process.stdout.write(`[computer] ${message}${suffix}
22672
22740
  `);
22673
22741
  };
22742
+ for (const notice of ignoredCertPinNotices()) log(notice);
22674
22743
  if (process.env.SLAYZONE_SUPERVISED !== "1") {
22675
22744
  const { installAgentHooks: installAgentHooks2 } = await Promise.resolve().then(() => (init_agent_hooks2(), agent_hooks_exports));
22676
22745
  await installAgentHooks2(log);