nextclaw 0.35.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.
Files changed (61) hide show
  1. package/dist/cli/app/index.js +4 -4859
  2. package/dist/cli/app/index.js.map +1 -1
  3. package/dist/cli/launcher/index.js +1 -1
  4. package/dist/nextclaw-cli-app-BITGWcdZ.js +4943 -0
  5. package/dist/nextclaw-cli-app-BITGWcdZ.js.map +1 -0
  6. package/dist/{nextclaw-distribution.utils-CfUuTwjt.js → nextclaw-distribution.utils-BneKclXo.js} +3 -1
  7. package/dist/nextclaw-distribution.utils-BneKclXo.js.map +1 -0
  8. package/package.json +14 -14
  9. package/resources/USAGE.md +63 -1
  10. package/ui-dist/assets/api-CVjdSvJk.js +13 -0
  11. package/ui-dist/assets/{appearance-settings-page-CfwDf9ag.js → appearance-settings-page-BOorfaUx.js} +1 -1
  12. package/ui-dist/assets/{channels-list-page-Dyn83ZlS.js → channels-list-page-DuZJvYB6.js} +1 -1
  13. package/ui-dist/assets/{chat-page-CL0IMvgF.js → chat-page-8PTMgI0t.js} +1 -1
  14. package/ui-dist/assets/{config-split-page-D4D4HFhq.js → config-split-page-UuhsVcQD.js} +1 -1
  15. package/ui-dist/assets/{confirm-dialog-Cv6-9Nnj.js → confirm-dialog-Bnosa8UF.js} +1 -1
  16. package/ui-dist/assets/{desktop-update-config-Bckgulvm.js → desktop-update-config-CM4lsX4R.js} +1 -1
  17. package/ui-dist/assets/{dist-BrRgjiq1.js → dist-D17RZAsz.js} +1 -1
  18. package/ui-dist/assets/{dist-Bh_tj3FN.js → dist-_zZ8P38T.js} +1 -1
  19. package/ui-dist/assets/{doc-browser-BlWjhmnM.js → doc-browser-CiLvybSC.js} +1 -1
  20. package/ui-dist/assets/{doc-browser-context-NGYa66Xg.js → doc-browser-context-CerkiK_N.js} +1 -1
  21. package/ui-dist/assets/doc-browser-z7vdH3Ez.js +1 -0
  22. package/ui-dist/assets/{form-actions-CeKgh4SQ.js → form-actions-Cmv23dN4.js} +1 -1
  23. package/ui-dist/assets/index-CWdgBpSc.css +1 -0
  24. package/ui-dist/assets/index-DlL8uKXu.js +109 -0
  25. package/ui-dist/assets/loader-circle-vUsFtTfb.js +1 -0
  26. package/ui-dist/assets/mcp-marketplace-page-BV8WB3_L.js +1 -0
  27. package/ui-dist/assets/{mcp-marketplace-page-DfNmMqKs.js → mcp-marketplace-page-DN_fqcLd.js} +1 -1
  28. package/ui-dist/assets/{model-config-page-DHzyDV5v.js → model-config-page-BxaeCMbJ.js} +1 -1
  29. package/ui-dist/assets/{navigation-link-CzGgQGi_.js → navigation-link-k9rsmtC4.js} +1 -1
  30. package/ui-dist/assets/plus-Dc760kHj.js +1 -0
  31. package/ui-dist/assets/{provider-scoped-model-input-CjrHwzFb.js → provider-scoped-model-input-DLSAB6a5.js} +1 -1
  32. package/ui-dist/assets/{providers-config-page-ZPvJWHFN.js → providers-config-page-DM5N0aeS.js} +1 -1
  33. package/ui-dist/assets/{react-BXRf96P-.js → react-BgRLh3ee.js} +1 -1
  34. package/ui-dist/assets/{refresh-cw-ChKuqgPR.js → refresh-cw-axvJLmRG.js} +1 -1
  35. package/ui-dist/assets/remote-Cj1GBMaN.js +1 -0
  36. package/ui-dist/assets/rotate-cw-CFByP2O8.js +1 -0
  37. package/ui-dist/assets/{runtime-config-page-CGpPQD8K.js → runtime-config-page-WNzZyjMW.js} +1 -1
  38. package/ui-dist/assets/{save-BWGNAKV5.js → save-DbR4SGk8.js} +1 -1
  39. package/ui-dist/assets/{search-DFcT_9wJ.js → search-M9lrb8id.js} +1 -1
  40. package/ui-dist/assets/{search-config-page-DBBzVAQZ.js → search-config-page-DmsID8FQ.js} +1 -1
  41. package/ui-dist/assets/{secrets-config-page-bnJ8sIB7.js → secrets-config-page-C10OwI0b.js} +1 -1
  42. package/ui-dist/assets/security-config-CvU5JxDT.js +1 -0
  43. package/ui-dist/assets/{select-BnNrv8si.js → select-D_YBzMdv.js} +1 -1
  44. package/ui-dist/assets/{settings-2-CFHrgKTX.js → settings-2-zy40UwTo.js} +1 -1
  45. package/ui-dist/assets/skeleton-BmAtzjDM.js +1 -0
  46. package/ui-dist/assets/{tag-chip-L5w4xfS7.js → tag-chip-D7QRdcph.js} +1 -1
  47. package/ui-dist/assets/x-D9Y9hf_r.js +1 -0
  48. package/ui-dist/index.html +19 -19
  49. package/dist/nextclaw-distribution.utils-CfUuTwjt.js.map +0 -1
  50. package/ui-dist/assets/api-Dket1cmq.js +0 -13
  51. package/ui-dist/assets/doc-browser-Da9hDsoQ.js +0 -1
  52. package/ui-dist/assets/index-Bz5yNMXA.js +0 -109
  53. package/ui-dist/assets/index-DSLK7zgR.css +0 -1
  54. package/ui-dist/assets/loader-circle-DLp2jNIf.js +0 -1
  55. package/ui-dist/assets/mcp-marketplace-page-CIWbjXM8.js +0 -1
  56. package/ui-dist/assets/plus-Dfj3tJ_m.js +0 -1
  57. package/ui-dist/assets/remote-C2enAZbs.js +0 -1
  58. package/ui-dist/assets/rotate-cw-BGkZpcc7.js +0 -1
  59. package/ui-dist/assets/security-config-CfbXSpgX.js +0 -1
  60. package/ui-dist/assets/skeleton-tfWhGuJT.js +0 -1
  61. package/ui-dist/assets/x-BCSE3LCQ.js +0 -1
