agentapprove 0.0.1 → 0.1.0
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/LICENSE +9 -9
- package/README.md +84 -20
- package/dist/cli.js +4675 -0
- package/package.json +37 -17
- package/cli.js +0 -23
package/dist/cli.js
ADDED
|
@@ -0,0 +1,4675 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
9
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
10
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
11
|
+
for (let key of __getOwnPropNames(mod))
|
|
12
|
+
if (!__hasOwnProp.call(to, key))
|
|
13
|
+
__defProp(to, key, {
|
|
14
|
+
get: () => mod[key],
|
|
15
|
+
enumerable: true
|
|
16
|
+
});
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
20
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
21
|
+
|
|
22
|
+
// node_modules/sisteransi/src/index.js
|
|
23
|
+
var require_src = __commonJS((exports, module) => {
|
|
24
|
+
var ESC = "\x1B";
|
|
25
|
+
var CSI = `${ESC}[`;
|
|
26
|
+
var beep = "\x07";
|
|
27
|
+
var cursor = {
|
|
28
|
+
to(x, y) {
|
|
29
|
+
if (!y)
|
|
30
|
+
return `${CSI}${x + 1}G`;
|
|
31
|
+
return `${CSI}${y + 1};${x + 1}H`;
|
|
32
|
+
},
|
|
33
|
+
move(x, y) {
|
|
34
|
+
let ret = "";
|
|
35
|
+
if (x < 0)
|
|
36
|
+
ret += `${CSI}${-x}D`;
|
|
37
|
+
else if (x > 0)
|
|
38
|
+
ret += `${CSI}${x}C`;
|
|
39
|
+
if (y < 0)
|
|
40
|
+
ret += `${CSI}${-y}A`;
|
|
41
|
+
else if (y > 0)
|
|
42
|
+
ret += `${CSI}${y}B`;
|
|
43
|
+
return ret;
|
|
44
|
+
},
|
|
45
|
+
up: (count = 1) => `${CSI}${count}A`,
|
|
46
|
+
down: (count = 1) => `${CSI}${count}B`,
|
|
47
|
+
forward: (count = 1) => `${CSI}${count}C`,
|
|
48
|
+
backward: (count = 1) => `${CSI}${count}D`,
|
|
49
|
+
nextLine: (count = 1) => `${CSI}E`.repeat(count),
|
|
50
|
+
prevLine: (count = 1) => `${CSI}F`.repeat(count),
|
|
51
|
+
left: `${CSI}G`,
|
|
52
|
+
hide: `${CSI}?25l`,
|
|
53
|
+
show: `${CSI}?25h`,
|
|
54
|
+
save: `${ESC}7`,
|
|
55
|
+
restore: `${ESC}8`
|
|
56
|
+
};
|
|
57
|
+
var scroll = {
|
|
58
|
+
up: (count = 1) => `${CSI}S`.repeat(count),
|
|
59
|
+
down: (count = 1) => `${CSI}T`.repeat(count)
|
|
60
|
+
};
|
|
61
|
+
var erase = {
|
|
62
|
+
screen: `${CSI}2J`,
|
|
63
|
+
up: (count = 1) => `${CSI}1J`.repeat(count),
|
|
64
|
+
down: (count = 1) => `${CSI}J`.repeat(count),
|
|
65
|
+
line: `${CSI}2K`,
|
|
66
|
+
lineEnd: `${CSI}K`,
|
|
67
|
+
lineStart: `${CSI}1K`,
|
|
68
|
+
lines(count) {
|
|
69
|
+
let clear = "";
|
|
70
|
+
for (let i = 0;i < count; i++)
|
|
71
|
+
clear += this.line + (i < count - 1 ? cursor.up() : "");
|
|
72
|
+
if (count)
|
|
73
|
+
clear += cursor.left;
|
|
74
|
+
return clear;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
module.exports = { cursor, scroll, erase, beep };
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// node_modules/picocolors/picocolors.js
|
|
81
|
+
var require_picocolors = __commonJS((exports, module) => {
|
|
82
|
+
var p = process || {};
|
|
83
|
+
var argv = p.argv || [];
|
|
84
|
+
var env = p.env || {};
|
|
85
|
+
var isColorSupported = !(!!env.NO_COLOR || argv.includes("--no-color")) && (!!env.FORCE_COLOR || argv.includes("--color") || p.platform === "win32" || (p.stdout || {}).isTTY && env.TERM !== "dumb" || !!env.CI);
|
|
86
|
+
var formatter = (open, close, replace = open) => (input) => {
|
|
87
|
+
let string = "" + input, index = string.indexOf(close, open.length);
|
|
88
|
+
return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close;
|
|
89
|
+
};
|
|
90
|
+
var replaceClose = (string, close, replace, index) => {
|
|
91
|
+
let result = "", cursor = 0;
|
|
92
|
+
do {
|
|
93
|
+
result += string.substring(cursor, index) + replace;
|
|
94
|
+
cursor = index + close.length;
|
|
95
|
+
index = string.indexOf(close, cursor);
|
|
96
|
+
} while (~index);
|
|
97
|
+
return result + string.substring(cursor);
|
|
98
|
+
};
|
|
99
|
+
var createColors = (enabled = isColorSupported) => {
|
|
100
|
+
let f = enabled ? formatter : () => String;
|
|
101
|
+
return {
|
|
102
|
+
isColorSupported: enabled,
|
|
103
|
+
reset: f("\x1B[0m", "\x1B[0m"),
|
|
104
|
+
bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"),
|
|
105
|
+
dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"),
|
|
106
|
+
italic: f("\x1B[3m", "\x1B[23m"),
|
|
107
|
+
underline: f("\x1B[4m", "\x1B[24m"),
|
|
108
|
+
inverse: f("\x1B[7m", "\x1B[27m"),
|
|
109
|
+
hidden: f("\x1B[8m", "\x1B[28m"),
|
|
110
|
+
strikethrough: f("\x1B[9m", "\x1B[29m"),
|
|
111
|
+
black: f("\x1B[30m", "\x1B[39m"),
|
|
112
|
+
red: f("\x1B[31m", "\x1B[39m"),
|
|
113
|
+
green: f("\x1B[32m", "\x1B[39m"),
|
|
114
|
+
yellow: f("\x1B[33m", "\x1B[39m"),
|
|
115
|
+
blue: f("\x1B[34m", "\x1B[39m"),
|
|
116
|
+
magenta: f("\x1B[35m", "\x1B[39m"),
|
|
117
|
+
cyan: f("\x1B[36m", "\x1B[39m"),
|
|
118
|
+
white: f("\x1B[37m", "\x1B[39m"),
|
|
119
|
+
gray: f("\x1B[90m", "\x1B[39m"),
|
|
120
|
+
bgBlack: f("\x1B[40m", "\x1B[49m"),
|
|
121
|
+
bgRed: f("\x1B[41m", "\x1B[49m"),
|
|
122
|
+
bgGreen: f("\x1B[42m", "\x1B[49m"),
|
|
123
|
+
bgYellow: f("\x1B[43m", "\x1B[49m"),
|
|
124
|
+
bgBlue: f("\x1B[44m", "\x1B[49m"),
|
|
125
|
+
bgMagenta: f("\x1B[45m", "\x1B[49m"),
|
|
126
|
+
bgCyan: f("\x1B[46m", "\x1B[49m"),
|
|
127
|
+
bgWhite: f("\x1B[47m", "\x1B[49m"),
|
|
128
|
+
blackBright: f("\x1B[90m", "\x1B[39m"),
|
|
129
|
+
redBright: f("\x1B[91m", "\x1B[39m"),
|
|
130
|
+
greenBright: f("\x1B[92m", "\x1B[39m"),
|
|
131
|
+
yellowBright: f("\x1B[93m", "\x1B[39m"),
|
|
132
|
+
blueBright: f("\x1B[94m", "\x1B[39m"),
|
|
133
|
+
magentaBright: f("\x1B[95m", "\x1B[39m"),
|
|
134
|
+
cyanBright: f("\x1B[96m", "\x1B[39m"),
|
|
135
|
+
whiteBright: f("\x1B[97m", "\x1B[39m"),
|
|
136
|
+
bgBlackBright: f("\x1B[100m", "\x1B[49m"),
|
|
137
|
+
bgRedBright: f("\x1B[101m", "\x1B[49m"),
|
|
138
|
+
bgGreenBright: f("\x1B[102m", "\x1B[49m"),
|
|
139
|
+
bgYellowBright: f("\x1B[103m", "\x1B[49m"),
|
|
140
|
+
bgBlueBright: f("\x1B[104m", "\x1B[49m"),
|
|
141
|
+
bgMagentaBright: f("\x1B[105m", "\x1B[49m"),
|
|
142
|
+
bgCyanBright: f("\x1B[106m", "\x1B[49m"),
|
|
143
|
+
bgWhiteBright: f("\x1B[107m", "\x1B[49m")
|
|
144
|
+
};
|
|
145
|
+
};
|
|
146
|
+
module.exports = createColors();
|
|
147
|
+
module.exports.createColors = createColors;
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// node_modules/qrcode-terminal/vendor/QRCode/QRMode.js
|
|
151
|
+
var require_QRMode = __commonJS((exports, module) => {
|
|
152
|
+
module.exports = {
|
|
153
|
+
MODE_NUMBER: 1 << 0,
|
|
154
|
+
MODE_ALPHA_NUM: 1 << 1,
|
|
155
|
+
MODE_8BIT_BYTE: 1 << 2,
|
|
156
|
+
MODE_KANJI: 1 << 3
|
|
157
|
+
};
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// node_modules/qrcode-terminal/vendor/QRCode/QR8bitByte.js
|
|
161
|
+
var require_QR8bitByte = __commonJS((exports, module) => {
|
|
162
|
+
var QRMode = require_QRMode();
|
|
163
|
+
function QR8bitByte(data) {
|
|
164
|
+
this.mode = QRMode.MODE_8BIT_BYTE;
|
|
165
|
+
this.data = data;
|
|
166
|
+
}
|
|
167
|
+
QR8bitByte.prototype = {
|
|
168
|
+
getLength: function() {
|
|
169
|
+
return this.data.length;
|
|
170
|
+
},
|
|
171
|
+
write: function(buffer) {
|
|
172
|
+
for (var i = 0;i < this.data.length; i++) {
|
|
173
|
+
buffer.put(this.data.charCodeAt(i), 8);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
module.exports = QR8bitByte;
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// node_modules/qrcode-terminal/vendor/QRCode/QRMath.js
|
|
181
|
+
var require_QRMath = __commonJS((exports, module) => {
|
|
182
|
+
var QRMath = {
|
|
183
|
+
glog: function(n) {
|
|
184
|
+
if (n < 1) {
|
|
185
|
+
throw new Error("glog(" + n + ")");
|
|
186
|
+
}
|
|
187
|
+
return QRMath.LOG_TABLE[n];
|
|
188
|
+
},
|
|
189
|
+
gexp: function(n) {
|
|
190
|
+
while (n < 0) {
|
|
191
|
+
n += 255;
|
|
192
|
+
}
|
|
193
|
+
while (n >= 256) {
|
|
194
|
+
n -= 255;
|
|
195
|
+
}
|
|
196
|
+
return QRMath.EXP_TABLE[n];
|
|
197
|
+
},
|
|
198
|
+
EXP_TABLE: new Array(256),
|
|
199
|
+
LOG_TABLE: new Array(256)
|
|
200
|
+
};
|
|
201
|
+
for (i = 0;i < 8; i++) {
|
|
202
|
+
QRMath.EXP_TABLE[i] = 1 << i;
|
|
203
|
+
}
|
|
204
|
+
var i;
|
|
205
|
+
for (i = 8;i < 256; i++) {
|
|
206
|
+
QRMath.EXP_TABLE[i] = QRMath.EXP_TABLE[i - 4] ^ QRMath.EXP_TABLE[i - 5] ^ QRMath.EXP_TABLE[i - 6] ^ QRMath.EXP_TABLE[i - 8];
|
|
207
|
+
}
|
|
208
|
+
var i;
|
|
209
|
+
for (i = 0;i < 255; i++) {
|
|
210
|
+
QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]] = i;
|
|
211
|
+
}
|
|
212
|
+
var i;
|
|
213
|
+
module.exports = QRMath;
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// node_modules/qrcode-terminal/vendor/QRCode/QRPolynomial.js
|
|
217
|
+
var require_QRPolynomial = __commonJS((exports, module) => {
|
|
218
|
+
var QRMath = require_QRMath();
|
|
219
|
+
function QRPolynomial(num, shift) {
|
|
220
|
+
if (num.length === undefined) {
|
|
221
|
+
throw new Error(num.length + "/" + shift);
|
|
222
|
+
}
|
|
223
|
+
var offset = 0;
|
|
224
|
+
while (offset < num.length && num[offset] === 0) {
|
|
225
|
+
offset++;
|
|
226
|
+
}
|
|
227
|
+
this.num = new Array(num.length - offset + shift);
|
|
228
|
+
for (var i = 0;i < num.length - offset; i++) {
|
|
229
|
+
this.num[i] = num[i + offset];
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
QRPolynomial.prototype = {
|
|
233
|
+
get: function(index) {
|
|
234
|
+
return this.num[index];
|
|
235
|
+
},
|
|
236
|
+
getLength: function() {
|
|
237
|
+
return this.num.length;
|
|
238
|
+
},
|
|
239
|
+
multiply: function(e2) {
|
|
240
|
+
var num = new Array(this.getLength() + e2.getLength() - 1);
|
|
241
|
+
for (var i = 0;i < this.getLength(); i++) {
|
|
242
|
+
for (var j3 = 0;j3 < e2.getLength(); j3++) {
|
|
243
|
+
num[i + j3] ^= QRMath.gexp(QRMath.glog(this.get(i)) + QRMath.glog(e2.get(j3)));
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return new QRPolynomial(num, 0);
|
|
247
|
+
},
|
|
248
|
+
mod: function(e2) {
|
|
249
|
+
if (this.getLength() - e2.getLength() < 0) {
|
|
250
|
+
return this;
|
|
251
|
+
}
|
|
252
|
+
var ratio = QRMath.glog(this.get(0)) - QRMath.glog(e2.get(0));
|
|
253
|
+
var num = new Array(this.getLength());
|
|
254
|
+
for (var i = 0;i < this.getLength(); i++) {
|
|
255
|
+
num[i] = this.get(i);
|
|
256
|
+
}
|
|
257
|
+
for (var x2 = 0;x2 < e2.getLength(); x2++) {
|
|
258
|
+
num[x2] ^= QRMath.gexp(QRMath.glog(e2.get(x2)) + ratio);
|
|
259
|
+
}
|
|
260
|
+
return new QRPolynomial(num, 0).mod(e2);
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
module.exports = QRPolynomial;
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// node_modules/qrcode-terminal/vendor/QRCode/QRMaskPattern.js
|
|
267
|
+
var require_QRMaskPattern = __commonJS((exports, module) => {
|
|
268
|
+
module.exports = {
|
|
269
|
+
PATTERN000: 0,
|
|
270
|
+
PATTERN001: 1,
|
|
271
|
+
PATTERN010: 2,
|
|
272
|
+
PATTERN011: 3,
|
|
273
|
+
PATTERN100: 4,
|
|
274
|
+
PATTERN101: 5,
|
|
275
|
+
PATTERN110: 6,
|
|
276
|
+
PATTERN111: 7
|
|
277
|
+
};
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
// node_modules/qrcode-terminal/vendor/QRCode/QRUtil.js
|
|
281
|
+
var require_QRUtil = __commonJS((exports, module) => {
|
|
282
|
+
var QRMode = require_QRMode();
|
|
283
|
+
var QRPolynomial = require_QRPolynomial();
|
|
284
|
+
var QRMath = require_QRMath();
|
|
285
|
+
var QRMaskPattern = require_QRMaskPattern();
|
|
286
|
+
var QRUtil = {
|
|
287
|
+
PATTERN_POSITION_TABLE: [
|
|
288
|
+
[],
|
|
289
|
+
[6, 18],
|
|
290
|
+
[6, 22],
|
|
291
|
+
[6, 26],
|
|
292
|
+
[6, 30],
|
|
293
|
+
[6, 34],
|
|
294
|
+
[6, 22, 38],
|
|
295
|
+
[6, 24, 42],
|
|
296
|
+
[6, 26, 46],
|
|
297
|
+
[6, 28, 50],
|
|
298
|
+
[6, 30, 54],
|
|
299
|
+
[6, 32, 58],
|
|
300
|
+
[6, 34, 62],
|
|
301
|
+
[6, 26, 46, 66],
|
|
302
|
+
[6, 26, 48, 70],
|
|
303
|
+
[6, 26, 50, 74],
|
|
304
|
+
[6, 30, 54, 78],
|
|
305
|
+
[6, 30, 56, 82],
|
|
306
|
+
[6, 30, 58, 86],
|
|
307
|
+
[6, 34, 62, 90],
|
|
308
|
+
[6, 28, 50, 72, 94],
|
|
309
|
+
[6, 26, 50, 74, 98],
|
|
310
|
+
[6, 30, 54, 78, 102],
|
|
311
|
+
[6, 28, 54, 80, 106],
|
|
312
|
+
[6, 32, 58, 84, 110],
|
|
313
|
+
[6, 30, 58, 86, 114],
|
|
314
|
+
[6, 34, 62, 90, 118],
|
|
315
|
+
[6, 26, 50, 74, 98, 122],
|
|
316
|
+
[6, 30, 54, 78, 102, 126],
|
|
317
|
+
[6, 26, 52, 78, 104, 130],
|
|
318
|
+
[6, 30, 56, 82, 108, 134],
|
|
319
|
+
[6, 34, 60, 86, 112, 138],
|
|
320
|
+
[6, 30, 58, 86, 114, 142],
|
|
321
|
+
[6, 34, 62, 90, 118, 146],
|
|
322
|
+
[6, 30, 54, 78, 102, 126, 150],
|
|
323
|
+
[6, 24, 50, 76, 102, 128, 154],
|
|
324
|
+
[6, 28, 54, 80, 106, 132, 158],
|
|
325
|
+
[6, 32, 58, 84, 110, 136, 162],
|
|
326
|
+
[6, 26, 54, 82, 110, 138, 166],
|
|
327
|
+
[6, 30, 58, 86, 114, 142, 170]
|
|
328
|
+
],
|
|
329
|
+
G15: 1 << 10 | 1 << 8 | 1 << 5 | 1 << 4 | 1 << 2 | 1 << 1 | 1 << 0,
|
|
330
|
+
G18: 1 << 12 | 1 << 11 | 1 << 10 | 1 << 9 | 1 << 8 | 1 << 5 | 1 << 2 | 1 << 0,
|
|
331
|
+
G15_MASK: 1 << 14 | 1 << 12 | 1 << 10 | 1 << 4 | 1 << 1,
|
|
332
|
+
getBCHTypeInfo: function(data) {
|
|
333
|
+
var d2 = data << 10;
|
|
334
|
+
while (QRUtil.getBCHDigit(d2) - QRUtil.getBCHDigit(QRUtil.G15) >= 0) {
|
|
335
|
+
d2 ^= QRUtil.G15 << QRUtil.getBCHDigit(d2) - QRUtil.getBCHDigit(QRUtil.G15);
|
|
336
|
+
}
|
|
337
|
+
return (data << 10 | d2) ^ QRUtil.G15_MASK;
|
|
338
|
+
},
|
|
339
|
+
getBCHTypeNumber: function(data) {
|
|
340
|
+
var d2 = data << 12;
|
|
341
|
+
while (QRUtil.getBCHDigit(d2) - QRUtil.getBCHDigit(QRUtil.G18) >= 0) {
|
|
342
|
+
d2 ^= QRUtil.G18 << QRUtil.getBCHDigit(d2) - QRUtil.getBCHDigit(QRUtil.G18);
|
|
343
|
+
}
|
|
344
|
+
return data << 12 | d2;
|
|
345
|
+
},
|
|
346
|
+
getBCHDigit: function(data) {
|
|
347
|
+
var digit = 0;
|
|
348
|
+
while (data !== 0) {
|
|
349
|
+
digit++;
|
|
350
|
+
data >>>= 1;
|
|
351
|
+
}
|
|
352
|
+
return digit;
|
|
353
|
+
},
|
|
354
|
+
getPatternPosition: function(typeNumber) {
|
|
355
|
+
return QRUtil.PATTERN_POSITION_TABLE[typeNumber - 1];
|
|
356
|
+
},
|
|
357
|
+
getMask: function(maskPattern, i, j3) {
|
|
358
|
+
switch (maskPattern) {
|
|
359
|
+
case QRMaskPattern.PATTERN000:
|
|
360
|
+
return (i + j3) % 2 === 0;
|
|
361
|
+
case QRMaskPattern.PATTERN001:
|
|
362
|
+
return i % 2 === 0;
|
|
363
|
+
case QRMaskPattern.PATTERN010:
|
|
364
|
+
return j3 % 3 === 0;
|
|
365
|
+
case QRMaskPattern.PATTERN011:
|
|
366
|
+
return (i + j3) % 3 === 0;
|
|
367
|
+
case QRMaskPattern.PATTERN100:
|
|
368
|
+
return (Math.floor(i / 2) + Math.floor(j3 / 3)) % 2 === 0;
|
|
369
|
+
case QRMaskPattern.PATTERN101:
|
|
370
|
+
return i * j3 % 2 + i * j3 % 3 === 0;
|
|
371
|
+
case QRMaskPattern.PATTERN110:
|
|
372
|
+
return (i * j3 % 2 + i * j3 % 3) % 2 === 0;
|
|
373
|
+
case QRMaskPattern.PATTERN111:
|
|
374
|
+
return (i * j3 % 3 + (i + j3) % 2) % 2 === 0;
|
|
375
|
+
default:
|
|
376
|
+
throw new Error("bad maskPattern:" + maskPattern);
|
|
377
|
+
}
|
|
378
|
+
},
|
|
379
|
+
getErrorCorrectPolynomial: function(errorCorrectLength) {
|
|
380
|
+
var a3 = new QRPolynomial([1], 0);
|
|
381
|
+
for (var i = 0;i < errorCorrectLength; i++) {
|
|
382
|
+
a3 = a3.multiply(new QRPolynomial([1, QRMath.gexp(i)], 0));
|
|
383
|
+
}
|
|
384
|
+
return a3;
|
|
385
|
+
},
|
|
386
|
+
getLengthInBits: function(mode, type) {
|
|
387
|
+
if (1 <= type && type < 10) {
|
|
388
|
+
switch (mode) {
|
|
389
|
+
case QRMode.MODE_NUMBER:
|
|
390
|
+
return 10;
|
|
391
|
+
case QRMode.MODE_ALPHA_NUM:
|
|
392
|
+
return 9;
|
|
393
|
+
case QRMode.MODE_8BIT_BYTE:
|
|
394
|
+
return 8;
|
|
395
|
+
case QRMode.MODE_KANJI:
|
|
396
|
+
return 8;
|
|
397
|
+
default:
|
|
398
|
+
throw new Error("mode:" + mode);
|
|
399
|
+
}
|
|
400
|
+
} else if (type < 27) {
|
|
401
|
+
switch (mode) {
|
|
402
|
+
case QRMode.MODE_NUMBER:
|
|
403
|
+
return 12;
|
|
404
|
+
case QRMode.MODE_ALPHA_NUM:
|
|
405
|
+
return 11;
|
|
406
|
+
case QRMode.MODE_8BIT_BYTE:
|
|
407
|
+
return 16;
|
|
408
|
+
case QRMode.MODE_KANJI:
|
|
409
|
+
return 10;
|
|
410
|
+
default:
|
|
411
|
+
throw new Error("mode:" + mode);
|
|
412
|
+
}
|
|
413
|
+
} else if (type < 41) {
|
|
414
|
+
switch (mode) {
|
|
415
|
+
case QRMode.MODE_NUMBER:
|
|
416
|
+
return 14;
|
|
417
|
+
case QRMode.MODE_ALPHA_NUM:
|
|
418
|
+
return 13;
|
|
419
|
+
case QRMode.MODE_8BIT_BYTE:
|
|
420
|
+
return 16;
|
|
421
|
+
case QRMode.MODE_KANJI:
|
|
422
|
+
return 12;
|
|
423
|
+
default:
|
|
424
|
+
throw new Error("mode:" + mode);
|
|
425
|
+
}
|
|
426
|
+
} else {
|
|
427
|
+
throw new Error("type:" + type);
|
|
428
|
+
}
|
|
429
|
+
},
|
|
430
|
+
getLostPoint: function(qrCode) {
|
|
431
|
+
var moduleCount = qrCode.getModuleCount();
|
|
432
|
+
var lostPoint = 0;
|
|
433
|
+
var row = 0;
|
|
434
|
+
var col = 0;
|
|
435
|
+
for (row = 0;row < moduleCount; row++) {
|
|
436
|
+
for (col = 0;col < moduleCount; col++) {
|
|
437
|
+
var sameCount = 0;
|
|
438
|
+
var dark = qrCode.isDark(row, col);
|
|
439
|
+
for (var r2 = -1;r2 <= 1; r2++) {
|
|
440
|
+
if (row + r2 < 0 || moduleCount <= row + r2) {
|
|
441
|
+
continue;
|
|
442
|
+
}
|
|
443
|
+
for (var c = -1;c <= 1; c++) {
|
|
444
|
+
if (col + c < 0 || moduleCount <= col + c) {
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
if (r2 === 0 && c === 0) {
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
if (dark === qrCode.isDark(row + r2, col + c)) {
|
|
451
|
+
sameCount++;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
if (sameCount > 5) {
|
|
456
|
+
lostPoint += 3 + sameCount - 5;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
for (row = 0;row < moduleCount - 1; row++) {
|
|
461
|
+
for (col = 0;col < moduleCount - 1; col++) {
|
|
462
|
+
var count = 0;
|
|
463
|
+
if (qrCode.isDark(row, col))
|
|
464
|
+
count++;
|
|
465
|
+
if (qrCode.isDark(row + 1, col))
|
|
466
|
+
count++;
|
|
467
|
+
if (qrCode.isDark(row, col + 1))
|
|
468
|
+
count++;
|
|
469
|
+
if (qrCode.isDark(row + 1, col + 1))
|
|
470
|
+
count++;
|
|
471
|
+
if (count === 0 || count === 4) {
|
|
472
|
+
lostPoint += 3;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
for (row = 0;row < moduleCount; row++) {
|
|
477
|
+
for (col = 0;col < moduleCount - 6; col++) {
|
|
478
|
+
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)) {
|
|
479
|
+
lostPoint += 40;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
for (col = 0;col < moduleCount; col++) {
|
|
484
|
+
for (row = 0;row < moduleCount - 6; row++) {
|
|
485
|
+
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)) {
|
|
486
|
+
lostPoint += 40;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
var darkCount = 0;
|
|
491
|
+
for (col = 0;col < moduleCount; col++) {
|
|
492
|
+
for (row = 0;row < moduleCount; row++) {
|
|
493
|
+
if (qrCode.isDark(row, col)) {
|
|
494
|
+
darkCount++;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5;
|
|
499
|
+
lostPoint += ratio * 10;
|
|
500
|
+
return lostPoint;
|
|
501
|
+
}
|
|
502
|
+
};
|
|
503
|
+
module.exports = QRUtil;
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
// node_modules/qrcode-terminal/vendor/QRCode/QRErrorCorrectLevel.js
|
|
507
|
+
var require_QRErrorCorrectLevel = __commonJS((exports, module) => {
|
|
508
|
+
module.exports = {
|
|
509
|
+
L: 1,
|
|
510
|
+
M: 0,
|
|
511
|
+
Q: 3,
|
|
512
|
+
H: 2
|
|
513
|
+
};
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
// node_modules/qrcode-terminal/vendor/QRCode/QRRSBlock.js
|
|
517
|
+
var require_QRRSBlock = __commonJS((exports, module) => {
|
|
518
|
+
var QRErrorCorrectLevel = require_QRErrorCorrectLevel();
|
|
519
|
+
function QRRSBlock(totalCount, dataCount) {
|
|
520
|
+
this.totalCount = totalCount;
|
|
521
|
+
this.dataCount = dataCount;
|
|
522
|
+
}
|
|
523
|
+
QRRSBlock.RS_BLOCK_TABLE = [
|
|
524
|
+
[1, 26, 19],
|
|
525
|
+
[1, 26, 16],
|
|
526
|
+
[1, 26, 13],
|
|
527
|
+
[1, 26, 9],
|
|
528
|
+
[1, 44, 34],
|
|
529
|
+
[1, 44, 28],
|
|
530
|
+
[1, 44, 22],
|
|
531
|
+
[1, 44, 16],
|
|
532
|
+
[1, 70, 55],
|
|
533
|
+
[1, 70, 44],
|
|
534
|
+
[2, 35, 17],
|
|
535
|
+
[2, 35, 13],
|
|
536
|
+
[1, 100, 80],
|
|
537
|
+
[2, 50, 32],
|
|
538
|
+
[2, 50, 24],
|
|
539
|
+
[4, 25, 9],
|
|
540
|
+
[1, 134, 108],
|
|
541
|
+
[2, 67, 43],
|
|
542
|
+
[2, 33, 15, 2, 34, 16],
|
|
543
|
+
[2, 33, 11, 2, 34, 12],
|
|
544
|
+
[2, 86, 68],
|
|
545
|
+
[4, 43, 27],
|
|
546
|
+
[4, 43, 19],
|
|
547
|
+
[4, 43, 15],
|
|
548
|
+
[2, 98, 78],
|
|
549
|
+
[4, 49, 31],
|
|
550
|
+
[2, 32, 14, 4, 33, 15],
|
|
551
|
+
[4, 39, 13, 1, 40, 14],
|
|
552
|
+
[2, 121, 97],
|
|
553
|
+
[2, 60, 38, 2, 61, 39],
|
|
554
|
+
[4, 40, 18, 2, 41, 19],
|
|
555
|
+
[4, 40, 14, 2, 41, 15],
|
|
556
|
+
[2, 146, 116],
|
|
557
|
+
[3, 58, 36, 2, 59, 37],
|
|
558
|
+
[4, 36, 16, 4, 37, 17],
|
|
559
|
+
[4, 36, 12, 4, 37, 13],
|
|
560
|
+
[2, 86, 68, 2, 87, 69],
|
|
561
|
+
[4, 69, 43, 1, 70, 44],
|
|
562
|
+
[6, 43, 19, 2, 44, 20],
|
|
563
|
+
[6, 43, 15, 2, 44, 16],
|
|
564
|
+
[4, 101, 81],
|
|
565
|
+
[1, 80, 50, 4, 81, 51],
|
|
566
|
+
[4, 50, 22, 4, 51, 23],
|
|
567
|
+
[3, 36, 12, 8, 37, 13],
|
|
568
|
+
[2, 116, 92, 2, 117, 93],
|
|
569
|
+
[6, 58, 36, 2, 59, 37],
|
|
570
|
+
[4, 46, 20, 6, 47, 21],
|
|
571
|
+
[7, 42, 14, 4, 43, 15],
|
|
572
|
+
[4, 133, 107],
|
|
573
|
+
[8, 59, 37, 1, 60, 38],
|
|
574
|
+
[8, 44, 20, 4, 45, 21],
|
|
575
|
+
[12, 33, 11, 4, 34, 12],
|
|
576
|
+
[3, 145, 115, 1, 146, 116],
|
|
577
|
+
[4, 64, 40, 5, 65, 41],
|
|
578
|
+
[11, 36, 16, 5, 37, 17],
|
|
579
|
+
[11, 36, 12, 5, 37, 13],
|
|
580
|
+
[5, 109, 87, 1, 110, 88],
|
|
581
|
+
[5, 65, 41, 5, 66, 42],
|
|
582
|
+
[5, 54, 24, 7, 55, 25],
|
|
583
|
+
[11, 36, 12],
|
|
584
|
+
[5, 122, 98, 1, 123, 99],
|
|
585
|
+
[7, 73, 45, 3, 74, 46],
|
|
586
|
+
[15, 43, 19, 2, 44, 20],
|
|
587
|
+
[3, 45, 15, 13, 46, 16],
|
|
588
|
+
[1, 135, 107, 5, 136, 108],
|
|
589
|
+
[10, 74, 46, 1, 75, 47],
|
|
590
|
+
[1, 50, 22, 15, 51, 23],
|
|
591
|
+
[2, 42, 14, 17, 43, 15],
|
|
592
|
+
[5, 150, 120, 1, 151, 121],
|
|
593
|
+
[9, 69, 43, 4, 70, 44],
|
|
594
|
+
[17, 50, 22, 1, 51, 23],
|
|
595
|
+
[2, 42, 14, 19, 43, 15],
|
|
596
|
+
[3, 141, 113, 4, 142, 114],
|
|
597
|
+
[3, 70, 44, 11, 71, 45],
|
|
598
|
+
[17, 47, 21, 4, 48, 22],
|
|
599
|
+
[9, 39, 13, 16, 40, 14],
|
|
600
|
+
[3, 135, 107, 5, 136, 108],
|
|
601
|
+
[3, 67, 41, 13, 68, 42],
|
|
602
|
+
[15, 54, 24, 5, 55, 25],
|
|
603
|
+
[15, 43, 15, 10, 44, 16],
|
|
604
|
+
[4, 144, 116, 4, 145, 117],
|
|
605
|
+
[17, 68, 42],
|
|
606
|
+
[17, 50, 22, 6, 51, 23],
|
|
607
|
+
[19, 46, 16, 6, 47, 17],
|
|
608
|
+
[2, 139, 111, 7, 140, 112],
|
|
609
|
+
[17, 74, 46],
|
|
610
|
+
[7, 54, 24, 16, 55, 25],
|
|
611
|
+
[34, 37, 13],
|
|
612
|
+
[4, 151, 121, 5, 152, 122],
|
|
613
|
+
[4, 75, 47, 14, 76, 48],
|
|
614
|
+
[11, 54, 24, 14, 55, 25],
|
|
615
|
+
[16, 45, 15, 14, 46, 16],
|
|
616
|
+
[6, 147, 117, 4, 148, 118],
|
|
617
|
+
[6, 73, 45, 14, 74, 46],
|
|
618
|
+
[11, 54, 24, 16, 55, 25],
|
|
619
|
+
[30, 46, 16, 2, 47, 17],
|
|
620
|
+
[8, 132, 106, 4, 133, 107],
|
|
621
|
+
[8, 75, 47, 13, 76, 48],
|
|
622
|
+
[7, 54, 24, 22, 55, 25],
|
|
623
|
+
[22, 45, 15, 13, 46, 16],
|
|
624
|
+
[10, 142, 114, 2, 143, 115],
|
|
625
|
+
[19, 74, 46, 4, 75, 47],
|
|
626
|
+
[28, 50, 22, 6, 51, 23],
|
|
627
|
+
[33, 46, 16, 4, 47, 17],
|
|
628
|
+
[8, 152, 122, 4, 153, 123],
|
|
629
|
+
[22, 73, 45, 3, 74, 46],
|
|
630
|
+
[8, 53, 23, 26, 54, 24],
|
|
631
|
+
[12, 45, 15, 28, 46, 16],
|
|
632
|
+
[3, 147, 117, 10, 148, 118],
|
|
633
|
+
[3, 73, 45, 23, 74, 46],
|
|
634
|
+
[4, 54, 24, 31, 55, 25],
|
|
635
|
+
[11, 45, 15, 31, 46, 16],
|
|
636
|
+
[7, 146, 116, 7, 147, 117],
|
|
637
|
+
[21, 73, 45, 7, 74, 46],
|
|
638
|
+
[1, 53, 23, 37, 54, 24],
|
|
639
|
+
[19, 45, 15, 26, 46, 16],
|
|
640
|
+
[5, 145, 115, 10, 146, 116],
|
|
641
|
+
[19, 75, 47, 10, 76, 48],
|
|
642
|
+
[15, 54, 24, 25, 55, 25],
|
|
643
|
+
[23, 45, 15, 25, 46, 16],
|
|
644
|
+
[13, 145, 115, 3, 146, 116],
|
|
645
|
+
[2, 74, 46, 29, 75, 47],
|
|
646
|
+
[42, 54, 24, 1, 55, 25],
|
|
647
|
+
[23, 45, 15, 28, 46, 16],
|
|
648
|
+
[17, 145, 115],
|
|
649
|
+
[10, 74, 46, 23, 75, 47],
|
|
650
|
+
[10, 54, 24, 35, 55, 25],
|
|
651
|
+
[19, 45, 15, 35, 46, 16],
|
|
652
|
+
[17, 145, 115, 1, 146, 116],
|
|
653
|
+
[14, 74, 46, 21, 75, 47],
|
|
654
|
+
[29, 54, 24, 19, 55, 25],
|
|
655
|
+
[11, 45, 15, 46, 46, 16],
|
|
656
|
+
[13, 145, 115, 6, 146, 116],
|
|
657
|
+
[14, 74, 46, 23, 75, 47],
|
|
658
|
+
[44, 54, 24, 7, 55, 25],
|
|
659
|
+
[59, 46, 16, 1, 47, 17],
|
|
660
|
+
[12, 151, 121, 7, 152, 122],
|
|
661
|
+
[12, 75, 47, 26, 76, 48],
|
|
662
|
+
[39, 54, 24, 14, 55, 25],
|
|
663
|
+
[22, 45, 15, 41, 46, 16],
|
|
664
|
+
[6, 151, 121, 14, 152, 122],
|
|
665
|
+
[6, 75, 47, 34, 76, 48],
|
|
666
|
+
[46, 54, 24, 10, 55, 25],
|
|
667
|
+
[2, 45, 15, 64, 46, 16],
|
|
668
|
+
[17, 152, 122, 4, 153, 123],
|
|
669
|
+
[29, 74, 46, 14, 75, 47],
|
|
670
|
+
[49, 54, 24, 10, 55, 25],
|
|
671
|
+
[24, 45, 15, 46, 46, 16],
|
|
672
|
+
[4, 152, 122, 18, 153, 123],
|
|
673
|
+
[13, 74, 46, 32, 75, 47],
|
|
674
|
+
[48, 54, 24, 14, 55, 25],
|
|
675
|
+
[42, 45, 15, 32, 46, 16],
|
|
676
|
+
[20, 147, 117, 4, 148, 118],
|
|
677
|
+
[40, 75, 47, 7, 76, 48],
|
|
678
|
+
[43, 54, 24, 22, 55, 25],
|
|
679
|
+
[10, 45, 15, 67, 46, 16],
|
|
680
|
+
[19, 148, 118, 6, 149, 119],
|
|
681
|
+
[18, 75, 47, 31, 76, 48],
|
|
682
|
+
[34, 54, 24, 34, 55, 25],
|
|
683
|
+
[20, 45, 15, 61, 46, 16]
|
|
684
|
+
];
|
|
685
|
+
QRRSBlock.getRSBlocks = function(typeNumber, errorCorrectLevel) {
|
|
686
|
+
var rsBlock = QRRSBlock.getRsBlockTable(typeNumber, errorCorrectLevel);
|
|
687
|
+
if (rsBlock === undefined) {
|
|
688
|
+
throw new Error("bad rs block @ typeNumber:" + typeNumber + "/errorCorrectLevel:" + errorCorrectLevel);
|
|
689
|
+
}
|
|
690
|
+
var length = rsBlock.length / 3;
|
|
691
|
+
var list = [];
|
|
692
|
+
for (var i = 0;i < length; i++) {
|
|
693
|
+
var count = rsBlock[i * 3 + 0];
|
|
694
|
+
var totalCount = rsBlock[i * 3 + 1];
|
|
695
|
+
var dataCount = rsBlock[i * 3 + 2];
|
|
696
|
+
for (var j3 = 0;j3 < count; j3++) {
|
|
697
|
+
list.push(new QRRSBlock(totalCount, dataCount));
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
return list;
|
|
701
|
+
};
|
|
702
|
+
QRRSBlock.getRsBlockTable = function(typeNumber, errorCorrectLevel) {
|
|
703
|
+
switch (errorCorrectLevel) {
|
|
704
|
+
case QRErrorCorrectLevel.L:
|
|
705
|
+
return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0];
|
|
706
|
+
case QRErrorCorrectLevel.M:
|
|
707
|
+
return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1];
|
|
708
|
+
case QRErrorCorrectLevel.Q:
|
|
709
|
+
return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2];
|
|
710
|
+
case QRErrorCorrectLevel.H:
|
|
711
|
+
return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3];
|
|
712
|
+
default:
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
};
|
|
716
|
+
module.exports = QRRSBlock;
|
|
717
|
+
});
|
|
718
|
+
|
|
719
|
+
// node_modules/qrcode-terminal/vendor/QRCode/QRBitBuffer.js
|
|
720
|
+
var require_QRBitBuffer = __commonJS((exports, module) => {
|
|
721
|
+
function QRBitBuffer() {
|
|
722
|
+
this.buffer = [];
|
|
723
|
+
this.length = 0;
|
|
724
|
+
}
|
|
725
|
+
QRBitBuffer.prototype = {
|
|
726
|
+
get: function(index) {
|
|
727
|
+
var bufIndex = Math.floor(index / 8);
|
|
728
|
+
return (this.buffer[bufIndex] >>> 7 - index % 8 & 1) == 1;
|
|
729
|
+
},
|
|
730
|
+
put: function(num, length) {
|
|
731
|
+
for (var i = 0;i < length; i++) {
|
|
732
|
+
this.putBit((num >>> length - i - 1 & 1) == 1);
|
|
733
|
+
}
|
|
734
|
+
},
|
|
735
|
+
getLengthInBits: function() {
|
|
736
|
+
return this.length;
|
|
737
|
+
},
|
|
738
|
+
putBit: function(bit) {
|
|
739
|
+
var bufIndex = Math.floor(this.length / 8);
|
|
740
|
+
if (this.buffer.length <= bufIndex) {
|
|
741
|
+
this.buffer.push(0);
|
|
742
|
+
}
|
|
743
|
+
if (bit) {
|
|
744
|
+
this.buffer[bufIndex] |= 128 >>> this.length % 8;
|
|
745
|
+
}
|
|
746
|
+
this.length++;
|
|
747
|
+
}
|
|
748
|
+
};
|
|
749
|
+
module.exports = QRBitBuffer;
|
|
750
|
+
});
|
|
751
|
+
|
|
752
|
+
// node_modules/qrcode-terminal/vendor/QRCode/index.js
|
|
753
|
+
var require_QRCode = __commonJS((exports, module) => {
|
|
754
|
+
var QR8bitByte = require_QR8bitByte();
|
|
755
|
+
var QRUtil = require_QRUtil();
|
|
756
|
+
var QRPolynomial = require_QRPolynomial();
|
|
757
|
+
var QRRSBlock = require_QRRSBlock();
|
|
758
|
+
var QRBitBuffer = require_QRBitBuffer();
|
|
759
|
+
function QRCode(typeNumber, errorCorrectLevel) {
|
|
760
|
+
this.typeNumber = typeNumber;
|
|
761
|
+
this.errorCorrectLevel = errorCorrectLevel;
|
|
762
|
+
this.modules = null;
|
|
763
|
+
this.moduleCount = 0;
|
|
764
|
+
this.dataCache = null;
|
|
765
|
+
this.dataList = [];
|
|
766
|
+
}
|
|
767
|
+
QRCode.prototype = {
|
|
768
|
+
addData: function(data) {
|
|
769
|
+
var newData = new QR8bitByte(data);
|
|
770
|
+
this.dataList.push(newData);
|
|
771
|
+
this.dataCache = null;
|
|
772
|
+
},
|
|
773
|
+
isDark: function(row, col) {
|
|
774
|
+
if (row < 0 || this.moduleCount <= row || col < 0 || this.moduleCount <= col) {
|
|
775
|
+
throw new Error(row + "," + col);
|
|
776
|
+
}
|
|
777
|
+
return this.modules[row][col];
|
|
778
|
+
},
|
|
779
|
+
getModuleCount: function() {
|
|
780
|
+
return this.moduleCount;
|
|
781
|
+
},
|
|
782
|
+
make: function() {
|
|
783
|
+
if (this.typeNumber < 1) {
|
|
784
|
+
var typeNumber = 1;
|
|
785
|
+
for (typeNumber = 1;typeNumber < 40; typeNumber++) {
|
|
786
|
+
var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, this.errorCorrectLevel);
|
|
787
|
+
var buffer = new QRBitBuffer;
|
|
788
|
+
var totalDataCount = 0;
|
|
789
|
+
for (var i = 0;i < rsBlocks.length; i++) {
|
|
790
|
+
totalDataCount += rsBlocks[i].dataCount;
|
|
791
|
+
}
|
|
792
|
+
for (var x2 = 0;x2 < this.dataList.length; x2++) {
|
|
793
|
+
var data = this.dataList[x2];
|
|
794
|
+
buffer.put(data.mode, 4);
|
|
795
|
+
buffer.put(data.getLength(), QRUtil.getLengthInBits(data.mode, typeNumber));
|
|
796
|
+
data.write(buffer);
|
|
797
|
+
}
|
|
798
|
+
if (buffer.getLengthInBits() <= totalDataCount * 8)
|
|
799
|
+
break;
|
|
800
|
+
}
|
|
801
|
+
this.typeNumber = typeNumber;
|
|
802
|
+
}
|
|
803
|
+
this.makeImpl(false, this.getBestMaskPattern());
|
|
804
|
+
},
|
|
805
|
+
makeImpl: function(test, maskPattern) {
|
|
806
|
+
this.moduleCount = this.typeNumber * 4 + 17;
|
|
807
|
+
this.modules = new Array(this.moduleCount);
|
|
808
|
+
for (var row = 0;row < this.moduleCount; row++) {
|
|
809
|
+
this.modules[row] = new Array(this.moduleCount);
|
|
810
|
+
for (var col = 0;col < this.moduleCount; col++) {
|
|
811
|
+
this.modules[row][col] = null;
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
this.setupPositionProbePattern(0, 0);
|
|
815
|
+
this.setupPositionProbePattern(this.moduleCount - 7, 0);
|
|
816
|
+
this.setupPositionProbePattern(0, this.moduleCount - 7);
|
|
817
|
+
this.setupPositionAdjustPattern();
|
|
818
|
+
this.setupTimingPattern();
|
|
819
|
+
this.setupTypeInfo(test, maskPattern);
|
|
820
|
+
if (this.typeNumber >= 7) {
|
|
821
|
+
this.setupTypeNumber(test);
|
|
822
|
+
}
|
|
823
|
+
if (this.dataCache === null) {
|
|
824
|
+
this.dataCache = QRCode.createData(this.typeNumber, this.errorCorrectLevel, this.dataList);
|
|
825
|
+
}
|
|
826
|
+
this.mapData(this.dataCache, maskPattern);
|
|
827
|
+
},
|
|
828
|
+
setupPositionProbePattern: function(row, col) {
|
|
829
|
+
for (var r2 = -1;r2 <= 7; r2++) {
|
|
830
|
+
if (row + r2 <= -1 || this.moduleCount <= row + r2)
|
|
831
|
+
continue;
|
|
832
|
+
for (var c = -1;c <= 7; c++) {
|
|
833
|
+
if (col + c <= -1 || this.moduleCount <= col + c)
|
|
834
|
+
continue;
|
|
835
|
+
if (0 <= r2 && r2 <= 6 && (c === 0 || c === 6) || 0 <= c && c <= 6 && (r2 === 0 || r2 === 6) || 2 <= r2 && r2 <= 4 && 2 <= c && c <= 4) {
|
|
836
|
+
this.modules[row + r2][col + c] = true;
|
|
837
|
+
} else {
|
|
838
|
+
this.modules[row + r2][col + c] = false;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
},
|
|
843
|
+
getBestMaskPattern: function() {
|
|
844
|
+
var minLostPoint = 0;
|
|
845
|
+
var pattern = 0;
|
|
846
|
+
for (var i = 0;i < 8; i++) {
|
|
847
|
+
this.makeImpl(true, i);
|
|
848
|
+
var lostPoint = QRUtil.getLostPoint(this);
|
|
849
|
+
if (i === 0 || minLostPoint > lostPoint) {
|
|
850
|
+
minLostPoint = lostPoint;
|
|
851
|
+
pattern = i;
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
return pattern;
|
|
855
|
+
},
|
|
856
|
+
createMovieClip: function(target_mc, instance_name, depth) {
|
|
857
|
+
var qr_mc = target_mc.createEmptyMovieClip(instance_name, depth);
|
|
858
|
+
var cs = 1;
|
|
859
|
+
this.make();
|
|
860
|
+
for (var row = 0;row < this.modules.length; row++) {
|
|
861
|
+
var y3 = row * cs;
|
|
862
|
+
for (var col = 0;col < this.modules[row].length; col++) {
|
|
863
|
+
var x2 = col * cs;
|
|
864
|
+
var dark = this.modules[row][col];
|
|
865
|
+
if (dark) {
|
|
866
|
+
qr_mc.beginFill(0, 100);
|
|
867
|
+
qr_mc.moveTo(x2, y3);
|
|
868
|
+
qr_mc.lineTo(x2 + cs, y3);
|
|
869
|
+
qr_mc.lineTo(x2 + cs, y3 + cs);
|
|
870
|
+
qr_mc.lineTo(x2, y3 + cs);
|
|
871
|
+
qr_mc.endFill();
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
return qr_mc;
|
|
876
|
+
},
|
|
877
|
+
setupTimingPattern: function() {
|
|
878
|
+
for (var r2 = 8;r2 < this.moduleCount - 8; r2++) {
|
|
879
|
+
if (this.modules[r2][6] !== null) {
|
|
880
|
+
continue;
|
|
881
|
+
}
|
|
882
|
+
this.modules[r2][6] = r2 % 2 === 0;
|
|
883
|
+
}
|
|
884
|
+
for (var c = 8;c < this.moduleCount - 8; c++) {
|
|
885
|
+
if (this.modules[6][c] !== null) {
|
|
886
|
+
continue;
|
|
887
|
+
}
|
|
888
|
+
this.modules[6][c] = c % 2 === 0;
|
|
889
|
+
}
|
|
890
|
+
},
|
|
891
|
+
setupPositionAdjustPattern: function() {
|
|
892
|
+
var pos = QRUtil.getPatternPosition(this.typeNumber);
|
|
893
|
+
for (var i = 0;i < pos.length; i++) {
|
|
894
|
+
for (var j3 = 0;j3 < pos.length; j3++) {
|
|
895
|
+
var row = pos[i];
|
|
896
|
+
var col = pos[j3];
|
|
897
|
+
if (this.modules[row][col] !== null) {
|
|
898
|
+
continue;
|
|
899
|
+
}
|
|
900
|
+
for (var r2 = -2;r2 <= 2; r2++) {
|
|
901
|
+
for (var c = -2;c <= 2; c++) {
|
|
902
|
+
if (Math.abs(r2) === 2 || Math.abs(c) === 2 || r2 === 0 && c === 0) {
|
|
903
|
+
this.modules[row + r2][col + c] = true;
|
|
904
|
+
} else {
|
|
905
|
+
this.modules[row + r2][col + c] = false;
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
},
|
|
912
|
+
setupTypeNumber: function(test) {
|
|
913
|
+
var bits = QRUtil.getBCHTypeNumber(this.typeNumber);
|
|
914
|
+
var mod;
|
|
915
|
+
for (var i = 0;i < 18; i++) {
|
|
916
|
+
mod = !test && (bits >> i & 1) === 1;
|
|
917
|
+
this.modules[Math.floor(i / 3)][i % 3 + this.moduleCount - 8 - 3] = mod;
|
|
918
|
+
}
|
|
919
|
+
for (var x2 = 0;x2 < 18; x2++) {
|
|
920
|
+
mod = !test && (bits >> x2 & 1) === 1;
|
|
921
|
+
this.modules[x2 % 3 + this.moduleCount - 8 - 3][Math.floor(x2 / 3)] = mod;
|
|
922
|
+
}
|
|
923
|
+
},
|
|
924
|
+
setupTypeInfo: function(test, maskPattern) {
|
|
925
|
+
var data = this.errorCorrectLevel << 3 | maskPattern;
|
|
926
|
+
var bits = QRUtil.getBCHTypeInfo(data);
|
|
927
|
+
var mod;
|
|
928
|
+
for (var v3 = 0;v3 < 15; v3++) {
|
|
929
|
+
mod = !test && (bits >> v3 & 1) === 1;
|
|
930
|
+
if (v3 < 6) {
|
|
931
|
+
this.modules[v3][8] = mod;
|
|
932
|
+
} else if (v3 < 8) {
|
|
933
|
+
this.modules[v3 + 1][8] = mod;
|
|
934
|
+
} else {
|
|
935
|
+
this.modules[this.moduleCount - 15 + v3][8] = mod;
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
for (var h2 = 0;h2 < 15; h2++) {
|
|
939
|
+
mod = !test && (bits >> h2 & 1) === 1;
|
|
940
|
+
if (h2 < 8) {
|
|
941
|
+
this.modules[8][this.moduleCount - h2 - 1] = mod;
|
|
942
|
+
} else if (h2 < 9) {
|
|
943
|
+
this.modules[8][15 - h2 - 1 + 1] = mod;
|
|
944
|
+
} else {
|
|
945
|
+
this.modules[8][15 - h2 - 1] = mod;
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
this.modules[this.moduleCount - 8][8] = !test;
|
|
949
|
+
},
|
|
950
|
+
mapData: function(data, maskPattern) {
|
|
951
|
+
var inc = -1;
|
|
952
|
+
var row = this.moduleCount - 1;
|
|
953
|
+
var bitIndex = 7;
|
|
954
|
+
var byteIndex = 0;
|
|
955
|
+
for (var col = this.moduleCount - 1;col > 0; col -= 2) {
|
|
956
|
+
if (col === 6)
|
|
957
|
+
col--;
|
|
958
|
+
while (true) {
|
|
959
|
+
for (var c = 0;c < 2; c++) {
|
|
960
|
+
if (this.modules[row][col - c] === null) {
|
|
961
|
+
var dark = false;
|
|
962
|
+
if (byteIndex < data.length) {
|
|
963
|
+
dark = (data[byteIndex] >>> bitIndex & 1) === 1;
|
|
964
|
+
}
|
|
965
|
+
var mask = QRUtil.getMask(maskPattern, row, col - c);
|
|
966
|
+
if (mask) {
|
|
967
|
+
dark = !dark;
|
|
968
|
+
}
|
|
969
|
+
this.modules[row][col - c] = dark;
|
|
970
|
+
bitIndex--;
|
|
971
|
+
if (bitIndex === -1) {
|
|
972
|
+
byteIndex++;
|
|
973
|
+
bitIndex = 7;
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
row += inc;
|
|
978
|
+
if (row < 0 || this.moduleCount <= row) {
|
|
979
|
+
row -= inc;
|
|
980
|
+
inc = -inc;
|
|
981
|
+
break;
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
};
|
|
987
|
+
QRCode.PAD0 = 236;
|
|
988
|
+
QRCode.PAD1 = 17;
|
|
989
|
+
QRCode.createData = function(typeNumber, errorCorrectLevel, dataList) {
|
|
990
|
+
var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectLevel);
|
|
991
|
+
var buffer = new QRBitBuffer;
|
|
992
|
+
for (var i = 0;i < dataList.length; i++) {
|
|
993
|
+
var data = dataList[i];
|
|
994
|
+
buffer.put(data.mode, 4);
|
|
995
|
+
buffer.put(data.getLength(), QRUtil.getLengthInBits(data.mode, typeNumber));
|
|
996
|
+
data.write(buffer);
|
|
997
|
+
}
|
|
998
|
+
var totalDataCount = 0;
|
|
999
|
+
for (var x2 = 0;x2 < rsBlocks.length; x2++) {
|
|
1000
|
+
totalDataCount += rsBlocks[x2].dataCount;
|
|
1001
|
+
}
|
|
1002
|
+
if (buffer.getLengthInBits() > totalDataCount * 8) {
|
|
1003
|
+
throw new Error("code length overflow. (" + buffer.getLengthInBits() + ">" + totalDataCount * 8 + ")");
|
|
1004
|
+
}
|
|
1005
|
+
if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) {
|
|
1006
|
+
buffer.put(0, 4);
|
|
1007
|
+
}
|
|
1008
|
+
while (buffer.getLengthInBits() % 8 !== 0) {
|
|
1009
|
+
buffer.putBit(false);
|
|
1010
|
+
}
|
|
1011
|
+
while (true) {
|
|
1012
|
+
if (buffer.getLengthInBits() >= totalDataCount * 8) {
|
|
1013
|
+
break;
|
|
1014
|
+
}
|
|
1015
|
+
buffer.put(QRCode.PAD0, 8);
|
|
1016
|
+
if (buffer.getLengthInBits() >= totalDataCount * 8) {
|
|
1017
|
+
break;
|
|
1018
|
+
}
|
|
1019
|
+
buffer.put(QRCode.PAD1, 8);
|
|
1020
|
+
}
|
|
1021
|
+
return QRCode.createBytes(buffer, rsBlocks);
|
|
1022
|
+
};
|
|
1023
|
+
QRCode.createBytes = function(buffer, rsBlocks) {
|
|
1024
|
+
var offset = 0;
|
|
1025
|
+
var maxDcCount = 0;
|
|
1026
|
+
var maxEcCount = 0;
|
|
1027
|
+
var dcdata = new Array(rsBlocks.length);
|
|
1028
|
+
var ecdata = new Array(rsBlocks.length);
|
|
1029
|
+
for (var r2 = 0;r2 < rsBlocks.length; r2++) {
|
|
1030
|
+
var dcCount = rsBlocks[r2].dataCount;
|
|
1031
|
+
var ecCount = rsBlocks[r2].totalCount - dcCount;
|
|
1032
|
+
maxDcCount = Math.max(maxDcCount, dcCount);
|
|
1033
|
+
maxEcCount = Math.max(maxEcCount, ecCount);
|
|
1034
|
+
dcdata[r2] = new Array(dcCount);
|
|
1035
|
+
for (var i = 0;i < dcdata[r2].length; i++) {
|
|
1036
|
+
dcdata[r2][i] = 255 & buffer.buffer[i + offset];
|
|
1037
|
+
}
|
|
1038
|
+
offset += dcCount;
|
|
1039
|
+
var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount);
|
|
1040
|
+
var rawPoly = new QRPolynomial(dcdata[r2], rsPoly.getLength() - 1);
|
|
1041
|
+
var modPoly = rawPoly.mod(rsPoly);
|
|
1042
|
+
ecdata[r2] = new Array(rsPoly.getLength() - 1);
|
|
1043
|
+
for (var x2 = 0;x2 < ecdata[r2].length; x2++) {
|
|
1044
|
+
var modIndex = x2 + modPoly.getLength() - ecdata[r2].length;
|
|
1045
|
+
ecdata[r2][x2] = modIndex >= 0 ? modPoly.get(modIndex) : 0;
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
var totalCodeCount = 0;
|
|
1049
|
+
for (var y3 = 0;y3 < rsBlocks.length; y3++) {
|
|
1050
|
+
totalCodeCount += rsBlocks[y3].totalCount;
|
|
1051
|
+
}
|
|
1052
|
+
var data = new Array(totalCodeCount);
|
|
1053
|
+
var index = 0;
|
|
1054
|
+
for (var z2 = 0;z2 < maxDcCount; z2++) {
|
|
1055
|
+
for (var s = 0;s < rsBlocks.length; s++) {
|
|
1056
|
+
if (z2 < dcdata[s].length) {
|
|
1057
|
+
data[index++] = dcdata[s][z2];
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
for (var xx = 0;xx < maxEcCount; xx++) {
|
|
1062
|
+
for (var t = 0;t < rsBlocks.length; t++) {
|
|
1063
|
+
if (xx < ecdata[t].length) {
|
|
1064
|
+
data[index++] = ecdata[t][xx];
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
return data;
|
|
1069
|
+
};
|
|
1070
|
+
module.exports = QRCode;
|
|
1071
|
+
});
|
|
1072
|
+
|
|
1073
|
+
// node_modules/qrcode-terminal/lib/main.js
|
|
1074
|
+
var require_main = __commonJS((exports, module) => {
|
|
1075
|
+
var QRCode = require_QRCode();
|
|
1076
|
+
var QRErrorCorrectLevel = require_QRErrorCorrectLevel();
|
|
1077
|
+
var black = "\x1B[40m \x1B[0m";
|
|
1078
|
+
var white = "\x1B[47m \x1B[0m";
|
|
1079
|
+
var toCell = function(isBlack) {
|
|
1080
|
+
return isBlack ? black : white;
|
|
1081
|
+
};
|
|
1082
|
+
var repeat = function(color) {
|
|
1083
|
+
return {
|
|
1084
|
+
times: function(count) {
|
|
1085
|
+
return new Array(count).join(color);
|
|
1086
|
+
}
|
|
1087
|
+
};
|
|
1088
|
+
};
|
|
1089
|
+
var fill = function(length, value) {
|
|
1090
|
+
var arr = new Array(length);
|
|
1091
|
+
for (var i = 0;i < length; i++) {
|
|
1092
|
+
arr[i] = value;
|
|
1093
|
+
}
|
|
1094
|
+
return arr;
|
|
1095
|
+
};
|
|
1096
|
+
module.exports = {
|
|
1097
|
+
error: QRErrorCorrectLevel.L,
|
|
1098
|
+
generate: function(input, opts, cb) {
|
|
1099
|
+
if (typeof opts === "function") {
|
|
1100
|
+
cb = opts;
|
|
1101
|
+
opts = {};
|
|
1102
|
+
}
|
|
1103
|
+
var qrcode = new QRCode(-1, this.error);
|
|
1104
|
+
qrcode.addData(input);
|
|
1105
|
+
qrcode.make();
|
|
1106
|
+
var output = "";
|
|
1107
|
+
if (opts && opts.small) {
|
|
1108
|
+
var BLACK = true, WHITE = false;
|
|
1109
|
+
var moduleCount = qrcode.getModuleCount();
|
|
1110
|
+
var moduleData = qrcode.modules.slice();
|
|
1111
|
+
var oddRow = moduleCount % 2 === 1;
|
|
1112
|
+
if (oddRow) {
|
|
1113
|
+
moduleData.push(fill(moduleCount, WHITE));
|
|
1114
|
+
}
|
|
1115
|
+
var platte = {
|
|
1116
|
+
WHITE_ALL: "█",
|
|
1117
|
+
WHITE_BLACK: "▀",
|
|
1118
|
+
BLACK_WHITE: "▄",
|
|
1119
|
+
BLACK_ALL: " "
|
|
1120
|
+
};
|
|
1121
|
+
var borderTop = repeat(platte.BLACK_WHITE).times(moduleCount + 3);
|
|
1122
|
+
var borderBottom = repeat(platte.WHITE_BLACK).times(moduleCount + 3);
|
|
1123
|
+
output += borderTop + `
|
|
1124
|
+
`;
|
|
1125
|
+
for (var row = 0;row < moduleCount; row += 2) {
|
|
1126
|
+
output += platte.WHITE_ALL;
|
|
1127
|
+
for (var col = 0;col < moduleCount; col++) {
|
|
1128
|
+
if (moduleData[row][col] === WHITE && moduleData[row + 1][col] === WHITE) {
|
|
1129
|
+
output += platte.WHITE_ALL;
|
|
1130
|
+
} else if (moduleData[row][col] === WHITE && moduleData[row + 1][col] === BLACK) {
|
|
1131
|
+
output += platte.WHITE_BLACK;
|
|
1132
|
+
} else if (moduleData[row][col] === BLACK && moduleData[row + 1][col] === WHITE) {
|
|
1133
|
+
output += platte.BLACK_WHITE;
|
|
1134
|
+
} else {
|
|
1135
|
+
output += platte.BLACK_ALL;
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
output += platte.WHITE_ALL + `
|
|
1139
|
+
`;
|
|
1140
|
+
}
|
|
1141
|
+
if (!oddRow) {
|
|
1142
|
+
output += borderBottom;
|
|
1143
|
+
}
|
|
1144
|
+
} else {
|
|
1145
|
+
var border = repeat(white).times(qrcode.getModuleCount() + 3);
|
|
1146
|
+
output += border + `
|
|
1147
|
+
`;
|
|
1148
|
+
qrcode.modules.forEach(function(row2) {
|
|
1149
|
+
output += white;
|
|
1150
|
+
output += row2.map(toCell).join("");
|
|
1151
|
+
output += white + `
|
|
1152
|
+
`;
|
|
1153
|
+
});
|
|
1154
|
+
output += border;
|
|
1155
|
+
}
|
|
1156
|
+
if (cb)
|
|
1157
|
+
cb(output);
|
|
1158
|
+
else
|
|
1159
|
+
console.log(output);
|
|
1160
|
+
},
|
|
1161
|
+
setErrorLevel: function(error) {
|
|
1162
|
+
this.error = QRErrorCorrectLevel[error] || this.error;
|
|
1163
|
+
}
|
|
1164
|
+
};
|
|
1165
|
+
});
|
|
1166
|
+
|
|
1167
|
+
// node_modules/@clack/core/dist/index.mjs
|
|
1168
|
+
var import_sisteransi = __toESM(require_src(), 1);
|
|
1169
|
+
import { stdin as $, stdout as k } from "node:process";
|
|
1170
|
+
import * as f from "node:readline";
|
|
1171
|
+
import _ from "node:readline";
|
|
1172
|
+
import { WriteStream as U } from "node:tty";
|
|
1173
|
+
function q({ onlyFirst: e = false } = {}) {
|
|
1174
|
+
const F = ["[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?(?:\\u0007|\\u001B\\u005C|\\u009C))", "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))"].join("|");
|
|
1175
|
+
return new RegExp(F, e ? undefined : "g");
|
|
1176
|
+
}
|
|
1177
|
+
var J = q();
|
|
1178
|
+
function S(e) {
|
|
1179
|
+
if (typeof e != "string")
|
|
1180
|
+
throw new TypeError(`Expected a \`string\`, got \`${typeof e}\``);
|
|
1181
|
+
return e.replace(J, "");
|
|
1182
|
+
}
|
|
1183
|
+
function T(e) {
|
|
1184
|
+
return e && e.__esModule && Object.prototype.hasOwnProperty.call(e, "default") ? e.default : e;
|
|
1185
|
+
}
|
|
1186
|
+
var j = { exports: {} };
|
|
1187
|
+
(function(e) {
|
|
1188
|
+
var u = {};
|
|
1189
|
+
e.exports = u, u.eastAsianWidth = function(t) {
|
|
1190
|
+
var s = t.charCodeAt(0), C = t.length == 2 ? t.charCodeAt(1) : 0, D = s;
|
|
1191
|
+
return 55296 <= s && s <= 56319 && 56320 <= C && C <= 57343 && (s &= 1023, C &= 1023, D = s << 10 | C, D += 65536), D == 12288 || 65281 <= D && D <= 65376 || 65504 <= D && D <= 65510 ? "F" : D == 8361 || 65377 <= D && D <= 65470 || 65474 <= D && D <= 65479 || 65482 <= D && D <= 65487 || 65490 <= D && D <= 65495 || 65498 <= D && D <= 65500 || 65512 <= D && D <= 65518 ? "H" : 4352 <= D && D <= 4447 || 4515 <= D && D <= 4519 || 4602 <= D && D <= 4607 || 9001 <= D && D <= 9002 || 11904 <= D && D <= 11929 || 11931 <= D && D <= 12019 || 12032 <= D && D <= 12245 || 12272 <= D && D <= 12283 || 12289 <= D && D <= 12350 || 12353 <= D && D <= 12438 || 12441 <= D && D <= 12543 || 12549 <= D && D <= 12589 || 12593 <= D && D <= 12686 || 12688 <= D && D <= 12730 || 12736 <= D && D <= 12771 || 12784 <= D && D <= 12830 || 12832 <= D && D <= 12871 || 12880 <= D && D <= 13054 || 13056 <= D && D <= 19903 || 19968 <= D && D <= 42124 || 42128 <= D && D <= 42182 || 43360 <= D && D <= 43388 || 44032 <= D && D <= 55203 || 55216 <= D && D <= 55238 || 55243 <= D && D <= 55291 || 63744 <= D && D <= 64255 || 65040 <= D && D <= 65049 || 65072 <= D && D <= 65106 || 65108 <= D && D <= 65126 || 65128 <= D && D <= 65131 || 110592 <= D && D <= 110593 || 127488 <= D && D <= 127490 || 127504 <= D && D <= 127546 || 127552 <= D && D <= 127560 || 127568 <= D && D <= 127569 || 131072 <= D && D <= 194367 || 177984 <= D && D <= 196605 || 196608 <= D && D <= 262141 ? "W" : 32 <= D && D <= 126 || 162 <= D && D <= 163 || 165 <= D && D <= 166 || D == 172 || D == 175 || 10214 <= D && D <= 10221 || 10629 <= D && D <= 10630 ? "Na" : D == 161 || D == 164 || 167 <= D && D <= 168 || D == 170 || 173 <= D && D <= 174 || 176 <= D && D <= 180 || 182 <= D && D <= 186 || 188 <= D && D <= 191 || D == 198 || D == 208 || 215 <= D && D <= 216 || 222 <= D && D <= 225 || D == 230 || 232 <= D && D <= 234 || 236 <= D && D <= 237 || D == 240 || 242 <= D && D <= 243 || 247 <= D && D <= 250 || D == 252 || D == 254 || D == 257 || D == 273 || D == 275 || D == 283 || 294 <= D && D <= 295 || D == 299 || 305 <= D && D <= 307 || D == 312 || 319 <= D && D <= 322 || D == 324 || 328 <= D && D <= 331 || D == 333 || 338 <= D && D <= 339 || 358 <= D && D <= 359 || D == 363 || D == 462 || D == 464 || D == 466 || D == 468 || D == 470 || D == 472 || D == 474 || D == 476 || D == 593 || D == 609 || D == 708 || D == 711 || 713 <= D && D <= 715 || D == 717 || D == 720 || 728 <= D && D <= 731 || D == 733 || D == 735 || 768 <= D && D <= 879 || 913 <= D && D <= 929 || 931 <= D && D <= 937 || 945 <= D && D <= 961 || 963 <= D && D <= 969 || D == 1025 || 1040 <= D && D <= 1103 || D == 1105 || D == 8208 || 8211 <= D && D <= 8214 || 8216 <= D && D <= 8217 || 8220 <= D && D <= 8221 || 8224 <= D && D <= 8226 || 8228 <= D && D <= 8231 || D == 8240 || 8242 <= D && D <= 8243 || D == 8245 || D == 8251 || D == 8254 || D == 8308 || D == 8319 || 8321 <= D && D <= 8324 || D == 8364 || D == 8451 || D == 8453 || D == 8457 || D == 8467 || D == 8470 || 8481 <= D && D <= 8482 || D == 8486 || D == 8491 || 8531 <= D && D <= 8532 || 8539 <= D && D <= 8542 || 8544 <= D && D <= 8555 || 8560 <= D && D <= 8569 || D == 8585 || 8592 <= D && D <= 8601 || 8632 <= D && D <= 8633 || D == 8658 || D == 8660 || D == 8679 || D == 8704 || 8706 <= D && D <= 8707 || 8711 <= D && D <= 8712 || D == 8715 || D == 8719 || D == 8721 || D == 8725 || D == 8730 || 8733 <= D && D <= 8736 || D == 8739 || D == 8741 || 8743 <= D && D <= 8748 || D == 8750 || 8756 <= D && D <= 8759 || 8764 <= D && D <= 8765 || D == 8776 || D == 8780 || D == 8786 || 8800 <= D && D <= 8801 || 8804 <= D && D <= 8807 || 8810 <= D && D <= 8811 || 8814 <= D && D <= 8815 || 8834 <= D && D <= 8835 || 8838 <= D && D <= 8839 || D == 8853 || D == 8857 || D == 8869 || D == 8895 || D == 8978 || 9312 <= D && D <= 9449 || 9451 <= D && D <= 9547 || 9552 <= D && D <= 9587 || 9600 <= D && D <= 9615 || 9618 <= D && D <= 9621 || 9632 <= D && D <= 9633 || 9635 <= D && D <= 9641 || 9650 <= D && D <= 9651 || 9654 <= D && D <= 9655 || 9660 <= D && D <= 9661 || 9664 <= D && D <= 9665 || 9670 <= D && D <= 9672 || D == 9675 || 9678 <= D && D <= 9681 || 9698 <= D && D <= 9701 || D == 9711 || 9733 <= D && D <= 9734 || D == 9737 || 9742 <= D && D <= 9743 || 9748 <= D && D <= 9749 || D == 9756 || D == 9758 || D == 9792 || D == 9794 || 9824 <= D && D <= 9825 || 9827 <= D && D <= 9829 || 9831 <= D && D <= 9834 || 9836 <= D && D <= 9837 || D == 9839 || 9886 <= D && D <= 9887 || 9918 <= D && D <= 9919 || 9924 <= D && D <= 9933 || 9935 <= D && D <= 9953 || D == 9955 || 9960 <= D && D <= 9983 || D == 10045 || D == 10071 || 10102 <= D && D <= 10111 || 11093 <= D && D <= 11097 || 12872 <= D && D <= 12879 || 57344 <= D && D <= 63743 || 65024 <= D && D <= 65039 || D == 65533 || 127232 <= D && D <= 127242 || 127248 <= D && D <= 127277 || 127280 <= D && D <= 127337 || 127344 <= D && D <= 127386 || 917760 <= D && D <= 917999 || 983040 <= D && D <= 1048573 || 1048576 <= D && D <= 1114109 ? "A" : "N";
|
|
1192
|
+
}, u.characterLength = function(t) {
|
|
1193
|
+
var s = this.eastAsianWidth(t);
|
|
1194
|
+
return s == "F" || s == "W" || s == "A" ? 2 : 1;
|
|
1195
|
+
};
|
|
1196
|
+
function F(t) {
|
|
1197
|
+
return t.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[^\uD800-\uDFFF]/g) || [];
|
|
1198
|
+
}
|
|
1199
|
+
u.length = function(t) {
|
|
1200
|
+
for (var s = F(t), C = 0, D = 0;D < s.length; D++)
|
|
1201
|
+
C = C + this.characterLength(s[D]);
|
|
1202
|
+
return C;
|
|
1203
|
+
}, u.slice = function(t, s, C) {
|
|
1204
|
+
textLen = u.length(t), s = s || 0, C = C || 1, s < 0 && (s = textLen + s), C < 0 && (C = textLen + C);
|
|
1205
|
+
for (var D = "", i = 0, n = F(t), E = 0;E < n.length; E++) {
|
|
1206
|
+
var h = n[E], o = u.length(h);
|
|
1207
|
+
if (i >= s - (o == 2 ? 1 : 0))
|
|
1208
|
+
if (i + o <= C)
|
|
1209
|
+
D += h;
|
|
1210
|
+
else
|
|
1211
|
+
break;
|
|
1212
|
+
i += o;
|
|
1213
|
+
}
|
|
1214
|
+
return D;
|
|
1215
|
+
};
|
|
1216
|
+
})(j);
|
|
1217
|
+
var Q = j.exports;
|
|
1218
|
+
var X = T(Q);
|
|
1219
|
+
var DD = function() {
|
|
1220
|
+
return /\uD83C\uDFF4\uDB40\uDC67\uDB40\uDC62(?:\uDB40\uDC77\uDB40\uDC6C\uDB40\uDC73|\uDB40\uDC73\uDB40\uDC63\uDB40\uDC74|\uDB40\uDC65\uDB40\uDC6E\uDB40\uDC67)\uDB40\uDC7F|(?:\uD83E\uDDD1\uD83C\uDFFF\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFF\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB-\uDFFE])|(?:\uD83E\uDDD1\uD83C\uDFFE\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFE\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB-\uDFFD\uDFFF])|(?:\uD83E\uDDD1\uD83C\uDFFD\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFD\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|(?:\uD83E\uDDD1\uD83C\uDFFC\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFC\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB\uDFFD-\uDFFF])|(?:\uD83E\uDDD1\uD83C\uDFFB\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFB\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFC-\uDFFF])|\uD83D\uDC68(?:\uD83C\uDFFB(?:\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFF])|\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFF]))|\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFC-\uDFFF])|[\u2695\u2696\u2708]\uFE0F|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD]))?|(?:\uD83C[\uDFFC-\uDFFF])\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFF])|\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFF]))|\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83D\uDC68|(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFE])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFE\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFD\uDFFF])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFC\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFD-\uDFFF])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|(?:\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])\uFE0F|\u200D(?:(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D[\uDC66\uDC67])|\uD83D[\uDC66\uDC67])|\uD83C\uDFFF|\uD83C\uDFFE|\uD83C\uDFFD|\uD83C\uDFFC)?|(?:\uD83D\uDC69(?:\uD83C\uDFFB\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69])|(?:\uD83C[\uDFFC-\uDFFF])\u200D\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69]))|\uD83E\uDDD1(?:\uD83C[\uDFFB-\uDFFF])\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1)(?:\uD83C[\uDFFB-\uDFFF])|\uD83D\uDC69\u200D\uD83D\uDC69\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|\uD83D\uDC69(?:\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFE\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFC\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFB\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD]))|\uD83E\uDDD1(?:\u200D(?:\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFE\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFC\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFB\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD]))|\uD83D\uDC69\u200D\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC69\u200D\uD83D\uDC69\u200D(?:\uD83D[\uDC66\uDC67])|\uD83D\uDC69\u200D\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|(?:\uD83D\uDC41\uFE0F\u200D\uD83D\uDDE8|\uD83E\uDDD1(?:\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708]|\uD83C\uDFFB\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])|\uD83D\uDC69(?:\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708]|\uD83C\uDFFB\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])|\uD83D\uDE36\u200D\uD83C\uDF2B|\uD83C\uDFF3\uFE0F\u200D\u26A7|\uD83D\uDC3B\u200D\u2744|(?:(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD])(?:\uD83C[\uDFFB-\uDFFF])|\uD83D\uDC6F|\uD83E[\uDD3C\uDDDE\uDDDF])\u200D[\u2640\u2642]|(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])\u200D[\u2640\u2642]|\uD83C\uDFF4\u200D\u2620|(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD])\u200D[\u2640\u2642]|[\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u2328\u23CF\u23ED-\u23EF\u23F1\u23F2\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB\u25FC\u2600-\u2604\u260E\u2611\u2618\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u2692\u2694-\u2697\u2699\u269B\u269C\u26A0\u26A7\u26B0\u26B1\u26C8\u26CF\u26D1\u26D3\u26E9\u26F0\u26F1\u26F4\u26F7\u26F8\u2702\u2708\u2709\u270F\u2712\u2714\u2716\u271D\u2721\u2733\u2734\u2744\u2747\u2763\u27A1\u2934\u2935\u2B05-\u2B07\u3030\u303D\u3297\u3299]|\uD83C[\uDD70\uDD71\uDD7E\uDD7F\uDE02\uDE37\uDF21\uDF24-\uDF2C\uDF36\uDF7D\uDF96\uDF97\uDF99-\uDF9B\uDF9E\uDF9F\uDFCD\uDFCE\uDFD4-\uDFDF\uDFF5\uDFF7]|\uD83D[\uDC3F\uDCFD\uDD49\uDD4A\uDD6F\uDD70\uDD73\uDD76-\uDD79\uDD87\uDD8A-\uDD8D\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA\uDECB\uDECD-\uDECF\uDEE0-\uDEE5\uDEE9\uDEF0\uDEF3])\uFE0F|\uD83C\uDFF3\uFE0F\u200D\uD83C\uDF08|\uD83D\uDC69\u200D\uD83D\uDC67|\uD83D\uDC69\u200D\uD83D\uDC66|\uD83D\uDE35\u200D\uD83D\uDCAB|\uD83D\uDE2E\u200D\uD83D\uDCA8|\uD83D\uDC15\u200D\uD83E\uDDBA|\uD83E\uDDD1(?:\uD83C\uDFFF|\uD83C\uDFFE|\uD83C\uDFFD|\uD83C\uDFFC|\uD83C\uDFFB)?|\uD83D\uDC69(?:\uD83C\uDFFF|\uD83C\uDFFE|\uD83C\uDFFD|\uD83C\uDFFC|\uD83C\uDFFB)?|\uD83C\uDDFD\uD83C\uDDF0|\uD83C\uDDF6\uD83C\uDDE6|\uD83C\uDDF4\uD83C\uDDF2|\uD83D\uDC08\u200D\u2B1B|\u2764\uFE0F\u200D(?:\uD83D\uDD25|\uD83E\uDE79)|\uD83D\uDC41\uFE0F|\uD83C\uDFF3\uFE0F|\uD83C\uDDFF(?:\uD83C[\uDDE6\uDDF2\uDDFC])|\uD83C\uDDFE(?:\uD83C[\uDDEA\uDDF9])|\uD83C\uDDFC(?:\uD83C[\uDDEB\uDDF8])|\uD83C\uDDFB(?:\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDEE\uDDF3\uDDFA])|\uD83C\uDDFA(?:\uD83C[\uDDE6\uDDEC\uDDF2\uDDF3\uDDF8\uDDFE\uDDFF])|\uD83C\uDDF9(?:\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDED\uDDEF-\uDDF4\uDDF7\uDDF9\uDDFB\uDDFC\uDDFF])|\uD83C\uDDF8(?:\uD83C[\uDDE6-\uDDEA\uDDEC-\uDDF4\uDDF7-\uDDF9\uDDFB\uDDFD-\uDDFF])|\uD83C\uDDF7(?:\uD83C[\uDDEA\uDDF4\uDDF8\uDDFA\uDDFC])|\uD83C\uDDF5(?:\uD83C[\uDDE6\uDDEA-\uDDED\uDDF0-\uDDF3\uDDF7-\uDDF9\uDDFC\uDDFE])|\uD83C\uDDF3(?:\uD83C[\uDDE6\uDDE8\uDDEA-\uDDEC\uDDEE\uDDF1\uDDF4\uDDF5\uDDF7\uDDFA\uDDFF])|\uD83C\uDDF2(?:\uD83C[\uDDE6\uDDE8-\uDDED\uDDF0-\uDDFF])|\uD83C\uDDF1(?:\uD83C[\uDDE6-\uDDE8\uDDEE\uDDF0\uDDF7-\uDDFB\uDDFE])|\uD83C\uDDF0(?:\uD83C[\uDDEA\uDDEC-\uDDEE\uDDF2\uDDF3\uDDF5\uDDF7\uDDFC\uDDFE\uDDFF])|\uD83C\uDDEF(?:\uD83C[\uDDEA\uDDF2\uDDF4\uDDF5])|\uD83C\uDDEE(?:\uD83C[\uDDE8-\uDDEA\uDDF1-\uDDF4\uDDF6-\uDDF9])|\uD83C\uDDED(?:\uD83C[\uDDF0\uDDF2\uDDF3\uDDF7\uDDF9\uDDFA])|\uD83C\uDDEC(?:\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEE\uDDF1-\uDDF3\uDDF5-\uDDFA\uDDFC\uDDFE])|\uD83C\uDDEB(?:\uD83C[\uDDEE-\uDDF0\uDDF2\uDDF4\uDDF7])|\uD83C\uDDEA(?:\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDED\uDDF7-\uDDFA])|\uD83C\uDDE9(?:\uD83C[\uDDEA\uDDEC\uDDEF\uDDF0\uDDF2\uDDF4\uDDFF])|\uD83C\uDDE8(?:\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDEE\uDDF0-\uDDF5\uDDF7\uDDFA-\uDDFF])|\uD83C\uDDE7(?:\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEF\uDDF1-\uDDF4\uDDF6-\uDDF9\uDDFB\uDDFC\uDDFE\uDDFF])|\uD83C\uDDE6(?:\uD83C[\uDDE8-\uDDEC\uDDEE\uDDF1\uDDF2\uDDF4\uDDF6-\uDDFA\uDDFC\uDDFD\uDDFF])|[#\*0-9]\uFE0F\u20E3|\u2764\uFE0F|(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD])(?:\uD83C[\uDFFB-\uDFFF])|(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])|\uD83C\uDFF4|(?:[\u270A\u270B]|\uD83C[\uDF85\uDFC2\uDFC7]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDC8F\uDC91\uDCAA\uDD7A\uDD95\uDD96\uDE4C\uDE4F\uDEC0\uDECC]|\uD83E[\uDD0C\uDD0F\uDD18-\uDD1C\uDD1E\uDD1F\uDD30-\uDD34\uDD36\uDD77\uDDB5\uDDB6\uDDBB\uDDD2\uDDD3\uDDD5])(?:\uD83C[\uDFFB-\uDFFF])|(?:[\u261D\u270C\u270D]|\uD83D[\uDD74\uDD90])(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])|[\u270A\u270B]|\uD83C[\uDF85\uDFC2\uDFC7]|\uD83D[\uDC08\uDC15\uDC3B\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDC8F\uDC91\uDCAA\uDD7A\uDD95\uDD96\uDE2E\uDE35\uDE36\uDE4C\uDE4F\uDEC0\uDECC]|\uD83E[\uDD0C\uDD0F\uDD18-\uDD1C\uDD1E\uDD1F\uDD30-\uDD34\uDD36\uDD77\uDDB5\uDDB6\uDDBB\uDDD2\uDDD3\uDDD5]|\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD]|\uD83D\uDC6F|\uD83E[\uDD3C\uDDDE\uDDDF]|[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF84\uDF86-\uDF93\uDFA0-\uDFC1\uDFC5\uDFC6\uDFC8\uDFC9\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC07\uDC09-\uDC14\uDC16-\uDC3A\uDC3C-\uDC3E\uDC40\uDC44\uDC45\uDC51-\uDC65\uDC6A\uDC79-\uDC7B\uDC7D-\uDC80\uDC84\uDC88-\uDC8E\uDC90\uDC92-\uDCA9\uDCAB-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDDA4\uDDFB-\uDE2D\uDE2F-\uDE34\uDE37-\uDE44\uDE48-\uDE4A\uDE80-\uDEA2\uDEA4-\uDEB3\uDEB7-\uDEBF\uDEC1-\uDEC5\uDED0-\uDED2\uDED5-\uDED7\uDEEB\uDEEC\uDEF4-\uDEFC\uDFE0-\uDFEB]|\uD83E[\uDD0D\uDD0E\uDD10-\uDD17\uDD1D\uDD20-\uDD25\uDD27-\uDD2F\uDD3A\uDD3F-\uDD45\uDD47-\uDD76\uDD78\uDD7A-\uDDB4\uDDB7\uDDBA\uDDBC-\uDDCB\uDDD0\uDDE0-\uDDFF\uDE70-\uDE74\uDE78-\uDE7A\uDE80-\uDE86\uDE90-\uDEA8\uDEB0-\uDEB6\uDEC0-\uDEC2\uDED0-\uDED6]|(?:[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u270A\u270B\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF93\uDFA0-\uDFCA\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF4\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC3E\uDC40\uDC42-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDD7A\uDD95\uDD96\uDDA4\uDDFB-\uDE4F\uDE80-\uDEC5\uDECC\uDED0-\uDED2\uDED5-\uDED7\uDEEB\uDEEC\uDEF4-\uDEFC\uDFE0-\uDFEB]|\uD83E[\uDD0C-\uDD3A\uDD3C-\uDD45\uDD47-\uDD78\uDD7A-\uDDCB\uDDCD-\uDDFF\uDE70-\uDE74\uDE78-\uDE7A\uDE80-\uDE86\uDE90-\uDEA8\uDEB0-\uDEB6\uDEC0-\uDEC2\uDED0-\uDED6])|(?:[#\*0-9\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692-\u2697\u2699\u269B\u269C\u26A0\u26A1\u26A7\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299]|\uD83C[\uDC04\uDCCF\uDD70\uDD71\uDD7E\uDD7F\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE02\uDE1A\uDE2F\uDE32-\uDE3A\uDE50\uDE51\uDF00-\uDF21\uDF24-\uDF93\uDF96\uDF97\uDF99-\uDF9B\uDF9E-\uDFF0\uDFF3-\uDFF5\uDFF7-\uDFFF]|\uD83D[\uDC00-\uDCFD\uDCFF-\uDD3D\uDD49-\uDD4E\uDD50-\uDD67\uDD6F\uDD70\uDD73-\uDD7A\uDD87\uDD8A-\uDD8D\uDD90\uDD95\uDD96\uDDA4\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA-\uDE4F\uDE80-\uDEC5\uDECB-\uDED2\uDED5-\uDED7\uDEE0-\uDEE5\uDEE9\uDEEB\uDEEC\uDEF0\uDEF3-\uDEFC\uDFE0-\uDFEB]|\uD83E[\uDD0C-\uDD3A\uDD3C-\uDD45\uDD47-\uDD78\uDD7A-\uDDCB\uDDCD-\uDDFF\uDE70-\uDE74\uDE78-\uDE7A\uDE80-\uDE86\uDE90-\uDEA8\uDEB0-\uDEB6\uDEC0-\uDEC2\uDED0-\uDED6])\uFE0F|(?:[\u261D\u26F9\u270A-\u270D]|\uD83C[\uDF85\uDFC2-\uDFC4\uDFC7\uDFCA-\uDFCC]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66-\uDC78\uDC7C\uDC81-\uDC83\uDC85-\uDC87\uDC8F\uDC91\uDCAA\uDD74\uDD75\uDD7A\uDD90\uDD95\uDD96\uDE45-\uDE47\uDE4B-\uDE4F\uDEA3\uDEB4-\uDEB6\uDEC0\uDECC]|\uD83E[\uDD0C\uDD0F\uDD18-\uDD1F\uDD26\uDD30-\uDD39\uDD3C-\uDD3E\uDD77\uDDB5\uDDB6\uDDB8\uDDB9\uDDBB\uDDCD-\uDDCF\uDDD1-\uDDDD])/g;
|
|
1221
|
+
};
|
|
1222
|
+
var uD = T(DD);
|
|
1223
|
+
function A(e, u = {}) {
|
|
1224
|
+
if (typeof e != "string" || e.length === 0 || (u = { ambiguousIsNarrow: true, ...u }, e = S(e), e.length === 0))
|
|
1225
|
+
return 0;
|
|
1226
|
+
e = e.replace(uD(), " ");
|
|
1227
|
+
const F = u.ambiguousIsNarrow ? 1 : 2;
|
|
1228
|
+
let t = 0;
|
|
1229
|
+
for (const s of e) {
|
|
1230
|
+
const C = s.codePointAt(0);
|
|
1231
|
+
if (C <= 31 || C >= 127 && C <= 159 || C >= 768 && C <= 879)
|
|
1232
|
+
continue;
|
|
1233
|
+
switch (X.eastAsianWidth(s)) {
|
|
1234
|
+
case "F":
|
|
1235
|
+
case "W":
|
|
1236
|
+
t += 2;
|
|
1237
|
+
break;
|
|
1238
|
+
case "A":
|
|
1239
|
+
t += F;
|
|
1240
|
+
break;
|
|
1241
|
+
default:
|
|
1242
|
+
t += 1;
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
return t;
|
|
1246
|
+
}
|
|
1247
|
+
var d = 10;
|
|
1248
|
+
var M = (e = 0) => (u) => `\x1B[${u + e}m`;
|
|
1249
|
+
var P = (e = 0) => (u) => `\x1B[${38 + e};5;${u}m`;
|
|
1250
|
+
var W = (e = 0) => (u, F, t) => `\x1B[${38 + e};2;${u};${F};${t}m`;
|
|
1251
|
+
var r = { modifier: { reset: [0, 0], bold: [1, 22], dim: [2, 22], italic: [3, 23], underline: [4, 24], overline: [53, 55], inverse: [7, 27], hidden: [8, 28], strikethrough: [9, 29] }, color: { black: [30, 39], red: [31, 39], green: [32, 39], yellow: [33, 39], blue: [34, 39], magenta: [35, 39], cyan: [36, 39], white: [37, 39], blackBright: [90, 39], gray: [90, 39], grey: [90, 39], redBright: [91, 39], greenBright: [92, 39], yellowBright: [93, 39], blueBright: [94, 39], magentaBright: [95, 39], cyanBright: [96, 39], whiteBright: [97, 39] }, bgColor: { bgBlack: [40, 49], bgRed: [41, 49], bgGreen: [42, 49], bgYellow: [43, 49], bgBlue: [44, 49], bgMagenta: [45, 49], bgCyan: [46, 49], bgWhite: [47, 49], bgBlackBright: [100, 49], bgGray: [100, 49], bgGrey: [100, 49], bgRedBright: [101, 49], bgGreenBright: [102, 49], bgYellowBright: [103, 49], bgBlueBright: [104, 49], bgMagentaBright: [105, 49], bgCyanBright: [106, 49], bgWhiteBright: [107, 49] } };
|
|
1252
|
+
Object.keys(r.modifier);
|
|
1253
|
+
var FD = Object.keys(r.color);
|
|
1254
|
+
var eD = Object.keys(r.bgColor);
|
|
1255
|
+
[...FD, ...eD];
|
|
1256
|
+
function tD() {
|
|
1257
|
+
const e = new Map;
|
|
1258
|
+
for (const [u, F] of Object.entries(r)) {
|
|
1259
|
+
for (const [t, s] of Object.entries(F))
|
|
1260
|
+
r[t] = { open: `\x1B[${s[0]}m`, close: `\x1B[${s[1]}m` }, F[t] = r[t], e.set(s[0], s[1]);
|
|
1261
|
+
Object.defineProperty(r, u, { value: F, enumerable: false });
|
|
1262
|
+
}
|
|
1263
|
+
return Object.defineProperty(r, "codes", { value: e, enumerable: false }), r.color.close = "\x1B[39m", r.bgColor.close = "\x1B[49m", r.color.ansi = M(), r.color.ansi256 = P(), r.color.ansi16m = W(), r.bgColor.ansi = M(d), r.bgColor.ansi256 = P(d), r.bgColor.ansi16m = W(d), Object.defineProperties(r, { rgbToAnsi256: { value: (u, F, t) => u === F && F === t ? u < 8 ? 16 : u > 248 ? 231 : Math.round((u - 8) / 247 * 24) + 232 : 16 + 36 * Math.round(u / 255 * 5) + 6 * Math.round(F / 255 * 5) + Math.round(t / 255 * 5), enumerable: false }, hexToRgb: { value: (u) => {
|
|
1264
|
+
const F = /[a-f\d]{6}|[a-f\d]{3}/i.exec(u.toString(16));
|
|
1265
|
+
if (!F)
|
|
1266
|
+
return [0, 0, 0];
|
|
1267
|
+
let [t] = F;
|
|
1268
|
+
t.length === 3 && (t = [...t].map((C) => C + C).join(""));
|
|
1269
|
+
const s = Number.parseInt(t, 16);
|
|
1270
|
+
return [s >> 16 & 255, s >> 8 & 255, s & 255];
|
|
1271
|
+
}, enumerable: false }, hexToAnsi256: { value: (u) => r.rgbToAnsi256(...r.hexToRgb(u)), enumerable: false }, ansi256ToAnsi: { value: (u) => {
|
|
1272
|
+
if (u < 8)
|
|
1273
|
+
return 30 + u;
|
|
1274
|
+
if (u < 16)
|
|
1275
|
+
return 90 + (u - 8);
|
|
1276
|
+
let F, t, s;
|
|
1277
|
+
if (u >= 232)
|
|
1278
|
+
F = ((u - 232) * 10 + 8) / 255, t = F, s = F;
|
|
1279
|
+
else {
|
|
1280
|
+
u -= 16;
|
|
1281
|
+
const i = u % 36;
|
|
1282
|
+
F = Math.floor(u / 36) / 5, t = Math.floor(i / 6) / 5, s = i % 6 / 5;
|
|
1283
|
+
}
|
|
1284
|
+
const C = Math.max(F, t, s) * 2;
|
|
1285
|
+
if (C === 0)
|
|
1286
|
+
return 30;
|
|
1287
|
+
let D = 30 + (Math.round(s) << 2 | Math.round(t) << 1 | Math.round(F));
|
|
1288
|
+
return C === 2 && (D += 60), D;
|
|
1289
|
+
}, enumerable: false }, rgbToAnsi: { value: (u, F, t) => r.ansi256ToAnsi(r.rgbToAnsi256(u, F, t)), enumerable: false }, hexToAnsi: { value: (u) => r.ansi256ToAnsi(r.hexToAnsi256(u)), enumerable: false } }), r;
|
|
1290
|
+
}
|
|
1291
|
+
var sD = tD();
|
|
1292
|
+
var g = new Set(["\x1B", ""]);
|
|
1293
|
+
var CD = 39;
|
|
1294
|
+
var b = "\x07";
|
|
1295
|
+
var O = "[";
|
|
1296
|
+
var iD = "]";
|
|
1297
|
+
var I = "m";
|
|
1298
|
+
var w = `${iD}8;;`;
|
|
1299
|
+
var N = (e) => `${g.values().next().value}${O}${e}${I}`;
|
|
1300
|
+
var L = (e) => `${g.values().next().value}${w}${e}${b}`;
|
|
1301
|
+
var rD = (e) => e.split(" ").map((u) => A(u));
|
|
1302
|
+
var y = (e, u, F) => {
|
|
1303
|
+
const t = [...u];
|
|
1304
|
+
let s = false, C = false, D = A(S(e[e.length - 1]));
|
|
1305
|
+
for (const [i, n] of t.entries()) {
|
|
1306
|
+
const E = A(n);
|
|
1307
|
+
if (D + E <= F ? e[e.length - 1] += n : (e.push(n), D = 0), g.has(n) && (s = true, C = t.slice(i + 1).join("").startsWith(w)), s) {
|
|
1308
|
+
C ? n === b && (s = false, C = false) : n === I && (s = false);
|
|
1309
|
+
continue;
|
|
1310
|
+
}
|
|
1311
|
+
D += E, D === F && i < t.length - 1 && (e.push(""), D = 0);
|
|
1312
|
+
}
|
|
1313
|
+
!D && e[e.length - 1].length > 0 && e.length > 1 && (e[e.length - 2] += e.pop());
|
|
1314
|
+
};
|
|
1315
|
+
var ED = (e) => {
|
|
1316
|
+
const u = e.split(" ");
|
|
1317
|
+
let F = u.length;
|
|
1318
|
+
for (;F > 0 && !(A(u[F - 1]) > 0); )
|
|
1319
|
+
F--;
|
|
1320
|
+
return F === u.length ? e : u.slice(0, F).join(" ") + u.slice(F).join("");
|
|
1321
|
+
};
|
|
1322
|
+
var oD = (e, u, F = {}) => {
|
|
1323
|
+
if (F.trim !== false && e.trim() === "")
|
|
1324
|
+
return "";
|
|
1325
|
+
let t = "", s, C;
|
|
1326
|
+
const D = rD(e);
|
|
1327
|
+
let i = [""];
|
|
1328
|
+
for (const [E, h] of e.split(" ").entries()) {
|
|
1329
|
+
F.trim !== false && (i[i.length - 1] = i[i.length - 1].trimStart());
|
|
1330
|
+
let o = A(i[i.length - 1]);
|
|
1331
|
+
if (E !== 0 && (o >= u && (F.wordWrap === false || F.trim === false) && (i.push(""), o = 0), (o > 0 || F.trim === false) && (i[i.length - 1] += " ", o++)), F.hard && D[E] > u) {
|
|
1332
|
+
const B = u - o, p = 1 + Math.floor((D[E] - B - 1) / u);
|
|
1333
|
+
Math.floor((D[E] - 1) / u) < p && i.push(""), y(i, h, u);
|
|
1334
|
+
continue;
|
|
1335
|
+
}
|
|
1336
|
+
if (o + D[E] > u && o > 0 && D[E] > 0) {
|
|
1337
|
+
if (F.wordWrap === false && o < u) {
|
|
1338
|
+
y(i, h, u);
|
|
1339
|
+
continue;
|
|
1340
|
+
}
|
|
1341
|
+
i.push("");
|
|
1342
|
+
}
|
|
1343
|
+
if (o + D[E] > u && F.wordWrap === false) {
|
|
1344
|
+
y(i, h, u);
|
|
1345
|
+
continue;
|
|
1346
|
+
}
|
|
1347
|
+
i[i.length - 1] += h;
|
|
1348
|
+
}
|
|
1349
|
+
F.trim !== false && (i = i.map((E) => ED(E)));
|
|
1350
|
+
const n = [...i.join(`
|
|
1351
|
+
`)];
|
|
1352
|
+
for (const [E, h] of n.entries()) {
|
|
1353
|
+
if (t += h, g.has(h)) {
|
|
1354
|
+
const { groups: B } = new RegExp(`(?:\\${O}(?<code>\\d+)m|\\${w}(?<uri>.*)${b})`).exec(n.slice(E).join("")) || { groups: {} };
|
|
1355
|
+
if (B.code !== undefined) {
|
|
1356
|
+
const p = Number.parseFloat(B.code);
|
|
1357
|
+
s = p === CD ? undefined : p;
|
|
1358
|
+
} else
|
|
1359
|
+
B.uri !== undefined && (C = B.uri.length === 0 ? undefined : B.uri);
|
|
1360
|
+
}
|
|
1361
|
+
const o = sD.codes.get(Number(s));
|
|
1362
|
+
n[E + 1] === `
|
|
1363
|
+
` ? (C && (t += L("")), s && o && (t += N(o))) : h === `
|
|
1364
|
+
` && (s && o && (t += N(s)), C && (t += L(C)));
|
|
1365
|
+
}
|
|
1366
|
+
return t;
|
|
1367
|
+
};
|
|
1368
|
+
function R(e, u, F) {
|
|
1369
|
+
return String(e).normalize().replace(/\r\n/g, `
|
|
1370
|
+
`).split(`
|
|
1371
|
+
`).map((t) => oD(t, u, F)).join(`
|
|
1372
|
+
`);
|
|
1373
|
+
}
|
|
1374
|
+
var nD = Object.defineProperty;
|
|
1375
|
+
var aD = (e, u, F) => (u in e) ? nD(e, u, { enumerable: true, configurable: true, writable: true, value: F }) : e[u] = F;
|
|
1376
|
+
var a = (e, u, F) => (aD(e, typeof u != "symbol" ? u + "" : u, F), F);
|
|
1377
|
+
function hD(e, u) {
|
|
1378
|
+
if (e === u)
|
|
1379
|
+
return;
|
|
1380
|
+
const F = e.split(`
|
|
1381
|
+
`), t = u.split(`
|
|
1382
|
+
`), s = [];
|
|
1383
|
+
for (let C = 0;C < Math.max(F.length, t.length); C++)
|
|
1384
|
+
F[C] !== t[C] && s.push(C);
|
|
1385
|
+
return s;
|
|
1386
|
+
}
|
|
1387
|
+
var V = Symbol("clack:cancel");
|
|
1388
|
+
function lD(e) {
|
|
1389
|
+
return e === V;
|
|
1390
|
+
}
|
|
1391
|
+
function v(e, u) {
|
|
1392
|
+
e.isTTY && e.setRawMode(u);
|
|
1393
|
+
}
|
|
1394
|
+
var z = new Map([["k", "up"], ["j", "down"], ["h", "left"], ["l", "right"]]);
|
|
1395
|
+
var xD = new Set(["up", "down", "left", "right", "space", "enter"]);
|
|
1396
|
+
|
|
1397
|
+
class x {
|
|
1398
|
+
constructor({ render: u, input: F = $, output: t = k, ...s }, C = true) {
|
|
1399
|
+
a(this, "input"), a(this, "output"), a(this, "rl"), a(this, "opts"), a(this, "_track", false), a(this, "_render"), a(this, "_cursor", 0), a(this, "state", "initial"), a(this, "value"), a(this, "error", ""), a(this, "subscribers", new Map), a(this, "_prevFrame", ""), this.opts = s, this.onKeypress = this.onKeypress.bind(this), this.close = this.close.bind(this), this.render = this.render.bind(this), this._render = u.bind(this), this._track = C, this.input = F, this.output = t;
|
|
1400
|
+
}
|
|
1401
|
+
prompt() {
|
|
1402
|
+
const u = new U(0);
|
|
1403
|
+
return u._write = (F, t, s) => {
|
|
1404
|
+
this._track && (this.value = this.rl.line.replace(/\t/g, ""), this._cursor = this.rl.cursor, this.emit("value", this.value)), s();
|
|
1405
|
+
}, this.input.pipe(u), this.rl = _.createInterface({ input: this.input, output: u, tabSize: 2, prompt: "", escapeCodeTimeout: 50 }), _.emitKeypressEvents(this.input, this.rl), this.rl.prompt(), this.opts.initialValue !== undefined && this._track && this.rl.write(this.opts.initialValue), this.input.on("keypress", this.onKeypress), v(this.input, true), this.output.on("resize", this.render), this.render(), new Promise((F, t) => {
|
|
1406
|
+
this.once("submit", () => {
|
|
1407
|
+
this.output.write(import_sisteransi.cursor.show), this.output.off("resize", this.render), v(this.input, false), F(this.value);
|
|
1408
|
+
}), this.once("cancel", () => {
|
|
1409
|
+
this.output.write(import_sisteransi.cursor.show), this.output.off("resize", this.render), v(this.input, false), F(V);
|
|
1410
|
+
});
|
|
1411
|
+
});
|
|
1412
|
+
}
|
|
1413
|
+
on(u, F) {
|
|
1414
|
+
const t = this.subscribers.get(u) ?? [];
|
|
1415
|
+
t.push({ cb: F }), this.subscribers.set(u, t);
|
|
1416
|
+
}
|
|
1417
|
+
once(u, F) {
|
|
1418
|
+
const t = this.subscribers.get(u) ?? [];
|
|
1419
|
+
t.push({ cb: F, once: true }), this.subscribers.set(u, t);
|
|
1420
|
+
}
|
|
1421
|
+
emit(u, ...F) {
|
|
1422
|
+
const t = this.subscribers.get(u) ?? [], s = [];
|
|
1423
|
+
for (const C of t)
|
|
1424
|
+
C.cb(...F), C.once && s.push(() => t.splice(t.indexOf(C), 1));
|
|
1425
|
+
for (const C of s)
|
|
1426
|
+
C();
|
|
1427
|
+
}
|
|
1428
|
+
unsubscribe() {
|
|
1429
|
+
this.subscribers.clear();
|
|
1430
|
+
}
|
|
1431
|
+
onKeypress(u, F) {
|
|
1432
|
+
if (this.state === "error" && (this.state = "active"), F?.name && !this._track && z.has(F.name) && this.emit("cursor", z.get(F.name)), F?.name && xD.has(F.name) && this.emit("cursor", F.name), u && (u.toLowerCase() === "y" || u.toLowerCase() === "n") && this.emit("confirm", u.toLowerCase() === "y"), u === "\t" && this.opts.placeholder && (this.value || (this.rl.write(this.opts.placeholder), this.emit("value", this.opts.placeholder))), u && this.emit("key", u.toLowerCase()), F?.name === "return") {
|
|
1433
|
+
if (this.opts.validate) {
|
|
1434
|
+
const t = this.opts.validate(this.value);
|
|
1435
|
+
t && (this.error = t, this.state = "error", this.rl.write(this.value));
|
|
1436
|
+
}
|
|
1437
|
+
this.state !== "error" && (this.state = "submit");
|
|
1438
|
+
}
|
|
1439
|
+
u === "\x03" && (this.state = "cancel"), (this.state === "submit" || this.state === "cancel") && this.emit("finalize"), this.render(), (this.state === "submit" || this.state === "cancel") && this.close();
|
|
1440
|
+
}
|
|
1441
|
+
close() {
|
|
1442
|
+
this.input.unpipe(), this.input.removeListener("keypress", this.onKeypress), this.output.write(`
|
|
1443
|
+
`), v(this.input, false), this.rl.close(), this.emit(`${this.state}`, this.value), this.unsubscribe();
|
|
1444
|
+
}
|
|
1445
|
+
restoreCursor() {
|
|
1446
|
+
const u = R(this._prevFrame, process.stdout.columns, { hard: true }).split(`
|
|
1447
|
+
`).length - 1;
|
|
1448
|
+
this.output.write(import_sisteransi.cursor.move(-999, u * -1));
|
|
1449
|
+
}
|
|
1450
|
+
render() {
|
|
1451
|
+
const u = R(this._render(this) ?? "", process.stdout.columns, { hard: true });
|
|
1452
|
+
if (u !== this._prevFrame) {
|
|
1453
|
+
if (this.state === "initial")
|
|
1454
|
+
this.output.write(import_sisteransi.cursor.hide);
|
|
1455
|
+
else {
|
|
1456
|
+
const F = hD(this._prevFrame, u);
|
|
1457
|
+
if (this.restoreCursor(), F && F?.length === 1) {
|
|
1458
|
+
const t = F[0];
|
|
1459
|
+
this.output.write(import_sisteransi.cursor.move(0, t)), this.output.write(import_sisteransi.erase.lines(1));
|
|
1460
|
+
const s = u.split(`
|
|
1461
|
+
`);
|
|
1462
|
+
this.output.write(s[t]), this._prevFrame = u, this.output.write(import_sisteransi.cursor.move(0, s.length - t - 1));
|
|
1463
|
+
return;
|
|
1464
|
+
} else if (F && F?.length > 1) {
|
|
1465
|
+
const t = F[0];
|
|
1466
|
+
this.output.write(import_sisteransi.cursor.move(0, t)), this.output.write(import_sisteransi.erase.down());
|
|
1467
|
+
const s = u.split(`
|
|
1468
|
+
`).slice(t);
|
|
1469
|
+
this.output.write(s.join(`
|
|
1470
|
+
`)), this._prevFrame = u;
|
|
1471
|
+
return;
|
|
1472
|
+
}
|
|
1473
|
+
this.output.write(import_sisteransi.erase.down());
|
|
1474
|
+
}
|
|
1475
|
+
this.output.write(u), this.state === "initial" && (this.state = "active"), this._prevFrame = u;
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
class BD extends x {
|
|
1481
|
+
get cursor() {
|
|
1482
|
+
return this.value ? 0 : 1;
|
|
1483
|
+
}
|
|
1484
|
+
get _value() {
|
|
1485
|
+
return this.cursor === 0;
|
|
1486
|
+
}
|
|
1487
|
+
constructor(u) {
|
|
1488
|
+
super(u, false), this.value = !!u.initialValue, this.on("value", () => {
|
|
1489
|
+
this.value = this._value;
|
|
1490
|
+
}), this.on("confirm", (F) => {
|
|
1491
|
+
this.output.write(import_sisteransi.cursor.move(0, -1)), this.value = F, this.state = "submit", this.close();
|
|
1492
|
+
}), this.on("cursor", () => {
|
|
1493
|
+
this.value = !this.value;
|
|
1494
|
+
});
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
var fD = Object.defineProperty;
|
|
1498
|
+
var gD = (e, u, F) => (u in e) ? fD(e, u, { enumerable: true, configurable: true, writable: true, value: F }) : e[u] = F;
|
|
1499
|
+
var K = (e, u, F) => (gD(e, typeof u != "symbol" ? u + "" : u, F), F);
|
|
1500
|
+
var vD = class extends x {
|
|
1501
|
+
constructor(u) {
|
|
1502
|
+
super(u, false), K(this, "options"), K(this, "cursor", 0), this.options = u.options, this.value = [...u.initialValues ?? []], this.cursor = Math.max(this.options.findIndex(({ value: F }) => F === u.cursorAt), 0), this.on("key", (F) => {
|
|
1503
|
+
F === "a" && this.toggleAll();
|
|
1504
|
+
}), this.on("cursor", (F) => {
|
|
1505
|
+
switch (F) {
|
|
1506
|
+
case "left":
|
|
1507
|
+
case "up":
|
|
1508
|
+
this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1;
|
|
1509
|
+
break;
|
|
1510
|
+
case "down":
|
|
1511
|
+
case "right":
|
|
1512
|
+
this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1;
|
|
1513
|
+
break;
|
|
1514
|
+
case "space":
|
|
1515
|
+
this.toggleValue();
|
|
1516
|
+
break;
|
|
1517
|
+
}
|
|
1518
|
+
});
|
|
1519
|
+
}
|
|
1520
|
+
get _value() {
|
|
1521
|
+
return this.options[this.cursor].value;
|
|
1522
|
+
}
|
|
1523
|
+
toggleAll() {
|
|
1524
|
+
const u = this.value.length === this.options.length;
|
|
1525
|
+
this.value = u ? [] : this.options.map((F) => F.value);
|
|
1526
|
+
}
|
|
1527
|
+
toggleValue() {
|
|
1528
|
+
const u = this.value.includes(this._value);
|
|
1529
|
+
this.value = u ? this.value.filter((F) => F !== this._value) : [...this.value, this._value];
|
|
1530
|
+
}
|
|
1531
|
+
};
|
|
1532
|
+
var wD = Object.defineProperty;
|
|
1533
|
+
var yD = (e, u, F) => (u in e) ? wD(e, u, { enumerable: true, configurable: true, writable: true, value: F }) : e[u] = F;
|
|
1534
|
+
var Z = (e, u, F) => (yD(e, typeof u != "symbol" ? u + "" : u, F), F);
|
|
1535
|
+
var $D = class extends x {
|
|
1536
|
+
constructor(u) {
|
|
1537
|
+
super(u, false), Z(this, "options"), Z(this, "cursor", 0), this.options = u.options, this.cursor = this.options.findIndex(({ value: F }) => F === u.initialValue), this.cursor === -1 && (this.cursor = 0), this.changeValue(), this.on("cursor", (F) => {
|
|
1538
|
+
switch (F) {
|
|
1539
|
+
case "left":
|
|
1540
|
+
case "up":
|
|
1541
|
+
this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1;
|
|
1542
|
+
break;
|
|
1543
|
+
case "down":
|
|
1544
|
+
case "right":
|
|
1545
|
+
this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1;
|
|
1546
|
+
break;
|
|
1547
|
+
}
|
|
1548
|
+
this.changeValue();
|
|
1549
|
+
});
|
|
1550
|
+
}
|
|
1551
|
+
get _value() {
|
|
1552
|
+
return this.options[this.cursor];
|
|
1553
|
+
}
|
|
1554
|
+
changeValue() {
|
|
1555
|
+
this.value = this._value.value;
|
|
1556
|
+
}
|
|
1557
|
+
};
|
|
1558
|
+
var WD = globalThis.process.platform.startsWith("win");
|
|
1559
|
+
function OD({ input: e = $, output: u = k, overwrite: F = true, hideCursor: t = true } = {}) {
|
|
1560
|
+
const s = f.createInterface({ input: e, output: u, prompt: "", tabSize: 1 });
|
|
1561
|
+
f.emitKeypressEvents(e, s), e.isTTY && e.setRawMode(true);
|
|
1562
|
+
const C = (D, { name: i }) => {
|
|
1563
|
+
if (String(D) === "\x03") {
|
|
1564
|
+
t && u.write(import_sisteransi.cursor.show), process.exit(0);
|
|
1565
|
+
return;
|
|
1566
|
+
}
|
|
1567
|
+
if (!F)
|
|
1568
|
+
return;
|
|
1569
|
+
let n = i === "return" ? 0 : -1, E = i === "return" ? -1 : 0;
|
|
1570
|
+
f.moveCursor(u, n, E, () => {
|
|
1571
|
+
f.clearLine(u, 1, () => {
|
|
1572
|
+
e.once("keypress", C);
|
|
1573
|
+
});
|
|
1574
|
+
});
|
|
1575
|
+
};
|
|
1576
|
+
return t && u.write(import_sisteransi.cursor.hide), e.once("keypress", C), () => {
|
|
1577
|
+
e.off("keypress", C), t && u.write(import_sisteransi.cursor.show), e.isTTY && !WD && e.setRawMode(false), s.terminal = false, s.close();
|
|
1578
|
+
};
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
// node_modules/@clack/prompts/dist/index.mjs
|
|
1582
|
+
var import_picocolors = __toESM(require_picocolors(), 1);
|
|
1583
|
+
var import_sisteransi2 = __toESM(require_src(), 1);
|
|
1584
|
+
import h from "node:process";
|
|
1585
|
+
function K2() {
|
|
1586
|
+
return h.platform !== "win32" ? h.env.TERM !== "linux" : !!h.env.CI || !!h.env.WT_SESSION || !!h.env.TERMINUS_SUBLIME || h.env.ConEmuTask === "{cmd::Cmder}" || h.env.TERM_PROGRAM === "Terminus-Sublime" || h.env.TERM_PROGRAM === "vscode" || h.env.TERM === "xterm-256color" || h.env.TERM === "alacritty" || h.env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
|
|
1587
|
+
}
|
|
1588
|
+
var C = K2();
|
|
1589
|
+
var u = (s, n) => C ? s : n;
|
|
1590
|
+
var Y = u("◆", "*");
|
|
1591
|
+
var P2 = u("■", "x");
|
|
1592
|
+
var V2 = u("▲", "x");
|
|
1593
|
+
var M2 = u("◇", "o");
|
|
1594
|
+
var Q2 = u("┌", "T");
|
|
1595
|
+
var a2 = u("│", "|");
|
|
1596
|
+
var $2 = u("└", "—");
|
|
1597
|
+
var I2 = u("●", ">");
|
|
1598
|
+
var T2 = u("○", " ");
|
|
1599
|
+
var j2 = u("◻", "[•]");
|
|
1600
|
+
var b2 = u("◼", "[+]");
|
|
1601
|
+
var B = u("◻", "[ ]");
|
|
1602
|
+
var X2 = u("▪", "•");
|
|
1603
|
+
var G = u("─", "-");
|
|
1604
|
+
var H = u("╮", "+");
|
|
1605
|
+
var ee = u("├", "+");
|
|
1606
|
+
var te = u("╯", "+");
|
|
1607
|
+
var se = u("●", "•");
|
|
1608
|
+
var re = u("◆", "*");
|
|
1609
|
+
var ie = u("▲", "!");
|
|
1610
|
+
var ne = u("■", "x");
|
|
1611
|
+
var y2 = (s) => {
|
|
1612
|
+
switch (s) {
|
|
1613
|
+
case "initial":
|
|
1614
|
+
case "active":
|
|
1615
|
+
return import_picocolors.default.cyan(Y);
|
|
1616
|
+
case "cancel":
|
|
1617
|
+
return import_picocolors.default.red(P2);
|
|
1618
|
+
case "error":
|
|
1619
|
+
return import_picocolors.default.yellow(V2);
|
|
1620
|
+
case "submit":
|
|
1621
|
+
return import_picocolors.default.green(M2);
|
|
1622
|
+
}
|
|
1623
|
+
};
|
|
1624
|
+
var E = (s) => {
|
|
1625
|
+
const { cursor: n, options: t, style: i } = s, r2 = s.maxItems ?? 1 / 0, o = Math.max(process.stdout.rows - 4, 0), c = Math.min(o, Math.max(r2, 5));
|
|
1626
|
+
let l2 = 0;
|
|
1627
|
+
n >= l2 + c - 3 ? l2 = Math.max(Math.min(n - c + 3, t.length - c), 0) : n < l2 + 2 && (l2 = Math.max(n - 2, 0));
|
|
1628
|
+
const d2 = c < t.length && l2 > 0, p = c < t.length && l2 + c < t.length;
|
|
1629
|
+
return t.slice(l2, l2 + c).map((S2, f2, x2) => {
|
|
1630
|
+
const g2 = f2 === 0 && d2, m2 = f2 === x2.length - 1 && p;
|
|
1631
|
+
return g2 || m2 ? import_picocolors.default.dim("...") : i(S2, f2 + l2 === n);
|
|
1632
|
+
});
|
|
1633
|
+
};
|
|
1634
|
+
var ce = (s) => {
|
|
1635
|
+
const n = s.active ?? "Yes", t = s.inactive ?? "No";
|
|
1636
|
+
return new BD({ active: n, inactive: t, initialValue: s.initialValue ?? true, render() {
|
|
1637
|
+
const i = `${import_picocolors.default.gray(a2)}
|
|
1638
|
+
${y2(this.state)} ${s.message}
|
|
1639
|
+
`, r2 = this.value ? n : t;
|
|
1640
|
+
switch (this.state) {
|
|
1641
|
+
case "submit":
|
|
1642
|
+
return `${i}${import_picocolors.default.gray(a2)} ${import_picocolors.default.dim(r2)}`;
|
|
1643
|
+
case "cancel":
|
|
1644
|
+
return `${i}${import_picocolors.default.gray(a2)} ${import_picocolors.default.strikethrough(import_picocolors.default.dim(r2))}
|
|
1645
|
+
${import_picocolors.default.gray(a2)}`;
|
|
1646
|
+
default:
|
|
1647
|
+
return `${i}${import_picocolors.default.cyan(a2)} ${this.value ? `${import_picocolors.default.green(I2)} ${n}` : `${import_picocolors.default.dim(T2)} ${import_picocolors.default.dim(n)}`} ${import_picocolors.default.dim("/")} ${this.value ? `${import_picocolors.default.dim(T2)} ${import_picocolors.default.dim(t)}` : `${import_picocolors.default.green(I2)} ${t}`}
|
|
1648
|
+
${import_picocolors.default.cyan($2)}
|
|
1649
|
+
`;
|
|
1650
|
+
}
|
|
1651
|
+
} }).prompt();
|
|
1652
|
+
};
|
|
1653
|
+
var le = (s) => {
|
|
1654
|
+
const n = (t, i) => {
|
|
1655
|
+
const r2 = t.label ?? String(t.value);
|
|
1656
|
+
switch (i) {
|
|
1657
|
+
case "selected":
|
|
1658
|
+
return `${import_picocolors.default.dim(r2)}`;
|
|
1659
|
+
case "active":
|
|
1660
|
+
return `${import_picocolors.default.green(I2)} ${r2} ${t.hint ? import_picocolors.default.dim(`(${t.hint})`) : ""}`;
|
|
1661
|
+
case "cancelled":
|
|
1662
|
+
return `${import_picocolors.default.strikethrough(import_picocolors.default.dim(r2))}`;
|
|
1663
|
+
default:
|
|
1664
|
+
return `${import_picocolors.default.dim(T2)} ${import_picocolors.default.dim(r2)}`;
|
|
1665
|
+
}
|
|
1666
|
+
};
|
|
1667
|
+
return new $D({ options: s.options, initialValue: s.initialValue, render() {
|
|
1668
|
+
const t = `${import_picocolors.default.gray(a2)}
|
|
1669
|
+
${y2(this.state)} ${s.message}
|
|
1670
|
+
`;
|
|
1671
|
+
switch (this.state) {
|
|
1672
|
+
case "submit":
|
|
1673
|
+
return `${t}${import_picocolors.default.gray(a2)} ${n(this.options[this.cursor], "selected")}`;
|
|
1674
|
+
case "cancel":
|
|
1675
|
+
return `${t}${import_picocolors.default.gray(a2)} ${n(this.options[this.cursor], "cancelled")}
|
|
1676
|
+
${import_picocolors.default.gray(a2)}`;
|
|
1677
|
+
default:
|
|
1678
|
+
return `${t}${import_picocolors.default.cyan(a2)} ${E({ cursor: this.cursor, options: this.options, maxItems: s.maxItems, style: (i, r2) => n(i, r2 ? "active" : "inactive") }).join(`
|
|
1679
|
+
${import_picocolors.default.cyan(a2)} `)}
|
|
1680
|
+
${import_picocolors.default.cyan($2)}
|
|
1681
|
+
`;
|
|
1682
|
+
}
|
|
1683
|
+
} }).prompt();
|
|
1684
|
+
};
|
|
1685
|
+
var $e = (s) => {
|
|
1686
|
+
const n = (t, i) => {
|
|
1687
|
+
const r2 = t.label ?? String(t.value);
|
|
1688
|
+
return i === "active" ? `${import_picocolors.default.cyan(j2)} ${r2} ${t.hint ? import_picocolors.default.dim(`(${t.hint})`) : ""}` : i === "selected" ? `${import_picocolors.default.green(b2)} ${import_picocolors.default.dim(r2)}` : i === "cancelled" ? `${import_picocolors.default.strikethrough(import_picocolors.default.dim(r2))}` : i === "active-selected" ? `${import_picocolors.default.green(b2)} ${r2} ${t.hint ? import_picocolors.default.dim(`(${t.hint})`) : ""}` : i === "submitted" ? `${import_picocolors.default.dim(r2)}` : `${import_picocolors.default.dim(B)} ${import_picocolors.default.dim(r2)}`;
|
|
1689
|
+
};
|
|
1690
|
+
return new vD({ options: s.options, initialValues: s.initialValues, required: s.required ?? true, cursorAt: s.cursorAt, validate(t) {
|
|
1691
|
+
if (this.required && t.length === 0)
|
|
1692
|
+
return `Please select at least one option.
|
|
1693
|
+
${import_picocolors.default.reset(import_picocolors.default.dim(`Press ${import_picocolors.default.gray(import_picocolors.default.bgWhite(import_picocolors.default.inverse(" space ")))} to select, ${import_picocolors.default.gray(import_picocolors.default.bgWhite(import_picocolors.default.inverse(" enter ")))} to submit`))}`;
|
|
1694
|
+
}, render() {
|
|
1695
|
+
let t = `${import_picocolors.default.gray(a2)}
|
|
1696
|
+
${y2(this.state)} ${s.message}
|
|
1697
|
+
`;
|
|
1698
|
+
const i = (r2, o) => {
|
|
1699
|
+
const c = this.value.includes(r2.value);
|
|
1700
|
+
return o && c ? n(r2, "active-selected") : c ? n(r2, "selected") : n(r2, o ? "active" : "inactive");
|
|
1701
|
+
};
|
|
1702
|
+
switch (this.state) {
|
|
1703
|
+
case "submit":
|
|
1704
|
+
return `${t}${import_picocolors.default.gray(a2)} ${this.options.filter(({ value: r2 }) => this.value.includes(r2)).map((r2) => n(r2, "submitted")).join(import_picocolors.default.dim(", ")) || import_picocolors.default.dim("none")}`;
|
|
1705
|
+
case "cancel": {
|
|
1706
|
+
const r2 = this.options.filter(({ value: o }) => this.value.includes(o)).map((o) => n(o, "cancelled")).join(import_picocolors.default.dim(", "));
|
|
1707
|
+
return `${t}${import_picocolors.default.gray(a2)} ${r2.trim() ? `${r2}
|
|
1708
|
+
${import_picocolors.default.gray(a2)}` : ""}`;
|
|
1709
|
+
}
|
|
1710
|
+
case "error": {
|
|
1711
|
+
const r2 = this.error.split(`
|
|
1712
|
+
`).map((o, c) => c === 0 ? `${import_picocolors.default.yellow($2)} ${import_picocolors.default.yellow(o)}` : ` ${o}`).join(`
|
|
1713
|
+
`);
|
|
1714
|
+
return t + import_picocolors.default.yellow(a2) + " " + E({ options: this.options, cursor: this.cursor, maxItems: s.maxItems, style: i }).join(`
|
|
1715
|
+
${import_picocolors.default.yellow(a2)} `) + `
|
|
1716
|
+
` + r2 + `
|
|
1717
|
+
`;
|
|
1718
|
+
}
|
|
1719
|
+
default:
|
|
1720
|
+
return `${t}${import_picocolors.default.cyan(a2)} ${E({ options: this.options, cursor: this.cursor, maxItems: s.maxItems, style: i }).join(`
|
|
1721
|
+
${import_picocolors.default.cyan(a2)} `)}
|
|
1722
|
+
${import_picocolors.default.cyan($2)}
|
|
1723
|
+
`;
|
|
1724
|
+
}
|
|
1725
|
+
} }).prompt();
|
|
1726
|
+
};
|
|
1727
|
+
var R2 = (s) => s.replace(ye(), "");
|
|
1728
|
+
var me = (s = "", n = "") => {
|
|
1729
|
+
const t = `
|
|
1730
|
+
${s}
|
|
1731
|
+
`.split(`
|
|
1732
|
+
`), i = R2(n).length, r2 = Math.max(t.reduce((c, l2) => (l2 = R2(l2), l2.length > c ? l2.length : c), 0), i) + 2, o = t.map((c) => `${import_picocolors.default.gray(a2)} ${import_picocolors.default.dim(c)}${" ".repeat(r2 - R2(c).length)}${import_picocolors.default.gray(a2)}`).join(`
|
|
1733
|
+
`);
|
|
1734
|
+
process.stdout.write(`${import_picocolors.default.gray(a2)}
|
|
1735
|
+
${import_picocolors.default.green(M2)} ${import_picocolors.default.reset(n)} ${import_picocolors.default.gray(G.repeat(Math.max(r2 - i - 1, 1)) + H)}
|
|
1736
|
+
${o}
|
|
1737
|
+
${import_picocolors.default.gray(ee + G.repeat(r2 + 2) + te)}
|
|
1738
|
+
`);
|
|
1739
|
+
};
|
|
1740
|
+
var he = (s = "") => {
|
|
1741
|
+
process.stdout.write(`${import_picocolors.default.gray($2)} ${import_picocolors.default.red(s)}
|
|
1742
|
+
|
|
1743
|
+
`);
|
|
1744
|
+
};
|
|
1745
|
+
var pe = (s = "") => {
|
|
1746
|
+
process.stdout.write(`${import_picocolors.default.gray(Q2)} ${s}
|
|
1747
|
+
`);
|
|
1748
|
+
};
|
|
1749
|
+
var ge = (s = "") => {
|
|
1750
|
+
process.stdout.write(`${import_picocolors.default.gray(a2)}
|
|
1751
|
+
${import_picocolors.default.gray($2)} ${s}
|
|
1752
|
+
|
|
1753
|
+
`);
|
|
1754
|
+
};
|
|
1755
|
+
var v2 = { message: (s = "", { symbol: n = import_picocolors.default.gray(a2) } = {}) => {
|
|
1756
|
+
const t = [`${import_picocolors.default.gray(a2)}`];
|
|
1757
|
+
if (s) {
|
|
1758
|
+
const [i, ...r2] = s.split(`
|
|
1759
|
+
`);
|
|
1760
|
+
t.push(`${n} ${i}`, ...r2.map((o) => `${import_picocolors.default.gray(a2)} ${o}`));
|
|
1761
|
+
}
|
|
1762
|
+
process.stdout.write(`${t.join(`
|
|
1763
|
+
`)}
|
|
1764
|
+
`);
|
|
1765
|
+
}, info: (s) => {
|
|
1766
|
+
v2.message(s, { symbol: import_picocolors.default.blue(se) });
|
|
1767
|
+
}, success: (s) => {
|
|
1768
|
+
v2.message(s, { symbol: import_picocolors.default.green(re) });
|
|
1769
|
+
}, step: (s) => {
|
|
1770
|
+
v2.message(s, { symbol: import_picocolors.default.green(M2) });
|
|
1771
|
+
}, warn: (s) => {
|
|
1772
|
+
v2.message(s, { symbol: import_picocolors.default.yellow(ie) });
|
|
1773
|
+
}, warning: (s) => {
|
|
1774
|
+
v2.warn(s);
|
|
1775
|
+
}, error: (s) => {
|
|
1776
|
+
v2.message(s, { symbol: import_picocolors.default.red(ne) });
|
|
1777
|
+
} };
|
|
1778
|
+
var _2 = () => {
|
|
1779
|
+
const s = C ? ["◒", "◐", "◓", "◑"] : ["•", "o", "O", "0"], n = C ? 80 : 120;
|
|
1780
|
+
let t, i, r2 = false, o = "";
|
|
1781
|
+
const c = (g2) => {
|
|
1782
|
+
const m2 = g2 > 1 ? "Something went wrong" : "Canceled";
|
|
1783
|
+
r2 && x2(m2, g2);
|
|
1784
|
+
}, l2 = () => c(2), d2 = () => c(1), p = () => {
|
|
1785
|
+
process.on("uncaughtExceptionMonitor", l2), process.on("unhandledRejection", l2), process.on("SIGINT", d2), process.on("SIGTERM", d2), process.on("exit", c);
|
|
1786
|
+
}, S2 = () => {
|
|
1787
|
+
process.removeListener("uncaughtExceptionMonitor", l2), process.removeListener("unhandledRejection", l2), process.removeListener("SIGINT", d2), process.removeListener("SIGTERM", d2), process.removeListener("exit", c);
|
|
1788
|
+
}, f2 = (g2 = "") => {
|
|
1789
|
+
r2 = true, t = OD(), o = g2.replace(/\.+$/, ""), process.stdout.write(`${import_picocolors.default.gray(a2)}
|
|
1790
|
+
`);
|
|
1791
|
+
let m2 = 0, w2 = 0;
|
|
1792
|
+
p(), i = setInterval(() => {
|
|
1793
|
+
const L2 = import_picocolors.default.magenta(s[m2]), O2 = ".".repeat(Math.floor(w2)).slice(0, 3);
|
|
1794
|
+
process.stdout.write(import_sisteransi2.cursor.move(-999, 0)), process.stdout.write(import_sisteransi2.erase.down(1)), process.stdout.write(`${L2} ${o}${O2}`), m2 = m2 + 1 < s.length ? m2 + 1 : 0, w2 = w2 < s.length ? w2 + 0.125 : 0;
|
|
1795
|
+
}, n);
|
|
1796
|
+
}, x2 = (g2 = "", m2 = 0) => {
|
|
1797
|
+
o = g2 ?? o, r2 = false, clearInterval(i);
|
|
1798
|
+
const w2 = m2 === 0 ? import_picocolors.default.green(M2) : m2 === 1 ? import_picocolors.default.red(P2) : import_picocolors.default.red(V2);
|
|
1799
|
+
process.stdout.write(import_sisteransi2.cursor.move(-999, 0)), process.stdout.write(import_sisteransi2.erase.down(1)), process.stdout.write(`${w2} ${o}
|
|
1800
|
+
`), S2(), t();
|
|
1801
|
+
};
|
|
1802
|
+
return { start: f2, stop: x2, message: (g2 = "") => {
|
|
1803
|
+
o = g2 ?? o;
|
|
1804
|
+
} };
|
|
1805
|
+
};
|
|
1806
|
+
function ye() {
|
|
1807
|
+
const s = ["[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)", "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))"].join("|");
|
|
1808
|
+
return new RegExp(s, "g");
|
|
1809
|
+
}
|
|
1810
|
+
var ve = async (s, n) => {
|
|
1811
|
+
const t = {}, i = Object.keys(s);
|
|
1812
|
+
for (const r2 of i) {
|
|
1813
|
+
const o = s[r2], c = await o({ results: t })?.catch((l2) => {
|
|
1814
|
+
throw l2;
|
|
1815
|
+
});
|
|
1816
|
+
if (typeof n?.onCancel == "function" && lD(c)) {
|
|
1817
|
+
t[r2] = "canceled", n.onCancel({ results: t });
|
|
1818
|
+
continue;
|
|
1819
|
+
}
|
|
1820
|
+
t[r2] = c;
|
|
1821
|
+
}
|
|
1822
|
+
return t;
|
|
1823
|
+
};
|
|
1824
|
+
|
|
1825
|
+
// node_modules/chalk/source/vendor/ansi-styles/index.js
|
|
1826
|
+
var ANSI_BACKGROUND_OFFSET = 10;
|
|
1827
|
+
var wrapAnsi16 = (offset = 0) => (code) => `\x1B[${code + offset}m`;
|
|
1828
|
+
var wrapAnsi256 = (offset = 0) => (code) => `\x1B[${38 + offset};5;${code}m`;
|
|
1829
|
+
var wrapAnsi16m = (offset = 0) => (red, green, blue) => `\x1B[${38 + offset};2;${red};${green};${blue}m`;
|
|
1830
|
+
var styles = {
|
|
1831
|
+
modifier: {
|
|
1832
|
+
reset: [0, 0],
|
|
1833
|
+
bold: [1, 22],
|
|
1834
|
+
dim: [2, 22],
|
|
1835
|
+
italic: [3, 23],
|
|
1836
|
+
underline: [4, 24],
|
|
1837
|
+
overline: [53, 55],
|
|
1838
|
+
inverse: [7, 27],
|
|
1839
|
+
hidden: [8, 28],
|
|
1840
|
+
strikethrough: [9, 29]
|
|
1841
|
+
},
|
|
1842
|
+
color: {
|
|
1843
|
+
black: [30, 39],
|
|
1844
|
+
red: [31, 39],
|
|
1845
|
+
green: [32, 39],
|
|
1846
|
+
yellow: [33, 39],
|
|
1847
|
+
blue: [34, 39],
|
|
1848
|
+
magenta: [35, 39],
|
|
1849
|
+
cyan: [36, 39],
|
|
1850
|
+
white: [37, 39],
|
|
1851
|
+
blackBright: [90, 39],
|
|
1852
|
+
gray: [90, 39],
|
|
1853
|
+
grey: [90, 39],
|
|
1854
|
+
redBright: [91, 39],
|
|
1855
|
+
greenBright: [92, 39],
|
|
1856
|
+
yellowBright: [93, 39],
|
|
1857
|
+
blueBright: [94, 39],
|
|
1858
|
+
magentaBright: [95, 39],
|
|
1859
|
+
cyanBright: [96, 39],
|
|
1860
|
+
whiteBright: [97, 39]
|
|
1861
|
+
},
|
|
1862
|
+
bgColor: {
|
|
1863
|
+
bgBlack: [40, 49],
|
|
1864
|
+
bgRed: [41, 49],
|
|
1865
|
+
bgGreen: [42, 49],
|
|
1866
|
+
bgYellow: [43, 49],
|
|
1867
|
+
bgBlue: [44, 49],
|
|
1868
|
+
bgMagenta: [45, 49],
|
|
1869
|
+
bgCyan: [46, 49],
|
|
1870
|
+
bgWhite: [47, 49],
|
|
1871
|
+
bgBlackBright: [100, 49],
|
|
1872
|
+
bgGray: [100, 49],
|
|
1873
|
+
bgGrey: [100, 49],
|
|
1874
|
+
bgRedBright: [101, 49],
|
|
1875
|
+
bgGreenBright: [102, 49],
|
|
1876
|
+
bgYellowBright: [103, 49],
|
|
1877
|
+
bgBlueBright: [104, 49],
|
|
1878
|
+
bgMagentaBright: [105, 49],
|
|
1879
|
+
bgCyanBright: [106, 49],
|
|
1880
|
+
bgWhiteBright: [107, 49]
|
|
1881
|
+
}
|
|
1882
|
+
};
|
|
1883
|
+
var modifierNames = Object.keys(styles.modifier);
|
|
1884
|
+
var foregroundColorNames = Object.keys(styles.color);
|
|
1885
|
+
var backgroundColorNames = Object.keys(styles.bgColor);
|
|
1886
|
+
var colorNames = [...foregroundColorNames, ...backgroundColorNames];
|
|
1887
|
+
function assembleStyles() {
|
|
1888
|
+
const codes = new Map;
|
|
1889
|
+
for (const [groupName, group] of Object.entries(styles)) {
|
|
1890
|
+
for (const [styleName, style] of Object.entries(group)) {
|
|
1891
|
+
styles[styleName] = {
|
|
1892
|
+
open: `\x1B[${style[0]}m`,
|
|
1893
|
+
close: `\x1B[${style[1]}m`
|
|
1894
|
+
};
|
|
1895
|
+
group[styleName] = styles[styleName];
|
|
1896
|
+
codes.set(style[0], style[1]);
|
|
1897
|
+
}
|
|
1898
|
+
Object.defineProperty(styles, groupName, {
|
|
1899
|
+
value: group,
|
|
1900
|
+
enumerable: false
|
|
1901
|
+
});
|
|
1902
|
+
}
|
|
1903
|
+
Object.defineProperty(styles, "codes", {
|
|
1904
|
+
value: codes,
|
|
1905
|
+
enumerable: false
|
|
1906
|
+
});
|
|
1907
|
+
styles.color.close = "\x1B[39m";
|
|
1908
|
+
styles.bgColor.close = "\x1B[49m";
|
|
1909
|
+
styles.color.ansi = wrapAnsi16();
|
|
1910
|
+
styles.color.ansi256 = wrapAnsi256();
|
|
1911
|
+
styles.color.ansi16m = wrapAnsi16m();
|
|
1912
|
+
styles.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET);
|
|
1913
|
+
styles.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET);
|
|
1914
|
+
styles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET);
|
|
1915
|
+
Object.defineProperties(styles, {
|
|
1916
|
+
rgbToAnsi256: {
|
|
1917
|
+
value(red, green, blue) {
|
|
1918
|
+
if (red === green && green === blue) {
|
|
1919
|
+
if (red < 8) {
|
|
1920
|
+
return 16;
|
|
1921
|
+
}
|
|
1922
|
+
if (red > 248) {
|
|
1923
|
+
return 231;
|
|
1924
|
+
}
|
|
1925
|
+
return Math.round((red - 8) / 247 * 24) + 232;
|
|
1926
|
+
}
|
|
1927
|
+
return 16 + 36 * Math.round(red / 255 * 5) + 6 * Math.round(green / 255 * 5) + Math.round(blue / 255 * 5);
|
|
1928
|
+
},
|
|
1929
|
+
enumerable: false
|
|
1930
|
+
},
|
|
1931
|
+
hexToRgb: {
|
|
1932
|
+
value(hex) {
|
|
1933
|
+
const matches = /[a-f\d]{6}|[a-f\d]{3}/i.exec(hex.toString(16));
|
|
1934
|
+
if (!matches) {
|
|
1935
|
+
return [0, 0, 0];
|
|
1936
|
+
}
|
|
1937
|
+
let [colorString] = matches;
|
|
1938
|
+
if (colorString.length === 3) {
|
|
1939
|
+
colorString = [...colorString].map((character) => character + character).join("");
|
|
1940
|
+
}
|
|
1941
|
+
const integer = Number.parseInt(colorString, 16);
|
|
1942
|
+
return [
|
|
1943
|
+
integer >> 16 & 255,
|
|
1944
|
+
integer >> 8 & 255,
|
|
1945
|
+
integer & 255
|
|
1946
|
+
];
|
|
1947
|
+
},
|
|
1948
|
+
enumerable: false
|
|
1949
|
+
},
|
|
1950
|
+
hexToAnsi256: {
|
|
1951
|
+
value: (hex) => styles.rgbToAnsi256(...styles.hexToRgb(hex)),
|
|
1952
|
+
enumerable: false
|
|
1953
|
+
},
|
|
1954
|
+
ansi256ToAnsi: {
|
|
1955
|
+
value(code) {
|
|
1956
|
+
if (code < 8) {
|
|
1957
|
+
return 30 + code;
|
|
1958
|
+
}
|
|
1959
|
+
if (code < 16) {
|
|
1960
|
+
return 90 + (code - 8);
|
|
1961
|
+
}
|
|
1962
|
+
let red;
|
|
1963
|
+
let green;
|
|
1964
|
+
let blue;
|
|
1965
|
+
if (code >= 232) {
|
|
1966
|
+
red = ((code - 232) * 10 + 8) / 255;
|
|
1967
|
+
green = red;
|
|
1968
|
+
blue = red;
|
|
1969
|
+
} else {
|
|
1970
|
+
code -= 16;
|
|
1971
|
+
const remainder = code % 36;
|
|
1972
|
+
red = Math.floor(code / 36) / 5;
|
|
1973
|
+
green = Math.floor(remainder / 6) / 5;
|
|
1974
|
+
blue = remainder % 6 / 5;
|
|
1975
|
+
}
|
|
1976
|
+
const value = Math.max(red, green, blue) * 2;
|
|
1977
|
+
if (value === 0) {
|
|
1978
|
+
return 30;
|
|
1979
|
+
}
|
|
1980
|
+
let result = 30 + (Math.round(blue) << 2 | Math.round(green) << 1 | Math.round(red));
|
|
1981
|
+
if (value === 2) {
|
|
1982
|
+
result += 60;
|
|
1983
|
+
}
|
|
1984
|
+
return result;
|
|
1985
|
+
},
|
|
1986
|
+
enumerable: false
|
|
1987
|
+
},
|
|
1988
|
+
rgbToAnsi: {
|
|
1989
|
+
value: (red, green, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green, blue)),
|
|
1990
|
+
enumerable: false
|
|
1991
|
+
},
|
|
1992
|
+
hexToAnsi: {
|
|
1993
|
+
value: (hex) => styles.ansi256ToAnsi(styles.hexToAnsi256(hex)),
|
|
1994
|
+
enumerable: false
|
|
1995
|
+
}
|
|
1996
|
+
});
|
|
1997
|
+
return styles;
|
|
1998
|
+
}
|
|
1999
|
+
var ansiStyles = assembleStyles();
|
|
2000
|
+
var ansi_styles_default = ansiStyles;
|
|
2001
|
+
|
|
2002
|
+
// node_modules/chalk/source/vendor/supports-color/index.js
|
|
2003
|
+
import process2 from "node:process";
|
|
2004
|
+
import os from "node:os";
|
|
2005
|
+
import tty from "node:tty";
|
|
2006
|
+
function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process2.argv) {
|
|
2007
|
+
const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
|
|
2008
|
+
const position = argv.indexOf(prefix + flag);
|
|
2009
|
+
const terminatorPosition = argv.indexOf("--");
|
|
2010
|
+
return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition);
|
|
2011
|
+
}
|
|
2012
|
+
var { env } = process2;
|
|
2013
|
+
var flagForceColor;
|
|
2014
|
+
if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) {
|
|
2015
|
+
flagForceColor = 0;
|
|
2016
|
+
} else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) {
|
|
2017
|
+
flagForceColor = 1;
|
|
2018
|
+
}
|
|
2019
|
+
function envForceColor() {
|
|
2020
|
+
if ("FORCE_COLOR" in env) {
|
|
2021
|
+
if (env.FORCE_COLOR === "true") {
|
|
2022
|
+
return 1;
|
|
2023
|
+
}
|
|
2024
|
+
if (env.FORCE_COLOR === "false") {
|
|
2025
|
+
return 0;
|
|
2026
|
+
}
|
|
2027
|
+
return env.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3);
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
function translateLevel(level) {
|
|
2031
|
+
if (level === 0) {
|
|
2032
|
+
return false;
|
|
2033
|
+
}
|
|
2034
|
+
return {
|
|
2035
|
+
level,
|
|
2036
|
+
hasBasic: true,
|
|
2037
|
+
has256: level >= 2,
|
|
2038
|
+
has16m: level >= 3
|
|
2039
|
+
};
|
|
2040
|
+
}
|
|
2041
|
+
function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
|
|
2042
|
+
const noFlagForceColor = envForceColor();
|
|
2043
|
+
if (noFlagForceColor !== undefined) {
|
|
2044
|
+
flagForceColor = noFlagForceColor;
|
|
2045
|
+
}
|
|
2046
|
+
const forceColor = sniffFlags ? flagForceColor : noFlagForceColor;
|
|
2047
|
+
if (forceColor === 0) {
|
|
2048
|
+
return 0;
|
|
2049
|
+
}
|
|
2050
|
+
if (sniffFlags) {
|
|
2051
|
+
if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) {
|
|
2052
|
+
return 3;
|
|
2053
|
+
}
|
|
2054
|
+
if (hasFlag("color=256")) {
|
|
2055
|
+
return 2;
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
if ("TF_BUILD" in env && "AGENT_NAME" in env) {
|
|
2059
|
+
return 1;
|
|
2060
|
+
}
|
|
2061
|
+
if (haveStream && !streamIsTTY && forceColor === undefined) {
|
|
2062
|
+
return 0;
|
|
2063
|
+
}
|
|
2064
|
+
const min = forceColor || 0;
|
|
2065
|
+
if (env.TERM === "dumb") {
|
|
2066
|
+
return min;
|
|
2067
|
+
}
|
|
2068
|
+
if (process2.platform === "win32") {
|
|
2069
|
+
const osRelease = os.release().split(".");
|
|
2070
|
+
if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
|
|
2071
|
+
return Number(osRelease[2]) >= 14931 ? 3 : 2;
|
|
2072
|
+
}
|
|
2073
|
+
return 1;
|
|
2074
|
+
}
|
|
2075
|
+
if ("CI" in env) {
|
|
2076
|
+
if (["GITHUB_ACTIONS", "GITEA_ACTIONS", "CIRCLECI"].some((key) => (key in env))) {
|
|
2077
|
+
return 3;
|
|
2078
|
+
}
|
|
2079
|
+
if (["TRAVIS", "APPVEYOR", "GITLAB_CI", "BUILDKITE", "DRONE"].some((sign) => (sign in env)) || env.CI_NAME === "codeship") {
|
|
2080
|
+
return 1;
|
|
2081
|
+
}
|
|
2082
|
+
return min;
|
|
2083
|
+
}
|
|
2084
|
+
if ("TEAMCITY_VERSION" in env) {
|
|
2085
|
+
return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0;
|
|
2086
|
+
}
|
|
2087
|
+
if (env.COLORTERM === "truecolor") {
|
|
2088
|
+
return 3;
|
|
2089
|
+
}
|
|
2090
|
+
if (env.TERM === "xterm-kitty") {
|
|
2091
|
+
return 3;
|
|
2092
|
+
}
|
|
2093
|
+
if (env.TERM === "xterm-ghostty") {
|
|
2094
|
+
return 3;
|
|
2095
|
+
}
|
|
2096
|
+
if (env.TERM === "wezterm") {
|
|
2097
|
+
return 3;
|
|
2098
|
+
}
|
|
2099
|
+
if ("TERM_PROGRAM" in env) {
|
|
2100
|
+
const version = Number.parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10);
|
|
2101
|
+
switch (env.TERM_PROGRAM) {
|
|
2102
|
+
case "iTerm.app": {
|
|
2103
|
+
return version >= 3 ? 3 : 2;
|
|
2104
|
+
}
|
|
2105
|
+
case "Apple_Terminal": {
|
|
2106
|
+
return 2;
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
if (/-256(color)?$/i.test(env.TERM)) {
|
|
2111
|
+
return 2;
|
|
2112
|
+
}
|
|
2113
|
+
if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) {
|
|
2114
|
+
return 1;
|
|
2115
|
+
}
|
|
2116
|
+
if ("COLORTERM" in env) {
|
|
2117
|
+
return 1;
|
|
2118
|
+
}
|
|
2119
|
+
return min;
|
|
2120
|
+
}
|
|
2121
|
+
function createSupportsColor(stream, options = {}) {
|
|
2122
|
+
const level = _supportsColor(stream, {
|
|
2123
|
+
streamIsTTY: stream && stream.isTTY,
|
|
2124
|
+
...options
|
|
2125
|
+
});
|
|
2126
|
+
return translateLevel(level);
|
|
2127
|
+
}
|
|
2128
|
+
var supportsColor = {
|
|
2129
|
+
stdout: createSupportsColor({ isTTY: tty.isatty(1) }),
|
|
2130
|
+
stderr: createSupportsColor({ isTTY: tty.isatty(2) })
|
|
2131
|
+
};
|
|
2132
|
+
var supports_color_default = supportsColor;
|
|
2133
|
+
|
|
2134
|
+
// node_modules/chalk/source/utilities.js
|
|
2135
|
+
function stringReplaceAll(string, substring, replacer) {
|
|
2136
|
+
let index = string.indexOf(substring);
|
|
2137
|
+
if (index === -1) {
|
|
2138
|
+
return string;
|
|
2139
|
+
}
|
|
2140
|
+
const substringLength = substring.length;
|
|
2141
|
+
let endIndex = 0;
|
|
2142
|
+
let returnValue = "";
|
|
2143
|
+
do {
|
|
2144
|
+
returnValue += string.slice(endIndex, index) + substring + replacer;
|
|
2145
|
+
endIndex = index + substringLength;
|
|
2146
|
+
index = string.indexOf(substring, endIndex);
|
|
2147
|
+
} while (index !== -1);
|
|
2148
|
+
returnValue += string.slice(endIndex);
|
|
2149
|
+
return returnValue;
|
|
2150
|
+
}
|
|
2151
|
+
function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) {
|
|
2152
|
+
let endIndex = 0;
|
|
2153
|
+
let returnValue = "";
|
|
2154
|
+
do {
|
|
2155
|
+
const gotCR = string[index - 1] === "\r";
|
|
2156
|
+
returnValue += string.slice(endIndex, gotCR ? index - 1 : index) + prefix + (gotCR ? `\r
|
|
2157
|
+
` : `
|
|
2158
|
+
`) + postfix;
|
|
2159
|
+
endIndex = index + 1;
|
|
2160
|
+
index = string.indexOf(`
|
|
2161
|
+
`, endIndex);
|
|
2162
|
+
} while (index !== -1);
|
|
2163
|
+
returnValue += string.slice(endIndex);
|
|
2164
|
+
return returnValue;
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2167
|
+
// node_modules/chalk/source/index.js
|
|
2168
|
+
var { stdout: stdoutColor, stderr: stderrColor } = supports_color_default;
|
|
2169
|
+
var GENERATOR = Symbol("GENERATOR");
|
|
2170
|
+
var STYLER = Symbol("STYLER");
|
|
2171
|
+
var IS_EMPTY = Symbol("IS_EMPTY");
|
|
2172
|
+
var levelMapping = [
|
|
2173
|
+
"ansi",
|
|
2174
|
+
"ansi",
|
|
2175
|
+
"ansi256",
|
|
2176
|
+
"ansi16m"
|
|
2177
|
+
];
|
|
2178
|
+
var styles2 = Object.create(null);
|
|
2179
|
+
var applyOptions = (object, options = {}) => {
|
|
2180
|
+
if (options.level && !(Number.isInteger(options.level) && options.level >= 0 && options.level <= 3)) {
|
|
2181
|
+
throw new Error("The `level` option should be an integer from 0 to 3");
|
|
2182
|
+
}
|
|
2183
|
+
const colorLevel = stdoutColor ? stdoutColor.level : 0;
|
|
2184
|
+
object.level = options.level === undefined ? colorLevel : options.level;
|
|
2185
|
+
};
|
|
2186
|
+
var chalkFactory = (options) => {
|
|
2187
|
+
const chalk = (...strings) => strings.join(" ");
|
|
2188
|
+
applyOptions(chalk, options);
|
|
2189
|
+
Object.setPrototypeOf(chalk, createChalk.prototype);
|
|
2190
|
+
return chalk;
|
|
2191
|
+
};
|
|
2192
|
+
function createChalk(options) {
|
|
2193
|
+
return chalkFactory(options);
|
|
2194
|
+
}
|
|
2195
|
+
Object.setPrototypeOf(createChalk.prototype, Function.prototype);
|
|
2196
|
+
for (const [styleName, style] of Object.entries(ansi_styles_default)) {
|
|
2197
|
+
styles2[styleName] = {
|
|
2198
|
+
get() {
|
|
2199
|
+
const builder = createBuilder(this, createStyler(style.open, style.close, this[STYLER]), this[IS_EMPTY]);
|
|
2200
|
+
Object.defineProperty(this, styleName, { value: builder });
|
|
2201
|
+
return builder;
|
|
2202
|
+
}
|
|
2203
|
+
};
|
|
2204
|
+
}
|
|
2205
|
+
styles2.visible = {
|
|
2206
|
+
get() {
|
|
2207
|
+
const builder = createBuilder(this, this[STYLER], true);
|
|
2208
|
+
Object.defineProperty(this, "visible", { value: builder });
|
|
2209
|
+
return builder;
|
|
2210
|
+
}
|
|
2211
|
+
};
|
|
2212
|
+
var getModelAnsi = (model, level, type, ...arguments_) => {
|
|
2213
|
+
if (model === "rgb") {
|
|
2214
|
+
if (level === "ansi16m") {
|
|
2215
|
+
return ansi_styles_default[type].ansi16m(...arguments_);
|
|
2216
|
+
}
|
|
2217
|
+
if (level === "ansi256") {
|
|
2218
|
+
return ansi_styles_default[type].ansi256(ansi_styles_default.rgbToAnsi256(...arguments_));
|
|
2219
|
+
}
|
|
2220
|
+
return ansi_styles_default[type].ansi(ansi_styles_default.rgbToAnsi(...arguments_));
|
|
2221
|
+
}
|
|
2222
|
+
if (model === "hex") {
|
|
2223
|
+
return getModelAnsi("rgb", level, type, ...ansi_styles_default.hexToRgb(...arguments_));
|
|
2224
|
+
}
|
|
2225
|
+
return ansi_styles_default[type][model](...arguments_);
|
|
2226
|
+
};
|
|
2227
|
+
var usedModels = ["rgb", "hex", "ansi256"];
|
|
2228
|
+
for (const model of usedModels) {
|
|
2229
|
+
styles2[model] = {
|
|
2230
|
+
get() {
|
|
2231
|
+
const { level } = this;
|
|
2232
|
+
return function(...arguments_) {
|
|
2233
|
+
const styler = createStyler(getModelAnsi(model, levelMapping[level], "color", ...arguments_), ansi_styles_default.color.close, this[STYLER]);
|
|
2234
|
+
return createBuilder(this, styler, this[IS_EMPTY]);
|
|
2235
|
+
};
|
|
2236
|
+
}
|
|
2237
|
+
};
|
|
2238
|
+
const bgModel = "bg" + model[0].toUpperCase() + model.slice(1);
|
|
2239
|
+
styles2[bgModel] = {
|
|
2240
|
+
get() {
|
|
2241
|
+
const { level } = this;
|
|
2242
|
+
return function(...arguments_) {
|
|
2243
|
+
const styler = createStyler(getModelAnsi(model, levelMapping[level], "bgColor", ...arguments_), ansi_styles_default.bgColor.close, this[STYLER]);
|
|
2244
|
+
return createBuilder(this, styler, this[IS_EMPTY]);
|
|
2245
|
+
};
|
|
2246
|
+
}
|
|
2247
|
+
};
|
|
2248
|
+
}
|
|
2249
|
+
var proto = Object.defineProperties(() => {}, {
|
|
2250
|
+
...styles2,
|
|
2251
|
+
level: {
|
|
2252
|
+
enumerable: true,
|
|
2253
|
+
get() {
|
|
2254
|
+
return this[GENERATOR].level;
|
|
2255
|
+
},
|
|
2256
|
+
set(level) {
|
|
2257
|
+
this[GENERATOR].level = level;
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
2260
|
+
});
|
|
2261
|
+
var createStyler = (open, close, parent) => {
|
|
2262
|
+
let openAll;
|
|
2263
|
+
let closeAll;
|
|
2264
|
+
if (parent === undefined) {
|
|
2265
|
+
openAll = open;
|
|
2266
|
+
closeAll = close;
|
|
2267
|
+
} else {
|
|
2268
|
+
openAll = parent.openAll + open;
|
|
2269
|
+
closeAll = close + parent.closeAll;
|
|
2270
|
+
}
|
|
2271
|
+
return {
|
|
2272
|
+
open,
|
|
2273
|
+
close,
|
|
2274
|
+
openAll,
|
|
2275
|
+
closeAll,
|
|
2276
|
+
parent
|
|
2277
|
+
};
|
|
2278
|
+
};
|
|
2279
|
+
var createBuilder = (self, _styler, _isEmpty) => {
|
|
2280
|
+
const builder = (...arguments_) => applyStyle(builder, arguments_.length === 1 ? "" + arguments_[0] : arguments_.join(" "));
|
|
2281
|
+
Object.setPrototypeOf(builder, proto);
|
|
2282
|
+
builder[GENERATOR] = self;
|
|
2283
|
+
builder[STYLER] = _styler;
|
|
2284
|
+
builder[IS_EMPTY] = _isEmpty;
|
|
2285
|
+
return builder;
|
|
2286
|
+
};
|
|
2287
|
+
var applyStyle = (self, string) => {
|
|
2288
|
+
if (self.level <= 0 || !string) {
|
|
2289
|
+
return self[IS_EMPTY] ? "" : string;
|
|
2290
|
+
}
|
|
2291
|
+
let styler = self[STYLER];
|
|
2292
|
+
if (styler === undefined) {
|
|
2293
|
+
return string;
|
|
2294
|
+
}
|
|
2295
|
+
const { openAll, closeAll } = styler;
|
|
2296
|
+
if (string.includes("\x1B")) {
|
|
2297
|
+
while (styler !== undefined) {
|
|
2298
|
+
string = stringReplaceAll(string, styler.close, styler.open);
|
|
2299
|
+
styler = styler.parent;
|
|
2300
|
+
}
|
|
2301
|
+
}
|
|
2302
|
+
const lfIndex = string.indexOf(`
|
|
2303
|
+
`);
|
|
2304
|
+
if (lfIndex !== -1) {
|
|
2305
|
+
string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex);
|
|
2306
|
+
}
|
|
2307
|
+
return openAll + string + closeAll;
|
|
2308
|
+
};
|
|
2309
|
+
Object.defineProperties(createChalk.prototype, styles2);
|
|
2310
|
+
var chalk = createChalk();
|
|
2311
|
+
var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
|
|
2312
|
+
var source_default = chalk;
|
|
2313
|
+
|
|
2314
|
+
// src/cli.ts
|
|
2315
|
+
var import_qrcode_terminal = __toESM(require_main(), 1);
|
|
2316
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, copyFileSync, renameSync, readdirSync, statSync } from "fs";
|
|
2317
|
+
import { homedir, hostname, platform } from "os";
|
|
2318
|
+
import { join, dirname, basename } from "path";
|
|
2319
|
+
import { execSync, spawnSync } from "child_process";
|
|
2320
|
+
import { randomBytes, createHash } from "crypto";
|
|
2321
|
+
var VERSION = "0.1.0";
|
|
2322
|
+
function getApiUrl() {
|
|
2323
|
+
return process.env.AGENTAPPROVE_API || "https://api.agentapprove.com";
|
|
2324
|
+
}
|
|
2325
|
+
var API_URL = getApiUrl();
|
|
2326
|
+
var API_VERSION = process.env.AGENTAPPROVE_API_VERSION || "v001";
|
|
2327
|
+
function hasFlag2(flag) {
|
|
2328
|
+
return process.argv.slice(2).includes(flag);
|
|
2329
|
+
}
|
|
2330
|
+
function updateEnvValue(key, value) {
|
|
2331
|
+
const envPath = join(getAgentApproveDir(), "env");
|
|
2332
|
+
if (!existsSync(envPath))
|
|
2333
|
+
return;
|
|
2334
|
+
let content = readFileSync(envPath, "utf-8");
|
|
2335
|
+
const pattern = new RegExp(`^${key}=.*$`, "m");
|
|
2336
|
+
if (pattern.test(content)) {
|
|
2337
|
+
content = content.replace(pattern, `${key}=${value}`);
|
|
2338
|
+
} else {
|
|
2339
|
+
content = content.trimEnd() + `
|
|
2340
|
+
${key}=${value}
|
|
2341
|
+
`;
|
|
2342
|
+
}
|
|
2343
|
+
writeFileSync(envPath, content, { mode: 384 });
|
|
2344
|
+
}
|
|
2345
|
+
function getCommand() {
|
|
2346
|
+
const args = process.argv.slice(2);
|
|
2347
|
+
const filtered = [];
|
|
2348
|
+
for (let i = 0;i < args.length; i++) {
|
|
2349
|
+
const arg = args[i];
|
|
2350
|
+
if (arg.startsWith("--")) {
|
|
2351
|
+
if (!arg.includes("=") && args[i + 1] && !args[i + 1].startsWith("--")) {
|
|
2352
|
+
i++;
|
|
2353
|
+
}
|
|
2354
|
+
} else {
|
|
2355
|
+
filtered.push(arg);
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
return filtered[0] || "install";
|
|
2359
|
+
}
|
|
2360
|
+
var AGENTS = {
|
|
2361
|
+
"claude-code": {
|
|
2362
|
+
name: "Claude Code",
|
|
2363
|
+
configPath: join(homedir(), ".claude", "settings.json"),
|
|
2364
|
+
hooksKey: "hooks",
|
|
2365
|
+
hooks: [
|
|
2366
|
+
{ name: "PreToolUse", file: "claude-pre-tool.sh", description: "Before tool execution (approval)", hasMatcher: true, isApprovalHook: true },
|
|
2367
|
+
{ name: "PostToolUse", file: "claude-post-tool.sh", description: "After tool execution (logging)", hasMatcher: true },
|
|
2368
|
+
{ name: "PostToolUseFailure", file: "claude-post-tool-failure.sh", description: "Tool execution failed", hasMatcher: true },
|
|
2369
|
+
{ name: "PermissionRequest", file: "notify-hook.sh", description: "Permission requests", hasMatcher: true, isApprovalHook: true },
|
|
2370
|
+
{ name: "UserPromptSubmit", file: "claude-user-prompt.sh", description: "User prompt submitted", hasMatcher: false },
|
|
2371
|
+
{ name: "Stop", file: "claude-stop.sh", description: "Session/task completed", hasMatcher: false, timeout: 600 },
|
|
2372
|
+
{ name: "SessionStart", file: "claude-session-start.sh", description: "Session started", hasMatcher: false },
|
|
2373
|
+
{ name: "SessionEnd", file: "claude-session-end.sh", description: "Session ended", hasMatcher: false }
|
|
2374
|
+
]
|
|
2375
|
+
},
|
|
2376
|
+
cursor: {
|
|
2377
|
+
name: "Cursor",
|
|
2378
|
+
configPath: join(homedir(), ".cursor", "hooks.json"),
|
|
2379
|
+
hooksKey: "hooks",
|
|
2380
|
+
hooks: [
|
|
2381
|
+
{ name: "sessionStart", file: "cursor-session-start.sh", description: "Session started" },
|
|
2382
|
+
{ name: "sessionEnd", file: "cursor-session-end.sh", description: "Session ended" },
|
|
2383
|
+
{ name: "beforeShellExecution", file: "cursor-approval.sh", description: "Shell command approval", isApprovalHook: true },
|
|
2384
|
+
{ name: "beforeMCPExecution", file: "cursor-mcp-approval.sh", description: "MCP tool approval", isApprovalHook: true },
|
|
2385
|
+
{ name: "preToolUse", file: "cursor-pre-tool.sh", description: "Built-in tool approval", isApprovalHook: true },
|
|
2386
|
+
{ name: "afterShellExecution", file: "cursor-shell-complete.sh", description: "Shell completion logging" },
|
|
2387
|
+
{ name: "afterMCPExecution", file: "cursor-mcp-complete.sh", description: "MCP completion logging" },
|
|
2388
|
+
{ name: "postToolUse", file: "cursor-post-tool.sh", description: "Built-in tool completion" },
|
|
2389
|
+
{ name: "beforeSubmitPrompt", file: "cursor-start.sh", description: "Before prompt submitted" },
|
|
2390
|
+
{ name: "subagentStart", file: "cursor-subagent-start.sh", description: "Subagent started" },
|
|
2391
|
+
{ name: "subagentStop", file: "cursor-subagent-stop.sh", description: "Subagent completed" },
|
|
2392
|
+
{ name: "preCompact", file: "cursor-precompact.sh", description: "Context compaction" },
|
|
2393
|
+
{ name: "stop", file: "cursor-stop.sh", description: "Agent stopped" },
|
|
2394
|
+
{ name: "afterAgentThought", file: "cursor-thought.sh", description: "Agent thinking" },
|
|
2395
|
+
{ name: "afterAgentResponse", file: "cursor-response.sh", description: "Agent response" }
|
|
2396
|
+
]
|
|
2397
|
+
},
|
|
2398
|
+
"gemini-cli": {
|
|
2399
|
+
name: "Gemini CLI",
|
|
2400
|
+
configPath: join(homedir(), ".gemini", "settings.json"),
|
|
2401
|
+
hooksKey: "hooks",
|
|
2402
|
+
hooks: [
|
|
2403
|
+
{ name: "SessionStart", file: "gemini-session-start.sh", description: "Session started" },
|
|
2404
|
+
{ name: "BeforeAgent", file: "gemini-before-agent.sh", description: "User prompt capture" },
|
|
2405
|
+
{ name: "BeforeModel", file: "gemini-before-model.sh", description: "Before LLM request" },
|
|
2406
|
+
{ name: "AfterModel", file: "gemini-after-model.sh", description: "After LLM response" },
|
|
2407
|
+
{ name: "BeforeTool", file: "gemini-before-tool.sh", description: "Tool approval", isApprovalHook: true },
|
|
2408
|
+
{ name: "AfterTool", file: "gemini-after-tool.sh", description: "Tool completion logging" },
|
|
2409
|
+
{ name: "AfterAgent", file: "gemini-stop.sh", description: "Agent stop (iOS input)", timeout: 600 },
|
|
2410
|
+
{ name: "Notification", file: "gemini-notification.sh", description: "Notifications (logging only)" },
|
|
2411
|
+
{ name: "SessionEnd", file: "gemini-session-end.sh", description: "Session ended" }
|
|
2412
|
+
]
|
|
2413
|
+
},
|
|
2414
|
+
"vscode-agent": {
|
|
2415
|
+
name: "VS Code Agent Hooks",
|
|
2416
|
+
configPath: join(homedir(), ".agentapprove", "hooks", "vscode-hooks.json"),
|
|
2417
|
+
hooksKey: "hooks",
|
|
2418
|
+
hooks: [
|
|
2419
|
+
{ name: "SessionStart", file: "github-session-start.sh", description: "Session started" },
|
|
2420
|
+
{ name: "UserPromptSubmit", file: "github-user-prompt.sh", description: "User prompt submitted" },
|
|
2421
|
+
{ name: "PreToolUse", file: "github-pre-tool.sh", description: "Tool approval", isApprovalHook: true },
|
|
2422
|
+
{ name: "PostToolUse", file: "github-post-tool.sh", description: "Tool completion logging" },
|
|
2423
|
+
{ name: "PreCompact", file: "github-precompact.sh", description: "Context compaction" },
|
|
2424
|
+
{ name: "SubagentStart", file: "github-subagent-start.sh", description: "Subagent started" },
|
|
2425
|
+
{ name: "SubagentStop", file: "github-subagent-stop.sh", description: "Subagent completed" },
|
|
2426
|
+
{ name: "Stop", file: "github-stop.sh", description: "Agent stopped (iOS input)", timeout: 600 }
|
|
2427
|
+
]
|
|
2428
|
+
},
|
|
2429
|
+
"copilot-cli": {
|
|
2430
|
+
name: "Copilot CLI",
|
|
2431
|
+
configPath: join(homedir(), ".agentapprove", "copilot-cli-hooks.json"),
|
|
2432
|
+
hooksKey: "hooks",
|
|
2433
|
+
hooks: [
|
|
2434
|
+
{ name: "sessionStart", file: "github-session-start.sh", description: "Session started" },
|
|
2435
|
+
{ name: "sessionEnd", file: "github-session-end.sh", description: "Session ended" },
|
|
2436
|
+
{ name: "userPromptSubmitted", file: "github-user-prompt.sh", description: "User prompt submitted" },
|
|
2437
|
+
{ name: "preToolUse", file: "github-pre-tool.sh", description: "Tool approval", isApprovalHook: true },
|
|
2438
|
+
{ name: "postToolUse", file: "github-post-tool.sh", description: "Tool completion logging" },
|
|
2439
|
+
{ name: "errorOccurred", file: "github-error.sh", description: "Error logging" }
|
|
2440
|
+
]
|
|
2441
|
+
},
|
|
2442
|
+
openclaw: {
|
|
2443
|
+
name: "OpenClaw",
|
|
2444
|
+
configPath: join(homedir(), ".openclaw", "openclaw.json"),
|
|
2445
|
+
hooksKey: "plugins",
|
|
2446
|
+
hooks: [
|
|
2447
|
+
{ name: "agentapprove", file: "openclaw-plugin.js", description: "Tool approval + event monitoring", isApprovalHook: true, isPlugin: true }
|
|
2448
|
+
]
|
|
2449
|
+
}
|
|
2450
|
+
};
|
|
2451
|
+
function findGitBash() {
|
|
2452
|
+
if (!isWindows())
|
|
2453
|
+
return null;
|
|
2454
|
+
const candidates = [
|
|
2455
|
+
process.env.CLAUDE_CODE_GIT_BASH_PATH,
|
|
2456
|
+
"C:\\Program Files\\Git\\bin\\bash.exe",
|
|
2457
|
+
"C:\\Program Files (x86)\\Git\\bin\\bash.exe",
|
|
2458
|
+
join(process.env.LOCALAPPDATA || "", "Programs", "Git", "bin", "bash.exe"),
|
|
2459
|
+
join(process.env.ProgramW6432 || "", "Git", "bin", "bash.exe")
|
|
2460
|
+
].filter(Boolean);
|
|
2461
|
+
for (const candidate of candidates) {
|
|
2462
|
+
if (existsSync(candidate)) {
|
|
2463
|
+
return candidate;
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
try {
|
|
2467
|
+
const result = execSync("where bash", { encoding: "utf-8", stdio: ["pipe", "pipe", "ignore"] });
|
|
2468
|
+
const firstLine = result.trim().split(`
|
|
2469
|
+
`)[0];
|
|
2470
|
+
if (firstLine && existsSync(firstLine)) {
|
|
2471
|
+
return firstLine;
|
|
2472
|
+
}
|
|
2473
|
+
} catch {}
|
|
2474
|
+
return null;
|
|
2475
|
+
}
|
|
2476
|
+
function toGitBashPath(windowsPath) {
|
|
2477
|
+
let result = windowsPath.replace(/\\/g, "/");
|
|
2478
|
+
result = result.replace(/^([A-Za-z]):\//, (_3, drive) => `/${drive.toLowerCase()}/`);
|
|
2479
|
+
return result;
|
|
2480
|
+
}
|
|
2481
|
+
function buildHookCommand(hookScriptPath, agentId) {
|
|
2482
|
+
if (!isWindows()) {
|
|
2483
|
+
return hookScriptPath;
|
|
2484
|
+
}
|
|
2485
|
+
const gitBash = findGitBash();
|
|
2486
|
+
if (!gitBash) {
|
|
2487
|
+
return `bash "${toGitBashPath(hookScriptPath)}"`;
|
|
2488
|
+
}
|
|
2489
|
+
return `"${gitBash}" "${toGitBashPath(hookScriptPath)}"`;
|
|
2490
|
+
}
|
|
2491
|
+
function getVSCodeSettingsPaths() {
|
|
2492
|
+
const paths = [];
|
|
2493
|
+
if (process.platform === "darwin") {
|
|
2494
|
+
paths.push({ path: join(homedir(), "Library", "Application Support", "Code", "User", "settings.json"), variant: "VS Code" });
|
|
2495
|
+
paths.push({ path: join(homedir(), "Library", "Application Support", "Code - Insiders", "User", "settings.json"), variant: "VS Code Insiders" });
|
|
2496
|
+
} else if (process.platform === "win32") {
|
|
2497
|
+
const appdata = process.env.APPDATA || join(homedir(), "AppData", "Roaming");
|
|
2498
|
+
paths.push({ path: join(appdata, "Code", "User", "settings.json"), variant: "VS Code" });
|
|
2499
|
+
paths.push({ path: join(appdata, "Code - Insiders", "User", "settings.json"), variant: "VS Code Insiders" });
|
|
2500
|
+
} else {
|
|
2501
|
+
paths.push({ path: join(homedir(), ".config", "Code", "User", "settings.json"), variant: "VS Code" });
|
|
2502
|
+
paths.push({ path: join(homedir(), ".config", "Code - Insiders", "User", "settings.json"), variant: "VS Code Insiders" });
|
|
2503
|
+
}
|
|
2504
|
+
return paths;
|
|
2505
|
+
}
|
|
2506
|
+
function findInstalledVSCodeVariants() {
|
|
2507
|
+
return getVSCodeSettingsPaths().filter(({ path }) => existsSync(dirname(path)));
|
|
2508
|
+
}
|
|
2509
|
+
function stripJsonComments(content) {
|
|
2510
|
+
let result = "";
|
|
2511
|
+
let inString = false;
|
|
2512
|
+
let escaped = false;
|
|
2513
|
+
let i = 0;
|
|
2514
|
+
while (i < content.length) {
|
|
2515
|
+
const ch = content[i];
|
|
2516
|
+
if (escaped) {
|
|
2517
|
+
result += ch;
|
|
2518
|
+
escaped = false;
|
|
2519
|
+
i++;
|
|
2520
|
+
continue;
|
|
2521
|
+
}
|
|
2522
|
+
if (ch === "\\" && inString) {
|
|
2523
|
+
result += ch;
|
|
2524
|
+
escaped = true;
|
|
2525
|
+
i++;
|
|
2526
|
+
continue;
|
|
2527
|
+
}
|
|
2528
|
+
if (ch === '"') {
|
|
2529
|
+
inString = !inString;
|
|
2530
|
+
result += ch;
|
|
2531
|
+
i++;
|
|
2532
|
+
continue;
|
|
2533
|
+
}
|
|
2534
|
+
if (!inString) {
|
|
2535
|
+
if (ch === "/" && content[i + 1] === "/") {
|
|
2536
|
+
while (i < content.length && content[i] !== `
|
|
2537
|
+
`)
|
|
2538
|
+
i++;
|
|
2539
|
+
continue;
|
|
2540
|
+
}
|
|
2541
|
+
if (ch === "/" && content[i + 1] === "*") {
|
|
2542
|
+
i += 2;
|
|
2543
|
+
while (i < content.length && !(content[i] === "*" && content[i + 1] === "/"))
|
|
2544
|
+
i++;
|
|
2545
|
+
i += 2;
|
|
2546
|
+
continue;
|
|
2547
|
+
}
|
|
2548
|
+
}
|
|
2549
|
+
result += ch;
|
|
2550
|
+
i++;
|
|
2551
|
+
}
|
|
2552
|
+
result = result.replace(/,(\s*[}\]])/g, "$1");
|
|
2553
|
+
return result;
|
|
2554
|
+
}
|
|
2555
|
+
function readJsoncConfig(configPath) {
|
|
2556
|
+
if (!existsSync(configPath))
|
|
2557
|
+
return {};
|
|
2558
|
+
try {
|
|
2559
|
+
const content = readFileSync(configPath, "utf-8");
|
|
2560
|
+
const stripped = stripJsonComments(content);
|
|
2561
|
+
return JSON.parse(stripped);
|
|
2562
|
+
} catch {
|
|
2563
|
+
return {};
|
|
2564
|
+
}
|
|
2565
|
+
}
|
|
2566
|
+
function addToVSCodeHookLocations() {
|
|
2567
|
+
const hookLocation = "~/.agentapprove/hooks";
|
|
2568
|
+
const modified = [];
|
|
2569
|
+
for (const { path: settingsPath, variant } of findInstalledVSCodeVariants()) {
|
|
2570
|
+
try {
|
|
2571
|
+
const config = readJsoncConfig(settingsPath);
|
|
2572
|
+
const locations = config["chat.hookFilesLocations"] || {};
|
|
2573
|
+
if (locations[hookLocation] === true) {
|
|
2574
|
+
continue;
|
|
2575
|
+
}
|
|
2576
|
+
locations[hookLocation] = true;
|
|
2577
|
+
config["chat.hookFilesLocations"] = locations;
|
|
2578
|
+
writeJsonConfig(settingsPath, config);
|
|
2579
|
+
modified.push(`${variant}: ${settingsPath}`);
|
|
2580
|
+
} catch {}
|
|
2581
|
+
}
|
|
2582
|
+
return modified;
|
|
2583
|
+
}
|
|
2584
|
+
function removeFromVSCodeHookLocations() {
|
|
2585
|
+
const hookLocation = "~/.agentapprove/hooks";
|
|
2586
|
+
const modified = [];
|
|
2587
|
+
for (const { path: settingsPath, variant } of getVSCodeSettingsPaths()) {
|
|
2588
|
+
if (!existsSync(settingsPath))
|
|
2589
|
+
continue;
|
|
2590
|
+
try {
|
|
2591
|
+
const config = readJsoncConfig(settingsPath);
|
|
2592
|
+
const locations = config["chat.hookFilesLocations"];
|
|
2593
|
+
if (!locations || !(hookLocation in locations))
|
|
2594
|
+
continue;
|
|
2595
|
+
delete locations[hookLocation];
|
|
2596
|
+
if (Object.keys(locations).length === 0) {
|
|
2597
|
+
delete config["chat.hookFilesLocations"];
|
|
2598
|
+
} else {
|
|
2599
|
+
config["chat.hookFilesLocations"] = locations;
|
|
2600
|
+
}
|
|
2601
|
+
writeJsonConfig(settingsPath, config);
|
|
2602
|
+
modified.push(`${variant}: ${settingsPath}`);
|
|
2603
|
+
} catch {}
|
|
2604
|
+
}
|
|
2605
|
+
return modified;
|
|
2606
|
+
}
|
|
2607
|
+
function detectInstalledAgents() {
|
|
2608
|
+
const installed = [];
|
|
2609
|
+
for (const [id, agent] of Object.entries(AGENTS)) {
|
|
2610
|
+
if (id === "vscode-agent") {
|
|
2611
|
+
if (findInstalledVSCodeVariants().length > 0) {
|
|
2612
|
+
installed.push(id);
|
|
2613
|
+
}
|
|
2614
|
+
} else if (id === "copilot-cli") {
|
|
2615
|
+
if (existsSync(join(homedir(), ".copilot"))) {
|
|
2616
|
+
installed.push(id);
|
|
2617
|
+
}
|
|
2618
|
+
} else if (id === "openclaw") {
|
|
2619
|
+
if (existsSync(join(homedir(), ".openclaw"))) {
|
|
2620
|
+
installed.push(id);
|
|
2621
|
+
}
|
|
2622
|
+
} else {
|
|
2623
|
+
const configDir = dirname(agent.configPath);
|
|
2624
|
+
if (existsSync(configDir)) {
|
|
2625
|
+
installed.push(id);
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
}
|
|
2629
|
+
return installed;
|
|
2630
|
+
}
|
|
2631
|
+
function getAgentApproveDir() {
|
|
2632
|
+
return join(homedir(), ".agentapprove");
|
|
2633
|
+
}
|
|
2634
|
+
function readExistingConfig() {
|
|
2635
|
+
const envPath = join(getAgentApproveDir(), "env");
|
|
2636
|
+
if (!existsSync(envPath)) {
|
|
2637
|
+
return null;
|
|
2638
|
+
}
|
|
2639
|
+
try {
|
|
2640
|
+
const content = readFileSync(envPath, "utf-8");
|
|
2641
|
+
const config = {};
|
|
2642
|
+
for (const line of content.split(`
|
|
2643
|
+
`)) {
|
|
2644
|
+
if (line.startsWith("#") || !line.includes("="))
|
|
2645
|
+
continue;
|
|
2646
|
+
const [key, ...valueParts] = line.split("=");
|
|
2647
|
+
const value = valueParts.join("=").trim();
|
|
2648
|
+
switch (key.trim()) {
|
|
2649
|
+
case "AGENTAPPROVE_API":
|
|
2650
|
+
config.apiUrl = value;
|
|
2651
|
+
break;
|
|
2652
|
+
case "AGENTAPPROVE_TOKEN":
|
|
2653
|
+
config.token = value;
|
|
2654
|
+
break;
|
|
2655
|
+
case "AGENTAPPROVE_PRIVACY":
|
|
2656
|
+
config.privacy = value;
|
|
2657
|
+
break;
|
|
2658
|
+
case "AGENTAPPROVE_RETENTION_DAYS":
|
|
2659
|
+
config.retentionDays = value === "forever" ? 365 : parseInt(value, 10);
|
|
2660
|
+
break;
|
|
2661
|
+
case "AGENTAPPROVE_DEBUG_LOG":
|
|
2662
|
+
config.debugLog = value === "true";
|
|
2663
|
+
break;
|
|
2664
|
+
case "AGENTAPPROVE_E2E_MODE":
|
|
2665
|
+
config.e2eMode = value;
|
|
2666
|
+
break;
|
|
2667
|
+
case "AGENTAPPROVE_FAIL_BEHAVIOR":
|
|
2668
|
+
config.failBehavior = value;
|
|
2669
|
+
break;
|
|
2670
|
+
}
|
|
2671
|
+
}
|
|
2672
|
+
return config;
|
|
2673
|
+
} catch {
|
|
2674
|
+
return null;
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
function discoverE2EKeys() {
|
|
2678
|
+
const dir = getAgentApproveDir();
|
|
2679
|
+
if (!existsSync(dir))
|
|
2680
|
+
return [];
|
|
2681
|
+
const keys = [];
|
|
2682
|
+
const currentPath = join(dir, "e2e-key");
|
|
2683
|
+
if (existsSync(currentPath)) {
|
|
2684
|
+
try {
|
|
2685
|
+
const hex = readFileSync(currentPath, "utf-8").trim();
|
|
2686
|
+
if (hex.length === 64) {
|
|
2687
|
+
const keyId = createHash("sha256").update(Buffer.from(hex, "hex")).digest("hex").slice(0, 8);
|
|
2688
|
+
keys.push({ keyId, keyHex: hex, path: currentPath, isCurrent: true, modifiedAt: statSync(currentPath).mtime });
|
|
2689
|
+
}
|
|
2690
|
+
} catch {}
|
|
2691
|
+
}
|
|
2692
|
+
try {
|
|
2693
|
+
const files = readdirSync(dir).filter((f2) => f2.startsWith("e2e-key.") && f2.endsWith(".bak"));
|
|
2694
|
+
for (const file of files) {
|
|
2695
|
+
const fullPath = join(dir, file);
|
|
2696
|
+
try {
|
|
2697
|
+
const hex = readFileSync(fullPath, "utf-8").trim();
|
|
2698
|
+
if (hex.length === 64) {
|
|
2699
|
+
const keyId = createHash("sha256").update(Buffer.from(hex, "hex")).digest("hex").slice(0, 8);
|
|
2700
|
+
keys.push({ keyId, keyHex: hex, path: fullPath, isCurrent: false, modifiedAt: statSync(fullPath).mtime });
|
|
2701
|
+
}
|
|
2702
|
+
} catch {}
|
|
2703
|
+
}
|
|
2704
|
+
} catch {}
|
|
2705
|
+
return keys;
|
|
2706
|
+
}
|
|
2707
|
+
function readRotationConfig() {
|
|
2708
|
+
const configPath = join(getAgentApproveDir(), "e2e-rotation.json");
|
|
2709
|
+
if (!existsSync(configPath))
|
|
2710
|
+
return null;
|
|
2711
|
+
try {
|
|
2712
|
+
return JSON.parse(readFileSync(configPath, "utf-8"));
|
|
2713
|
+
} catch {
|
|
2714
|
+
return null;
|
|
2715
|
+
}
|
|
2716
|
+
}
|
|
2717
|
+
function writeRotationConfig(config) {
|
|
2718
|
+
const configPath = join(getAgentApproveDir(), "e2e-rotation.json");
|
|
2719
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
|
|
2720
|
+
}
|
|
2721
|
+
function ensureAgentApproveDir() {
|
|
2722
|
+
const dir = getAgentApproveDir();
|
|
2723
|
+
if (!existsSync(dir)) {
|
|
2724
|
+
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
2725
|
+
}
|
|
2726
|
+
const hooksDir = join(dir, "hooks");
|
|
2727
|
+
if (!existsSync(hooksDir)) {
|
|
2728
|
+
mkdirSync(hooksDir, { recursive: true, mode: 448 });
|
|
2729
|
+
}
|
|
2730
|
+
}
|
|
2731
|
+
function migrateE2ERootKey() {
|
|
2732
|
+
const dir = getAgentApproveDir();
|
|
2733
|
+
const keyPath = join(dir, "e2e-key");
|
|
2734
|
+
const rootKeyPath = join(dir, "e2e-root-key");
|
|
2735
|
+
const rotationPath = join(dir, "e2e-rotation.json");
|
|
2736
|
+
if (!existsSync(keyPath) || existsSync(rootKeyPath))
|
|
2737
|
+
return;
|
|
2738
|
+
try {
|
|
2739
|
+
const keyHex = readFileSync(keyPath, "utf-8").trim();
|
|
2740
|
+
if (keyHex.length !== 64)
|
|
2741
|
+
return;
|
|
2742
|
+
writeFileSync(rootKeyPath, keyHex, { mode: 384 });
|
|
2743
|
+
if (!existsSync(rotationPath)) {
|
|
2744
|
+
const keyId = createHash("sha256").update(Buffer.from(keyHex, "hex")).digest("hex").slice(0, 8);
|
|
2745
|
+
writeRotationConfig({
|
|
2746
|
+
rootKeyId: keyId,
|
|
2747
|
+
epoch: 0,
|
|
2748
|
+
periodSeconds: 0,
|
|
2749
|
+
startedAt: new Date().toISOString()
|
|
2750
|
+
});
|
|
2751
|
+
}
|
|
2752
|
+
} catch {}
|
|
2753
|
+
}
|
|
2754
|
+
function backupConfig(configPath) {
|
|
2755
|
+
if (!existsSync(configPath)) {
|
|
2756
|
+
return null;
|
|
2757
|
+
}
|
|
2758
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
2759
|
+
const backupPath = `${configPath}.backup.${timestamp}`;
|
|
2760
|
+
copyFileSync(configPath, backupPath);
|
|
2761
|
+
return backupPath;
|
|
2762
|
+
}
|
|
2763
|
+
function readJsonConfig(configPath) {
|
|
2764
|
+
if (!existsSync(configPath)) {
|
|
2765
|
+
return {};
|
|
2766
|
+
}
|
|
2767
|
+
try {
|
|
2768
|
+
const content = readFileSync(configPath, "utf-8");
|
|
2769
|
+
return JSON.parse(content);
|
|
2770
|
+
} catch {
|
|
2771
|
+
return {};
|
|
2772
|
+
}
|
|
2773
|
+
}
|
|
2774
|
+
function writeJsonConfig(configPath, config) {
|
|
2775
|
+
const dir = dirname(configPath);
|
|
2776
|
+
if (!existsSync(dir)) {
|
|
2777
|
+
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
2778
|
+
}
|
|
2779
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + `
|
|
2780
|
+
`);
|
|
2781
|
+
}
|
|
2782
|
+
async function createPairingSession(configuredAgents, e2eKeyId) {
|
|
2783
|
+
try {
|
|
2784
|
+
const machineHostname = hostname();
|
|
2785
|
+
const response = await fetch(`${API_URL}/${API_VERSION}/pair`, {
|
|
2786
|
+
method: "POST",
|
|
2787
|
+
headers: { "Content-Type": "application/json" },
|
|
2788
|
+
body: JSON.stringify({
|
|
2789
|
+
clientInfo: `agentapprove-cli/${VERSION}`,
|
|
2790
|
+
hostname: machineHostname,
|
|
2791
|
+
configuredAgents: configuredAgents?.join(","),
|
|
2792
|
+
...e2eKeyId && { e2eKeyId }
|
|
2793
|
+
})
|
|
2794
|
+
});
|
|
2795
|
+
if (!response.ok) {
|
|
2796
|
+
const text = await response.text();
|
|
2797
|
+
return { sessionCode: "", qrUrl: "", expiresIn: 0, error: `HTTP ${response.status}: ${text.slice(0, 100)}` };
|
|
2798
|
+
}
|
|
2799
|
+
return await response.json();
|
|
2800
|
+
} catch (err) {
|
|
2801
|
+
return { sessionCode: "", qrUrl: "", expiresIn: 0, error: String(err) };
|
|
2802
|
+
}
|
|
2803
|
+
}
|
|
2804
|
+
async function pollPairingSession(sessionCode) {
|
|
2805
|
+
try {
|
|
2806
|
+
const response = await fetch(`${API_URL}/${API_VERSION}/pair/${sessionCode}`);
|
|
2807
|
+
return await response.json();
|
|
2808
|
+
} catch {
|
|
2809
|
+
return { status: "error" };
|
|
2810
|
+
}
|
|
2811
|
+
}
|
|
2812
|
+
function sleep(ms) {
|
|
2813
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2814
|
+
}
|
|
2815
|
+
async function waitForPairing(sessionCode, onProgress, onCancel) {
|
|
2816
|
+
let cancelled = false;
|
|
2817
|
+
const handleKeypress = (key) => {
|
|
2818
|
+
const char = key.toString();
|
|
2819
|
+
if (char === "\x1B") {
|
|
2820
|
+
cancelled = true;
|
|
2821
|
+
onCancel();
|
|
2822
|
+
}
|
|
2823
|
+
};
|
|
2824
|
+
if (process.stdin.isTTY) {
|
|
2825
|
+
process.stdin.setRawMode(true);
|
|
2826
|
+
process.stdin.resume();
|
|
2827
|
+
process.stdin.on("data", handleKeypress);
|
|
2828
|
+
}
|
|
2829
|
+
const cleanup = () => {
|
|
2830
|
+
if (process.stdin.isTTY) {
|
|
2831
|
+
process.stdin.removeListener("data", handleKeypress);
|
|
2832
|
+
process.stdin.setRawMode(false);
|
|
2833
|
+
process.stdin.pause();
|
|
2834
|
+
}
|
|
2835
|
+
};
|
|
2836
|
+
const maxAttempts = 150;
|
|
2837
|
+
for (let i = 0;i < maxAttempts; i++) {
|
|
2838
|
+
if (cancelled) {
|
|
2839
|
+
cleanup();
|
|
2840
|
+
return "cancelled";
|
|
2841
|
+
}
|
|
2842
|
+
const result = await pollPairingSession(sessionCode);
|
|
2843
|
+
if (result.status === "completed" && result.token) {
|
|
2844
|
+
cleanup();
|
|
2845
|
+
return {
|
|
2846
|
+
token: result.token,
|
|
2847
|
+
privacy: result.privacy || "full",
|
|
2848
|
+
email: result.email || "",
|
|
2849
|
+
apiUrl: result.apiUrl || API_URL,
|
|
2850
|
+
e2eServerKey: result.e2eServerKey
|
|
2851
|
+
};
|
|
2852
|
+
}
|
|
2853
|
+
if (result.status === "expired") {
|
|
2854
|
+
cleanup();
|
|
2855
|
+
return null;
|
|
2856
|
+
}
|
|
2857
|
+
if (result.expiresIn) {
|
|
2858
|
+
onProgress(result.expiresIn);
|
|
2859
|
+
}
|
|
2860
|
+
await sleep(2000);
|
|
2861
|
+
}
|
|
2862
|
+
cleanup();
|
|
2863
|
+
return null;
|
|
2864
|
+
}
|
|
2865
|
+
function saveEnvConfig(config) {
|
|
2866
|
+
const envPath = join(getAgentApproveDir(), "env");
|
|
2867
|
+
const configSetAt = config.configSetAt || Math.floor(Date.now() / 1000);
|
|
2868
|
+
const retentionDisplay = `${config.retentionDays ?? 30}`;
|
|
2869
|
+
const e2eEnabled = config.e2eEnabled !== false;
|
|
2870
|
+
const content = `# Agent Approve Configuration
|
|
2871
|
+
# Copyright 2026 Agent Approve LLC - https://agentapprove.com
|
|
2872
|
+
# Created: ${new Date().toISOString()}
|
|
2873
|
+
# CLI Version: ${VERSION}
|
|
2874
|
+
|
|
2875
|
+
AGENTAPPROVE_API=${config.apiUrl}
|
|
2876
|
+
AGENTAPPROVE_TOKEN=${config.token}
|
|
2877
|
+
AGENTAPPROVE_PRIVACY=${config.privacy}
|
|
2878
|
+
|
|
2879
|
+
# Data retention policy (number of days, or "forever" to keep all events)
|
|
2880
|
+
AGENTAPPROVE_RETENTION_DAYS=${retentionDisplay}
|
|
2881
|
+
|
|
2882
|
+
# Config sync timestamp (for bidirectional sync with cloud)
|
|
2883
|
+
AGENTAPPROVE_CONFIG_SET_AT=${configSetAt}
|
|
2884
|
+
|
|
2885
|
+
# Debug logging (set to "true" to enable hook debug log)
|
|
2886
|
+
# Log file: ~/.agentapprove/hook-debug.log (max 5MB, auto-resets)
|
|
2887
|
+
AGENTAPPROVE_DEBUG_LOG=${config.debugLog ? "true" : "false"}
|
|
2888
|
+
|
|
2889
|
+
# E2E encryption mode: "observe" (full E2E, no approval hooks) or "approval" (hybrid E2E)
|
|
2890
|
+
AGENTAPPROVE_E2E_MODE=${config.e2eMode || "approval"}
|
|
2891
|
+
|
|
2892
|
+
# E2E encryption enabled/disabled (synced from server settings)
|
|
2893
|
+
AGENTAPPROVE_E2E_ENABLED=${e2eEnabled ? "true" : "false"}
|
|
2894
|
+
|
|
2895
|
+
# Hook behavior when API is unreachable: "allow" | "deny" | "ask"
|
|
2896
|
+
AGENTAPPROVE_FAIL_BEHAVIOR=${config.failBehavior || "ask"}
|
|
2897
|
+
|
|
2898
|
+
# To change settings, run: npx agentapprove install
|
|
2899
|
+
`;
|
|
2900
|
+
writeFileSync(envPath, content, { mode: 384 });
|
|
2901
|
+
}
|
|
2902
|
+
var KEYCHAIN_SERVICE = "com.agentapprove";
|
|
2903
|
+
var KEYCHAIN_ACCOUNT = "api-token";
|
|
2904
|
+
var CREDENTIAL_TARGET = "AgentApprove/api-token";
|
|
2905
|
+
function isWindows() {
|
|
2906
|
+
return platform() === "win32";
|
|
2907
|
+
}
|
|
2908
|
+
function isKeychainAvailable() {
|
|
2909
|
+
return platform() === "darwin";
|
|
2910
|
+
}
|
|
2911
|
+
function isCredentialManagerAvailable() {
|
|
2912
|
+
if (!isWindows())
|
|
2913
|
+
return false;
|
|
2914
|
+
try {
|
|
2915
|
+
execSync("where cmdkey", { stdio: "ignore" });
|
|
2916
|
+
return true;
|
|
2917
|
+
} catch {
|
|
2918
|
+
return false;
|
|
2919
|
+
}
|
|
2920
|
+
}
|
|
2921
|
+
async function storeTokenInKeychain(token) {
|
|
2922
|
+
if (isKeychainAvailable()) {
|
|
2923
|
+
try {
|
|
2924
|
+
spawnSync("security", [
|
|
2925
|
+
"delete-generic-password",
|
|
2926
|
+
"-s",
|
|
2927
|
+
KEYCHAIN_SERVICE,
|
|
2928
|
+
"-a",
|
|
2929
|
+
KEYCHAIN_ACCOUNT
|
|
2930
|
+
], { stdio: "ignore" });
|
|
2931
|
+
const addResult = spawnSync("security", [
|
|
2932
|
+
"add-generic-password",
|
|
2933
|
+
"-s",
|
|
2934
|
+
KEYCHAIN_SERVICE,
|
|
2935
|
+
"-a",
|
|
2936
|
+
KEYCHAIN_ACCOUNT,
|
|
2937
|
+
"-w",
|
|
2938
|
+
token,
|
|
2939
|
+
"-U"
|
|
2940
|
+
], { stdio: "ignore" });
|
|
2941
|
+
if (addResult.status !== 0 || addResult.error) {
|
|
2942
|
+
throw addResult.error || new Error("security add-generic-password failed");
|
|
2943
|
+
}
|
|
2944
|
+
v2.success("Token stored in macOS Keychain");
|
|
2945
|
+
return true;
|
|
2946
|
+
} catch (err) {
|
|
2947
|
+
v2.warn("Could not store token in Keychain (file fallback will be used)");
|
|
2948
|
+
return false;
|
|
2949
|
+
}
|
|
2950
|
+
}
|
|
2951
|
+
if (isCredentialManagerAvailable()) {
|
|
2952
|
+
try {
|
|
2953
|
+
spawnSync("cmdkey", [`/delete:${CREDENTIAL_TARGET}`], { stdio: "ignore" });
|
|
2954
|
+
const addResult = spawnSync("cmdkey", [
|
|
2955
|
+
`/generic:${CREDENTIAL_TARGET}`,
|
|
2956
|
+
"/user:agentapprove",
|
|
2957
|
+
`/pass:${token}`
|
|
2958
|
+
], { stdio: "ignore" });
|
|
2959
|
+
if (addResult.status !== 0 || addResult.error) {
|
|
2960
|
+
throw addResult.error || new Error("cmdkey add credential failed");
|
|
2961
|
+
}
|
|
2962
|
+
v2.success("Token stored in Windows Credential Manager");
|
|
2963
|
+
return true;
|
|
2964
|
+
} catch (err) {
|
|
2965
|
+
v2.warn("Could not store token in Credential Manager (file fallback will be used)");
|
|
2966
|
+
return false;
|
|
2967
|
+
}
|
|
2968
|
+
}
|
|
2969
|
+
return false;
|
|
2970
|
+
}
|
|
2971
|
+
function deleteTokenFromKeychain() {
|
|
2972
|
+
if (isKeychainAvailable()) {
|
|
2973
|
+
try {
|
|
2974
|
+
execSync(`security delete-generic-password -s "${KEYCHAIN_SERVICE}" -a "${KEYCHAIN_ACCOUNT}"`, {
|
|
2975
|
+
stdio: "ignore"
|
|
2976
|
+
});
|
|
2977
|
+
return true;
|
|
2978
|
+
} catch {
|
|
2979
|
+
return false;
|
|
2980
|
+
}
|
|
2981
|
+
}
|
|
2982
|
+
if (isCredentialManagerAvailable()) {
|
|
2983
|
+
try {
|
|
2984
|
+
execSync(`cmdkey /delete:${CREDENTIAL_TARGET}`, { stdio: "ignore" });
|
|
2985
|
+
return true;
|
|
2986
|
+
} catch {
|
|
2987
|
+
return false;
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
return false;
|
|
2991
|
+
}
|
|
2992
|
+
async function pushConfigToCloud(config) {
|
|
2993
|
+
try {
|
|
2994
|
+
const response = await fetch(`${config.apiUrl}/${API_VERSION}/config`, {
|
|
2995
|
+
method: "PUT",
|
|
2996
|
+
headers: {
|
|
2997
|
+
"Content-Type": "application/json",
|
|
2998
|
+
Authorization: `Bearer ${config.token}`
|
|
2999
|
+
},
|
|
3000
|
+
body: JSON.stringify({
|
|
3001
|
+
privacyTier: config.privacy,
|
|
3002
|
+
retentionDays: config.retentionDays,
|
|
3003
|
+
failBehavior: config.failBehavior,
|
|
3004
|
+
configSetAt: config.configSetAt
|
|
3005
|
+
})
|
|
3006
|
+
});
|
|
3007
|
+
return response.ok;
|
|
3008
|
+
} catch (error) {
|
|
3009
|
+
return false;
|
|
3010
|
+
}
|
|
3011
|
+
}
|
|
3012
|
+
function installHooksForAgent(agentId, hooksDir, mode = "approval") {
|
|
3013
|
+
const agent = AGENTS[agentId];
|
|
3014
|
+
if (!agent) {
|
|
3015
|
+
return { success: false, backupPath: null, hooks: [] };
|
|
3016
|
+
}
|
|
3017
|
+
const backupPath = backupConfig(agent.configPath);
|
|
3018
|
+
const config = readJsonConfig(agent.configPath);
|
|
3019
|
+
if (!config[agent.hooksKey]) {
|
|
3020
|
+
config[agent.hooksKey] = {};
|
|
3021
|
+
}
|
|
3022
|
+
const hooksConfig = config[agent.hooksKey];
|
|
3023
|
+
const installedHooks = [];
|
|
3024
|
+
const hooksToInstall = mode === "observe" ? agent.hooks.filter((h2) => !h2.isApprovalHook) : agent.hooks;
|
|
3025
|
+
for (const hook of hooksToInstall) {
|
|
3026
|
+
const hookPath = join(hooksDir, hook.file);
|
|
3027
|
+
const hookCommand = buildHookCommand(hookPath, agentId);
|
|
3028
|
+
if (agentId === "claude-code") {
|
|
3029
|
+
if (!hooksConfig[hook.name]) {
|
|
3030
|
+
hooksConfig[hook.name] = [];
|
|
3031
|
+
}
|
|
3032
|
+
const hookArray = hooksConfig[hook.name];
|
|
3033
|
+
const hasMatcher = hook.hasMatcher ?? true;
|
|
3034
|
+
const hookTimeout = hook.timeout;
|
|
3035
|
+
const existingIdx = hookArray.findIndex((h2) => h2.hooks?.some((hookScript) => {
|
|
3036
|
+
if (typeof hookScript === "string")
|
|
3037
|
+
return hookScript.includes("agentapprove");
|
|
3038
|
+
if (typeof hookScript === "object" && hookScript.command)
|
|
3039
|
+
return hookScript.command.includes("agentapprove");
|
|
3040
|
+
return false;
|
|
3041
|
+
}));
|
|
3042
|
+
const hookEntry = hasMatcher ? { matcher: "*", hooks: [{ type: "command", command: hookCommand }] } : { hooks: [{ type: "command", command: hookCommand }] };
|
|
3043
|
+
if (hookTimeout) {
|
|
3044
|
+
hookEntry.timeout = hookTimeout;
|
|
3045
|
+
}
|
|
3046
|
+
if (existingIdx >= 0) {
|
|
3047
|
+
hookArray[existingIdx] = hookEntry;
|
|
3048
|
+
} else {
|
|
3049
|
+
hookArray.push(hookEntry);
|
|
3050
|
+
}
|
|
3051
|
+
installedHooks.push(hook.name);
|
|
3052
|
+
} else if (agentId === "cursor") {
|
|
3053
|
+
const existing = hooksConfig[hook.name];
|
|
3054
|
+
const needsTimeout = hook.name === "stop" || hook.name === "subagentStop";
|
|
3055
|
+
const hookEntry = needsTimeout ? { command: hookCommand, timeout: 600, loop_limit: 10 } : { command: hookCommand };
|
|
3056
|
+
if (!existing) {
|
|
3057
|
+
hooksConfig[hook.name] = [hookEntry];
|
|
3058
|
+
} else if (Array.isArray(existing)) {
|
|
3059
|
+
const ourIdx = existing.findIndex((h2) => {
|
|
3060
|
+
if (typeof h2 === "object" && h2 !== null && "command" in h2) {
|
|
3061
|
+
return h2.command.includes("agentapprove");
|
|
3062
|
+
}
|
|
3063
|
+
if (typeof h2 === "string")
|
|
3064
|
+
return h2.includes("agentapprove");
|
|
3065
|
+
return false;
|
|
3066
|
+
});
|
|
3067
|
+
if (ourIdx >= 0) {
|
|
3068
|
+
existing[ourIdx] = hookEntry;
|
|
3069
|
+
} else {
|
|
3070
|
+
existing.push(hookEntry);
|
|
3071
|
+
}
|
|
3072
|
+
hooksConfig[hook.name] = existing.map((h2) => {
|
|
3073
|
+
if (typeof h2 === "string")
|
|
3074
|
+
return { command: h2 };
|
|
3075
|
+
return h2;
|
|
3076
|
+
});
|
|
3077
|
+
} else if (typeof existing === "object" && existing !== null) {
|
|
3078
|
+
if ("command" in existing && existing.command.includes("agentapprove")) {
|
|
3079
|
+
hooksConfig[hook.name] = [hookEntry];
|
|
3080
|
+
} else {
|
|
3081
|
+
hooksConfig[hook.name] = [existing, hookEntry];
|
|
3082
|
+
}
|
|
3083
|
+
} else if (typeof existing === "string") {
|
|
3084
|
+
if (existing.includes("agentapprove")) {
|
|
3085
|
+
hooksConfig[hook.name] = [hookEntry];
|
|
3086
|
+
} else {
|
|
3087
|
+
hooksConfig[hook.name] = [{ command: existing }, hookEntry];
|
|
3088
|
+
}
|
|
3089
|
+
}
|
|
3090
|
+
installedHooks.push(hook.name);
|
|
3091
|
+
} else if (agentId === "gemini-cli") {
|
|
3092
|
+
if (!hooksConfig[hook.name]) {
|
|
3093
|
+
hooksConfig[hook.name] = [];
|
|
3094
|
+
}
|
|
3095
|
+
const hookArray = hooksConfig[hook.name];
|
|
3096
|
+
const cleanedArray = hookArray.filter((h2) => {
|
|
3097
|
+
if (!h2.hooks || !Array.isArray(h2.hooks))
|
|
3098
|
+
return true;
|
|
3099
|
+
return !h2.hooks.some((hookScript) => {
|
|
3100
|
+
if (typeof hookScript === "string")
|
|
3101
|
+
return hookScript.includes("agentapprove");
|
|
3102
|
+
if (typeof hookScript === "object" && hookScript !== null && "command" in hookScript) {
|
|
3103
|
+
return hookScript.command.includes("agentapprove");
|
|
3104
|
+
}
|
|
3105
|
+
return false;
|
|
3106
|
+
});
|
|
3107
|
+
});
|
|
3108
|
+
const hookTimeout = hook.timeout;
|
|
3109
|
+
const hookObject = hookTimeout ? { type: "command", command: hookCommand, timeout: hookTimeout * 1000 } : { type: "command", command: hookCommand };
|
|
3110
|
+
const hookEntry = { hooks: [hookObject] };
|
|
3111
|
+
cleanedArray.push(hookEntry);
|
|
3112
|
+
hooksConfig[hook.name] = cleanedArray;
|
|
3113
|
+
installedHooks.push(hook.name);
|
|
3114
|
+
} else if (agentId === "vscode-agent") {
|
|
3115
|
+
if (!hooksConfig[hook.name]) {
|
|
3116
|
+
hooksConfig[hook.name] = [];
|
|
3117
|
+
}
|
|
3118
|
+
const hookArray = hooksConfig[hook.name];
|
|
3119
|
+
const cleanedArray = hookArray.filter((h2) => {
|
|
3120
|
+
return !(h2.command || "").includes("agentapprove");
|
|
3121
|
+
});
|
|
3122
|
+
const hookEntry = {
|
|
3123
|
+
type: "command",
|
|
3124
|
+
command: hookCommand
|
|
3125
|
+
};
|
|
3126
|
+
if (hook.isApprovalHook) {
|
|
3127
|
+
hookEntry.timeout = 120;
|
|
3128
|
+
}
|
|
3129
|
+
if (hook.timeout) {
|
|
3130
|
+
hookEntry.timeout = hook.timeout;
|
|
3131
|
+
}
|
|
3132
|
+
cleanedArray.push(hookEntry);
|
|
3133
|
+
hooksConfig[hook.name] = cleanedArray;
|
|
3134
|
+
installedHooks.push(hook.name);
|
|
3135
|
+
} else if (agentId === "copilot-cli") {
|
|
3136
|
+
config["version"] = 1;
|
|
3137
|
+
if (!hooksConfig[hook.name]) {
|
|
3138
|
+
hooksConfig[hook.name] = [];
|
|
3139
|
+
}
|
|
3140
|
+
const hookArray = hooksConfig[hook.name];
|
|
3141
|
+
const cleanedArray = hookArray.filter((h2) => {
|
|
3142
|
+
const cmd = h2.bash || h2.command || "";
|
|
3143
|
+
return !cmd.includes("agentapprove");
|
|
3144
|
+
});
|
|
3145
|
+
const hookEntry = {
|
|
3146
|
+
type: "command",
|
|
3147
|
+
bash: isWindows() ? toGitBashPath(hookPath) : hookPath
|
|
3148
|
+
};
|
|
3149
|
+
if (hook.isApprovalHook) {
|
|
3150
|
+
hookEntry.timeoutSec = 120;
|
|
3151
|
+
}
|
|
3152
|
+
cleanedArray.push(hookEntry);
|
|
3153
|
+
hooksConfig[hook.name] = cleanedArray;
|
|
3154
|
+
installedHooks.push(hook.name);
|
|
3155
|
+
} else if (agentId === "openclaw") {
|
|
3156
|
+
const extensionsDir = join(homedir(), ".openclaw", "extensions", "agentapprove");
|
|
3157
|
+
mkdirSync(extensionsDir, { recursive: true });
|
|
3158
|
+
const pluginSrc = join(hooksDir, "openclaw-plugin.js");
|
|
3159
|
+
if (existsSync(pluginSrc)) {
|
|
3160
|
+
copyFileSync(pluginSrc, join(extensionsDir, "index.js"));
|
|
3161
|
+
}
|
|
3162
|
+
const manifest = {
|
|
3163
|
+
id: "agentapprove",
|
|
3164
|
+
name: "Agent Approve",
|
|
3165
|
+
description: "Mobile approval for AI agent tool execution",
|
|
3166
|
+
version: VERSION,
|
|
3167
|
+
homepage: "https://agentapprove.com",
|
|
3168
|
+
configSchema: {
|
|
3169
|
+
type: "object",
|
|
3170
|
+
additionalProperties: false,
|
|
3171
|
+
properties: {
|
|
3172
|
+
apiUrl: { type: "string", default: API_URL },
|
|
3173
|
+
timeout: { type: "number", default: 300 },
|
|
3174
|
+
failBehavior: { type: "string", enum: ["allow", "deny", "ask"], default: "ask" },
|
|
3175
|
+
privacyTier: { type: "string", enum: ["minimal", "summary", "full"], default: "full" },
|
|
3176
|
+
debug: { type: "boolean", default: false }
|
|
3177
|
+
}
|
|
3178
|
+
}
|
|
3179
|
+
};
|
|
3180
|
+
writeFileSync(join(extensionsDir, "openclaw.plugin.json"), JSON.stringify(manifest, null, 2));
|
|
3181
|
+
const pkgJson = {
|
|
3182
|
+
name: "@agentapprove/openclaw",
|
|
3183
|
+
version: VERSION,
|
|
3184
|
+
main: "./index.js",
|
|
3185
|
+
openclaw: { extensions: ["./index.js"] }
|
|
3186
|
+
};
|
|
3187
|
+
writeFileSync(join(extensionsDir, "package.json"), JSON.stringify(pkgJson, null, 2));
|
|
3188
|
+
if (!config.plugins) {
|
|
3189
|
+
config.plugins = {};
|
|
3190
|
+
}
|
|
3191
|
+
const plugins = config.plugins;
|
|
3192
|
+
if (!plugins.entries) {
|
|
3193
|
+
plugins.entries = {};
|
|
3194
|
+
}
|
|
3195
|
+
const entries = plugins.entries;
|
|
3196
|
+
entries.agentapprove = {
|
|
3197
|
+
enabled: mode === "approval",
|
|
3198
|
+
config: {
|
|
3199
|
+
apiUrl: API_URL,
|
|
3200
|
+
timeout: 300,
|
|
3201
|
+
failBehavior: "ask",
|
|
3202
|
+
privacyTier: "full"
|
|
3203
|
+
}
|
|
3204
|
+
};
|
|
3205
|
+
if (!config.hooks) {
|
|
3206
|
+
config.hooks = {};
|
|
3207
|
+
}
|
|
3208
|
+
const hooks = config.hooks;
|
|
3209
|
+
if (!hooks.internal) {
|
|
3210
|
+
hooks.internal = {};
|
|
3211
|
+
}
|
|
3212
|
+
hooks.internal.enabled = true;
|
|
3213
|
+
installedHooks.push("agentapprove");
|
|
3214
|
+
}
|
|
3215
|
+
}
|
|
3216
|
+
if (mode === "observe") {
|
|
3217
|
+
const approvalHooks = agent.hooks.filter((h2) => h2.isApprovalHook);
|
|
3218
|
+
for (const hook of approvalHooks) {
|
|
3219
|
+
if (hooksConfig[hook.name]) {
|
|
3220
|
+
if (agentId === "claude-code" || agentId === "gemini-cli") {
|
|
3221
|
+
const hookArray = hooksConfig[hook.name];
|
|
3222
|
+
if (Array.isArray(hookArray)) {
|
|
3223
|
+
const cleaned = hookArray.filter((h2) => {
|
|
3224
|
+
if (!h2.hooks || !Array.isArray(h2.hooks))
|
|
3225
|
+
return true;
|
|
3226
|
+
return !h2.hooks.some((hs) => {
|
|
3227
|
+
if (typeof hs === "string")
|
|
3228
|
+
return hs.includes("agentapprove");
|
|
3229
|
+
if (typeof hs === "object" && hs !== null && "command" in hs) {
|
|
3230
|
+
return hs.command.includes("agentapprove");
|
|
3231
|
+
}
|
|
3232
|
+
return false;
|
|
3233
|
+
});
|
|
3234
|
+
});
|
|
3235
|
+
if (cleaned.length === 0) {
|
|
3236
|
+
delete hooksConfig[hook.name];
|
|
3237
|
+
} else {
|
|
3238
|
+
hooksConfig[hook.name] = cleaned;
|
|
3239
|
+
}
|
|
3240
|
+
}
|
|
3241
|
+
} else if (agentId === "vscode-agent") {
|
|
3242
|
+
const hookArray = hooksConfig[hook.name];
|
|
3243
|
+
if (Array.isArray(hookArray)) {
|
|
3244
|
+
const cleaned = hookArray.filter((h2) => !(h2.command || "").includes("agentapprove"));
|
|
3245
|
+
if (cleaned.length === 0) {
|
|
3246
|
+
delete hooksConfig[hook.name];
|
|
3247
|
+
} else {
|
|
3248
|
+
hooksConfig[hook.name] = cleaned;
|
|
3249
|
+
}
|
|
3250
|
+
}
|
|
3251
|
+
} else if (agentId === "copilot-cli") {
|
|
3252
|
+
const hookArray = hooksConfig[hook.name];
|
|
3253
|
+
if (Array.isArray(hookArray)) {
|
|
3254
|
+
const cleaned = hookArray.filter((h2) => {
|
|
3255
|
+
const cmd = h2.bash || h2.command || "";
|
|
3256
|
+
return !cmd.includes("agentapprove");
|
|
3257
|
+
});
|
|
3258
|
+
if (cleaned.length === 0) {
|
|
3259
|
+
delete hooksConfig[hook.name];
|
|
3260
|
+
} else {
|
|
3261
|
+
hooksConfig[hook.name] = cleaned;
|
|
3262
|
+
}
|
|
3263
|
+
}
|
|
3264
|
+
} else if (agentId === "cursor") {
|
|
3265
|
+
const existing = hooksConfig[hook.name];
|
|
3266
|
+
if (Array.isArray(existing)) {
|
|
3267
|
+
const cleaned = existing.filter((h2) => {
|
|
3268
|
+
if (typeof h2 === "object" && h2 !== null && "command" in h2) {
|
|
3269
|
+
return !h2.command.includes("agentapprove");
|
|
3270
|
+
}
|
|
3271
|
+
if (typeof h2 === "string")
|
|
3272
|
+
return !h2.includes("agentapprove");
|
|
3273
|
+
return true;
|
|
3274
|
+
});
|
|
3275
|
+
if (cleaned.length === 0) {
|
|
3276
|
+
delete hooksConfig[hook.name];
|
|
3277
|
+
} else {
|
|
3278
|
+
hooksConfig[hook.name] = cleaned;
|
|
3279
|
+
}
|
|
3280
|
+
}
|
|
3281
|
+
} else if (agentId === "openclaw") {
|
|
3282
|
+
const pluginsObj = hooksConfig;
|
|
3283
|
+
const entries = pluginsObj?.entries;
|
|
3284
|
+
if (entries?.agentapprove && typeof entries.agentapprove === "object") {
|
|
3285
|
+
entries.agentapprove.enabled = false;
|
|
3286
|
+
}
|
|
3287
|
+
}
|
|
3288
|
+
}
|
|
3289
|
+
}
|
|
3290
|
+
}
|
|
3291
|
+
writeJsonConfig(agent.configPath, config);
|
|
3292
|
+
return { success: true, backupPath, hooks: installedHooks };
|
|
3293
|
+
}
|
|
3294
|
+
function getManualInstructions(agentId, hooksDir) {
|
|
3295
|
+
const agent = AGENTS[agentId];
|
|
3296
|
+
if (!agent)
|
|
3297
|
+
return "";
|
|
3298
|
+
if (agentId === "claude-code") {
|
|
3299
|
+
const hooksObj = {};
|
|
3300
|
+
for (const hook of agent.hooks) {
|
|
3301
|
+
const hasMatcher = hook.hasMatcher ?? true;
|
|
3302
|
+
const hookTimeout = hook.timeout;
|
|
3303
|
+
const hookPath = join(hooksDir, hook.file);
|
|
3304
|
+
const hookCmd = buildHookCommand(hookPath, agentId);
|
|
3305
|
+
const entry = hasMatcher ? { matcher: "*", hooks: [{ type: "command", command: hookCmd }] } : { hooks: [{ type: "command", command: hookCmd }] };
|
|
3306
|
+
if (hookTimeout) {
|
|
3307
|
+
entry.timeout = hookTimeout;
|
|
3308
|
+
}
|
|
3309
|
+
hooksObj[hook.name] = [entry];
|
|
3310
|
+
}
|
|
3311
|
+
return JSON.stringify({ hooks: hooksObj }, null, 2);
|
|
3312
|
+
} else if (agentId === "cursor") {
|
|
3313
|
+
const hooksObj = {};
|
|
3314
|
+
for (const hook of agent.hooks) {
|
|
3315
|
+
const hookPath = join(hooksDir, hook.file);
|
|
3316
|
+
const hookCmd = buildHookCommand(hookPath, agentId);
|
|
3317
|
+
const needsTimeout = hook.name === "stop" || hook.name === "subagentStop";
|
|
3318
|
+
hooksObj[hook.name] = needsTimeout ? [{ command: hookCmd, timeout: 600, loop_limit: 10 }] : [{ command: hookCmd }];
|
|
3319
|
+
}
|
|
3320
|
+
return JSON.stringify({ hooks: hooksObj }, null, 2);
|
|
3321
|
+
} else if (agentId === "gemini-cli") {
|
|
3322
|
+
const hooksObj = {};
|
|
3323
|
+
for (const hook of agent.hooks) {
|
|
3324
|
+
const hookTimeout = hook.timeout;
|
|
3325
|
+
const hookCmd = buildHookCommand(join(hooksDir, hook.file), agentId);
|
|
3326
|
+
const hookObject = hookTimeout ? { type: "command", command: hookCmd, timeout: hookTimeout * 1000 } : { type: "command", command: hookCmd };
|
|
3327
|
+
hooksObj[hook.name] = [{ hooks: [hookObject] }];
|
|
3328
|
+
}
|
|
3329
|
+
return JSON.stringify({ hooks: hooksObj }, null, 2);
|
|
3330
|
+
} else if (agentId === "vscode-agent") {
|
|
3331
|
+
const hooksObj = {};
|
|
3332
|
+
for (const hook of agent.hooks) {
|
|
3333
|
+
const hookPath = join(hooksDir, hook.file);
|
|
3334
|
+
const hookCmd = buildHookCommand(hookPath, agentId);
|
|
3335
|
+
const isApproval = hook.isApprovalHook;
|
|
3336
|
+
const hookTimeout = hook.timeout;
|
|
3337
|
+
const entry = {
|
|
3338
|
+
type: "command",
|
|
3339
|
+
command: hookCmd
|
|
3340
|
+
};
|
|
3341
|
+
if (isApproval)
|
|
3342
|
+
entry.timeout = 120;
|
|
3343
|
+
if (hookTimeout)
|
|
3344
|
+
entry.timeout = hookTimeout;
|
|
3345
|
+
hooksObj[hook.name] = [entry];
|
|
3346
|
+
}
|
|
3347
|
+
return JSON.stringify({ hooks: hooksObj }, null, 2);
|
|
3348
|
+
} else if (agentId === "copilot-cli") {
|
|
3349
|
+
const hooksObj = {};
|
|
3350
|
+
for (const hook of agent.hooks) {
|
|
3351
|
+
const hookPath = join(hooksDir, hook.file);
|
|
3352
|
+
const isApproval = hook.isApprovalHook;
|
|
3353
|
+
const entry = {
|
|
3354
|
+
type: "command",
|
|
3355
|
+
bash: isWindows() ? toGitBashPath(hookPath) : hookPath
|
|
3356
|
+
};
|
|
3357
|
+
if (isApproval)
|
|
3358
|
+
entry.timeoutSec = 120;
|
|
3359
|
+
hooksObj[hook.name] = [entry];
|
|
3360
|
+
}
|
|
3361
|
+
return JSON.stringify({ version: 1, hooks: hooksObj }, null, 2);
|
|
3362
|
+
} else if (agentId === "openclaw") {
|
|
3363
|
+
return [
|
|
3364
|
+
"# Install the Agent Approve plugin for OpenClaw:",
|
|
3365
|
+
"#",
|
|
3366
|
+
"# Option 1: Via npm (when published)",
|
|
3367
|
+
"# openclaw plugins install @agentapprove/openclaw",
|
|
3368
|
+
"#",
|
|
3369
|
+
"# Option 2: Manual install",
|
|
3370
|
+
"# 1. Copy the plugin bundle to ~/.openclaw/extensions/agentapprove/",
|
|
3371
|
+
"# 2. Add to your OpenClaw config (~/.openclaw/openclaw.json):",
|
|
3372
|
+
JSON.stringify({
|
|
3373
|
+
plugins: {
|
|
3374
|
+
entries: {
|
|
3375
|
+
agentapprove: {
|
|
3376
|
+
enabled: true,
|
|
3377
|
+
config: {
|
|
3378
|
+
apiUrl: API_URL,
|
|
3379
|
+
timeout: 300,
|
|
3380
|
+
failBehavior: "ask",
|
|
3381
|
+
privacyTier: "full"
|
|
3382
|
+
}
|
|
3383
|
+
}
|
|
3384
|
+
}
|
|
3385
|
+
},
|
|
3386
|
+
hooks: {
|
|
3387
|
+
internal: { enabled: true }
|
|
3388
|
+
}
|
|
3389
|
+
}, null, 2),
|
|
3390
|
+
"# 3. Restart the OpenClaw gateway"
|
|
3391
|
+
].join(`
|
|
3392
|
+
`);
|
|
3393
|
+
}
|
|
3394
|
+
return "";
|
|
3395
|
+
}
|
|
3396
|
+
var HOOK_FILES = [
|
|
3397
|
+
"common.sh",
|
|
3398
|
+
"claude-pre-tool.sh",
|
|
3399
|
+
"claude-post-tool.sh",
|
|
3400
|
+
"claude-post-tool-failure.sh",
|
|
3401
|
+
"claude-notification.sh",
|
|
3402
|
+
"claude-user-prompt.sh",
|
|
3403
|
+
"claude-prompt.sh",
|
|
3404
|
+
"claude-session-start.sh",
|
|
3405
|
+
"claude-session-end.sh",
|
|
3406
|
+
"claude-stop.sh",
|
|
3407
|
+
"notify-hook.sh",
|
|
3408
|
+
"completion-hook.sh",
|
|
3409
|
+
"cursor-session-start.sh",
|
|
3410
|
+
"cursor-session-end.sh",
|
|
3411
|
+
"cursor-approval.sh",
|
|
3412
|
+
"cursor-mcp-approval.sh",
|
|
3413
|
+
"cursor-pre-tool.sh",
|
|
3414
|
+
"cursor-shell-complete.sh",
|
|
3415
|
+
"cursor-mcp-complete.sh",
|
|
3416
|
+
"cursor-post-tool.sh",
|
|
3417
|
+
"cursor-start.sh",
|
|
3418
|
+
"cursor-subagent-start.sh",
|
|
3419
|
+
"cursor-subagent-stop.sh",
|
|
3420
|
+
"cursor-precompact.sh",
|
|
3421
|
+
"cursor-stop.sh",
|
|
3422
|
+
"cursor-thought.sh",
|
|
3423
|
+
"cursor-response.sh",
|
|
3424
|
+
"gemini-before-tool.sh",
|
|
3425
|
+
"gemini-after-tool.sh",
|
|
3426
|
+
"gemini-before-agent.sh",
|
|
3427
|
+
"gemini-after-agent.sh",
|
|
3428
|
+
"gemini-before-model.sh",
|
|
3429
|
+
"gemini-after-model.sh",
|
|
3430
|
+
"gemini-notification.sh",
|
|
3431
|
+
"gemini-session-start.sh",
|
|
3432
|
+
"gemini-session-end.sh",
|
|
3433
|
+
"gemini-stop.sh",
|
|
3434
|
+
"github-session-start.sh",
|
|
3435
|
+
"github-session-end.sh",
|
|
3436
|
+
"github-user-prompt.sh",
|
|
3437
|
+
"github-pre-tool.sh",
|
|
3438
|
+
"github-post-tool.sh",
|
|
3439
|
+
"github-error.sh",
|
|
3440
|
+
"github-stop.sh",
|
|
3441
|
+
"github-subagent-start.sh",
|
|
3442
|
+
"github-subagent-stop.sh",
|
|
3443
|
+
"github-precompact.sh",
|
|
3444
|
+
"openclaw-plugin.js"
|
|
3445
|
+
];
|
|
3446
|
+
async function copyHookScripts(hooksDir, token) {
|
|
3447
|
+
let downloaded = 0;
|
|
3448
|
+
const failed = [];
|
|
3449
|
+
for (const file of HOOK_FILES) {
|
|
3450
|
+
try {
|
|
3451
|
+
const response = await fetch(`${API_URL}/${API_VERSION}/hooks/${file}?format=raw`, {
|
|
3452
|
+
headers: {
|
|
3453
|
+
Authorization: `Bearer ${token}`
|
|
3454
|
+
}
|
|
3455
|
+
});
|
|
3456
|
+
if (response.ok) {
|
|
3457
|
+
const content = await response.text();
|
|
3458
|
+
const isShellScript = content.startsWith("#!/") || content.startsWith("# ");
|
|
3459
|
+
const isJsBundle = file.endsWith(".js") && (content.startsWith("//") || content.startsWith("import ") || content.startsWith("var ") || content.startsWith("const ") || content.startsWith("export "));
|
|
3460
|
+
if (isShellScript || isJsBundle) {
|
|
3461
|
+
const filePath = join(hooksDir, file);
|
|
3462
|
+
writeFileSync(filePath, content, { mode: isShellScript ? 493 : 420 });
|
|
3463
|
+
downloaded++;
|
|
3464
|
+
} else {
|
|
3465
|
+
failed.push(`${file} (invalid content)`);
|
|
3466
|
+
}
|
|
3467
|
+
} else {
|
|
3468
|
+
failed.push(`${file} (${response.status})`);
|
|
3469
|
+
}
|
|
3470
|
+
} catch (err) {
|
|
3471
|
+
failed.push(`${file} (${err instanceof Error ? err.message : "network error"})`);
|
|
3472
|
+
}
|
|
3473
|
+
}
|
|
3474
|
+
return { downloaded, failed };
|
|
3475
|
+
}
|
|
3476
|
+
var SYSTEM_DEPS = [
|
|
3477
|
+
{
|
|
3478
|
+
name: "curl",
|
|
3479
|
+
description: "HTTP requests to Agent Approve API",
|
|
3480
|
+
required: true,
|
|
3481
|
+
install: {
|
|
3482
|
+
macos: "brew install curl",
|
|
3483
|
+
apt: "sudo apt-get install -y curl",
|
|
3484
|
+
dnf: "sudo dnf install -y curl",
|
|
3485
|
+
alpine: "apk add curl",
|
|
3486
|
+
windows: "included with Git Bash"
|
|
3487
|
+
}
|
|
3488
|
+
},
|
|
3489
|
+
{
|
|
3490
|
+
name: "jq",
|
|
3491
|
+
description: "JSON parsing in hook scripts",
|
|
3492
|
+
required: true,
|
|
3493
|
+
install: {
|
|
3494
|
+
macos: "brew install jq",
|
|
3495
|
+
apt: "sudo apt-get install -y jq",
|
|
3496
|
+
dnf: "sudo dnf install -y jq",
|
|
3497
|
+
alpine: "apk add jq",
|
|
3498
|
+
windows: "winget install jqlang.jq"
|
|
3499
|
+
}
|
|
3500
|
+
},
|
|
3501
|
+
{
|
|
3502
|
+
name: "openssl",
|
|
3503
|
+
description: "E2E encryption (AES-256, SHA-256, HMAC)",
|
|
3504
|
+
required: true,
|
|
3505
|
+
install: {
|
|
3506
|
+
macos: "brew install openssl",
|
|
3507
|
+
apt: "sudo apt-get install -y openssl",
|
|
3508
|
+
dnf: "sudo dnf install -y openssl",
|
|
3509
|
+
alpine: "apk add openssl",
|
|
3510
|
+
windows: "included with Git Bash"
|
|
3511
|
+
}
|
|
3512
|
+
},
|
|
3513
|
+
{
|
|
3514
|
+
name: "xxd",
|
|
3515
|
+
description: "Hex encoding for E2E encryption",
|
|
3516
|
+
required: true,
|
|
3517
|
+
install: {
|
|
3518
|
+
macos: "brew install vim",
|
|
3519
|
+
apt: "sudo apt-get install -y xxd",
|
|
3520
|
+
dnf: "sudo dnf install -y vim-common",
|
|
3521
|
+
alpine: "apk add vim",
|
|
3522
|
+
windows: "included with Git Bash"
|
|
3523
|
+
}
|
|
3524
|
+
},
|
|
3525
|
+
{
|
|
3526
|
+
name: "base64",
|
|
3527
|
+
description: "Encoding encrypted payloads",
|
|
3528
|
+
required: true,
|
|
3529
|
+
install: {
|
|
3530
|
+
macos: "included with macOS",
|
|
3531
|
+
apt: "sudo apt-get install -y coreutils",
|
|
3532
|
+
dnf: "sudo dnf install -y coreutils",
|
|
3533
|
+
alpine: "apk add coreutils",
|
|
3534
|
+
windows: "included with Git Bash"
|
|
3535
|
+
}
|
|
3536
|
+
}
|
|
3537
|
+
];
|
|
3538
|
+
function checkCommand(name) {
|
|
3539
|
+
try {
|
|
3540
|
+
if (isWindows()) {
|
|
3541
|
+
const gitBashPath = findGitBash();
|
|
3542
|
+
if (gitBashPath) {
|
|
3543
|
+
execSync(`"${gitBashPath}" -c "command -v ${name}"`, { stdio: "ignore" });
|
|
3544
|
+
return true;
|
|
3545
|
+
}
|
|
3546
|
+
execSync(`where ${name}`, { stdio: "ignore" });
|
|
3547
|
+
return true;
|
|
3548
|
+
}
|
|
3549
|
+
execSync(`command -v ${name}`, { stdio: "ignore" });
|
|
3550
|
+
return true;
|
|
3551
|
+
} catch {
|
|
3552
|
+
return false;
|
|
3553
|
+
}
|
|
3554
|
+
}
|
|
3555
|
+
function detectPackageManager() {
|
|
3556
|
+
if (platform() === "darwin")
|
|
3557
|
+
return "macos";
|
|
3558
|
+
if (isWindows())
|
|
3559
|
+
return "windows";
|
|
3560
|
+
try {
|
|
3561
|
+
execSync("command -v apk", { stdio: "ignore" });
|
|
3562
|
+
return "alpine";
|
|
3563
|
+
} catch {}
|
|
3564
|
+
try {
|
|
3565
|
+
execSync("command -v apt-get", { stdio: "ignore" });
|
|
3566
|
+
return "apt";
|
|
3567
|
+
} catch {}
|
|
3568
|
+
try {
|
|
3569
|
+
execSync("command -v dnf", { stdio: "ignore" });
|
|
3570
|
+
return "dnf";
|
|
3571
|
+
} catch {}
|
|
3572
|
+
return "apt";
|
|
3573
|
+
}
|
|
3574
|
+
async function checkSystemDependencies() {
|
|
3575
|
+
if (isWindows()) {
|
|
3576
|
+
const gitBash = findGitBash();
|
|
3577
|
+
if (!gitBash) {
|
|
3578
|
+
me(`Agent Approve hooks require Git Bash (included with Git for Windows).
|
|
3579
|
+
|
|
3580
|
+
` + `Download Git for Windows:
|
|
3581
|
+
` + source_default.cyan("https://git-scm.com/download/win") + `
|
|
3582
|
+
|
|
3583
|
+
` + `Or install via winget:
|
|
3584
|
+
` + source_default.cyan("winget install Git.Git"), "Git Bash Required");
|
|
3585
|
+
const continueWithout = await ce({
|
|
3586
|
+
message: "Continue without Git Bash? (hooks will not work)",
|
|
3587
|
+
initialValue: false
|
|
3588
|
+
});
|
|
3589
|
+
if (lD(continueWithout) || !continueWithout) {
|
|
3590
|
+
he("Install Git for Windows and try again.");
|
|
3591
|
+
process.exit(0);
|
|
3592
|
+
}
|
|
3593
|
+
} else {
|
|
3594
|
+
v2.info(`Git Bash found: ${gitBash}`);
|
|
3595
|
+
}
|
|
3596
|
+
}
|
|
3597
|
+
const missing = [];
|
|
3598
|
+
for (const dep of SYSTEM_DEPS) {
|
|
3599
|
+
if (!checkCommand(dep.name)) {
|
|
3600
|
+
missing.push(dep);
|
|
3601
|
+
}
|
|
3602
|
+
}
|
|
3603
|
+
if (missing.length === 0)
|
|
3604
|
+
return true;
|
|
3605
|
+
const pm = detectPackageManager();
|
|
3606
|
+
const pmLabel = pm === "macos" ? "macOS (Homebrew)" : pm === "windows" ? "Windows" : pm === "apt" ? "apt (Debian/Ubuntu)" : pm === "dnf" ? "dnf (Fedora/RHEL)" : "apk (Alpine)";
|
|
3607
|
+
const requiredMissing = missing.filter((d2) => d2.required);
|
|
3608
|
+
const lines = missing.map((dep) => {
|
|
3609
|
+
const cmd = dep.install[pm];
|
|
3610
|
+
return `${source_default.red("x")} ${source_default.bold(dep.name)} - ${dep.description}
|
|
3611
|
+
Install: ${source_default.cyan(cmd)}`;
|
|
3612
|
+
});
|
|
3613
|
+
me(lines.join(`
|
|
3614
|
+
|
|
3615
|
+
`), `Missing dependencies (${pmLabel})`);
|
|
3616
|
+
if (requiredMissing.length > 0) {
|
|
3617
|
+
const installCommands = new Map;
|
|
3618
|
+
for (const dep of requiredMissing) {
|
|
3619
|
+
const cmd = dep.install[pm];
|
|
3620
|
+
if (!installCommands.has(cmd)) {
|
|
3621
|
+
installCommands.set(cmd, []);
|
|
3622
|
+
}
|
|
3623
|
+
installCommands.get(cmd).push(dep.name);
|
|
3624
|
+
}
|
|
3625
|
+
const uniqueCommands = Array.from(installCommands.keys());
|
|
3626
|
+
const allSamePrefix = uniqueCommands.every((c) => c.startsWith("sudo apt-get") || c.startsWith("sudo dnf") || c.startsWith("apk") || c.startsWith("brew") || c.startsWith("winget"));
|
|
3627
|
+
let combinedCommand = "";
|
|
3628
|
+
if (allSamePrefix && pm === "apt") {
|
|
3629
|
+
combinedCommand = `sudo apt-get install -y ${requiredMissing.map((d2) => d2.name === "base64" ? "coreutils" : d2.name === "xxd" ? "xxd" : d2.name).join(" ")}`;
|
|
3630
|
+
} else if (allSamePrefix && pm === "dnf") {
|
|
3631
|
+
combinedCommand = `sudo dnf install -y ${requiredMissing.map((d2) => d2.name === "base64" ? "coreutils" : d2.name === "xxd" ? "vim-common" : d2.name).join(" ")}`;
|
|
3632
|
+
} else if (allSamePrefix && pm === "alpine") {
|
|
3633
|
+
combinedCommand = `apk add ${requiredMissing.map((d2) => d2.name === "base64" ? "coreutils" : d2.name === "xxd" ? "vim" : d2.name).join(" ")}`;
|
|
3634
|
+
} else if (allSamePrefix && pm === "macos") {
|
|
3635
|
+
const brewPkgs = requiredMissing.filter((d2) => d2.install.macos !== "included with macOS").map((d2) => d2.name === "xxd" ? "vim" : d2.name);
|
|
3636
|
+
if (brewPkgs.length > 0) {
|
|
3637
|
+
combinedCommand = `brew install ${brewPkgs.join(" ")}`;
|
|
3638
|
+
}
|
|
3639
|
+
} else if (pm === "windows") {
|
|
3640
|
+
const winPkgs = requiredMissing.filter((d2) => d2.install.windows !== "included with Git Bash").map((d2) => d2.install.windows);
|
|
3641
|
+
if (winPkgs.length > 0) {
|
|
3642
|
+
combinedCommand = winPkgs.join(" && ");
|
|
3643
|
+
}
|
|
3644
|
+
}
|
|
3645
|
+
const action = await le({
|
|
3646
|
+
message: `${requiredMissing.length} required ${requiredMissing.length === 1 ? "dependency is" : "dependencies are"} missing. Hooks will not work without ${requiredMissing.length === 1 ? "it" : "them"}.`,
|
|
3647
|
+
options: [
|
|
3648
|
+
...combinedCommand ? [{ value: "install", label: "Install now", hint: combinedCommand }] : [],
|
|
3649
|
+
{ value: "continue", label: "Continue anyway", hint: "hooks may not work correctly" },
|
|
3650
|
+
{ value: "exit", label: "Exit and install manually" }
|
|
3651
|
+
]
|
|
3652
|
+
});
|
|
3653
|
+
if (lD(action) || action === "exit") {
|
|
3654
|
+
he("Install the missing dependencies and try again.");
|
|
3655
|
+
process.exit(0);
|
|
3656
|
+
}
|
|
3657
|
+
if (action === "install" && combinedCommand) {
|
|
3658
|
+
const installSpinner = _2();
|
|
3659
|
+
installSpinner.start("Installing dependencies");
|
|
3660
|
+
try {
|
|
3661
|
+
execSync(combinedCommand, { stdio: "pipe" });
|
|
3662
|
+
installSpinner.stop("Dependencies installed");
|
|
3663
|
+
const stillMissing = requiredMissing.filter((d2) => !checkCommand(d2.name));
|
|
3664
|
+
if (stillMissing.length > 0) {
|
|
3665
|
+
v2.warn(`Could not verify: ${stillMissing.map((d2) => d2.name).join(", ")}. Continuing anyway.`);
|
|
3666
|
+
}
|
|
3667
|
+
} catch (err) {
|
|
3668
|
+
installSpinner.stop("Installation failed");
|
|
3669
|
+
v2.error(`Failed to install: ${err instanceof Error ? err.message : "unknown error"}`);
|
|
3670
|
+
v2.info("Please install the dependencies manually and try again.");
|
|
3671
|
+
const continueAnyway = await ce({
|
|
3672
|
+
message: "Continue with installation anyway?",
|
|
3673
|
+
initialValue: false
|
|
3674
|
+
});
|
|
3675
|
+
if (lD(continueAnyway) || !continueAnyway) {
|
|
3676
|
+
process.exit(1);
|
|
3677
|
+
}
|
|
3678
|
+
}
|
|
3679
|
+
}
|
|
3680
|
+
}
|
|
3681
|
+
return true;
|
|
3682
|
+
}
|
|
3683
|
+
async function installCommand() {
|
|
3684
|
+
console.clear();
|
|
3685
|
+
pe(source_default.bgCyan.black(" Agent Approve ") + source_default.gray(` Hooks Installer v${VERSION}`));
|
|
3686
|
+
migrateE2ERootKey();
|
|
3687
|
+
const isCustomApi = !API_URL.includes("agentapprove.com");
|
|
3688
|
+
if (isCustomApi) {
|
|
3689
|
+
v2.warn(`Using custom API: ${API_URL}`);
|
|
3690
|
+
}
|
|
3691
|
+
await checkSystemDependencies();
|
|
3692
|
+
const existingConfig = readExistingConfig();
|
|
3693
|
+
const hasExistingToken = !!(existingConfig?.token && existingConfig.token.length > 10);
|
|
3694
|
+
if (existingConfig) {
|
|
3695
|
+
const tokenPreview = hasExistingToken ? existingConfig.token.slice(0, 15) + "..." : "not set";
|
|
3696
|
+
const e2eKeyPath2 = join(getAgentApproveDir(), "e2e-key");
|
|
3697
|
+
let e2eLine = "";
|
|
3698
|
+
if (existsSync(e2eKeyPath2)) {
|
|
3699
|
+
const keyHex = readFileSync(e2eKeyPath2, "utf-8").trim();
|
|
3700
|
+
const keyId = createHash("sha256").update(Buffer.from(keyHex, "hex")).digest("hex").slice(0, 8);
|
|
3701
|
+
e2eLine = `
|
|
3702
|
+
E2E Key: ${keyId}`;
|
|
3703
|
+
}
|
|
3704
|
+
me(`Token: ${tokenPreview}
|
|
3705
|
+
Privacy: ${existingConfig.privacy || "unknown"}${e2eLine}`, "Existing configuration found");
|
|
3706
|
+
} else {
|
|
3707
|
+
me(`Approve AI agent actions from your iPhone or Apple Watch.
|
|
3708
|
+
Installs hooks for Claude Code, Cursor, Gemini CLI, VS Code, and Copilot CLI.`, "About");
|
|
3709
|
+
}
|
|
3710
|
+
const installedAgents = detectInstalledAgents();
|
|
3711
|
+
const agentOptions = Object.entries(AGENTS).map(([id, agent]) => ({
|
|
3712
|
+
value: id,
|
|
3713
|
+
label: agent.name,
|
|
3714
|
+
hint: installedAgents.includes(id) ? "Detected" : "Not found"
|
|
3715
|
+
}));
|
|
3716
|
+
const defaultPrivacy = existingConfig?.privacy || "full";
|
|
3717
|
+
const defaultDebugLog = existingConfig?.debugLog ?? false;
|
|
3718
|
+
const defaultFailBehavior = existingConfig?.failBehavior || "ask";
|
|
3719
|
+
const setup = await ve({
|
|
3720
|
+
agents: () => $e({
|
|
3721
|
+
message: "Select agents to configure",
|
|
3722
|
+
options: agentOptions,
|
|
3723
|
+
initialValues: installedAgents,
|
|
3724
|
+
required: true
|
|
3725
|
+
}),
|
|
3726
|
+
privacy: () => le({
|
|
3727
|
+
message: "Privacy tier - controls what data is stored in event logs",
|
|
3728
|
+
initialValue: defaultPrivacy,
|
|
3729
|
+
options: [
|
|
3730
|
+
{ value: "full", label: "Full", hint: "Complete command details stored in logs" },
|
|
3731
|
+
{ value: "summary", label: "Summary", hint: "Truncated to 50 chars in logs" },
|
|
3732
|
+
{ value: "minimal", label: "Minimal", hint: "Tool name only in logs, most private" }
|
|
3733
|
+
]
|
|
3734
|
+
}),
|
|
3735
|
+
retention: () => le({
|
|
3736
|
+
message: "Data retention - how long to keep event history",
|
|
3737
|
+
initialValue: "30",
|
|
3738
|
+
options: [
|
|
3739
|
+
{ value: "365", label: "1 Year", hint: "Delete events older than 1 year" },
|
|
3740
|
+
{ value: "90", label: "90 Days", hint: "Delete events older than 90 days" },
|
|
3741
|
+
{ value: "30", label: "30 Days", hint: "Delete events older than 30 days (recommended)" },
|
|
3742
|
+
{ value: "7", label: "1 Week", hint: "Delete events older than 7 days" },
|
|
3743
|
+
{ value: "1", label: "1 Day", hint: "Delete events older than 1 day" }
|
|
3744
|
+
]
|
|
3745
|
+
}),
|
|
3746
|
+
failBehavior: () => le({
|
|
3747
|
+
message: "If Agent Approve is unreachable, hooks should:",
|
|
3748
|
+
initialValue: defaultFailBehavior,
|
|
3749
|
+
options: [
|
|
3750
|
+
{ value: "ask", label: "Ask", hint: "Fall back to the agent's built-in approval dialog (recommended)" },
|
|
3751
|
+
{ value: "deny", label: "Deny", hint: "Block all commands until service is restored" },
|
|
3752
|
+
{ value: "allow", label: "Allow", hint: "Allow all commands to proceed without approval" }
|
|
3753
|
+
]
|
|
3754
|
+
}),
|
|
3755
|
+
debugLog: () => ce({
|
|
3756
|
+
message: "Enable debug logging? (writes to ~/.agentapprove/hook-debug.log)",
|
|
3757
|
+
initialValue: defaultDebugLog
|
|
3758
|
+
}),
|
|
3759
|
+
installMethod: () => le({
|
|
3760
|
+
message: "How would you like to install hooks?",
|
|
3761
|
+
options: [
|
|
3762
|
+
{ value: "auto", label: "Automatic", hint: "Back up configs and install hooks for me" },
|
|
3763
|
+
{ value: "manual", label: "Manual", hint: "Show me what to add (I'll do it myself)" }
|
|
3764
|
+
]
|
|
3765
|
+
})
|
|
3766
|
+
}, {
|
|
3767
|
+
onCancel: () => {
|
|
3768
|
+
he("Installation cancelled");
|
|
3769
|
+
process.exit(0);
|
|
3770
|
+
}
|
|
3771
|
+
});
|
|
3772
|
+
const { agents: selectedAgents, privacy, retention, failBehavior, debugLog, installMethod } = setup;
|
|
3773
|
+
const retentionDays = parseInt(retention, 10) || 30;
|
|
3774
|
+
ensureAgentApproveDir();
|
|
3775
|
+
const hooksDir = join(getAgentApproveDir(), "hooks");
|
|
3776
|
+
const connectionOptions = [];
|
|
3777
|
+
if (hasExistingToken) {
|
|
3778
|
+
const tokenPreview = existingConfig.token.slice(0, 15) + "...";
|
|
3779
|
+
connectionOptions.push({
|
|
3780
|
+
value: "existing",
|
|
3781
|
+
label: "Use existing token",
|
|
3782
|
+
hint: tokenPreview
|
|
3783
|
+
});
|
|
3784
|
+
}
|
|
3785
|
+
connectionOptions.push({ value: "qr", label: "Scan QR code", hint: hasExistingToken ? undefined : "Recommended" });
|
|
3786
|
+
const connectionMethod = await le({
|
|
3787
|
+
message: "Connect to iOS app (required for hook download)",
|
|
3788
|
+
options: connectionOptions
|
|
3789
|
+
});
|
|
3790
|
+
if (lD(connectionMethod)) {
|
|
3791
|
+
he("Installation cancelled");
|
|
3792
|
+
process.exit(0);
|
|
3793
|
+
}
|
|
3794
|
+
let token = null;
|
|
3795
|
+
let finalPrivacy = privacy;
|
|
3796
|
+
let email = "";
|
|
3797
|
+
let apiUrl = API_URL;
|
|
3798
|
+
let useE2E = true;
|
|
3799
|
+
if (connectionMethod === "existing") {
|
|
3800
|
+
token = existingConfig.token;
|
|
3801
|
+
apiUrl = existingConfig.apiUrl || API_URL;
|
|
3802
|
+
const e2eKeyExists = existsSync(join(getAgentApproveDir(), "e2e-key"));
|
|
3803
|
+
useE2E = e2eKeyExists;
|
|
3804
|
+
v2.success("Using existing token");
|
|
3805
|
+
} else if (connectionMethod === "qr") {
|
|
3806
|
+
const skipE2E = hasFlag2("--no-e2e");
|
|
3807
|
+
useE2E = !skipE2E;
|
|
3808
|
+
const agentApproveDir = getAgentApproveDir();
|
|
3809
|
+
const existingKeyPath = join(agentApproveDir, "e2e-key");
|
|
3810
|
+
let e2eUserKey = null;
|
|
3811
|
+
if (useE2E) {
|
|
3812
|
+
if (existsSync(existingKeyPath)) {
|
|
3813
|
+
const oldKeyHex = readFileSync(existingKeyPath, "utf-8").trim();
|
|
3814
|
+
const oldKeyId = createHash("sha256").update(Buffer.from(oldKeyHex, "hex")).digest("hex").slice(0, 8);
|
|
3815
|
+
v2.info(`Existing E2E key found (Key ID: ${oldKeyId})`);
|
|
3816
|
+
const keyAction = await le({
|
|
3817
|
+
message: "Reuse existing encryption key or generate a new one?",
|
|
3818
|
+
options: [
|
|
3819
|
+
{ value: "reuse", label: "Reuse existing key", hint: "recommended — keeps old events decryptable" },
|
|
3820
|
+
{ value: "backup", label: "Back up old key, then generate new" },
|
|
3821
|
+
{ value: "discard", label: "Discard old key and generate new" },
|
|
3822
|
+
{ value: "disable", label: "Disable E2E encryption", hint: "events visible on web dashboard" }
|
|
3823
|
+
]
|
|
3824
|
+
});
|
|
3825
|
+
if (lD(keyAction)) {
|
|
3826
|
+
he("Installation cancelled");
|
|
3827
|
+
process.exit(0);
|
|
3828
|
+
}
|
|
3829
|
+
if (keyAction === "disable") {
|
|
3830
|
+
useE2E = false;
|
|
3831
|
+
} else if (keyAction === "reuse") {
|
|
3832
|
+
e2eUserKey = oldKeyHex;
|
|
3833
|
+
} else {
|
|
3834
|
+
if (keyAction === "backup") {
|
|
3835
|
+
const backupPath = join(agentApproveDir, `e2e-key.${oldKeyId}.bak`);
|
|
3836
|
+
renameSync(existingKeyPath, backupPath);
|
|
3837
|
+
v2.success(`Old key backed up to ${backupPath}`);
|
|
3838
|
+
}
|
|
3839
|
+
e2eUserKey = randomBytes(32).toString("hex");
|
|
3840
|
+
}
|
|
3841
|
+
} else {
|
|
3842
|
+
if (!skipE2E) {
|
|
3843
|
+
const e2eChoice = await ce({
|
|
3844
|
+
message: "Enable end-to-end encryption?",
|
|
3845
|
+
initialValue: true
|
|
3846
|
+
});
|
|
3847
|
+
if (lD(e2eChoice)) {
|
|
3848
|
+
he("Installation cancelled");
|
|
3849
|
+
process.exit(0);
|
|
3850
|
+
}
|
|
3851
|
+
useE2E = e2eChoice;
|
|
3852
|
+
}
|
|
3853
|
+
if (useE2E) {
|
|
3854
|
+
e2eUserKey = randomBytes(32).toString("hex");
|
|
3855
|
+
}
|
|
3856
|
+
}
|
|
3857
|
+
}
|
|
3858
|
+
if (!useE2E) {
|
|
3859
|
+
v2.info("E2E encryption disabled — event content will be visible on the web dashboard");
|
|
3860
|
+
}
|
|
3861
|
+
const e2eKeyId = e2eUserKey ? createHash("sha256").update(Buffer.from(e2eUserKey, "hex")).digest("hex").slice(0, 8) : undefined;
|
|
3862
|
+
const session = await createPairingSession(selectedAgents, e2eKeyId);
|
|
3863
|
+
if (!session || session.error) {
|
|
3864
|
+
v2.error(`Failed to create pairing session: ${session?.error || "Unknown error"}`);
|
|
3865
|
+
he("Cannot continue without token");
|
|
3866
|
+
process.exit(1);
|
|
3867
|
+
} else {
|
|
3868
|
+
const machineHost = hostname();
|
|
3869
|
+
let qrUrlWithE2e = `${session.qrUrl}&host=${encodeURIComponent(machineHost)}`;
|
|
3870
|
+
if (e2eUserKey) {
|
|
3871
|
+
const e2eKeyBase64url = Buffer.from(e2eUserKey, "hex").toString("base64url");
|
|
3872
|
+
qrUrlWithE2e += `&e2eKey=${e2eKeyBase64url}`;
|
|
3873
|
+
}
|
|
3874
|
+
let qrDisplay = "";
|
|
3875
|
+
import_qrcode_terminal.default.generate(qrUrlWithE2e, { small: true }, (qr) => {
|
|
3876
|
+
qrDisplay = qr;
|
|
3877
|
+
});
|
|
3878
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
3879
|
+
me(qrDisplay + `
|
|
3880
|
+
Session: ${session.sessionCode}`, "Scan with Agent Approve iOS app");
|
|
3881
|
+
const pairingSpinner = _2();
|
|
3882
|
+
pairingSpinner.start("Waiting for iOS app...");
|
|
3883
|
+
const result = await waitForPairing(session.sessionCode, (expiresIn) => {
|
|
3884
|
+
const minutes = Math.floor(expiresIn / 60);
|
|
3885
|
+
const seconds = expiresIn % 60;
|
|
3886
|
+
pairingSpinner.message(`Waiting for iOS app... ${minutes}:${seconds.toString().padStart(2, "0")}`);
|
|
3887
|
+
}, () => {});
|
|
3888
|
+
if (result === "cancelled") {
|
|
3889
|
+
pairingSpinner.stop("Cancelled");
|
|
3890
|
+
he("Installation cancelled");
|
|
3891
|
+
process.exit(0);
|
|
3892
|
+
} else if (result) {
|
|
3893
|
+
token = result.token;
|
|
3894
|
+
finalPrivacy = result.privacy;
|
|
3895
|
+
email = result.email;
|
|
3896
|
+
if (e2eUserKey && e2eKeyId) {
|
|
3897
|
+
const agentApproveDir2 = getAgentApproveDir();
|
|
3898
|
+
const rootKeyPath = join(agentApproveDir2, "e2e-root-key");
|
|
3899
|
+
writeFileSync(rootKeyPath, e2eUserKey, { mode: 384 });
|
|
3900
|
+
const userKeyPath = join(agentApproveDir2, "e2e-key");
|
|
3901
|
+
writeFileSync(userKeyPath, e2eUserKey, { mode: 384 });
|
|
3902
|
+
writeRotationConfig({
|
|
3903
|
+
rootKeyId: e2eKeyId,
|
|
3904
|
+
epoch: 0,
|
|
3905
|
+
periodSeconds: 0,
|
|
3906
|
+
startedAt: new Date().toISOString()
|
|
3907
|
+
});
|
|
3908
|
+
if (result.e2eServerKey) {
|
|
3909
|
+
const serverKeyPath = join(agentApproveDir2, "e2e-server-key");
|
|
3910
|
+
writeFileSync(serverKeyPath, result.e2eServerKey, { mode: 384 });
|
|
3911
|
+
}
|
|
3912
|
+
}
|
|
3913
|
+
if (e2eKeyId) {
|
|
3914
|
+
v2.success(`E2E encryption keys saved (Key ID: ${e2eKeyId})`);
|
|
3915
|
+
}
|
|
3916
|
+
const connectedMsg = email ? `Connected! Account: ${email}, Privacy: ${finalPrivacy}` : `Connected! Privacy: ${finalPrivacy}`;
|
|
3917
|
+
pairingSpinner.stop(connectedMsg);
|
|
3918
|
+
} else {
|
|
3919
|
+
pairingSpinner.stop("Session expired");
|
|
3920
|
+
he("Session expired. Please try again.");
|
|
3921
|
+
process.exit(1);
|
|
3922
|
+
}
|
|
3923
|
+
}
|
|
3924
|
+
}
|
|
3925
|
+
if (!token) {
|
|
3926
|
+
he("Cannot continue without token");
|
|
3927
|
+
process.exit(1);
|
|
3928
|
+
}
|
|
3929
|
+
const configSetAt = Math.floor(Date.now() / 1000);
|
|
3930
|
+
saveEnvConfig({
|
|
3931
|
+
apiUrl,
|
|
3932
|
+
token,
|
|
3933
|
+
privacy: finalPrivacy,
|
|
3934
|
+
retentionDays,
|
|
3935
|
+
debugLog,
|
|
3936
|
+
configSetAt,
|
|
3937
|
+
e2eEnabled: useE2E,
|
|
3938
|
+
failBehavior
|
|
3939
|
+
});
|
|
3940
|
+
v2.success("Configuration saved to ~/.agentapprove/env");
|
|
3941
|
+
await storeTokenInKeychain(token);
|
|
3942
|
+
pushConfigToCloud({
|
|
3943
|
+
apiUrl,
|
|
3944
|
+
token,
|
|
3945
|
+
privacy: finalPrivacy,
|
|
3946
|
+
retentionDays,
|
|
3947
|
+
failBehavior,
|
|
3948
|
+
configSetAt
|
|
3949
|
+
}).catch(() => {});
|
|
3950
|
+
const downloadSpinner = _2();
|
|
3951
|
+
downloadSpinner.start("Downloading hook scripts");
|
|
3952
|
+
const downloadResult = await copyHookScripts(hooksDir, token);
|
|
3953
|
+
if (downloadResult.failed.length > 0) {
|
|
3954
|
+
downloadSpinner.stop(`Downloaded ${downloadResult.downloaded} hooks, ${downloadResult.failed.length} failed`);
|
|
3955
|
+
v2.warn(`Failed to download: ${downloadResult.failed.join(", ")}`);
|
|
3956
|
+
} else {
|
|
3957
|
+
downloadSpinner.stop(`Hook scripts downloaded (${downloadResult.downloaded} files)`);
|
|
3958
|
+
}
|
|
3959
|
+
const e2eKeyPath = join(homedir(), ".agentapprove", "e2e-key");
|
|
3960
|
+
const hasE2EKey = existsSync(e2eKeyPath);
|
|
3961
|
+
let installMode = "approval";
|
|
3962
|
+
if (hasE2EKey) {
|
|
3963
|
+
const modeChoice = await le({
|
|
3964
|
+
message: "Choose your security mode:",
|
|
3965
|
+
options: [
|
|
3966
|
+
{
|
|
3967
|
+
value: "approval",
|
|
3968
|
+
label: "Approval Mode (recommended)",
|
|
3969
|
+
hint: "Agent asks permission before running commands. Server evaluates policies via encrypted channel."
|
|
3970
|
+
},
|
|
3971
|
+
{
|
|
3972
|
+
value: "observe",
|
|
3973
|
+
label: "Observe Mode (full E2E)",
|
|
3974
|
+
hint: "Agent runs freely. All events are end-to-end encrypted. No server policy evaluation."
|
|
3975
|
+
}
|
|
3976
|
+
]
|
|
3977
|
+
});
|
|
3978
|
+
if (lD(modeChoice)) {
|
|
3979
|
+
he("Installation cancelled");
|
|
3980
|
+
process.exit(0);
|
|
3981
|
+
}
|
|
3982
|
+
installMode = modeChoice;
|
|
3983
|
+
if (installMode === "observe") {
|
|
3984
|
+
v2.info("Observe mode: All events are E2E encrypted. No approval hooks will be installed.");
|
|
3985
|
+
} else {
|
|
3986
|
+
v2.info("Approval mode: Commands are sent via encrypted channel for policy evaluation.");
|
|
3987
|
+
}
|
|
3988
|
+
const envPath = join(getAgentApproveDir(), "env");
|
|
3989
|
+
if (existsSync(envPath)) {
|
|
3990
|
+
let envContent = readFileSync(envPath, "utf-8");
|
|
3991
|
+
if (envContent.includes("AGENTAPPROVE_E2E_MODE=")) {
|
|
3992
|
+
envContent = envContent.replace(/AGENTAPPROVE_E2E_MODE=\w+/, `AGENTAPPROVE_E2E_MODE=${installMode}`);
|
|
3993
|
+
} else {
|
|
3994
|
+
envContent += `
|
|
3995
|
+
# E2E encryption mode: "observe" (full E2E, no approval hooks) or "approval" (hybrid E2E)
|
|
3996
|
+
AGENTAPPROVE_E2E_MODE=${installMode}
|
|
3997
|
+
`;
|
|
3998
|
+
}
|
|
3999
|
+
writeFileSync(envPath, envContent, { mode: 384 });
|
|
4000
|
+
}
|
|
4001
|
+
}
|
|
4002
|
+
if (installMethod === "auto") {
|
|
4003
|
+
const filesToModify = [];
|
|
4004
|
+
for (const agentId of selectedAgents) {
|
|
4005
|
+
const agent = AGENTS[agentId];
|
|
4006
|
+
filesToModify.push(agent.configPath);
|
|
4007
|
+
if (agentId === "vscode-agent") {
|
|
4008
|
+
const vsCodeVariants = findInstalledVSCodeVariants();
|
|
4009
|
+
for (const { path: settingsPath, variant } of vsCodeVariants) {
|
|
4010
|
+
filesToModify.push(`${settingsPath} (${variant} - register hook path)`);
|
|
4011
|
+
}
|
|
4012
|
+
}
|
|
4013
|
+
}
|
|
4014
|
+
me(`${filesToModify.join(`
|
|
4015
|
+
`)}
|
|
4016
|
+
|
|
4017
|
+
Backups will be created with timestamp`, "Files to be modified");
|
|
4018
|
+
const confirm = await ce({
|
|
4019
|
+
message: "Proceed with automatic installation?"
|
|
4020
|
+
});
|
|
4021
|
+
if (lD(confirm) || !confirm) {
|
|
4022
|
+
he("Installation cancelled");
|
|
4023
|
+
process.exit(0);
|
|
4024
|
+
}
|
|
4025
|
+
for (const agentId of selectedAgents) {
|
|
4026
|
+
const agent = AGENTS[agentId];
|
|
4027
|
+
const spinner = _2();
|
|
4028
|
+
spinner.start(`Configuring ${agent.name}`);
|
|
4029
|
+
const result = installHooksForAgent(agentId, hooksDir, installMode);
|
|
4030
|
+
if (result.success) {
|
|
4031
|
+
const installedHookNames = result.hooks.join(", ");
|
|
4032
|
+
const backupMsg = result.backupPath ? source_default.dim(` (backup created)`) : "";
|
|
4033
|
+
const modeLabel = installMode === "observe" ? source_default.cyan(" [observe]") : source_default.green(" [approval]");
|
|
4034
|
+
spinner.stop(`${agent.name}${modeLabel}: ${installedHookNames}${backupMsg}`);
|
|
4035
|
+
if (agentId === "vscode-agent") {
|
|
4036
|
+
const vsCodeSpinner = _2();
|
|
4037
|
+
vsCodeSpinner.start("Registering hook path in VS Code settings");
|
|
4038
|
+
const modifiedPaths = addToVSCodeHookLocations();
|
|
4039
|
+
if (modifiedPaths.length > 0) {
|
|
4040
|
+
vsCodeSpinner.stop(`Registered in: ${modifiedPaths.join(", ")}`);
|
|
4041
|
+
} else {
|
|
4042
|
+
const variants = findInstalledVSCodeVariants();
|
|
4043
|
+
if (variants.length === 0) {
|
|
4044
|
+
vsCodeSpinner.stop(source_default.yellow("No VS Code installation found - add ~/.agentapprove/hooks manually in VS Code settings"));
|
|
4045
|
+
} else {
|
|
4046
|
+
vsCodeSpinner.stop("Hook path already registered in VS Code settings");
|
|
4047
|
+
}
|
|
4048
|
+
}
|
|
4049
|
+
}
|
|
4050
|
+
if (agentId === "copilot-cli") {
|
|
4051
|
+
v2.info(source_default.dim(` To use with Copilot CLI, copy to your repo:
|
|
4052
|
+
` + ` cp ${agent.configPath} .github/hooks/agentapprove.json`));
|
|
4053
|
+
}
|
|
4054
|
+
} else {
|
|
4055
|
+
spinner.stop(`${agent.name} configuration failed`);
|
|
4056
|
+
}
|
|
4057
|
+
}
|
|
4058
|
+
} else {
|
|
4059
|
+
for (const agentId of selectedAgents) {
|
|
4060
|
+
const agent = AGENTS[agentId];
|
|
4061
|
+
const instructions = getManualInstructions(agentId, hooksDir);
|
|
4062
|
+
me(instructions, `${agent.name} - ${agent.configPath}`);
|
|
4063
|
+
}
|
|
4064
|
+
await ce({
|
|
4065
|
+
message: "Press Enter when done"
|
|
4066
|
+
});
|
|
4067
|
+
}
|
|
4068
|
+
const modeDesc = hasE2EKey ? installMode === "observe" ? source_default.cyan("E2E Observe Mode") + " - All events encrypted, no approval hooks" : source_default.green("E2E Approval Mode") + " - Commands sent via encrypted channel for policy evaluation" : "Standard mode (no E2E keys found)";
|
|
4069
|
+
me(`Mode: ${modeDesc}
|
|
4070
|
+
|
|
4071
|
+
npx agentapprove pair Link a new iOS device
|
|
4072
|
+
npx agentapprove status Show current configuration
|
|
4073
|
+
npx agentapprove disable Temporarily disable hooks
|
|
4074
|
+
npx agentapprove uninstall Remove all hooks and config
|
|
4075
|
+
npx agentapprove restore Restore original configs`, "Useful commands");
|
|
4076
|
+
ge(`${source_default.green("Agent Approve is ready!")} ${source_default.dim("Learn more at")} ${source_default.cyan("agentapprove.com")}`);
|
|
4077
|
+
}
|
|
4078
|
+
async function statusCommand() {
|
|
4079
|
+
console.log(source_default.cyan(`
|
|
4080
|
+
Agent Approve Status
|
|
4081
|
+
`));
|
|
4082
|
+
const envPath = join(getAgentApproveDir(), "env");
|
|
4083
|
+
if (!existsSync(envPath)) {
|
|
4084
|
+
console.log(source_default.yellow(" Not configured. Run `npx agentapprove` to set up.\n"));
|
|
4085
|
+
return;
|
|
4086
|
+
}
|
|
4087
|
+
const envContent = readFileSync(envPath, "utf-8");
|
|
4088
|
+
const lines = envContent.split(`
|
|
4089
|
+
`);
|
|
4090
|
+
for (const line of lines) {
|
|
4091
|
+
if (line.startsWith("AGENTAPPROVE_")) {
|
|
4092
|
+
const [key, value] = line.split("=");
|
|
4093
|
+
const displayKey = key.replace("AGENTAPPROVE_", "").toLowerCase();
|
|
4094
|
+
const displayValue = key.includes("TOKEN") ? value.slice(0, 15) + "..." : value;
|
|
4095
|
+
console.log(` ${source_default.dim(displayKey + ":")} ${displayValue}`);
|
|
4096
|
+
}
|
|
4097
|
+
}
|
|
4098
|
+
const e2eKeyFile = join(getAgentApproveDir(), "e2e-key");
|
|
4099
|
+
const e2eServerKeyFile = join(getAgentApproveDir(), "e2e-server-key");
|
|
4100
|
+
if (existsSync(e2eKeyFile)) {
|
|
4101
|
+
console.log(` ${source_default.green("✓")} E2E user key: installed`);
|
|
4102
|
+
}
|
|
4103
|
+
if (existsSync(e2eServerKeyFile)) {
|
|
4104
|
+
console.log(` ${source_default.green("✓")} E2E server key: installed`);
|
|
4105
|
+
}
|
|
4106
|
+
if (!existsSync(e2eKeyFile) && !existsSync(e2eServerKeyFile)) {
|
|
4107
|
+
console.log(` ${source_default.dim(" E2E encryption: not configured")}`);
|
|
4108
|
+
}
|
|
4109
|
+
console.log();
|
|
4110
|
+
for (const [agentId, agent] of Object.entries(AGENTS)) {
|
|
4111
|
+
if (existsSync(agent.configPath)) {
|
|
4112
|
+
const config = readJsonConfig(agent.configPath);
|
|
4113
|
+
const hooksConfig = config[agent.hooksKey];
|
|
4114
|
+
if (hooksConfig) {
|
|
4115
|
+
const installedHooks = agent.hooks.filter((h2) => {
|
|
4116
|
+
const hookEntry = hooksConfig[h2.name];
|
|
4117
|
+
if (!hookEntry)
|
|
4118
|
+
return false;
|
|
4119
|
+
const str = JSON.stringify(hookEntry);
|
|
4120
|
+
return str.includes("agentapprove");
|
|
4121
|
+
});
|
|
4122
|
+
if (installedHooks.length > 0) {
|
|
4123
|
+
console.log(` ${source_default.green("✓")} ${agent.name}: ${installedHooks.map((h2) => h2.name).join(", ")}`);
|
|
4124
|
+
}
|
|
4125
|
+
}
|
|
4126
|
+
}
|
|
4127
|
+
}
|
|
4128
|
+
console.log();
|
|
4129
|
+
}
|
|
4130
|
+
async function disableCommand() {
|
|
4131
|
+
const envPath = join(getAgentApproveDir(), "env");
|
|
4132
|
+
const disabledPath = join(getAgentApproveDir(), "env.disabled");
|
|
4133
|
+
if (!existsSync(envPath)) {
|
|
4134
|
+
if (existsSync(disabledPath)) {
|
|
4135
|
+
console.log(source_default.yellow(`
|
|
4136
|
+
Already disabled.
|
|
4137
|
+
`));
|
|
4138
|
+
} else {
|
|
4139
|
+
console.log(source_default.yellow("\n Not configured. Run `npx agentapprove` to set up.\n"));
|
|
4140
|
+
}
|
|
4141
|
+
return;
|
|
4142
|
+
}
|
|
4143
|
+
const { renameSync: renameSync2 } = await import("fs");
|
|
4144
|
+
renameSync2(envPath, disabledPath);
|
|
4145
|
+
console.log(source_default.green(`
|
|
4146
|
+
✓ Agent Approve disabled.`));
|
|
4147
|
+
console.log(source_default.dim(" Hooks will no longer call the approval API."));
|
|
4148
|
+
console.log(source_default.dim(" Run `npx agentapprove enable` to re-enable.\n"));
|
|
4149
|
+
}
|
|
4150
|
+
async function enableCommand() {
|
|
4151
|
+
const envPath = join(getAgentApproveDir(), "env");
|
|
4152
|
+
const disabledPath = join(getAgentApproveDir(), "env.disabled");
|
|
4153
|
+
if (existsSync(envPath)) {
|
|
4154
|
+
console.log(source_default.yellow(`
|
|
4155
|
+
Already enabled.
|
|
4156
|
+
`));
|
|
4157
|
+
return;
|
|
4158
|
+
}
|
|
4159
|
+
if (!existsSync(disabledPath)) {
|
|
4160
|
+
console.log(source_default.yellow("\n Not configured. Run `npx agentapprove` to set up.\n"));
|
|
4161
|
+
return;
|
|
4162
|
+
}
|
|
4163
|
+
const { renameSync: renameSync2 } = await import("fs");
|
|
4164
|
+
renameSync2(disabledPath, envPath);
|
|
4165
|
+
console.log(source_default.green(`
|
|
4166
|
+
✓ Agent Approve enabled.
|
|
4167
|
+
`));
|
|
4168
|
+
}
|
|
4169
|
+
async function uninstallCommand() {
|
|
4170
|
+
console.log(source_default.cyan(`
|
|
4171
|
+
Uninstalling Agent Approve hooks...
|
|
4172
|
+
`));
|
|
4173
|
+
for (const [agentId, agent] of Object.entries(AGENTS)) {
|
|
4174
|
+
if (!existsSync(agent.configPath))
|
|
4175
|
+
continue;
|
|
4176
|
+
const config = readJsonConfig(agent.configPath);
|
|
4177
|
+
const hooksConfig = config[agent.hooksKey];
|
|
4178
|
+
if (!hooksConfig)
|
|
4179
|
+
continue;
|
|
4180
|
+
let modified = false;
|
|
4181
|
+
for (const hook of agent.hooks) {
|
|
4182
|
+
const hookEntry = hooksConfig[hook.name];
|
|
4183
|
+
if (!hookEntry)
|
|
4184
|
+
continue;
|
|
4185
|
+
if (Array.isArray(hookEntry)) {
|
|
4186
|
+
const filtered = hookEntry.filter((e2) => {
|
|
4187
|
+
const str = JSON.stringify(e2);
|
|
4188
|
+
return !str.includes("agentapprove");
|
|
4189
|
+
});
|
|
4190
|
+
if (filtered.length !== hookEntry.length) {
|
|
4191
|
+
if (filtered.length === 0) {
|
|
4192
|
+
delete hooksConfig[hook.name];
|
|
4193
|
+
} else {
|
|
4194
|
+
hooksConfig[hook.name] = filtered;
|
|
4195
|
+
}
|
|
4196
|
+
modified = true;
|
|
4197
|
+
}
|
|
4198
|
+
} else if (typeof hookEntry === "string" && hookEntry.includes("agentapprove")) {
|
|
4199
|
+
delete hooksConfig[hook.name];
|
|
4200
|
+
modified = true;
|
|
4201
|
+
}
|
|
4202
|
+
}
|
|
4203
|
+
if (hooksConfig["Prompt"]) {
|
|
4204
|
+
delete hooksConfig["Prompt"];
|
|
4205
|
+
modified = true;
|
|
4206
|
+
}
|
|
4207
|
+
if (modified) {
|
|
4208
|
+
writeJsonConfig(agent.configPath, config);
|
|
4209
|
+
console.log(` ${source_default.green("✓")} Removed hooks from ${agent.name}`);
|
|
4210
|
+
}
|
|
4211
|
+
}
|
|
4212
|
+
const envPath = join(getAgentApproveDir(), "env");
|
|
4213
|
+
if (existsSync(envPath)) {
|
|
4214
|
+
const { unlinkSync } = await import("fs");
|
|
4215
|
+
unlinkSync(envPath);
|
|
4216
|
+
console.log(` ${source_default.green("✓")} Removed configuration`);
|
|
4217
|
+
}
|
|
4218
|
+
if (deleteTokenFromKeychain()) {
|
|
4219
|
+
console.log(` ${source_default.green("✓")} Removed token from credential store`);
|
|
4220
|
+
}
|
|
4221
|
+
const removedVSCode = removeFromVSCodeHookLocations();
|
|
4222
|
+
if (removedVSCode.length > 0) {
|
|
4223
|
+
for (const path of removedVSCode) {
|
|
4224
|
+
console.log(` ${source_default.green("✓")} Removed hook path from ${path}`);
|
|
4225
|
+
}
|
|
4226
|
+
}
|
|
4227
|
+
const hooksDir = join(getAgentApproveDir(), "hooks");
|
|
4228
|
+
const agentApproveDir = getAgentApproveDir();
|
|
4229
|
+
for (const configPath of [
|
|
4230
|
+
join(hooksDir, "vscode-hooks.json"),
|
|
4231
|
+
join(agentApproveDir, "copilot-cli-hooks.json"),
|
|
4232
|
+
join(hooksDir, "copilot-cli-hooks.json")
|
|
4233
|
+
]) {
|
|
4234
|
+
if (existsSync(configPath)) {
|
|
4235
|
+
const { unlinkSync } = await import("fs");
|
|
4236
|
+
unlinkSync(configPath);
|
|
4237
|
+
console.log(` ${source_default.green("✓")} Removed ${basename(configPath)}`);
|
|
4238
|
+
}
|
|
4239
|
+
}
|
|
4240
|
+
console.log(source_default.green(`
|
|
4241
|
+
Agent Approve uninstalled.`));
|
|
4242
|
+
console.log(source_default.dim(" Hook scripts remain in ~/.agentapprove/hooks"));
|
|
4243
|
+
console.log(source_default.dim(` Backups remain as .backup files
|
|
4244
|
+
`));
|
|
4245
|
+
}
|
|
4246
|
+
async function restoreCommand() {
|
|
4247
|
+
console.log(source_default.cyan(`
|
|
4248
|
+
Restoring original configurations...
|
|
4249
|
+
`));
|
|
4250
|
+
const { readdirSync: readdirSync2, copyFileSync: copyFileSync2 } = await import("fs");
|
|
4251
|
+
for (const [agentId, agent] of Object.entries(AGENTS)) {
|
|
4252
|
+
const configDir = dirname(agent.configPath);
|
|
4253
|
+
if (!existsSync(configDir))
|
|
4254
|
+
continue;
|
|
4255
|
+
const files = readdirSync2(configDir);
|
|
4256
|
+
const backups = files.filter((f2) => f2.startsWith(agent.configPath.split("/").pop()) && f2.includes(".backup.")).sort().reverse();
|
|
4257
|
+
if (backups.length > 0) {
|
|
4258
|
+
const latestBackup = join(configDir, backups[0]);
|
|
4259
|
+
copyFileSync2(latestBackup, agent.configPath);
|
|
4260
|
+
console.log(` ${source_default.green("✓")} ${agent.name} restored from ${backups[0]}`);
|
|
4261
|
+
}
|
|
4262
|
+
}
|
|
4263
|
+
console.log();
|
|
4264
|
+
}
|
|
4265
|
+
async function initRepoCommand() {
|
|
4266
|
+
const cliHooksPath = join(getAgentApproveDir(), "copilot-cli-hooks.json");
|
|
4267
|
+
if (!existsSync(cliHooksPath)) {
|
|
4268
|
+
console.log(source_default.yellow(`
|
|
4269
|
+
Copilot CLI hooks not found. Run the installer first with Copilot CLI selected.`));
|
|
4270
|
+
console.log(source_default.dim(` npx agentapprove install
|
|
4271
|
+
`));
|
|
4272
|
+
process.exit(1);
|
|
4273
|
+
}
|
|
4274
|
+
let repoRoot = process.cwd();
|
|
4275
|
+
let found = false;
|
|
4276
|
+
while (repoRoot !== dirname(repoRoot)) {
|
|
4277
|
+
if (existsSync(join(repoRoot, ".git"))) {
|
|
4278
|
+
found = true;
|
|
4279
|
+
break;
|
|
4280
|
+
}
|
|
4281
|
+
repoRoot = dirname(repoRoot);
|
|
4282
|
+
}
|
|
4283
|
+
if (!found) {
|
|
4284
|
+
console.log(source_default.yellow(`
|
|
4285
|
+
Not inside a git repository. Navigate to a repo and try again.
|
|
4286
|
+
`));
|
|
4287
|
+
process.exit(1);
|
|
4288
|
+
}
|
|
4289
|
+
const targetDir = join(repoRoot, ".github", "hooks");
|
|
4290
|
+
const targetFile = join(targetDir, "agentapprove.json");
|
|
4291
|
+
console.log(source_default.cyan(`
|
|
4292
|
+
Installing Copilot CLI hooks to ${targetFile}
|
|
4293
|
+
`));
|
|
4294
|
+
if (existsSync(targetFile)) {
|
|
4295
|
+
console.log(source_default.yellow(" agentapprove.json already exists in .github/hooks/"));
|
|
4296
|
+
const overwrite = await ce({
|
|
4297
|
+
message: "Overwrite existing file?"
|
|
4298
|
+
});
|
|
4299
|
+
if (lD(overwrite) || !overwrite) {
|
|
4300
|
+
console.log(source_default.dim(` Skipped.
|
|
4301
|
+
`));
|
|
4302
|
+
process.exit(0);
|
|
4303
|
+
}
|
|
4304
|
+
}
|
|
4305
|
+
if (!existsSync(targetDir)) {
|
|
4306
|
+
mkdirSync(targetDir, { recursive: true });
|
|
4307
|
+
}
|
|
4308
|
+
copyFileSync(cliHooksPath, targetFile);
|
|
4309
|
+
console.log(source_default.green(` ✓ Copied Copilot CLI hooks to .github/hooks/agentapprove.json`));
|
|
4310
|
+
console.log(source_default.dim(` Commit this file so Copilot coding agent uses your hooks.
|
|
4311
|
+
`));
|
|
4312
|
+
}
|
|
4313
|
+
function helpCommand() {
|
|
4314
|
+
console.log(`
|
|
4315
|
+
${source_default.cyan("Agent Approve")} - Approve AI agent actions from your iPhone or Apple Watch
|
|
4316
|
+
${source_default.dim("Supports macOS, Linux, and Windows (via Git Bash)")}
|
|
4317
|
+
|
|
4318
|
+
${source_default.yellow("Usage:")}
|
|
4319
|
+
npx agentapprove [command] [options]
|
|
4320
|
+
|
|
4321
|
+
${source_default.yellow("Commands:")}
|
|
4322
|
+
${source_default.green("install")} Run the installation wizard (default)
|
|
4323
|
+
${source_default.green("pair")} Link a new iOS device with existing E2E keys
|
|
4324
|
+
${source_default.green("refresh")} Generate a new token (when expired)
|
|
4325
|
+
${source_default.green("init-repo")} Add Copilot CLI hooks to current repo (.github/hooks/)
|
|
4326
|
+
${source_default.green("status")} Show current configuration and installed hooks
|
|
4327
|
+
${source_default.green("disable")} Temporarily disable hooks
|
|
4328
|
+
${source_default.green("enable")} Re-enable hooks after disabling
|
|
4329
|
+
${source_default.green("uninstall")} Remove all hooks from agent configs
|
|
4330
|
+
${source_default.green("restore")} Restore original configs from backups
|
|
4331
|
+
${source_default.green("help")} Show this help message
|
|
4332
|
+
|
|
4333
|
+
${source_default.yellow("Options:")}
|
|
4334
|
+
--no-e2e Skip E2E encryption (events visible on web dashboard)
|
|
4335
|
+
|
|
4336
|
+
${source_default.yellow("Examples:")}
|
|
4337
|
+
npx agentapprove # Run installer
|
|
4338
|
+
npx agentapprove pair # Link a new iOS device
|
|
4339
|
+
npx agentapprove refresh # Get new token
|
|
4340
|
+
npx agentapprove status # Check status
|
|
4341
|
+
|
|
4342
|
+
${source_default.dim("Learn more at")} ${source_default.cyan("https://agentapprove.com")}
|
|
4343
|
+
`);
|
|
4344
|
+
}
|
|
4345
|
+
async function refreshCommand() {
|
|
4346
|
+
console.clear();
|
|
4347
|
+
pe(source_default.bgCyan.black(" Agent Approve ") + source_default.gray(" Token Refresh"));
|
|
4348
|
+
const existingConfig = readExistingConfig();
|
|
4349
|
+
if (!existingConfig) {
|
|
4350
|
+
v2.error('No existing configuration found. Run "npx agentapprove install" first.');
|
|
4351
|
+
process.exit(1);
|
|
4352
|
+
}
|
|
4353
|
+
me(`Your API token has expired (30 days). Tokens extend automatically on use,
|
|
4354
|
+
but if unused for 30 days they expire. Get a new one below.`, "Token Expired");
|
|
4355
|
+
let token = null;
|
|
4356
|
+
let apiUrl = API_URL;
|
|
4357
|
+
const session = await createPairingSession();
|
|
4358
|
+
if (!session || session.error) {
|
|
4359
|
+
v2.error(`Failed to create pairing session: ${session?.error || "Unknown error"}`);
|
|
4360
|
+
process.exit(1);
|
|
4361
|
+
}
|
|
4362
|
+
let qrDisplay = "";
|
|
4363
|
+
import_qrcode_terminal.default.generate(session.qrUrl, { small: true }, (qr) => {
|
|
4364
|
+
qrDisplay = qr;
|
|
4365
|
+
});
|
|
4366
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
4367
|
+
me(qrDisplay + `
|
|
4368
|
+
Session: ${session.sessionCode}`, "Scan with Agent Approve iOS app");
|
|
4369
|
+
const pairingSpinner = _2();
|
|
4370
|
+
pairingSpinner.start("Waiting for iOS app...");
|
|
4371
|
+
const result = await waitForPairing(session.sessionCode, (expiresIn) => {
|
|
4372
|
+
const minutes = Math.floor(expiresIn / 60);
|
|
4373
|
+
const seconds = expiresIn % 60;
|
|
4374
|
+
pairingSpinner.message(`Waiting for iOS app... ${minutes}:${seconds.toString().padStart(2, "0")}`);
|
|
4375
|
+
}, () => {});
|
|
4376
|
+
if (result === "cancelled") {
|
|
4377
|
+
pairingSpinner.stop("Cancelled");
|
|
4378
|
+
he("Refresh cancelled");
|
|
4379
|
+
process.exit(0);
|
|
4380
|
+
} else if (result) {
|
|
4381
|
+
token = result.token;
|
|
4382
|
+
pairingSpinner.stop(`Connected! ${result.email ? `Account: ${result.email}` : ""}`);
|
|
4383
|
+
} else {
|
|
4384
|
+
pairingSpinner.stop("Session expired");
|
|
4385
|
+
v2.error('Session expired. Run "npx agentapprove refresh" to try again.');
|
|
4386
|
+
process.exit(1);
|
|
4387
|
+
}
|
|
4388
|
+
const configSetAt = Math.floor(Date.now() / 1000);
|
|
4389
|
+
const privacy = existingConfig.privacy || "full";
|
|
4390
|
+
const retentionDays = existingConfig.retentionDays ?? 30;
|
|
4391
|
+
const failBehavior = existingConfig.failBehavior || "ask";
|
|
4392
|
+
saveEnvConfig({
|
|
4393
|
+
apiUrl,
|
|
4394
|
+
token,
|
|
4395
|
+
privacy,
|
|
4396
|
+
retentionDays,
|
|
4397
|
+
debugLog: existingConfig.debugLog,
|
|
4398
|
+
failBehavior,
|
|
4399
|
+
configSetAt
|
|
4400
|
+
});
|
|
4401
|
+
pushConfigToCloud({
|
|
4402
|
+
apiUrl,
|
|
4403
|
+
token,
|
|
4404
|
+
privacy,
|
|
4405
|
+
retentionDays,
|
|
4406
|
+
failBehavior,
|
|
4407
|
+
configSetAt
|
|
4408
|
+
}).catch(() => {});
|
|
4409
|
+
const updateSpinner = _2();
|
|
4410
|
+
updateSpinner.start("Updating hook scripts with new token");
|
|
4411
|
+
try {
|
|
4412
|
+
await updateHookScriptsWithToken(token, apiUrl);
|
|
4413
|
+
updateSpinner.stop("Hook scripts updated");
|
|
4414
|
+
} catch (error) {
|
|
4415
|
+
updateSpinner.stop("Warning: Could not update all hook scripts");
|
|
4416
|
+
v2.warn('You may need to run "npx agentapprove install" to reconfigure hooks.');
|
|
4417
|
+
}
|
|
4418
|
+
ge(source_default.green("Token refreshed successfully!"));
|
|
4419
|
+
}
|
|
4420
|
+
async function pairCommand() {
|
|
4421
|
+
console.clear();
|
|
4422
|
+
pe(source_default.bgCyan.black(" Agent Approve ") + source_default.gray(" Device Pairing"));
|
|
4423
|
+
const existingConfig = readExistingConfig();
|
|
4424
|
+
const keys = discoverE2EKeys();
|
|
4425
|
+
if (keys.length === 0 && !existingConfig) {
|
|
4426
|
+
v2.error("No E2E keys or configuration found.");
|
|
4427
|
+
v2.info('Run "npx agentapprove install" to set up Agent Approve first.');
|
|
4428
|
+
process.exit(1);
|
|
4429
|
+
}
|
|
4430
|
+
const keyOptions = [];
|
|
4431
|
+
for (const key of keys) {
|
|
4432
|
+
const dateStr = key.modifiedAt.toLocaleDateString("en-US", { year: "numeric", month: "short", day: "numeric" });
|
|
4433
|
+
if (key.isCurrent) {
|
|
4434
|
+
keyOptions.push({
|
|
4435
|
+
value: key.keyId,
|
|
4436
|
+
label: `Current key (${key.keyId})`,
|
|
4437
|
+
hint: `actively in use — ${dateStr}`
|
|
4438
|
+
});
|
|
4439
|
+
} else {
|
|
4440
|
+
keyOptions.push({
|
|
4441
|
+
value: key.keyId,
|
|
4442
|
+
label: `Backup key (${key.keyId})`,
|
|
4443
|
+
hint: dateStr
|
|
4444
|
+
});
|
|
4445
|
+
}
|
|
4446
|
+
}
|
|
4447
|
+
keyOptions.push({
|
|
4448
|
+
value: "__generate__",
|
|
4449
|
+
label: "Generate a new key",
|
|
4450
|
+
hint: "creates a fresh key for this pairing"
|
|
4451
|
+
});
|
|
4452
|
+
keyOptions.push({
|
|
4453
|
+
value: "__disable__",
|
|
4454
|
+
label: "Disable E2E encryption",
|
|
4455
|
+
hint: "events visible on web dashboard"
|
|
4456
|
+
});
|
|
4457
|
+
const skipE2E = hasFlag2("--no-e2e");
|
|
4458
|
+
const selectedKeyId = skipE2E ? "__disable__" : await le({
|
|
4459
|
+
message: "Select an E2E key to pair with your iOS device",
|
|
4460
|
+
options: keyOptions
|
|
4461
|
+
});
|
|
4462
|
+
if (lD(selectedKeyId)) {
|
|
4463
|
+
he("Pairing cancelled");
|
|
4464
|
+
process.exit(0);
|
|
4465
|
+
}
|
|
4466
|
+
let e2eUserKey = null;
|
|
4467
|
+
let e2eKeyId;
|
|
4468
|
+
if (selectedKeyId === "__disable__") {
|
|
4469
|
+
v2.info("E2E encryption disabled — event content will be visible on the web dashboard");
|
|
4470
|
+
updateEnvValue("AGENTAPPROVE_E2E_ENABLED", "false");
|
|
4471
|
+
} else if (selectedKeyId === "__generate__") {
|
|
4472
|
+
const currentKey = keys.find((k3) => k3.isCurrent);
|
|
4473
|
+
if (currentKey) {
|
|
4474
|
+
const agentApproveDir2 = getAgentApproveDir();
|
|
4475
|
+
const backupPath = join(agentApproveDir2, `e2e-key.${currentKey.keyId}.bak`);
|
|
4476
|
+
renameSync(currentKey.path, backupPath);
|
|
4477
|
+
v2.info(`Previous key backed up (${currentKey.keyId})`);
|
|
4478
|
+
}
|
|
4479
|
+
e2eUserKey = randomBytes(32).toString("hex");
|
|
4480
|
+
e2eKeyId = createHash("sha256").update(Buffer.from(e2eUserKey, "hex")).digest("hex").slice(0, 8);
|
|
4481
|
+
const agentApproveDir = getAgentApproveDir();
|
|
4482
|
+
ensureAgentApproveDir();
|
|
4483
|
+
writeFileSync(join(agentApproveDir, "e2e-root-key"), e2eUserKey, { mode: 384 });
|
|
4484
|
+
writeFileSync(join(agentApproveDir, "e2e-key"), e2eUserKey, { mode: 384 });
|
|
4485
|
+
writeRotationConfig({
|
|
4486
|
+
rootKeyId: e2eKeyId,
|
|
4487
|
+
epoch: 0,
|
|
4488
|
+
periodSeconds: 0,
|
|
4489
|
+
startedAt: new Date().toISOString()
|
|
4490
|
+
});
|
|
4491
|
+
v2.success(`New key generated (${e2eKeyId})`);
|
|
4492
|
+
} else {
|
|
4493
|
+
const selectedKey = keys.find((k3) => k3.keyId === selectedKeyId);
|
|
4494
|
+
if (!selectedKey) {
|
|
4495
|
+
v2.error("Selected key not found.");
|
|
4496
|
+
process.exit(1);
|
|
4497
|
+
}
|
|
4498
|
+
e2eUserKey = selectedKey.keyHex;
|
|
4499
|
+
e2eKeyId = selectedKey.keyId;
|
|
4500
|
+
v2.info(`Using key ${e2eKeyId}${selectedKey.isCurrent ? " (current)" : " (backup)"}`);
|
|
4501
|
+
}
|
|
4502
|
+
if (e2eUserKey) {
|
|
4503
|
+
updateEnvValue("AGENTAPPROVE_E2E_ENABLED", "true");
|
|
4504
|
+
}
|
|
4505
|
+
const installedAgents = detectInstalledAgents();
|
|
4506
|
+
const session = await createPairingSession(installedAgents.length > 0 ? installedAgents : undefined, e2eKeyId);
|
|
4507
|
+
if (!session || session.error) {
|
|
4508
|
+
v2.error(`Failed to create pairing session: ${session?.error || "Unknown error"}`);
|
|
4509
|
+
process.exit(1);
|
|
4510
|
+
}
|
|
4511
|
+
const machineHost = hostname();
|
|
4512
|
+
let qrUrlWithE2e = `${session.qrUrl}&host=${encodeURIComponent(machineHost)}`;
|
|
4513
|
+
if (e2eUserKey) {
|
|
4514
|
+
const e2eKeyBase64url = Buffer.from(e2eUserKey, "hex").toString("base64url");
|
|
4515
|
+
qrUrlWithE2e += `&e2eKey=${e2eKeyBase64url}`;
|
|
4516
|
+
}
|
|
4517
|
+
let qrDisplay = "";
|
|
4518
|
+
import_qrcode_terminal.default.generate(qrUrlWithE2e, { small: true }, (qr) => {
|
|
4519
|
+
qrDisplay = qr;
|
|
4520
|
+
});
|
|
4521
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
4522
|
+
const noteLabel = e2eKeyId ? `Session: ${session.sessionCode}
|
|
4523
|
+
Key ID: ${e2eKeyId}` : `Session: ${session.sessionCode}`;
|
|
4524
|
+
me(qrDisplay + `
|
|
4525
|
+
${noteLabel}`, "Scan with Agent Approve iOS app");
|
|
4526
|
+
const pairingSpinner = _2();
|
|
4527
|
+
pairingSpinner.start("Waiting for iOS app...");
|
|
4528
|
+
const result = await waitForPairing(session.sessionCode, (expiresIn) => {
|
|
4529
|
+
const minutes = Math.floor(expiresIn / 60);
|
|
4530
|
+
const seconds = expiresIn % 60;
|
|
4531
|
+
pairingSpinner.message(`Waiting for iOS app... ${minutes}:${seconds.toString().padStart(2, "0")}`);
|
|
4532
|
+
}, () => {});
|
|
4533
|
+
if (result === "cancelled") {
|
|
4534
|
+
pairingSpinner.stop("Cancelled");
|
|
4535
|
+
he("Pairing cancelled");
|
|
4536
|
+
process.exit(0);
|
|
4537
|
+
}
|
|
4538
|
+
if (!result) {
|
|
4539
|
+
pairingSpinner.stop("Session expired");
|
|
4540
|
+
v2.error('Session expired. Run "npx agentapprove pair" to try again.');
|
|
4541
|
+
process.exit(1);
|
|
4542
|
+
}
|
|
4543
|
+
pairingSpinner.stop(`Paired! ${result.email ? `Account: ${result.email}` : ""}`);
|
|
4544
|
+
if (e2eUserKey && e2eKeyId) {
|
|
4545
|
+
const rootKeyPath = join(getAgentApproveDir(), "e2e-root-key");
|
|
4546
|
+
if (!existsSync(rootKeyPath) || readFileSync(rootKeyPath, "utf-8").trim() !== e2eUserKey) {
|
|
4547
|
+
writeFileSync(rootKeyPath, e2eUserKey, { mode: 384 });
|
|
4548
|
+
}
|
|
4549
|
+
if (!readRotationConfig()) {
|
|
4550
|
+
writeRotationConfig({
|
|
4551
|
+
rootKeyId: e2eKeyId,
|
|
4552
|
+
epoch: 0,
|
|
4553
|
+
periodSeconds: 0,
|
|
4554
|
+
startedAt: new Date().toISOString()
|
|
4555
|
+
});
|
|
4556
|
+
}
|
|
4557
|
+
if (result.e2eServerKey) {
|
|
4558
|
+
const serverKeyPath = join(getAgentApproveDir(), "e2e-server-key");
|
|
4559
|
+
writeFileSync(serverKeyPath, result.e2eServerKey, { mode: 384 });
|
|
4560
|
+
}
|
|
4561
|
+
}
|
|
4562
|
+
const apiUrl = API_URL;
|
|
4563
|
+
const configSetAt = Math.floor(Date.now() / 1000);
|
|
4564
|
+
saveEnvConfig({
|
|
4565
|
+
apiUrl,
|
|
4566
|
+
token: result.token,
|
|
4567
|
+
privacy: existingConfig?.privacy || result.privacy || "full",
|
|
4568
|
+
retentionDays: existingConfig?.retentionDays ?? 30,
|
|
4569
|
+
debugLog: existingConfig?.debugLog,
|
|
4570
|
+
failBehavior: existingConfig?.failBehavior || "ask",
|
|
4571
|
+
configSetAt
|
|
4572
|
+
});
|
|
4573
|
+
const hooksDir = join(getAgentApproveDir(), "hooks");
|
|
4574
|
+
if (existsSync(hooksDir)) {
|
|
4575
|
+
const updateSpinner = _2();
|
|
4576
|
+
updateSpinner.start("Updating hook scripts with new token");
|
|
4577
|
+
try {
|
|
4578
|
+
await updateHookScriptsWithToken(result.token, apiUrl);
|
|
4579
|
+
updateSpinner.stop("Hook scripts updated");
|
|
4580
|
+
} catch {
|
|
4581
|
+
updateSpinner.stop("Warning: Could not update all hook scripts");
|
|
4582
|
+
v2.warn('You may need to run "npx agentapprove install" to reconfigure hooks.');
|
|
4583
|
+
}
|
|
4584
|
+
}
|
|
4585
|
+
pushConfigToCloud({
|
|
4586
|
+
apiUrl,
|
|
4587
|
+
token: result.token,
|
|
4588
|
+
privacy: existingConfig?.privacy || result.privacy || "full",
|
|
4589
|
+
retentionDays: existingConfig?.retentionDays ?? 30,
|
|
4590
|
+
configSetAt
|
|
4591
|
+
}).catch(() => {});
|
|
4592
|
+
ge(source_default.green(`Device paired with key ${e2eKeyId}`));
|
|
4593
|
+
}
|
|
4594
|
+
async function updateHookScriptsWithToken(token, apiUrl) {
|
|
4595
|
+
const hooksDir = join(getAgentApproveDir(), "hooks");
|
|
4596
|
+
if (!existsSync(hooksDir)) {
|
|
4597
|
+
throw new Error("Hooks directory not found");
|
|
4598
|
+
}
|
|
4599
|
+
const hookFiles = [
|
|
4600
|
+
"claude-pre-tool.sh",
|
|
4601
|
+
"claude-post-tool.sh",
|
|
4602
|
+
"claude-prompt.sh",
|
|
4603
|
+
"claude-session-start.sh",
|
|
4604
|
+
"claude-session-end.sh",
|
|
4605
|
+
"completion-hook.sh",
|
|
4606
|
+
"notify-hook.sh",
|
|
4607
|
+
"cursor-approval.sh",
|
|
4608
|
+
"cursor-mcp-approval.sh",
|
|
4609
|
+
"cursor-shell-complete.sh",
|
|
4610
|
+
"cursor-start.sh",
|
|
4611
|
+
"cursor-stop.sh",
|
|
4612
|
+
"cursor-thought.sh",
|
|
4613
|
+
"cursor-response.sh",
|
|
4614
|
+
"gemini-approval.sh",
|
|
4615
|
+
"gemini-complete.sh"
|
|
4616
|
+
];
|
|
4617
|
+
for (const file of hookFiles) {
|
|
4618
|
+
const filePath = join(hooksDir, file);
|
|
4619
|
+
if (existsSync(filePath)) {
|
|
4620
|
+
let content = readFileSync(filePath, "utf-8");
|
|
4621
|
+
content = content.replace(/export AGENTAPPROVE_TOKEN=".*"/, `export AGENTAPPROVE_TOKEN="${token}"`);
|
|
4622
|
+
content = content.replace(/export AGENTAPPROVE_API=".*"/, `export AGENTAPPROVE_API="${apiUrl}"`);
|
|
4623
|
+
writeFileSync(filePath, content, "utf-8");
|
|
4624
|
+
}
|
|
4625
|
+
}
|
|
4626
|
+
}
|
|
4627
|
+
async function main() {
|
|
4628
|
+
const command = getCommand();
|
|
4629
|
+
switch (command) {
|
|
4630
|
+
case "install":
|
|
4631
|
+
await installCommand();
|
|
4632
|
+
break;
|
|
4633
|
+
case "refresh":
|
|
4634
|
+
await refreshCommand();
|
|
4635
|
+
break;
|
|
4636
|
+
case "pair":
|
|
4637
|
+
await pairCommand();
|
|
4638
|
+
break;
|
|
4639
|
+
case "status":
|
|
4640
|
+
await statusCommand();
|
|
4641
|
+
break;
|
|
4642
|
+
case "disable":
|
|
4643
|
+
await disableCommand();
|
|
4644
|
+
break;
|
|
4645
|
+
case "enable":
|
|
4646
|
+
await enableCommand();
|
|
4647
|
+
break;
|
|
4648
|
+
case "uninstall":
|
|
4649
|
+
await uninstallCommand();
|
|
4650
|
+
break;
|
|
4651
|
+
case "restore":
|
|
4652
|
+
await restoreCommand();
|
|
4653
|
+
break;
|
|
4654
|
+
case "init-repo":
|
|
4655
|
+
await initRepoCommand();
|
|
4656
|
+
break;
|
|
4657
|
+
case "help":
|
|
4658
|
+
case "--help":
|
|
4659
|
+
case "-h":
|
|
4660
|
+
helpCommand();
|
|
4661
|
+
break;
|
|
4662
|
+
default:
|
|
4663
|
+
console.log(source_default.red(`
|
|
4664
|
+
Unknown command: ${command}`));
|
|
4665
|
+
console.log(source_default.dim(` Run 'npx agentapprove help' for usage.
|
|
4666
|
+
`));
|
|
4667
|
+
process.exit(1);
|
|
4668
|
+
}
|
|
4669
|
+
}
|
|
4670
|
+
main().then(() => {
|
|
4671
|
+
process.exit(0);
|
|
4672
|
+
}).catch((error) => {
|
|
4673
|
+
console.error(source_default.red("Error:"), error.message);
|
|
4674
|
+
process.exit(1);
|
|
4675
|
+
});
|