@longleash/cli 0.1.0-rc.1 → 0.1.0-rc.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/longleash.mjs CHANGED
@@ -1,11 +1,1106 @@
1
1
  #!/usr/bin/env node
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __commonJS = (cb, mod) => function __require() {
9
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+
28
+ // ../../node_modules/.pnpm/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRMode.js
29
+ var require_QRMode = __commonJS({
30
+ "../../node_modules/.pnpm/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRMode.js"(exports, module) {
31
+ module.exports = {
32
+ MODE_NUMBER: 1 << 0,
33
+ MODE_ALPHA_NUM: 1 << 1,
34
+ MODE_8BIT_BYTE: 1 << 2,
35
+ MODE_KANJI: 1 << 3
36
+ };
37
+ }
38
+ });
39
+
40
+ // ../../node_modules/.pnpm/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QR8bitByte.js
41
+ var require_QR8bitByte = __commonJS({
42
+ "../../node_modules/.pnpm/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QR8bitByte.js"(exports, module) {
43
+ var QRMode = require_QRMode();
44
+ function QR8bitByte(data) {
45
+ this.mode = QRMode.MODE_8BIT_BYTE;
46
+ this.data = data;
47
+ }
48
+ QR8bitByte.prototype = {
49
+ getLength: function() {
50
+ return this.data.length;
51
+ },
52
+ write: function(buffer) {
53
+ for (var i = 0; i < this.data.length; i++) {
54
+ buffer.put(this.data.charCodeAt(i), 8);
55
+ }
56
+ }
57
+ };
58
+ module.exports = QR8bitByte;
59
+ }
60
+ });
61
+
62
+ // ../../node_modules/.pnpm/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRMath.js
63
+ var require_QRMath = __commonJS({
64
+ "../../node_modules/.pnpm/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRMath.js"(exports, module) {
65
+ var QRMath = {
66
+ glog: function(n) {
67
+ if (n < 1) {
68
+ throw new Error("glog(" + n + ")");
69
+ }
70
+ return QRMath.LOG_TABLE[n];
71
+ },
72
+ gexp: function(n) {
73
+ while (n < 0) {
74
+ n += 255;
75
+ }
76
+ while (n >= 256) {
77
+ n -= 255;
78
+ }
79
+ return QRMath.EXP_TABLE[n];
80
+ },
81
+ EXP_TABLE: new Array(256),
82
+ LOG_TABLE: new Array(256)
83
+ };
84
+ for (i = 0; i < 8; i++) {
85
+ QRMath.EXP_TABLE[i] = 1 << i;
86
+ }
87
+ var i;
88
+ for (i = 8; i < 256; i++) {
89
+ QRMath.EXP_TABLE[i] = QRMath.EXP_TABLE[i - 4] ^ QRMath.EXP_TABLE[i - 5] ^ QRMath.EXP_TABLE[i - 6] ^ QRMath.EXP_TABLE[i - 8];
90
+ }
91
+ var i;
92
+ for (i = 0; i < 255; i++) {
93
+ QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]] = i;
94
+ }
95
+ var i;
96
+ module.exports = QRMath;
97
+ }
98
+ });
99
+
100
+ // ../../node_modules/.pnpm/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRPolynomial.js
101
+ var require_QRPolynomial = __commonJS({
102
+ "../../node_modules/.pnpm/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRPolynomial.js"(exports, module) {
103
+ var QRMath = require_QRMath();
104
+ function QRPolynomial(num, shift) {
105
+ if (num.length === void 0) {
106
+ throw new Error(num.length + "/" + shift);
107
+ }
108
+ var offset = 0;
109
+ while (offset < num.length && num[offset] === 0) {
110
+ offset++;
111
+ }
112
+ this.num = new Array(num.length - offset + shift);
113
+ for (var i = 0; i < num.length - offset; i++) {
114
+ this.num[i] = num[i + offset];
115
+ }
116
+ }
117
+ QRPolynomial.prototype = {
118
+ get: function(index) {
119
+ return this.num[index];
120
+ },
121
+ getLength: function() {
122
+ return this.num.length;
123
+ },
124
+ multiply: function(e) {
125
+ var num = new Array(this.getLength() + e.getLength() - 1);
126
+ for (var i = 0; i < this.getLength(); i++) {
127
+ for (var j = 0; j < e.getLength(); j++) {
128
+ num[i + j] ^= QRMath.gexp(QRMath.glog(this.get(i)) + QRMath.glog(e.get(j)));
129
+ }
130
+ }
131
+ return new QRPolynomial(num, 0);
132
+ },
133
+ mod: function(e) {
134
+ if (this.getLength() - e.getLength() < 0) {
135
+ return this;
136
+ }
137
+ var ratio = QRMath.glog(this.get(0)) - QRMath.glog(e.get(0));
138
+ var num = new Array(this.getLength());
139
+ for (var i = 0; i < this.getLength(); i++) {
140
+ num[i] = this.get(i);
141
+ }
142
+ for (var x = 0; x < e.getLength(); x++) {
143
+ num[x] ^= QRMath.gexp(QRMath.glog(e.get(x)) + ratio);
144
+ }
145
+ return new QRPolynomial(num, 0).mod(e);
146
+ }
147
+ };
148
+ module.exports = QRPolynomial;
149
+ }
150
+ });
151
+
152
+ // ../../node_modules/.pnpm/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRMaskPattern.js
153
+ var require_QRMaskPattern = __commonJS({
154
+ "../../node_modules/.pnpm/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRMaskPattern.js"(exports, module) {
155
+ module.exports = {
156
+ PATTERN000: 0,
157
+ PATTERN001: 1,
158
+ PATTERN010: 2,
159
+ PATTERN011: 3,
160
+ PATTERN100: 4,
161
+ PATTERN101: 5,
162
+ PATTERN110: 6,
163
+ PATTERN111: 7
164
+ };
165
+ }
166
+ });
167
+
168
+ // ../../node_modules/.pnpm/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRUtil.js
169
+ var require_QRUtil = __commonJS({
170
+ "../../node_modules/.pnpm/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRUtil.js"(exports, module) {
171
+ var QRMode = require_QRMode();
172
+ var QRPolynomial = require_QRPolynomial();
173
+ var QRMath = require_QRMath();
174
+ var QRMaskPattern = require_QRMaskPattern();
175
+ var QRUtil = {
176
+ PATTERN_POSITION_TABLE: [
177
+ [],
178
+ [6, 18],
179
+ [6, 22],
180
+ [6, 26],
181
+ [6, 30],
182
+ [6, 34],
183
+ [6, 22, 38],
184
+ [6, 24, 42],
185
+ [6, 26, 46],
186
+ [6, 28, 50],
187
+ [6, 30, 54],
188
+ [6, 32, 58],
189
+ [6, 34, 62],
190
+ [6, 26, 46, 66],
191
+ [6, 26, 48, 70],
192
+ [6, 26, 50, 74],
193
+ [6, 30, 54, 78],
194
+ [6, 30, 56, 82],
195
+ [6, 30, 58, 86],
196
+ [6, 34, 62, 90],
197
+ [6, 28, 50, 72, 94],
198
+ [6, 26, 50, 74, 98],
199
+ [6, 30, 54, 78, 102],
200
+ [6, 28, 54, 80, 106],
201
+ [6, 32, 58, 84, 110],
202
+ [6, 30, 58, 86, 114],
203
+ [6, 34, 62, 90, 118],
204
+ [6, 26, 50, 74, 98, 122],
205
+ [6, 30, 54, 78, 102, 126],
206
+ [6, 26, 52, 78, 104, 130],
207
+ [6, 30, 56, 82, 108, 134],
208
+ [6, 34, 60, 86, 112, 138],
209
+ [6, 30, 58, 86, 114, 142],
210
+ [6, 34, 62, 90, 118, 146],
211
+ [6, 30, 54, 78, 102, 126, 150],
212
+ [6, 24, 50, 76, 102, 128, 154],
213
+ [6, 28, 54, 80, 106, 132, 158],
214
+ [6, 32, 58, 84, 110, 136, 162],
215
+ [6, 26, 54, 82, 110, 138, 166],
216
+ [6, 30, 58, 86, 114, 142, 170]
217
+ ],
218
+ G15: 1 << 10 | 1 << 8 | 1 << 5 | 1 << 4 | 1 << 2 | 1 << 1 | 1 << 0,
219
+ G18: 1 << 12 | 1 << 11 | 1 << 10 | 1 << 9 | 1 << 8 | 1 << 5 | 1 << 2 | 1 << 0,
220
+ G15_MASK: 1 << 14 | 1 << 12 | 1 << 10 | 1 << 4 | 1 << 1,
221
+ getBCHTypeInfo: function(data) {
222
+ var d = data << 10;
223
+ while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15) >= 0) {
224
+ d ^= QRUtil.G15 << QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15);
225
+ }
226
+ return (data << 10 | d) ^ QRUtil.G15_MASK;
227
+ },
228
+ getBCHTypeNumber: function(data) {
229
+ var d = data << 12;
230
+ while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18) >= 0) {
231
+ d ^= QRUtil.G18 << QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18);
232
+ }
233
+ return data << 12 | d;
234
+ },
235
+ getBCHDigit: function(data) {
236
+ var digit = 0;
237
+ while (data !== 0) {
238
+ digit++;
239
+ data >>>= 1;
240
+ }
241
+ return digit;
242
+ },
243
+ getPatternPosition: function(typeNumber) {
244
+ return QRUtil.PATTERN_POSITION_TABLE[typeNumber - 1];
245
+ },
246
+ getMask: function(maskPattern, i, j) {
247
+ switch (maskPattern) {
248
+ case QRMaskPattern.PATTERN000:
249
+ return (i + j) % 2 === 0;
250
+ case QRMaskPattern.PATTERN001:
251
+ return i % 2 === 0;
252
+ case QRMaskPattern.PATTERN010:
253
+ return j % 3 === 0;
254
+ case QRMaskPattern.PATTERN011:
255
+ return (i + j) % 3 === 0;
256
+ case QRMaskPattern.PATTERN100:
257
+ return (Math.floor(i / 2) + Math.floor(j / 3)) % 2 === 0;
258
+ case QRMaskPattern.PATTERN101:
259
+ return i * j % 2 + i * j % 3 === 0;
260
+ case QRMaskPattern.PATTERN110:
261
+ return (i * j % 2 + i * j % 3) % 2 === 0;
262
+ case QRMaskPattern.PATTERN111:
263
+ return (i * j % 3 + (i + j) % 2) % 2 === 0;
264
+ default:
265
+ throw new Error("bad maskPattern:" + maskPattern);
266
+ }
267
+ },
268
+ getErrorCorrectPolynomial: function(errorCorrectLength) {
269
+ var a = new QRPolynomial([1], 0);
270
+ for (var i = 0; i < errorCorrectLength; i++) {
271
+ a = a.multiply(new QRPolynomial([1, QRMath.gexp(i)], 0));
272
+ }
273
+ return a;
274
+ },
275
+ getLengthInBits: function(mode, type) {
276
+ if (1 <= type && type < 10) {
277
+ switch (mode) {
278
+ case QRMode.MODE_NUMBER:
279
+ return 10;
280
+ case QRMode.MODE_ALPHA_NUM:
281
+ return 9;
282
+ case QRMode.MODE_8BIT_BYTE:
283
+ return 8;
284
+ case QRMode.MODE_KANJI:
285
+ return 8;
286
+ default:
287
+ throw new Error("mode:" + mode);
288
+ }
289
+ } else if (type < 27) {
290
+ switch (mode) {
291
+ case QRMode.MODE_NUMBER:
292
+ return 12;
293
+ case QRMode.MODE_ALPHA_NUM:
294
+ return 11;
295
+ case QRMode.MODE_8BIT_BYTE:
296
+ return 16;
297
+ case QRMode.MODE_KANJI:
298
+ return 10;
299
+ default:
300
+ throw new Error("mode:" + mode);
301
+ }
302
+ } else if (type < 41) {
303
+ switch (mode) {
304
+ case QRMode.MODE_NUMBER:
305
+ return 14;
306
+ case QRMode.MODE_ALPHA_NUM:
307
+ return 13;
308
+ case QRMode.MODE_8BIT_BYTE:
309
+ return 16;
310
+ case QRMode.MODE_KANJI:
311
+ return 12;
312
+ default:
313
+ throw new Error("mode:" + mode);
314
+ }
315
+ } else {
316
+ throw new Error("type:" + type);
317
+ }
318
+ },
319
+ getLostPoint: function(qrCode) {
320
+ var moduleCount = qrCode.getModuleCount();
321
+ var lostPoint = 0;
322
+ var row = 0;
323
+ var col = 0;
324
+ for (row = 0; row < moduleCount; row++) {
325
+ for (col = 0; col < moduleCount; col++) {
326
+ var sameCount = 0;
327
+ var dark = qrCode.isDark(row, col);
328
+ for (var r = -1; r <= 1; r++) {
329
+ if (row + r < 0 || moduleCount <= row + r) {
330
+ continue;
331
+ }
332
+ for (var c = -1; c <= 1; c++) {
333
+ if (col + c < 0 || moduleCount <= col + c) {
334
+ continue;
335
+ }
336
+ if (r === 0 && c === 0) {
337
+ continue;
338
+ }
339
+ if (dark === qrCode.isDark(row + r, col + c)) {
340
+ sameCount++;
341
+ }
342
+ }
343
+ }
344
+ if (sameCount > 5) {
345
+ lostPoint += 3 + sameCount - 5;
346
+ }
347
+ }
348
+ }
349
+ for (row = 0; row < moduleCount - 1; row++) {
350
+ for (col = 0; col < moduleCount - 1; col++) {
351
+ var count = 0;
352
+ if (qrCode.isDark(row, col)) count++;
353
+ if (qrCode.isDark(row + 1, col)) count++;
354
+ if (qrCode.isDark(row, col + 1)) count++;
355
+ if (qrCode.isDark(row + 1, col + 1)) count++;
356
+ if (count === 0 || count === 4) {
357
+ lostPoint += 3;
358
+ }
359
+ }
360
+ }
361
+ for (row = 0; row < moduleCount; row++) {
362
+ for (col = 0; col < moduleCount - 6; col++) {
363
+ if (qrCode.isDark(row, col) && !qrCode.isDark(row, col + 1) && qrCode.isDark(row, col + 2) && qrCode.isDark(row, col + 3) && qrCode.isDark(row, col + 4) && !qrCode.isDark(row, col + 5) && qrCode.isDark(row, col + 6)) {
364
+ lostPoint += 40;
365
+ }
366
+ }
367
+ }
368
+ for (col = 0; col < moduleCount; col++) {
369
+ for (row = 0; row < moduleCount - 6; row++) {
370
+ if (qrCode.isDark(row, col) && !qrCode.isDark(row + 1, col) && qrCode.isDark(row + 2, col) && qrCode.isDark(row + 3, col) && qrCode.isDark(row + 4, col) && !qrCode.isDark(row + 5, col) && qrCode.isDark(row + 6, col)) {
371
+ lostPoint += 40;
372
+ }
373
+ }
374
+ }
375
+ var darkCount = 0;
376
+ for (col = 0; col < moduleCount; col++) {
377
+ for (row = 0; row < moduleCount; row++) {
378
+ if (qrCode.isDark(row, col)) {
379
+ darkCount++;
380
+ }
381
+ }
382
+ }
383
+ var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5;
384
+ lostPoint += ratio * 10;
385
+ return lostPoint;
386
+ }
387
+ };
388
+ module.exports = QRUtil;
389
+ }
390
+ });
391
+
392
+ // ../../node_modules/.pnpm/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRErrorCorrectLevel.js
393
+ var require_QRErrorCorrectLevel = __commonJS({
394
+ "../../node_modules/.pnpm/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRErrorCorrectLevel.js"(exports, module) {
395
+ module.exports = {
396
+ L: 1,
397
+ M: 0,
398
+ Q: 3,
399
+ H: 2
400
+ };
401
+ }
402
+ });
403
+
404
+ // ../../node_modules/.pnpm/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRRSBlock.js
405
+ var require_QRRSBlock = __commonJS({
406
+ "../../node_modules/.pnpm/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRRSBlock.js"(exports, module) {
407
+ var QRErrorCorrectLevel = require_QRErrorCorrectLevel();
408
+ function QRRSBlock(totalCount, dataCount) {
409
+ this.totalCount = totalCount;
410
+ this.dataCount = dataCount;
411
+ }
412
+ QRRSBlock.RS_BLOCK_TABLE = [
413
+ // L
414
+ // M
415
+ // Q
416
+ // H
417
+ // 1
418
+ [1, 26, 19],
419
+ [1, 26, 16],
420
+ [1, 26, 13],
421
+ [1, 26, 9],
422
+ // 2
423
+ [1, 44, 34],
424
+ [1, 44, 28],
425
+ [1, 44, 22],
426
+ [1, 44, 16],
427
+ // 3
428
+ [1, 70, 55],
429
+ [1, 70, 44],
430
+ [2, 35, 17],
431
+ [2, 35, 13],
432
+ // 4
433
+ [1, 100, 80],
434
+ [2, 50, 32],
435
+ [2, 50, 24],
436
+ [4, 25, 9],
437
+ // 5
438
+ [1, 134, 108],
439
+ [2, 67, 43],
440
+ [2, 33, 15, 2, 34, 16],
441
+ [2, 33, 11, 2, 34, 12],
442
+ // 6
443
+ [2, 86, 68],
444
+ [4, 43, 27],
445
+ [4, 43, 19],
446
+ [4, 43, 15],
447
+ // 7
448
+ [2, 98, 78],
449
+ [4, 49, 31],
450
+ [2, 32, 14, 4, 33, 15],
451
+ [4, 39, 13, 1, 40, 14],
452
+ // 8
453
+ [2, 121, 97],
454
+ [2, 60, 38, 2, 61, 39],
455
+ [4, 40, 18, 2, 41, 19],
456
+ [4, 40, 14, 2, 41, 15],
457
+ // 9
458
+ [2, 146, 116],
459
+ [3, 58, 36, 2, 59, 37],
460
+ [4, 36, 16, 4, 37, 17],
461
+ [4, 36, 12, 4, 37, 13],
462
+ // 10
463
+ [2, 86, 68, 2, 87, 69],
464
+ [4, 69, 43, 1, 70, 44],
465
+ [6, 43, 19, 2, 44, 20],
466
+ [6, 43, 15, 2, 44, 16],
467
+ // 11
468
+ [4, 101, 81],
469
+ [1, 80, 50, 4, 81, 51],
470
+ [4, 50, 22, 4, 51, 23],
471
+ [3, 36, 12, 8, 37, 13],
472
+ // 12
473
+ [2, 116, 92, 2, 117, 93],
474
+ [6, 58, 36, 2, 59, 37],
475
+ [4, 46, 20, 6, 47, 21],
476
+ [7, 42, 14, 4, 43, 15],
477
+ // 13
478
+ [4, 133, 107],
479
+ [8, 59, 37, 1, 60, 38],
480
+ [8, 44, 20, 4, 45, 21],
481
+ [12, 33, 11, 4, 34, 12],
482
+ // 14
483
+ [3, 145, 115, 1, 146, 116],
484
+ [4, 64, 40, 5, 65, 41],
485
+ [11, 36, 16, 5, 37, 17],
486
+ [11, 36, 12, 5, 37, 13],
487
+ // 15
488
+ [5, 109, 87, 1, 110, 88],
489
+ [5, 65, 41, 5, 66, 42],
490
+ [5, 54, 24, 7, 55, 25],
491
+ [11, 36, 12],
492
+ // 16
493
+ [5, 122, 98, 1, 123, 99],
494
+ [7, 73, 45, 3, 74, 46],
495
+ [15, 43, 19, 2, 44, 20],
496
+ [3, 45, 15, 13, 46, 16],
497
+ // 17
498
+ [1, 135, 107, 5, 136, 108],
499
+ [10, 74, 46, 1, 75, 47],
500
+ [1, 50, 22, 15, 51, 23],
501
+ [2, 42, 14, 17, 43, 15],
502
+ // 18
503
+ [5, 150, 120, 1, 151, 121],
504
+ [9, 69, 43, 4, 70, 44],
505
+ [17, 50, 22, 1, 51, 23],
506
+ [2, 42, 14, 19, 43, 15],
507
+ // 19
508
+ [3, 141, 113, 4, 142, 114],
509
+ [3, 70, 44, 11, 71, 45],
510
+ [17, 47, 21, 4, 48, 22],
511
+ [9, 39, 13, 16, 40, 14],
512
+ // 20
513
+ [3, 135, 107, 5, 136, 108],
514
+ [3, 67, 41, 13, 68, 42],
515
+ [15, 54, 24, 5, 55, 25],
516
+ [15, 43, 15, 10, 44, 16],
517
+ // 21
518
+ [4, 144, 116, 4, 145, 117],
519
+ [17, 68, 42],
520
+ [17, 50, 22, 6, 51, 23],
521
+ [19, 46, 16, 6, 47, 17],
522
+ // 22
523
+ [2, 139, 111, 7, 140, 112],
524
+ [17, 74, 46],
525
+ [7, 54, 24, 16, 55, 25],
526
+ [34, 37, 13],
527
+ // 23
528
+ [4, 151, 121, 5, 152, 122],
529
+ [4, 75, 47, 14, 76, 48],
530
+ [11, 54, 24, 14, 55, 25],
531
+ [16, 45, 15, 14, 46, 16],
532
+ // 24
533
+ [6, 147, 117, 4, 148, 118],
534
+ [6, 73, 45, 14, 74, 46],
535
+ [11, 54, 24, 16, 55, 25],
536
+ [30, 46, 16, 2, 47, 17],
537
+ // 25
538
+ [8, 132, 106, 4, 133, 107],
539
+ [8, 75, 47, 13, 76, 48],
540
+ [7, 54, 24, 22, 55, 25],
541
+ [22, 45, 15, 13, 46, 16],
542
+ // 26
543
+ [10, 142, 114, 2, 143, 115],
544
+ [19, 74, 46, 4, 75, 47],
545
+ [28, 50, 22, 6, 51, 23],
546
+ [33, 46, 16, 4, 47, 17],
547
+ // 27
548
+ [8, 152, 122, 4, 153, 123],
549
+ [22, 73, 45, 3, 74, 46],
550
+ [8, 53, 23, 26, 54, 24],
551
+ [12, 45, 15, 28, 46, 16],
552
+ // 28
553
+ [3, 147, 117, 10, 148, 118],
554
+ [3, 73, 45, 23, 74, 46],
555
+ [4, 54, 24, 31, 55, 25],
556
+ [11, 45, 15, 31, 46, 16],
557
+ // 29
558
+ [7, 146, 116, 7, 147, 117],
559
+ [21, 73, 45, 7, 74, 46],
560
+ [1, 53, 23, 37, 54, 24],
561
+ [19, 45, 15, 26, 46, 16],
562
+ // 30
563
+ [5, 145, 115, 10, 146, 116],
564
+ [19, 75, 47, 10, 76, 48],
565
+ [15, 54, 24, 25, 55, 25],
566
+ [23, 45, 15, 25, 46, 16],
567
+ // 31
568
+ [13, 145, 115, 3, 146, 116],
569
+ [2, 74, 46, 29, 75, 47],
570
+ [42, 54, 24, 1, 55, 25],
571
+ [23, 45, 15, 28, 46, 16],
572
+ // 32
573
+ [17, 145, 115],
574
+ [10, 74, 46, 23, 75, 47],
575
+ [10, 54, 24, 35, 55, 25],
576
+ [19, 45, 15, 35, 46, 16],
577
+ // 33
578
+ [17, 145, 115, 1, 146, 116],
579
+ [14, 74, 46, 21, 75, 47],
580
+ [29, 54, 24, 19, 55, 25],
581
+ [11, 45, 15, 46, 46, 16],
582
+ // 34
583
+ [13, 145, 115, 6, 146, 116],
584
+ [14, 74, 46, 23, 75, 47],
585
+ [44, 54, 24, 7, 55, 25],
586
+ [59, 46, 16, 1, 47, 17],
587
+ // 35
588
+ [12, 151, 121, 7, 152, 122],
589
+ [12, 75, 47, 26, 76, 48],
590
+ [39, 54, 24, 14, 55, 25],
591
+ [22, 45, 15, 41, 46, 16],
592
+ // 36
593
+ [6, 151, 121, 14, 152, 122],
594
+ [6, 75, 47, 34, 76, 48],
595
+ [46, 54, 24, 10, 55, 25],
596
+ [2, 45, 15, 64, 46, 16],
597
+ // 37
598
+ [17, 152, 122, 4, 153, 123],
599
+ [29, 74, 46, 14, 75, 47],
600
+ [49, 54, 24, 10, 55, 25],
601
+ [24, 45, 15, 46, 46, 16],
602
+ // 38
603
+ [4, 152, 122, 18, 153, 123],
604
+ [13, 74, 46, 32, 75, 47],
605
+ [48, 54, 24, 14, 55, 25],
606
+ [42, 45, 15, 32, 46, 16],
607
+ // 39
608
+ [20, 147, 117, 4, 148, 118],
609
+ [40, 75, 47, 7, 76, 48],
610
+ [43, 54, 24, 22, 55, 25],
611
+ [10, 45, 15, 67, 46, 16],
612
+ // 40
613
+ [19, 148, 118, 6, 149, 119],
614
+ [18, 75, 47, 31, 76, 48],
615
+ [34, 54, 24, 34, 55, 25],
616
+ [20, 45, 15, 61, 46, 16]
617
+ ];
618
+ QRRSBlock.getRSBlocks = function(typeNumber, errorCorrectLevel) {
619
+ var rsBlock = QRRSBlock.getRsBlockTable(typeNumber, errorCorrectLevel);
620
+ if (rsBlock === void 0) {
621
+ throw new Error("bad rs block @ typeNumber:" + typeNumber + "/errorCorrectLevel:" + errorCorrectLevel);
622
+ }
623
+ var length = rsBlock.length / 3;
624
+ var list = [];
625
+ for (var i = 0; i < length; i++) {
626
+ var count = rsBlock[i * 3 + 0];
627
+ var totalCount = rsBlock[i * 3 + 1];
628
+ var dataCount = rsBlock[i * 3 + 2];
629
+ for (var j = 0; j < count; j++) {
630
+ list.push(new QRRSBlock(totalCount, dataCount));
631
+ }
632
+ }
633
+ return list;
634
+ };
635
+ QRRSBlock.getRsBlockTable = function(typeNumber, errorCorrectLevel) {
636
+ switch (errorCorrectLevel) {
637
+ case QRErrorCorrectLevel.L:
638
+ return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0];
639
+ case QRErrorCorrectLevel.M:
640
+ return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1];
641
+ case QRErrorCorrectLevel.Q:
642
+ return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2];
643
+ case QRErrorCorrectLevel.H:
644
+ return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3];
645
+ default:
646
+ return void 0;
647
+ }
648
+ };
649
+ module.exports = QRRSBlock;
650
+ }
651
+ });
652
+
653
+ // ../../node_modules/.pnpm/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRBitBuffer.js
654
+ var require_QRBitBuffer = __commonJS({
655
+ "../../node_modules/.pnpm/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRBitBuffer.js"(exports, module) {
656
+ function QRBitBuffer() {
657
+ this.buffer = [];
658
+ this.length = 0;
659
+ }
660
+ QRBitBuffer.prototype = {
661
+ get: function(index) {
662
+ var bufIndex = Math.floor(index / 8);
663
+ return (this.buffer[bufIndex] >>> 7 - index % 8 & 1) == 1;
664
+ },
665
+ put: function(num, length) {
666
+ for (var i = 0; i < length; i++) {
667
+ this.putBit((num >>> length - i - 1 & 1) == 1);
668
+ }
669
+ },
670
+ getLengthInBits: function() {
671
+ return this.length;
672
+ },
673
+ putBit: function(bit) {
674
+ var bufIndex = Math.floor(this.length / 8);
675
+ if (this.buffer.length <= bufIndex) {
676
+ this.buffer.push(0);
677
+ }
678
+ if (bit) {
679
+ this.buffer[bufIndex] |= 128 >>> this.length % 8;
680
+ }
681
+ this.length++;
682
+ }
683
+ };
684
+ module.exports = QRBitBuffer;
685
+ }
686
+ });
687
+
688
+ // ../../node_modules/.pnpm/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/index.js
689
+ var require_QRCode = __commonJS({
690
+ "../../node_modules/.pnpm/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/index.js"(exports, module) {
691
+ var QR8bitByte = require_QR8bitByte();
692
+ var QRUtil = require_QRUtil();
693
+ var QRPolynomial = require_QRPolynomial();
694
+ var QRRSBlock = require_QRRSBlock();
695
+ var QRBitBuffer = require_QRBitBuffer();
696
+ function QRCode(typeNumber, errorCorrectLevel) {
697
+ this.typeNumber = typeNumber;
698
+ this.errorCorrectLevel = errorCorrectLevel;
699
+ this.modules = null;
700
+ this.moduleCount = 0;
701
+ this.dataCache = null;
702
+ this.dataList = [];
703
+ }
704
+ QRCode.prototype = {
705
+ addData: function(data) {
706
+ var newData = new QR8bitByte(data);
707
+ this.dataList.push(newData);
708
+ this.dataCache = null;
709
+ },
710
+ isDark: function(row, col) {
711
+ if (row < 0 || this.moduleCount <= row || col < 0 || this.moduleCount <= col) {
712
+ throw new Error(row + "," + col);
713
+ }
714
+ return this.modules[row][col];
715
+ },
716
+ getModuleCount: function() {
717
+ return this.moduleCount;
718
+ },
719
+ make: function() {
720
+ if (this.typeNumber < 1) {
721
+ var typeNumber = 1;
722
+ for (typeNumber = 1; typeNumber < 40; typeNumber++) {
723
+ var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, this.errorCorrectLevel);
724
+ var buffer = new QRBitBuffer();
725
+ var totalDataCount = 0;
726
+ for (var i = 0; i < rsBlocks.length; i++) {
727
+ totalDataCount += rsBlocks[i].dataCount;
728
+ }
729
+ for (var x = 0; x < this.dataList.length; x++) {
730
+ var data = this.dataList[x];
731
+ buffer.put(data.mode, 4);
732
+ buffer.put(data.getLength(), QRUtil.getLengthInBits(data.mode, typeNumber));
733
+ data.write(buffer);
734
+ }
735
+ if (buffer.getLengthInBits() <= totalDataCount * 8)
736
+ break;
737
+ }
738
+ this.typeNumber = typeNumber;
739
+ }
740
+ this.makeImpl(false, this.getBestMaskPattern());
741
+ },
742
+ makeImpl: function(test, maskPattern) {
743
+ this.moduleCount = this.typeNumber * 4 + 17;
744
+ this.modules = new Array(this.moduleCount);
745
+ for (var row = 0; row < this.moduleCount; row++) {
746
+ this.modules[row] = new Array(this.moduleCount);
747
+ for (var col = 0; col < this.moduleCount; col++) {
748
+ this.modules[row][col] = null;
749
+ }
750
+ }
751
+ this.setupPositionProbePattern(0, 0);
752
+ this.setupPositionProbePattern(this.moduleCount - 7, 0);
753
+ this.setupPositionProbePattern(0, this.moduleCount - 7);
754
+ this.setupPositionAdjustPattern();
755
+ this.setupTimingPattern();
756
+ this.setupTypeInfo(test, maskPattern);
757
+ if (this.typeNumber >= 7) {
758
+ this.setupTypeNumber(test);
759
+ }
760
+ if (this.dataCache === null) {
761
+ this.dataCache = QRCode.createData(this.typeNumber, this.errorCorrectLevel, this.dataList);
762
+ }
763
+ this.mapData(this.dataCache, maskPattern);
764
+ },
765
+ setupPositionProbePattern: function(row, col) {
766
+ for (var r = -1; r <= 7; r++) {
767
+ if (row + r <= -1 || this.moduleCount <= row + r) continue;
768
+ for (var c = -1; c <= 7; c++) {
769
+ if (col + c <= -1 || this.moduleCount <= col + c) continue;
770
+ if (0 <= r && r <= 6 && (c === 0 || c === 6) || 0 <= c && c <= 6 && (r === 0 || r === 6) || 2 <= r && r <= 4 && 2 <= c && c <= 4) {
771
+ this.modules[row + r][col + c] = true;
772
+ } else {
773
+ this.modules[row + r][col + c] = false;
774
+ }
775
+ }
776
+ }
777
+ },
778
+ getBestMaskPattern: function() {
779
+ var minLostPoint = 0;
780
+ var pattern = 0;
781
+ for (var i = 0; i < 8; i++) {
782
+ this.makeImpl(true, i);
783
+ var lostPoint = QRUtil.getLostPoint(this);
784
+ if (i === 0 || minLostPoint > lostPoint) {
785
+ minLostPoint = lostPoint;
786
+ pattern = i;
787
+ }
788
+ }
789
+ return pattern;
790
+ },
791
+ createMovieClip: function(target_mc, instance_name, depth) {
792
+ var qr_mc = target_mc.createEmptyMovieClip(instance_name, depth);
793
+ var cs = 1;
794
+ this.make();
795
+ for (var row = 0; row < this.modules.length; row++) {
796
+ var y = row * cs;
797
+ for (var col = 0; col < this.modules[row].length; col++) {
798
+ var x = col * cs;
799
+ var dark = this.modules[row][col];
800
+ if (dark) {
801
+ qr_mc.beginFill(0, 100);
802
+ qr_mc.moveTo(x, y);
803
+ qr_mc.lineTo(x + cs, y);
804
+ qr_mc.lineTo(x + cs, y + cs);
805
+ qr_mc.lineTo(x, y + cs);
806
+ qr_mc.endFill();
807
+ }
808
+ }
809
+ }
810
+ return qr_mc;
811
+ },
812
+ setupTimingPattern: function() {
813
+ for (var r = 8; r < this.moduleCount - 8; r++) {
814
+ if (this.modules[r][6] !== null) {
815
+ continue;
816
+ }
817
+ this.modules[r][6] = r % 2 === 0;
818
+ }
819
+ for (var c = 8; c < this.moduleCount - 8; c++) {
820
+ if (this.modules[6][c] !== null) {
821
+ continue;
822
+ }
823
+ this.modules[6][c] = c % 2 === 0;
824
+ }
825
+ },
826
+ setupPositionAdjustPattern: function() {
827
+ var pos = QRUtil.getPatternPosition(this.typeNumber);
828
+ for (var i = 0; i < pos.length; i++) {
829
+ for (var j = 0; j < pos.length; j++) {
830
+ var row = pos[i];
831
+ var col = pos[j];
832
+ if (this.modules[row][col] !== null) {
833
+ continue;
834
+ }
835
+ for (var r = -2; r <= 2; r++) {
836
+ for (var c = -2; c <= 2; c++) {
837
+ if (Math.abs(r) === 2 || Math.abs(c) === 2 || r === 0 && c === 0) {
838
+ this.modules[row + r][col + c] = true;
839
+ } else {
840
+ this.modules[row + r][col + c] = false;
841
+ }
842
+ }
843
+ }
844
+ }
845
+ }
846
+ },
847
+ setupTypeNumber: function(test) {
848
+ var bits = QRUtil.getBCHTypeNumber(this.typeNumber);
849
+ var mod;
850
+ for (var i = 0; i < 18; i++) {
851
+ mod = !test && (bits >> i & 1) === 1;
852
+ this.modules[Math.floor(i / 3)][i % 3 + this.moduleCount - 8 - 3] = mod;
853
+ }
854
+ for (var x = 0; x < 18; x++) {
855
+ mod = !test && (bits >> x & 1) === 1;
856
+ this.modules[x % 3 + this.moduleCount - 8 - 3][Math.floor(x / 3)] = mod;
857
+ }
858
+ },
859
+ setupTypeInfo: function(test, maskPattern) {
860
+ var data = this.errorCorrectLevel << 3 | maskPattern;
861
+ var bits = QRUtil.getBCHTypeInfo(data);
862
+ var mod;
863
+ for (var v = 0; v < 15; v++) {
864
+ mod = !test && (bits >> v & 1) === 1;
865
+ if (v < 6) {
866
+ this.modules[v][8] = mod;
867
+ } else if (v < 8) {
868
+ this.modules[v + 1][8] = mod;
869
+ } else {
870
+ this.modules[this.moduleCount - 15 + v][8] = mod;
871
+ }
872
+ }
873
+ for (var h = 0; h < 15; h++) {
874
+ mod = !test && (bits >> h & 1) === 1;
875
+ if (h < 8) {
876
+ this.modules[8][this.moduleCount - h - 1] = mod;
877
+ } else if (h < 9) {
878
+ this.modules[8][15 - h - 1 + 1] = mod;
879
+ } else {
880
+ this.modules[8][15 - h - 1] = mod;
881
+ }
882
+ }
883
+ this.modules[this.moduleCount - 8][8] = !test;
884
+ },
885
+ mapData: function(data, maskPattern) {
886
+ var inc = -1;
887
+ var row = this.moduleCount - 1;
888
+ var bitIndex = 7;
889
+ var byteIndex = 0;
890
+ for (var col = this.moduleCount - 1; col > 0; col -= 2) {
891
+ if (col === 6) col--;
892
+ while (true) {
893
+ for (var c = 0; c < 2; c++) {
894
+ if (this.modules[row][col - c] === null) {
895
+ var dark = false;
896
+ if (byteIndex < data.length) {
897
+ dark = (data[byteIndex] >>> bitIndex & 1) === 1;
898
+ }
899
+ var mask = QRUtil.getMask(maskPattern, row, col - c);
900
+ if (mask) {
901
+ dark = !dark;
902
+ }
903
+ this.modules[row][col - c] = dark;
904
+ bitIndex--;
905
+ if (bitIndex === -1) {
906
+ byteIndex++;
907
+ bitIndex = 7;
908
+ }
909
+ }
910
+ }
911
+ row += inc;
912
+ if (row < 0 || this.moduleCount <= row) {
913
+ row -= inc;
914
+ inc = -inc;
915
+ break;
916
+ }
917
+ }
918
+ }
919
+ }
920
+ };
921
+ QRCode.PAD0 = 236;
922
+ QRCode.PAD1 = 17;
923
+ QRCode.createData = function(typeNumber, errorCorrectLevel, dataList) {
924
+ var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectLevel);
925
+ var buffer = new QRBitBuffer();
926
+ for (var i = 0; i < dataList.length; i++) {
927
+ var data = dataList[i];
928
+ buffer.put(data.mode, 4);
929
+ buffer.put(data.getLength(), QRUtil.getLengthInBits(data.mode, typeNumber));
930
+ data.write(buffer);
931
+ }
932
+ var totalDataCount = 0;
933
+ for (var x = 0; x < rsBlocks.length; x++) {
934
+ totalDataCount += rsBlocks[x].dataCount;
935
+ }
936
+ if (buffer.getLengthInBits() > totalDataCount * 8) {
937
+ throw new Error("code length overflow. (" + buffer.getLengthInBits() + ">" + totalDataCount * 8 + ")");
938
+ }
939
+ if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) {
940
+ buffer.put(0, 4);
941
+ }
942
+ while (buffer.getLengthInBits() % 8 !== 0) {
943
+ buffer.putBit(false);
944
+ }
945
+ while (true) {
946
+ if (buffer.getLengthInBits() >= totalDataCount * 8) {
947
+ break;
948
+ }
949
+ buffer.put(QRCode.PAD0, 8);
950
+ if (buffer.getLengthInBits() >= totalDataCount * 8) {
951
+ break;
952
+ }
953
+ buffer.put(QRCode.PAD1, 8);
954
+ }
955
+ return QRCode.createBytes(buffer, rsBlocks);
956
+ };
957
+ QRCode.createBytes = function(buffer, rsBlocks) {
958
+ var offset = 0;
959
+ var maxDcCount = 0;
960
+ var maxEcCount = 0;
961
+ var dcdata = new Array(rsBlocks.length);
962
+ var ecdata = new Array(rsBlocks.length);
963
+ for (var r = 0; r < rsBlocks.length; r++) {
964
+ var dcCount = rsBlocks[r].dataCount;
965
+ var ecCount = rsBlocks[r].totalCount - dcCount;
966
+ maxDcCount = Math.max(maxDcCount, dcCount);
967
+ maxEcCount = Math.max(maxEcCount, ecCount);
968
+ dcdata[r] = new Array(dcCount);
969
+ for (var i = 0; i < dcdata[r].length; i++) {
970
+ dcdata[r][i] = 255 & buffer.buffer[i + offset];
971
+ }
972
+ offset += dcCount;
973
+ var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount);
974
+ var rawPoly = new QRPolynomial(dcdata[r], rsPoly.getLength() - 1);
975
+ var modPoly = rawPoly.mod(rsPoly);
976
+ ecdata[r] = new Array(rsPoly.getLength() - 1);
977
+ for (var x = 0; x < ecdata[r].length; x++) {
978
+ var modIndex = x + modPoly.getLength() - ecdata[r].length;
979
+ ecdata[r][x] = modIndex >= 0 ? modPoly.get(modIndex) : 0;
980
+ }
981
+ }
982
+ var totalCodeCount = 0;
983
+ for (var y = 0; y < rsBlocks.length; y++) {
984
+ totalCodeCount += rsBlocks[y].totalCount;
985
+ }
986
+ var data = new Array(totalCodeCount);
987
+ var index = 0;
988
+ for (var z = 0; z < maxDcCount; z++) {
989
+ for (var s = 0; s < rsBlocks.length; s++) {
990
+ if (z < dcdata[s].length) {
991
+ data[index++] = dcdata[s][z];
992
+ }
993
+ }
994
+ }
995
+ for (var xx = 0; xx < maxEcCount; xx++) {
996
+ for (var t = 0; t < rsBlocks.length; t++) {
997
+ if (xx < ecdata[t].length) {
998
+ data[index++] = ecdata[t][xx];
999
+ }
1000
+ }
1001
+ }
1002
+ return data;
1003
+ };
1004
+ module.exports = QRCode;
1005
+ }
1006
+ });
1007
+
1008
+ // ../../node_modules/.pnpm/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/lib/main.js
1009
+ var require_main = __commonJS({
1010
+ "../../node_modules/.pnpm/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/lib/main.js"(exports, module) {
1011
+ var QRCode = require_QRCode();
1012
+ var QRErrorCorrectLevel = require_QRErrorCorrectLevel();
1013
+ var black = "\x1B[40m \x1B[0m";
1014
+ var white = "\x1B[47m \x1B[0m";
1015
+ var toCell = function(isBlack) {
1016
+ return isBlack ? black : white;
1017
+ };
1018
+ var repeat = function(color) {
1019
+ return {
1020
+ times: function(count) {
1021
+ return new Array(count).join(color);
1022
+ }
1023
+ };
1024
+ };
1025
+ var fill = function(length, value) {
1026
+ var arr = new Array(length);
1027
+ for (var i = 0; i < length; i++) {
1028
+ arr[i] = value;
1029
+ }
1030
+ return arr;
1031
+ };
1032
+ module.exports = {
1033
+ error: QRErrorCorrectLevel.L,
1034
+ generate: function(input2, opts, cb) {
1035
+ if (typeof opts === "function") {
1036
+ cb = opts;
1037
+ opts = {};
1038
+ }
1039
+ var qrcode2 = new QRCode(-1, this.error);
1040
+ qrcode2.addData(input2);
1041
+ qrcode2.make();
1042
+ var output2 = "";
1043
+ if (opts && opts.small) {
1044
+ var BLACK = true, WHITE = false;
1045
+ var moduleCount = qrcode2.getModuleCount();
1046
+ var moduleData = qrcode2.modules.slice();
1047
+ var oddRow = moduleCount % 2 === 1;
1048
+ if (oddRow) {
1049
+ moduleData.push(fill(moduleCount, WHITE));
1050
+ }
1051
+ var platte = {
1052
+ WHITE_ALL: "\u2588",
1053
+ WHITE_BLACK: "\u2580",
1054
+ BLACK_WHITE: "\u2584",
1055
+ BLACK_ALL: " "
1056
+ };
1057
+ var borderTop = repeat(platte.BLACK_WHITE).times(moduleCount + 3);
1058
+ var borderBottom = repeat(platte.WHITE_BLACK).times(moduleCount + 3);
1059
+ output2 += borderTop + "\n";
1060
+ for (var row = 0; row < moduleCount; row += 2) {
1061
+ output2 += platte.WHITE_ALL;
1062
+ for (var col = 0; col < moduleCount; col++) {
1063
+ if (moduleData[row][col] === WHITE && moduleData[row + 1][col] === WHITE) {
1064
+ output2 += platte.WHITE_ALL;
1065
+ } else if (moduleData[row][col] === WHITE && moduleData[row + 1][col] === BLACK) {
1066
+ output2 += platte.WHITE_BLACK;
1067
+ } else if (moduleData[row][col] === BLACK && moduleData[row + 1][col] === WHITE) {
1068
+ output2 += platte.BLACK_WHITE;
1069
+ } else {
1070
+ output2 += platte.BLACK_ALL;
1071
+ }
1072
+ }
1073
+ output2 += platte.WHITE_ALL + "\n";
1074
+ }
1075
+ if (!oddRow) {
1076
+ output2 += borderBottom;
1077
+ }
1078
+ } else {
1079
+ var border = repeat(white).times(qrcode2.getModuleCount() + 3);
1080
+ output2 += border + "\n";
1081
+ qrcode2.modules.forEach(function(row2) {
1082
+ output2 += white;
1083
+ output2 += row2.map(toCell).join("");
1084
+ output2 += white + "\n";
1085
+ });
1086
+ output2 += border;
1087
+ }
1088
+ if (cb) cb(output2);
1089
+ else console.log(output2);
1090
+ },
1091
+ setErrorLevel: function(error) {
1092
+ this.error = QRErrorCorrectLevel[error] || this.error;
1093
+ }
1094
+ };
1095
+ }
1096
+ });
2
1097
 