@@ -1,4864 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import { t as createNextclawDistribution } from "../../nextclaw-distribution.utils-CfUuTwjt.js";
3
- import { createRequire } from "node:module";
4
- import { APP_NAME, APP_TAGLINE, getConfigPath, getDataDir, getRunPath, loadConfig, resolveConfigSecrets } from "@nextclaw/core";
5
- import { Command, InvalidArgumentError } from "commander";
6
- import { constants, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
7
- import { createHash, randomBytes } from "node:crypto";
8
- import { execFile } from "node:child_process";
9
- import path, { join, resolve } from "node:path";
10
- import { tmpdir } from "node:os";
11
- import "@nextclaw/server";
12
- import { NextclawDistributionService, NextclawServiceRuntime, readLearningLoopRuntimeConfig } from "@nextclaw/service";
13
- import { AppHomeService, AppInstanceStorageService, AppPublishService, AppPublishValidationService } from "@nextclaw/app-runtime";
14
- import { McpServiceAppRuntimeService, buildServiceActionId, getServiceAppManifestPath, mergeServiceAppRuntimeActions, readServiceAppManifest } from "@nextclaw/kernel";
15
- import { access, mkdtemp, readFile, rm, stat } from "node:fs/promises";
16
- import { promisify } from "node:util";
17
- var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
18
- var __require = /* @__PURE__ */ createRequire(import.meta.url);
19
- //#endregion
20
- //#region ../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/constants.js
21
- var require_constants = /* @__PURE__ */ __commonJSMin(((exports, module) => {
22
- const BINARY_TYPES = [
23
- "nodebuffer",
24
- "arraybuffer",
25
- "fragments"
26
- ];
27
- const hasBlob = typeof Blob !== "undefined";
28
- if (hasBlob) BINARY_TYPES.push("blob");
29
- module.exports = {
30
- BINARY_TYPES,
31
- CLOSE_TIMEOUT: 3e4,
32
- EMPTY_BUFFER: Buffer.alloc(0),
33
- GUID: "258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
34
- hasBlob,
35
- kForOnEventAttribute: Symbol("kIsForOnEventAttribute"),
36
- kListener: Symbol("kListener"),
37
- kStatusCode: Symbol("status-code"),
38
- kWebSocket: Symbol("websocket"),
39
- NOOP: () => {}
40
- };
41
- }));
42
- //#endregion
43
- //#region ../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/buffer-util.js
44
- var require_buffer_util = /* @__PURE__ */ __commonJSMin(((exports, module) => {
45
- const { EMPTY_BUFFER } = require_constants();
46
- const FastBuffer = Buffer[Symbol.species];
47
- /**
48
- * Merges an array of buffers into a new buffer.
49
- *
50
- * @param {Buffer[]} list The array of buffers to concat
51
- * @param {Number} totalLength The total length of buffers in the list
52
- * @return {Buffer} The resulting buffer
53
- * @public
54
- */
55
- function concat(list, totalLength) {
56
- if (list.length === 0) return EMPTY_BUFFER;
57
- if (list.length === 1) return list[0];
58
- const target = Buffer.allocUnsafe(totalLength);
59
- let offset = 0;
60
- for (let i = 0; i < list.length; i++) {
61
- const buf = list[i];
62
- target.set(buf, offset);
63
- offset += buf.length;
64
- }
65
- if (offset < totalLength) return new FastBuffer(target.buffer, target.byteOffset, offset);
66
- return target;
67
- }
68
- /**
69
- * Masks a buffer using the given mask.
70
- *
71
- * @param {Buffer} source The buffer to mask
72
- * @param {Buffer} mask The mask to use
73
- * @param {Buffer} output The buffer where to store the result
74
- * @param {Number} offset The offset at which to start writing
75
- * @param {Number} length The number of bytes to mask.
76
- * @public
77
- */
78
- function _mask(source, mask, output, offset, length) {
79
- for (let i = 0; i < length; i++) output[offset + i] = source[i] ^ mask[i & 3];
80
- }
81
- /**
82
- * Unmasks a buffer using the given mask.
83
- *
84
- * @param {Buffer} buffer The buffer to unmask
85
- * @param {Buffer} mask The mask to use
86
- * @public
87
- */
88
- function _unmask(buffer, mask) {
89
- for (let i = 0; i < buffer.length; i++) buffer[i] ^= mask[i & 3];
90
- }
91
- /**
92
- * Converts a buffer to an `ArrayBuffer`.
93
- *
94
- * @param {Buffer} buf The buffer to convert
95
- * @return {ArrayBuffer} Converted buffer
96
- * @public
97
- */
98
- function toArrayBuffer(buf) {
99
- if (buf.length === buf.buffer.byteLength) return buf.buffer;
100
- return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.length);
101
- }
102
- /**
103
- * Converts `data` to a `Buffer`.
104
- *
105
- * @param {*} data The data to convert
106
- * @return {Buffer} The buffer
107
- * @throws {TypeError}
108
- * @public
109
- */
110
- function toBuffer(data) {
111
- toBuffer.readOnly = true;
112
- if (Buffer.isBuffer(data)) return data;
113
- let buf;
114
- if (data instanceof ArrayBuffer) buf = new FastBuffer(data);
115
- else if (ArrayBuffer.isView(data)) buf = new FastBuffer(data.buffer, data.byteOffset, data.byteLength);
116
- else {
117
- buf = Buffer.from(data);
118
- toBuffer.readOnly = false;
119
- }
120
- return buf;
121
- }
122
- module.exports = {
123
- concat,
124
- mask: _mask,
125
- toArrayBuffer,
126
- toBuffer,
127
- unmask: _unmask
128
- };
129
- /* istanbul ignore else */
130
- if (!process.env.WS_NO_BUFFER_UTIL) try {
131
- const bufferUtil = __require("bufferutil");
132
- module.exports.mask = function(source, mask, output, offset, length) {
133
- if (length < 48) _mask(source, mask, output, offset, length);
134
- else bufferUtil.mask(source, mask, output, offset, length);
135
- };
136
- module.exports.unmask = function(buffer, mask) {
137
- if (buffer.length < 32) _unmask(buffer, mask);
138
- else bufferUtil.unmask(buffer, mask);
139
- };
140
- } catch (e) {}
141
- }));
142
- //#endregion
143
- //#region ../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/limiter.js
144
- var require_limiter = /* @__PURE__ */ __commonJSMin(((exports, module) => {
145
- const kDone = Symbol("kDone");
146
- const kRun = Symbol("kRun");
147
- /**
148
- * A very simple job queue with adjustable concurrency. Adapted from
149
- * https://github.com/STRML/async-limiter
150
- */
151
- var Limiter = class {
152
- /**
153
- * Creates a new `Limiter`.
154
- *
155
- * @param {Number} [concurrency=Infinity] The maximum number of jobs allowed
156
- * to run concurrently
157
- */
158
- constructor(concurrency) {
159
- this[kDone] = () => {
160
- this.pending--;
161
- this[kRun]();
162
- };
163
- this.concurrency = concurrency || Infinity;
164
- this.jobs = [];
165
- this.pending = 0;
166
- }
167
- /**
168
- * Adds a job to the queue.
169
- *
170
- * @param {Function} job The job to run
171
- * @public
172
- */
173
- add(job) {
174
- this.jobs.push(job);
175
- this[kRun]();
176
- }
177
- /**
178
- * Removes a job from the queue and runs it if possible.
179
- *
180
- * @private
181
- */
182
- [kRun]() {
183
- if (this.pending === this.concurrency) return;
184
- if (this.jobs.length) {
185
- const job = this.jobs.shift();
186
- this.pending++;
187
- job(this[kDone]);
188
- }
189
- }
190
- };
191
- module.exports = Limiter;
192
- }));
193
- //#endregion
194
- //#region ../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/permessage-deflate.js
195
- var require_permessage_deflate = /* @__PURE__ */ __commonJSMin(((exports, module) => {
196
- const zlib = __require("zlib");
197
- const bufferUtil = require_buffer_util();
198
- const Limiter = require_limiter();
199
- const { kStatusCode } = require_constants();
200
- const FastBuffer = Buffer[Symbol.species];
201
- const TRAILER = Buffer.from([
202
- 0,
203
- 0,
204
- 255,
205
- 255
206
- ]);
207
- const kPerMessageDeflate = Symbol("permessage-deflate");
208
- const kTotalLength = Symbol("total-length");
209
- const kCallback = Symbol("callback");
210
- const kBuffers = Symbol("buffers");
211
- const kError = Symbol("error");
212
- let zlibLimiter;
213
- /**
214
- * permessage-deflate implementation.
215
- */
216
- var PerMessageDeflate = class {
217
- /**
218
- * Creates a PerMessageDeflate instance.
219
- *
220
- * @param {Object} [options] Configuration options
221
- * @param {(Boolean|Number)} [options.clientMaxWindowBits] Advertise support
222
- * for, or request, a custom client window size
223
- * @param {Boolean} [options.clientNoContextTakeover=false] Advertise/
224
- * acknowledge disabling of client context takeover
225
- * @param {Number} [options.concurrencyLimit=10] The number of concurrent
226
- * calls to zlib
227
- * @param {(Boolean|Number)} [options.serverMaxWindowBits] Request/confirm the
228
- * use of a custom server window size
229
- * @param {Boolean} [options.serverNoContextTakeover=false] Request/accept
230
- * disabling of server context takeover
231
- * @param {Number} [options.threshold=1024] Size (in bytes) below which
232
- * messages should not be compressed if context takeover is disabled
233
- * @param {Object} [options.zlibDeflateOptions] Options to pass to zlib on
234
- * deflate
235
- * @param {Object} [options.zlibInflateOptions] Options to pass to zlib on
236
- * inflate
237
- * @param {Boolean} [isServer=false] Create the instance in either server or
238
- * client mode
239
- * @param {Number} [maxPayload=0] The maximum allowed message length
240
- */
241
- constructor(options, isServer, maxPayload) {
242
- this._maxPayload = maxPayload | 0;
243
- this._options = options || {};
244
- this._threshold = this._options.threshold !== void 0 ? this._options.threshold : 1024;
245
- this._isServer = !!isServer;
246
- this._deflate = null;
247
- this._inflate = null;
248
- this.params = null;
249
- if (!zlibLimiter) zlibLimiter = new Limiter(this._options.concurrencyLimit !== void 0 ? this._options.concurrencyLimit : 10);
250
- }
251
- /**
252
- * @type {String}
253
- */
254
- static get extensionName() {
255
- return "permessage-deflate";
256
- }
257
- /**
258
- * Create an extension negotiation offer.
259
- *
260
- * @return {Object} Extension parameters
261
- * @public
262
- */
263
- offer() {
264
- const params = {};
265
- if (this._options.serverNoContextTakeover) params.server_no_context_takeover = true;
266
- if (this._options.clientNoContextTakeover) params.client_no_context_takeover = true;
267
- if (this._options.serverMaxWindowBits) params.server_max_window_bits = this._options.serverMaxWindowBits;
268
- if (this._options.clientMaxWindowBits) params.client_max_window_bits = this._options.clientMaxWindowBits;
269
- else if (this._options.clientMaxWindowBits == null) params.client_max_window_bits = true;
270
- return params;
271
- }
272
- /**
273
- * Accept an extension negotiation offer/response.
274
- *
275
- * @param {Array} configurations The extension negotiation offers/reponse
276
- * @return {Object} Accepted configuration
277
- * @public
278
- */
279
- accept(configurations) {
280
- configurations = this.normalizeParams(configurations);
281
- this.params = this._isServer ? this.acceptAsServer(configurations) : this.acceptAsClient(configurations);
282
- return this.params;
283
- }
284
- /**
285
- * Releases all resources used by the extension.
286
- *
287
- * @public
288
- */
289
- cleanup() {
290
- if (this._inflate) {
291
- this._inflate.close();
292
- this._inflate = null;
293
- }
294
- if (this._deflate) {
295
- const callback = this._deflate[kCallback];
296
- this._deflate.close();
297
- this._deflate = null;
298
- if (callback) callback(/* @__PURE__ */ new Error("The deflate stream was closed while data was being processed"));
299
- }
300
- }
301
- /**
302
- * Accept an extension negotiation offer.
303
- *
304
- * @param {Array} offers The extension negotiation offers
305
- * @return {Object} Accepted configuration
306
- * @private
307
- */
308
- acceptAsServer(offers) {
309
- const opts = this._options;
310
- const accepted = offers.find((params) => {
311
- 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) return false;
312
- return true;
313
- });
314
- if (!accepted) throw new Error("None of the extension offers can be accepted");
315
- if (opts.serverNoContextTakeover) accepted.server_no_context_takeover = true;
316
- if (opts.clientNoContextTakeover) accepted.client_no_context_takeover = true;
317
- if (typeof opts.serverMaxWindowBits === "number") accepted.server_max_window_bits = opts.serverMaxWindowBits;
318
- if (typeof opts.clientMaxWindowBits === "number") accepted.client_max_window_bits = opts.clientMaxWindowBits;
319
- else if (accepted.client_max_window_bits === true || opts.clientMaxWindowBits === false) delete accepted.client_max_window_bits;
320
- return accepted;
321
- }
322
- /**
323
- * Accept the extension negotiation response.
324
- *
325
- * @param {Array} response The extension negotiation response
326
- * @return {Object} Accepted configuration
327
- * @private
328
- */
329
- acceptAsClient(response) {
330
- const params = response[0];
331
- if (this._options.clientNoContextTakeover === false && params.client_no_context_takeover) throw new Error("Unexpected parameter \"client_no_context_takeover\"");
332
- if (!params.client_max_window_bits) {
333
- if (typeof this._options.clientMaxWindowBits === "number") params.client_max_window_bits = this._options.clientMaxWindowBits;
334
- } else if (this._options.clientMaxWindowBits === false || typeof this._options.clientMaxWindowBits === "number" && params.client_max_window_bits > this._options.clientMaxWindowBits) throw new Error("Unexpected or invalid parameter \"client_max_window_bits\"");
335
- return params;
336
- }
337
- /**
338
- * Normalize parameters.
339
- *
340
- * @param {Array} configurations The extension negotiation offers/reponse
341
- * @return {Array} The offers/response with normalized parameters
342
- * @private
343
- */
344
- normalizeParams(configurations) {
345
- configurations.forEach((params) => {
346
- Object.keys(params).forEach((key) => {
347
- let value = params[key];
348
- if (value.length > 1) throw new Error(`Parameter "${key}" must have only a single value`);
349
- value = value[0];
350
- if (key === "client_max_window_bits") {
351
- if (value !== true) {
352
- const num = +value;
353
- if (!Number.isInteger(num) || num < 8 || num > 15) throw new TypeError(`Invalid value for parameter "${key}": ${value}`);
354
- value = num;
355
- } else if (!this._isServer) throw new TypeError(`Invalid value for parameter "${key}": ${value}`);
356
- } else if (key === "server_max_window_bits") {
357
- const num = +value;
358
- if (!Number.isInteger(num) || num < 8 || num > 15) throw new TypeError(`Invalid value for parameter "${key}": ${value}`);
359
- value = num;
360
- } else if (key === "client_no_context_takeover" || key === "server_no_context_takeover") {
361
- if (value !== true) throw new TypeError(`Invalid value for parameter "${key}": ${value}`);
362
- } else throw new Error(`Unknown parameter "${key}"`);
363
- params[key] = value;
364
- });
365
- });
366
- return configurations;
367
- }
368
- /**
369
- * Decompress data. Concurrency limited.
370
- *
371
- * @param {Buffer} data Compressed data
372
- * @param {Boolean} fin Specifies whether or not this is the last fragment
373
- * @param {Function} callback Callback
374
- * @public
375
- */
376
- decompress(data, fin, callback) {
377
- zlibLimiter.add((done) => {
378
- this._decompress(data, fin, (err, result) => {
379
- done();
380
- callback(err, result);
381
- });
382
- });
383
- }
384
- /**
385
- * Compress data. Concurrency limited.
386
- *
387
- * @param {(Buffer|String)} data Data to compress
388
- * @param {Boolean} fin Specifies whether or not this is the last fragment
389
- * @param {Function} callback Callback
390
- * @public
391
- */
392
- compress(data, fin, callback) {
393
- zlibLimiter.add((done) => {
394
- this._compress(data, fin, (err, result) => {
395
- done();
396
- callback(err, result);
397
- });
398
- });
399
- }
400
- /**
401
- * Decompress data.
402
- *
403
- * @param {Buffer} data Compressed data
404
- * @param {Boolean} fin Specifies whether or not this is the last fragment
405
- * @param {Function} callback Callback
406
- * @private
407
- */
408
- _decompress(data, fin, callback) {
409
- const endpoint = this._isServer ? "client" : "server";
410
- if (!this._inflate) {
411
- const key = `${endpoint}_max_window_bits`;
412
- const windowBits = typeof this.params[key] !== "number" ? zlib.Z_DEFAULT_WINDOWBITS : this.params[key];
413
- this._inflate = zlib.createInflateRaw({
414
- ...this._options.zlibInflateOptions,
415
- windowBits
416
- });
417
- this._inflate[kPerMessageDeflate] = this;
418
- this._inflate[kTotalLength] = 0;
419
- this._inflate[kBuffers] = [];
420
- this._inflate.on("error", inflateOnError);
421
- this._inflate.on("data", inflateOnData);
422
- }
423
- this._inflate[kCallback] = callback;
424
- this._inflate.write(data);
425
- if (fin) this._inflate.write(TRAILER);
426
- this._inflate.flush(() => {
427
- const err = this._inflate[kError];
428
- if (err) {
429
- this._inflate.close();
430
- this._inflate = null;
431
- callback(err);
432
- return;
433
- }
434
- const data = bufferUtil.concat(this._inflate[kBuffers], this._inflate[kTotalLength]);
435
- if (this._inflate._readableState.endEmitted) {
436
- this._inflate.close();
437
- this._inflate = null;
438
- } else {
439
- this._inflate[kTotalLength] = 0;
440
- this._inflate[kBuffers] = [];
441
- if (fin && this.params[`${endpoint}_no_context_takeover`]) this._inflate.reset();
442
- }
443
- callback(null, data);
444
- });
445
- }
446
- /**
447
- * Compress data.
448
- *
449
- * @param {(Buffer|String)} data Data to compress
450
- * @param {Boolean} fin Specifies whether or not this is the last fragment
451
- * @param {Function} callback Callback
452
- * @private
453
- */
454
- _compress(data, fin, callback) {
455
- const endpoint = this._isServer ? "server" : "client";
456
- if (!this._deflate) {
457
- const key = `${endpoint}_max_window_bits`;
458
- const windowBits = typeof this.params[key] !== "number" ? zlib.Z_DEFAULT_WINDOWBITS : this.params[key];
459
- this._deflate = zlib.createDeflateRaw({
460
- ...this._options.zlibDeflateOptions,
461
- windowBits
462
- });
463
- this._deflate[kTotalLength] = 0;
464
- this._deflate[kBuffers] = [];
465
- this._deflate.on("data", deflateOnData);
466
- }
467
- this._deflate[kCallback] = callback;
468
- this._deflate.write(data);
469
- this._deflate.flush(zlib.Z_SYNC_FLUSH, () => {
470
- if (!this._deflate) return;
471
- let data = bufferUtil.concat(this._deflate[kBuffers], this._deflate[kTotalLength]);
472
- if (fin) data = new FastBuffer(data.buffer, data.byteOffset, data.length - 4);
473
- this._deflate[kCallback] = null;
474
- this._deflate[kTotalLength] = 0;
475
- this._deflate[kBuffers] = [];
476
- if (fin && this.params[`${endpoint}_no_context_takeover`]) this._deflate.reset();
477
- callback(null, data);
478
- });
479
- }
480
- };
481
- module.exports = PerMessageDeflate;
482
- /**
483
- * The listener of the `zlib.DeflateRaw` stream `'data'` event.
484
- *
485
- * @param {Buffer} chunk A chunk of data
486
- * @private
487
- */
488
- function deflateOnData(chunk) {
489
- this[kBuffers].push(chunk);
490
- this[kTotalLength] += chunk.length;
491
- }
492
- /**
493
- * The listener of the `zlib.InflateRaw` stream `'data'` event.
494
- *
495
- * @param {Buffer} chunk A chunk of data
496
- * @private
497
- */
498
- function inflateOnData(chunk) {
499
- this[kTotalLength] += chunk.length;
500
- if (this[kPerMessageDeflate]._maxPayload < 1 || this[kTotalLength] <= this[kPerMessageDeflate]._maxPayload) {
501
- this[kBuffers].push(chunk);
502
- return;
503
- }
504
- this[kError] = /* @__PURE__ */ new RangeError("Max payload size exceeded");
505
- this[kError].code = "WS_ERR_UNSUPPORTED_MESSAGE_LENGTH";
506
- this[kError][kStatusCode] = 1009;
507
- this.removeListener("data", inflateOnData);
508
- this.reset();
509
- }
510
- /**
511
- * The listener of the `zlib.InflateRaw` stream `'error'` event.
512
- *
513
- * @param {Error} err The emitted error
514
- * @private
515
- */
516
- function inflateOnError(err) {
517
- this[kPerMessageDeflate]._inflate = null;
518
- if (this[kError]) {
519
- this[kCallback](this[kError]);
520
- return;
521
- }
522
- err[kStatusCode] = 1007;
523
- this[kCallback](err);
524
- }
525
- }));
526
- //#endregion
527
- //#region ../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/validation.js
528
- var require_validation = /* @__PURE__ */ __commonJSMin(((exports, module) => {
529
- const { isUtf8 } = __require("buffer");
530
- const { hasBlob } = require_constants();
531
- const tokenChars = [
532
- 0,
533
- 0,
534
- 0,
535
- 0,
536
- 0,
537
- 0,
538
- 0,
539
- 0,
540
- 0,
541
- 0,
542
- 0,
543
- 0,
544
- 0,
545
- 0,
546
- 0,
547
- 0,
548
- 0,
549
- 0,
550
- 0,
551
- 0,
552
- 0,
553
- 0,
554
- 0,
555
- 0,
556
- 0,
557
- 0,
558
- 0,
559
- 0,
560
- 0,
561
- 0,
562
- 0,
563
- 0,
564
- 0,
565
- 1,
566
- 0,
567
- 1,
568
- 1,
569
- 1,
570
- 1,
571
- 1,
572
- 0,
573
- 0,
574
- 1,
575
- 1,
576
- 0,
577
- 1,
578
- 1,
579
- 0,
580
- 1,
581
- 1,
582
- 1,
583
- 1,
584
- 1,
585
- 1,
586
- 1,
587
- 1,
588
- 1,
589
- 1,
590
- 0,
591
- 0,
592
- 0,
593
- 0,
594
- 0,
595
- 0,
596
- 0,
597
- 1,
598
- 1,
599
- 1,
600
- 1,
601
- 1,
602
- 1,
603
- 1,
604
- 1,
605
- 1,
606
- 1,
607
- 1,
608
- 1,
609
- 1,
610
- 1,
611
- 1,
612
- 1,
613
- 1,
614
- 1,
615
- 1,
616
- 1,
617
- 1,
618
- 1,
619
- 1,
620
- 1,
621
- 1,
622
- 1,
623
- 0,
624
- 0,
625
- 0,
626
- 1,
627
- 1,
628
- 1,
629
- 1,
630
- 1,
631
- 1,
632
- 1,
633
- 1,
634
- 1,
635
- 1,
636
- 1,
637
- 1,
638
- 1,
639
- 1,
640
- 1,
641
- 1,
642
- 1,
643
- 1,
644
- 1,
645
- 1,
646
- 1,
647
- 1,
648
- 1,
649
- 1,
650
- 1,
651
- 1,
652
- 1,
653
- 1,
654
- 1,
655
- 0,
656
- 1,
657
- 0,
658
- 1,
659
- 0
660
- ];
661
- /**
662
- * Checks if a status code is allowed in a close frame.
663
- *
664
- * @param {Number} code The status code
665
- * @return {Boolean} `true` if the status code is valid, else `false`
666
- * @public
667
- */
668
- function isValidStatusCode(code) {
669
- return code >= 1e3 && code <= 1014 && code !== 1004 && code !== 1005 && code !== 1006 || code >= 3e3 && code <= 4999;
670
- }
671
- /**
672
- * Checks if a given buffer contains only correct UTF-8.
673
- * Ported from https://www.cl.cam.ac.uk/%7Emgk25/ucs/utf8_check.c by
674
- * Markus Kuhn.
675
- *
676
- * @param {Buffer} buf The buffer to check
677
- * @return {Boolean} `true` if `buf` contains only correct UTF-8, else `false`
678
- * @public
679
- */
680
- function _isValidUTF8(buf) {
681
- const len = buf.length;
682
- let i = 0;
683
- while (i < len) if ((buf[i] & 128) === 0) i++;
684
- else if ((buf[i] & 224) === 192) {
685
- if (i + 1 === len || (buf[i + 1] & 192) !== 128 || (buf[i] & 254) === 192) return false;
686
- i += 2;
687
- } else if ((buf[i] & 240) === 224) {
688
- if (i + 2 >= len || (buf[i + 1] & 192) !== 128 || (buf[i + 2] & 192) !== 128 || buf[i] === 224 && (buf[i + 1] & 224) === 128 || buf[i] === 237 && (buf[i + 1] & 224) === 160) return false;
689
- i += 3;
690
- } else if ((buf[i] & 248) === 240) {
691
- if (i + 3 >= len || (buf[i + 1] & 192) !== 128 || (buf[i + 2] & 192) !== 128 || (buf[i + 3] & 192) !== 128 || buf[i] === 240 && (buf[i + 1] & 240) === 128 || buf[i] === 244 && buf[i + 1] > 143 || buf[i] > 244) return false;
692
- i += 4;
693
- } else return false;
694
- return true;
695
- }
696
- /**
697
- * Determines whether a value is a `Blob`.
698
- *
699
- * @param {*} value The value to be tested
700
- * @return {Boolean} `true` if `value` is a `Blob`, else `false`
701
- * @private
702
- */
703
- function isBlob(value) {
704
- return hasBlob && typeof value === "object" && typeof value.arrayBuffer === "function" && typeof value.type === "string" && typeof value.stream === "function" && (value[Symbol.toStringTag] === "Blob" || value[Symbol.toStringTag] === "File");
705
- }
706
- module.exports = {
707
- isBlob,
708
- isValidStatusCode,
709
- isValidUTF8: _isValidUTF8,
710
- tokenChars
711
- };
712
- if (isUtf8) module.exports.isValidUTF8 = function(buf) {
713
- return buf.length < 24 ? _isValidUTF8(buf) : isUtf8(buf);
714
- };
715
- else if (!process.env.WS_NO_UTF_8_VALIDATE) try {
716
- const isValidUTF8 = __require("utf-8-validate");
717
- module.exports.isValidUTF8 = function(buf) {
718
- return buf.length < 32 ? _isValidUTF8(buf) : isValidUTF8(buf);
719
- };
720
- } catch (e) {}
721
- }));
722
- //#endregion
723
- //#region ../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/receiver.js
724
- var require_receiver = /* @__PURE__ */ __commonJSMin(((exports, module) => {
725
- const { Writable } = __require("stream");
726
- const PerMessageDeflate = require_permessage_deflate();
727
- const { BINARY_TYPES, EMPTY_BUFFER, kStatusCode, kWebSocket } = require_constants();
728
- const { concat, toArrayBuffer, unmask } = require_buffer_util();
729
- const { isValidStatusCode, isValidUTF8 } = require_validation();
730
- const FastBuffer = Buffer[Symbol.species];
731
- const GET_INFO = 0;
732
- const GET_PAYLOAD_LENGTH_16 = 1;
733
- const GET_PAYLOAD_LENGTH_64 = 2;
734
- const GET_MASK = 3;
735
- const GET_DATA = 4;
736
- const INFLATING = 5;
737
- const DEFER_EVENT = 6;
738
- /**
739
- * HyBi Receiver implementation.
740
- *
741
- * @extends Writable
742
- */
743
- var Receiver = class extends Writable {
744
- /**
745
- * Creates a Receiver instance.
746
- *
747
- * @param {Object} [options] Options object
748
- * @param {Boolean} [options.allowSynchronousEvents=true] Specifies whether
749
- * any of the `'message'`, `'ping'`, and `'pong'` events can be emitted
750
- * multiple times in the same tick
751
- * @param {String} [options.binaryType=nodebuffer] The type for binary data
752
- * @param {Object} [options.extensions] An object containing the negotiated
753
- * extensions
754
- * @param {Boolean} [options.isServer=false] Specifies whether to operate in
755
- * client or server mode
756
- * @param {Number} [options.maxPayload=0] The maximum allowed message length
757
- * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
758
- * not to skip UTF-8 validation for text and close messages
759
- */
760
- constructor(options = {}) {
761
- super();
762
- this._allowSynchronousEvents = options.allowSynchronousEvents !== void 0 ? options.allowSynchronousEvents : true;
763
- this._binaryType = options.binaryType || BINARY_TYPES[0];
764
- this._extensions = options.extensions || {};
765
- this._isServer = !!options.isServer;
766
- this._maxPayload = options.maxPayload | 0;
767
- this._skipUTF8Validation = !!options.skipUTF8Validation;
768
- this[kWebSocket] = void 0;
769
- this._bufferedBytes = 0;
770
- this._buffers = [];
771
- this._compressed = false;
772
- this._payloadLength = 0;
773
- this._mask = void 0;
774
- this._fragmented = 0;
775
- this._masked = false;
776
- this._fin = false;
777
- this._opcode = 0;
778
- this._totalPayloadLength = 0;
779
- this._messageLength = 0;
780
- this._fragments = [];
781
- this._errored = false;
782
- this._loop = false;
783
- this._state = GET_INFO;
784
- }
785
- /**
786
- * Implements `Writable.prototype._write()`.
787
- *
788
- * @param {Buffer} chunk The chunk of data to write
789
- * @param {String} encoding The character encoding of `chunk`
790
- * @param {Function} cb Callback
791
- * @private
792
- */
793
- _write(chunk, encoding, cb) {
794
- if (this._opcode === 8 && this._state == GET_INFO) return cb();
795
- this._bufferedBytes += chunk.length;
796
- this._buffers.push(chunk);
797
- this.startLoop(cb);
798
- }
799
- /**
800
- * Consumes `n` bytes from the buffered data.
801
- *
802
- * @param {Number} n The number of bytes to consume
803
- * @return {Buffer} The consumed bytes
804
- * @private
805
- */
806
- consume(n) {
807
- this._bufferedBytes -= n;
808
- if (n === this._buffers[0].length) return this._buffers.shift();
809
- if (n < this._buffers[0].length) {
810
- const buf = this._buffers[0];
811
- this._buffers[0] = new FastBuffer(buf.buffer, buf.byteOffset + n, buf.length - n);
812
- return new FastBuffer(buf.buffer, buf.byteOffset, n);
813
- }
814
- const dst = Buffer.allocUnsafe(n);
815
- do {
816
- const buf = this._buffers[0];
817
- const offset = dst.length - n;
818
- if (n >= buf.length) dst.set(this._buffers.shift(), offset);
819
- else {
820
- dst.set(new Uint8Array(buf.buffer, buf.byteOffset, n), offset);
821
- this._buffers[0] = new FastBuffer(buf.buffer, buf.byteOffset + n, buf.length - n);
822
- }
823
- n -= buf.length;
824
- } while (n > 0);
825
- return dst;
826
- }
827
- /**
828
- * Starts the parsing loop.
829
- *
830
- * @param {Function} cb Callback
831
- * @private
832
- */
833
- startLoop(cb) {
834
- this._loop = true;
835
- do
836
- switch (this._state) {
837
- case GET_INFO:
838
- this.getInfo(cb);
839
- break;
840
- case GET_PAYLOAD_LENGTH_16:
841
- this.getPayloadLength16(cb);
842
- break;
843
- case GET_PAYLOAD_LENGTH_64:
844
- this.getPayloadLength64(cb);
845
- break;
846
- case GET_MASK:
847
- this.getMask();
848
- break;
849
- case GET_DATA:
850
- this.getData(cb);
851
- break;
852
- case INFLATING:
853
- case DEFER_EVENT:
854
- this._loop = false;
855
- return;
856
- }
857
- while (this._loop);
858
- if (!this._errored) cb();
859
- }
860
- /**
861
- * Reads the first two bytes of a frame.
862
- *
863
- * @param {Function} cb Callback
864
- * @private
865
- */
866
- getInfo(cb) {
867
- if (this._bufferedBytes < 2) {
868
- this._loop = false;
869
- return;
870
- }
871
- const buf = this.consume(2);
872
- if ((buf[0] & 48) !== 0) {
873
- cb(this.createError(RangeError, "RSV2 and RSV3 must be clear", true, 1002, "WS_ERR_UNEXPECTED_RSV_2_3"));
874
- return;
875
- }
876
- const compressed = (buf[0] & 64) === 64;
877
- if (compressed && !this._extensions[PerMessageDeflate.extensionName]) {
878
- cb(this.createError(RangeError, "RSV1 must be clear", true, 1002, "WS_ERR_UNEXPECTED_RSV_1"));
879
- return;
880
- }
881
- this._fin = (buf[0] & 128) === 128;
882
- this._opcode = buf[0] & 15;
883
- this._payloadLength = buf[1] & 127;
884
- if (this._opcode === 0) {
885
- if (compressed) {
886
- cb(this.createError(RangeError, "RSV1 must be clear", true, 1002, "WS_ERR_UNEXPECTED_RSV_1"));
887
- return;
888
- }
889
- if (!this._fragmented) {
890
- cb(this.createError(RangeError, "invalid opcode 0", true, 1002, "WS_ERR_INVALID_OPCODE"));
891
- return;
892
- }
893
- this._opcode = this._fragmented;
894
- } else if (this._opcode === 1 || this._opcode === 2) {
895
- if (this._fragmented) {
896
- cb(this.createError(RangeError, `invalid opcode ${this._opcode}`, true, 1002, "WS_ERR_INVALID_OPCODE"));
897
- return;
898
- }
899
- this._compressed = compressed;
900
- } else if (this._opcode > 7 && this._opcode < 11) {
901
- if (!this._fin) {
902
- cb(this.createError(RangeError, "FIN must be set", true, 1002, "WS_ERR_EXPECTED_FIN"));
903
- return;
904
- }
905
- if (compressed) {
906
- cb(this.createError(RangeError, "RSV1 must be clear", true, 1002, "WS_ERR_UNEXPECTED_RSV_1"));
907
- return;
908
- }
909
- if (this._payloadLength > 125 || this._opcode === 8 && this._payloadLength === 1) {
910
- cb(this.createError(RangeError, `invalid payload length ${this._payloadLength}`, true, 1002, "WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH"));
911
- return;
912
- }
913
- } else {
914
- cb(this.createError(RangeError, `invalid opcode ${this._opcode}`, true, 1002, "WS_ERR_INVALID_OPCODE"));
915
- return;
916
- }
917
- if (!this._fin && !this._fragmented) this._fragmented = this._opcode;
918
- this._masked = (buf[1] & 128) === 128;
919
- if (this._isServer) {
920
- if (!this._masked) {
921
- cb(this.createError(RangeError, "MASK must be set", true, 1002, "WS_ERR_EXPECTED_MASK"));
922
- return;
923
- }
924
- } else if (this._masked) {
925
- cb(this.createError(RangeError, "MASK must be clear", true, 1002, "WS_ERR_UNEXPECTED_MASK"));
926
- return;
927
- }
928
- if (this._payloadLength === 126) this._state = GET_PAYLOAD_LENGTH_16;
929
- else if (this._payloadLength === 127) this._state = GET_PAYLOAD_LENGTH_64;
930
- else this.haveLength(cb);
931
- }
932
- /**
933
- * Gets extended payload length (7+16).
934
- *
935
- * @param {Function} cb Callback
936
- * @private
937
- */
938
- getPayloadLength16(cb) {
939
- if (this._bufferedBytes < 2) {
940
- this._loop = false;
941
- return;
942
- }
943
- this._payloadLength = this.consume(2).readUInt16BE(0);
944
- this.haveLength(cb);
945
- }
946
- /**
947
- * Gets extended payload length (7+64).
948
- *
949
- * @param {Function} cb Callback
950
- * @private
951
- */
952
- getPayloadLength64(cb) {
953
- if (this._bufferedBytes < 8) {
954
- this._loop = false;
955
- return;
956
- }
957
- const buf = this.consume(8);
958
- const num = buf.readUInt32BE(0);
959
- if (num > Math.pow(2, 21) - 1) {
960
- cb(this.createError(RangeError, "Unsupported WebSocket frame: payload length > 2^53 - 1", false, 1009, "WS_ERR_UNSUPPORTED_DATA_PAYLOAD_LENGTH"));
961
- return;
962
- }
963
- this._payloadLength = num * Math.pow(2, 32) + buf.readUInt32BE(4);
964
- this.haveLength(cb);
965
- }
966
- /**
967
- * Payload length has been read.
968
- *
969
- * @param {Function} cb Callback
970
- * @private
971
- */
972
- haveLength(cb) {
973
- if (this._payloadLength && this._opcode < 8) {
974
- this._totalPayloadLength += this._payloadLength;
975
- if (this._totalPayloadLength > this._maxPayload && this._maxPayload > 0) {
976
- cb(this.createError(RangeError, "Max payload size exceeded", false, 1009, "WS_ERR_UNSUPPORTED_MESSAGE_LENGTH"));
977
- return;
978
- }
979
- }
980
- if (this._masked) this._state = GET_MASK;
981
- else this._state = GET_DATA;
982
- }
983
- /**
984
- * Reads mask bytes.
985
- *
986
- * @private
987
- */
988
- getMask() {
989
- if (this._bufferedBytes < 4) {
990
- this._loop = false;
991
- return;
992
- }
993
- this._mask = this.consume(4);
994
- this._state = GET_DATA;
995
- }
996
- /**
997
- * Reads data bytes.
998
- *
999
- * @param {Function} cb Callback
1000
- * @private
1001
- */
1002
- getData(cb) {
1003
- let data = EMPTY_BUFFER;
1004
- if (this._payloadLength) {
1005
- if (this._bufferedBytes < this._payloadLength) {
1006
- this._loop = false;
1007
- return;
1008
- }
1009
- data = this.consume(this._payloadLength);
1010
- if (this._masked && (this._mask[0] | this._mask[1] | this._mask[2] | this._mask[3]) !== 0) unmask(data, this._mask);
1011
- }
1012
- if (this._opcode > 7) {
1013
- this.controlMessage(data, cb);
1014
- return;
1015
- }
1016
- if (this._compressed) {
1017
- this._state = INFLATING;
1018
- this.decompress(data, cb);
1019
- return;
1020
- }
1021
- if (data.length) {
1022
- this._messageLength = this._totalPayloadLength;
1023
- this._fragments.push(data);
1024
- }
1025
- this.dataMessage(cb);
1026
- }
1027
- /**
1028
- * Decompresses data.
1029
- *
1030
- * @param {Buffer} data Compressed data
1031
- * @param {Function} cb Callback
1032
- * @private
1033
- */
1034
- decompress(data, cb) {
1035
- this._extensions[PerMessageDeflate.extensionName].decompress(data, this._fin, (err, buf) => {
1036
- if (err) return cb(err);
1037
- if (buf.length) {
1038
- this._messageLength += buf.length;
1039
- if (this._messageLength > this._maxPayload && this._maxPayload > 0) {
1040
- cb(this.createError(RangeError, "Max payload size exceeded", false, 1009, "WS_ERR_UNSUPPORTED_MESSAGE_LENGTH"));
1041
- return;
1042
- }
1043
- this._fragments.push(buf);
1044
- }
1045
- this.dataMessage(cb);
1046
- if (this._state === GET_INFO) this.startLoop(cb);
1047
- });
1048
- }
1049
- /**
1050
- * Handles a data message.
1051
- *
1052
- * @param {Function} cb Callback
1053
- * @private
1054
- */
1055
- dataMessage(cb) {
1056
- if (!this._fin) {
1057
- this._state = GET_INFO;
1058
- return;
1059
- }
1060
- const messageLength = this._messageLength;
1061
- const fragments = this._fragments;
1062
- this._totalPayloadLength = 0;
1063
- this._messageLength = 0;
1064
- this._fragmented = 0;
1065
- this._fragments = [];
1066
- if (this._opcode === 2) {
1067
- let data;
1068
- if (this._binaryType === "nodebuffer") data = concat(fragments, messageLength);
1069
- else if (this._binaryType === "arraybuffer") data = toArrayBuffer(concat(fragments, messageLength));
1070
- else if (this._binaryType === "blob") data = new Blob(fragments);
1071
- else data = fragments;
1072
- if (this._allowSynchronousEvents) {
1073
- this.emit("message", data, true);
1074
- this._state = GET_INFO;
1075
- } else {
1076
- this._state = DEFER_EVENT;
1077
- setImmediate(() => {
1078
- this.emit("message", data, true);
1079
- this._state = GET_INFO;
1080
- this.startLoop(cb);
1081
- });
1082
- }
1083
- } else {
1084
- const buf = concat(fragments, messageLength);
1085
- if (!this._skipUTF8Validation && !isValidUTF8(buf)) {
1086
- cb(this.createError(Error, "invalid UTF-8 sequence", true, 1007, "WS_ERR_INVALID_UTF8"));
1087
- return;
1088
- }
1089
- if (this._state === INFLATING || this._allowSynchronousEvents) {
1090
- this.emit("message", buf, false);
1091
- this._state = GET_INFO;
1092
- } else {
1093
- this._state = DEFER_EVENT;
1094
- setImmediate(() => {
1095
- this.emit("message", buf, false);
1096
- this._state = GET_INFO;
1097
- this.startLoop(cb);
1098
- });
1099
- }
1100
- }
1101
- }
1102
- /**
1103
- * Handles a control message.
1104
- *
1105
- * @param {Buffer} data Data to handle
1106
- * @return {(Error|RangeError|undefined)} A possible error
1107
- * @private
1108
- */
1109
- controlMessage(data, cb) {
1110
- if (this._opcode === 8) {
1111
- if (data.length === 0) {
1112
- this._loop = false;
1113
- this.emit("conclude", 1005, EMPTY_BUFFER);
1114
- this.end();
1115
- } else {
1116
- const code = data.readUInt16BE(0);
1117
- if (!isValidStatusCode(code)) {
1118
- cb(this.createError(RangeError, `invalid status code ${code}`, true, 1002, "WS_ERR_INVALID_CLOSE_CODE"));
1119
- return;
1120
- }
1121
- const buf = new FastBuffer(data.buffer, data.byteOffset + 2, data.length - 2);
1122
- if (!this._skipUTF8Validation && !isValidUTF8(buf)) {
1123
- cb(this.createError(Error, "invalid UTF-8 sequence", true, 1007, "WS_ERR_INVALID_UTF8"));
1124
- return;
1125
- }
1126
- this._loop = false;
1127
- this.emit("conclude", code, buf);
1128
- this.end();
1129
- }
1130
- this._state = GET_INFO;
1131
- return;
1132
- }
1133
- if (this._allowSynchronousEvents) {
1134
- this.emit(this._opcode === 9 ? "ping" : "pong", data);
1135
- this._state = GET_INFO;
1136
- } else {
1137
- this._state = DEFER_EVENT;
1138
- setImmediate(() => {
1139
- this.emit(this._opcode === 9 ? "ping" : "pong", data);
1140
- this._state = GET_INFO;
1141
- this.startLoop(cb);
1142
- });
1143
- }
1144
- }
1145
- /**
1146
- * Builds an error object.
1147
- *
1148
- * @param {function(new:Error|RangeError)} ErrorCtor The error constructor
1149
- * @param {String} message The error message
1150
- * @param {Boolean} prefix Specifies whether or not to add a default prefix to
1151
- * `message`
1152
- * @param {Number} statusCode The status code
1153
- * @param {String} errorCode The exposed error code
1154
- * @return {(Error|RangeError)} The error
1155
- * @private
1156
- */
1157
- createError(ErrorCtor, message, prefix, statusCode, errorCode) {
1158
- this._loop = false;
1159
- this._errored = true;
1160
- const err = new ErrorCtor(prefix ? `Invalid WebSocket frame: ${message}` : message);
1161
- Error.captureStackTrace(err, this.createError);
1162
- err.code = errorCode;
1163
- err[kStatusCode] = statusCode;
1164
- return err;
1165
- }
1166
- };
1167
- module.exports = Receiver;
1168
- }));
1169
- //#endregion
1170
- //#region ../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/sender.js
1171
- var require_sender = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1172
- const { Duplex: Duplex$3 } = __require("stream");
1173
- const { randomFillSync } = __require("crypto");
1174
- const PerMessageDeflate = require_permessage_deflate();
1175
- const { EMPTY_BUFFER, kWebSocket, NOOP } = require_constants();
1176
- const { isBlob, isValidStatusCode } = require_validation();
1177
- const { mask: applyMask, toBuffer } = require_buffer_util();
1178
- const kByteLength = Symbol("kByteLength");
1179
- const maskBuffer = Buffer.alloc(4);
1180
- const RANDOM_POOL_SIZE = 8 * 1024;
1181
- let randomPool;
1182
- let randomPoolPointer = RANDOM_POOL_SIZE;
1183
- const DEFAULT = 0;
1184
- const DEFLATING = 1;
1185
- const GET_BLOB_DATA = 2;
1186
- module.exports = class Sender {
1187
- /**
1188
- * Creates a Sender instance.
1189
- *
1190
- * @param {Duplex} socket The connection socket
1191
- * @param {Object} [extensions] An object containing the negotiated extensions
1192
- * @param {Function} [generateMask] The function used to generate the masking
1193
- * key
1194
- */
1195
- constructor(socket, extensions, generateMask) {
1196
- this._extensions = extensions || {};
1197
- if (generateMask) {
1198
- this._generateMask = generateMask;
1199
- this._maskBuffer = Buffer.alloc(4);
1200
- }
1201
- this._socket = socket;
1202
- this._firstFragment = true;
1203
- this._compress = false;
1204
- this._bufferedBytes = 0;
1205
- this._queue = [];
1206
- this._state = DEFAULT;
1207
- this.onerror = NOOP;
1208
- this[kWebSocket] = void 0;
1209
- }
1210
- /**
1211
- * Frames a piece of data according to the HyBi WebSocket protocol.
1212
- *
1213
- * @param {(Buffer|String)} data The data to frame
1214
- * @param {Object} options Options object
1215
- * @param {Boolean} [options.fin=false] Specifies whether or not to set the
1216
- * FIN bit
1217
- * @param {Function} [options.generateMask] The function used to generate the
1218
- * masking key
1219
- * @param {Boolean} [options.mask=false] Specifies whether or not to mask
1220
- * `data`
1221
- * @param {Buffer} [options.maskBuffer] The buffer used to store the masking
1222
- * key
1223
- * @param {Number} options.opcode The opcode
1224
- * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be
1225
- * modified
1226
- * @param {Boolean} [options.rsv1=false] Specifies whether or not to set the
1227
- * RSV1 bit
1228
- * @return {(Buffer|String)[]} The framed data
1229
- * @public
1230
- */
1231
- static frame(data, options) {
1232
- let mask;
1233
- let merge = false;
1234
- let offset = 2;
1235
- let skipMasking = false;
1236
- if (options.mask) {
1237
- mask = options.maskBuffer || maskBuffer;
1238
- if (options.generateMask) options.generateMask(mask);
1239
- else {
1240
- if (randomPoolPointer === RANDOM_POOL_SIZE) {
1241
- /* istanbul ignore else */
1242
- if (randomPool === void 0) randomPool = Buffer.alloc(RANDOM_POOL_SIZE);
1243
- randomFillSync(randomPool, 0, RANDOM_POOL_SIZE);
1244
- randomPoolPointer = 0;
1245
- }
1246
- mask[0] = randomPool[randomPoolPointer++];
1247
- mask[1] = randomPool[randomPoolPointer++];
1248
- mask[2] = randomPool[randomPoolPointer++];
1249
- mask[3] = randomPool[randomPoolPointer++];
1250
- }
1251
- skipMasking = (mask[0] | mask[1] | mask[2] | mask[3]) === 0;
1252
- offset = 6;
1253
- }
1254
- let dataLength;
1255
- if (typeof data === "string") if ((!options.mask || skipMasking) && options[kByteLength] !== void 0) dataLength = options[kByteLength];
1256
- else {
1257
- data = Buffer.from(data);
1258
- dataLength = data.length;
1259
- }
1260
- else {
1261
- dataLength = data.length;
1262
- merge = options.mask && options.readOnly && !skipMasking;
1263
- }
1264
- let payloadLength = dataLength;
1265
- if (dataLength >= 65536) {
1266
- offset += 8;
1267
- payloadLength = 127;
1268
- } else if (dataLength > 125) {
1269
- offset += 2;
1270
- payloadLength = 126;
1271
- }
1272
- const target = Buffer.allocUnsafe(merge ? dataLength + offset : offset);
1273
- target[0] = options.fin ? options.opcode | 128 : options.opcode;
1274
- if (options.rsv1) target[0] |= 64;
1275
- target[1] = payloadLength;
1276
- if (payloadLength === 126) target.writeUInt16BE(dataLength, 2);
1277
- else if (payloadLength === 127) {
1278
- target[2] = target[3] = 0;
1279
- target.writeUIntBE(dataLength, 4, 6);
1280
- }
1281
- if (!options.mask) return [target, data];
1282
- target[1] |= 128;
1283
- target[offset - 4] = mask[0];
1284
- target[offset - 3] = mask[1];
1285
- target[offset - 2] = mask[2];
1286
- target[offset - 1] = mask[3];
1287
- if (skipMasking) return [target, data];
1288
- if (merge) {
1289
- applyMask(data, mask, target, offset, dataLength);
1290
- return [target];
1291
- }
1292
- applyMask(data, mask, data, 0, dataLength);
1293
- return [target, data];
1294
- }
1295
- /**
1296
- * Sends a close message to the other peer.
1297
- *
1298
- * @param {Number} [code] The status code component of the body
1299
- * @param {(String|Buffer)} [data] The message component of the body
1300
- * @param {Boolean} [mask=false] Specifies whether or not to mask the message
1301
- * @param {Function} [cb] Callback
1302
- * @public
1303
- */
1304
- close(code, data, mask, cb) {
1305
- let buf;
1306
- if (code === void 0) buf = EMPTY_BUFFER;
1307
- else if (typeof code !== "number" || !isValidStatusCode(code)) throw new TypeError("First argument must be a valid error code number");
1308
- else if (data === void 0 || !data.length) {
1309
- buf = Buffer.allocUnsafe(2);
1310
- buf.writeUInt16BE(code, 0);
1311
- } else {
1312
- const length = Buffer.byteLength(data);
1313
- if (length > 123) throw new RangeError("The message must not be greater than 123 bytes");
1314
- buf = Buffer.allocUnsafe(2 + length);
1315
- buf.writeUInt16BE(code, 0);
1316
- if (typeof data === "string") buf.write(data, 2);
1317
- else buf.set(data, 2);
1318
- }
1319
- const options = {
1320
- [kByteLength]: buf.length,
1321
- fin: true,
1322
- generateMask: this._generateMask,
1323
- mask,
1324
- maskBuffer: this._maskBuffer,
1325
- opcode: 8,
1326
- readOnly: false,
1327
- rsv1: false
1328
- };
1329
- if (this._state !== DEFAULT) this.enqueue([
1330
- this.dispatch,
1331
- buf,
1332
- false,
1333
- options,
1334
- cb
1335
- ]);
1336
- else this.sendFrame(Sender.frame(buf, options), cb);
1337
- }
1338
- /**
1339
- * Sends a ping message to the other peer.
1340
- *
1341
- * @param {*} data The message to send
1342
- * @param {Boolean} [mask=false] Specifies whether or not to mask `data`
1343
- * @param {Function} [cb] Callback
1344
- * @public
1345
- */
1346
- ping(data, mask, cb) {
1347
- let byteLength;
1348
- let readOnly;
1349
- if (typeof data === "string") {
1350
- byteLength = Buffer.byteLength(data);
1351
- readOnly = false;
1352
- } else if (isBlob(data)) {
1353
- byteLength = data.size;
1354
- readOnly = false;
1355
- } else {
1356
- data = toBuffer(data);
1357
- byteLength = data.length;
1358
- readOnly = toBuffer.readOnly;
1359
- }
1360
- if (byteLength > 125) throw new RangeError("The data size must not be greater than 125 bytes");
1361
- const options = {
1362
- [kByteLength]: byteLength,
1363
- fin: true,
1364
- generateMask: this._generateMask,
1365
- mask,
1366
- maskBuffer: this._maskBuffer,
1367
- opcode: 9,
1368
- readOnly,
1369
- rsv1: false
1370
- };
1371
- if (isBlob(data)) if (this._state !== DEFAULT) this.enqueue([
1372
- this.getBlobData,
1373
- data,
1374
- false,
1375
- options,
1376
- cb
1377
- ]);
1378
- else this.getBlobData(data, false, options, cb);
1379
- else if (this._state !== DEFAULT) this.enqueue([
1380
- this.dispatch,
1381
- data,
1382
- false,
1383
- options,
1384
- cb
1385
- ]);
1386
- else this.sendFrame(Sender.frame(data, options), cb);
1387
- }
1388
- /**
1389
- * Sends a pong message to the other peer.
1390
- *
1391
- * @param {*} data The message to send
1392
- * @param {Boolean} [mask=false] Specifies whether or not to mask `data`
1393
- * @param {Function} [cb] Callback
1394
- * @public
1395
- */
1396
- pong(data, mask, cb) {
1397
- let byteLength;
1398
- let readOnly;
1399
- if (typeof data === "string") {
1400
- byteLength = Buffer.byteLength(data);
1401
- readOnly = false;
1402
- } else if (isBlob(data)) {
1403
- byteLength = data.size;
1404
- readOnly = false;
1405
- } else {
1406
- data = toBuffer(data);
1407
- byteLength = data.length;
1408
- readOnly = toBuffer.readOnly;
1409
- }
1410
- if (byteLength > 125) throw new RangeError("The data size must not be greater than 125 bytes");
1411
- const options = {
1412
- [kByteLength]: byteLength,
1413
- fin: true,
1414
- generateMask: this._generateMask,
1415
- mask,
1416
- maskBuffer: this._maskBuffer,
1417
- opcode: 10,
1418
- readOnly,
1419
- rsv1: false
1420
- };
1421
- if (isBlob(data)) if (this._state !== DEFAULT) this.enqueue([
1422
- this.getBlobData,
1423
- data,
1424
- false,
1425
- options,
1426
- cb
1427
- ]);
1428
- else this.getBlobData(data, false, options, cb);
1429
- else if (this._state !== DEFAULT) this.enqueue([
1430
- this.dispatch,
1431
- data,
1432
- false,
1433
- options,
1434
- cb
1435
- ]);
1436
- else this.sendFrame(Sender.frame(data, options), cb);
1437
- }
1438
- /**
1439
- * Sends a data message to the other peer.
1440
- *
1441
- * @param {*} data The message to send
1442
- * @param {Object} options Options object
1443
- * @param {Boolean} [options.binary=false] Specifies whether `data` is binary
1444
- * or text
1445
- * @param {Boolean} [options.compress=false] Specifies whether or not to
1446
- * compress `data`
1447
- * @param {Boolean} [options.fin=false] Specifies whether the fragment is the
1448
- * last one
1449
- * @param {Boolean} [options.mask=false] Specifies whether or not to mask
1450
- * `data`
1451
- * @param {Function} [cb] Callback
1452
- * @public
1453
- */
1454
- send(data, options, cb) {
1455
- const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
1456
- let opcode = options.binary ? 2 : 1;
1457
- let rsv1 = options.compress;
1458
- let byteLength;
1459
- let readOnly;
1460
- if (typeof data === "string") {
1461
- byteLength = Buffer.byteLength(data);
1462
- readOnly = false;
1463
- } else if (isBlob(data)) {
1464
- byteLength = data.size;
1465
- readOnly = false;
1466
- } else {
1467
- data = toBuffer(data);
1468
- byteLength = data.length;
1469
- readOnly = toBuffer.readOnly;
1470
- }
1471
- if (this._firstFragment) {
1472
- this._firstFragment = false;
1473
- if (rsv1 && perMessageDeflate && perMessageDeflate.params[perMessageDeflate._isServer ? "server_no_context_takeover" : "client_no_context_takeover"]) rsv1 = byteLength >= perMessageDeflate._threshold;
1474
- this._compress = rsv1;
1475
- } else {
1476
- rsv1 = false;
1477
- opcode = 0;
1478
- }
1479
- if (options.fin) this._firstFragment = true;
1480
- const opts = {
1481
- [kByteLength]: byteLength,
1482
- fin: options.fin,
1483
- generateMask: this._generateMask,
1484
- mask: options.mask,
1485
- maskBuffer: this._maskBuffer,
1486
- opcode,
1487
- readOnly,
1488
- rsv1
1489
- };
1490
- if (isBlob(data)) if (this._state !== DEFAULT) this.enqueue([
1491
- this.getBlobData,
1492
- data,
1493
- this._compress,
1494
- opts,
1495
- cb
1496
- ]);
1497
- else this.getBlobData(data, this._compress, opts, cb);
1498
- else if (this._state !== DEFAULT) this.enqueue([
1499
- this.dispatch,
1500
- data,
1501
- this._compress,
1502
- opts,
1503
- cb
1504
- ]);
1505
- else this.dispatch(data, this._compress, opts, cb);
1506
- }
1507
- /**
1508
- * Gets the contents of a blob as binary data.
1509
- *
1510
- * @param {Blob} blob The blob
1511
- * @param {Boolean} [compress=false] Specifies whether or not to compress
1512
- * the data
1513
- * @param {Object} options Options object
1514
- * @param {Boolean} [options.fin=false] Specifies whether or not to set the
1515
- * FIN bit
1516
- * @param {Function} [options.generateMask] The function used to generate the
1517
- * masking key
1518
- * @param {Boolean} [options.mask=false] Specifies whether or not to mask
1519
- * `data`
1520
- * @param {Buffer} [options.maskBuffer] The buffer used to store the masking
1521
- * key
1522
- * @param {Number} options.opcode The opcode
1523
- * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be
1524
- * modified
1525
- * @param {Boolean} [options.rsv1=false] Specifies whether or not to set the
1526
- * RSV1 bit
1527
- * @param {Function} [cb] Callback
1528
- * @private
1529
- */
1530
- getBlobData(blob, compress, options, cb) {
1531
- this._bufferedBytes += options[kByteLength];
1532
- this._state = GET_BLOB_DATA;
1533
- blob.arrayBuffer().then((arrayBuffer) => {
1534
- if (this._socket.destroyed) {
1535
- const err = /* @__PURE__ */ new Error("The socket was closed while the blob was being read");
1536
- process.nextTick(callCallbacks, this, err, cb);
1537
- return;
1538
- }
1539
- this._bufferedBytes -= options[kByteLength];
1540
- const data = toBuffer(arrayBuffer);
1541
- if (!compress) {
1542
- this._state = DEFAULT;
1543
- this.sendFrame(Sender.frame(data, options), cb);
1544
- this.dequeue();
1545
- } else this.dispatch(data, compress, options, cb);
1546
- }).catch((err) => {
1547
- process.nextTick(onError, this, err, cb);
1548
- });
1549
- }
1550
- /**
1551
- * Dispatches a message.
1552
- *
1553
- * @param {(Buffer|String)} data The message to send
1554
- * @param {Boolean} [compress=false] Specifies whether or not to compress
1555
- * `data`
1556
- * @param {Object} options Options object
1557
- * @param {Boolean} [options.fin=false] Specifies whether or not to set the
1558
- * FIN bit
1559
- * @param {Function} [options.generateMask] The function used to generate the
1560
- * masking key
1561
- * @param {Boolean} [options.mask=false] Specifies whether or not to mask
1562
- * `data`
1563
- * @param {Buffer} [options.maskBuffer] The buffer used to store the masking
1564
- * key
1565
- * @param {Number} options.opcode The opcode
1566
- * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be
1567
- * modified
1568
- * @param {Boolean} [options.rsv1=false] Specifies whether or not to set the
1569
- * RSV1 bit
1570
- * @param {Function} [cb] Callback
1571
- * @private
1572
- */
1573
- dispatch(data, compress, options, cb) {
1574
- if (!compress) {
1575
- this.sendFrame(Sender.frame(data, options), cb);
1576
- return;
1577
- }
1578
- const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
1579
- this._bufferedBytes += options[kByteLength];
1580
- this._state = DEFLATING;
1581
- perMessageDeflate.compress(data, options.fin, (_, buf) => {
1582
- if (this._socket.destroyed) {
1583
- callCallbacks(this, /* @__PURE__ */ new Error("The socket was closed while data was being compressed"), cb);
1584
- return;
1585
- }
1586
- this._bufferedBytes -= options[kByteLength];
1587
- this._state = DEFAULT;
1588
- options.readOnly = false;
1589
- this.sendFrame(Sender.frame(buf, options), cb);
1590
- this.dequeue();
1591
- });
1592
- }
1593
- /**
1594
- * Executes queued send operations.
1595
- *
1596
- * @private
1597
- */
1598
- dequeue() {
1599
- while (this._state === DEFAULT && this._queue.length) {
1600
- const params = this._queue.shift();
1601
- this._bufferedBytes -= params[3][kByteLength];
1602
- Reflect.apply(params[0], this, params.slice(1));
1603
- }
1604
- }
1605
- /**
1606
- * Enqueues a send operation.
1607
- *
1608
- * @param {Array} params Send operation parameters.
1609
- * @private
1610
- */
1611
- enqueue(params) {
1612
- this._bufferedBytes += params[3][kByteLength];
1613
- this._queue.push(params);
1614
- }
1615
- /**
1616
- * Sends a frame.
1617
- *
1618
- * @param {(Buffer | String)[]} list The frame to send
1619
- * @param {Function} [cb] Callback
1620
- * @private
1621
- */
1622
- sendFrame(list, cb) {
1623
- if (list.length === 2) {
1624
- this._socket.cork();
1625
- this._socket.write(list[0]);
1626
- this._socket.write(list[1], cb);
1627
- this._socket.uncork();
1628
- } else this._socket.write(list[0], cb);
1629
- }
1630
- };
1631
- /**
1632
- * Calls queued callbacks with an error.
1633
- *
1634
- * @param {Sender} sender The `Sender` instance
1635
- * @param {Error} err The error to call the callbacks with
1636
- * @param {Function} [cb] The first callback
1637
- * @private
1638
- */
1639
- function callCallbacks(sender, err, cb) {
1640
- if (typeof cb === "function") cb(err);
1641
- for (let i = 0; i < sender._queue.length; i++) {
1642
- const params = sender._queue[i];
1643
- const callback = params[params.length - 1];
1644
- if (typeof callback === "function") callback(err);
1645
- }
1646
- }
1647
- /**
1648
- * Handles a `Sender` error.
1649
- *
1650
- * @param {Sender} sender The `Sender` instance
1651
- * @param {Error} err The error
1652
- * @param {Function} [cb] The first pending callback
1653
- * @private
1654
- */
1655
- function onError(sender, err, cb) {
1656
- callCallbacks(sender, err, cb);
1657
- sender.onerror(err);
1658
- }
1659
- }));
1660
- //#endregion
1661
- //#region ../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/event-target.js
1662
- var require_event_target = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1663
- const { kForOnEventAttribute, kListener } = require_constants();
1664
- const kCode = Symbol("kCode");
1665
- const kData = Symbol("kData");
1666
- const kError = Symbol("kError");
1667
- const kMessage = Symbol("kMessage");
1668
- const kReason = Symbol("kReason");
1669
- const kTarget = Symbol("kTarget");
1670
- const kType = Symbol("kType");
1671
- const kWasClean = Symbol("kWasClean");
1672
- /**
1673
- * Class representing an event.
1674
- */
1675
- var Event = class {
1676
- /**
1677
- * Create a new `Event`.
1678
- *
1679
- * @param {String} type The name of the event
1680
- * @throws {TypeError} If the `type` argument is not specified
1681
- */
1682
- constructor(type) {
1683
- this[kTarget] = null;
1684
- this[kType] = type;
1685
- }
1686
- /**
1687
- * @type {*}
1688
- */
1689
- get target() {
1690
- return this[kTarget];
1691
- }
1692
- /**
1693
- * @type {String}
1694
- */
1695
- get type() {
1696
- return this[kType];
1697
- }
1698
- };
1699
- Object.defineProperty(Event.prototype, "target", { enumerable: true });
1700
- Object.defineProperty(Event.prototype, "type", { enumerable: true });
1701
- /**
1702
- * Class representing a close event.
1703
- *
1704
- * @extends Event
1705
- */
1706
- var CloseEvent = class extends Event {
1707
- /**
1708
- * Create a new `CloseEvent`.
1709
- *
1710
- * @param {String} type The name of the event
1711
- * @param {Object} [options] A dictionary object that allows for setting
1712
- * attributes via object members of the same name
1713
- * @param {Number} [options.code=0] The status code explaining why the
1714
- * connection was closed
1715
- * @param {String} [options.reason=''] A human-readable string explaining why
1716
- * the connection was closed
1717
- * @param {Boolean} [options.wasClean=false] Indicates whether or not the
1718
- * connection was cleanly closed
1719
- */
1720
- constructor(type, options = {}) {
1721
- super(type);
1722
- this[kCode] = options.code === void 0 ? 0 : options.code;
1723
- this[kReason] = options.reason === void 0 ? "" : options.reason;
1724
- this[kWasClean] = options.wasClean === void 0 ? false : options.wasClean;
1725
- }
1726
- /**
1727
- * @type {Number}
1728
- */
1729
- get code() {
1730
- return this[kCode];
1731
- }
1732
- /**
1733
- * @type {String}
1734
- */
1735
- get reason() {
1736
- return this[kReason];
1737
- }
1738
- /**
1739
- * @type {Boolean}
1740
- */
1741
- get wasClean() {
1742
- return this[kWasClean];
1743
- }
1744
- };
1745
- Object.defineProperty(CloseEvent.prototype, "code", { enumerable: true });
1746
- Object.defineProperty(CloseEvent.prototype, "reason", { enumerable: true });
1747
- Object.defineProperty(CloseEvent.prototype, "wasClean", { enumerable: true });
1748
- /**
1749
- * Class representing an error event.
1750
- *
1751
- * @extends Event
1752
- */
1753
- var ErrorEvent = class extends Event {
1754
- /**
1755
- * Create a new `ErrorEvent`.
1756
- *
1757
- * @param {String} type The name of the event
1758
- * @param {Object} [options] A dictionary object that allows for setting
1759
- * attributes via object members of the same name
1760
- * @param {*} [options.error=null] The error that generated this event
1761
- * @param {String} [options.message=''] The error message
1762
- */
1763
- constructor(type, options = {}) {
1764
- super(type);
1765
- this[kError] = options.error === void 0 ? null : options.error;
1766
- this[kMessage] = options.message === void 0 ? "" : options.message;
1767
- }
1768
- /**
1769
- * @type {*}
1770
- */
1771
- get error() {
1772
- return this[kError];
1773
- }
1774
- /**
1775
- * @type {String}
1776
- */
1777
- get message() {
1778
- return this[kMessage];
1779
- }
1780
- };
1781
- Object.defineProperty(ErrorEvent.prototype, "error", { enumerable: true });
1782
- Object.defineProperty(ErrorEvent.prototype, "message", { enumerable: true });
1783
- /**
1784
- * Class representing a message event.
1785
- *
1786
- * @extends Event
1787
- */
1788
- var MessageEvent = class extends Event {
1789
- /**
1790
- * Create a new `MessageEvent`.
1791
- *
1792
- * @param {String} type The name of the event
1793
- * @param {Object} [options] A dictionary object that allows for setting
1794
- * attributes via object members of the same name
1795
- * @param {*} [options.data=null] The message content
1796
- */
1797
- constructor(type, options = {}) {
1798
- super(type);
1799
- this[kData] = options.data === void 0 ? null : options.data;
1800
- }
1801
- /**
1802
- * @type {*}
1803
- */
1804
- get data() {
1805
- return this[kData];
1806
- }
1807
- };
1808
- Object.defineProperty(MessageEvent.prototype, "data", { enumerable: true });
1809
- module.exports = {
1810
- CloseEvent,
1811
- ErrorEvent,
1812
- Event,
1813
- EventTarget: {
1814
- addEventListener(type, handler, options = {}) {
1815
- for (const listener of this.listeners(type)) if (!options[kForOnEventAttribute] && listener[kListener] === handler && !listener[kForOnEventAttribute]) return;
1816
- let wrapper;
1817
- if (type === "message") wrapper = function onMessage(data, isBinary) {
1818
- const event = new MessageEvent("message", { data: isBinary ? data : data.toString() });
1819
- event[kTarget] = this;
1820
- callListener(handler, this, event);
1821
- };
1822
- else if (type === "close") wrapper = function onClose(code, message) {
1823
- const event = new CloseEvent("close", {
1824
- code,
1825
- reason: message.toString(),
1826
- wasClean: this._closeFrameReceived && this._closeFrameSent
1827
- });
1828
- event[kTarget] = this;
1829
- callListener(handler, this, event);
1830
- };
1831
- else if (type === "error") wrapper = function onError(error) {
1832
- const event = new ErrorEvent("error", {
1833
- error,
1834
- message: error.message
1835
- });
1836
- event[kTarget] = this;
1837
- callListener(handler, this, event);
1838
- };
1839
- else if (type === "open") wrapper = function onOpen() {
1840
- const event = new Event("open");
1841
- event[kTarget] = this;
1842
- callListener(handler, this, event);
1843
- };
1844
- else return;
1845
- wrapper[kForOnEventAttribute] = !!options[kForOnEventAttribute];
1846
- wrapper[kListener] = handler;
1847
- if (options.once) this.once(type, wrapper);
1848
- else this.on(type, wrapper);
1849
- },
1850
- removeEventListener(type, handler) {
1851
- for (const listener of this.listeners(type)) if (listener[kListener] === handler && !listener[kForOnEventAttribute]) {
1852
- this.removeListener(type, listener);
1853
- break;
1854
- }
1855
- }
1856
- },
1857
- MessageEvent
1858
- };
1859
- /**
1860
- * Call an event listener
1861
- *
1862
- * @param {(Function|Object)} listener The listener to call
1863
- * @param {*} thisArg The value to use as `this`` when calling the listener
1864
- * @param {Event} event The event to pass to the listener
1865
- * @private
1866
- */
1867
- function callListener(listener, thisArg, event) {
1868
- if (typeof listener === "object" && listener.handleEvent) listener.handleEvent.call(listener, event);
1869
- else listener.call(thisArg, event);
1870
- }
1871
- }));
1872
- //#endregion
1873
- //#region ../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/extension.js
1874
- var require_extension = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1875
- const { tokenChars } = require_validation();
1876
- /**
1877
- * Adds an offer to the map of extension offers or a parameter to the map of
1878
- * parameters.
1879
- *
1880
- * @param {Object} dest The map of extension offers or parameters
1881
- * @param {String} name The extension or parameter name
1882
- * @param {(Object|Boolean|String)} elem The extension parameters or the
1883
- * parameter value
1884
- * @private
1885
- */
1886
- function push(dest, name, elem) {
1887
- if (dest[name] === void 0) dest[name] = [elem];
1888
- else dest[name].push(elem);
1889
- }
1890
- /**
1891
- * Parses the `Sec-WebSocket-Extensions` header into an object.
1892
- *
1893
- * @param {String} header The field value of the header
1894
- * @return {Object} The parsed object
1895
- * @public
1896
- */
1897
- function parse(header) {
1898
- const offers = Object.create(null);
1899
- let params = Object.create(null);
1900
- let mustUnescape = false;
1901
- let isEscaping = false;
1902
- let inQuotes = false;
1903
- let extensionName;
1904
- let paramName;
1905
- let start = -1;
1906
- let code = -1;
1907
- let end = -1;
1908
- let i = 0;
1909
- for (; i < header.length; i++) {
1910
- code = header.charCodeAt(i);
1911
- if (extensionName === void 0) if (end === -1 && tokenChars[code] === 1) {
1912
- if (start === -1) start = i;
1913
- } else if (i !== 0 && (code === 32 || code === 9)) {
1914
- if (end === -1 && start !== -1) end = i;
1915
- } else if (code === 59 || code === 44) {
1916
- if (start === -1) throw new SyntaxError(`Unexpected character at index ${i}`);
1917
- if (end === -1) end = i;
1918
- const name = header.slice(start, end);
1919
- if (code === 44) {
1920
- push(offers, name, params);
1921
- params = Object.create(null);
1922
- } else extensionName = name;
1923
- start = end = -1;
1924
- } else throw new SyntaxError(`Unexpected character at index ${i}`);
1925
- else if (paramName === void 0) if (end === -1 && tokenChars[code] === 1) {
1926
- if (start === -1) start = i;
1927
- } else if (code === 32 || code === 9) {
1928
- if (end === -1 && start !== -1) end = i;
1929
- } else if (code === 59 || code === 44) {
1930
- if (start === -1) throw new SyntaxError(`Unexpected character at index ${i}`);
1931
- if (end === -1) end = i;
1932
- push(params, header.slice(start, end), true);
1933
- if (code === 44) {
1934
- push(offers, extensionName, params);
1935
- params = Object.create(null);
1936
- extensionName = void 0;
1937
- }
1938
- start = end = -1;
1939
- } else if (code === 61 && start !== -1 && end === -1) {
1940
- paramName = header.slice(start, i);
1941
- start = end = -1;
1942
- } else throw new SyntaxError(`Unexpected character at index ${i}`);
1943
- else if (isEscaping) {
1944
- if (tokenChars[code] !== 1) throw new SyntaxError(`Unexpected character at index ${i}`);
1945
- if (start === -1) start = i;
1946
- else if (!mustUnescape) mustUnescape = true;
1947
- isEscaping = false;
1948
- } else if (inQuotes) if (tokenChars[code] === 1) {
1949
- if (start === -1) start = i;
1950
- } else if (code === 34 && start !== -1) {
1951
- inQuotes = false;
1952
- end = i;
1953
- } else if (code === 92) isEscaping = true;
1954
- else throw new SyntaxError(`Unexpected character at index ${i}`);
1955
- else if (code === 34 && header.charCodeAt(i - 1) === 61) inQuotes = true;
1956
- else if (end === -1 && tokenChars[code] === 1) {
1957
- if (start === -1) start = i;
1958
- } else if (start !== -1 && (code === 32 || code === 9)) {
1959
- if (end === -1) end = i;
1960
- } else if (code === 59 || code === 44) {
1961
- if (start === -1) throw new SyntaxError(`Unexpected character at index ${i}`);
1962
- if (end === -1) end = i;
1963
- let value = header.slice(start, end);
1964
- if (mustUnescape) {
1965
- value = value.replace(/\\/g, "");
1966
- mustUnescape = false;
1967
- }
1968
- push(params, paramName, value);
1969
- if (code === 44) {
1970
- push(offers, extensionName, params);
1971
- params = Object.create(null);
1972
- extensionName = void 0;
1973
- }
1974
- paramName = void 0;
1975
- start = end = -1;
1976
- } else throw new SyntaxError(`Unexpected character at index ${i}`);
1977
- }
1978
- if (start === -1 || inQuotes || code === 32 || code === 9) throw new SyntaxError("Unexpected end of input");
1979
- if (end === -1) end = i;
1980
- const token = header.slice(start, end);
1981
- if (extensionName === void 0) push(offers, token, params);
1982
- else {
1983
- if (paramName === void 0) push(params, token, true);
1984
- else if (mustUnescape) push(params, paramName, token.replace(/\\/g, ""));
1985
- else push(params, paramName, token);
1986
- push(offers, extensionName, params);
1987
- }
1988
- return offers;
1989
- }
1990
- /**
1991
- * Builds the `Sec-WebSocket-Extensions` header field value.
1992
- *
1993
- * @param {Object} extensions The map of extensions and parameters to format
1994
- * @return {String} A string representing the given object
1995
- * @public
1996
- */
1997
- function format(extensions) {
1998
- return Object.keys(extensions).map((extension) => {
1999
- let configurations = extensions[extension];
2000
- if (!Array.isArray(configurations)) configurations = [configurations];
2001
- return configurations.map((params) => {
2002
- return [extension].concat(Object.keys(params).map((k) => {
2003
- let values = params[k];
2004
- if (!Array.isArray(values)) values = [values];
2005
- return values.map((v) => v === true ? k : `${k}=${v}`).join("; ");
2006
- })).join("; ");
2007
- }).join(", ");
2008
- }).join(", ");
2009
- }
2010
- module.exports = {
2011
- format,
2012
- parse
2013
- };
2014
- }));
2015
- //#endregion
2016
- //#region ../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/websocket.js
2017
- var require_websocket = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2018
- const EventEmitter$1 = __require("events");
2019
- const https = __require("https");
2020
- const http$1 = __require("http");
2021
- const net = __require("net");
2022
- const tls = __require("tls");
2023
- const { randomBytes: randomBytes$1, createHash: createHash$2 } = __require("crypto");
2024
- const { Duplex: Duplex$2, Readable } = __require("stream");
2025
- const { URL: URL$1 } = __require("url");
2026
- const PerMessageDeflate = require_permessage_deflate();
2027
- const Receiver = require_receiver();
2028
- const Sender = require_sender();
2029
- const { isBlob } = require_validation();
2030
- const { BINARY_TYPES, CLOSE_TIMEOUT, EMPTY_BUFFER, GUID, kForOnEventAttribute, kListener, kStatusCode, kWebSocket, NOOP } = require_constants();
2031
- const { EventTarget: { addEventListener, removeEventListener } } = require_event_target();
2032
- const { format, parse } = require_extension();
2033
- const { toBuffer } = require_buffer_util();
2034
- const kAborted = Symbol("kAborted");
2035
- const protocolVersions = [8, 13];
2036
- const readyStates = [
2037
- "CONNECTING",
2038
- "OPEN",
2039
- "CLOSING",
2040
- "CLOSED"
2041
- ];
2042
- const subprotocolRegex = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
2043
- /**
2044
- * Class representing a WebSocket.
2045
- *
2046
- * @extends EventEmitter
2047
- */
2048
- var WebSocket = class WebSocket extends EventEmitter$1 {
2049
- /**
2050
- * Create a new `WebSocket`.
2051
- *
2052
- * @param {(String|URL)} address The URL to which to connect
2053
- * @param {(String|String[])} [protocols] The subprotocols
2054
- * @param {Object} [options] Connection options
2055
- */
2056
- constructor(address, protocols, options) {
2057
- super();
2058
- this._binaryType = BINARY_TYPES[0];
2059
- this._closeCode = 1006;
2060
- this._closeFrameReceived = false;
2061
- this._closeFrameSent = false;
2062
- this._closeMessage = EMPTY_BUFFER;
2063
- this._closeTimer = null;
2064
- this._errorEmitted = false;
2065
- this._extensions = {};
2066
- this._paused = false;
2067
- this._protocol = "";
2068
- this._readyState = WebSocket.CONNECTING;
2069
- this._receiver = null;
2070
- this._sender = null;
2071
- this._socket = null;
2072
- if (address !== null) {
2073
- this._bufferedAmount = 0;
2074
- this._isServer = false;
2075
- this._redirects = 0;
2076
- if (protocols === void 0) protocols = [];
2077
- else if (!Array.isArray(protocols)) if (typeof protocols === "object" && protocols !== null) {
2078
- options = protocols;
2079
- protocols = [];
2080
- } else protocols = [protocols];
2081
- initAsClient(this, address, protocols, options);
2082
- } else {
2083
- this._autoPong = options.autoPong;
2084
- this._closeTimeout = options.closeTimeout;
2085
- this._isServer = true;
2086
- }
2087
- }
2088
- /**
2089
- * For historical reasons, the custom "nodebuffer" type is used by the default
2090
- * instead of "blob".
2091
- *
2092
- * @type {String}
2093
- */
2094
- get binaryType() {
2095
- return this._binaryType;
2096
- }
2097
- set binaryType(type) {
2098
- if (!BINARY_TYPES.includes(type)) return;
2099
- this._binaryType = type;
2100
- if (this._receiver) this._receiver._binaryType = type;
2101
- }
2102
- /**
2103
- * @type {Number}
2104
- */
2105
- get bufferedAmount() {
2106
- if (!this._socket) return this._bufferedAmount;
2107
- return this._socket._writableState.length + this._sender._bufferedBytes;
2108
- }
2109
- /**
2110
- * @type {String}
2111
- */
2112
- get extensions() {
2113
- return Object.keys(this._extensions).join();
2114
- }
2115
- /**
2116
- * @type {Boolean}
2117
- */
2118
- get isPaused() {
2119
- return this._paused;
2120
- }
2121
- /**
2122
- * @type {Function}
2123
- */
2124
- /* istanbul ignore next */
2125
- get onclose() {
2126
- return null;
2127
- }
2128
- /**
2129
- * @type {Function}
2130
- */
2131
- /* istanbul ignore next */
2132
- get onerror() {
2133
- return null;
2134
- }
2135
- /**
2136
- * @type {Function}
2137
- */
2138
- /* istanbul ignore next */
2139
- get onopen() {
2140
- return null;
2141
- }
2142
- /**
2143
- * @type {Function}
2144
- */
2145
- /* istanbul ignore next */
2146
- get onmessage() {
2147
- return null;
2148
- }
2149
- /**
2150
- * @type {String}
2151
- */
2152
- get protocol() {
2153
- return this._protocol;
2154
- }
2155
- /**
2156
- * @type {Number}
2157
- */
2158
- get readyState() {
2159
- return this._readyState;
2160
- }
2161
- /**
2162
- * @type {String}
2163
- */
2164
- get url() {
2165
- return this._url;
2166
- }
2167
- /**
2168
- * Set up the socket and the internal resources.
2169
- *
2170
- * @param {Duplex} socket The network socket between the server and client
2171
- * @param {Buffer} head The first packet of the upgraded stream
2172
- * @param {Object} options Options object
2173
- * @param {Boolean} [options.allowSynchronousEvents=false] Specifies whether
2174
- * any of the `'message'`, `'ping'`, and `'pong'` events can be emitted
2175
- * multiple times in the same tick
2176
- * @param {Function} [options.generateMask] The function used to generate the
2177
- * masking key
2178
- * @param {Number} [options.maxPayload=0] The maximum allowed message size
2179
- * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
2180
- * not to skip UTF-8 validation for text and close messages
2181
- * @private
2182
- */
2183
- setSocket(socket, head, options) {
2184
- const receiver = new Receiver({
2185
- allowSynchronousEvents: options.allowSynchronousEvents,
2186
- binaryType: this.binaryType,
2187
- extensions: this._extensions,
2188
- isServer: this._isServer,
2189
- maxPayload: options.maxPayload,
2190
- skipUTF8Validation: options.skipUTF8Validation
2191
- });
2192
- const sender = new Sender(socket, this._extensions, options.generateMask);
2193
- this._receiver = receiver;
2194
- this._sender = sender;
2195
- this._socket = socket;
2196
- receiver[kWebSocket] = this;
2197
- sender[kWebSocket] = this;
2198
- socket[kWebSocket] = this;
2199
- receiver.on("conclude", receiverOnConclude);
2200
- receiver.on("drain", receiverOnDrain);
2201
- receiver.on("error", receiverOnError);
2202
- receiver.on("message", receiverOnMessage);
2203
- receiver.on("ping", receiverOnPing);
2204
- receiver.on("pong", receiverOnPong);
2205
- sender.onerror = senderOnError;
2206
- if (socket.setTimeout) socket.setTimeout(0);
2207
- if (socket.setNoDelay) socket.setNoDelay();
2208
- if (head.length > 0) socket.unshift(head);
2209
- socket.on("close", socketOnClose);
2210
- socket.on("data", socketOnData);
2211
- socket.on("end", socketOnEnd);
2212
- socket.on("error", socketOnError);
2213
- this._readyState = WebSocket.OPEN;
2214
- this.emit("open");
2215
- }
2216
- /**
2217
- * Emit the `'close'` event.
2218
- *
2219
- * @private
2220
- */
2221
- emitClose() {
2222
- if (!this._socket) {
2223
- this._readyState = WebSocket.CLOSED;
2224
- this.emit("close", this._closeCode, this._closeMessage);
2225
- return;
2226
- }
2227
- if (this._extensions[PerMessageDeflate.extensionName]) this._extensions[PerMessageDeflate.extensionName].cleanup();
2228
- this._receiver.removeAllListeners();
2229
- this._readyState = WebSocket.CLOSED;
2230
- this.emit("close", this._closeCode, this._closeMessage);
2231
- }
2232
- /**
2233
- * Start a closing handshake.
2234
- *
2235
- * +----------+ +-----------+ +----------+
2236
- * - - -|ws.close()|-->|close frame|-->|ws.close()|- - -
2237
- * | +----------+ +-----------+ +----------+ |
2238
- * +----------+ +-----------+ |
2239
- * CLOSING |ws.close()|<--|close frame|<--+-----+ CLOSING
2240
- * +----------+ +-----------+ |
2241
- * | | | +---+ |
2242
- * +------------------------+-->|fin| - - - -
2243
- * | +---+ | +---+
2244
- * - - - - -|fin|<---------------------+
2245
- * +---+
2246
- *
2247
- * @param {Number} [code] Status code explaining why the connection is closing
2248
- * @param {(String|Buffer)} [data] The reason why the connection is
2249
- * closing
2250
- * @public
2251
- */
2252
- close(code, data) {
2253
- if (this.readyState === WebSocket.CLOSED) return;
2254
- if (this.readyState === WebSocket.CONNECTING) {
2255
- abortHandshake(this, this._req, "WebSocket was closed before the connection was established");
2256
- return;
2257
- }
2258
- if (this.readyState === WebSocket.CLOSING) {
2259
- if (this._closeFrameSent && (this._closeFrameReceived || this._receiver._writableState.errorEmitted)) this._socket.end();
2260
- return;
2261
- }
2262
- this._readyState = WebSocket.CLOSING;
2263
- this._sender.close(code, data, !this._isServer, (err) => {
2264
- if (err) return;
2265
- this._closeFrameSent = true;
2266
- if (this._closeFrameReceived || this._receiver._writableState.errorEmitted) this._socket.end();
2267
- });
2268
- setCloseTimer(this);
2269
- }
2270
- /**
2271
- * Pause the socket.
2272
- *
2273
- * @public
2274
- */
2275
- pause() {
2276
- if (this.readyState === WebSocket.CONNECTING || this.readyState === WebSocket.CLOSED) return;
2277
- this._paused = true;
2278
- this._socket.pause();
2279
- }
2280
- /**
2281
- * Send a ping.
2282
- *
2283
- * @param {*} [data] The data to send
2284
- * @param {Boolean} [mask] Indicates whether or not to mask `data`
2285
- * @param {Function} [cb] Callback which is executed when the ping is sent
2286
- * @public
2287
- */
2288
- ping(data, mask, cb) {
2289
- if (this.readyState === WebSocket.CONNECTING) throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");
2290
- if (typeof data === "function") {
2291
- cb = data;
2292
- data = mask = void 0;
2293
- } else if (typeof mask === "function") {
2294
- cb = mask;
2295
- mask = void 0;
2296
- }
2297
- if (typeof data === "number") data = data.toString();
2298
- if (this.readyState !== WebSocket.OPEN) {
2299
- sendAfterClose(this, data, cb);
2300
- return;
2301
- }
2302
- if (mask === void 0) mask = !this._isServer;
2303
- this._sender.ping(data || EMPTY_BUFFER, mask, cb);
2304
- }
2305
- /**
2306
- * Send a pong.
2307
- *
2308
- * @param {*} [data] The data to send
2309
- * @param {Boolean} [mask] Indicates whether or not to mask `data`
2310
- * @param {Function} [cb] Callback which is executed when the pong is sent
2311
- * @public
2312
- */
2313
- pong(data, mask, cb) {
2314
- if (this.readyState === WebSocket.CONNECTING) throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");
2315
- if (typeof data === "function") {
2316
- cb = data;
2317
- data = mask = void 0;
2318
- } else if (typeof mask === "function") {
2319
- cb = mask;
2320
- mask = void 0;
2321
- }
2322
- if (typeof data === "number") data = data.toString();
2323
- if (this.readyState !== WebSocket.OPEN) {
2324
- sendAfterClose(this, data, cb);
2325
- return;
2326
- }
2327
- if (mask === void 0) mask = !this._isServer;
2328
- this._sender.pong(data || EMPTY_BUFFER, mask, cb);
2329
- }
2330
- /**
2331
- * Resume the socket.
2332
- *
2333
- * @public
2334
- */
2335
- resume() {
2336
- if (this.readyState === WebSocket.CONNECTING || this.readyState === WebSocket.CLOSED) return;
2337
- this._paused = false;
2338
- if (!this._receiver._writableState.needDrain) this._socket.resume();
2339
- }
2340
- /**
2341
- * Send a data message.
2342
- *
2343
- * @param {*} data The message to send
2344
- * @param {Object} [options] Options object
2345
- * @param {Boolean} [options.binary] Specifies whether `data` is binary or
2346
- * text
2347
- * @param {Boolean} [options.compress] Specifies whether or not to compress
2348
- * `data`
2349
- * @param {Boolean} [options.fin=true] Specifies whether the fragment is the
2350
- * last one
2351
- * @param {Boolean} [options.mask] Specifies whether or not to mask `data`
2352
- * @param {Function} [cb] Callback which is executed when data is written out
2353
- * @public
2354
- */
2355
- send(data, options, cb) {
2356
- if (this.readyState === WebSocket.CONNECTING) throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");
2357
- if (typeof options === "function") {
2358
- cb = options;
2359
- options = {};
2360
- }
2361
- if (typeof data === "number") data = data.toString();
2362
- if (this.readyState !== WebSocket.OPEN) {
2363
- sendAfterClose(this, data, cb);
2364
- return;
2365
- }
2366
- const opts = {
2367
- binary: typeof data !== "string",
2368
- mask: !this._isServer,
2369
- compress: true,
2370
- fin: true,
2371
- ...options
2372
- };
2373
- if (!this._extensions[PerMessageDeflate.extensionName]) opts.compress = false;
2374
- this._sender.send(data || EMPTY_BUFFER, opts, cb);
2375
- }
2376
- /**
2377
- * Forcibly close the connection.
2378
- *
2379
- * @public
2380
- */
2381
- terminate() {
2382
- if (this.readyState === WebSocket.CLOSED) return;
2383
- if (this.readyState === WebSocket.CONNECTING) {
2384
- abortHandshake(this, this._req, "WebSocket was closed before the connection was established");
2385
- return;
2386
- }
2387
- if (this._socket) {
2388
- this._readyState = WebSocket.CLOSING;
2389
- this._socket.destroy();
2390
- }
2391
- }
2392
- };
2393
- /**
2394
- * @constant {Number} CONNECTING
2395
- * @memberof WebSocket
2396
- */
2397
- Object.defineProperty(WebSocket, "CONNECTING", {
2398
- enumerable: true,
2399
- value: readyStates.indexOf("CONNECTING")
2400
- });
2401
- /**
2402
- * @constant {Number} CONNECTING
2403
- * @memberof WebSocket.prototype
2404
- */
2405
- Object.defineProperty(WebSocket.prototype, "CONNECTING", {
2406
- enumerable: true,
2407
- value: readyStates.indexOf("CONNECTING")
2408
- });
2409
- /**
2410
- * @constant {Number} OPEN
2411
- * @memberof WebSocket
2412
- */
2413
- Object.defineProperty(WebSocket, "OPEN", {
2414
- enumerable: true,
2415
- value: readyStates.indexOf("OPEN")
2416
- });
2417
- /**
2418
- * @constant {Number} OPEN
2419
- * @memberof WebSocket.prototype
2420
- */
2421
- Object.defineProperty(WebSocket.prototype, "OPEN", {
2422
- enumerable: true,
2423
- value: readyStates.indexOf("OPEN")
2424
- });
2425
- /**
2426
- * @constant {Number} CLOSING
2427
- * @memberof WebSocket
2428
- */
2429
- Object.defineProperty(WebSocket, "CLOSING", {
2430
- enumerable: true,
2431
- value: readyStates.indexOf("CLOSING")
2432
- });
2433
- /**
2434
- * @constant {Number} CLOSING
2435
- * @memberof WebSocket.prototype
2436
- */
2437
- Object.defineProperty(WebSocket.prototype, "CLOSING", {
2438
- enumerable: true,
2439
- value: readyStates.indexOf("CLOSING")
2440
- });
2441
- /**
2442
- * @constant {Number} CLOSED
2443
- * @memberof WebSocket
2444
- */
2445
- Object.defineProperty(WebSocket, "CLOSED", {
2446
- enumerable: true,
2447
- value: readyStates.indexOf("CLOSED")
2448
- });
2449
- /**
2450
- * @constant {Number} CLOSED
2451
- * @memberof WebSocket.prototype
2452
- */
2453
- Object.defineProperty(WebSocket.prototype, "CLOSED", {
2454
- enumerable: true,
2455
- value: readyStates.indexOf("CLOSED")
2456
- });
2457
- [
2458
- "binaryType",
2459
- "bufferedAmount",
2460
- "extensions",
2461
- "isPaused",
2462
- "protocol",
2463
- "readyState",
2464
- "url"
2465
- ].forEach((property) => {
2466
- Object.defineProperty(WebSocket.prototype, property, { enumerable: true });
2467
- });
2468
- [
2469
- "open",
2470
- "error",
2471
- "close",
2472
- "message"
2473
- ].forEach((method) => {
2474
- Object.defineProperty(WebSocket.prototype, `on${method}`, {
2475
- enumerable: true,
2476
- get() {
2477
- for (const listener of this.listeners(method)) if (listener[kForOnEventAttribute]) return listener[kListener];
2478
- return null;
2479
- },
2480
- set(handler) {
2481
- for (const listener of this.listeners(method)) if (listener[kForOnEventAttribute]) {
2482
- this.removeListener(method, listener);
2483
- break;
2484
- }
2485
- if (typeof handler !== "function") return;
2486
- this.addEventListener(method, handler, { [kForOnEventAttribute]: true });
2487
- }
2488
- });
2489
- });
2490
- WebSocket.prototype.addEventListener = addEventListener;
2491
- WebSocket.prototype.removeEventListener = removeEventListener;
2492
- module.exports = WebSocket;
2493
- /**
2494
- * Initialize a WebSocket client.
2495
- *
2496
- * @param {WebSocket} websocket The client to initialize
2497
- * @param {(String|URL)} address The URL to which to connect
2498
- * @param {Array} protocols The subprotocols
2499
- * @param {Object} [options] Connection options
2500
- * @param {Boolean} [options.allowSynchronousEvents=true] Specifies whether any
2501
- * of the `'message'`, `'ping'`, and `'pong'` events can be emitted multiple
2502
- * times in the same tick
2503
- * @param {Boolean} [options.autoPong=true] Specifies whether or not to
2504
- * automatically send a pong in response to a ping
2505
- * @param {Number} [options.closeTimeout=30000] Duration in milliseconds to wait
2506
- * for the closing handshake to finish after `websocket.close()` is called
2507
- * @param {Function} [options.finishRequest] A function which can be used to
2508
- * customize the headers of each http request before it is sent
2509
- * @param {Boolean} [options.followRedirects=false] Whether or not to follow
2510
- * redirects
2511
- * @param {Function} [options.generateMask] The function used to generate the
2512
- * masking key
2513
- * @param {Number} [options.handshakeTimeout] Timeout in milliseconds for the
2514
- * handshake request
2515
- * @param {Number} [options.maxPayload=104857600] The maximum allowed message
2516
- * size
2517
- * @param {Number} [options.maxRedirects=10] The maximum number of redirects
2518
- * allowed
2519
- * @param {String} [options.origin] Value of the `Origin` or
2520
- * `Sec-WebSocket-Origin` header
2521
- * @param {(Boolean|Object)} [options.perMessageDeflate=true] Enable/disable
2522
- * permessage-deflate
2523
- * @param {Number} [options.protocolVersion=13] Value of the
2524
- * `Sec-WebSocket-Version` header
2525
- * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
2526
- * not to skip UTF-8 validation for text and close messages
2527
- * @private
2528
- */
2529
- function initAsClient(websocket, address, protocols, options) {
2530
- const opts = {
2531
- allowSynchronousEvents: true,
2532
- autoPong: true,
2533
- closeTimeout: CLOSE_TIMEOUT,
2534
- protocolVersion: protocolVersions[1],
2535
- maxPayload: 100 * 1024 * 1024,
2536
- skipUTF8Validation: false,
2537
- perMessageDeflate: true,
2538
- followRedirects: false,
2539
- maxRedirects: 10,
2540
- ...options,
2541
- socketPath: void 0,
2542
- hostname: void 0,
2543
- protocol: void 0,
2544
- timeout: void 0,
2545
- method: "GET",
2546
- host: void 0,
2547
- path: void 0,
2548
- port: void 0
2549
- };
2550
- websocket._autoPong = opts.autoPong;
2551
- websocket._closeTimeout = opts.closeTimeout;
2552
- if (!protocolVersions.includes(opts.protocolVersion)) throw new RangeError(`Unsupported protocol version: ${opts.protocolVersion} (supported versions: ${protocolVersions.join(", ")})`);
2553
- let parsedUrl;
2554
- if (address instanceof URL$1) parsedUrl = address;
2555
- else try {
2556
- parsedUrl = new URL$1(address);
2557
- } catch (e) {
2558
- throw new SyntaxError(`Invalid URL: ${address}`);
2559
- }
2560
- if (parsedUrl.protocol === "http:") parsedUrl.protocol = "ws:";
2561
- else if (parsedUrl.protocol === "https:") parsedUrl.protocol = "wss:";
2562
- websocket._url = parsedUrl.href;
2563
- const isSecure = parsedUrl.protocol === "wss:";
2564
- const isIpcUrl = parsedUrl.protocol === "ws+unix:";
2565
- let invalidUrlMessage;
2566
- if (parsedUrl.protocol !== "ws:" && !isSecure && !isIpcUrl) invalidUrlMessage = "The URL's protocol must be one of \"ws:\", \"wss:\", \"http:\", \"https:\", or \"ws+unix:\"";
2567
- else if (isIpcUrl && !parsedUrl.pathname) invalidUrlMessage = "The URL's pathname is empty";
2568
- else if (parsedUrl.hash) invalidUrlMessage = "The URL contains a fragment identifier";
2569
- if (invalidUrlMessage) {
2570
- const err = new SyntaxError(invalidUrlMessage);
2571
- if (websocket._redirects === 0) throw err;
2572
- else {
2573
- emitErrorAndClose(websocket, err);
2574
- return;
2575
- }
2576
- }
2577
- const defaultPort = isSecure ? 443 : 80;
2578
- const key = randomBytes$1(16).toString("base64");
2579
- const request = isSecure ? https.request : http$1.request;
2580
- const protocolSet = /* @__PURE__ */ new Set();
2581
- let perMessageDeflate;
2582
- opts.createConnection = opts.createConnection || (isSecure ? tlsConnect : netConnect);
2583
- opts.defaultPort = opts.defaultPort || defaultPort;
2584
- opts.port = parsedUrl.port || defaultPort;
2585
- opts.host = parsedUrl.hostname.startsWith("[") ? parsedUrl.hostname.slice(1, -1) : parsedUrl.hostname;
2586
- opts.headers = {
2587
- ...opts.headers,
2588
- "Sec-WebSocket-Version": opts.protocolVersion,
2589
- "Sec-WebSocket-Key": key,
2590
- Connection: "Upgrade",
2591
- Upgrade: "websocket"
2592
- };
2593
- opts.path = parsedUrl.pathname + parsedUrl.search;
2594
- opts.timeout = opts.handshakeTimeout;
2595
- if (opts.perMessageDeflate) {
2596
- perMessageDeflate = new PerMessageDeflate(opts.perMessageDeflate !== true ? opts.perMessageDeflate : {}, false, opts.maxPayload);
2597
- opts.headers["Sec-WebSocket-Extensions"] = format({ [PerMessageDeflate.extensionName]: perMessageDeflate.offer() });
2598
- }
2599
- if (protocols.length) {
2600
- for (const protocol of protocols) {
2601
- if (typeof protocol !== "string" || !subprotocolRegex.test(protocol) || protocolSet.has(protocol)) throw new SyntaxError("An invalid or duplicated subprotocol was specified");
2602
- protocolSet.add(protocol);
2603
- }
2604
- opts.headers["Sec-WebSocket-Protocol"] = protocols.join(",");
2605
- }
2606
- if (opts.origin) if (opts.protocolVersion < 13) opts.headers["Sec-WebSocket-Origin"] = opts.origin;
2607
- else opts.headers.Origin = opts.origin;
2608
- if (parsedUrl.username || parsedUrl.password) opts.auth = `${parsedUrl.username}:${parsedUrl.password}`;
2609
- if (isIpcUrl) {
2610
- const parts = opts.path.split(":");
2611
- opts.socketPath = parts[0];
2612
- opts.path = parts[1];
2613
- }
2614
- let req;
2615
- if (opts.followRedirects) {
2616
- if (websocket._redirects === 0) {
2617
- websocket._originalIpc = isIpcUrl;
2618
- websocket._originalSecure = isSecure;
2619
- websocket._originalHostOrSocketPath = isIpcUrl ? opts.socketPath : parsedUrl.host;
2620
- const headers = options && options.headers;
2621
- options = {
2622
- ...options,
2623
- headers: {}
2624
- };
2625
- if (headers) for (const [key, value] of Object.entries(headers)) options.headers[key.toLowerCase()] = value;
2626
- } else if (websocket.listenerCount("redirect") === 0) {
2627
- const isSameHost = isIpcUrl ? websocket._originalIpc ? opts.socketPath === websocket._originalHostOrSocketPath : false : websocket._originalIpc ? false : parsedUrl.host === websocket._originalHostOrSocketPath;
2628
- if (!isSameHost || websocket._originalSecure && !isSecure) {
2629
- delete opts.headers.authorization;
2630
- delete opts.headers.cookie;
2631
- if (!isSameHost) delete opts.headers.host;
2632
- opts.auth = void 0;
2633
- }
2634
- }
2635
- if (opts.auth && !options.headers.authorization) options.headers.authorization = "Basic " + Buffer.from(opts.auth).toString("base64");
2636
- req = websocket._req = request(opts);
2637
- if (websocket._redirects) websocket.emit("redirect", websocket.url, req);
2638
- } else req = websocket._req = request(opts);
2639
- if (opts.timeout) req.on("timeout", () => {
2640
- abortHandshake(websocket, req, "Opening handshake has timed out");
2641
- });
2642
- req.on("error", (err) => {
2643
- if (req === null || req[kAborted]) return;
2644
- req = websocket._req = null;
2645
- emitErrorAndClose(websocket, err);
2646
- });
2647
- req.on("response", (res) => {
2648
- const location = res.headers.location;
2649
- const statusCode = res.statusCode;
2650
- if (location && opts.followRedirects && statusCode >= 300 && statusCode < 400) {
2651
- if (++websocket._redirects > opts.maxRedirects) {
2652
- abortHandshake(websocket, req, "Maximum redirects exceeded");
2653
- return;
2654
- }
2655
- req.abort();
2656
- let addr;
2657
- try {
2658
- addr = new URL$1(location, address);
2659
- } catch (e) {
2660
- emitErrorAndClose(websocket, /* @__PURE__ */ new SyntaxError(`Invalid URL: ${location}`));
2661
- return;
2662
- }
2663
- initAsClient(websocket, addr, protocols, options);
2664
- } else if (!websocket.emit("unexpected-response", req, res)) abortHandshake(websocket, req, `Unexpected server response: ${res.statusCode}`);
2665
- });
2666
- req.on("upgrade", (res, socket, head) => {
2667
- websocket.emit("upgrade", res);
2668
- if (websocket.readyState !== WebSocket.CONNECTING) return;
2669
- req = websocket._req = null;
2670
- const upgrade = res.headers.upgrade;
2671
- if (upgrade === void 0 || upgrade.toLowerCase() !== "websocket") {
2672
- abortHandshake(websocket, socket, "Invalid Upgrade header");
2673
- return;
2674
- }
2675
- const digest = createHash$2("sha1").update(key + GUID).digest("base64");
2676
- if (res.headers["sec-websocket-accept"] !== digest) {
2677
- abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
2678
- return;
2679
- }
2680
- const serverProt = res.headers["sec-websocket-protocol"];
2681
- let protError;
2682
- if (serverProt !== void 0) {
2683
- if (!protocolSet.size) protError = "Server sent a subprotocol but none was requested";
2684
- else if (!protocolSet.has(serverProt)) protError = "Server sent an invalid subprotocol";
2685
- } else if (protocolSet.size) protError = "Server sent no subprotocol";
2686
- if (protError) {
2687
- abortHandshake(websocket, socket, protError);
2688
- return;
2689
- }
2690
- if (serverProt) websocket._protocol = serverProt;
2691
- const secWebSocketExtensions = res.headers["sec-websocket-extensions"];
2692
- if (secWebSocketExtensions !== void 0) {
2693
- if (!perMessageDeflate) {
2694
- abortHandshake(websocket, socket, "Server sent a Sec-WebSocket-Extensions header but no extension was requested");
2695
- return;
2696
- }
2697
- let extensions;
2698
- try {
2699
- extensions = parse(secWebSocketExtensions);
2700
- } catch (err) {
2701
- abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Extensions header");
2702
- return;
2703
- }
2704
- const extensionNames = Object.keys(extensions);
2705
- if (extensionNames.length !== 1 || extensionNames[0] !== PerMessageDeflate.extensionName) {
2706
- abortHandshake(websocket, socket, "Server indicated an extension that was not requested");
2707
- return;
2708
- }
2709
- try {
2710
- perMessageDeflate.accept(extensions[PerMessageDeflate.extensionName]);
2711
- } catch (err) {
2712
- abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Extensions header");
2713
- return;
2714
- }
2715
- websocket._extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
2716
- }
2717
- websocket.setSocket(socket, head, {
2718
- allowSynchronousEvents: opts.allowSynchronousEvents,
2719
- generateMask: opts.generateMask,
2720
- maxPayload: opts.maxPayload,
2721
- skipUTF8Validation: opts.skipUTF8Validation
2722
- });
2723
- });
2724
- if (opts.finishRequest) opts.finishRequest(req, websocket);
2725
- else req.end();
2726
- }
2727
- /**
2728
- * Emit the `'error'` and `'close'` events.
2729
- *
2730
- * @param {WebSocket} websocket The WebSocket instance
2731
- * @param {Error} The error to emit
2732
- * @private
2733
- */
2734
- function emitErrorAndClose(websocket, err) {
2735
- websocket._readyState = WebSocket.CLOSING;
2736
- websocket._errorEmitted = true;
2737
- websocket.emit("error", err);
2738
- websocket.emitClose();
2739
- }
2740
- /**
2741
- * Create a `net.Socket` and initiate a connection.
2742
- *
2743
- * @param {Object} options Connection options
2744
- * @return {net.Socket} The newly created socket used to start the connection
2745
- * @private
2746
- */
2747
- function netConnect(options) {
2748
- options.path = options.socketPath;
2749
- return net.connect(options);
2750
- }
2751
- /**
2752
- * Create a `tls.TLSSocket` and initiate a connection.
2753
- *
2754
- * @param {Object} options Connection options
2755
- * @return {tls.TLSSocket} The newly created socket used to start the connection
2756
- * @private
2757
- */
2758
- function tlsConnect(options) {
2759
- options.path = void 0;
2760
- if (!options.servername && options.servername !== "") options.servername = net.isIP(options.host) ? "" : options.host;
2761
- return tls.connect(options);
2762
- }
2763
- /**
2764
- * Abort the handshake and emit an error.
2765
- *
2766
- * @param {WebSocket} websocket The WebSocket instance
2767
- * @param {(http.ClientRequest|net.Socket|tls.Socket)} stream The request to
2768
- * abort or the socket to destroy
2769
- * @param {String} message The error message
2770
- * @private
2771
- */
2772
- function abortHandshake(websocket, stream, message) {
2773
- websocket._readyState = WebSocket.CLOSING;
2774
- const err = new Error(message);
2775
- Error.captureStackTrace(err, abortHandshake);
2776
- if (stream.setHeader) {
2777
- stream[kAborted] = true;
2778
- stream.abort();
2779
- if (stream.socket && !stream.socket.destroyed) stream.socket.destroy();
2780
- process.nextTick(emitErrorAndClose, websocket, err);
2781
- } else {
2782
- stream.destroy(err);
2783
- stream.once("error", websocket.emit.bind(websocket, "error"));
2784
- stream.once("close", websocket.emitClose.bind(websocket));
2785
- }
2786
- }
2787
- /**
2788
- * Handle cases where the `ping()`, `pong()`, or `send()` methods are called
2789
- * when the `readyState` attribute is `CLOSING` or `CLOSED`.
2790
- *
2791
- * @param {WebSocket} websocket The WebSocket instance
2792
- * @param {*} [data] The data to send
2793
- * @param {Function} [cb] Callback
2794
- * @private
2795
- */
2796
- function sendAfterClose(websocket, data, cb) {
2797
- if (data) {
2798
- const length = isBlob(data) ? data.size : toBuffer(data).length;
2799
- if (websocket._socket) websocket._sender._bufferedBytes += length;
2800
- else websocket._bufferedAmount += length;
2801
- }
2802
- if (cb) {
2803
- const err = /* @__PURE__ */ new Error(`WebSocket is not open: readyState ${websocket.readyState} (${readyStates[websocket.readyState]})`);
2804
- process.nextTick(cb, err);
2805
- }
2806
- }
2807
- /**
2808
- * The listener of the `Receiver` `'conclude'` event.
2809
- *
2810
- * @param {Number} code The status code
2811
- * @param {Buffer} reason The reason for closing
2812
- * @private
2813
- */
2814
- function receiverOnConclude(code, reason) {
2815
- const websocket = this[kWebSocket];
2816
- websocket._closeFrameReceived = true;
2817
- websocket._closeMessage = reason;
2818
- websocket._closeCode = code;
2819
- if (websocket._socket[kWebSocket] === void 0) return;
2820
- websocket._socket.removeListener("data", socketOnData);
2821
- process.nextTick(resume, websocket._socket);
2822
- if (code === 1005) websocket.close();
2823
- else websocket.close(code, reason);
2824
- }
2825
- /**
2826
- * The listener of the `Receiver` `'drain'` event.
2827
- *
2828
- * @private
2829
- */
2830
- function receiverOnDrain() {
2831
- const websocket = this[kWebSocket];
2832
- if (!websocket.isPaused) websocket._socket.resume();
2833
- }
2834
- /**
2835
- * The listener of the `Receiver` `'error'` event.
2836
- *
2837
- * @param {(RangeError|Error)} err The emitted error
2838
- * @private
2839
- */
2840
- function receiverOnError(err) {
2841
- const websocket = this[kWebSocket];
2842
- if (websocket._socket[kWebSocket] !== void 0) {
2843
- websocket._socket.removeListener("data", socketOnData);
2844
- process.nextTick(resume, websocket._socket);
2845
- websocket.close(err[kStatusCode]);
2846
- }
2847
- if (!websocket._errorEmitted) {
2848
- websocket._errorEmitted = true;
2849
- websocket.emit("error", err);
2850
- }
2851
- }
2852
- /**
2853
- * The listener of the `Receiver` `'finish'` event.
2854
- *
2855
- * @private
2856
- */
2857
- function receiverOnFinish() {
2858
- this[kWebSocket].emitClose();
2859
- }
2860
- /**
2861
- * The listener of the `Receiver` `'message'` event.
2862
- *
2863
- * @param {Buffer|ArrayBuffer|Buffer[])} data The message
2864
- * @param {Boolean} isBinary Specifies whether the message is binary or not
2865
- * @private
2866
- */
2867
- function receiverOnMessage(data, isBinary) {
2868
- this[kWebSocket].emit("message", data, isBinary);
2869
- }
2870
- /**
2871
- * The listener of the `Receiver` `'ping'` event.
2872
- *
2873
- * @param {Buffer} data The data included in the ping frame
2874
- * @private
2875
- */
2876
- function receiverOnPing(data) {
2877
- const websocket = this[kWebSocket];
2878
- if (websocket._autoPong) websocket.pong(data, !this._isServer, NOOP);
2879
- websocket.emit("ping", data);
2880
- }
2881
- /**
2882
- * The listener of the `Receiver` `'pong'` event.
2883
- *
2884
- * @param {Buffer} data The data included in the pong frame
2885
- * @private
2886
- */
2887
- function receiverOnPong(data) {
2888
- this[kWebSocket].emit("pong", data);
2889
- }
2890
- /**
2891
- * Resume a readable stream
2892
- *
2893
- * @param {Readable} stream The readable stream
2894
- * @private
2895
- */
2896
- function resume(stream) {
2897
- stream.resume();
2898
- }
2899
- /**
2900
- * The `Sender` error event handler.
2901
- *
2902
- * @param {Error} The error
2903
- * @private
2904
- */
2905
- function senderOnError(err) {
2906
- const websocket = this[kWebSocket];
2907
- if (websocket.readyState === WebSocket.CLOSED) return;
2908
- if (websocket.readyState === WebSocket.OPEN) {
2909
- websocket._readyState = WebSocket.CLOSING;
2910
- setCloseTimer(websocket);
2911
- }
2912
- this._socket.end();
2913
- if (!websocket._errorEmitted) {
2914
- websocket._errorEmitted = true;
2915
- websocket.emit("error", err);
2916
- }
2917
- }
2918
- /**
2919
- * Set a timer to destroy the underlying raw socket of a WebSocket.
2920
- *
2921
- * @param {WebSocket} websocket The WebSocket instance
2922
- * @private
2923
- */
2924
- function setCloseTimer(websocket) {
2925
- websocket._closeTimer = setTimeout(websocket._socket.destroy.bind(websocket._socket), websocket._closeTimeout);
2926
- }
2927
- /**
2928
- * The listener of the socket `'close'` event.
2929
- *
2930
- * @private
2931
- */
2932
- function socketOnClose() {
2933
- const websocket = this[kWebSocket];
2934
- this.removeListener("close", socketOnClose);
2935
- this.removeListener("data", socketOnData);
2936
- this.removeListener("end", socketOnEnd);
2937
- websocket._readyState = WebSocket.CLOSING;
2938
- if (!this._readableState.endEmitted && !websocket._closeFrameReceived && !websocket._receiver._writableState.errorEmitted && this._readableState.length !== 0) {
2939
- const chunk = this.read(this._readableState.length);
2940
- websocket._receiver.write(chunk);
2941
- }
2942
- websocket._receiver.end();
2943
- this[kWebSocket] = void 0;
2944
- clearTimeout(websocket._closeTimer);
2945
- if (websocket._receiver._writableState.finished || websocket._receiver._writableState.errorEmitted) websocket.emitClose();
2946
- else {
2947
- websocket._receiver.on("error", receiverOnFinish);
2948
- websocket._receiver.on("finish", receiverOnFinish);
2949
- }
2950
- }
2951
- /**
2952
- * The listener of the socket `'data'` event.
2953
- *
2954
- * @param {Buffer} chunk A chunk of data
2955
- * @private
2956
- */
2957
- function socketOnData(chunk) {
2958
- if (!this[kWebSocket]._receiver.write(chunk)) this.pause();
2959
- }
2960
- /**
2961
- * The listener of the socket `'end'` event.
2962
- *
2963
- * @private
2964
- */
2965
- function socketOnEnd() {
2966
- const websocket = this[kWebSocket];
2967
- websocket._readyState = WebSocket.CLOSING;
2968
- websocket._receiver.end();
2969
- this.end();
2970
- }
2971
- /**
2972
- * The listener of the socket `'error'` event.
2973
- *
2974
- * @private
2975
- */
2976
- function socketOnError() {
2977
- const websocket = this[kWebSocket];
2978
- this.removeListener("error", socketOnError);
2979
- this.on("error", NOOP);
2980
- if (websocket) {
2981
- websocket._readyState = WebSocket.CLOSING;
2982
- this.destroy();
2983
- }
2984
- }
2985
- }));
2986
- //#endregion
2987
- //#region ../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/stream.js
2988
- var require_stream = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2989
- require_websocket();
2990
- const { Duplex: Duplex$1 } = __require("stream");
2991
- /**
2992
- * Emits the `'close'` event on a stream.
2993
- *
2994
- * @param {Duplex} stream The stream.
2995
- * @private
2996
- */
2997
- function emitClose(stream) {
2998
- stream.emit("close");
2999
- }
3000
- /**
3001
- * The listener of the `'end'` event.
3002
- *
3003
- * @private
3004
- */
3005
- function duplexOnEnd() {
3006
- if (!this.destroyed && this._writableState.finished) this.destroy();
3007
- }
3008
- /**
3009
- * The listener of the `'error'` event.
3010
- *
3011
- * @param {Error} err The error
3012
- * @private
3013
- */
3014
- function duplexOnError(err) {
3015
- this.removeListener("error", duplexOnError);
3016
- this.destroy();
3017
- if (this.listenerCount("error") === 0) this.emit("error", err);
3018
- }
3019
- /**
3020
- * Wraps a `WebSocket` in a duplex stream.
3021
- *
3022
- * @param {WebSocket} ws The `WebSocket` to wrap
3023
- * @param {Object} [options] The options for the `Duplex` constructor
3024
- * @return {Duplex} The duplex stream
3025
- * @public
3026
- */
3027
- function createWebSocketStream(ws, options) {
3028
- let terminateOnDestroy = true;
3029
- const duplex = new Duplex$1({
3030
- ...options,
3031
- autoDestroy: false,
3032
- emitClose: false,
3033
- objectMode: false,
3034
- writableObjectMode: false
3035
- });
3036
- ws.on("message", function message(msg, isBinary) {
3037
- const data = !isBinary && duplex._readableState.objectMode ? msg.toString() : msg;
3038
- if (!duplex.push(data)) ws.pause();
3039
- });
3040
- ws.once("error", function error(err) {
3041
- if (duplex.destroyed) return;
3042
- terminateOnDestroy = false;
3043
- duplex.destroy(err);
3044
- });
3045
- ws.once("close", function close() {
3046
- if (duplex.destroyed) return;
3047
- duplex.push(null);
3048
- });
3049
- duplex._destroy = function(err, callback) {
3050
- if (ws.readyState === ws.CLOSED) {
3051
- callback(err);
3052
- process.nextTick(emitClose, duplex);
3053
- return;
3054
- }
3055
- let called = false;
3056
- ws.once("error", function error(err) {
3057
- called = true;
3058
- callback(err);
3059
- });
3060
- ws.once("close", function close() {
3061
- if (!called) callback(err);
3062
- process.nextTick(emitClose, duplex);
3063
- });
3064
- if (terminateOnDestroy) ws.terminate();
3065
- };
3066
- duplex._final = function(callback) {
3067
- if (ws.readyState === ws.CONNECTING) {
3068
- ws.once("open", function open() {
3069
- duplex._final(callback);
3070
- });
3071
- return;
3072
- }
3073
- if (ws._socket === null) return;
3074
- if (ws._socket._writableState.finished) {
3075
- callback();
3076
- if (duplex._readableState.endEmitted) duplex.destroy();
3077
- } else {
3078
- ws._socket.once("finish", function finish() {
3079
- callback();
3080
- });
3081
- ws.close();
3082
- }
3083
- };
3084
- duplex._read = function() {
3085
- if (ws.isPaused) ws.resume();
3086
- };
3087
- duplex._write = function(chunk, encoding, callback) {
3088
- if (ws.readyState === ws.CONNECTING) {
3089
- ws.once("open", function open() {
3090
- duplex._write(chunk, encoding, callback);
3091
- });
3092
- return;
3093
- }
3094
- ws.send(chunk, callback);
3095
- };
3096
- duplex.on("end", duplexOnEnd);
3097
- duplex.on("error", duplexOnError);
3098
- return duplex;
3099
- }
3100
- module.exports = createWebSocketStream;
3101
- }));
3102
- //#endregion
3103
- //#region ../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/subprotocol.js
3104
- var require_subprotocol = /* @__PURE__ */ __commonJSMin(((exports, module) => {
3105
- const { tokenChars } = require_validation();
3106
- /**
3107
- * Parses the `Sec-WebSocket-Protocol` header into a set of subprotocol names.
3108
- *
3109
- * @param {String} header The field value of the header
3110
- * @return {Set} The subprotocol names
3111
- * @public
3112
- */
3113
- function parse(header) {
3114
- const protocols = /* @__PURE__ */ new Set();
3115
- let start = -1;
3116
- let end = -1;
3117
- let i = 0;
3118
- for (; i < header.length; i++) {
3119
- const code = header.charCodeAt(i);
3120
- if (end === -1 && tokenChars[code] === 1) {
3121
- if (start === -1) start = i;
3122
- } else if (i !== 0 && (code === 32 || code === 9)) {
3123
- if (end === -1 && start !== -1) end = i;
3124
- } else if (code === 44) {
3125
- if (start === -1) throw new SyntaxError(`Unexpected character at index ${i}`);
3126
- if (end === -1) end = i;
3127
- const protocol = header.slice(start, end);
3128
- if (protocols.has(protocol)) throw new SyntaxError(`The "${protocol}" subprotocol is duplicated`);
3129
- protocols.add(protocol);
3130
- start = end = -1;
3131
- } else throw new SyntaxError(`Unexpected character at index ${i}`);
3132
- }
3133
- if (start === -1 || end !== -1) throw new SyntaxError("Unexpected end of input");
3134
- const protocol = header.slice(start, i);
3135
- if (protocols.has(protocol)) throw new SyntaxError(`The "${protocol}" subprotocol is duplicated`);
3136
- protocols.add(protocol);
3137
- return protocols;
3138
- }
3139
- module.exports = { parse };
3140
- }));
3141
- //#endregion
3142
- //#region ../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/websocket-server.js
3143
- var require_websocket_server = /* @__PURE__ */ __commonJSMin(((exports, module) => {
3144
- const EventEmitter = __require("events");
3145
- const http = __require("http");
3146
- const { Duplex } = __require("stream");
3147
- const { createHash: createHash$1 } = __require("crypto");
3148
- const extension = require_extension();
3149
- const PerMessageDeflate = require_permessage_deflate();
3150
- const subprotocol = require_subprotocol();
3151
- const WebSocket = require_websocket();
3152
- const { CLOSE_TIMEOUT, GUID, kWebSocket } = require_constants();
3153
- const keyRegex = /^[+/0-9A-Za-z]{22}==$/;
3154
- const RUNNING = 0;
3155
- const CLOSING = 1;
3156
- const CLOSED = 2;
3157
- /**
3158
- * Class representing a WebSocket server.
3159
- *
3160
- * @extends EventEmitter
3161
- */
3162
- var WebSocketServer = class extends EventEmitter {
3163
- /**
3164
- * Create a `WebSocketServer` instance.
3165
- *
3166
- * @param {Object} options Configuration options
3167
- * @param {Boolean} [options.allowSynchronousEvents=true] Specifies whether
3168
- * any of the `'message'`, `'ping'`, and `'pong'` events can be emitted
3169
- * multiple times in the same tick
3170
- * @param {Boolean} [options.autoPong=true] Specifies whether or not to
3171
- * automatically send a pong in response to a ping
3172
- * @param {Number} [options.backlog=511] The maximum length of the queue of
3173
- * pending connections
3174
- * @param {Boolean} [options.clientTracking=true] Specifies whether or not to
3175
- * track clients
3176
- * @param {Number} [options.closeTimeout=30000] Duration in milliseconds to
3177
- * wait for the closing handshake to finish after `websocket.close()` is
3178
- * called
3179
- * @param {Function} [options.handleProtocols] A hook to handle protocols
3180
- * @param {String} [options.host] The hostname where to bind the server
3181
- * @param {Number} [options.maxPayload=104857600] The maximum allowed message
3182
- * size
3183
- * @param {Boolean} [options.noServer=false] Enable no server mode
3184
- * @param {String} [options.path] Accept only connections matching this path
3185
- * @param {(Boolean|Object)} [options.perMessageDeflate=false] Enable/disable
3186
- * permessage-deflate
3187
- * @param {Number} [options.port] The port where to bind the server
3188
- * @param {(http.Server|https.Server)} [options.server] A pre-created HTTP/S
3189
- * server to use
3190
- * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
3191
- * not to skip UTF-8 validation for text and close messages
3192
- * @param {Function} [options.verifyClient] A hook to reject connections
3193
- * @param {Function} [options.WebSocket=WebSocket] Specifies the `WebSocket`
3194
- * class to use. It must be the `WebSocket` class or class that extends it
3195
- * @param {Function} [callback] A listener for the `listening` event
3196
- */
3197
- constructor(options, callback) {
3198
- super();
3199
- options = {
3200
- allowSynchronousEvents: true,
3201
- autoPong: true,
3202
- maxPayload: 100 * 1024 * 1024,
3203
- skipUTF8Validation: false,
3204
- perMessageDeflate: false,
3205
- handleProtocols: null,
3206
- clientTracking: true,
3207
- closeTimeout: CLOSE_TIMEOUT,
3208
- verifyClient: null,
3209
- noServer: false,
3210
- backlog: null,
3211
- server: null,
3212
- host: null,
3213
- path: null,
3214
- port: null,
3215
- WebSocket,
3216
- ...options
3217
- };
3218
- if (options.port == null && !options.server && !options.noServer || options.port != null && (options.server || options.noServer) || options.server && options.noServer) throw new TypeError("One and only one of the \"port\", \"server\", or \"noServer\" options must be specified");
3219
- if (options.port != null) {
3220
- this._server = http.createServer((req, res) => {
3221
- const body = http.STATUS_CODES[426];
3222
- res.writeHead(426, {
3223
- "Content-Length": body.length,
3224
- "Content-Type": "text/plain"
3225
- });
3226
- res.end(body);
3227
- });
3228
- this._server.listen(options.port, options.host, options.backlog, callback);
3229
- } else if (options.server) this._server = options.server;
3230
- if (this._server) {
3231
- const emitConnection = this.emit.bind(this, "connection");
3232
- this._removeListeners = addListeners(this._server, {
3233
- listening: this.emit.bind(this, "listening"),
3234
- error: this.emit.bind(this, "error"),
3235
- upgrade: (req, socket, head) => {
3236
- this.handleUpgrade(req, socket, head, emitConnection);
3237
- }
3238
- });
3239
- }
3240
- if (options.perMessageDeflate === true) options.perMessageDeflate = {};
3241
- if (options.clientTracking) {
3242
- this.clients = /* @__PURE__ */ new Set();
3243
- this._shouldEmitClose = false;
3244
- }
3245
- this.options = options;
3246
- this._state = RUNNING;
3247
- }
3248
- /**
3249
- * Returns the bound address, the address family name, and port of the server
3250
- * as reported by the operating system if listening on an IP socket.
3251
- * If the server is listening on a pipe or UNIX domain socket, the name is
3252
- * returned as a string.
3253
- *
3254
- * @return {(Object|String|null)} The address of the server
3255
- * @public
3256
- */
3257
- address() {
3258
- if (this.options.noServer) throw new Error("The server is operating in \"noServer\" mode");
3259
- if (!this._server) return null;
3260
- return this._server.address();
3261
- }
3262
- /**
3263
- * Stop the server from accepting new connections and emit the `'close'` event
3264
- * when all existing connections are closed.
3265
- *
3266
- * @param {Function} [cb] A one-time listener for the `'close'` event
3267
- * @public
3268
- */
3269
- close(cb) {
3270
- if (this._state === CLOSED) {
3271
- if (cb) this.once("close", () => {
3272
- cb(/* @__PURE__ */ new Error("The server is not running"));
3273
- });
3274
- process.nextTick(emitClose, this);
3275
- return;
3276
- }
3277
- if (cb) this.once("close", cb);
3278
- if (this._state === CLOSING) return;
3279
- this._state = CLOSING;
3280
- if (this.options.noServer || this.options.server) {
3281
- if (this._server) {
3282
- this._removeListeners();
3283
- this._removeListeners = this._server = null;
3284
- }
3285
- if (this.clients) if (!this.clients.size) process.nextTick(emitClose, this);
3286
- else this._shouldEmitClose = true;
3287
- else process.nextTick(emitClose, this);
3288
- } else {
3289
- const server = this._server;
3290
- this._removeListeners();
3291
- this._removeListeners = this._server = null;
3292
- server.close(() => {
3293
- emitClose(this);
3294
- });
3295
- }
3296
- }
3297
- /**
3298
- * See if a given request should be handled by this server instance.
3299
- *
3300
- * @param {http.IncomingMessage} req Request object to inspect
3301
- * @return {Boolean} `true` if the request is valid, else `false`
3302
- * @public
3303
- */
3304
- shouldHandle(req) {
3305
- if (this.options.path) {
3306
- const index = req.url.indexOf("?");
3307
- if ((index !== -1 ? req.url.slice(0, index) : req.url) !== this.options.path) return false;
3308
- }
3309
- return true;
3310
- }
3311
- /**
3312
- * Handle a HTTP Upgrade request.
3313
- *
3314
- * @param {http.IncomingMessage} req The request object
3315
- * @param {Duplex} socket The network socket between the server and client
3316
- * @param {Buffer} head The first packet of the upgraded stream
3317
- * @param {Function} cb Callback
3318
- * @public
3319
- */
3320
- handleUpgrade(req, socket, head, cb) {
3321
- socket.on("error", socketOnError);
3322
- const key = req.headers["sec-websocket-key"];
3323
- const upgrade = req.headers.upgrade;
3324
- const version = +req.headers["sec-websocket-version"];
3325
- if (req.method !== "GET") {
3326
- abortHandshakeOrEmitwsClientError(this, req, socket, 405, "Invalid HTTP method");
3327
- return;
3328
- }
3329
- if (upgrade === void 0 || upgrade.toLowerCase() !== "websocket") {
3330
- abortHandshakeOrEmitwsClientError(this, req, socket, 400, "Invalid Upgrade header");
3331
- return;
3332
- }
3333
- if (key === void 0 || !keyRegex.test(key)) {
3334
- abortHandshakeOrEmitwsClientError(this, req, socket, 400, "Missing or invalid Sec-WebSocket-Key header");
3335
- return;
3336
- }
3337
- if (version !== 13 && version !== 8) {
3338
- abortHandshakeOrEmitwsClientError(this, req, socket, 400, "Missing or invalid Sec-WebSocket-Version header", { "Sec-WebSocket-Version": "13, 8" });
3339
- return;
3340
- }
3341
- if (!this.shouldHandle(req)) {
3342
- abortHandshake(socket, 400);
3343
- return;
3344
- }
3345
- const secWebSocketProtocol = req.headers["sec-websocket-protocol"];
3346
- let protocols = /* @__PURE__ */ new Set();
3347
- if (secWebSocketProtocol !== void 0) try {
3348
- protocols = subprotocol.parse(secWebSocketProtocol);
3349
- } catch (err) {
3350
- abortHandshakeOrEmitwsClientError(this, req, socket, 400, "Invalid Sec-WebSocket-Protocol header");
3351
- return;
3352
- }
3353
- const secWebSocketExtensions = req.headers["sec-websocket-extensions"];
3354
- const extensions = {};
3355
- if (this.options.perMessageDeflate && secWebSocketExtensions !== void 0) {
3356
- const perMessageDeflate = new PerMessageDeflate(this.options.perMessageDeflate, true, this.options.maxPayload);
3357
- try {
3358
- const offers = extension.parse(secWebSocketExtensions);
3359
- if (offers[PerMessageDeflate.extensionName]) {
3360
- perMessageDeflate.accept(offers[PerMessageDeflate.extensionName]);
3361
- extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
3362
- }
3363
- } catch (err) {
3364
- abortHandshakeOrEmitwsClientError(this, req, socket, 400, "Invalid or unacceptable Sec-WebSocket-Extensions header");
3365
- return;
3366
- }
3367
- }
3368
- if (this.options.verifyClient) {
3369
- const info = {
3370
- origin: req.headers[`${version === 8 ? "sec-websocket-origin" : "origin"}`],
3371
- secure: !!(req.socket.authorized || req.socket.encrypted),
3372
- req
3373
- };
3374
- if (this.options.verifyClient.length === 2) {
3375
- this.options.verifyClient(info, (verified, code, message, headers) => {
3376
- if (!verified) return abortHandshake(socket, code || 401, message, headers);
3377
- this.completeUpgrade(extensions, key, protocols, req, socket, head, cb);
3378
- });
3379
- return;
3380
- }
3381
- if (!this.options.verifyClient(info)) return abortHandshake(socket, 401);
3382
- }
3383
- this.completeUpgrade(extensions, key, protocols, req, socket, head, cb);
3384
- }
3385
- /**
3386
- * Upgrade the connection to WebSocket.
3387
- *
3388
- * @param {Object} extensions The accepted extensions
3389
- * @param {String} key The value of the `Sec-WebSocket-Key` header
3390
- * @param {Set} protocols The subprotocols
3391
- * @param {http.IncomingMessage} req The request object
3392
- * @param {Duplex} socket The network socket between the server and client
3393
- * @param {Buffer} head The first packet of the upgraded stream
3394
- * @param {Function} cb Callback
3395
- * @throws {Error} If called more than once with the same socket
3396
- * @private
3397
- */
3398
- completeUpgrade(extensions, key, protocols, req, socket, head, cb) {
3399
- if (!socket.readable || !socket.writable) return socket.destroy();
3400
- if (socket[kWebSocket]) throw new Error("server.handleUpgrade() was called more than once with the same socket, possibly due to a misconfiguration");
3401
- if (this._state > RUNNING) return abortHandshake(socket, 503);
3402
- const headers = [
3403
- "HTTP/1.1 101 Switching Protocols",
3404
- "Upgrade: websocket",
3405
- "Connection: Upgrade",
3406
- `Sec-WebSocket-Accept: ${createHash$1("sha1").update(key + GUID).digest("base64")}`
3407
- ];
3408
- const ws = new this.options.WebSocket(null, void 0, this.options);
3409
- if (protocols.size) {
3410
- const protocol = this.options.handleProtocols ? this.options.handleProtocols(protocols, req) : protocols.values().next().value;
3411
- if (protocol) {
3412
- headers.push(`Sec-WebSocket-Protocol: ${protocol}`);
3413
- ws._protocol = protocol;
3414
- }
3415
- }
3416
- if (extensions[PerMessageDeflate.extensionName]) {
3417
- const params = extensions[PerMessageDeflate.extensionName].params;
3418
- const value = extension.format({ [PerMessageDeflate.extensionName]: [params] });
3419
- headers.push(`Sec-WebSocket-Extensions: ${value}`);
3420
- ws._extensions = extensions;
3421
- }
3422
- this.emit("headers", headers, req);
3423
- socket.write(headers.concat("\r\n").join("\r\n"));
3424
- socket.removeListener("error", socketOnError);
3425
- ws.setSocket(socket, head, {
3426
- allowSynchronousEvents: this.options.allowSynchronousEvents,
3427
- maxPayload: this.options.maxPayload,
3428
- skipUTF8Validation: this.options.skipUTF8Validation
3429
- });
3430
- if (this.clients) {
3431
- this.clients.add(ws);
3432
- ws.on("close", () => {
3433
- this.clients.delete(ws);
3434
- if (this._shouldEmitClose && !this.clients.size) process.nextTick(emitClose, this);
3435
- });
3436
- }
3437
- cb(ws, req);
3438
- }
3439
- };
3440
- module.exports = WebSocketServer;
3441
- /**
3442
- * Add event listeners on an `EventEmitter` using a map of <event, listener>
3443
- * pairs.
3444
- *
3445
- * @param {EventEmitter} server The event emitter
3446
- * @param {Object.<String, Function>} map The listeners to add
3447
- * @return {Function} A function that will remove the added listeners when
3448
- * called
3449
- * @private
3450
- */
3451
- function addListeners(server, map) {
3452
- for (const event of Object.keys(map)) server.on(event, map[event]);
3453
- return function removeListeners() {
3454
- for (const event of Object.keys(map)) server.removeListener(event, map[event]);
3455
- };
3456
- }
3457
- /**
3458
- * Emit a `'close'` event on an `EventEmitter`.
3459
- *
3460
- * @param {EventEmitter} server The event emitter
3461
- * @private
3462
- */
3463
- function emitClose(server) {
3464
- server._state = CLOSED;
3465
- server.emit("close");
3466
- }
3467
- /**
3468
- * Handle socket errors.
3469
- *
3470
- * @private
3471
- */
3472
- function socketOnError() {
3473
- this.destroy();
3474
- }
3475
- /**
3476
- * Close the connection when preconditions are not fulfilled.
3477
- *
3478
- * @param {Duplex} socket The socket of the upgrade request
3479
- * @param {Number} code The HTTP response status code
3480
- * @param {String} [message] The HTTP response body
3481
- * @param {Object} [headers] Additional HTTP response headers
3482
- * @private
3483
- */
3484
- function abortHandshake(socket, code, message, headers) {
3485
- message = message || http.STATUS_CODES[code];
3486
- headers = {
3487
- Connection: "close",
3488
- "Content-Type": "text/html",
3489
- "Content-Length": Buffer.byteLength(message),
3490
- ...headers
3491
- };
3492
- socket.once("finish", socket.destroy);
3493
- socket.end(`HTTP/1.1 ${code} ${http.STATUS_CODES[code]}\r\n` + Object.keys(headers).map((h) => `${h}: ${headers[h]}`).join("\r\n") + "\r\n\r\n" + message);
3494
- }
3495
- /**
3496
- * Emit a `'wsClientError'` event on a `WebSocketServer` if there is at least
3497
- * one listener for it, otherwise call `abortHandshake()`.
3498
- *
3499
- * @param {WebSocketServer} server The WebSocket server
3500
- * @param {http.IncomingMessage} req The request object
3501
- * @param {Duplex} socket The socket of the upgrade request
3502
- * @param {Number} code The HTTP response status code
3503
- * @param {String} message The HTTP response body
3504
- * @param {Object} [headers] The HTTP response headers
3505
- * @private
3506
- */
3507
- function abortHandshakeOrEmitwsClientError(server, req, socket, code, message, headers) {
3508
- if (server.listenerCount("wsClientError")) {
3509
- const err = new Error(message);
3510
- Error.captureStackTrace(err, abortHandshakeOrEmitwsClientError);
3511
- server.emit("wsClientError", err, socket, req);
3512
- } else abortHandshake(socket, code, message, headers);
3513
- }
3514
- }));
3515
- require_stream();
3516
- require_receiver();
3517
- require_sender();
3518
- require_websocket();
3519
- require_websocket_server();
3520
- //#endregion
3521
- //#region ../nextclaw-remote/dist/index.js
3522
- function registerRemoteCommands(program, runtime) {
3523
- const remote = program.command("remote").description("Manage remote access");
3524
- remote.command("enable").description("Enable service-managed remote access").option("--api-base <url>", "Platform API base (supports /v1 suffix)").option("--name <name>", "Device display name").action(async (opts) => runtime.enable(opts));
3525
- remote.command("disable").description("Disable service-managed remote access").action(async () => runtime.disable());
3526
- remote.command("status").description("Show remote access status").option("--json", "Print JSON").action(async (opts) => runtime.status(opts));
3527
- remote.command("doctor").description("Run remote access diagnostics").option("--json", "Print JSON").action(async (opts) => runtime.doctor(opts));
3528
- remote.command("connect").description("Foreground debug mode: register this machine and keep the connector online").option("--api-base <url>", "Platform API base (supports /v1 suffix)").option("--local-origin <url>", "Local NextClaw UI origin (default: active service or http://127.0.0.1:55667)").option("--name <name>", "Device display name").option("--once", "Connect once without auto-reconnect", false).action(async (opts) => runtime.connect(opts));
3529
- }
3530
- //#endregion
3531
- //#region src/cli/app/register-agents-commands.ts
3532
- function registerAgentsCommands(program, nextclaw) {
3533
- const agentsCommands = nextclaw.commands.agents;
3534
- const agents = program.command("agents").description("Manage agents");
3535
- agents.command("list").description("List available agents").option("--json", "Output JSON", false).action((opts) => agentsCommands.list(opts));
3536
- agents.command("runtimes").description("List available agent runtimes").option("--probe", "Actively probe runtime readiness", false).option("--json", "Output JSON", false).action(async (opts) => agentsCommands.runtimes(opts));
3537
- agents.command("runtime").description("Manage one agent runtime").command("config <runtimeId>").description("Show or update agent runtime configuration").option("--inject-nextclaw-context <boolean>", "Inject NextClaw instructions, workspace context, and skills", parseBoolean).option("--json", "Output JSON", false).action(async (runtimeId, opts) => agentsCommands.runtimeConfig(runtimeId, opts));
3538
- agents.command("new <agentId>").description("Create a new agent").option("--name <name>", "Agent display name").option("--description <description>", "Agent description").option("--avatar <avatar>", "Remote avatar URL or local image path").option("--home <path>", "Agent home directory").option("--runtime <runtime>", "Agent runtime kind, for example native or codex").option("--json", "Output JSON", false).action(async (agentId, opts) => agentsCommands.create(agentId, opts));
3539
- agents.command("update <agentId>").description("Update an existing agent").option("--name <name>", "Agent display name").option("--description <description>", "Agent description").option("--avatar <avatar>", "Remote avatar URL or local image path").option("--runtime <runtime>", "Agent runtime kind, for example native or codex").option("--json", "Output JSON", false).action(async (agentId, opts) => agentsCommands.update(agentId, opts));
3540
- agents.command("remove <agentId>").description("Remove an agent").option("--json", "Output JSON", false).action(async (agentId, opts) => agentsCommands.remove(agentId, opts));
3541
- }
3542
- function parseBoolean(value) {
3543
- if (value === "true") return true;
3544
- if (value === "false") return false;
3545
- throw new InvalidArgumentError("Expected \"true\" or \"false\".");
3546
- }
3547
- //#endregion
3548
- //#region src/cli/app/register-learning-loop-commands.ts
3549
- function readLearningLoopThresholdOrExit(value) {
3550
- const threshold = Number.parseInt(value, 10);
3551
- if (!Number.isInteger(threshold) || threshold < 1) {
3552
- console.error(`Invalid learning loop threshold: ${value}. Expected an integer >= 1.`);
3553
- process.exit(1);
3554
- }
3555
- return threshold;
3556
- }
3557
- function registerLearningLoopCommands(program, nextclaw) {
3558
- const configCommands = nextclaw.commands.config;
3559
- const learningLoop = program.command("learning-loop").description("Manage the learning loop");
3560
- learningLoop.command("status").description("Show current learning loop settings").option("--json", "Output JSON", false).action((opts) => {
3561
- const status = readLearningLoopRuntimeConfig(loadConfig());
3562
- if (opts.json) {
3563
- console.log(JSON.stringify(status, null, 2));
3564
- return;
3565
- }
3566
- console.log("Learning loop");
3567
- console.log(` enabled: ${status.enabled}`);
3568
- console.log(` toolCallThreshold: ${status.toolCallThreshold}`);
3569
- });
3570
- learningLoop.command("enable").description("Enable the learning loop").action(async () => {
3571
- await configCommands.set("agents.learningLoop.enabled", "true", { json: true });
3572
- console.log("✓ Enabled learning loop.");
3573
- });
3574
- learningLoop.command("disable").description("Disable the learning loop").action(async () => {
3575
- await configCommands.set("agents.learningLoop.enabled", "false", { json: true });
3576
- console.log("✓ Disabled learning loop.");
3577
- });
3578
- learningLoop.command("threshold <count>").description("Set the tool-call threshold that triggers a learning-loop review").action(async (count) => {
3579
- const threshold = readLearningLoopThresholdOrExit(count);
3580
- await configCommands.set("agents.learningLoop.toolCallThreshold", String(threshold), { json: true });
3581
- console.log(`✓ Updated learning loop tool-call threshold to ${threshold}.`);
3582
- });
3583
- }
3584
- //#endregion
3585
- //#region src/cli/app/register-skills-commands.ts
3586
- function registerSkillsCommands(program, nextclaw) {
3587
- const skillsCommands = nextclaw.commands.skills;
3588
- const skills = program.command("skills").description("Manage skills");
3589
- skills.command("installed").description("List installed skills from the local runtime").option("--workdir <dir>", "Workspace directory to inspect").option("--scope <scope>", "Filter by scope: all|builtin|project|workspace", "all").option("-q, --query <text>", "Filter installed skills by query").option("--json", "Output JSON", false).action(async (opts) => await skillsCommands.installed(opts));
3590
- skills.command("info <selector>").description("Show installed skill details from the local runtime").option("--workdir <dir>", "Workspace directory to inspect").option("--json", "Output JSON", false).action(async (selector, opts) => await skillsCommands.info(selector, opts));
3591
- skills.command("install <slug>").description("Install a skill from NextClaw marketplace").option("--api-base <url>", "Marketplace API base URL").option("--workdir <dir>", "Workspace directory to install into").option("--dir <dir>", "Skills directory name (default: skills)").option("-f, --force", "Overwrite existing skill files", false).action(async (slug, opts) => await skillsCommands.install({
3592
- slug,
3593
- ...opts,
3594
- apiBaseUrl: opts.apiBase
3595
- }));
3596
- const withRepeatableTag = (value, previous = []) => [...previous, value];
3597
- skills.command("publish <dir>").description("Upload or create a skill in marketplace").option("--meta <path>", "Marketplace metadata file (default: <dir>/marketplace.json)").option("--slug <slug>", "Skill name segment (default: directory name)").option("--package-name <name>", "Canonical package name, for example @alice/demo-skill").option("--scope <scope>", "Package scope, for example alice or nextclaw").option("--name <name>", "Skill display name").option("--summary <summary>", "Skill summary").option("--description <description>", "Skill description").option("--author <author>", "Skill author").option("--tag <tag>", "Skill tag (repeatable)", withRepeatableTag, []).option("--source-repo <url>", "Source repository URL").option("--homepage <url>", "Homepage URL").option("--published-at <datetime>", "Published time (ISO datetime)").option("--updated-at <datetime>", "Updated time (ISO datetime)").option("--api-base <url>", "Marketplace API base URL").option("--token <token>", "Marketplace admin token for official @nextclaw/* publishing").action(async (dir, opts) => await skillsCommands.publish({
3598
- dir,
3599
- ...opts,
3600
- apiBaseUrl: opts.apiBase
3601
- }));
3602
- skills.command("update <dir>").description("Update an existing skill in marketplace").option("--meta <path>", "Marketplace metadata file (default: <dir>/marketplace.json)").option("--slug <slug>", "Skill name segment (default: directory name)").option("--package-name <name>", "Canonical package name, for example @alice/demo-skill").option("--scope <scope>", "Package scope, for example alice or nextclaw").option("--name <name>", "Skill display name").option("--summary <summary>", "Skill summary").option("--description <description>", "Skill description").option("--author <author>", "Skill author").option("--tag <tag>", "Skill tag (repeatable)", withRepeatableTag, []).option("--source-repo <url>", "Source repository URL").option("--homepage <url>", "Homepage URL").option("--updated-at <datetime>", "Updated time (ISO datetime)").option("--api-base <url>", "Marketplace API base URL").option("--token <token>", "Marketplace admin token for official @nextclaw/* publishing").action(async (dir, opts) => await skillsCommands.update({
3603
- dir,
3604
- ...opts,
3605
- apiBaseUrl: opts.apiBase
3606
- }));
3607
- const marketplaceSkills = program.command("marketplace").description("Browse and install marketplace items").command("skills").description("Browse marketplace skill catalog");
3608
- marketplaceSkills.command("search").description("Search marketplace skills").option("--api-base <url>", "Marketplace API base URL").option("-q, --query <text>", "Search text").option("--tag <tag>", "Filter by tag").option("--sort <sort>", "Sort by relevance or updated").option("--page <n>", "Page number").option("--page-size <n>", "Page size").option("--json", "Output JSON", false).action(async (opts) => await skillsCommands.marketplaceSearch(opts));
3609
- marketplaceSkills.command("info <slug>").description("Show marketplace skill details").option("--api-base <url>", "Marketplace API base URL").option("--json", "Output JSON", false).action(async (slug, opts) => await skillsCommands.marketplaceInfo(slug, opts));
3610
- marketplaceSkills.command("recommend").description("List recommended marketplace skills").option("--api-base <url>", "Marketplace API base URL").option("--scene <scene>", "Recommendation scene").option("--limit <n>", "Maximum number of items").option("--json", "Output JSON", false).action(async (opts) => await skillsCommands.marketplaceRecommend(opts));
3611
- marketplaceSkills.command("update <slug>").description("Update an installed marketplace skill").option("--api-base <url>", "Marketplace API base URL").option("--workdir <dir>", "Workspace directory to update").option("--dir <dir>", "Skills directory name (default: skills)").option("-f, --force", "Overwrite local skill files", false).option("--json", "Output JSON", false).action(async (slug, opts) => await skillsCommands.marketplaceUpdateInstalled(slug, opts));
3612
- marketplaceSkills.command("install <slug>").description("Install a skill from marketplace").option("--api-base <url>", "Marketplace API base URL").option("--workdir <dir>", "Workspace directory to install into").option("--dir <dir>", "Skills directory name (default: skills)").option("-f, --force", "Overwrite existing skill files", false).action(async (slug, opts) => await skillsCommands.install({
3613
- slug,
3614
- ...opts,
3615
- apiBaseUrl: opts.apiBase
3616
- }));
3617
- }
3618
- //#endregion
3619
- //#region src/cli/app/service-command-registration.utils.ts
3620
- const registerHostServiceControls = ({ program, nextclaw }) => {
3621
- const serviceCommands = nextclaw.commands.service;
3622
- const service = program.command("service").description("Manage host service integrations");
3623
- service.command("install-systemd").description("Install a managed Linux systemd service for NextClaw").option("--user", "Install a user-level systemd unit", false).option("--system", "Install a system-wide systemd unit", false).option("--dry-run", "Show what would be installed without making changes", false).option("--json", "Output JSON", false).action(async (opts) => serviceCommands.installSystemd(opts));
3624
- service.command("uninstall-systemd").description("Remove a managed Linux systemd service for NextClaw").option("--user", "Remove a user-level systemd unit", false).option("--system", "Remove a system-wide systemd unit", false).option("--dry-run", "Show what would be removed without making changes", false).option("--json", "Output JSON", false).action(async (opts) => serviceCommands.uninstallSystemd(opts));
3625
- service.command("install-launch-agent").description("Install a managed macOS LaunchAgent for NextClaw").option("--dry-run", "Show what would be installed without making changes", false).option("--json", "Output JSON", false).action(async (opts) => serviceCommands.installLaunchAgent(opts));
3626
- service.command("uninstall-launch-agent").description("Remove a managed macOS LaunchAgent for NextClaw").option("--dry-run", "Show what would be removed without making changes", false).option("--json", "Output JSON", false).action(async (opts) => serviceCommands.uninstallLaunchAgent(opts));
3627
- service.command("install-task").description("Install a managed Windows Scheduled Task for NextClaw").option("--dry-run", "Show what would be installed without making changes", false).option("--json", "Output JSON", false).action(async (opts) => serviceCommands.installWindowsTask(opts));
3628
- service.command("uninstall-task").description("Remove a managed Windows Scheduled Task for NextClaw").option("--dry-run", "Show what would be removed without making changes", false).option("--json", "Output JSON", false).action(async (opts) => serviceCommands.uninstallWindowsTask(opts));
3629
- const autostart = service.command("autostart").description("Inspect host autostart status");
3630
- autostart.command("status").description("Show host autostart status").option("--user", "Inspect the user-level autostart owner", false).option("--system", "Inspect the system-level autostart owner", false).option("--json", "Output JSON", false).action(async (opts) => serviceCommands.autostartStatus(opts));
3631
- autostart.command("doctor").description("Diagnose host autostart setup").option("--user", "Inspect the user-level autostart owner", false).option("--system", "Inspect the system-level autostart owner", false).option("--json", "Output JSON", false).action(async (opts) => serviceCommands.autostartDoctor(opts));
3632
- };
3633
- //#endregion
3634
- //#region src/cli/app/services/service-app-dev.service.ts
3635
- var ServiceAppDevService = class {
3636
- instanceStorageService = new AppInstanceStorageService();
3637
- constructor(params = {}) {
3638
- this.params = params;
3639
- }
3640
- inspect = async (target) => {
3641
- const appPath = path.resolve(target);
3642
- const issues = [];
3643
- const loaded = await this.loadServiceApp(appPath, issues);
3644
- if (!loaded) return this.buildDevReport(appPath, void 0, [], issues);
3645
- if (this.hasErrors(issues)) return this.buildDevReport(appPath, this.toServiceAppRecord(appPath, loaded.manifest, this.idleRuntimeStatus), [], issues);
3646
- const runtime = this.createRuntimeService();
3647
- const storage = await this.createDevStorage(appPath, loaded.manifest.id);
3648
- try {
3649
- const startRecord = this.toServiceAppRecord(appPath, loaded.manifest, runtime, storage);
3650
- const runtimeActions = await runtime.listActions({
3651
- app: startRecord,
3652
- manifest: loaded.manifest
3653
- });
3654
- const record = this.toServiceAppRecord(appPath, loaded.manifest, runtime, storage);
3655
- const actions = mergeServiceAppRuntimeActions({
3656
- record,
3657
- manifest: loaded.manifest,
3658
- runtimeActions
3659
- });
3660
- this.collectRuntimeIssues(record, actions, issues);
3661
- return this.buildDevReport(appPath, record, actions, issues);
3662
- } finally {
3663
- await runtime.dispose();
3664
- }
3665
- };
3666
- call = async (target, actionName, input) => {
3667
- const appPath = path.resolve(target);
3668
- const issues = [];
3669
- const loaded = await this.loadServiceApp(appPath, issues);
3670
- if (!loaded) return this.buildCallReport(appPath, void 0, void 0, void 0, issues);
3671
- if (this.hasErrors(issues)) return this.buildCallReport(appPath, this.toServiceAppRecord(appPath, loaded.manifest, this.idleRuntimeStatus), void 0, void 0, issues);
3672
- const action = actionName.trim();
3673
- if (!Object.hasOwn(loaded.manifest.actions, action)) {
3674
- issues.push({
3675
- severity: "error",
3676
- code: "service.action.notDeclared",
3677
- message: `service-app.json does not declare action: ${action || "(empty)"}.`
3678
- });
3679
- const record = this.toServiceAppRecord(appPath, loaded.manifest, this.idleRuntimeStatus);
3680
- return this.buildCallReport(appPath, record, void 0, void 0, issues);
3681
- }
3682
- const runtime = this.createRuntimeService();
3683
- const storage = await this.createDevStorage(appPath, loaded.manifest.id);
3684
- try {
3685
- const record = this.toServiceAppRecord(appPath, loaded.manifest, runtime, storage);
3686
- const result = await runtime.invokeAction({
3687
- app: record,
3688
- manifest: loaded.manifest,
3689
- actionName: action,
3690
- input
3691
- });
3692
- const nextRecord = this.toServiceAppRecord(appPath, loaded.manifest, runtime, storage);
3693
- return this.buildCallReport(appPath, nextRecord, buildServiceActionId(loaded.manifest.id, action), result, issues);
3694
- } catch (error) {
3695
- const record = this.toServiceAppRecord(appPath, loaded.manifest, runtime, storage);
3696
- issues.push({
3697
- severity: "error",
3698
- code: "service.runtime.callFailed",
3699
- message: error instanceof Error ? error.message : String(error)
3700
- });
3701
- return this.buildCallReport(appPath, record, buildServiceActionId(loaded.manifest.id, action), void 0, issues);
3702
- } finally {
3703
- await runtime.dispose();
3704
- }
3705
- };
3706
- loadServiceApp = async (appPath, issues) => {
3707
- try {
3708
- const manifest = await readServiceAppManifest(appPath);
3709
- if (manifest.id !== path.basename(appPath)) issues.push({
3710
- severity: "error",
3711
- code: "service.id.invalid",
3712
- message: `service-app.json id must equal directory name: ${path.basename(appPath)}.`
3713
- });
3714
- if (!manifest.enabled) issues.push({
3715
- severity: "error",
3716
- code: "service.disabled",
3717
- message: "Service App is disabled and cannot be started by app dev."
3718
- });
3719
- return { manifest };
3720
- } catch (error) {
3721
- issues.push({
3722
- severity: "error",
3723
- code: "service.manifest.readFailed",
3724
- message: error instanceof Error ? error.message : String(error)
3725
- });
3726
- return null;
3727
- }
3728
- };
3729
- createRuntimeService = () => this.params.runtimeService ?? new McpServiceAppRuntimeService({ getConfig: this.params.getConfig ?? this.loadRuntimeConfig });
3730
- loadRuntimeConfig = () => {
3731
- const configPath = getConfigPath();
3732
- return resolveConfigSecrets(loadConfig(configPath), { configPath });
3733
- };
3734
- idleRuntimeStatus = { getStatus: () => ({ status: "idle" }) };
3735
- createDevStorage = async (appPath, appId) => {
3736
- const sourceId = createHash("sha256").update(appPath).digest("hex").slice(0, 16);
3737
- const instanceDirectory = path.join(new AppHomeService().getAppHomeDirectory(), "dev-instances", appId, sourceId, "default");
3738
- return (await this.instanceStorageService.materialize({
3739
- appId,
3740
- instanceId: "default",
3741
- instanceDirectory
3742
- })).storage;
3743
- };
3744
- toServiceAppRecord = (dirPath, manifest, runtime, storage) => {
3745
- const runtimeStatus = runtime.getStatus(manifest.id);
3746
- const record = {
3747
- id: manifest.id,
3748
- title: manifest.title,
3749
- dirPath,
3750
- manifestPath: getServiceAppManifestPath(dirPath),
3751
- command: manifest.command,
3752
- args: manifest.args,
3753
- cwd: dirPath,
3754
- enabled: manifest.enabled,
3755
- protocol: manifest.protocol,
3756
- status: manifest.enabled ? runtimeStatus.status : "stopped",
3757
- dataDirectory: storage?.dataDirectory,
3758
- instanceId: storage?.instanceId,
3759
- storage,
3760
- isolation: "full-user"
3761
- };
3762
- if (manifest.description) record.description = manifest.description;
3763
- if (runtimeStatus.lastError) record.lastError = runtimeStatus.lastError;
3764
- if (runtimeStatus.lastStartedAt) record.lastStartedAt = runtimeStatus.lastStartedAt;
3765
- if (runtimeStatus.lastReadyAt) record.lastReadyAt = runtimeStatus.lastReadyAt;
3766
- if (runtimeStatus.lastFailedAt) record.lastFailedAt = runtimeStatus.lastFailedAt;
3767
- return record;
3768
- };
3769
- collectRuntimeIssues = (record, actions, issues) => {
3770
- if (record.status === "failed") issues.push({
3771
- severity: "error",
3772
- code: "service.runtime.startFailed",
3773
- message: record.lastError ?? "Service App runtime failed to start."
3774
- });
3775
- for (const action of actions) {
3776
- if (action.runtimeState === "missing") issues.push({
3777
- severity: "error",
3778
- code: "service.action.runtimeMissing",
3779
- message: `Declared action is missing from runtime tools/list: ${action.name}.`
3780
- });
3781
- if (action.runtimeState === "undeclared") issues.push({
3782
- severity: "error",
3783
- code: "service.action.runtimeUndeclared",
3784
- message: `Runtime exposes an undeclared action: ${action.name}.`,
3785
- fixHint: `Add "${action.name}" to service-app.json actions or remove it from the MCP server.`
3786
- });
3787
- }
3788
- };
3789
- buildDevReport = (target, app, actions, issues) => ({
3790
- ok: !issues.some((issue) => issue.severity === "error"),
3791
- target,
3792
- app,
3793
- actions,
3794
- issues
3795
- });
3796
- buildCallReport = (target, app, actionId, result, issues) => ({
3797
- ok: !issues.some((issue) => issue.severity === "error"),
3798
- target,
3799
- actionId,
3800
- app,
3801
- result,
3802
- issues
3803
- });
3804
- hasErrors = (issues) => issues.some((issue) => issue.severity === "error");
3805
- };
3806
- //#endregion
3807
- //#region src/cli/app/controllers/app-call-command.controller.ts
3808
- var AppCallCommandController = class {
3809
- constructor(serviceAppDevService = new ServiceAppDevService()) {
3810
- this.serviceAppDevService = serviceAppDevService;
3811
- }
3812
- call = async (target, actionName, options) => {
3813
- const input = this.parseInput(options.input);
3814
- if (!input.ok) {
3815
- this.writeInputError(input.message, Boolean(options.json));
3816
- process.exitCode = 1;
3817
- return;
3818
- }
3819
- const report = await this.serviceAppDevService.call(target, actionName, input.value);
3820
- process.stdout.write(options.json ? `${JSON.stringify(report, null, 2)}\n` : this.format(report));
3821
- if (!report.ok) process.exitCode = 1;
3822
- };
3823
- parseInput = (raw) => {
3824
- if (!raw) return {
3825
- ok: true,
3826
- value: {}
3827
- };
3828
- try {
3829
- const parsed = JSON.parse(raw);
3830
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {
3831
- ok: false,
3832
- message: "--input must be a JSON object."
3833
- };
3834
- return {
3835
- ok: true,
3836
- value: parsed
3837
- };
3838
- } catch (error) {
3839
- return {
3840
- ok: false,
3841
- message: `--input is not valid JSON: ${error instanceof Error ? error.message : String(error)}`
3842
- };
3843
- }
3844
- };
3845
- writeInputError = (message, json) => {
3846
- if (json) {
3847
- process.stdout.write(`${JSON.stringify({
3848
- ok: false,
3849
- issues: [{
3850
- severity: "error",
3851
- code: "input.invalid",
3852
- message
3853
- }]
3854
- }, null, 2)}\n`);
3855
- return;
3856
- }
3857
- process.stdout.write(`NextClaw service app call failed\n\nErrors:\n- [input.invalid] ${message}\n`);
3858
- };
3859
- format = (report) => {
3860
- return [
3861
- report.ok ? `NextClaw service app call passed: ${report.actionId ?? "(unknown)"}\n` : `NextClaw service app call failed: ${report.actionId ?? "(unknown)"}\n`,
3862
- report.app ? [
3863
- `App: ${report.app.title} (${report.app.id})`,
3864
- `Status: ${report.app.status}`,
3865
- report.app.lastError ? `Last error: ${report.app.lastError}` : ""
3866
- ].filter(Boolean).join("\n") : "",
3867
- report.result === void 0 ? "" : `Result:\n${JSON.stringify(report.result, null, 2)}\n`,
3868
- this.formatIssueSection("Errors", report.issues.filter((issue) => issue.severity === "error")),
3869
- this.formatIssueSection("Warnings", report.issues.filter((issue) => issue.severity === "warning"))
3870
- ].filter(Boolean).join("\n");
3871
- };
3872
- formatIssueSection = (title, issues) => {
3873
- if (issues.length === 0) return "";
3874
- return `${title}:\n${issues.flatMap((issue) => [`- [${issue.code}] ${issue.message}`, issue.fixHint ? ` Fix: ${issue.fixHint}` : ""].filter(Boolean)).join("\n")}\n`;
3875
- };
3876
- };
3877
- //#endregion
3878
- //#region src/cli/app/utils/app-check.utils.ts
3879
- const PANEL_MANIFEST_FILE = "panel-app.json";
3880
- const SERVICE_MANIFEST_FILE = "service-app.json";
3881
- const VALID_AGENT_CAPABILITIES = new Set(["agent:send", "agent:generateObject"]);
3882
- const VALID_SERVICE_ACTION_RISKS = new Set([
3883
- "read",
3884
- "write",
3885
- "external",
3886
- "dangerous"
3887
- ]);
3888
- const KEBAB_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
3889
- const SERVICE_ACTION_ID_PATTERN = /^([a-z0-9]+(?:-[a-z0-9]+)*)\.(.+)$/;
3890
- function isRecord(value) {
3891
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
3892
- }
3893
- function readOptionalString(record, key) {
3894
- return typeof record[key] === "string" && record[key].trim() ? record[key].trim() : void 0;
3895
- }
3896
- function readRequiredString(record, key, prefix) {
3897
- const value = readOptionalString(record, key);
3898
- if (!value) return { issue: {
3899
- severity: "error",
3900
- code: `${prefix}.${key}.missing`,
3901
- message: `${prefix}-app.json ${key} is required.`
3902
- } };
3903
- return { value };
3904
- }
3905
- function getRecommendedStringIssue(record, key, prefix) {
3906
- if (!readOptionalString(record, key)) return {
3907
- severity: "warning",
3908
- code: `${prefix}.${key}.missing`,
3909
- message: `${prefix}-app.json ${key} is recommended.`
3910
- };
3911
- }
3912
- function readStringArray(value, key) {
3913
- if (value === void 0) return { values: [] };
3914
- if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) return {
3915
- values: [],
3916
- issue: {
3917
- severity: "error",
3918
- code: `${key}.invalid`,
3919
- message: `${key} must be a string array.`
3920
- }
3921
- };
3922
- return { values: [...new Set(value.map((entry) => entry.trim()).filter(Boolean))] };
3923
- }
3924
- async function getTargetDirectoryIssue(appPath) {
3925
- try {
3926
- if ((await stat(appPath)).isDirectory()) return;
3927
- return {
3928
- severity: "error",
3929
- code: "app.target.notDirectory",
3930
- message: "App check target must be a directory."
3931
- };
3932
- } catch {
3933
- return {
3934
- severity: "error",
3935
- code: "app.target.missing",
3936
- message: "App check target directory does not exist."
3937
- };
3938
- }
3939
- }
3940
- async function readJsonObject(filePath) {
3941
- try {
3942
- const parsed = JSON.parse(await readFile(filePath, "utf8"));
3943
- if (isRecord(parsed)) return { value: parsed };
3944
- return {
3945
- value: null,
3946
- issue: {
3947
- severity: "error",
3948
- code: "manifest.notObject",
3949
- message: `${path.basename(filePath)} must contain a JSON object.`
3950
- }
3951
- };
3952
- } catch (error) {
3953
- return {
3954
- value: null,
3955
- issue: {
3956
- severity: "error",
3957
- code: "manifest.jsonInvalid",
3958
- message: `${path.basename(filePath)} is not valid JSON.`,
3959
- fixHint: error instanceof Error ? error.message : String(error)
3960
- }
3961
- };
3962
- }
3963
- }
3964
- async function fileExists(filePath) {
3965
- try {
3966
- await access(filePath, constants.F_OK);
3967
- return true;
3968
- } catch {
3969
- return false;
3970
- }
3971
- }
3972
- async function getMissingRelativeFileIssue(appPath, relativePath, code, message) {
3973
- const resolved = resolveRelativeFile(appPath, relativePath);
3974
- if (!resolved || !await fileExists(resolved)) return {
3975
- severity: "error",
3976
- code,
3977
- message: `${message}: ${relativePath}.`
3978
- };
3979
- }
3980
- function resolveRelativeFile(appPath, relativePath) {
3981
- if (!isRelativeResource(relativePath)) return null;
3982
- const resolved = path.resolve(appPath, relativePath);
3983
- return resolved === appPath || resolved.startsWith(`${appPath}${path.sep}`) ? resolved : null;
3984
- }
3985
- function isRelativeResource(value) {
3986
- return Boolean(value) && !value.startsWith("/") && !/^(?:[a-z]+:|#)/i.test(value);
3987
- }
3988
- function extractHtmlAssetPaths(html) {
3989
- return [...html.matchAll(/<(?:img|link)\b[^>]*(?:src|href)\s*=\s*["']([^"']+)["']/gi)].map((match) => match[1] ?? "").filter(isRelativeResource);
3990
- }
3991
- function extractScriptSrcs(html) {
3992
- return [...html.matchAll(/<script\b[^>]*src\s*=\s*["']([^"']+)["'][^>]*>/gi)].map((match) => match[1] ?? "");
3993
- }
3994
- function inferWorkspaceRoot(appPath, containerName) {
3995
- const container = path.dirname(appPath);
3996
- return path.basename(container) === containerName ? path.dirname(container) : null;
3997
- }
3998
- function isNodeCommand(command) {
3999
- const name = path.basename(command).toLowerCase();
4000
- return name === "node" || name === "node.exe";
4001
- }
4002
- //#endregion
4003
- //#region src/cli/app/services/panel-app-check.service.ts
4004
- var PanelAppCheckService = class {
4005
- check = async (appPath) => {
4006
- const issues = [];
4007
- this.checkDirectoryName(appPath, issues);
4008
- const manifestResult = await readJsonObject(path.join(appPath, PANEL_MANIFEST_FILE));
4009
- this.pushIssue(issues, manifestResult.issue);
4010
- if (!manifestResult.value) return issues;
4011
- const entry = this.checkManifestFields(appPath, manifestResult.value, issues);
4012
- this.checkCapabilities(manifestResult.value.capabilities, issues);
4013
- const declaredActions = this.checkActions(manifestResult.value.actions, issues);
4014
- await this.checkWorkspaceServiceActions(appPath, declaredActions, issues);
4015
- await this.checkEntry(appPath, entry, manifestResult.value, declaredActions, issues);
4016
- return issues;
4017
- };
4018
- checkDirectoryName = (appPath, issues) => {
4019
- if (!path.basename(appPath).endsWith(".panel")) issues.push({
4020
- severity: "error",
4021
- code: "panel.directory.invalid",
4022
- message: "Panel App directory name must end with .panel.",
4023
- fixHint: "Rename the directory to <app-id>.panel."
4024
- });
4025
- };
4026
- checkManifestFields = (appPath, manifest, issues) => {
4027
- const appId = path.basename(appPath).replace(/\.panel$/, "");
4028
- const id = readOptionalString(manifest, "id");
4029
- if (id && (!KEBAB_ID_PATTERN.test(id) || id !== appId)) issues.push({
4030
- severity: "error",
4031
- code: "panel.id.invalid",
4032
- message: `panel-app.json id must equal directory app id: ${appId}.`,
4033
- fixHint: "Remove id or set it to the directory name without .panel."
4034
- });
4035
- const title = readRequiredString(manifest, "title", "panel");
4036
- this.pushIssue(issues, title.issue);
4037
- this.pushIssue(issues, getRecommendedStringIssue(manifest, "description", "panel"));
4038
- this.pushIssue(issues, getRecommendedStringIssue(manifest, "icon", "panel"));
4039
- const entry = readRequiredString(manifest, "entry", "panel");
4040
- this.pushIssue(issues, entry.issue);
4041
- return entry.value;
4042
- };
4043
- checkCapabilities = (value, issues) => {
4044
- const capabilities = readStringArray(value, "panel.capabilities");
4045
- this.pushIssue(issues, capabilities.issue);
4046
- for (const capability of capabilities.values) {
4047
- if (VALID_AGENT_CAPABILITIES.has(capability)) continue;
4048
- issues.push({
4049
- severity: "error",
4050
- code: "panel.capability.invalid",
4051
- message: `Invalid agent capability: ${capability}.`,
4052
- fixHint: capability.includes(".") ? "Use agent:send or agent:generateObject, with a colon." : "Allowed values are agent:send and agent:generateObject."
4053
- });
4054
- }
4055
- };
4056
- checkActions = (value, issues) => {
4057
- const actions = readStringArray(value, "panel.actions");
4058
- this.pushIssue(issues, actions.issue);
4059
- for (const action of actions.values) {
4060
- if (SERVICE_ACTION_ID_PATTERN.test(action)) continue;
4061
- issues.push({
4062
- severity: "error",
4063
- code: "panel.action.invalid",
4064
- message: `Invalid service action id: ${action}.`,
4065
- fixHint: "Use <service-app-id>.<tool-name>, for example workspace-files.list."
4066
- });
4067
- }
4068
- return actions.values;
4069
- };
4070
- checkWorkspaceServiceActions = async (appPath, actions, issues) => {
4071
- const workspaceRoot = inferWorkspaceRoot(appPath, "panels");
4072
- if (!workspaceRoot || actions.length === 0) return;
4073
- const cache = /* @__PURE__ */ new Map();
4074
- for (const action of actions) {
4075
- const match = action.match(SERVICE_ACTION_ID_PATTERN);
4076
- if (!match) continue;
4077
- await this.checkWorkspaceServiceAction(workspaceRoot, match[1] ?? "", match[2] ?? "", cache, issues);
4078
- }
4079
- };
4080
- checkWorkspaceServiceAction = async (workspaceRoot, serviceId, actionName, cache, issues) => {
4081
- const manifest = await this.readCachedServiceManifest(workspaceRoot, serviceId, cache, issues);
4082
- if (!manifest) return;
4083
- const actions = manifest.actions;
4084
- if (!isRecord(actions) || !isRecord(actions[actionName])) issues.push({
4085
- severity: "error",
4086
- code: "panel.action.missingServiceAction",
4087
- message: `Declared service action does not exist: ${serviceId}.${actionName}.`,
4088
- fixHint: `Add "${actionName}" to service-apps/${serviceId}/service-app.json actions or fix panel-app.json actions.`
4089
- });
4090
- };
4091
- checkEntry = async (appPath, entry, manifest, declaredActions, issues) => {
4092
- if (!entry) return;
4093
- const entryPath = resolveRelativeFile(appPath, entry);
4094
- if (!entryPath) {
4095
- issues.push({
4096
- severity: "error",
4097
- code: "panel.entry.invalid",
4098
- message: `Panel entry must be a relative file inside the app directory: ${entry}.`
4099
- });
4100
- return;
4101
- }
4102
- if (!await fileExists(entryPath)) {
4103
- issues.push({
4104
- severity: "error",
4105
- code: "panel.entry.missing",
4106
- message: `Panel entry file does not exist: ${entry}.`
4107
- });
4108
- return;
4109
- }
4110
- await this.checkEntryContent(appPath, entryPath, manifest, declaredActions, issues);
4111
- };
4112
- checkEntryContent = async (appPath, entryPath, manifest, declaredActions, issues) => {
4113
- const html = await readFile(entryPath, "utf8");
4114
- this.checkLegacyMeta(html, issues);
4115
- await this.checkIcon(appPath, manifest.icon, issues);
4116
- await this.checkRelativeAssets(appPath, html, issues);
4117
- const scripts = await this.readReferencedScripts(appPath, html, issues);
4118
- this.checkBridgeUsage([html, ...scripts].join("\n"), manifest, declaredActions, issues);
4119
- };
4120
- checkLegacyMeta = (html, issues) => {
4121
- if (/name\s*=\s*["']nextclaw-panel-(actions|capabilities)["']/i.test(html)) issues.push({
4122
- severity: "error",
4123
- code: "panel.meta.deprecated",
4124
- message: "Directory Panel Apps must not declare NextClaw actions or capabilities in HTML meta tags.",
4125
- fixHint: "Move actions and capabilities into panel-app.json."
4126
- });
4127
- };
4128
- checkIcon = async (appPath, icon, issues) => {
4129
- if (typeof icon !== "string" || !this.isRelativeIconFile(icon)) return;
4130
- this.pushIssue(issues, await getMissingRelativeFileIssue(appPath, icon, "panel.icon.missing", "Panel icon file does not exist"));
4131
- };
4132
- isRelativeIconFile = (icon) => isRelativeResource(icon) && (icon.includes("/") || /\.(?:svg|png|jpe?g|gif|webp|ico)$/i.test(icon));
4133
- checkRelativeAssets = async (appPath, html, issues) => {
4134
- for (const asset of extractHtmlAssetPaths(html)) this.pushIssue(issues, await getMissingRelativeFileIssue(appPath, asset, "panel.asset.missing", "Panel asset file does not exist"));
4135
- };
4136
- readReferencedScripts = async (appPath, html, issues) => {
4137
- const scripts = [];
4138
- for (const src of extractScriptSrcs(html)) {
4139
- if (!isRelativeResource(src)) continue;
4140
- const scriptPath = resolveRelativeFile(appPath, src);
4141
- if (scriptPath && await fileExists(scriptPath)) scripts.push(await readFile(scriptPath, "utf8"));
4142
- else issues.push({
4143
- severity: "error",
4144
- code: "panel.script.missing",
4145
- message: `Panel script file does not exist: ${src}.`
4146
- });
4147
- }
4148
- return scripts;
4149
- };
4150
- checkBridgeUsage = (code, manifest, declaredActions, issues) => {
4151
- const capabilities = readStringArray(manifest.capabilities, "panel.capabilities");
4152
- this.checkRequiredCapability(code, "generateObject", "agent:generateObject", capabilities.values, issues);
4153
- this.checkRequiredCapability(code, "send", "agent:send", capabilities.values, issues);
4154
- this.checkServiceActionInvocations(code, declaredActions, issues);
4155
- };
4156
- checkRequiredCapability = (code, method, capability, declared, issues) => {
4157
- if (new RegExp(`nextclaw\\s*\\.\\s*agent\\s*\\.\\s*${method}\\b`).test(code) && !declared.includes(capability)) issues.push({
4158
- severity: "error",
4159
- code: "panel.capability.missing",
4160
- message: `Panel code calls window.nextclaw.agent.${method} but panel-app.json does not declare ${capability}.`,
4161
- fixHint: `Add "${capability}" to panel-app.json capabilities.`
4162
- });
4163
- };
4164
- checkServiceActionInvocations = (code, declaredActions, issues) => {
4165
- const literalActions = [...code.matchAll(/serviceActions\s*\.\s*invoke\s*\(\s*["']([^"']+)["']/g)].map((match) => match[1] ?? "");
4166
- if (literalActions.length === 0 && /serviceActions\s*\.\s*invoke\s*\(/.test(code)) issues.push({
4167
- severity: "warning",
4168
- code: "panel.action.dynamicInvoke",
4169
- message: "Panel code calls serviceActions.invoke with a dynamic action id.",
4170
- fixHint: "Make sure every possible action id is declared in panel-app.json actions."
4171
- });
4172
- for (const action of literalActions) if (!declaredActions.includes(action)) issues.push({
4173
- severity: "error",
4174
- code: "panel.action.missingDeclaration",
4175
- message: `Panel code invokes ${action} but panel-app.json actions does not declare it.`,
4176
- fixHint: `Add "${action}" to panel-app.json actions or update the invoke call.`
4177
- });
4178
- };
4179
- readCachedServiceManifest = async (workspaceRoot, serviceId, cache, issues) => {
4180
- if (cache.has(serviceId)) return cache.get(serviceId) ?? null;
4181
- const manifestPath = path.join(workspaceRoot, "service-apps", serviceId, SERVICE_MANIFEST_FILE);
4182
- if (!await fileExists(manifestPath)) {
4183
- issues.push({
4184
- severity: "error",
4185
- code: "panel.action.serviceMissing",
4186
- message: `Declared service app does not exist: ${serviceId}.`,
4187
- fixHint: `Create service-apps/${serviceId}/service-app.json or remove actions for ${serviceId}.`
4188
- });
4189
- cache.set(serviceId, null);
4190
- return null;
4191
- }
4192
- const manifestResult = await readJsonObject(manifestPath);
4193
- if (!manifestResult.value) {
4194
- if (manifestResult.issue) issues.push({
4195
- ...manifestResult.issue,
4196
- code: `panel.action.${manifestResult.issue.code}`
4197
- });
4198
- cache.set(serviceId, null);
4199
- return null;
4200
- }
4201
- cache.set(serviceId, manifestResult.value);
4202
- return manifestResult.value;
4203
- };
4204
- pushIssue = (issues, issue) => {
4205
- if (issue) issues.push(issue);
4206
- };
4207
- };
4208
- //#endregion
4209
- //#region src/cli/app/services/service-app-check.service.ts
4210
- const execFileAsync = promisify(execFile);
4211
- var ServiceAppCheckService = class {
4212
- check = async (appPath) => {
4213
- const issues = [];
4214
- const manifestResult = await readJsonObject(path.join(appPath, SERVICE_MANIFEST_FILE));
4215
- this.pushIssue(issues, manifestResult.issue);
4216
- if (!manifestResult.value) return issues;
4217
- const serviceId = this.checkManifestFields(appPath, manifestResult.value, issues);
4218
- const command = readOptionalString(manifestResult.value, "command");
4219
- const args = readStringArray(manifestResult.value.args, "service.args");
4220
- this.pushIssue(issues, args.issue);
4221
- this.checkActions(serviceId, manifestResult.value.actions, issues);
4222
- await this.checkCommand(appPath, command, args.values, issues);
4223
- return issues;
4224
- };
4225
- checkManifestFields = (appPath, manifest, issues) => {
4226
- const id = readRequiredString(manifest, "id", "service");
4227
- this.pushIssue(issues, id.issue);
4228
- if (id.value && (!KEBAB_ID_PATTERN.test(id.value) || id.value !== path.basename(appPath))) issues.push({
4229
- severity: "error",
4230
- code: "service.id.invalid",
4231
- message: `service-app.json id must be kebab-case and equal directory name: ${path.basename(appPath)}.`
4232
- });
4233
- this.pushIssue(issues, readRequiredString(manifest, "title", "service").issue);
4234
- this.pushIssue(issues, readRequiredString(manifest, "command", "service").issue);
4235
- if ((readOptionalString(manifest, "protocol") ?? "mcp") !== "mcp") issues.push({
4236
- severity: "error",
4237
- code: "service.protocol.invalid",
4238
- message: "Service App protocol must be mcp."
4239
- });
4240
- return id.value;
4241
- };
4242
- checkActions = (serviceId, value, issues) => {
4243
- if (!isRecord(value) || Object.keys(value).length === 0) {
4244
- issues.push({
4245
- severity: "error",
4246
- code: "service.actions.invalid",
4247
- message: "service-app.json actions must be a non-empty object."
4248
- });
4249
- return;
4250
- }
4251
- for (const [name, action] of Object.entries(value)) {
4252
- this.checkActionName(serviceId, name, issues);
4253
- this.checkActionManifest(name, action, issues);
4254
- }
4255
- };
4256
- checkActionName = (serviceId, name, issues) => {
4257
- if (!name.trim()) issues.push({
4258
- severity: "error",
4259
- code: "service.action.nameEmpty",
4260
- message: "Service App action name cannot be empty."
4261
- });
4262
- if (serviceId && name.startsWith(`${serviceId}.`)) issues.push({
4263
- severity: "error",
4264
- code: "service.action.nameContainsServiceId",
4265
- message: `Service App action key should not include the service id prefix: ${name}.`,
4266
- fixHint: `Use "${name.slice(serviceId.length + 1)}" as the action key; Panel App actions use "${name}".`
4267
- });
4268
- };
4269
- checkActionManifest = (name, action, issues) => {
4270
- if (!isRecord(action)) {
4271
- issues.push({
4272
- severity: "error",
4273
- code: "service.action.invalid",
4274
- message: `Service App action ${name} must be an object.`
4275
- });
4276
- return;
4277
- }
4278
- const risk = readOptionalString(action, "risk");
4279
- if (!risk || !VALID_SERVICE_ACTION_RISKS.has(risk)) issues.push({
4280
- severity: "error",
4281
- code: "service.action.riskInvalid",
4282
- message: `Service App action ${name} must declare risk: read, write, external, or dangerous.`
4283
- });
4284
- if (action.inputSchema !== void 0 && !isRecord(action.inputSchema)) issues.push({
4285
- severity: "error",
4286
- code: "service.action.inputSchemaInvalid",
4287
- message: `Service App action ${name} inputSchema must be an object.`
4288
- });
4289
- };
4290
- checkCommand = async (appPath, command, args, issues) => {
4291
- if (!command || !isNodeCommand(command)) return;
4292
- const script = args.find((arg) => !arg.startsWith("-"));
4293
- if (!script) {
4294
- issues.push({
4295
- severity: "error",
4296
- code: "service.command.scriptMissing",
4297
- message: "Node Service App command must point to a script file in args.",
4298
- fixHint: "Use args like [\"server.mjs\"]."
4299
- });
4300
- return;
4301
- }
4302
- const scriptPath = resolveRelativeFile(appPath, script);
4303
- if (!scriptPath || !await fileExists(scriptPath)) {
4304
- issues.push({
4305
- severity: "error",
4306
- code: "service.command.scriptNotFound",
4307
- message: `Service App script does not exist: ${script}.`
4308
- });
4309
- return;
4310
- }
4311
- await this.checkNodeSyntax(scriptPath, issues);
4312
- };
4313
- checkNodeSyntax = async (scriptPath, issues) => {
4314
- if (!/\.(?:cjs|js|mjs)$/i.test(scriptPath)) return;
4315
- try {
4316
- await execFileAsync(process.execPath, ["--check", scriptPath], { timeout: 5e3 });
4317
- } catch (error) {
4318
- issues.push({
4319
- severity: "error",
4320
- code: "service.command.syntaxInvalid",
4321
- message: `Service App script has a JavaScript syntax error: ${path.basename(scriptPath)}.`,
4322
- fixHint: this.readExecError(error)
4323
- });
4324
- }
4325
- };
4326
- readExecError = (error) => {
4327
- const stderr = isRecord(error) && typeof error.stderr === "string" ? error.stderr.trim() : "";
4328
- if (stderr) return stderr;
4329
- return error instanceof Error ? error.message : String(error);
4330
- };
4331
- pushIssue = (issues, issue) => {
4332
- if (issue) issues.push(issue);
4333
- };
4334
- };
4335
- //#endregion
4336
- //#region src/cli/app/services/app-check.service.ts
4337
- var AppCheckService = class {
4338
- constructor(panelAppCheckService = new PanelAppCheckService(), serviceAppCheckService = new ServiceAppCheckService()) {
4339
- this.panelAppCheckService = panelAppCheckService;
4340
- this.serviceAppCheckService = serviceAppCheckService;
4341
- }
4342
- check = async (target) => {
4343
- const appPath = path.resolve(target);
4344
- const issues = [];
4345
- const directoryIssue = await getTargetDirectoryIssue(appPath);
4346
- this.pushIssue(issues, directoryIssue);
4347
- if (directoryIssue) return this.buildReport(appPath, "unknown", issues);
4348
- const hasPanelManifest = await fileExists(path.join(appPath, PANEL_MANIFEST_FILE));
4349
- const hasServiceManifest = await fileExists(path.join(appPath, SERVICE_MANIFEST_FILE));
4350
- if (hasPanelManifest && hasServiceManifest) {
4351
- issues.push({
4352
- severity: "error",
4353
- code: "app.manifest.mixed",
4354
- message: "App directory contains both panel-app.json and service-app.json.",
4355
- fixHint: "Keep Panel App and Service App in separate directories."
4356
- });
4357
- return this.buildReport(appPath, "mixed", issues);
4358
- }
4359
- if (hasPanelManifest) {
4360
- issues.push(...await this.panelAppCheckService.check(appPath));
4361
- return this.buildReport(appPath, "panel", issues);
4362
- }
4363
- if (hasServiceManifest) {
4364
- issues.push(...await this.serviceAppCheckService.check(appPath));
4365
- return this.buildReport(appPath, "service", issues);
4366
- }
4367
- issues.push({
4368
- severity: "error",
4369
- code: "app.manifest.missing",
4370
- message: "App directory must contain panel-app.json or service-app.json.",
4371
- fixHint: "Run this command on a Panel App directory or a Service App directory."
4372
- });
4373
- return this.buildReport(appPath, "unknown", issues);
4374
- };
4375
- buildReport = (target, kind, issues) => ({
4376
- ok: !issues.some((issue) => issue.severity === "error"),
4377
- kind,
4378
- target,
4379
- issues
4380
- });
4381
- pushIssue = (issues, issue) => {
4382
- if (issue) issues.push(issue);
4383
- };
4384
- };
4385
- //#endregion
4386
- //#region src/cli/app/controllers/app-check-command.controller.ts
4387
- var AppCheckCommandController = class {
4388
- constructor(appCheckService = new AppCheckService()) {
4389
- this.appCheckService = appCheckService;
4390
- }
4391
- check = async (target, options) => {
4392
- const report = await this.appCheckService.check(target);
4393
- const output = options.json ? `${JSON.stringify(report, null, 2)}\n` : this.formatReport(report);
4394
- process.stdout.write(output);
4395
- if (!report.ok) process.exitCode = 1;
4396
- };
4397
- formatReport = (report) => {
4398
- const heading = report.ok ? `NextClaw app check passed: ${report.target}\n` : `NextClaw app check failed: ${report.target}\n`;
4399
- const errors = report.issues.filter((issue) => issue.severity === "error");
4400
- const warnings = report.issues.filter((issue) => issue.severity === "warning");
4401
- if (report.issues.length === 0) return `${heading}\nNo issues found.\n`;
4402
- return [
4403
- heading,
4404
- this.formatIssueSection("Errors", errors),
4405
- this.formatIssueSection("Warnings", warnings)
4406
- ].filter(Boolean).join("\n");
4407
- };
4408
- formatIssueSection = (title, issues) => {
4409
- if (issues.length === 0) return "";
4410
- return `${title}:\n${issues.flatMap((issue) => [`- [${issue.code}] ${issue.message}`, issue.fixHint ? ` Fix: ${issue.fixHint}` : ""].filter(Boolean)).join("\n")}\n`;
4411
- };
4412
- };
4413
- //#endregion
4414
- //#region src/cli/app/controllers/app-dev-command.controller.ts
4415
- var AppDevCommandController = class {
4416
- constructor(serviceAppDevService = new ServiceAppDevService()) {
4417
- this.serviceAppDevService = serviceAppDevService;
4418
- }
4419
- dev = async (target, options) => {
4420
- const report = await this.serviceAppDevService.inspect(target);
4421
- process.stdout.write(options.json ? `${JSON.stringify(report, null, 2)}\n` : this.format(report));
4422
- if (!report.ok) process.exitCode = 1;
4423
- };
4424
- format = (report) => {
4425
- return [
4426
- report.ok ? `NextClaw service app dev passed: ${report.target}\n` : `NextClaw service app dev failed: ${report.target}\n`,
4427
- report.app ? [
4428
- `App: ${report.app.title} (${report.app.id})`,
4429
- `Status: ${report.app.status}`,
4430
- report.app.lastError ? `Last error: ${report.app.lastError}` : ""
4431
- ].filter(Boolean).join("\n") : "",
4432
- report.actions.length ? `Actions:\n${report.actions.map((action) => `- ${action.id} [${action.runtimeState ?? "declared"}] risk=${action.risk}`).join("\n")}\n` : "Actions: none\n",
4433
- this.formatIssueSection("Errors", report.issues.filter((issue) => issue.severity === "error")),
4434
- this.formatIssueSection("Warnings", report.issues.filter((issue) => issue.severity === "warning"))
4435
- ].filter(Boolean).join("\n");
4436
- };
4437
- formatIssueSection = (title, issues) => {
4438
- if (issues.length === 0) return "";
4439
- return `${title}:\n${issues.flatMap((issue) => [`- [${issue.code}] ${issue.message}`, issue.fixHint ? ` Fix: ${issue.fixHint}` : ""].filter(Boolean)).join("\n")}\n`;
4440
- };
4441
- };
4442
- //#endregion
4443
- //#region src/cli/app/services/service-app-live-runtime.service.ts
4444
- function readErrorMessage(error) {
4445
- if (error instanceof Error && error.message.trim().length > 0) return error.message.trim();
4446
- return String(error ?? "unknown error");
4447
- }
4448
- var ServiceAppLiveRuntimeService = class {
4449
- constructor(params = {}) {
4450
- this.params = params;
4451
- }
4452
- restart = async (appId) => {
4453
- const target = appId.trim();
4454
- const issues = [];
4455
- if (!target) {
4456
- issues.push({
4457
- severity: "error",
4458
- code: "service.id.invalid",
4459
- message: "Service App id is required."
4460
- });
4461
- return {
4462
- ok: false,
4463
- target,
4464
- issues
4465
- };
4466
- }
4467
- const apiClient = this.createApiClient();
4468
- if (!apiClient) {
4469
- issues.push({
4470
- severity: "error",
4471
- code: "service.runtime.notRunning",
4472
- message: "NextClaw UI runtime is not running; start NextClaw before restarting a live Service App."
4473
- });
4474
- return {
4475
- ok: false,
4476
- target,
4477
- issues
4478
- };
4479
- }
4480
- try {
4481
- return {
4482
- ok: true,
4483
- target,
4484
- app: await apiClient.request({
4485
- path: `/api/service-apps/${encodeURIComponent(target)}/restart`,
4486
- method: "POST"
4487
- }),
4488
- issues
4489
- };
4490
- } catch (error) {
4491
- issues.push({
4492
- severity: "error",
4493
- code: "service.runtime.restartFailed",
4494
- message: readErrorMessage(error)
4495
- });
4496
- return {
4497
- ok: false,
4498
- target,
4499
- issues
4500
- };
4501
- }
4502
- };
4503
- createApiClient = () => {
4504
- if (this.params.createApiClient) return this.params.createApiClient();
4505
- const apiBase = resolveLocalUiApiBase();
4506
- return apiBase ? new LocalUiApiClient(apiBase) : null;
4507
- };
4508
- };
4509
- var LocalUiApiClient = class {
4510
- cookie;
4511
- constructor(apiBase) {
4512
- this.apiBase = apiBase;
4513
- }
4514
- request = async (params) => {
4515
- const { body, method, path } = params;
4516
- const cookie = await this.getCookie();
4517
- const headers = {};
4518
- if (body) headers["Content-Type"] = "application/json";
4519
- if (cookie) headers.Cookie = cookie;
4520
- const requestInit = {
4521
- method: method ?? "GET",
4522
- headers
4523
- };
4524
- if (body) requestInit.body = JSON.stringify(body);
4525
- const response = await fetch(`${this.apiBase}${path}`, requestInit);
4526
- if (!response.ok) throw new Error(`api request failed with status ${response.status}`);
4527
- const payload = await response.json();
4528
- if (!payload.ok) throw new Error(payload.error?.message ?? "api request failed");
4529
- return payload.data;
4530
- };
4531
- getCookie = async () => {
4532
- if (this.cookie !== void 0) return this.cookie;
4533
- const response = await fetch(`${this.apiBase}/api/auth/bridge`, {
4534
- method: "POST",
4535
- headers: { "x-nextclaw-ui-bridge-secret": ensureUiBridgeSecret() }
4536
- });
4537
- if (!response.ok) throw new Error(`bridge auth failed with status ${response.status}`);
4538
- const payload = await response.json();
4539
- if (!payload.ok) throw new Error(payload.error?.message ?? "bridge auth failed");
4540
- this.cookie = typeof payload.data.cookie === "string" && payload.data.cookie.trim().length > 0 ? payload.data.cookie.trim() : null;
4541
- return this.cookie;
4542
- };
4543
- };
4544
- function resolveLocalUiApiBase() {
4545
- const state = readRunningRuntimeState(resolve(getRunPath(), "ui-runtime.json")) ?? readRunningRuntimeState(resolve(getRunPath(), "service.json"));
4546
- if (!state) return null;
4547
- if (typeof state.uiUrl === "string" && state.uiUrl.trim().length > 0) return state.uiUrl.replace(/\/+$/, "");
4548
- if (typeof state.apiUrl === "string" && state.apiUrl.trim().length > 0) return state.apiUrl.replace(/\/api\/?$/, "").replace(/\/+$/, "");
4549
- return null;
4550
- }
4551
- function readRunningRuntimeState(statePath) {
4552
- if (!existsSync(statePath)) return null;
4553
- try {
4554
- const state = JSON.parse(readFileSync(statePath, "utf-8"));
4555
- return typeof state.pid === "number" && isProcessRunning(state.pid) ? state : null;
4556
- } catch {
4557
- return null;
4558
- }
4559
- }
4560
- function isProcessRunning(pid) {
4561
- try {
4562
- process.kill(pid, 0);
4563
- return true;
4564
- } catch {
4565
- return false;
4566
- }
4567
- }
4568
- function ensureUiBridgeSecret() {
4569
- const dirPath = join(getDataDir(), "remote");
4570
- const secretPath = join(dirPath, "ui-bridge-secret");
4571
- if (existsSync(secretPath)) {
4572
- const existing = readFileSync(secretPath, "utf-8").trim();
4573
- if (existing.length > 0) return existing;
4574
- }
4575
- mkdirSync(dirPath, { recursive: true });
4576
- const secret = randomBytes(24).toString("hex");
4577
- writeFileSync(secretPath, `${secret}\n`, "utf-8");
4578
- return secret;
4579
- }
4580
- //#endregion
4581
- //#region src/cli/app/controllers/app-restart-command.controller.ts
4582
- var AppRestartCommandController = class {
4583
- constructor(liveRuntimeService = new ServiceAppLiveRuntimeService()) {
4584
- this.liveRuntimeService = liveRuntimeService;
4585
- }
4586
- restart = async (appId, options) => {
4587
- const report = await this.liveRuntimeService.restart(appId);
4588
- process.stdout.write(options.json ? `${JSON.stringify(report, null, 2)}\n` : this.format(report));
4589
- if (!report.ok) process.exitCode = 1;
4590
- };
4591
- format = (report) => {
4592
- return [
4593
- report.ok ? `NextClaw service app restart passed: ${report.target}\n` : `NextClaw service app restart failed: ${report.target || "(empty)"}\n`,
4594
- report.app ? [
4595
- `App: ${report.app.title} (${report.app.id})`,
4596
- `Status: ${report.app.status}`,
4597
- report.app.lastError ? `Last error: ${report.app.lastError}` : ""
4598
- ].filter(Boolean).join("\n") : "",
4599
- this.formatIssueSection("Errors", report.issues.filter((issue) => issue.severity === "error")),
4600
- this.formatIssueSection("Warnings", report.issues.filter((issue) => issue.severity === "warning"))
4601
- ].filter(Boolean).join("\n");
4602
- };
4603
- formatIssueSection = (title, issues) => {
4604
- if (issues.length === 0) return "";
4605
- return `${title}:\n${issues.flatMap((issue) => [`- [${issue.code}] ${issue.message}`, issue.fixHint ? ` Fix: ${issue.fixHint}` : ""].filter(Boolean)).join("\n")}\n`;
4606
- };
4607
- };
4608
- //#endregion
4609
- //#region src/cli/app/services/app-publishing.service.ts
4610
- var AppPublishingService = class {
4611
- constructor(validationService = new AppPublishValidationService(), publishService = new AppPublishService()) {
4612
- this.validationService = validationService;
4613
- this.publishService = publishService;
4614
- }
4615
- validate = async (params) => {
4616
- const result = await this.validationService.validate({
4617
- appDirectory: params.appDirectory,
4618
- metadataPath: params.metadataPath,
4619
- mode: "bundle"
4620
- });
4621
- if (result.profile !== "components") throw new Error("nextclaw app publish 只支持由 Panel App 或 Service App 组成的 schema v2 Mini App。");
4622
- if (result.distributionMode !== "bundle") throw new Error("NextClaw Mini App 只支持 bundle 分发。");
4623
- return result;
4624
- };
4625
- publish = async (params) => {
4626
- const validation = await this.validate(params);
4627
- if (validation.warnings.length > 0 && !params.allowWarnings) {
4628
- const warningList = validation.warnings.map((warning) => `[${warning.code}] ${warning.message}`).join("; ");
4629
- throw new Error(`发布校验包含警告:${warningList}。确认后可使用 --allow-warnings 继续。`);
4630
- }
4631
- const tempDirectory = await mkdtemp(path.join(tmpdir(), "nextclaw-app-publish-"));
4632
- try {
4633
- const result = await this.publishService.publish({
4634
- appDirectory: params.appDirectory,
4635
- metadataPath: params.metadataPath,
4636
- bundleOutputPath: path.join(tempDirectory, "artifact.napp"),
4637
- mode: "bundle"
4638
- });
4639
- const { item } = result;
4640
- return {
4641
- validation,
4642
- publish: {
4643
- created: result.created,
4644
- item: {
4645
- slug: item.slug,
4646
- appId: item.appId,
4647
- ownerScope: item.ownerScope,
4648
- appName: item.appName,
4649
- publishStatus: item.publishStatus,
4650
- name: item.name,
4651
- latestVersion: item.latestVersion,
4652
- webUrl: item.publishStatus === "published" ? item.webUrl : void 0
4653
- },
4654
- fileCount: result.fileCount
4655
- }
4656
- };
4657
- } catch (error) {
4658
- const message = error instanceof Error ? error.message : String(error);
4659
- throw new Error(message.replace(/缺少 marketplace publish token。请先登录 NextClaw,或传入 --token。?/g, "发布需要 NextClaw 平台登录态。请先运行 nextclaw login。").replace(/,或传入 --token。?/g, "。"));
4660
- } finally {
4661
- await rm(tempDirectory, {
4662
- recursive: true,
4663
- force: true
4664
- });
4665
- }
4666
- };
4667
- };
4668
- //#endregion
4669
- //#region src/cli/app/controllers/app-publish-command.controller.ts
4670
- const PLATFORM_APPS_URL = "https://platform.nextclaw.io/apps";
4671
- var AppPublishCommandController = class {
4672
- constructor(appPublishingService = new AppPublishingService()) {
4673
- this.appPublishingService = appPublishingService;
4674
- }
4675
- publish = async (target, options) => {
4676
- const { allowWarnings, json, meta } = options;
4677
- try {
4678
- const result = await this.appPublishingService.publish({
4679
- appDirectory: target,
4680
- metadataPath: meta,
4681
- allowWarnings
4682
- });
4683
- process.stdout.write(json ? `${JSON.stringify({
4684
- ok: true,
4685
- ...result
4686
- }, null, 2)}\n` : this.format(result));
4687
- } catch (error) {
4688
- this.writeError(error, Boolean(json));
4689
- process.exitCode = 1;
4690
- }
4691
- };
4692
- format = (result) => {
4693
- const { item } = result.publish;
4694
- if (item.publishStatus === "pending") return [
4695
- `Submitted ${item.name} (${item.appId}) ${item.latestVersion} for review.`,
4696
- "Status: pending",
4697
- "The app will appear in the App Marketplace after approval.",
4698
- `Manage submissions: ${PLATFORM_APPS_URL}`,
4699
- ""
4700
- ].join("\n");
4701
- return [
4702
- `${result.publish.created ? "Published" : "Updated"} ${item.name} (${item.appId}) ${item.latestVersion}.`,
4703
- "Status: published",
4704
- item.webUrl ? `Details: ${item.webUrl}` : "",
4705
- `Manage apps: ${PLATFORM_APPS_URL}`,
4706
- ""
4707
- ].filter((line, index, lines) => line || index === lines.length - 1).join("\n");
4708
- };
4709
- writeError = (error, json) => {
4710
- const message = error instanceof Error ? error.message : String(error);
4711
- process.stdout.write(json ? `${JSON.stringify({
4712
- ok: false,
4713
- error: { message }
4714
- }, null, 2)}\n` : `Mini App publish failed: ${message}\n`);
4715
- };
4716
- };
4717
- //#endregion
4718
- //#region src/cli/app/controllers/app-validate-publish-command.controller.ts
4719
- var AppValidatePublishCommandController = class {
4720
- constructor(appPublishingService = new AppPublishingService()) {
4721
- this.appPublishingService = appPublishingService;
4722
- }
4723
- validate = async (target, options) => {
4724
- try {
4725
- const validation = await this.appPublishingService.validate({
4726
- appDirectory: target,
4727
- metadataPath: options.meta
4728
- });
4729
- process.stdout.write(options.json ? `${JSON.stringify({
4730
- ok: true,
4731
- validation
4732
- }, null, 2)}\n` : this.format(validation));
4733
- } catch (error) {
4734
- this.writeError(error, Boolean(options.json));
4735
- process.exitCode = 1;
4736
- }
4737
- };
4738
- format = (validation) => {
4739
- const lines = [
4740
- `Mini App publish validation passed: ${validation.appId}@${validation.version}`,
4741
- `Components: ${validation.componentCount ?? 0}`,
4742
- `Bundle size: ${validation.bundleSizeBytes} bytes`,
4743
- `Metadata: ${validation.metadataPath}`
4744
- ];
4745
- for (const warning of validation.warnings) lines.push(`Warning [${warning.code}]: ${warning.message}`);
4746
- return `${lines.join("\n")}\n`;
4747
- };
4748
- writeError = (error, json) => {
4749
- const message = error instanceof Error ? error.message : String(error);
4750
- process.stdout.write(json ? `${JSON.stringify({
4751
- ok: false,
4752
- error: { message }
4753
- }, null, 2)}\n` : `Mini App publish validation failed: ${message}\n`);
4754
- };
4755
- };
4756
- //#endregion
4757
- //#region src/cli/app/register-app-commands.ts
4758
- function registerAppCommands(program) {
4759
- const app = program.command("app").description("Develop, validate, and publish NextClaw apps");
4760
- const appCheck = new AppCheckCommandController();
4761
- const appDev = new AppDevCommandController();
4762
- const appCall = new AppCallCommandController();
4763
- const appRestart = new AppRestartCommandController();
4764
- const appValidatePublish = new AppValidatePublishCommandController();
4765
- const appPublish = new AppPublishCommandController();
4766
- app.command("validate-publish <app-dir>").description("Validate a NextClaw Mini App before Marketplace submission").option("--meta <path>", "Use a custom marketplace metadata file").option("--json", "Output JSON", false).action(async (target, opts) => appValidatePublish.validate(target, opts));
4767
- app.command("publish <app-dir>").description("Submit a NextClaw Mini App to the App Marketplace").option("--meta <path>", "Use a custom marketplace metadata file").option("--allow-warnings", "Submit after reviewing validation warnings", false).option("--json", "Output JSON", false).action(async (target, opts) => appPublish.publish(target, opts));
4768
- app.command("check <app-dir>").description("Check a Panel App or Service App directory").option("--json", "Output JSON", false).action(async (target, opts) => appCheck.check(target, opts));
4769
- app.command("dev <service-app-dir>").description("Start a Service App through the real runtime and inspect its actions").option("--json", "Output JSON", false).action(async (target, opts) => appDev.dev(target, opts));
4770
- app.command("call <service-app-dir> <action-name>").description("Call a Service App action through the real runtime").option("--input <json>", "JSON object input for the action").option("--json", "Output JSON", false).action(async (target, actionName, opts) => appCall.call(target, actionName, opts));
4771
- app.command("restart <app-id>").description("Restart a live Service App runtime in the running NextClaw UI").option("--json", "Output JSON", false).action(async (appId, opts) => appRestart.restart(appId, opts));
4772
- }
4773
- //#endregion
4774
- //#region src/cli/app/register-project-commands.ts
4775
- function registerProjectCommands(program, nextclaw) {
4776
- const commands = nextclaw.commands.projects;
4777
- const projects = program.command("projects").description("Manage projects");
4778
- projects.command("list").description("List registered projects, including projects without sessions").option("--json", "Output JSON", false).action((options) => commands.list(options));
4779
- projects.command("templates").description("List built-in project templates").option("--json", "Output JSON", false).action((options) => commands.templates(options));
4780
- projects.command("create <name>").description("Create and register a project").option("--path <directory>", "Target directory").option("--template <template>", "Project template: empty or knowledge-base", "empty").option("--json", "Output JSON", false).action((name, options) => commands.create(name, options));
4781
- }
4782
- //#endregion
4783
- //#region src/cli/app/register-session-commands.ts
4784
- function registerSessionCommands(program, nextclaw) {
4785
- const commands = nextclaw.commands.sessions;
4786
- const sessions = program.command("sessions").description("Manage sessions");
4787
- sessions.command("rename <session-id> <label>").description("Rename a session").option("--json", "Output JSON", false).action((sessionId, label, options) => commands.rename(sessionId, label, options));
4788
- sessions.command("set-project <session-id> <directory>").description("Bind a session to an existing project directory").option("--json", "Output JSON", false).action((sessionId, directory, options) => commands.setProject(sessionId, directory, options));
4789
- sessions.command("clear-project <session-id>").description("Clear a session project binding").option("--json", "Output JSON", false).action((sessionId, options) => commands.clearProject(sessionId, options));
4790
- }
4791
- //#endregion
2
+ import { t as createNextclawDistribution } from "../../nextclaw-distribution.utils-BneKclXo.js";
3
+ import { NextclawDistributionService } from "@nextclaw/service";
4792
4
  //#region src/cli/app/index.ts
4793
- const LOGO = "🤖";
4794
- const program = new Command();
4795
- NextclawDistributionService.configure(createNextclawDistribution(import.meta.url));
4796
- const runtime = new NextclawServiceRuntime({ logo: LOGO });
4797
- const withRepeatableTag = (value, previous = []) => [...previous, value];
4798
- const registerRemoteCommandGroup = (target, nextclaw) => {
4799
- registerRemoteCommands(target, nextclaw.commands.remote);
4800
- };
4801
- program.name(APP_NAME).description(`${LOGO} ${APP_NAME} - ${APP_TAGLINE}`).version(runtime.version, "-v, --version", "show version");
4802
- program.command("onboard").description(`Initialize ${APP_NAME} configuration and workspace`).action(async () => runtime.onboard());
4803
- program.command("init").description(`Initialize ${APP_NAME} configuration and workspace`).option("-f, --force", "Overwrite existing template files").action(async (opts) => runtime.init({ force: Boolean(opts.force) }));
4804
- program.command("login").description("Sign in to NextClaw Platform and save the platform token locally (browser flow by default)").option("--api-base <url>", "Platform API base (supports /v1 suffix)").option("--email <email>", "Login email for direct password sign-in").option("--password <password>", "Login password for direct password sign-in").option("--no-open", "Do not open the browser automatically").action(async (opts) => runtime.login(opts));
4805
- const account = program.command("account").description("Inspect and manage your NextClaw account");
4806
- account.command("status").description("Show account status and personal marketplace publish readiness").option("--api-base <url>", "Platform API base (supports /v1 suffix)").option("--json", "Output JSON", false).action(async (opts) => runtime.account.status(opts));
4807
- account.command("set-username <username>").description("Set your NextClaw username for personal marketplace publishing").option("--api-base <url>", "Platform API base (supports /v1 suffix)").option("--json", "Output JSON", false).action(async (username, opts) => runtime.account.setUsername(username, opts));
4808
- registerRemoteCommandGroup(program, runtime);
4809
- program.command("gateway").description(`Start the ${APP_NAME} gateway`).option("-p, --port <port>", "Gateway port", "18790").option("-v, --verbose", "Verbose output", false).option("--ui", "Enable UI server", false).option("--ui-port <port>", "UI port").option("--ui-open", "Open browser when UI starts", false).action(async (opts) => runtime.commands.gateway.run(opts));
4810
- program.command("ui").description(`Start the ${APP_NAME} UI with gateway`).option("--port <port>", "UI port").option("--no-open", "Disable opening browser").action(async (opts) => runtime.commands.ui.run(opts));
4811
- program.command("start").description(`Start the ${APP_NAME} gateway + UI in the background`).option("--ui-port <port>", "UI port").option("--start-timeout <ms>", "Maximum wait time for startup readiness in milliseconds").option("--open", "Open browser after start", false).action(async (opts) => runtime.commands.start.run(opts));
4812
- program.command("restart").description(`Restart the ${APP_NAME} background service`).option("--ui-port <port>", "UI port").option("--start-timeout <ms>", "Maximum wait time for startup readiness in milliseconds").option("--open", "Open browser after restart", false).action(async (opts) => runtime.commands.restart.run(opts));
4813
- program.command("serve").description(`Run the ${APP_NAME} gateway + UI in the foreground`).option("--ui-port <port>", "UI port").option("--open", "Open browser after start", false).action(async (opts) => runtime.commands.serve.run(opts));
4814
- program.command("stop").description(`Stop the ${APP_NAME} background service`).action(async () => runtime.commands.stop.run());
4815
- registerHostServiceControls({
4816
- program,
4817
- nextclaw: runtime
4818
- });
4819
- program.command("agent").description("Interact with the agent directly").option("-m, --message <message>", "Message to send to the agent").option("-s, --session <session>", "Session ID", "cli:default").option("--model <model>", "Session model override for this run").option("--no-markdown", "Disable Markdown rendering").action(async (opts) => runtime.agent(opts));
4820
- program.command("update").description(`Update the ${APP_NAME} runtime`).option("--check", "Only check for a runtime update", false).option("--download-only", "Download an available runtime update without applying it", false).option("--download", "Alias for --download-only").option("--apply", "Apply the downloaded runtime update", false).option("--channel <channel>", "Update channel (stable or beta)").option("--manifest-url <url>", "Explicit runtime update manifest URL").option("--json", "Output JSON", false).action(async (opts) => runtime.update(opts));
4821
- registerSkillsCommands(program, runtime);
4822
- registerAgentsCommands(program, runtime);
4823
- registerProjectCommands(program, runtime);
4824
- registerSessionCommands(program, runtime);
4825
- registerAppCommands(program);
4826
- const config = program.command("config").description("Manage config values");
4827
- config.command("get <path>").description("Get a config value by dot path").option("--json", "Output JSON", false).action((path, opts) => runtime.commands.config.get(path, opts));
4828
- config.command("set <path> <value>").description("Set a config value by dot path").option("--json", "Parse value as JSON", false).action((path, value, opts) => runtime.commands.config.set(path, value, opts));
4829
- config.command("unset <path>").description("Remove a config value by dot path").action((path) => runtime.commands.config.unset(path));
4830
- registerLearningLoopCommands(program, runtime);
4831
- const mcp = program.command("mcp").description("Manage MCP servers");
4832
- mcp.command("list").description("List configured MCP servers").option("--json", "Output JSON", false).action((opts) => runtime.commands.mcp.list(opts));
4833
- mcp.command("add <name> [command...]").description("Add an MCP server (stdio by default, or use --transport http|sse)").allowUnknownOption(true).option("--transport <type>", "Transport type: stdio|http|sse", "stdio").option("--url <url>", "HTTP/SSE endpoint URL").option("--header <key=value>", "Transport header (repeatable)", withRepeatableTag, []).option("--env <key=value>", "stdio env var (repeatable)", withRepeatableTag, []).option("--cwd <dir>", "stdio working directory").option("--timeout-ms <ms>", "HTTP/SSE timeout in milliseconds").option("--stderr <mode>", "stdio stderr handling: inherit|pipe|ignore", "pipe").option("--disabled", "Create the server in disabled state", false).option("--all-agents", "Expose this server to all agents", false).option("--agent <id>", "Expose to an agent id (repeatable)", withRepeatableTag, []).option("--insecure", "Disable TLS verification for HTTP/SSE", false).action(async (name, command, opts) => runtime.commands.mcp.add(name, command ?? [], opts));
4834
- mcp.command("remove <name>").description("Remove an MCP server").action(async (name) => runtime.commands.mcp.remove(name));
4835
- mcp.command("enable <name>").description("Enable an MCP server").action(async (name) => runtime.commands.mcp.enable(name));
4836
- mcp.command("disable <name>").description("Disable an MCP server").action(async (name) => runtime.commands.mcp.disable(name));
4837
- mcp.command("doctor [name]").description("Check MCP server connectivity and tool discovery").option("--json", "Output JSON", false).action(async (name, opts) => runtime.commands.mcp.doctor(name, opts));
4838
- const secrets = program.command("secrets").description("Manage secrets refs/providers");
4839
- secrets.command("audit").description("Audit secret refs resolution status").option("--json", "Output JSON", false).option("--strict", "Exit non-zero when unresolved refs exist", false).action((opts) => runtime.commands.secrets.audit(opts));
4840
- secrets.command("configure").description("Configure a secret provider alias").requiredOption("--provider <alias>", "Provider alias").option("--source <source>", "Provider source (env|file|exec)").option("--prefix <prefix>", "Env key prefix (env source)").option("--path <path>", "Secret JSON file path (file source)").option("--command <command>", "Command for exec source").option("--arg <value>", "Exec argument (repeatable)", (value, previous = []) => [...previous, value], []).option("--cwd <dir>", "Exec working directory").option("--timeout-ms <ms>", "Exec timeout in milliseconds").option("--set-default", "Set as default alias for this source", false).option("--remove", "Remove provider alias", false).option("--json", "Output JSON", false).action((opts) => runtime.commands.secrets.configure(opts));
4841
- secrets.command("apply").description("Apply secret refs/providers/defaults patch").option("--file <path>", "Apply patch from JSON file").option("--path <config-path>", "Single ref target config path").option("--source <source>", "Single ref source (env|file|exec)").option("--id <secret-id>", "Single ref secret id").option("--provider <alias>", "Single ref provider alias").option("--remove", "Remove single ref (--path required)", false).option("--enable", "Enable secrets resolution", false).option("--disable", "Disable secrets resolution", false).option("--json", "Output JSON", false).action((opts) => runtime.commands.secrets.apply(opts));
4842
- secrets.command("reload").description("Trigger runtime secrets reload signal").option("--json", "Output JSON", false).action((opts) => runtime.commands.secrets.reload(opts));
4843
- const channels = program.command("channels").description("Manage channels");
4844
- channels.command("add").description("Configure a channel").requiredOption("--channel <id>", "Channel id").option("--code <code>", "Pairing code").option("--token <token>", "Connector token").option("--name <name>", "Display name").option("--url <url>", "API base URL").option("--http-url <url>", "Alias for --url").action((opts) => runtime.commands.channels.add(opts));
4845
- channels.command("list").description("List configured channels").option("--json", "Output JSON", false).action((opts) => runtime.commands.channels.list(opts));
4846
- channels.command("status").description("Show channel status").action(() => runtime.commands.channels.status());
4847
- channels.command("login").description("Link device via QR code").option("--channel <id>", "Plugin channel id").option("--account <id>", "Channel account id").option("--url <url>", "Channel API base URL").option("--http-url <url>", "Alias for --url").option("-v, --verbose", "Verbose output", false).action(async (opts) => runtime.commands.channels.login(opts));
4848
- const cron = program.command("cron").description("Manage scheduled tasks");
4849
- cron.command("list").option("--enabled-only", "Show only enabled jobs", false).option("-a, --all", "Deprecated: list all jobs (default behavior)", false).action(async (opts) => runtime.commands.cron.list({ enabledOnly: Boolean(opts.enabledOnly) }));
4850
- cron.command("add").requiredOption("-n, --name <name>", "Job name").requiredOption("-m, --message <message>", "Message for agent").option("--agent <id>", "Target agent id").option("--session <id>", "Target NCP session id").option("-e, --every <seconds>", "Run every N seconds").option("-c, --cron <expr>", "Cron expression").option("--at <iso>", "Run once at time (ISO format)").action(async (opts) => runtime.commands.cron.add(opts));
4851
- cron.command("remove <jobId>").action(async (jobId) => runtime.commands.cron.remove(jobId));
4852
- cron.command("enable <jobId>").option("--disable", "Disable instead of enable").action(async (jobId, opts) => runtime.commands.cron.enable(jobId, opts));
4853
- cron.command("disable <jobId>").action(async (jobId) => runtime.commands.cron.enable(jobId, { disable: true }));
4854
- cron.command("run <jobId>").option("-f, --force", "Run even if disabled").action(async (jobId, opts) => runtime.commands.cron.run(jobId, opts));
4855
- program.command("status").description(`Show ${APP_NAME} status`).option("--json", "Output JSON", false).option("--verbose", "Show extra diagnostics", false).option("--fix", "Fix stale service state when safe", false).action(async (opts) => runtime.commands.diagnostics.status(opts));
4856
- program.command("doctor").description(`Run ${APP_NAME} diagnostics`).option("--json", "Output JSON", false).option("--verbose", "Show extra diagnostics", false).option("--fix", "Fix stale service state when safe", false).action(async (opts) => runtime.commands.diagnostics.doctor(opts));
4857
- const logs = program.command("logs").description("Inspect local runtime logs");
4858
- logs.command("path").description("Show local log file paths").action(() => runtime.commands.logs.path());
4859
- logs.command("tail").description("Show recent local log entries").option("--lines <n>", "Number of lines to show", "40").option("--crash", "Tail crash.log instead of service.log", false).action((opts) => runtime.commands.logs.tail(opts));
4860
- program.command("usage").description("Show observed LLM usage snapshots, history, and prompt cache stats").option("--history", "Show recent usage history", false).option("--stats", "Show aggregated usage stats from local history", false).option("--limit <n>", "Maximum number of history records to show", "10").option("--json", "Output JSON", false).action(async (opts) => runtime.commands.usage.show(opts));
4861
- await program.parseAsync(process.argv);
5
+ NextclawDistributionService.configureRuntime(createNextclawDistribution(import.meta.url));
6
+ await import("../../nextclaw-cli-app-BITGWcdZ.js");
4862
7
  //#endregion
4863
8
  export {};
4864
9