3
1098
  // src/cli.ts
4
- import { existsSync as existsSync2, readFileSync as readFileSync3 } from "node:fs";
5
- import { homedir as homedir3 } from "node:os";
6
- import { delimiter, dirname as dirname3, join as join3, resolve as resolve3 } from "node:path";
1099
+ import { existsSync as existsSync3, readFileSync as readFileSync4 } from "node:fs";
1100
+ import { homedir as homedir4 } from "node:os";
1101
+ import { delimiter, dirname as dirname4, join as join4, resolve as resolve4 } from "node:path";
7
1102
  import { fileURLToPath } from "node:url";
8
- import { spawn, spawnSync as spawnSync2 } from "node:child_process";
1103
+ import { spawn, spawnSync as spawnSync3 } from "node:child_process";
9
1104
  import { createInterface } from "node:readline/promises";
10
1105
  import { stdin as input, stdout as output } from "node:process";
11
1106
 
@@ -100,12 +1195,12 @@ function captureConfigSnapshot(path = configPath()) {
100
1195
  previous: captureFile(`${path}.previous`)
101
1196
  };
102
1197
  }
103
- function restoreConfigSnapshot(snapshot) {
104
- const previous = `${snapshot.path}.previous`;
105
- assertNotSymlink(snapshot.path);
1198
+ function restoreConfigSnapshot(snapshot2) {
1199
+ const previous = `${snapshot2.path}.previous`;
1200
+ assertNotSymlink(snapshot2.path);
106
1201
  assertNotSymlink(previous);
107
- restoreFile(previous, snapshot.previous);
108
- restoreFile(snapshot.path, snapshot.config);
1202
+ restoreFile(previous, snapshot2.previous);
1203
+ restoreFile(snapshot2.path, snapshot2.config);
109
1204
  }
110
1205
  function configuredRoots(config) {
111
1206
  return Array.isArray(config.allowedRoots) ? config.allowedRoots.filter((root) => typeof root === "string" && root !== "") : [];
@@ -129,13 +1224,13 @@ function pathExists(path) {
129
1224
  throw error;
130
1225
  }
131
1226
  }
132
- function restoreFile(path, snapshot) {
133
- if (snapshot.content === null) {
1227
+ function restoreFile(path, snapshot2) {
1228
+ if (snapshot2.content === null) {
134
1229
  rmSync(path, { force: true });
135
1230
  return;
136
1231
  }
137
1232
  mkdirSync(dirname(path), { recursive: true, mode: 448 });
138
- writeBytesAtomically(path, snapshot.content, snapshot.mode);
1233
+ writeBytesAtomically(path, snapshot2.content, snapshot2.mode);
139
1234
  }
140
1235
  function writeBytesAtomically(path, content, mode) {
141
1236
  const temp = createTemporaryFile(path, content, mode, "atomic");
@@ -205,6 +1300,11 @@ function assertVersion(value) {
205
1300
  }
206
1301
  return value;
207
1302
  }
1303
+ function updatePackageSpec(target, currentVersion) {
1304
+ const selected = target ?? (currentVersion.includes("-") ? "rc" : "latest");
1305
+ if (selected === "latest" || selected === "rc") return `@longleash/cli@${selected}`;
1306
+ return `@longleash/cli@${assertVersion(selected)}`;
1307
+ }
208
1308
  function packageLocation(prefix) {
209
1309
  return join2(prefix, "node_modules", "@longleash", "cli");
210
1310
  }
@@ -301,27 +1401,42 @@ function prepareManagedInstall(version, env = process.env) {
301
1401
  const hadWrapper = pathExists2(paths.wrapper);
302
1402
  const hadMarker = pathExists2(marker);
303
1403
  let previousWrapper;
1404
+ let previousWrapperMode = 448;
304
1405
  let previousMarker;
305
1406
  let wrapperWritten = false;
306
1407
  let markerWritten = false;
1408
+ let migratedLegacyWrapper = false;
1409
+ let legacyWrapper;
307
1410
  const tempLink = join2(paths.home, `.current-${process.pid}-${randomUUID2()}`);
308
1411
  try {
309
1412
  mkdirSync2(paths.bin, { recursive: true, mode: 448 });
310
1413
  if (hadWrapper) {
311
- if (lstatSync2(paths.wrapper).isSymbolicLink()) throw new Error(`Refusing symlinked executable: ${paths.wrapper}`);
1414
+ const wrapperStat = lstatSync2(paths.wrapper);
1415
+ if (wrapperStat.isSymbolicLink()) throw new Error(`Refusing symlinked executable: ${paths.wrapper}`);
1416
+ if (!wrapperStat.isFile()) throw new Error(`Refusing non-file executable: ${paths.wrapper}`);
1417
+ if (process.getuid !== void 0 && wrapperStat.uid !== process.getuid()) {
1418
+ throw new Error(`Refusing executable owned by another user: ${paths.wrapper}`);
1419
+ }
312
1420
  previousWrapper = readFileSync2(paths.wrapper, "utf8");
1421
+ previousWrapperMode = wrapperStat.mode & 511;
313
1422
  if (!previousWrapper.includes(WRAPPER_MARKER)) {
314
- throw new Error(`Refusing to replace an unmanaged executable: ${paths.wrapper}`);
1423
+ legacyWrapper = recognizeLegacyWrapper(previousWrapper, wrapperStat.mode & 511, paths.wrapper);
1424
+ migratedLegacyWrapper = true;
315
1425
  }
316
1426
  }
317
1427
  if (hadMarker) {
318
1428
  if (lstatSync2(marker).isSymbolicLink()) throw new Error(`Refusing symlinked install marker: ${marker}`);
319
1429
  previousMarker = readFileSync2(marker, "utf8");
1430
+ const parsed = parseInstallMarker(previousMarker, marker);
1431
+ if (parsed.package !== PACKAGE_NAME) throw new Error(`Refusing unrecognized install marker: ${marker}`);
1432
+ legacyWrapper = parsed.legacyWrapper ?? legacyWrapper;
320
1433
  }
321
1434
  const activeCli = join2(paths.current, "node_modules", "@longleash", "cli", "bin", "longleash.mjs");
322
- writeExecutableAtomically(paths.wrapper, wrapper(activeCli));
1435
+ writeExecutableAtomically(paths.wrapper, wrapper(activeCli, process.execPath));
323
1436
  wrapperWritten = true;
324
- writeFileAtomically(marker, `${JSON.stringify({ schema: 1, package: PACKAGE_NAME }, null, 2)}
1437
+ const nextMarker = { schema: 1, package: PACKAGE_NAME };
1438
+ if (legacyWrapper) nextMarker.legacyWrapper = legacyWrapper;
1439
+ writeFileAtomically(marker, `${JSON.stringify(nextMarker, null, 2)}
325
1440
  `, 384);
326
1441
  markerWritten = true;
327
1442
  symlinkSync(release, tempLink, "dir");
@@ -330,6 +1445,7 @@ function prepareManagedInstall(version, env = process.env) {
330
1445
  }
331
1446
  renameSync2(tempLink, paths.current);
332
1447
  activated = true;
1448
+ return { migratedLegacyWrapper };
333
1449
  } catch (error) {
334
1450
  try {
335
1451
  rmSync2(tempLink, { force: true });
@@ -338,7 +1454,7 @@ function prepareManagedInstall(version, env = process.env) {
338
1454
  const restorationFailures = [];
339
1455
  if (wrapperWritten) {
340
1456
  try {
341
- restoreManagedFile(paths.wrapper, hadWrapper ? previousWrapper : void 0, 448);
1457
+ restoreManagedFile(paths.wrapper, hadWrapper ? previousWrapper : void 0, previousWrapperMode);
342
1458
  } catch (restoreError) {
343
1459
  restorationFailures.push(`executable: ${String(restoreError)}`);
344
1460
  }
@@ -378,23 +1494,83 @@ function prepareManagedInstall(version, env = process.env) {
378
1494
  function uninstallManagedRuntime(env = process.env) {
379
1495
  const paths = installPaths(env);
380
1496
  const marker = join2(paths.home, MARKER);
381
- if (!pathExists2(marker)) return { removed: false, configPreserved: resolve2(env.LONGLEASH_DATA ?? join2(homedir2(), ".longleash")) };
1497
+ if (!pathExists2(marker)) {
1498
+ return {
1499
+ removed: false,
1500
+ configPreserved: resolve2(env.LONGLEASH_DATA ?? join2(homedir2(), ".longleash")),
1501
+ legacyWrapperRestored: false
1502
+ };
1503
+ }
382
1504
  if (lstatSync2(marker).isSymbolicLink()) throw new Error(`Refusing symlinked install marker: ${marker}`);
383
- const parsed = JSON.parse(readFileSync2(marker, "utf8"));
1505
+ const parsed = parseInstallMarker(readFileSync2(marker, "utf8"), marker);
384
1506
  if (parsed.package !== PACKAGE_NAME) throw new Error(`Refusing to remove unrecognized install directory: ${paths.home}`);
1507
+ let legacyWrapperRestored = false;
385
1508
  if (pathExists2(paths.wrapper)) {
386
1509
  if (lstatSync2(paths.wrapper).isSymbolicLink()) throw new Error(`Refusing symlinked executable: ${paths.wrapper}`);
387
1510
  const wrapperText = readFileSync2(paths.wrapper, "utf8");
388
1511
  if (!wrapperText.includes(WRAPPER_MARKER)) throw new Error(`Refusing to remove an unmanaged executable: ${paths.wrapper}`);
389
- rmSync2(paths.wrapper);
1512
+ if (parsed.legacyWrapper) {
1513
+ const backup = validateLegacyBackup(parsed.legacyWrapper, paths.wrapper);
1514
+ writeFileAtomically(paths.wrapper, backup.content, backup.mode);
1515
+ legacyWrapperRestored = true;
1516
+ } else {
1517
+ rmSync2(paths.wrapper);
1518
+ }
390
1519
  }
391
1520
  rmSync2(paths.home, { recursive: true });
392
- return { removed: true, configPreserved: resolve2(env.LONGLEASH_DATA ?? join2(homedir2(), ".longleash")) };
1521
+ return {
1522
+ removed: true,
1523
+ configPreserved: resolve2(env.LONGLEASH_DATA ?? join2(homedir2(), ".longleash")),
1524
+ legacyWrapperRestored
1525
+ };
1526
+ }
1527
+ function recognizeLegacyWrapper(content, mode, wrapperPath) {
1528
+ if (Buffer.byteLength(content, "utf8") > 16 * 1024) {
1529
+ throw new Error(`Refusing to replace an unmanaged executable: ${wrapperPath}`);
1530
+ }
1531
+ const match = content.match(
1532
+ /^#!\/usr\/bin\/env bash\n# Created by the LongLeash installer\. The three values below are the settings; the behaviour\n# lives in \$LONGLEASH_DIR\/scripts\/longleash\.sh and updates with the code\.\nset -euo pipefail\nexport LONGLEASH_DIR="([^"\n]+)"\nexport LONGLEASH_DEFAULT_ROOTS="([^"\n]*)"\nexport LONGLEASH_DEFAULT_RELAY="([^"\n]*)"\nexec bash "\$LONGLEASH_DIR\/scripts\/longleash\.sh" "\$@"\n?$/
1533
+ );
1534
+ if (!match) throw new Error(`Refusing to replace an unmanaged executable: ${wrapperPath}`);
1535
+ const legacyRoot = match[1];
1536
+ if (!legacyRoot || resolve2(legacyRoot) !== legacyRoot) {
1537
+ throw new Error(`Refusing legacy executable with an unsafe checkout path: ${wrapperPath}`);
1538
+ }
1539
+ const legacyEntrypoint = join2(legacyRoot, "scripts", "longleash.sh");
1540
+ if (!pathExists2(legacyEntrypoint) || lstatSync2(legacyEntrypoint).isSymbolicLink() || !lstatSync2(legacyEntrypoint).isFile()) {
1541
+ throw new Error(`The verified legacy executable points to a missing checkout: ${legacyRoot}`);
1542
+ }
1543
+ return { content, mode: normalizeExecutableMode(mode) };
1544
+ }
1545
+ function validateLegacyBackup(value, wrapperPath) {
1546
+ if (!value || typeof value.content !== "string" || !Number.isSafeInteger(value.mode)) {
1547
+ throw new Error(`Refusing invalid legacy executable backup in the install marker: ${wrapperPath}`);
1548
+ }
1549
+ return recognizeLegacyWrapper(value.content, value.mode, wrapperPath);
1550
+ }
1551
+ function parseInstallMarker(content, markerPath) {
1552
+ let parsed;
1553
+ try {
1554
+ parsed = JSON.parse(content);
1555
+ } catch {
1556
+ throw new Error(`Refusing invalid install marker: ${markerPath}`);
1557
+ }
1558
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1559
+ throw new Error(`Refusing invalid install marker: ${markerPath}`);
1560
+ }
1561
+ const marker = parsed;
1562
+ if (marker.legacyWrapper !== void 0) validateLegacyBackup(marker.legacyWrapper, markerPath);
1563
+ return marker;
1564
+ }
1565
+ function normalizeExecutableMode(mode) {
1566
+ const normalized = mode & 511;
1567
+ if ((normalized & 64) === 0) throw new Error("The legacy LongLeash executable is not owner-executable.");
1568
+ return normalized;
393
1569
  }
394
- function wrapper(cli) {
1570
+ function wrapper(cli, node) {
395
1571
  return `#!/bin/sh
396
1572
  ${WRAPPER_MARKER}
397
- exec node ${shellQuote(cli)} "$@"
1573
+ exec ${shellQuote(node)} ${shellQuote(cli)} "$@"
398
1574
  `;
399
1575
  }
400
1576
  function shellQuote(value) {
@@ -516,15 +1692,544 @@ function processAlive(pid) {
516
1692
  }
517
1693
  }
518
1694
 
1695
+ // src/setup-input.ts
1696
+ function resolveAllowedRootAnswer(answer, defaultRoot) {
1697
+ const trimmed = answer.trim();
1698
+ if (/^(?:y|yes|n|no)$/i.test(trimmed)) {
1699
+ return {
1700
+ ok: false,
1701
+ message: `This question needs a folder path. Press Enter to use ${defaultRoot}, or type another existing folder.`
1702
+ };
1703
+ }
1704
+ try {
1705
+ const [root] = normalizeRoots([trimmed || defaultRoot]);
1706
+ if (!root) throw new Error("At least one allowed project directory is required.");
1707
+ return { ok: true, root };
1708
+ } catch (error) {
1709
+ return {
1710
+ ok: false,
1711
+ message: `That folder cannot be used: ${error instanceof Error ? error.message : String(error)}`
1712
+ };
1713
+ }
1714
+ }
1715
+
1716
+ // src/service.ts
1717
+ import {
1718
+ chmodSync as chmodSync2,
1719
+ existsSync as existsSync2,
1720
+ lstatSync as lstatSync3,
1721
+ mkdirSync as mkdirSync3,
1722
+ readFileSync as readFileSync3,
1723
+ renameSync as renameSync3,
1724
+ rmSync as rmSync3,
1725
+ writeFileSync as writeFileSync3
1726
+ } from "node:fs";
1727
+ import { homedir as homedir3 } from "node:os";
1728
+ import { dirname as dirname3, join as join3, resolve as resolve3 } from "node:path";
1729
+ import { randomUUID as randomUUID3 } from "node:crypto";
1730
+ import { spawnSync as spawnSync2 } from "node:child_process";
1731
+ var SERVICE_LABEL = "dev.longleash.daemon";
1732
+ var SYSTEMD_UNIT = "longleash.service";
1733
+ var MANAGED_MARKER = "Managed by @longleash/cli";
1734
+ function servicePaths(context = {}) {
1735
+ const env = context.env ?? process.env;
1736
+ const platform = supportedPlatform(context.platform ?? process.platform);
1737
+ const home = resolve3(context.home ?? homedir3());
1738
+ const data = dataDir(env);
1739
+ const logs = join3(data, "logs");
1740
+ const wrapper2 = installPaths(env).wrapper;
1741
+ if (platform === "darwin") {
1742
+ return {
1743
+ data,
1744
+ logs,
1745
+ stdout: join3(logs, "daemon.stdout.log"),
1746
+ stderr: join3(logs, "daemon.stderr.log"),
1747
+ definition: join3(home, "Library", "LaunchAgents", `${SERVICE_LABEL}.plist`),
1748
+ wrapper: wrapper2
1749
+ };
1750
+ }
1751
+ return {
1752
+ data,
1753
+ logs,
1754
+ stdout: "journal",
1755
+ stderr: "journal",
1756
+ definition: join3(home, ".config", "systemd", "user", SYSTEMD_UNIT),
1757
+ environment: join3(home, ".config", "longleash", "service.env"),
1758
+ wrapper: wrapper2
1759
+ };
1760
+ }
1761
+ function renderLaunchAgent(paths, env = process.env) {
1762
+ const path = requiredEnvironment(env, "PATH", "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin");
1763
+ const home = resolve3(env.HOME ?? homedir3());
1764
+ return `<?xml version="1.0" encoding="UTF-8"?>
1765
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
1766
+ <!-- ${MANAGED_MARKER} -->
1767
+ <plist version="1.0">
1768
+ <dict>
1769
+ <key>Label</key>
1770
+ <string>${xml(SERVICE_LABEL)}</string>
1771
+ <key>ProgramArguments</key>
1772
+ <array>
1773
+ <string>${xml(paths.wrapper)}</string>
1774
+ <string>run</string>
1775
+ </array>
1776
+ <key>EnvironmentVariables</key>
1777
+ <dict>
1778
+ <key>HOME</key>
1779
+ <string>${xml(home)}</string>
1780
+ <key>PATH</key>
1781
+ <string>${xml(path)}</string>
1782
+ <key>LONGLEASH_DATA</key>
1783
+ <string>${xml(paths.data)}</string>
1784
+ <key>LONGLEASH_SERVICE</key>
1785
+ <string>1</string>
1786
+ </dict>
1787
+ <key>WorkingDirectory</key>
1788
+ <string>${xml(home)}</string>
1789
+ <key>RunAtLoad</key>
1790
+ <true/>
1791
+ <key>KeepAlive</key>
1792
+ <true/>
1793
+ <key>ThrottleInterval</key>
1794
+ <integer>15</integer>
1795
+ <key>ProcessType</key>
1796
+ <string>Background</string>
1797
+ <key>Umask</key>
1798
+ <integer>63</integer>
1799
+ <key>StandardOutPath</key>
1800
+ <string>${xml(paths.stdout)}</string>
1801
+ <key>StandardErrorPath</key>
1802
+ <string>${xml(paths.stderr)}</string>
1803
+ </dict>
1804
+ </plist>
1805
+ `;
1806
+ }
1807
+ function renderSystemdUnit(paths, home) {
1808
+ if (!paths.environment) throw new Error("The Linux service environment path is missing.");
1809
+ return `# ${MANAGED_MARKER}
1810
+ [Unit]
1811
+ Description=LongLeash phone control plane
1812
+ Documentation=https://longleash.dev/docs/background-service
1813
+ StartLimitIntervalSec=60
1814
+ StartLimitBurst=5
1815
+
1816
+ [Service]
1817
+ Type=simple
1818
+ ExecStart=${systemdQuote(paths.wrapper)} run
1819
+ EnvironmentFile=${systemdPath(paths.environment)}
1820
+ WorkingDirectory=${systemdPath(home)}
1821
+ UMask=0077
1822
+ Restart=on-failure
1823
+ RestartSec=5s
1824
+ TimeoutStopSec=30s
1825
+ KillMode=mixed
1826
+
1827
+ [Install]
1828
+ WantedBy=default.target
1829
+ `;
1830
+ }
1831
+ function renderSystemdEnvironment(paths, env = process.env) {
1832
+ const home = resolve3(env.HOME ?? homedir3());
1833
+ const path = requiredEnvironment(env, "PATH", "/usr/local/bin:/usr/bin:/bin");
1834
+ return `# ${MANAGED_MARKER}
1835
+ HOME=${environmentQuote(home)}
1836
+ PATH=${environmentQuote(path)}
1837
+ LONGLEASH_DATA=${environmentQuote(paths.data)}
1838
+ LONGLEASH_SERVICE=1
1839
+ `;
1840
+ }
1841
+ function serviceState(context = {}) {
1842
+ const resolved = resolveContext(context);
1843
+ const installed = managedDefinitionExists(resolved.paths.definition);
1844
+ if (installed && resolved.paths.environment) assertManagedDefinition(resolved.paths.environment);
1845
+ if (resolved.platform === "darwin") {
1846
+ const loaded2 = launchdLoaded(resolved);
1847
+ return {
1848
+ platform: "darwin",
1849
+ installed,
1850
+ loaded: loaded2,
1851
+ active: loaded2,
1852
+ definition: resolved.paths.definition,
1853
+ logs: resolved.paths.logs,
1854
+ loginOnly: false
1855
+ };
1856
+ }
1857
+ const loaded = run(resolved, "systemctl", ["--user", "is-enabled", "--quiet", SYSTEMD_UNIT]).status === 0;
1858
+ const active = run(resolved, "systemctl", ["--user", "is-active", "--quiet", SYSTEMD_UNIT]).status === 0;
1859
+ return {
1860
+ platform: "linux",
1861
+ installed,
1862
+ loaded,
1863
+ active,
1864
+ definition: resolved.paths.definition,
1865
+ logs: "journalctl --user-unit longleash.service",
1866
+ loginOnly: !lingerEnabled(resolved)
1867
+ };
1868
+ }
1869
+ function installService(context = {}) {
1870
+ const resolved = resolveContext(context);
1871
+ assertManagedWrapper(resolved.paths.wrapper);
1872
+ mkdirSync3(resolved.paths.logs, { recursive: true, mode: 448 });
1873
+ if (resolved.platform === "darwin") installLaunchAgent(resolved);
1874
+ else installSystemdService(resolved);
1875
+ return serviceState(context);
1876
+ }
1877
+ function startService(context = {}) {
1878
+ const resolved = resolveContext(context);
1879
+ assertServiceInstallation(resolved);
1880
+ if (resolved.platform === "darwin") {
1881
+ if (launchdLoaded(resolved)) requireSuccess(resolved, "/bin/launchctl", ["kickstart", "-k", serviceTarget(resolved)]);
1882
+ else bootstrapLaunchAgent(resolved);
1883
+ } else {
1884
+ requireSuccess(resolved, "systemctl", ["--user", "start", SYSTEMD_UNIT]);
1885
+ }
1886
+ return serviceState(context);
1887
+ }
1888
+ function stopService(context = {}) {
1889
+ const resolved = resolveContext(context);
1890
+ if (resolved.platform === "darwin") {
1891
+ const loaded = launchdLoaded(resolved);
1892
+ if (loaded) {
1893
+ assertManagedDefinition(resolved.paths.definition);
1894
+ requireSuccess(resolved, "/bin/launchctl", ["bootout", serviceTarget(resolved)]);
1895
+ }
1896
+ } else if (run(resolved, "systemctl", ["--user", "is-active", "--quiet", SYSTEMD_UNIT]).status === 0) {
1897
+ assertManagedDefinition(resolved.paths.definition);
1898
+ requireSuccess(resolved, "systemctl", ["--user", "stop", SYSTEMD_UNIT]);
1899
+ }
1900
+ return serviceState(context);
1901
+ }
1902
+ function restartService(context = {}) {
1903
+ const resolved = resolveContext(context);
1904
+ assertServiceInstallation(resolved);
1905
+ if (resolved.platform === "darwin") {
1906
+ if (launchdLoaded(resolved)) requireSuccess(resolved, "/bin/launchctl", ["kickstart", "-k", serviceTarget(resolved)]);
1907
+ else bootstrapLaunchAgent(resolved);
1908
+ } else {
1909
+ requireSuccess(resolved, "systemctl", ["--user", "restart", SYSTEMD_UNIT]);
1910
+ }
1911
+ return serviceState(context);
1912
+ }
1913
+ function uninstallService(context = {}) {
1914
+ const resolved = resolveContext(context);
1915
+ const installed = managedDefinitionExists(resolved.paths.definition);
1916
+ if (resolved.platform === "darwin") {
1917
+ const loaded = launchdLoaded(resolved);
1918
+ if (loaded && !installed) {
1919
+ throw new Error(`Refusing to stop an unowned launchd job without a managed definition: ${resolved.paths.definition}`);
1920
+ }
1921
+ if (loaded) requireSuccess(resolved, "/bin/launchctl", ["bootout", serviceTarget(resolved)]);
1922
+ if (installed) rmSync3(resolved.paths.definition);
1923
+ } else {
1924
+ if (installed) {
1925
+ const disabled = run(resolved, "systemctl", ["--user", "disable", "--now", SYSTEMD_UNIT]);
1926
+ if (disabled.status !== 0 && run(resolved, "systemctl", ["--user", "is-enabled", "--quiet", SYSTEMD_UNIT]).status === 0) {
1927
+ throw commandError("systemctl", ["--user", "disable", "--now", SYSTEMD_UNIT], disabled);
1928
+ }
1929
+ rmSync3(resolved.paths.definition);
1930
+ }
1931
+ if (resolved.paths.environment && managedFileExists(resolved.paths.environment)) {
1932
+ assertManagedDefinition(resolved.paths.environment);
1933
+ rmSync3(resolved.paths.environment);
1934
+ }
1935
+ requireSuccess(resolved, "systemctl", ["--user", "daemon-reload"]);
1936
+ run(resolved, "systemctl", ["--user", "reset-failed", SYSTEMD_UNIT]);
1937
+ }
1938
+ return serviceState(context);
1939
+ }
1940
+ function showServiceLogs(follow, context = {}) {
1941
+ const resolved = resolveContext(context);
1942
+ if (resolved.platform === "darwin") {
1943
+ mkdirSync3(resolved.paths.logs, { recursive: true, mode: 448 });
1944
+ for (const path of [resolved.paths.stdout, resolved.paths.stderr]) {
1945
+ if (!existsSync2(path)) writeFileSync3(path, "", { mode: 384 });
1946
+ }
1947
+ return passthrough(resolved, "/usr/bin/tail", [...follow ? ["-f"] : [], "-n", "200", resolved.paths.stdout, resolved.paths.stderr]);
1948
+ }
1949
+ return passthrough(resolved, "journalctl", ["--user-unit", SYSTEMD_UNIT, "-n", "200", "--no-pager", ...follow ? ["--follow"] : []]);
1950
+ }
1951
+ function installLaunchAgent(context) {
1952
+ const content = renderLaunchAgent(context.paths, context.env);
1953
+ const previous = snapshot(context.paths.definition);
1954
+ const wasLoaded = launchdLoaded(context);
1955
+ if (wasLoaded && previous === null) {
1956
+ throw new Error(`Refusing to replace an unowned launchd job without a managed definition: ${context.paths.definition}`);
1957
+ }
1958
+ if (wasLoaded) requireSuccess(context, "/bin/launchctl", ["bootout", serviceTarget(context)]);
1959
+ try {
1960
+ writeManagedAtomically(context.paths.definition, content, 384, (temporary) => {
1961
+ requireSuccess(context, "/usr/bin/plutil", ["-lint", temporary]);
1962
+ });
1963
+ bootstrapLaunchAgent(context);
1964
+ } catch (error) {
1965
+ restore(context.paths.definition, previous);
1966
+ if (previous !== null && wasLoaded) run(context, "/bin/launchctl", ["bootstrap", launchDomain(context), context.paths.definition]);
1967
+ throw error;
1968
+ }
1969
+ }
1970
+ function bootstrapLaunchAgent(context) {
1971
+ const args = ["bootstrap", launchDomain(context), context.paths.definition];
1972
+ const first = run(context, "/bin/launchctl", args);
1973
+ if (first.status === 0 || launchdLoaded(context)) return;
1974
+ run(context, "/bin/launchctl", ["bootout", serviceTarget(context)]);
1975
+ const retry = run(context, "/bin/launchctl", args);
1976
+ if (retry.status === 0 || launchdLoaded(context)) return;
1977
+ const failure = commandError("/bin/launchctl", args, retry);
1978
+ const firstDetail = (first.stderr || first.stdout).trim().replace(/\s+/g, " ").slice(0, 240);
1979
+ throw new Error(
1980
+ `${failure.message} launchd still rejected the managed job after one stale-state recovery attempt${firstDetail ? ` (first response: ${firstDetail})` : ""}. Run \`longleash service logs\` for daemon output.`
1981
+ );
1982
+ }
1983
+ function installSystemdService(context) {
1984
+ if (!context.paths.environment) throw new Error("The Linux service environment path is missing.");
1985
+ const previousUnit = snapshot(context.paths.definition);
1986
+ const previousEnvironment = snapshot(context.paths.environment);
1987
+ const wasEnabled = run(context, "systemctl", ["--user", "is-enabled", "--quiet", SYSTEMD_UNIT]).status === 0;
1988
+ const wasActive = run(context, "systemctl", ["--user", "is-active", "--quiet", SYSTEMD_UNIT]).status === 0;
1989
+ try {
1990
+ writeManagedAtomically(context.paths.environment, renderSystemdEnvironment(context.paths, context.env), 384);
1991
+ writeManagedAtomically(context.paths.definition, renderSystemdUnit(context.paths, context.home), 384);
1992
+ requireSuccess(context, "systemctl", ["--user", "daemon-reload"]);
1993
+ if (wasActive) {
1994
+ if (!wasEnabled) requireSuccess(context, "systemctl", ["--user", "enable", SYSTEMD_UNIT]);
1995
+ requireSuccess(context, "systemctl", ["--user", "restart", SYSTEMD_UNIT]);
1996
+ } else {
1997
+ requireSuccess(context, "systemctl", ["--user", "enable", "--now", SYSTEMD_UNIT]);
1998
+ }
1999
+ } catch (error) {
2000
+ restore(context.paths.environment, previousEnvironment);
2001
+ restore(context.paths.definition, previousUnit);
2002
+ run(context, "systemctl", ["--user", "daemon-reload"]);
2003
+ if (previousUnit !== null) {
2004
+ if (wasEnabled) run(context, "systemctl", ["--user", "enable", SYSTEMD_UNIT]);
2005
+ else run(context, "systemctl", ["--user", "disable", SYSTEMD_UNIT]);
2006
+ if (wasActive) run(context, "systemctl", ["--user", "restart", SYSTEMD_UNIT]);
2007
+ else run(context, "systemctl", ["--user", "stop", SYSTEMD_UNIT]);
2008
+ }
2009
+ throw error;
2010
+ }
2011
+ }
2012
+ function resolveContext(context) {
2013
+ const env = context.env ?? process.env;
2014
+ const platform = supportedPlatform(context.platform ?? process.platform);
2015
+ const home = resolve3(context.home ?? homedir3());
2016
+ const uid = context.uid ?? process.getuid?.();
2017
+ if (!Number.isSafeInteger(uid) || uid < 0) throw new Error("Could not determine the current user id for the service manager.");
2018
+ return {
2019
+ env,
2020
+ platform,
2021
+ home,
2022
+ uid,
2023
+ runner: context.runner ?? defaultRunner,
2024
+ paths: servicePaths({ env, platform, home })
2025
+ };
2026
+ }
2027
+ function supportedPlatform(platform) {
2028
+ if (platform !== "darwin" && platform !== "linux") {
2029
+ throw new Error(`Background services are supported on macOS and systemd Linux, not ${platform}. Use \`longleash run\` instead.`);
2030
+ }
2031
+ return platform;
2032
+ }
2033
+ function defaultRunner(file, args, options = { encoding: "utf8" }) {
2034
+ const result = spawnSync2(file, args, { ...options, encoding: "utf8" });
2035
+ return {
2036
+ status: result.status ?? (result.error ? 1 : 0),
2037
+ stdout: result.stdout ?? "",
2038
+ stderr: result.stderr ?? (result.error?.message ?? "")
2039
+ };
2040
+ }
2041
+ function run(context, file, args) {
2042
+ return context.runner(file, args, { encoding: "utf8", env: context.env });
2043
+ }
2044
+ function requireSuccess(context, file, args) {
2045
+ const result = run(context, file, args);
2046
+ if (result.status !== 0) throw commandError(file, args, result);
2047
+ return result;
2048
+ }
2049
+ function commandError(file, args, result) {
2050
+ const detail = (result.stderr || result.stdout).trim().replace(/\s+/g, " ").slice(0, 400);
2051
+ return new Error(`${file} ${args.join(" ")} failed${detail ? `: ${detail}` : "."}`);
2052
+ }
2053
+ function passthrough(context, file, args) {
2054
+ const result = spawnSync2(file, args, { stdio: "inherit", env: context.env });
2055
+ return result.status ?? 1;
2056
+ }
2057
+ function launchDomain(context) {
2058
+ return `gui/${context.uid}`;
2059
+ }
2060
+ function serviceTarget(context) {
2061
+ return `${launchDomain(context)}/${SERVICE_LABEL}`;
2062
+ }
2063
+ function launchdLoaded(context) {
2064
+ return run(context, "/bin/launchctl", ["print", serviceTarget(context)]).status === 0;
2065
+ }
2066
+ function lingerEnabled(context) {
2067
+ const result = run(context, "loginctl", ["show-user", String(context.uid), "--property=Linger", "--value"]);
2068
+ return result.status === 0 && result.stdout.trim().toLowerCase() === "yes";
2069
+ }
2070
+ function assertManagedWrapper(path) {
2071
+ if (!existsSync2(path)) throw new Error(`Managed LongLeash executable is missing: ${path}. Run \`longleash setup\` first.`);
2072
+ if (lstatSync3(path).isSymbolicLink()) throw new Error(`Refusing symlinked LongLeash executable: ${path}`);
2073
+ if (!readFileSync3(path, "utf8").includes(MANAGED_MARKER)) throw new Error(`Refusing unmanaged LongLeash executable: ${path}`);
2074
+ }
2075
+ function assertManagedDefinition(path) {
2076
+ if (!managedFileExists(path)) throw new Error(`LongLeash background service is not installed: ${path}`);
2077
+ if (!readFileSync3(path, "utf8").includes(MANAGED_MARKER)) throw new Error(`Refusing unmanaged service definition: ${path}`);
2078
+ }
2079
+ function assertServiceInstallation(context) {
2080
+ assertManagedDefinition(context.paths.definition);
2081
+ if (context.paths.environment) assertManagedDefinition(context.paths.environment);
2082
+ assertManagedWrapper(context.paths.wrapper);
2083
+ }
2084
+ function managedDefinitionExists(path) {
2085
+ if (!managedFileExists(path)) return false;
2086
+ assertManagedDefinition(path);
2087
+ return true;
2088
+ }
2089
+ function managedFileExists(path) {
2090
+ if (!existsSync2(path)) return false;
2091
+ if (lstatSync3(path).isSymbolicLink()) throw new Error(`Refusing symlinked service path: ${path}`);
2092
+ return true;
2093
+ }
2094
+ function snapshot(path) {
2095
+ if (!managedFileExists(path)) return null;
2096
+ assertManagedDefinition(path);
2097
+ return { content: readFileSync3(path, "utf8"), mode: lstatSync3(path).mode & 511 };
2098
+ }
2099
+ function restore(path, previous) {
2100
+ if (previous === null) rmSync3(path, { force: true });
2101
+ else writeManagedAtomically(path, previous.content, previous.mode);
2102
+ }
2103
+ function writeManagedAtomically(path, content, mode, validate) {
2104
+ if (!content.includes(MANAGED_MARKER)) throw new Error("Refusing to write an unmarked service definition.");
2105
+ if (managedFileExists(path)) assertManagedDefinition(path);
2106
+ mkdirSync3(dirname3(path), { recursive: true, mode: 448 });
2107
+ const temporary = `${path}.${process.pid}.${randomUUID3()}.tmp`;
2108
+ try {
2109
+ writeFileSync3(temporary, content, { encoding: "utf8", mode, flag: "wx" });
2110
+ chmodSync2(temporary, mode);
2111
+ validate?.(temporary);
2112
+ renameSync3(temporary, path);
2113
+ } catch (error) {
2114
+ rmSync3(temporary, { force: true });
2115
+ throw error;
2116
+ }
2117
+ }
2118
+ function requiredEnvironment(env, name, fallback) {
2119
+ const value = env[name] || fallback;
2120
+ if (/[\0\r\n]/.test(value)) throw new Error(`Unsafe ${name} value for background service.`);
2121
+ return value;
2122
+ }
2123
+ function xml(value) {
2124
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
2125
+ }
2126
+ function systemdQuote(value) {
2127
+ if (/[\0\r\n]/.test(value)) throw new Error("Service paths may not contain control characters.");
2128
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/%/g, "%%")}"`;
2129
+ }
2130
+ function systemdPath(value) {
2131
+ if (/[\u0000-\u0008\u000a-\u001f\u007f-\u009f]/.test(value)) {
2132
+ throw new Error("Service paths may not contain control characters.");
2133
+ }
2134
+ return value.replace(/%/g, "%%").replace(/\\/g, "\\x5c").replace(/ /g, "\\x20").replace(/\t/g, "\\x09").replace(/"/g, "\\x22").replace(/'/g, "\\x27");
2135
+ }
2136
+ function environmentQuote(value) {
2137
+ if (/[\0\r\n]/.test(value)) throw new Error("Service environment values may not contain control characters.");
2138
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
2139
+ }
2140
+
2141
+ // src/terminal-qr.ts
2142
+ var import_qrcode_terminal = __toESM(require_main(), 1);
2143
+ function terminalQr(input2) {
2144
+ const source = qrModules(input2);
2145
+ const quiet = 4;
2146
+ const whiteRow = Array(source.length + quiet * 2).fill(false);
2147
+ const padded = [
2148
+ ...Array.from({ length: quiet }, () => [...whiteRow]),
2149
+ ...source.map((row) => [
2150
+ ...Array(quiet).fill(false),
2151
+ ...row,
2152
+ ...Array(quiet).fill(false)
2153
+ ]),
2154
+ ...Array.from({ length: quiet }, () => [...whiteRow])
2155
+ ];
2156
+ if (padded.length % 2 !== 0) padded.push([...whiteRow]);
2157
+ const output2 = [];
2158
+ for (let row = 0; row < padded.length; row += 2) {
2159
+ const top = padded[row];
2160
+ const bottom = padded[row + 1];
2161
+ if (!top || !bottom) throw new Error("QR row pairing failed");
2162
+ let line = "";
2163
+ let lastStyle = "";
2164
+ for (let col = 0; col < top.length; col += 1) {
2165
+ const rendered = cell(top[col] ?? false, bottom[col] ?? false);
2166
+ if (rendered.style !== lastStyle) {
2167
+ line += rendered.style;
2168
+ lastStyle = rendered.style;
2169
+ }
2170
+ line += rendered.glyph;
2171
+ }
2172
+ output2.push(`${line}\x1B[0m`);
2173
+ }
2174
+ return output2.join("\n");
2175
+ }
2176
+ function qrModules(input2) {
2177
+ let compact = "";
2178
+ import_qrcode_terminal.default.generate(input2, { small: true }, (value) => {
2179
+ compact = value;
2180
+ });
2181
+ const lines = compact.replace(/\n$/, "").split("\n");
2182
+ if (lines.length < 2) throw new Error("QR renderer returned no module rows");
2183
+ const moduleCount = [...lines[1] ?? ""].length - 2;
2184
+ if (moduleCount < 21) throw new Error("QR renderer returned an invalid module grid");
2185
+ const modules = [];
2186
+ for (const line of lines.slice(1)) {
2187
+ const cells = [...line].slice(1, moduleCount + 1);
2188
+ if (cells.length !== moduleCount) throw new Error("QR renderer returned a ragged module grid");
2189
+ const top = [];
2190
+ const bottom = [];
2191
+ for (const value of cells) {
2192
+ if (value === "\u2588") {
2193
+ top.push(false);
2194
+ bottom.push(false);
2195
+ } else if (value === "\u2580") {
2196
+ top.push(false);
2197
+ bottom.push(true);
2198
+ } else if (value === "\u2584") {
2199
+ top.push(true);
2200
+ bottom.push(false);
2201
+ } else if (value === " ") {
2202
+ top.push(true);
2203
+ bottom.push(true);
2204
+ } else throw new Error(`QR renderer returned an unknown cell ${JSON.stringify(value)}`);
2205
+ }
2206
+ modules.push(top, bottom);
2207
+ }
2208
+ return modules.slice(0, moduleCount);
2209
+ }
2210
+ function cell(top, bottom) {
2211
+ const black = "0;0;0";
2212
+ const white = "255;255;255";
2213
+ if (top === bottom) {
2214
+ const colour = top ? black : white;
2215
+ return { style: `\x1B[38;2;${colour};48;2;${colour}m`, glyph: "\u2588" };
2216
+ }
2217
+ return {
2218
+ // Black remains the continuous cell background, avoiding font seams through finder patterns.
2219
+ style: `\x1B[38;2;${white};48;2;${black}m`,
2220
+ glyph: top ? "\u2584" : "\u2580"
2221
+ };
2222
+ }
2223
+
519
2224
  // src/cli.ts
520
- var here = dirname3(fileURLToPath(import.meta.url));
521
- var packageRoot = resolve3(here, "..");
522
- var manifest = JSON.parse(readFileSync3(join3(packageRoot, "package.json"), "utf8"));
2225
+ var here = dirname4(fileURLToPath(import.meta.url));
2226
+ var packageRoot = resolve4(here, "..");
2227
+ var manifest = JSON.parse(readFileSync4(join4(packageRoot, "package.json"), "utf8"));
523
2228
  var VERSION = manifest.version;
524
- var runtime = join3(packageRoot, "runtime", "daemon");
525
- var daemonEntry = join3(runtime, "bin", "longleashd.mjs");
526
- var hooks = join3(runtime, "hooks");
527
- var devicesEntry = join3(runtime, "bin", "longleash-devices.mjs");
2229
+ var runtime = join4(packageRoot, "runtime", "daemon");
2230
+ var daemonEntry = join4(runtime, "bin", "longleashd.mjs");
2231
+ var hooks = join4(runtime, "hooks");
2232
+ var devicesEntry = join4(runtime, "bin", "longleash-devices.mjs");
528
2233
  async function main() {
529
2234
  const args = process.argv.slice(2);
530
2235
  const command = args[0];
@@ -541,11 +2246,13 @@ async function main() {
541
2246
  return 0;
542
2247
  }
543
2248
  if (command === "setup") return setup(parseSetup(args.slice(1)));
2249
+ if (command === "service") return serviceCommand(args.slice(1));
2250
+ if (command === "pair") return pair();
544
2251
  if (command === "doctor") return doctor(args.includes("--json"));
545
2252
  if (command === "hooks") return runHooks(args.slice(1));
546
2253
  if (command === "devices") return runNode(devicesEntry, []);
547
2254
  if (command === "revoke") return runNode(devicesEntry, ["revoke", ...args.slice(1)]);
548
- if (command === "update") return update(args[1] ?? "latest");
2255
+ if (command === "update") return update(args[1]);
549
2256
  if (command === "uninstall") return uninstall();
550
2257
  if (command === "run") return runDaemon(args.slice(1));
551
2258
  if (command?.startsWith("-")) {
@@ -561,20 +2268,33 @@ function help() {
561
2268
 
562
2269
  Usage:
563
2270
  longleash setup configure a stable user-local installation
2271
+ longleash service status show manager and authenticated daemon health
2272
+ longleash service start start the per-user background service
2273
+ longleash service stop stop it without removing settings or devices
2274
+ longleash service restart restart it after a configuration change
2275
+ longleash service logs [-f] show redacted persistent service logs
2276
+ longleash service uninstall remove only the background service
2277
+ longleash pair print a fresh pairing QR from the running service
564
2278
  longleash run [folders...] run in the foreground (terminal stays open)
565
2279
  longleash [folders...] shorthand for run
566
2280
  longleash doctor [--json] report package, config, and daemon health
567
2281
  longleash hooks [--remove] install or remove supported provider hooks
568
2282
  longleash devices list paired phones
569
2283
  longleash revoke <id|--all> revoke paired phone access
570
- longleash update [version] atomically install latest or an exact version
2284
+ longleash update [version] atomically update this channel or install an exact version
571
2285
  longleash uninstall remove runtime and hooks; preserve user data
572
- longleash where print the active npm package directory
573
-
574
- Setup never starts the daemon. Background service commands arrive in Workstream C.`);
2286
+ longleash where print the active npm package directory`);
575
2287
  }
576
2288
  function parseSetup(args) {
577
- const options = { roots: [], relay: "", yes: false, reuse: false, configureOnly: false, skipHooks: false };
2289
+ const options = {
2290
+ roots: [],
2291
+ relay: "",
2292
+ yes: false,
2293
+ reuse: false,
2294
+ configureOnly: false,
2295
+ skipHooks: false,
2296
+ service: "default"
2297
+ };
578
2298
  for (let i = 0; i < args.length; i += 1) {
579
2299
  const arg = args[i];
580
2300
  if (arg === "--root") {
@@ -589,6 +2309,8 @@ function parseSetup(args) {
589
2309
  else if (arg === "--reuse-config") options.reuse = true;
590
2310
  else if (arg === "--configure-only") options.configureOnly = true;
591
2311
  else if (arg === "--skip-hooks") options.skipHooks = true;
2312
+ else if (arg === "--service") options.service = "install";
2313
+ else if (arg === "--no-service") options.service = "skip";
592
2314
  else throw new Error(`Unknown setup option: ${arg}`);
593
2315
  }
594
2316
  return options;
@@ -608,6 +2330,11 @@ async function setup(options) {
608
2330
  options.relay = typeof existing.relayUrl === "string" ? existing.relayUrl : "off";
609
2331
  options.yes = true;
610
2332
  }
2333
+ let existingService = false;
2334
+ try {
2335
+ existingService = serviceState().installed;
2336
+ } catch {
2337
+ }
611
2338
  if (options.roots.length === 0 && options.yes) {
612
2339
  throw new Error("Non-interactive setup requires at least one explicit --root, or --reuse-config.");
613
2340
  }
@@ -619,17 +2346,26 @@ async function setup(options) {
619
2346
  try {
620
2347
  console.log("\nLongLeash setup\n");
621
2348
  console.log("Agents remain on this laptop. Choose only folders where they may work.");
622
- const rootAnswer = await prompt.question(`Allowed project folder [${process.cwd()}]: `);
623
- options.roots = [rootAnswer.trim() || process.cwd()];
2349
+ const defaultRoot = process.cwd();
2350
+ while (options.roots.length === 0) {
2351
+ const rootAnswer = await prompt.question(`Allowed project folder (press Enter for ${defaultRoot}): `);
2352
+ const resolved = resolveAllowedRootAnswer(rootAnswer, defaultRoot);
2353
+ if (resolved.ok) options.roots = [resolved.root];
2354
+ else console.log(`
2355
+ ${resolved.message}
2356
+ `);
2357
+ }
624
2358
  console.log("\nConnectivity: hosted works away from home; LAN-only has no internet relay.");
625
2359
  const relayAnswer = await prompt.question("Relay [hosted / lan / wss://your-relay/ws] (hosted): ");
626
2360
  options.relay = relayAnswer.trim() || "hosted";
2361
+ const serviceAnswer = await prompt.question("Keep LongLeash available after this terminal closes? [Y/n] ");
2362
+ options.service = /^n(?:o)?$/i.test(serviceAnswer.trim()) ? "skip" : "install";
627
2363
  const roots2 = normalizeRoots(options.roots);
628
2364
  const relay2 = normalizeRelay(options.relay);
629
2365
  console.log("\nReview before anything changes:");
630
2366
  for (const root of roots2) console.log(` allowed root ${root}`);
631
2367
  console.log(` connectivity ${relay2 ?? "LAN-only (no relay)"}`);
632
- console.log(" service not installed or started in this workstream");
2368
+ console.log(` service ${options.service === "install" ? "per-user background service at login" : "foreground only"}`);
633
2369
  const accepted = await prompt.question("\nApply this setup? [y/N] ");
634
2370
  if (!/^y(?:es)?$/i.test(accepted.trim())) {
635
2371
  console.log("Nothing changed.");
@@ -653,7 +2389,7 @@ async function setup(options) {
653
2389
  relay ?? "off",
654
2390
  "--skip-hooks"
655
2391
  ];
656
- const configured = spawnSync2(process.execPath, [prepared.cli, ...forwarded], {
2392
+ const configured = spawnSync3(process.execPath, [prepared.cli, ...forwarded], {
657
2393
  stdio: "inherit",
658
2394
  env: { ...process.env, LONGLEASH_INTERNAL_CONFIGURE: VERSION }
659
2395
  });
@@ -663,7 +2399,10 @@ async function setup(options) {
663
2399
  throw new Error("The staged package could not be configured. The active version was not changed.");
664
2400
  }
665
2401
  try {
666
- prepared.activate();
2402
+ const activation = prepared.activate();
2403
+ if (activation.migratedLegacyWrapper) {
2404
+ console.log("Migrated the verified legacy LongLeash command. Its checkout was preserved and will be restored if you uninstall this runtime.");
2405
+ }
667
2406
  } catch (error) {
668
2407
  prepared.rollback();
669
2408
  restoreConfigSnapshot(configSnapshot);
@@ -672,7 +2411,7 @@ async function setup(options) {
672
2411
  console.log(`
673
2412
  Activated @longleash/cli ${VERSION}.`);
674
2413
  if (!options.skipHooks) {
675
- const hooked = spawnSync2(process.execPath, [prepared.cli, "hooks"], { stdio: "inherit", env: process.env });
2414
+ const hooked = spawnSync3(process.execPath, [prepared.cli, "hooks"], { stdio: "inherit", env: process.env });
676
2415
  if (hooked.error || hooked.status !== 0) {
677
2416
  console.error("Provider hook setup needs attention. The runtime is installed; run `longleash hooks` after fixing the message above.");
678
2417
  }
@@ -687,6 +2426,22 @@ The executable is installed at ${paths.wrapper}, but ${paths.bin} is not on PATH
687
2426
  console.log(` export PATH=${quoteForShell(paths.bin)}:"$PATH"`);
688
2427
  console.log(`You can use ${paths.wrapper} immediately.`);
689
2428
  }
2429
+ const manageService = options.service === "install" || options.service === "default" && (options.reuse ? existingService : true);
2430
+ if (manageService) {
2431
+ try {
2432
+ const state = installService();
2433
+ const healthy = await waitForDaemon(15e3);
2434
+ if (!healthy) {
2435
+ throw new Error("The service manager started LongLeash, but its authenticated health check did not become ready. Run `longleash service logs`.");
2436
+ }
2437
+ console.log(`Background service installed and running (${state.platform}).`);
2438
+ console.log("Run `longleash pair` for a fresh QR; pairing secrets are not stored in service logs.");
2439
+ } catch (error) {
2440
+ throw new Error(`The verified runtime is installed, but the background service could not be activated: ${message(error)} You can use \`longleash run\` while diagnosing it.`);
2441
+ }
2442
+ } else {
2443
+ console.log("Background service was not installed. Run `longleash run` for foreground use or `longleash service install` later.");
2444
+ }
690
2445
  return 0;
691
2446
  }
692
2447
  const next = { ...existing, cliSchemaVersion: 1, allowedRoots: roots };
@@ -694,17 +2449,98 @@ The executable is installed at ${paths.wrapper}, but ${paths.bin} is not on PATH
694
2449
  else next.relayUrl = relay;
695
2450
  saveConfig(next);
696
2451
  console.log(`Saved configuration to ${configPath()}`);
697
- console.log("LongLeash was not started. Run `longleash run` when you are ready.");
2452
+ console.log("Configuration saved. The outer setup transaction decides whether to start the service.");
2453
+ return 0;
2454
+ }
2455
+ async function serviceCommand(args) {
2456
+ const command = args[0] ?? "status";
2457
+ if (command === "install") {
2458
+ const state = installService();
2459
+ if (!await waitForDaemon(15e3)) throw new Error("The service was installed but did not become healthy. Run `longleash service logs`.");
2460
+ printServiceState(state, true);
2461
+ return 0;
2462
+ }
2463
+ if (command === "start") {
2464
+ const state = startService();
2465
+ if (!await waitForDaemon(15e3)) throw new Error("The service manager started the job but the daemon did not become healthy. Run `longleash service logs`.");
2466
+ printServiceState(state, true);
2467
+ return 0;
2468
+ }
2469
+ if (command === "stop") {
2470
+ const state = stopService();
2471
+ printServiceState(state, false);
2472
+ return 0;
2473
+ }
2474
+ if (command === "restart") {
2475
+ const state = restartService();
2476
+ if (!await waitForDaemon(15e3)) throw new Error("The service restarted but the daemon did not become healthy. Run `longleash service logs`.");
2477
+ printServiceState(state, true);
2478
+ return 0;
2479
+ }
2480
+ if (command === "uninstall") {
2481
+ uninstallService();
2482
+ console.log(`Removed the per-user background service. Settings, devices, audit data, and runtime remain in ${configPath()}.`);
2483
+ return 0;
2484
+ }
2485
+ if (command === "logs") return showServiceLogs(args.includes("--follow") || args.includes("-f"));
2486
+ if (command === "status") {
2487
+ const state = serviceState();
2488
+ const healthy = await daemonHealth();
2489
+ if (args.includes("--json")) console.log(JSON.stringify({ ...state, healthy }, null, 2));
2490
+ else printServiceState(state, healthy);
2491
+ return state.installed && healthy ? 0 : 1;
2492
+ }
2493
+ throw new Error(`Unknown service command: ${command}`);
2494
+ }
2495
+ function printServiceState(state, healthy) {
2496
+ console.log("");
2497
+ console.log(` service ${state.installed ? "installed" : "not installed"} (${state.platform})`);
2498
+ console.log(` manager ${state.loaded ? "loaded" : "not loaded"}${state.active ? " \xB7 active" : ""}`);
2499
+ console.log(` daemon ${healthy ? "authenticated health check passed" : "not reachable"}`);
2500
+ console.log(` definition ${state.definition}`);
2501
+ console.log(` logs ${state.logs}`);
2502
+ if (state.platform === "linux" && state.loginOnly) {
2503
+ console.log(" login scope stops after logout; optional lingering is not enabled by LongLeash");
2504
+ }
2505
+ console.log("");
2506
+ }
2507
+ async function pair() {
2508
+ const response = await localDaemonRequest("/local/pairing", { method: "POST" });
2509
+ if (!response.ok) throw new Error(`The running daemon could not create a pairing challenge (${response.status}).`);
2510
+ const body = await response.json();
2511
+ if (typeof body.url !== "string" || !/^https?:\/\//.test(body.url)) throw new Error("The daemon returned an invalid pairing challenge.");
2512
+ console.log("\nScan this with your phone. The link is single-use and expires:\n");
2513
+ console.log(terminalQr(body.url));
2514
+ console.log(`
2515
+ ${body.url}
2516
+ `);
698
2517
  return 0;
699
2518
  }
700
2519
  async function runDaemon(explicitRoots) {
701
- if (!existsSync2(daemonEntry)) throw new Error("The npm package is incomplete: daemon runtime is missing.");
2520
+ if (!existsSync3(daemonEntry)) throw new Error("The npm package is incomplete: daemon runtime is missing.");
702
2521
  const config = loadConfig();
703
2522
  const roots = normalizeRoots(explicitRoots.length > 0 ? explicitRoots : configuredRoots(config));
704
2523
  const child = spawn(process.execPath, [daemonEntry, ...roots], { stdio: "inherit", env: process.env });
705
2524
  return await new Promise((resolveExit, reject) => {
706
- child.once("error", reject);
707
- child.once("exit", (code, signal) => resolveExit(code ?? (signal ? 1 : 0)));
2525
+ const forward = (signal) => {
2526
+ if (child.exitCode === null && child.signalCode === null) child.kill(signal);
2527
+ };
2528
+ const interrupt = () => forward("SIGINT");
2529
+ const terminate = () => forward("SIGTERM");
2530
+ const cleanup = () => {
2531
+ process.off("SIGINT", interrupt);
2532
+ process.off("SIGTERM", terminate);
2533
+ };
2534
+ process.on("SIGINT", interrupt);
2535
+ process.on("SIGTERM", terminate);
2536
+ child.once("error", (error) => {
2537
+ cleanup();
2538
+ reject(error);
2539
+ });
2540
+ child.once("exit", (code, signal) => {
2541
+ cleanup();
2542
+ resolveExit(code ?? (signal ? 1 : 0));
2543
+ });
708
2544
  });
709
2545
  }
710
2546
  async function doctor(json) {
@@ -715,10 +2551,10 @@ async function doctor(json) {
715
2551
  } catch (error) {
716
2552
  configError = message(error);
717
2553
  }
718
- const endpointPath = join3(process.env.LONGLEASH_DATA ?? join3(homedir3(), ".longleash"), "hook-endpoint.json");
2554
+ const endpointPath = join4(process.env.LONGLEASH_DATA ?? join4(homedir4(), ".longleash"), "hook-endpoint.json");
719
2555
  let daemon = { reachable: false };
720
2556
  try {
721
- const endpoint = JSON.parse(readFileSync3(endpointPath, "utf8"));
2557
+ const endpoint = JSON.parse(readFileSync4(endpointPath, "utf8"));
722
2558
  if (endpoint.url && endpoint.secret) {
723
2559
  const response = await fetch(endpoint.url.replace(/\/hook$/, "/health"), {
724
2560
  headers: { "x-longleash-hook": endpoint.secret },
@@ -759,12 +2595,12 @@ LongLeash ${VERSION}`);
759
2595
  async function runHooks(args) {
760
2596
  const remove = args.includes("--remove");
761
2597
  let failed = false;
762
- if (commandAvailable("claude") || remove) failed = await runNode(join3(hooks, "install-hooks.mjs"), remove ? ["--remove"] : []) !== 0 || failed;
763
- if (commandAvailable("codex") || remove) failed = await runNode(join3(hooks, "install-codex-hooks.mjs"), remove ? ["--remove"] : []) !== 0 || failed;
2598
+ if (commandAvailable("claude") || remove) failed = await runNode(join4(hooks, "install-hooks.mjs"), remove ? ["--remove"] : []) !== 0 || failed;
2599
+ if (commandAvailable("codex") || remove) failed = await runNode(join4(hooks, "install-codex-hooks.mjs"), remove ? ["--remove"] : []) !== 0 || failed;
764
2600
  return failed ? 1 : 0;
765
2601
  }
766
2602
  async function update(target) {
767
- const spec = target === "latest" ? "@longleash/cli@latest" : `@longleash/cli@${assertVersion(target)}`;
2603
+ const spec = updatePackageSpec(target, VERSION);
768
2604
  const child = spawn("npm", [
769
2605
  "exec",
770
2606
  "--yes",
@@ -785,18 +2621,69 @@ async function update(target) {
785
2621
  });
786
2622
  }
787
2623
  async function uninstall() {
2624
+ try {
2625
+ if (serviceState().installed) uninstallService();
2626
+ } catch (error) {
2627
+ throw new Error(`The background service could not be removed safely, so the runtime was preserved: ${message(error)}`);
2628
+ }
788
2629
  const hooksRemoved = await runHooks(["--remove"]);
789
2630
  if (hooksRemoved !== 0) throw new Error("Provider hooks could not be removed safely. The managed runtime was preserved.");
790
2631
  const result = uninstallManagedRuntime();
791
2632
  console.log(result.removed ? "Removed the managed LongLeash runtime and executable." : "No managed LongLeash runtime was installed.");
2633
+ if (result.legacyWrapperRestored) console.log("Restored the verified legacy LongLeash command that existed before the npm migration.");
792
2634
  console.log(`Preserved settings, paired devices, and audit data in ${result.configPreserved}.`);
793
2635
  return 0;
794
2636
  }
2637
+ async function daemonHealth() {
2638
+ try {
2639
+ const response = await localDaemonRequest("/health", { method: "GET" }, 1500);
2640
+ if (!response.ok) return false;
2641
+ const body = await response.json();
2642
+ return body.name === "longleash";
2643
+ } catch {
2644
+ return false;
2645
+ }
2646
+ }
2647
+ async function waitForDaemon(timeoutMs) {
2648
+ const deadline = Date.now() + timeoutMs;
2649
+ while (Date.now() < deadline) {
2650
+ if (await daemonHealth()) return true;
2651
+ await new Promise((resolveWait) => setTimeout(resolveWait, 200));
2652
+ }
2653
+ return false;
2654
+ }
2655
+ async function localDaemonRequest(path, init, timeoutMs = 5e3) {
2656
+ const endpointPath = join4(process.env.LONGLEASH_DATA ?? join4(homedir4(), ".longleash"), "hook-endpoint.json");
2657
+ let endpoint;
2658
+ try {
2659
+ endpoint = JSON.parse(readFileSync4(endpointPath, "utf8"));
2660
+ } catch {
2661
+ throw new Error("LongLeash is not reachable. Start it with `longleash service start` or `longleash run`.");
2662
+ }
2663
+ if (typeof endpoint.url !== "string" || typeof endpoint.secret !== "string" || endpoint.secret === "") {
2664
+ throw new Error(`Invalid local daemon endpoint: ${endpointPath}`);
2665
+ }
2666
+ const url = new URL(endpoint.url);
2667
+ if (url.protocol !== "http:" || url.pathname !== "/hook" || !localAddress(url.hostname)) {
2668
+ throw new Error(`Refusing a non-local daemon endpoint in ${endpointPath}`);
2669
+ }
2670
+ return fetch(`${url.origin}${path}`, {
2671
+ ...init,
2672
+ headers: { ...init.headers, "x-longleash-hook": endpoint.secret },
2673
+ signal: AbortSignal.timeout(timeoutMs)
2674
+ });
2675
+ }
2676
+ function localAddress(hostname) {
2677
+ if (hostname === "localhost" || hostname === "::1") return true;
2678
+ if (/^127\./.test(hostname) || /^10\./.test(hostname) || /^192\.168\./.test(hostname)) return true;
2679
+ const match = hostname.match(/^172\.(\d+)\./);
2680
+ return match !== null && Number(match[1]) >= 16 && Number(match[1]) <= 31;
2681
+ }
795
2682
  function commandAvailable(command) {
796
- return spawnSync2(command, ["--version"], { stdio: "ignore", timeout: 3e3 }).status === 0;
2683
+ return spawnSync3(command, ["--version"], { stdio: "ignore", timeout: 3e3 }).status === 0;
797
2684
  }
798
2685
  function pathDirectories() {
799
- return (process.env.PATH ?? "").split(delimiter).filter(Boolean).map((path) => resolve3(path));
2686
+ return (process.env.PATH ?? "").split(delimiter).filter(Boolean).map((path) => resolve4(path));
800
2687
  }
801
2688
  function quoteForShell(value) {
802
2689
  return `'${value.replace(/'/g, `'"'"'`)}'`;