weacpx 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 +21 -0
- package/README.md +261 -0
- package/config.example.json +30 -0
- package/dist/bridge/bridge-main.js +229 -0
- package/dist/cli.js +3224 -0
- package/package.json +54 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,3224 @@
|
|
|
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 __export = (target, all) => {
|
|
21
|
+
for (var name in all)
|
|
22
|
+
__defProp(target, name, {
|
|
23
|
+
get: all[name],
|
|
24
|
+
enumerable: true,
|
|
25
|
+
configurable: true,
|
|
26
|
+
set: (newValue) => all[name] = () => newValue
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
30
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
31
|
+
|
|
32
|
+
// node_modules/qrcode-terminal/vendor/QRCode/QRMode.js
|
|
33
|
+
var require_QRMode = __commonJS((exports, module) => {
|
|
34
|
+
module.exports = {
|
|
35
|
+
MODE_NUMBER: 1 << 0,
|
|
36
|
+
MODE_ALPHA_NUM: 1 << 1,
|
|
37
|
+
MODE_8BIT_BYTE: 1 << 2,
|
|
38
|
+
MODE_KANJI: 1 << 3
|
|
39
|
+
};
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// node_modules/qrcode-terminal/vendor/QRCode/QR8bitByte.js
|
|
43
|
+
var require_QR8bitByte = __commonJS((exports, module) => {
|
|
44
|
+
var QRMode = require_QRMode();
|
|
45
|
+
function QR8bitByte(data) {
|
|
46
|
+
this.mode = QRMode.MODE_8BIT_BYTE;
|
|
47
|
+
this.data = data;
|
|
48
|
+
}
|
|
49
|
+
QR8bitByte.prototype = {
|
|
50
|
+
getLength: function() {
|
|
51
|
+
return this.data.length;
|
|
52
|
+
},
|
|
53
|
+
write: function(buffer) {
|
|
54
|
+
for (var i = 0;i < this.data.length; i++) {
|
|
55
|
+
buffer.put(this.data.charCodeAt(i), 8);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
module.exports = QR8bitByte;
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// node_modules/qrcode-terminal/vendor/QRCode/QRMath.js
|
|
63
|
+
var require_QRMath = __commonJS((exports, module) => {
|
|
64
|
+
var QRMath = {
|
|
65
|
+
glog: function(n) {
|
|
66
|
+
if (n < 1) {
|
|
67
|
+
throw new Error("glog(" + n + ")");
|
|
68
|
+
}
|
|
69
|
+
return QRMath.LOG_TABLE[n];
|
|
70
|
+
},
|
|
71
|
+
gexp: function(n) {
|
|
72
|
+
while (n < 0) {
|
|
73
|
+
n += 255;
|
|
74
|
+
}
|
|
75
|
+
while (n >= 256) {
|
|
76
|
+
n -= 255;
|
|
77
|
+
}
|
|
78
|
+
return QRMath.EXP_TABLE[n];
|
|
79
|
+
},
|
|
80
|
+
EXP_TABLE: new Array(256),
|
|
81
|
+
LOG_TABLE: new Array(256)
|
|
82
|
+
};
|
|
83
|
+
for (i = 0;i < 8; i++) {
|
|
84
|
+
QRMath.EXP_TABLE[i] = 1 << i;
|
|
85
|
+
}
|
|
86
|
+
var i;
|
|
87
|
+
for (i = 8;i < 256; i++) {
|
|
88
|
+
QRMath.EXP_TABLE[i] = QRMath.EXP_TABLE[i - 4] ^ QRMath.EXP_TABLE[i - 5] ^ QRMath.EXP_TABLE[i - 6] ^ QRMath.EXP_TABLE[i - 8];
|
|
89
|
+
}
|
|
90
|
+
var i;
|
|
91
|
+
for (i = 0;i < 255; i++) {
|
|
92
|
+
QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]] = i;
|
|
93
|
+
}
|
|
94
|
+
var i;
|
|
95
|
+
module.exports = QRMath;
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// node_modules/qrcode-terminal/vendor/QRCode/QRPolynomial.js
|
|
99
|
+
var require_QRPolynomial = __commonJS((exports, module) => {
|
|
100
|
+
var QRMath = require_QRMath();
|
|
101
|
+
function QRPolynomial(num, shift) {
|
|
102
|
+
if (num.length === undefined) {
|
|
103
|
+
throw new Error(num.length + "/" + shift);
|
|
104
|
+
}
|
|
105
|
+
var offset = 0;
|
|
106
|
+
while (offset < num.length && num[offset] === 0) {
|
|
107
|
+
offset++;
|
|
108
|
+
}
|
|
109
|
+
this.num = new Array(num.length - offset + shift);
|
|
110
|
+
for (var i = 0;i < num.length - offset; i++) {
|
|
111
|
+
this.num[i] = num[i + offset];
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
QRPolynomial.prototype = {
|
|
115
|
+
get: function(index) {
|
|
116
|
+
return this.num[index];
|
|
117
|
+
},
|
|
118
|
+
getLength: function() {
|
|
119
|
+
return this.num.length;
|
|
120
|
+
},
|
|
121
|
+
multiply: function(e) {
|
|
122
|
+
var num = new Array(this.getLength() + e.getLength() - 1);
|
|
123
|
+
for (var i = 0;i < this.getLength(); i++) {
|
|
124
|
+
for (var j = 0;j < e.getLength(); j++) {
|
|
125
|
+
num[i + j] ^= QRMath.gexp(QRMath.glog(this.get(i)) + QRMath.glog(e.get(j)));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return new QRPolynomial(num, 0);
|
|
129
|
+
},
|
|
130
|
+
mod: function(e) {
|
|
131
|
+
if (this.getLength() - e.getLength() < 0) {
|
|
132
|
+
return this;
|
|
133
|
+
}
|
|
134
|
+
var ratio = QRMath.glog(this.get(0)) - QRMath.glog(e.get(0));
|
|
135
|
+
var num = new Array(this.getLength());
|
|
136
|
+
for (var i = 0;i < this.getLength(); i++) {
|
|
137
|
+
num[i] = this.get(i);
|
|
138
|
+
}
|
|
139
|
+
for (var x = 0;x < e.getLength(); x++) {
|
|
140
|
+
num[x] ^= QRMath.gexp(QRMath.glog(e.get(x)) + ratio);
|
|
141
|
+
}
|
|
142
|
+
return new QRPolynomial(num, 0).mod(e);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
module.exports = QRPolynomial;
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// node_modules/qrcode-terminal/vendor/QRCode/QRMaskPattern.js
|
|
149
|
+
var require_QRMaskPattern = __commonJS((exports, module) => {
|
|
150
|
+
module.exports = {
|
|
151
|
+
PATTERN000: 0,
|
|
152
|
+
PATTERN001: 1,
|
|
153
|
+
PATTERN010: 2,
|
|
154
|
+
PATTERN011: 3,
|
|
155
|
+
PATTERN100: 4,
|
|
156
|
+
PATTERN101: 5,
|
|
157
|
+
PATTERN110: 6,
|
|
158
|
+
PATTERN111: 7
|
|
159
|
+
};
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// node_modules/qrcode-terminal/vendor/QRCode/QRUtil.js
|
|
163
|
+
var require_QRUtil = __commonJS((exports, module) => {
|
|
164
|
+
var QRMode = require_QRMode();
|
|
165
|
+
var QRPolynomial = require_QRPolynomial();
|
|
166
|
+
var QRMath = require_QRMath();
|
|
167
|
+
var QRMaskPattern = require_QRMaskPattern();
|
|
168
|
+
var QRUtil = {
|
|
169
|
+
PATTERN_POSITION_TABLE: [
|
|
170
|
+
[],
|
|
171
|
+
[6, 18],
|
|
172
|
+
[6, 22],
|
|
173
|
+
[6, 26],
|
|
174
|
+
[6, 30],
|
|
175
|
+
[6, 34],
|
|
176
|
+
[6, 22, 38],
|
|
177
|
+
[6, 24, 42],
|
|
178
|
+
[6, 26, 46],
|
|
179
|
+
[6, 28, 50],
|
|
180
|
+
[6, 30, 54],
|
|
181
|
+
[6, 32, 58],
|
|
182
|
+
[6, 34, 62],
|
|
183
|
+
[6, 26, 46, 66],
|
|
184
|
+
[6, 26, 48, 70],
|
|
185
|
+
[6, 26, 50, 74],
|
|
186
|
+
[6, 30, 54, 78],
|
|
187
|
+
[6, 30, 56, 82],
|
|
188
|
+
[6, 30, 58, 86],
|
|
189
|
+
[6, 34, 62, 90],
|
|
190
|
+
[6, 28, 50, 72, 94],
|
|
191
|
+
[6, 26, 50, 74, 98],
|
|
192
|
+
[6, 30, 54, 78, 102],
|
|
193
|
+
[6, 28, 54, 80, 106],
|
|
194
|
+
[6, 32, 58, 84, 110],
|
|
195
|
+
[6, 30, 58, 86, 114],
|
|
196
|
+
[6, 34, 62, 90, 118],
|
|
197
|
+
[6, 26, 50, 74, 98, 122],
|
|
198
|
+
[6, 30, 54, 78, 102, 126],
|
|
199
|
+
[6, 26, 52, 78, 104, 130],
|
|
200
|
+
[6, 30, 56, 82, 108, 134],
|
|
201
|
+
[6, 34, 60, 86, 112, 138],
|
|
202
|
+
[6, 30, 58, 86, 114, 142],
|
|
203
|
+
[6, 34, 62, 90, 118, 146],
|
|
204
|
+
[6, 30, 54, 78, 102, 126, 150],
|
|
205
|
+
[6, 24, 50, 76, 102, 128, 154],
|
|
206
|
+
[6, 28, 54, 80, 106, 132, 158],
|
|
207
|
+
[6, 32, 58, 84, 110, 136, 162],
|
|
208
|
+
[6, 26, 54, 82, 110, 138, 166],
|
|
209
|
+
[6, 30, 58, 86, 114, 142, 170]
|
|
210
|
+
],
|
|
211
|
+
G15: 1 << 10 | 1 << 8 | 1 << 5 | 1 << 4 | 1 << 2 | 1 << 1 | 1 << 0,
|
|
212
|
+
G18: 1 << 12 | 1 << 11 | 1 << 10 | 1 << 9 | 1 << 8 | 1 << 5 | 1 << 2 | 1 << 0,
|
|
213
|
+
G15_MASK: 1 << 14 | 1 << 12 | 1 << 10 | 1 << 4 | 1 << 1,
|
|
214
|
+
getBCHTypeInfo: function(data) {
|
|
215
|
+
var d = data << 10;
|
|
216
|
+
while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15) >= 0) {
|
|
217
|
+
d ^= QRUtil.G15 << QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15);
|
|
218
|
+
}
|
|
219
|
+
return (data << 10 | d) ^ QRUtil.G15_MASK;
|
|
220
|
+
},
|
|
221
|
+
getBCHTypeNumber: function(data) {
|
|
222
|
+
var d = data << 12;
|
|
223
|
+
while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18) >= 0) {
|
|
224
|
+
d ^= QRUtil.G18 << QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18);
|
|
225
|
+
}
|
|
226
|
+
return data << 12 | d;
|
|
227
|
+
},
|
|
228
|
+
getBCHDigit: function(data) {
|
|
229
|
+
var digit = 0;
|
|
230
|
+
while (data !== 0) {
|
|
231
|
+
digit++;
|
|
232
|
+
data >>>= 1;
|
|
233
|
+
}
|
|
234
|
+
return digit;
|
|
235
|
+
},
|
|
236
|
+
getPatternPosition: function(typeNumber) {
|
|
237
|
+
return QRUtil.PATTERN_POSITION_TABLE[typeNumber - 1];
|
|
238
|
+
},
|
|
239
|
+
getMask: function(maskPattern, i, j) {
|
|
240
|
+
switch (maskPattern) {
|
|
241
|
+
case QRMaskPattern.PATTERN000:
|
|
242
|
+
return (i + j) % 2 === 0;
|
|
243
|
+
case QRMaskPattern.PATTERN001:
|
|
244
|
+
return i % 2 === 0;
|
|
245
|
+
case QRMaskPattern.PATTERN010:
|
|
246
|
+
return j % 3 === 0;
|
|
247
|
+
case QRMaskPattern.PATTERN011:
|
|
248
|
+
return (i + j) % 3 === 0;
|
|
249
|
+
case QRMaskPattern.PATTERN100:
|
|
250
|
+
return (Math.floor(i / 2) + Math.floor(j / 3)) % 2 === 0;
|
|
251
|
+
case QRMaskPattern.PATTERN101:
|
|
252
|
+
return i * j % 2 + i * j % 3 === 0;
|
|
253
|
+
case QRMaskPattern.PATTERN110:
|
|
254
|
+
return (i * j % 2 + i * j % 3) % 2 === 0;
|
|
255
|
+
case QRMaskPattern.PATTERN111:
|
|
256
|
+
return (i * j % 3 + (i + j) % 2) % 2 === 0;
|
|
257
|
+
default:
|
|
258
|
+
throw new Error("bad maskPattern:" + maskPattern);
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
getErrorCorrectPolynomial: function(errorCorrectLength) {
|
|
262
|
+
var a = new QRPolynomial([1], 0);
|
|
263
|
+
for (var i = 0;i < errorCorrectLength; i++) {
|
|
264
|
+
a = a.multiply(new QRPolynomial([1, QRMath.gexp(i)], 0));
|
|
265
|
+
}
|
|
266
|
+
return a;
|
|
267
|
+
},
|
|
268
|
+
getLengthInBits: function(mode, type) {
|
|
269
|
+
if (1 <= type && type < 10) {
|
|
270
|
+
switch (mode) {
|
|
271
|
+
case QRMode.MODE_NUMBER:
|
|
272
|
+
return 10;
|
|
273
|
+
case QRMode.MODE_ALPHA_NUM:
|
|
274
|
+
return 9;
|
|
275
|
+
case QRMode.MODE_8BIT_BYTE:
|
|
276
|
+
return 8;
|
|
277
|
+
case QRMode.MODE_KANJI:
|
|
278
|
+
return 8;
|
|
279
|
+
default:
|
|
280
|
+
throw new Error("mode:" + mode);
|
|
281
|
+
}
|
|
282
|
+
} else if (type < 27) {
|
|
283
|
+
switch (mode) {
|
|
284
|
+
case QRMode.MODE_NUMBER:
|
|
285
|
+
return 12;
|
|
286
|
+
case QRMode.MODE_ALPHA_NUM:
|
|
287
|
+
return 11;
|
|
288
|
+
case QRMode.MODE_8BIT_BYTE:
|
|
289
|
+
return 16;
|
|
290
|
+
case QRMode.MODE_KANJI:
|
|
291
|
+
return 10;
|
|
292
|
+
default:
|
|
293
|
+
throw new Error("mode:" + mode);
|
|
294
|
+
}
|
|
295
|
+
} else if (type < 41) {
|
|
296
|
+
switch (mode) {
|
|
297
|
+
case QRMode.MODE_NUMBER:
|
|
298
|
+
return 14;
|
|
299
|
+
case QRMode.MODE_ALPHA_NUM:
|
|
300
|
+
return 13;
|
|
301
|
+
case QRMode.MODE_8BIT_BYTE:
|
|
302
|
+
return 16;
|
|
303
|
+
case QRMode.MODE_KANJI:
|
|
304
|
+
return 12;
|
|
305
|
+
default:
|
|
306
|
+
throw new Error("mode:" + mode);
|
|
307
|
+
}
|
|
308
|
+
} else {
|
|
309
|
+
throw new Error("type:" + type);
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
getLostPoint: function(qrCode) {
|
|
313
|
+
var moduleCount = qrCode.getModuleCount();
|
|
314
|
+
var lostPoint = 0;
|
|
315
|
+
var row = 0;
|
|
316
|
+
var col = 0;
|
|
317
|
+
for (row = 0;row < moduleCount; row++) {
|
|
318
|
+
for (col = 0;col < moduleCount; col++) {
|
|
319
|
+
var sameCount = 0;
|
|
320
|
+
var dark = qrCode.isDark(row, col);
|
|
321
|
+
for (var r = -1;r <= 1; r++) {
|
|
322
|
+
if (row + r < 0 || moduleCount <= row + r) {
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
for (var c = -1;c <= 1; c++) {
|
|
326
|
+
if (col + c < 0 || moduleCount <= col + c) {
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
if (r === 0 && c === 0) {
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
if (dark === qrCode.isDark(row + r, col + c)) {
|
|
333
|
+
sameCount++;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
if (sameCount > 5) {
|
|
338
|
+
lostPoint += 3 + sameCount - 5;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
for (row = 0;row < moduleCount - 1; row++) {
|
|
343
|
+
for (col = 0;col < moduleCount - 1; col++) {
|
|
344
|
+
var count = 0;
|
|
345
|
+
if (qrCode.isDark(row, col))
|
|
346
|
+
count++;
|
|
347
|
+
if (qrCode.isDark(row + 1, col))
|
|
348
|
+
count++;
|
|
349
|
+
if (qrCode.isDark(row, col + 1))
|
|
350
|
+
count++;
|
|
351
|
+
if (qrCode.isDark(row + 1, col + 1))
|
|
352
|
+
count++;
|
|
353
|
+
if (count === 0 || count === 4) {
|
|
354
|
+
lostPoint += 3;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
for (row = 0;row < moduleCount; row++) {
|
|
359
|
+
for (col = 0;col < moduleCount - 6; col++) {
|
|
360
|
+
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)) {
|
|
361
|
+
lostPoint += 40;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
for (col = 0;col < moduleCount; col++) {
|
|
366
|
+
for (row = 0;row < moduleCount - 6; row++) {
|
|
367
|
+
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)) {
|
|
368
|
+
lostPoint += 40;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
var darkCount = 0;
|
|
373
|
+
for (col = 0;col < moduleCount; col++) {
|
|
374
|
+
for (row = 0;row < moduleCount; row++) {
|
|
375
|
+
if (qrCode.isDark(row, col)) {
|
|
376
|
+
darkCount++;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5;
|
|
381
|
+
lostPoint += ratio * 10;
|
|
382
|
+
return lostPoint;
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
module.exports = QRUtil;
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
// node_modules/qrcode-terminal/vendor/QRCode/QRErrorCorrectLevel.js
|
|
389
|
+
var require_QRErrorCorrectLevel = __commonJS((exports, module) => {
|
|
390
|
+
module.exports = {
|
|
391
|
+
L: 1,
|
|
392
|
+
M: 0,
|
|
393
|
+
Q: 3,
|
|
394
|
+
H: 2
|
|
395
|
+
};
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
// node_modules/qrcode-terminal/vendor/QRCode/QRRSBlock.js
|
|
399
|
+
var require_QRRSBlock = __commonJS((exports, module) => {
|
|
400
|
+
var QRErrorCorrectLevel = require_QRErrorCorrectLevel();
|
|
401
|
+
function QRRSBlock(totalCount, dataCount) {
|
|
402
|
+
this.totalCount = totalCount;
|
|
403
|
+
this.dataCount = dataCount;
|
|
404
|
+
}
|
|
405
|
+
QRRSBlock.RS_BLOCK_TABLE = [
|
|
406
|
+
[1, 26, 19],
|
|
407
|
+
[1, 26, 16],
|
|
408
|
+
[1, 26, 13],
|
|
409
|
+
[1, 26, 9],
|
|
410
|
+
[1, 44, 34],
|
|
411
|
+
[1, 44, 28],
|
|
412
|
+
[1, 44, 22],
|
|
413
|
+
[1, 44, 16],
|
|
414
|
+
[1, 70, 55],
|
|
415
|
+
[1, 70, 44],
|
|
416
|
+
[2, 35, 17],
|
|
417
|
+
[2, 35, 13],
|
|
418
|
+
[1, 100, 80],
|
|
419
|
+
[2, 50, 32],
|
|
420
|
+
[2, 50, 24],
|
|
421
|
+
[4, 25, 9],
|
|
422
|
+
[1, 134, 108],
|
|
423
|
+
[2, 67, 43],
|
|
424
|
+
[2, 33, 15, 2, 34, 16],
|
|
425
|
+
[2, 33, 11, 2, 34, 12],
|
|
426
|
+
[2, 86, 68],
|
|
427
|
+
[4, 43, 27],
|
|
428
|
+
[4, 43, 19],
|
|
429
|
+
[4, 43, 15],
|
|
430
|
+
[2, 98, 78],
|
|
431
|
+
[4, 49, 31],
|
|
432
|
+
[2, 32, 14, 4, 33, 15],
|
|
433
|
+
[4, 39, 13, 1, 40, 14],
|
|
434
|
+
[2, 121, 97],
|
|
435
|
+
[2, 60, 38, 2, 61, 39],
|
|
436
|
+
[4, 40, 18, 2, 41, 19],
|
|
437
|
+
[4, 40, 14, 2, 41, 15],
|
|
438
|
+
[2, 146, 116],
|
|
439
|
+
[3, 58, 36, 2, 59, 37],
|
|
440
|
+
[4, 36, 16, 4, 37, 17],
|
|
441
|
+
[4, 36, 12, 4, 37, 13],
|
|
442
|
+
[2, 86, 68, 2, 87, 69],
|
|
443
|
+
[4, 69, 43, 1, 70, 44],
|
|
444
|
+
[6, 43, 19, 2, 44, 20],
|
|
445
|
+
[6, 43, 15, 2, 44, 16],
|
|
446
|
+
[4, 101, 81],
|
|
447
|
+
[1, 80, 50, 4, 81, 51],
|
|
448
|
+
[4, 50, 22, 4, 51, 23],
|
|
449
|
+
[3, 36, 12, 8, 37, 13],
|
|
450
|
+
[2, 116, 92, 2, 117, 93],
|
|
451
|
+
[6, 58, 36, 2, 59, 37],
|
|
452
|
+
[4, 46, 20, 6, 47, 21],
|
|
453
|
+
[7, 42, 14, 4, 43, 15],
|
|
454
|
+
[4, 133, 107],
|
|
455
|
+
[8, 59, 37, 1, 60, 38],
|
|
456
|
+
[8, 44, 20, 4, 45, 21],
|
|
457
|
+
[12, 33, 11, 4, 34, 12],
|
|
458
|
+
[3, 145, 115, 1, 146, 116],
|
|
459
|
+
[4, 64, 40, 5, 65, 41],
|
|
460
|
+
[11, 36, 16, 5, 37, 17],
|
|
461
|
+
[11, 36, 12, 5, 37, 13],
|
|
462
|
+
[5, 109, 87, 1, 110, 88],
|
|
463
|
+
[5, 65, 41, 5, 66, 42],
|
|
464
|
+
[5, 54, 24, 7, 55, 25],
|
|
465
|
+
[11, 36, 12],
|
|
466
|
+
[5, 122, 98, 1, 123, 99],
|
|
467
|
+
[7, 73, 45, 3, 74, 46],
|
|
468
|
+
[15, 43, 19, 2, 44, 20],
|
|
469
|
+
[3, 45, 15, 13, 46, 16],
|
|
470
|
+
[1, 135, 107, 5, 136, 108],
|
|
471
|
+
[10, 74, 46, 1, 75, 47],
|
|
472
|
+
[1, 50, 22, 15, 51, 23],
|
|
473
|
+
[2, 42, 14, 17, 43, 15],
|
|
474
|
+
[5, 150, 120, 1, 151, 121],
|
|
475
|
+
[9, 69, 43, 4, 70, 44],
|
|
476
|
+
[17, 50, 22, 1, 51, 23],
|
|
477
|
+
[2, 42, 14, 19, 43, 15],
|
|
478
|
+
[3, 141, 113, 4, 142, 114],
|
|
479
|
+
[3, 70, 44, 11, 71, 45],
|
|
480
|
+
[17, 47, 21, 4, 48, 22],
|
|
481
|
+
[9, 39, 13, 16, 40, 14],
|
|
482
|
+
[3, 135, 107, 5, 136, 108],
|
|
483
|
+
[3, 67, 41, 13, 68, 42],
|
|
484
|
+
[15, 54, 24, 5, 55, 25],
|
|
485
|
+
[15, 43, 15, 10, 44, 16],
|
|
486
|
+
[4, 144, 116, 4, 145, 117],
|
|
487
|
+
[17, 68, 42],
|
|
488
|
+
[17, 50, 22, 6, 51, 23],
|
|
489
|
+
[19, 46, 16, 6, 47, 17],
|
|
490
|
+
[2, 139, 111, 7, 140, 112],
|
|
491
|
+
[17, 74, 46],
|
|
492
|
+
[7, 54, 24, 16, 55, 25],
|
|
493
|
+
[34, 37, 13],
|
|
494
|
+
[4, 151, 121, 5, 152, 122],
|
|
495
|
+
[4, 75, 47, 14, 76, 48],
|
|
496
|
+
[11, 54, 24, 14, 55, 25],
|
|
497
|
+
[16, 45, 15, 14, 46, 16],
|
|
498
|
+
[6, 147, 117, 4, 148, 118],
|
|
499
|
+
[6, 73, 45, 14, 74, 46],
|
|
500
|
+
[11, 54, 24, 16, 55, 25],
|
|
501
|
+
[30, 46, 16, 2, 47, 17],
|
|
502
|
+
[8, 132, 106, 4, 133, 107],
|
|
503
|
+
[8, 75, 47, 13, 76, 48],
|
|
504
|
+
[7, 54, 24, 22, 55, 25],
|
|
505
|
+
[22, 45, 15, 13, 46, 16],
|
|
506
|
+
[10, 142, 114, 2, 143, 115],
|
|
507
|
+
[19, 74, 46, 4, 75, 47],
|
|
508
|
+
[28, 50, 22, 6, 51, 23],
|
|
509
|
+
[33, 46, 16, 4, 47, 17],
|
|
510
|
+
[8, 152, 122, 4, 153, 123],
|
|
511
|
+
[22, 73, 45, 3, 74, 46],
|
|
512
|
+
[8, 53, 23, 26, 54, 24],
|
|
513
|
+
[12, 45, 15, 28, 46, 16],
|
|
514
|
+
[3, 147, 117, 10, 148, 118],
|
|
515
|
+
[3, 73, 45, 23, 74, 46],
|
|
516
|
+
[4, 54, 24, 31, 55, 25],
|
|
517
|
+
[11, 45, 15, 31, 46, 16],
|
|
518
|
+
[7, 146, 116, 7, 147, 117],
|
|
519
|
+
[21, 73, 45, 7, 74, 46],
|
|
520
|
+
[1, 53, 23, 37, 54, 24],
|
|
521
|
+
[19, 45, 15, 26, 46, 16],
|
|
522
|
+
[5, 145, 115, 10, 146, 116],
|
|
523
|
+
[19, 75, 47, 10, 76, 48],
|
|
524
|
+
[15, 54, 24, 25, 55, 25],
|
|
525
|
+
[23, 45, 15, 25, 46, 16],
|
|
526
|
+
[13, 145, 115, 3, 146, 116],
|
|
527
|
+
[2, 74, 46, 29, 75, 47],
|
|
528
|
+
[42, 54, 24, 1, 55, 25],
|
|
529
|
+
[23, 45, 15, 28, 46, 16],
|
|
530
|
+
[17, 145, 115],
|
|
531
|
+
[10, 74, 46, 23, 75, 47],
|
|
532
|
+
[10, 54, 24, 35, 55, 25],
|
|
533
|
+
[19, 45, 15, 35, 46, 16],
|
|
534
|
+
[17, 145, 115, 1, 146, 116],
|
|
535
|
+
[14, 74, 46, 21, 75, 47],
|
|
536
|
+
[29, 54, 24, 19, 55, 25],
|
|
537
|
+
[11, 45, 15, 46, 46, 16],
|
|
538
|
+
[13, 145, 115, 6, 146, 116],
|
|
539
|
+
[14, 74, 46, 23, 75, 47],
|
|
540
|
+
[44, 54, 24, 7, 55, 25],
|
|
541
|
+
[59, 46, 16, 1, 47, 17],
|
|
542
|
+
[12, 151, 121, 7, 152, 122],
|
|
543
|
+
[12, 75, 47, 26, 76, 48],
|
|
544
|
+
[39, 54, 24, 14, 55, 25],
|
|
545
|
+
[22, 45, 15, 41, 46, 16],
|
|
546
|
+
[6, 151, 121, 14, 152, 122],
|
|
547
|
+
[6, 75, 47, 34, 76, 48],
|
|
548
|
+
[46, 54, 24, 10, 55, 25],
|
|
549
|
+
[2, 45, 15, 64, 46, 16],
|
|
550
|
+
[17, 152, 122, 4, 153, 123],
|
|
551
|
+
[29, 74, 46, 14, 75, 47],
|
|
552
|
+
[49, 54, 24, 10, 55, 25],
|
|
553
|
+
[24, 45, 15, 46, 46, 16],
|
|
554
|
+
[4, 152, 122, 18, 153, 123],
|
|
555
|
+
[13, 74, 46, 32, 75, 47],
|
|
556
|
+
[48, 54, 24, 14, 55, 25],
|
|
557
|
+
[42, 45, 15, 32, 46, 16],
|
|
558
|
+
[20, 147, 117, 4, 148, 118],
|
|
559
|
+
[40, 75, 47, 7, 76, 48],
|
|
560
|
+
[43, 54, 24, 22, 55, 25],
|
|
561
|
+
[10, 45, 15, 67, 46, 16],
|
|
562
|
+
[19, 148, 118, 6, 149, 119],
|
|
563
|
+
[18, 75, 47, 31, 76, 48],
|
|
564
|
+
[34, 54, 24, 34, 55, 25],
|
|
565
|
+
[20, 45, 15, 61, 46, 16]
|
|
566
|
+
];
|
|
567
|
+
QRRSBlock.getRSBlocks = function(typeNumber, errorCorrectLevel) {
|
|
568
|
+
var rsBlock = QRRSBlock.getRsBlockTable(typeNumber, errorCorrectLevel);
|
|
569
|
+
if (rsBlock === undefined) {
|
|
570
|
+
throw new Error("bad rs block @ typeNumber:" + typeNumber + "/errorCorrectLevel:" + errorCorrectLevel);
|
|
571
|
+
}
|
|
572
|
+
var length = rsBlock.length / 3;
|
|
573
|
+
var list = [];
|
|
574
|
+
for (var i = 0;i < length; i++) {
|
|
575
|
+
var count = rsBlock[i * 3 + 0];
|
|
576
|
+
var totalCount = rsBlock[i * 3 + 1];
|
|
577
|
+
var dataCount = rsBlock[i * 3 + 2];
|
|
578
|
+
for (var j = 0;j < count; j++) {
|
|
579
|
+
list.push(new QRRSBlock(totalCount, dataCount));
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
return list;
|
|
583
|
+
};
|
|
584
|
+
QRRSBlock.getRsBlockTable = function(typeNumber, errorCorrectLevel) {
|
|
585
|
+
switch (errorCorrectLevel) {
|
|
586
|
+
case QRErrorCorrectLevel.L:
|
|
587
|
+
return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0];
|
|
588
|
+
case QRErrorCorrectLevel.M:
|
|
589
|
+
return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1];
|
|
590
|
+
case QRErrorCorrectLevel.Q:
|
|
591
|
+
return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2];
|
|
592
|
+
case QRErrorCorrectLevel.H:
|
|
593
|
+
return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3];
|
|
594
|
+
default:
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
};
|
|
598
|
+
module.exports = QRRSBlock;
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
// node_modules/qrcode-terminal/vendor/QRCode/QRBitBuffer.js
|
|
602
|
+
var require_QRBitBuffer = __commonJS((exports, module) => {
|
|
603
|
+
function QRBitBuffer() {
|
|
604
|
+
this.buffer = [];
|
|
605
|
+
this.length = 0;
|
|
606
|
+
}
|
|
607
|
+
QRBitBuffer.prototype = {
|
|
608
|
+
get: function(index) {
|
|
609
|
+
var bufIndex = Math.floor(index / 8);
|
|
610
|
+
return (this.buffer[bufIndex] >>> 7 - index % 8 & 1) == 1;
|
|
611
|
+
},
|
|
612
|
+
put: function(num, length) {
|
|
613
|
+
for (var i = 0;i < length; i++) {
|
|
614
|
+
this.putBit((num >>> length - i - 1 & 1) == 1);
|
|
615
|
+
}
|
|
616
|
+
},
|
|
617
|
+
getLengthInBits: function() {
|
|
618
|
+
return this.length;
|
|
619
|
+
},
|
|
620
|
+
putBit: function(bit) {
|
|
621
|
+
var bufIndex = Math.floor(this.length / 8);
|
|
622
|
+
if (this.buffer.length <= bufIndex) {
|
|
623
|
+
this.buffer.push(0);
|
|
624
|
+
}
|
|
625
|
+
if (bit) {
|
|
626
|
+
this.buffer[bufIndex] |= 128 >>> this.length % 8;
|
|
627
|
+
}
|
|
628
|
+
this.length++;
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
module.exports = QRBitBuffer;
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
// node_modules/qrcode-terminal/vendor/QRCode/index.js
|
|
635
|
+
var require_QRCode = __commonJS((exports, module) => {
|
|
636
|
+
var QR8bitByte = require_QR8bitByte();
|
|
637
|
+
var QRUtil = require_QRUtil();
|
|
638
|
+
var QRPolynomial = require_QRPolynomial();
|
|
639
|
+
var QRRSBlock = require_QRRSBlock();
|
|
640
|
+
var QRBitBuffer = require_QRBitBuffer();
|
|
641
|
+
function QRCode(typeNumber, errorCorrectLevel) {
|
|
642
|
+
this.typeNumber = typeNumber;
|
|
643
|
+
this.errorCorrectLevel = errorCorrectLevel;
|
|
644
|
+
this.modules = null;
|
|
645
|
+
this.moduleCount = 0;
|
|
646
|
+
this.dataCache = null;
|
|
647
|
+
this.dataList = [];
|
|
648
|
+
}
|
|
649
|
+
QRCode.prototype = {
|
|
650
|
+
addData: function(data) {
|
|
651
|
+
var newData = new QR8bitByte(data);
|
|
652
|
+
this.dataList.push(newData);
|
|
653
|
+
this.dataCache = null;
|
|
654
|
+
},
|
|
655
|
+
isDark: function(row, col) {
|
|
656
|
+
if (row < 0 || this.moduleCount <= row || col < 0 || this.moduleCount <= col) {
|
|
657
|
+
throw new Error(row + "," + col);
|
|
658
|
+
}
|
|
659
|
+
return this.modules[row][col];
|
|
660
|
+
},
|
|
661
|
+
getModuleCount: function() {
|
|
662
|
+
return this.moduleCount;
|
|
663
|
+
},
|
|
664
|
+
make: function() {
|
|
665
|
+
if (this.typeNumber < 1) {
|
|
666
|
+
var typeNumber = 1;
|
|
667
|
+
for (typeNumber = 1;typeNumber < 40; typeNumber++) {
|
|
668
|
+
var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, this.errorCorrectLevel);
|
|
669
|
+
var buffer = new QRBitBuffer;
|
|
670
|
+
var totalDataCount = 0;
|
|
671
|
+
for (var i = 0;i < rsBlocks.length; i++) {
|
|
672
|
+
totalDataCount += rsBlocks[i].dataCount;
|
|
673
|
+
}
|
|
674
|
+
for (var x = 0;x < this.dataList.length; x++) {
|
|
675
|
+
var data = this.dataList[x];
|
|
676
|
+
buffer.put(data.mode, 4);
|
|
677
|
+
buffer.put(data.getLength(), QRUtil.getLengthInBits(data.mode, typeNumber));
|
|
678
|
+
data.write(buffer);
|
|
679
|
+
}
|
|
680
|
+
if (buffer.getLengthInBits() <= totalDataCount * 8)
|
|
681
|
+
break;
|
|
682
|
+
}
|
|
683
|
+
this.typeNumber = typeNumber;
|
|
684
|
+
}
|
|
685
|
+
this.makeImpl(false, this.getBestMaskPattern());
|
|
686
|
+
},
|
|
687
|
+
makeImpl: function(test, maskPattern) {
|
|
688
|
+
this.moduleCount = this.typeNumber * 4 + 17;
|
|
689
|
+
this.modules = new Array(this.moduleCount);
|
|
690
|
+
for (var row = 0;row < this.moduleCount; row++) {
|
|
691
|
+
this.modules[row] = new Array(this.moduleCount);
|
|
692
|
+
for (var col = 0;col < this.moduleCount; col++) {
|
|
693
|
+
this.modules[row][col] = null;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
this.setupPositionProbePattern(0, 0);
|
|
697
|
+
this.setupPositionProbePattern(this.moduleCount - 7, 0);
|
|
698
|
+
this.setupPositionProbePattern(0, this.moduleCount - 7);
|
|
699
|
+
this.setupPositionAdjustPattern();
|
|
700
|
+
this.setupTimingPattern();
|
|
701
|
+
this.setupTypeInfo(test, maskPattern);
|
|
702
|
+
if (this.typeNumber >= 7) {
|
|
703
|
+
this.setupTypeNumber(test);
|
|
704
|
+
}
|
|
705
|
+
if (this.dataCache === null) {
|
|
706
|
+
this.dataCache = QRCode.createData(this.typeNumber, this.errorCorrectLevel, this.dataList);
|
|
707
|
+
}
|
|
708
|
+
this.mapData(this.dataCache, maskPattern);
|
|
709
|
+
},
|
|
710
|
+
setupPositionProbePattern: function(row, col) {
|
|
711
|
+
for (var r = -1;r <= 7; r++) {
|
|
712
|
+
if (row + r <= -1 || this.moduleCount <= row + r)
|
|
713
|
+
continue;
|
|
714
|
+
for (var c = -1;c <= 7; c++) {
|
|
715
|
+
if (col + c <= -1 || this.moduleCount <= col + c)
|
|
716
|
+
continue;
|
|
717
|
+
if (0 <= r && r <= 6 && (c === 0 || c === 6) || 0 <= c && c <= 6 && (r === 0 || r === 6) || 2 <= r && r <= 4 && 2 <= c && c <= 4) {
|
|
718
|
+
this.modules[row + r][col + c] = true;
|
|
719
|
+
} else {
|
|
720
|
+
this.modules[row + r][col + c] = false;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
},
|
|
725
|
+
getBestMaskPattern: function() {
|
|
726
|
+
var minLostPoint = 0;
|
|
727
|
+
var pattern = 0;
|
|
728
|
+
for (var i = 0;i < 8; i++) {
|
|
729
|
+
this.makeImpl(true, i);
|
|
730
|
+
var lostPoint = QRUtil.getLostPoint(this);
|
|
731
|
+
if (i === 0 || minLostPoint > lostPoint) {
|
|
732
|
+
minLostPoint = lostPoint;
|
|
733
|
+
pattern = i;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
return pattern;
|
|
737
|
+
},
|
|
738
|
+
createMovieClip: function(target_mc, instance_name, depth) {
|
|
739
|
+
var qr_mc = target_mc.createEmptyMovieClip(instance_name, depth);
|
|
740
|
+
var cs = 1;
|
|
741
|
+
this.make();
|
|
742
|
+
for (var row = 0;row < this.modules.length; row++) {
|
|
743
|
+
var y = row * cs;
|
|
744
|
+
for (var col = 0;col < this.modules[row].length; col++) {
|
|
745
|
+
var x = col * cs;
|
|
746
|
+
var dark = this.modules[row][col];
|
|
747
|
+
if (dark) {
|
|
748
|
+
qr_mc.beginFill(0, 100);
|
|
749
|
+
qr_mc.moveTo(x, y);
|
|
750
|
+
qr_mc.lineTo(x + cs, y);
|
|
751
|
+
qr_mc.lineTo(x + cs, y + cs);
|
|
752
|
+
qr_mc.lineTo(x, y + cs);
|
|
753
|
+
qr_mc.endFill();
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
return qr_mc;
|
|
758
|
+
},
|
|
759
|
+
setupTimingPattern: function() {
|
|
760
|
+
for (var r = 8;r < this.moduleCount - 8; r++) {
|
|
761
|
+
if (this.modules[r][6] !== null) {
|
|
762
|
+
continue;
|
|
763
|
+
}
|
|
764
|
+
this.modules[r][6] = r % 2 === 0;
|
|
765
|
+
}
|
|
766
|
+
for (var c = 8;c < this.moduleCount - 8; c++) {
|
|
767
|
+
if (this.modules[6][c] !== null) {
|
|
768
|
+
continue;
|
|
769
|
+
}
|
|
770
|
+
this.modules[6][c] = c % 2 === 0;
|
|
771
|
+
}
|
|
772
|
+
},
|
|
773
|
+
setupPositionAdjustPattern: function() {
|
|
774
|
+
var pos = QRUtil.getPatternPosition(this.typeNumber);
|
|
775
|
+
for (var i = 0;i < pos.length; i++) {
|
|
776
|
+
for (var j = 0;j < pos.length; j++) {
|
|
777
|
+
var row = pos[i];
|
|
778
|
+
var col = pos[j];
|
|
779
|
+
if (this.modules[row][col] !== null) {
|
|
780
|
+
continue;
|
|
781
|
+
}
|
|
782
|
+
for (var r = -2;r <= 2; r++) {
|
|
783
|
+
for (var c = -2;c <= 2; c++) {
|
|
784
|
+
if (Math.abs(r) === 2 || Math.abs(c) === 2 || r === 0 && c === 0) {
|
|
785
|
+
this.modules[row + r][col + c] = true;
|
|
786
|
+
} else {
|
|
787
|
+
this.modules[row + r][col + c] = false;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
},
|
|
794
|
+
setupTypeNumber: function(test) {
|
|
795
|
+
var bits = QRUtil.getBCHTypeNumber(this.typeNumber);
|
|
796
|
+
var mod;
|
|
797
|
+
for (var i = 0;i < 18; i++) {
|
|
798
|
+
mod = !test && (bits >> i & 1) === 1;
|
|
799
|
+
this.modules[Math.floor(i / 3)][i % 3 + this.moduleCount - 8 - 3] = mod;
|
|
800
|
+
}
|
|
801
|
+
for (var x = 0;x < 18; x++) {
|
|
802
|
+
mod = !test && (bits >> x & 1) === 1;
|
|
803
|
+
this.modules[x % 3 + this.moduleCount - 8 - 3][Math.floor(x / 3)] = mod;
|
|
804
|
+
}
|
|
805
|
+
},
|
|
806
|
+
setupTypeInfo: function(test, maskPattern) {
|
|
807
|
+
var data = this.errorCorrectLevel << 3 | maskPattern;
|
|
808
|
+
var bits = QRUtil.getBCHTypeInfo(data);
|
|
809
|
+
var mod;
|
|
810
|
+
for (var v = 0;v < 15; v++) {
|
|
811
|
+
mod = !test && (bits >> v & 1) === 1;
|
|
812
|
+
if (v < 6) {
|
|
813
|
+
this.modules[v][8] = mod;
|
|
814
|
+
} else if (v < 8) {
|
|
815
|
+
this.modules[v + 1][8] = mod;
|
|
816
|
+
} else {
|
|
817
|
+
this.modules[this.moduleCount - 15 + v][8] = mod;
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
for (var h = 0;h < 15; h++) {
|
|
821
|
+
mod = !test && (bits >> h & 1) === 1;
|
|
822
|
+
if (h < 8) {
|
|
823
|
+
this.modules[8][this.moduleCount - h - 1] = mod;
|
|
824
|
+
} else if (h < 9) {
|
|
825
|
+
this.modules[8][15 - h - 1 + 1] = mod;
|
|
826
|
+
} else {
|
|
827
|
+
this.modules[8][15 - h - 1] = mod;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
this.modules[this.moduleCount - 8][8] = !test;
|
|
831
|
+
},
|
|
832
|
+
mapData: function(data, maskPattern) {
|
|
833
|
+
var inc = -1;
|
|
834
|
+
var row = this.moduleCount - 1;
|
|
835
|
+
var bitIndex = 7;
|
|
836
|
+
var byteIndex = 0;
|
|
837
|
+
for (var col = this.moduleCount - 1;col > 0; col -= 2) {
|
|
838
|
+
if (col === 6)
|
|
839
|
+
col--;
|
|
840
|
+
while (true) {
|
|
841
|
+
for (var c = 0;c < 2; c++) {
|
|
842
|
+
if (this.modules[row][col - c] === null) {
|
|
843
|
+
var dark = false;
|
|
844
|
+
if (byteIndex < data.length) {
|
|
845
|
+
dark = (data[byteIndex] >>> bitIndex & 1) === 1;
|
|
846
|
+
}
|
|
847
|
+
var mask = QRUtil.getMask(maskPattern, row, col - c);
|
|
848
|
+
if (mask) {
|
|
849
|
+
dark = !dark;
|
|
850
|
+
}
|
|
851
|
+
this.modules[row][col - c] = dark;
|
|
852
|
+
bitIndex--;
|
|
853
|
+
if (bitIndex === -1) {
|
|
854
|
+
byteIndex++;
|
|
855
|
+
bitIndex = 7;
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
row += inc;
|
|
860
|
+
if (row < 0 || this.moduleCount <= row) {
|
|
861
|
+
row -= inc;
|
|
862
|
+
inc = -inc;
|
|
863
|
+
break;
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
};
|
|
869
|
+
QRCode.PAD0 = 236;
|
|
870
|
+
QRCode.PAD1 = 17;
|
|
871
|
+
QRCode.createData = function(typeNumber, errorCorrectLevel, dataList) {
|
|
872
|
+
var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectLevel);
|
|
873
|
+
var buffer = new QRBitBuffer;
|
|
874
|
+
for (var i = 0;i < dataList.length; i++) {
|
|
875
|
+
var data = dataList[i];
|
|
876
|
+
buffer.put(data.mode, 4);
|
|
877
|
+
buffer.put(data.getLength(), QRUtil.getLengthInBits(data.mode, typeNumber));
|
|
878
|
+
data.write(buffer);
|
|
879
|
+
}
|
|
880
|
+
var totalDataCount = 0;
|
|
881
|
+
for (var x = 0;x < rsBlocks.length; x++) {
|
|
882
|
+
totalDataCount += rsBlocks[x].dataCount;
|
|
883
|
+
}
|
|
884
|
+
if (buffer.getLengthInBits() > totalDataCount * 8) {
|
|
885
|
+
throw new Error("code length overflow. (" + buffer.getLengthInBits() + ">" + totalDataCount * 8 + ")");
|
|
886
|
+
}
|
|
887
|
+
if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) {
|
|
888
|
+
buffer.put(0, 4);
|
|
889
|
+
}
|
|
890
|
+
while (buffer.getLengthInBits() % 8 !== 0) {
|
|
891
|
+
buffer.putBit(false);
|
|
892
|
+
}
|
|
893
|
+
while (true) {
|
|
894
|
+
if (buffer.getLengthInBits() >= totalDataCount * 8) {
|
|
895
|
+
break;
|
|
896
|
+
}
|
|
897
|
+
buffer.put(QRCode.PAD0, 8);
|
|
898
|
+
if (buffer.getLengthInBits() >= totalDataCount * 8) {
|
|
899
|
+
break;
|
|
900
|
+
}
|
|
901
|
+
buffer.put(QRCode.PAD1, 8);
|
|
902
|
+
}
|
|
903
|
+
return QRCode.createBytes(buffer, rsBlocks);
|
|
904
|
+
};
|
|
905
|
+
QRCode.createBytes = function(buffer, rsBlocks) {
|
|
906
|
+
var offset = 0;
|
|
907
|
+
var maxDcCount = 0;
|
|
908
|
+
var maxEcCount = 0;
|
|
909
|
+
var dcdata = new Array(rsBlocks.length);
|
|
910
|
+
var ecdata = new Array(rsBlocks.length);
|
|
911
|
+
for (var r = 0;r < rsBlocks.length; r++) {
|
|
912
|
+
var dcCount = rsBlocks[r].dataCount;
|
|
913
|
+
var ecCount = rsBlocks[r].totalCount - dcCount;
|
|
914
|
+
maxDcCount = Math.max(maxDcCount, dcCount);
|
|
915
|
+
maxEcCount = Math.max(maxEcCount, ecCount);
|
|
916
|
+
dcdata[r] = new Array(dcCount);
|
|
917
|
+
for (var i = 0;i < dcdata[r].length; i++) {
|
|
918
|
+
dcdata[r][i] = 255 & buffer.buffer[i + offset];
|
|
919
|
+
}
|
|
920
|
+
offset += dcCount;
|
|
921
|
+
var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount);
|
|
922
|
+
var rawPoly = new QRPolynomial(dcdata[r], rsPoly.getLength() - 1);
|
|
923
|
+
var modPoly = rawPoly.mod(rsPoly);
|
|
924
|
+
ecdata[r] = new Array(rsPoly.getLength() - 1);
|
|
925
|
+
for (var x = 0;x < ecdata[r].length; x++) {
|
|
926
|
+
var modIndex = x + modPoly.getLength() - ecdata[r].length;
|
|
927
|
+
ecdata[r][x] = modIndex >= 0 ? modPoly.get(modIndex) : 0;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
var totalCodeCount = 0;
|
|
931
|
+
for (var y = 0;y < rsBlocks.length; y++) {
|
|
932
|
+
totalCodeCount += rsBlocks[y].totalCount;
|
|
933
|
+
}
|
|
934
|
+
var data = new Array(totalCodeCount);
|
|
935
|
+
var index = 0;
|
|
936
|
+
for (var z = 0;z < maxDcCount; z++) {
|
|
937
|
+
for (var s = 0;s < rsBlocks.length; s++) {
|
|
938
|
+
if (z < dcdata[s].length) {
|
|
939
|
+
data[index++] = dcdata[s][z];
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
for (var xx = 0;xx < maxEcCount; xx++) {
|
|
944
|
+
for (var t = 0;t < rsBlocks.length; t++) {
|
|
945
|
+
if (xx < ecdata[t].length) {
|
|
946
|
+
data[index++] = ecdata[t][xx];
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
return data;
|
|
951
|
+
};
|
|
952
|
+
module.exports = QRCode;
|
|
953
|
+
});
|
|
954
|
+
|
|
955
|
+
// node_modules/qrcode-terminal/lib/main.js
|
|
956
|
+
var require_main = __commonJS((exports, module) => {
|
|
957
|
+
var QRCode = require_QRCode();
|
|
958
|
+
var QRErrorCorrectLevel = require_QRErrorCorrectLevel();
|
|
959
|
+
var black = "\x1B[40m \x1B[0m";
|
|
960
|
+
var white = "\x1B[47m \x1B[0m";
|
|
961
|
+
var toCell = function(isBlack) {
|
|
962
|
+
return isBlack ? black : white;
|
|
963
|
+
};
|
|
964
|
+
var repeat = function(color) {
|
|
965
|
+
return {
|
|
966
|
+
times: function(count) {
|
|
967
|
+
return new Array(count).join(color);
|
|
968
|
+
}
|
|
969
|
+
};
|
|
970
|
+
};
|
|
971
|
+
var fill = function(length, value) {
|
|
972
|
+
var arr = new Array(length);
|
|
973
|
+
for (var i = 0;i < length; i++) {
|
|
974
|
+
arr[i] = value;
|
|
975
|
+
}
|
|
976
|
+
return arr;
|
|
977
|
+
};
|
|
978
|
+
module.exports = {
|
|
979
|
+
error: QRErrorCorrectLevel.L,
|
|
980
|
+
generate: function(input, opts, cb) {
|
|
981
|
+
if (typeof opts === "function") {
|
|
982
|
+
cb = opts;
|
|
983
|
+
opts = {};
|
|
984
|
+
}
|
|
985
|
+
var qrcode = new QRCode(-1, this.error);
|
|
986
|
+
qrcode.addData(input);
|
|
987
|
+
qrcode.make();
|
|
988
|
+
var output = "";
|
|
989
|
+
if (opts && opts.small) {
|
|
990
|
+
var BLACK = true, WHITE = false;
|
|
991
|
+
var moduleCount = qrcode.getModuleCount();
|
|
992
|
+
var moduleData = qrcode.modules.slice();
|
|
993
|
+
var oddRow = moduleCount % 2 === 1;
|
|
994
|
+
if (oddRow) {
|
|
995
|
+
moduleData.push(fill(moduleCount, WHITE));
|
|
996
|
+
}
|
|
997
|
+
var platte = {
|
|
998
|
+
WHITE_ALL: "█",
|
|
999
|
+
WHITE_BLACK: "▀",
|
|
1000
|
+
BLACK_WHITE: "▄",
|
|
1001
|
+
BLACK_ALL: " "
|
|
1002
|
+
};
|
|
1003
|
+
var borderTop = repeat(platte.BLACK_WHITE).times(moduleCount + 3);
|
|
1004
|
+
var borderBottom = repeat(platte.WHITE_BLACK).times(moduleCount + 3);
|
|
1005
|
+
output += borderTop + `
|
|
1006
|
+
`;
|
|
1007
|
+
for (var row = 0;row < moduleCount; row += 2) {
|
|
1008
|
+
output += platte.WHITE_ALL;
|
|
1009
|
+
for (var col = 0;col < moduleCount; col++) {
|
|
1010
|
+
if (moduleData[row][col] === WHITE && moduleData[row + 1][col] === WHITE) {
|
|
1011
|
+
output += platte.WHITE_ALL;
|
|
1012
|
+
} else if (moduleData[row][col] === WHITE && moduleData[row + 1][col] === BLACK) {
|
|
1013
|
+
output += platte.WHITE_BLACK;
|
|
1014
|
+
} else if (moduleData[row][col] === BLACK && moduleData[row + 1][col] === WHITE) {
|
|
1015
|
+
output += platte.BLACK_WHITE;
|
|
1016
|
+
} else {
|
|
1017
|
+
output += platte.BLACK_ALL;
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
output += platte.WHITE_ALL + `
|
|
1021
|
+
`;
|
|
1022
|
+
}
|
|
1023
|
+
if (!oddRow) {
|
|
1024
|
+
output += borderBottom;
|
|
1025
|
+
}
|
|
1026
|
+
} else {
|
|
1027
|
+
var border = repeat(white).times(qrcode.getModuleCount() + 3);
|
|
1028
|
+
output += border + `
|
|
1029
|
+
`;
|
|
1030
|
+
qrcode.modules.forEach(function(row2) {
|
|
1031
|
+
output += white;
|
|
1032
|
+
output += row2.map(toCell).join("");
|
|
1033
|
+
output += white + `
|
|
1034
|
+
`;
|
|
1035
|
+
});
|
|
1036
|
+
output += border;
|
|
1037
|
+
}
|
|
1038
|
+
if (cb)
|
|
1039
|
+
cb(output);
|
|
1040
|
+
else
|
|
1041
|
+
console.log(output);
|
|
1042
|
+
},
|
|
1043
|
+
setErrorLevel: function(error) {
|
|
1044
|
+
this.error = QRErrorCorrectLevel[error] || this.error;
|
|
1045
|
+
}
|
|
1046
|
+
};
|
|
1047
|
+
});
|
|
1048
|
+
|
|
1049
|
+
// src/weixin-sdk.ts
|
|
1050
|
+
var exports_weixin_sdk = {};
|
|
1051
|
+
__export(exports_weixin_sdk, {
|
|
1052
|
+
loadWeixinSdk: () => loadWeixinSdk,
|
|
1053
|
+
buildWeixinSdkSourceCandidates: () => buildWeixinSdkSourceCandidates,
|
|
1054
|
+
buildWeixinSdkImportCandidates: () => buildWeixinSdkImportCandidates
|
|
1055
|
+
});
|
|
1056
|
+
function buildWeixinSdkImportCandidates(explicitPath, _moduleUrl = import.meta.url) {
|
|
1057
|
+
const candidates = [];
|
|
1058
|
+
if (explicitPath) {
|
|
1059
|
+
candidates.push(explicitPath);
|
|
1060
|
+
}
|
|
1061
|
+
candidates.push("weixin-agent-sdk");
|
|
1062
|
+
return candidates;
|
|
1063
|
+
}
|
|
1064
|
+
function buildWeixinSdkSourceCandidates(explicitPath, _moduleUrl = import.meta.url) {
|
|
1065
|
+
if (explicitPath) {
|
|
1066
|
+
return [explicitPath];
|
|
1067
|
+
}
|
|
1068
|
+
return [];
|
|
1069
|
+
}
|
|
1070
|
+
async function loadWeixinSdk() {
|
|
1071
|
+
const candidates = buildWeixinSdkImportCandidates(process.env.WEACPX_WEIXIN_SDK, import.meta.url);
|
|
1072
|
+
const errors = [];
|
|
1073
|
+
for (const candidate of candidates) {
|
|
1074
|
+
try {
|
|
1075
|
+
return await import(candidate);
|
|
1076
|
+
} catch (error) {
|
|
1077
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1078
|
+
errors.push(`${candidate}: ${message}`);
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
throw new Error([
|
|
1082
|
+
"Unable to load weixin-agent-sdk.",
|
|
1083
|
+
"Tried:",
|
|
1084
|
+
...errors.map((entry) => `- ${entry}`),
|
|
1085
|
+
'Set WEACPX_WEIXIN_SDK to a local SDK entry file, or install the "weixin-agent-sdk" package.'
|
|
1086
|
+
].join(`
|
|
1087
|
+
`));
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
// src/login.ts
|
|
1091
|
+
var exports_login = {};
|
|
1092
|
+
__export(exports_login, {
|
|
1093
|
+
main: () => main,
|
|
1094
|
+
loginWithQrRendering: () => loginWithQrRendering
|
|
1095
|
+
});
|
|
1096
|
+
async function loadQrLoginSupport() {
|
|
1097
|
+
const candidates = buildWeixinSdkSourceCandidates(process.env.WEACPX_WEIXIN_SDK);
|
|
1098
|
+
for (const candidate of candidates) {
|
|
1099
|
+
try {
|
|
1100
|
+
const sourceUrl = candidate.startsWith("file:") ? candidate : new URL(candidate, import.meta.url).href;
|
|
1101
|
+
const accounts = await import(new URL("./src/auth/accounts.ts", sourceUrl).href);
|
|
1102
|
+
const loginQr = await import(new URL("./src/auth/login-qr.ts", sourceUrl).href);
|
|
1103
|
+
return { accounts, loginQr };
|
|
1104
|
+
} catch {}
|
|
1105
|
+
}
|
|
1106
|
+
return null;
|
|
1107
|
+
}
|
|
1108
|
+
async function loginWithQrRendering() {
|
|
1109
|
+
const support = await loadQrLoginSupport();
|
|
1110
|
+
if (!support) {
|
|
1111
|
+
const { login } = await loadWeixinSdk();
|
|
1112
|
+
await login();
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1115
|
+
const { accounts, loginQr } = support;
|
|
1116
|
+
const apiBaseUrl = accounts.DEFAULT_BASE_URL;
|
|
1117
|
+
console.log("正在启动微信扫码登录...");
|
|
1118
|
+
const startResult = await loginQr.startWeixinLoginWithQr({
|
|
1119
|
+
apiBaseUrl,
|
|
1120
|
+
botType: loginQr.DEFAULT_ILINK_BOT_TYPE
|
|
1121
|
+
});
|
|
1122
|
+
if (!startResult.qrcodeUrl) {
|
|
1123
|
+
throw new Error(startResult.message);
|
|
1124
|
+
}
|
|
1125
|
+
console.log(`
|
|
1126
|
+
使用微信扫描以下二维码,以完成连接:
|
|
1127
|
+
`);
|
|
1128
|
+
await new Promise((resolve) => {
|
|
1129
|
+
import_qrcode_terminal.default.generate(startResult.qrcodeUrl, { small: true }, (qr) => {
|
|
1130
|
+
console.log(qr);
|
|
1131
|
+
resolve();
|
|
1132
|
+
});
|
|
1133
|
+
});
|
|
1134
|
+
console.log(`
|
|
1135
|
+
等待扫码...
|
|
1136
|
+
`);
|
|
1137
|
+
const waitResult = await loginQr.waitForWeixinLogin({
|
|
1138
|
+
sessionKey: startResult.sessionKey,
|
|
1139
|
+
apiBaseUrl,
|
|
1140
|
+
timeoutMs: 480000,
|
|
1141
|
+
botType: loginQr.DEFAULT_ILINK_BOT_TYPE
|
|
1142
|
+
});
|
|
1143
|
+
if (!waitResult.connected || !waitResult.botToken || !waitResult.accountId) {
|
|
1144
|
+
throw new Error(waitResult.message);
|
|
1145
|
+
}
|
|
1146
|
+
const normalizedId = accounts.normalizeAccountId(waitResult.accountId);
|
|
1147
|
+
accounts.saveWeixinAccount(normalizedId, {
|
|
1148
|
+
token: waitResult.botToken,
|
|
1149
|
+
baseUrl: waitResult.baseUrl,
|
|
1150
|
+
userId: waitResult.userId
|
|
1151
|
+
});
|
|
1152
|
+
accounts.registerWeixinAccountId(normalizedId);
|
|
1153
|
+
console.log(`
|
|
1154
|
+
✅ 与微信连接成功!`);
|
|
1155
|
+
}
|
|
1156
|
+
async function main() {
|
|
1157
|
+
await loginWithQrRendering();
|
|
1158
|
+
}
|
|
1159
|
+
var import_qrcode_terminal;
|
|
1160
|
+
var init_login = __esm(async () => {
|
|
1161
|
+
import_qrcode_terminal = __toESM(require_main(), 1);
|
|
1162
|
+
if (false) {}
|
|
1163
|
+
});
|
|
1164
|
+
|
|
1165
|
+
// src/config/agent-templates.ts
|
|
1166
|
+
function getAgentTemplate(name) {
|
|
1167
|
+
const template = TEMPLATES[name];
|
|
1168
|
+
if (!template) {
|
|
1169
|
+
return null;
|
|
1170
|
+
}
|
|
1171
|
+
return {
|
|
1172
|
+
...template
|
|
1173
|
+
};
|
|
1174
|
+
}
|
|
1175
|
+
function listAgentTemplates() {
|
|
1176
|
+
return Object.keys(TEMPLATES);
|
|
1177
|
+
}
|
|
1178
|
+
var TEMPLATES;
|
|
1179
|
+
var init_agent_templates = __esm(() => {
|
|
1180
|
+
TEMPLATES = {
|
|
1181
|
+
codex: {
|
|
1182
|
+
driver: "codex"
|
|
1183
|
+
},
|
|
1184
|
+
claude: {
|
|
1185
|
+
driver: "claude"
|
|
1186
|
+
}
|
|
1187
|
+
};
|
|
1188
|
+
});
|
|
1189
|
+
|
|
1190
|
+
// src/formatting/render-text.ts
|
|
1191
|
+
function renderHelpText() {
|
|
1192
|
+
return [
|
|
1193
|
+
"可用命令:",
|
|
1194
|
+
"/agents",
|
|
1195
|
+
"/agent add <codex|claude>",
|
|
1196
|
+
"/agent rm <name>",
|
|
1197
|
+
"/workspaces",
|
|
1198
|
+
"/workspace 或 /ws",
|
|
1199
|
+
"/ws new <name> -d <path>",
|
|
1200
|
+
"/workspace rm <name>",
|
|
1201
|
+
"/sessions",
|
|
1202
|
+
"/session 或 /ss",
|
|
1203
|
+
"/ss <agent> -d <path>",
|
|
1204
|
+
"/ss new <agent> -d <path>",
|
|
1205
|
+
"/ss new <alias> -a <name> --ws <name>",
|
|
1206
|
+
"/ss attach <alias> -a <name> --ws <name> --name <transport-session>",
|
|
1207
|
+
"/use <alias>",
|
|
1208
|
+
"/status",
|
|
1209
|
+
"/cancel 或 /stop"
|
|
1210
|
+
].join(`
|
|
1211
|
+
`);
|
|
1212
|
+
}
|
|
1213
|
+
function renderAgents(config) {
|
|
1214
|
+
const names = Object.keys(config.agents);
|
|
1215
|
+
if (names.length === 0) {
|
|
1216
|
+
return "还没有注册任何 Agent。";
|
|
1217
|
+
}
|
|
1218
|
+
return ["已注册的 Agent:", ...names.map((name) => `- ${name}`)].join(`
|
|
1219
|
+
`);
|
|
1220
|
+
}
|
|
1221
|
+
function renderWorkspaces(config) {
|
|
1222
|
+
const names = Object.entries(config.workspaces);
|
|
1223
|
+
if (names.length === 0) {
|
|
1224
|
+
return "还没有注册任何工作区。";
|
|
1225
|
+
}
|
|
1226
|
+
return ["已注册的工作区:", ...names.map(([name, workspace]) => `- ${name}: ${workspace.cwd}`)].join(`
|
|
1227
|
+
`);
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
// src/logging/app-logger.ts
|
|
1231
|
+
import { appendFile, mkdir as mkdir5, readdir, rename, rm as rm4, stat } from "node:fs/promises";
|
|
1232
|
+
import { basename, dirname as dirname4, join as join2 } from "node:path";
|
|
1233
|
+
function createNoopAppLogger() {
|
|
1234
|
+
return {
|
|
1235
|
+
debug: async () => {},
|
|
1236
|
+
info: async () => {},
|
|
1237
|
+
error: async () => {},
|
|
1238
|
+
cleanup: async () => {}
|
|
1239
|
+
};
|
|
1240
|
+
}
|
|
1241
|
+
function createAppLogger(options) {
|
|
1242
|
+
const now = options.now ?? (() => new Date);
|
|
1243
|
+
return {
|
|
1244
|
+
debug: async (event, message, context) => {
|
|
1245
|
+
await writeLog("debug", event, message, context);
|
|
1246
|
+
},
|
|
1247
|
+
info: async (event, message, context) => {
|
|
1248
|
+
await writeLog("info", event, message, context);
|
|
1249
|
+
},
|
|
1250
|
+
error: async (event, message, context) => {
|
|
1251
|
+
await writeLog("error", event, message, context);
|
|
1252
|
+
},
|
|
1253
|
+
cleanup: async () => {
|
|
1254
|
+
await cleanupExpiredRotatedLogs(options.filePath, options.retentionDays, now);
|
|
1255
|
+
}
|
|
1256
|
+
};
|
|
1257
|
+
async function writeLog(level, event, message, context = {}) {
|
|
1258
|
+
if (LEVEL_ORDER[level] > LEVEL_ORDER[options.level]) {
|
|
1259
|
+
return;
|
|
1260
|
+
}
|
|
1261
|
+
const line = formatLogLine(now(), level, event, message, context);
|
|
1262
|
+
await mkdir5(dirname4(options.filePath), { recursive: true });
|
|
1263
|
+
await rotateIfNeeded(options.filePath, Buffer.byteLength(line), options.maxSizeBytes, options.maxFiles);
|
|
1264
|
+
await appendFile(options.filePath, line, "utf8");
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
async function rotateIfNeeded(filePath, incomingSize, maxSizeBytes, maxFiles) {
|
|
1268
|
+
let currentSize = 0;
|
|
1269
|
+
try {
|
|
1270
|
+
currentSize = (await stat(filePath)).size;
|
|
1271
|
+
} catch (error) {
|
|
1272
|
+
if (!isMissingFileError(error)) {
|
|
1273
|
+
throw error;
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
if (currentSize + incomingSize <= maxSizeBytes) {
|
|
1277
|
+
return;
|
|
1278
|
+
}
|
|
1279
|
+
if (currentSize === 0) {
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
1282
|
+
if (maxFiles <= 0) {
|
|
1283
|
+
await rm4(filePath, { force: true });
|
|
1284
|
+
return;
|
|
1285
|
+
}
|
|
1286
|
+
await rm4(`${filePath}.${maxFiles}`, { force: true });
|
|
1287
|
+
for (let index = maxFiles - 1;index >= 1; index -= 1) {
|
|
1288
|
+
const source = `${filePath}.${index}`;
|
|
1289
|
+
try {
|
|
1290
|
+
await rename(source, `${filePath}.${index + 1}`);
|
|
1291
|
+
} catch (error) {
|
|
1292
|
+
if (!isMissingFileError(error)) {
|
|
1293
|
+
throw error;
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
await rename(filePath, `${filePath}.1`);
|
|
1298
|
+
}
|
|
1299
|
+
async function cleanupExpiredRotatedLogs(filePath, retentionDays, now) {
|
|
1300
|
+
const parentDir = dirname4(filePath);
|
|
1301
|
+
const prefix = `${basename(filePath)}.`;
|
|
1302
|
+
const cutoff = now().getTime() - retentionDays * 24 * 60 * 60 * 1000;
|
|
1303
|
+
let files = [];
|
|
1304
|
+
try {
|
|
1305
|
+
files = await readdir(parentDir);
|
|
1306
|
+
} catch (error) {
|
|
1307
|
+
if (isMissingFileError(error)) {
|
|
1308
|
+
return;
|
|
1309
|
+
}
|
|
1310
|
+
throw error;
|
|
1311
|
+
}
|
|
1312
|
+
for (const file of files) {
|
|
1313
|
+
if (!file.startsWith(prefix) || !/^\d+$/.test(file.slice(prefix.length))) {
|
|
1314
|
+
continue;
|
|
1315
|
+
}
|
|
1316
|
+
const candidate = join2(parentDir, file);
|
|
1317
|
+
const details = await stat(candidate);
|
|
1318
|
+
if (details.mtime.getTime() < cutoff) {
|
|
1319
|
+
await rm4(candidate, { force: true });
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
function formatLogLine(time, level, event, message, context) {
|
|
1324
|
+
const fields = Object.entries(context).filter(([, value]) => value !== undefined).map(([key, value]) => `${key}=${formatValue(value)}`);
|
|
1325
|
+
const suffix = fields.length > 0 ? ` ${fields.join(" ")}` : "";
|
|
1326
|
+
return `${time.toISOString()} ${level.toUpperCase()} ${event} message=${formatValue(message)}${suffix}
|
|
1327
|
+
`;
|
|
1328
|
+
}
|
|
1329
|
+
function formatValue(value) {
|
|
1330
|
+
if (value === null) {
|
|
1331
|
+
return "null";
|
|
1332
|
+
}
|
|
1333
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
1334
|
+
return String(value);
|
|
1335
|
+
}
|
|
1336
|
+
return JSON.stringify(value);
|
|
1337
|
+
}
|
|
1338
|
+
function isMissingFileError(error) {
|
|
1339
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
1340
|
+
}
|
|
1341
|
+
var LEVEL_ORDER;
|
|
1342
|
+
var init_app_logger = __esm(() => {
|
|
1343
|
+
LEVEL_ORDER = {
|
|
1344
|
+
error: 0,
|
|
1345
|
+
info: 1,
|
|
1346
|
+
debug: 2
|
|
1347
|
+
};
|
|
1348
|
+
});
|
|
1349
|
+
|
|
1350
|
+
// src/commands/parse-command.ts
|
|
1351
|
+
function parseCommand(input) {
|
|
1352
|
+
const trimmed = input.trim();
|
|
1353
|
+
if (!trimmed.startsWith("/")) {
|
|
1354
|
+
return { kind: "prompt", text: trimmed };
|
|
1355
|
+
}
|
|
1356
|
+
const parts = tokenizeCommand(trimmed);
|
|
1357
|
+
const command = normalizeCommand(parts[0] ?? "");
|
|
1358
|
+
if (command === "/help")
|
|
1359
|
+
return { kind: "help" };
|
|
1360
|
+
if (command === "/agents")
|
|
1361
|
+
return { kind: "agents" };
|
|
1362
|
+
if (command === "/workspaces")
|
|
1363
|
+
return { kind: "workspaces" };
|
|
1364
|
+
if (command === "/sessions")
|
|
1365
|
+
return { kind: "sessions" };
|
|
1366
|
+
if (command === "/status")
|
|
1367
|
+
return { kind: "status" };
|
|
1368
|
+
if (command === "/cancel")
|
|
1369
|
+
return { kind: "cancel" };
|
|
1370
|
+
if (command === "/session" && parts.length === 1)
|
|
1371
|
+
return { kind: "sessions" };
|
|
1372
|
+
if (command === "/workspace" && parts.length === 1)
|
|
1373
|
+
return { kind: "workspaces" };
|
|
1374
|
+
if (command === "/use" && parts[1]) {
|
|
1375
|
+
return { kind: "session.use", alias: parts[1] };
|
|
1376
|
+
}
|
|
1377
|
+
if (command === "/agent" && parts[1] === "add" && parts[2]) {
|
|
1378
|
+
return { kind: "agent.add", template: parts[2] };
|
|
1379
|
+
}
|
|
1380
|
+
if (command === "/agent" && parts[1] === "rm" && parts[2]) {
|
|
1381
|
+
return { kind: "agent.rm", name: parts[2] };
|
|
1382
|
+
}
|
|
1383
|
+
if (command === "/workspace" && parts[1] === "new" && parts[2]) {
|
|
1384
|
+
const name = parts[2];
|
|
1385
|
+
let cwd = "";
|
|
1386
|
+
for (let index = 3;index < parts.length; index += 1) {
|
|
1387
|
+
if (parts[index] === "--cwd" || parts[index] === "-d") {
|
|
1388
|
+
cwd = parts[index + 1] ?? "";
|
|
1389
|
+
index += 1;
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
return { kind: "workspace.new", name, cwd };
|
|
1393
|
+
}
|
|
1394
|
+
if (command === "/workspace" && parts[1] === "rm" && parts[2]) {
|
|
1395
|
+
return { kind: "workspace.rm", name: parts[2] };
|
|
1396
|
+
}
|
|
1397
|
+
if (command === "/session" && parts[1] === "new" && parts[2]) {
|
|
1398
|
+
if (hasAnyFlag(parts, ["--agent", "-a"])) {
|
|
1399
|
+
const alias = parts[2];
|
|
1400
|
+
let agent = "";
|
|
1401
|
+
let workspace = "";
|
|
1402
|
+
for (let index = 3;index < parts.length; index += 1) {
|
|
1403
|
+
if (parts[index] === "--agent" || parts[index] === "-a") {
|
|
1404
|
+
agent = parts[index + 1] ?? "";
|
|
1405
|
+
index += 1;
|
|
1406
|
+
} else if (parts[index] === "--ws" || parts[index] === "-ws") {
|
|
1407
|
+
workspace = parts[index + 1] ?? "";
|
|
1408
|
+
index += 1;
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
return { kind: "session.new", alias, agent, workspace };
|
|
1412
|
+
}
|
|
1413
|
+
const cwd = readFlagValue(parts, ["--cwd", "-d"]);
|
|
1414
|
+
if (cwd) {
|
|
1415
|
+
return { kind: "session.shortcut.new", agent: parts[2], cwd };
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
if (command === "/session" && parts[1] && parts[1] !== "new" && parts[1] !== "attach") {
|
|
1419
|
+
const cwd = readFlagValue(parts, ["--cwd", "-d"]);
|
|
1420
|
+
if (cwd) {
|
|
1421
|
+
return { kind: "session.shortcut", agent: parts[1], cwd };
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
if (command === "/session" && parts[1] === "attach" && parts[2]) {
|
|
1425
|
+
const alias = parts[2];
|
|
1426
|
+
let agent = "";
|
|
1427
|
+
let workspace = "";
|
|
1428
|
+
let transportSession = "";
|
|
1429
|
+
for (let index = 3;index < parts.length; index += 1) {
|
|
1430
|
+
if (parts[index] === "--agent" || parts[index] === "-a") {
|
|
1431
|
+
agent = parts[index + 1] ?? "";
|
|
1432
|
+
index += 1;
|
|
1433
|
+
} else if (parts[index] === "--ws" || parts[index] === "-ws") {
|
|
1434
|
+
workspace = parts[index + 1] ?? "";
|
|
1435
|
+
index += 1;
|
|
1436
|
+
} else if (parts[index] === "--name") {
|
|
1437
|
+
transportSession = parts[index + 1] ?? "";
|
|
1438
|
+
index += 1;
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
return { kind: "session.attach", alias, agent, workspace, transportSession };
|
|
1442
|
+
}
|
|
1443
|
+
return { kind: "prompt", text: trimmed };
|
|
1444
|
+
}
|
|
1445
|
+
function hasAnyFlag(parts, flags) {
|
|
1446
|
+
return parts.some((part) => flags.includes(part));
|
|
1447
|
+
}
|
|
1448
|
+
function readFlagValue(parts, flags) {
|
|
1449
|
+
for (let index = 0;index < parts.length; index += 1) {
|
|
1450
|
+
if (flags.includes(parts[index] ?? "")) {
|
|
1451
|
+
return parts[index + 1] ?? "";
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
return "";
|
|
1455
|
+
}
|
|
1456
|
+
function normalizeCommand(command) {
|
|
1457
|
+
if (command === "/ss")
|
|
1458
|
+
return "/session";
|
|
1459
|
+
if (command === "/ws")
|
|
1460
|
+
return "/workspace";
|
|
1461
|
+
if (command === "/stop")
|
|
1462
|
+
return "/cancel";
|
|
1463
|
+
return command;
|
|
1464
|
+
}
|
|
1465
|
+
function tokenizeCommand(input) {
|
|
1466
|
+
const tokens = [];
|
|
1467
|
+
let current = "";
|
|
1468
|
+
let quote = null;
|
|
1469
|
+
for (const char of input) {
|
|
1470
|
+
if (quote) {
|
|
1471
|
+
if (char === quote) {
|
|
1472
|
+
quote = null;
|
|
1473
|
+
} else {
|
|
1474
|
+
current += char;
|
|
1475
|
+
}
|
|
1476
|
+
continue;
|
|
1477
|
+
}
|
|
1478
|
+
if (char === '"' || char === "'") {
|
|
1479
|
+
quote = char;
|
|
1480
|
+
continue;
|
|
1481
|
+
}
|
|
1482
|
+
if (/\s/.test(char)) {
|
|
1483
|
+
if (current.length > 0) {
|
|
1484
|
+
tokens.push(current);
|
|
1485
|
+
current = "";
|
|
1486
|
+
}
|
|
1487
|
+
continue;
|
|
1488
|
+
}
|
|
1489
|
+
current += char;
|
|
1490
|
+
}
|
|
1491
|
+
if (current.length > 0) {
|
|
1492
|
+
tokens.push(current);
|
|
1493
|
+
}
|
|
1494
|
+
return tokens;
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
// src/commands/command-router.ts
|
|
1498
|
+
import { access } from "node:fs/promises";
|
|
1499
|
+
import { basename as basename2, normalize } from "node:path";
|
|
1500
|
+
|
|
1501
|
+
class CommandRouter {
|
|
1502
|
+
sessions;
|
|
1503
|
+
transport;
|
|
1504
|
+
config;
|
|
1505
|
+
configStore;
|
|
1506
|
+
logger;
|
|
1507
|
+
constructor(sessions, transport, config, configStore, logger) {
|
|
1508
|
+
this.sessions = sessions;
|
|
1509
|
+
this.transport = transport;
|
|
1510
|
+
this.config = config;
|
|
1511
|
+
this.configStore = configStore;
|
|
1512
|
+
this.logger = logger ?? createNoopAppLogger();
|
|
1513
|
+
}
|
|
1514
|
+
async handle(chatKey, input) {
|
|
1515
|
+
const startedAt = Date.now();
|
|
1516
|
+
const command = parseCommand(input);
|
|
1517
|
+
await this.logger.debug("command.parsed", "parsed inbound command", {
|
|
1518
|
+
chatKey,
|
|
1519
|
+
kind: command.kind
|
|
1520
|
+
});
|
|
1521
|
+
return await this.executeCommand(chatKey, command.kind, startedAt, async () => {
|
|
1522
|
+
switch (command.kind) {
|
|
1523
|
+
case "help":
|
|
1524
|
+
return { text: renderHelpText() };
|
|
1525
|
+
case "agents":
|
|
1526
|
+
return { text: this.config ? renderAgents(this.config) : "No config loaded." };
|
|
1527
|
+
case "agent.add": {
|
|
1528
|
+
if (!this.config || !this.configStore) {
|
|
1529
|
+
return { text: "当前没有加载可写入的配置。" };
|
|
1530
|
+
}
|
|
1531
|
+
const template = getAgentTemplate(command.template);
|
|
1532
|
+
if (!template) {
|
|
1533
|
+
return { text: `暂不支持这个 Agent 模板。当前可用:${listAgentTemplates().join("、")}` };
|
|
1534
|
+
}
|
|
1535
|
+
const updated = await this.configStore.upsertAgent(command.template, template);
|
|
1536
|
+
this.replaceConfig(updated);
|
|
1537
|
+
return { text: `Agent「${command.template}」已保存` };
|
|
1538
|
+
}
|
|
1539
|
+
case "agent.rm": {
|
|
1540
|
+
if (!this.config || !this.configStore) {
|
|
1541
|
+
return { text: "当前没有加载可写入的配置。" };
|
|
1542
|
+
}
|
|
1543
|
+
if (!this.config.agents[command.name]) {
|
|
1544
|
+
return { text: "没有找到这个 Agent。" };
|
|
1545
|
+
}
|
|
1546
|
+
const updated = await this.configStore.removeAgent(command.name);
|
|
1547
|
+
this.replaceConfig(updated);
|
|
1548
|
+
return { text: `Agent「${command.name}」已删除` };
|
|
1549
|
+
}
|
|
1550
|
+
case "workspaces":
|
|
1551
|
+
return { text: this.config ? renderWorkspaces(this.config) : "No config loaded." };
|
|
1552
|
+
case "workspace.new": {
|
|
1553
|
+
if (!this.config || !this.configStore) {
|
|
1554
|
+
return { text: "当前没有加载可写入的配置。" };
|
|
1555
|
+
}
|
|
1556
|
+
if (!await pathExists(command.cwd)) {
|
|
1557
|
+
return { text: `工作区路径不存在:${command.cwd}` };
|
|
1558
|
+
}
|
|
1559
|
+
const updated = await this.configStore.upsertWorkspace(command.name, command.cwd);
|
|
1560
|
+
this.replaceConfig(updated);
|
|
1561
|
+
return { text: `工作区「${command.name}」已保存` };
|
|
1562
|
+
}
|
|
1563
|
+
case "workspace.rm": {
|
|
1564
|
+
if (!this.config || !this.configStore) {
|
|
1565
|
+
return { text: "当前没有加载可写入的配置。" };
|
|
1566
|
+
}
|
|
1567
|
+
const updated = await this.configStore.removeWorkspace(command.name);
|
|
1568
|
+
this.replaceConfig(updated);
|
|
1569
|
+
return { text: `工作区「${command.name}」已删除` };
|
|
1570
|
+
}
|
|
1571
|
+
case "sessions": {
|
|
1572
|
+
const sessions = await this.sessions.listSessions(chatKey);
|
|
1573
|
+
if (sessions.length === 0) {
|
|
1574
|
+
return { text: "还没有会话。请先执行 /session new <alias> --agent <name> --ws <name>。" };
|
|
1575
|
+
}
|
|
1576
|
+
return {
|
|
1577
|
+
text: [
|
|
1578
|
+
"会话列表:",
|
|
1579
|
+
...sessions.map((session) => `- ${session.alias} (${session.agent} @ ${session.workspace})${session.isCurrent ? " [当前]" : ""}`)
|
|
1580
|
+
].join(`
|
|
1581
|
+
`)
|
|
1582
|
+
};
|
|
1583
|
+
}
|
|
1584
|
+
case "session.new": {
|
|
1585
|
+
const session = this.sessions.resolveSession(command.alias, command.agent, command.workspace, `${command.workspace}:${command.alias}`);
|
|
1586
|
+
try {
|
|
1587
|
+
await this.ensureTransportSession(session);
|
|
1588
|
+
const exists = await this.checkTransportSession(session);
|
|
1589
|
+
if (!exists) {
|
|
1590
|
+
return this.renderSessionCreationVerificationError(session);
|
|
1591
|
+
}
|
|
1592
|
+
} catch (error) {
|
|
1593
|
+
return this.renderSessionCreationError(session, error);
|
|
1594
|
+
}
|
|
1595
|
+
await this.sessions.attachSession(command.alias, command.agent, command.workspace, session.transportSession);
|
|
1596
|
+
await this.sessions.useSession(chatKey, command.alias);
|
|
1597
|
+
await this.logger.info("session.created", "created and selected logical session", {
|
|
1598
|
+
alias: command.alias,
|
|
1599
|
+
agent: command.agent,
|
|
1600
|
+
workspace: command.workspace
|
|
1601
|
+
});
|
|
1602
|
+
return { text: `会话「${command.alias}」已创建并切换` };
|
|
1603
|
+
}
|
|
1604
|
+
case "session.shortcut":
|
|
1605
|
+
return await this.handleSessionShortcut(chatKey, command.agent, command.cwd, false);
|
|
1606
|
+
case "session.shortcut.new":
|
|
1607
|
+
return await this.handleSessionShortcut(chatKey, command.agent, command.cwd, true);
|
|
1608
|
+
case "session.attach": {
|
|
1609
|
+
const attached = this.sessions.resolveSession(command.alias, command.agent, command.workspace, command.transportSession);
|
|
1610
|
+
const exists = await this.checkTransportSession(attached);
|
|
1611
|
+
if (!exists) {
|
|
1612
|
+
return {
|
|
1613
|
+
text: [
|
|
1614
|
+
"没有找到可绑定的已有会话。",
|
|
1615
|
+
`请确认会话名是否正确,然后重新执行:/session attach ${command.alias} --agent ${command.agent} --ws ${command.workspace} --name <会话名>`
|
|
1616
|
+
].join(`
|
|
1617
|
+
`)
|
|
1618
|
+
};
|
|
1619
|
+
}
|
|
1620
|
+
await this.sessions.attachSession(command.alias, command.agent, command.workspace, command.transportSession);
|
|
1621
|
+
await this.sessions.useSession(chatKey, command.alias);
|
|
1622
|
+
await this.logger.info("session.attached", "attached existing transport session", {
|
|
1623
|
+
alias: command.alias,
|
|
1624
|
+
agent: command.agent,
|
|
1625
|
+
workspace: command.workspace,
|
|
1626
|
+
transportSession: command.transportSession
|
|
1627
|
+
});
|
|
1628
|
+
return { text: `会话「${command.alias}」已绑定并切换` };
|
|
1629
|
+
}
|
|
1630
|
+
case "session.use":
|
|
1631
|
+
await this.sessions.useSession(chatKey, command.alias);
|
|
1632
|
+
await this.logger.info("session.selected", "selected logical session", {
|
|
1633
|
+
alias: command.alias,
|
|
1634
|
+
chatKey
|
|
1635
|
+
});
|
|
1636
|
+
return { text: `已切换到会话「${command.alias}」` };
|
|
1637
|
+
case "status": {
|
|
1638
|
+
const session = await this.sessions.getCurrentSession(chatKey);
|
|
1639
|
+
if (!session) {
|
|
1640
|
+
return { text: "当前还没有选中的会话。请先执行 /session new ... 或 /use <alias>。" };
|
|
1641
|
+
}
|
|
1642
|
+
return {
|
|
1643
|
+
text: [
|
|
1644
|
+
"当前会话:",
|
|
1645
|
+
`- 名称:${session.alias}`,
|
|
1646
|
+
`- Agent:${session.agent}`,
|
|
1647
|
+
`- 工作区:${session.workspace}`
|
|
1648
|
+
].join(`
|
|
1649
|
+
`)
|
|
1650
|
+
};
|
|
1651
|
+
}
|
|
1652
|
+
case "cancel": {
|
|
1653
|
+
const session = await this.sessions.getCurrentSession(chatKey);
|
|
1654
|
+
if (!session) {
|
|
1655
|
+
return { text: "当前还没有选中的会话。请先执行 /session new ... 或 /use <alias>。" };
|
|
1656
|
+
}
|
|
1657
|
+
try {
|
|
1658
|
+
const result = await this.cancelTransportSession(session);
|
|
1659
|
+
return { text: result.message || "cancelled" };
|
|
1660
|
+
} catch (error) {
|
|
1661
|
+
return this.renderTransportError(session, error);
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
case "prompt": {
|
|
1665
|
+
const session = await this.sessions.getCurrentSession(chatKey);
|
|
1666
|
+
if (!session) {
|
|
1667
|
+
return { text: "当前还没有选中的会话。请先执行 /session new ... 或 /use <alias>。" };
|
|
1668
|
+
}
|
|
1669
|
+
try {
|
|
1670
|
+
const reply = await this.promptTransportSession(session, command.text);
|
|
1671
|
+
return { text: reply.text };
|
|
1672
|
+
} catch (error) {
|
|
1673
|
+
return this.renderTransportError(session, error);
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
});
|
|
1678
|
+
}
|
|
1679
|
+
async handleSessionShortcut(chatKey, agent, cwdInput, createNew) {
|
|
1680
|
+
if (!this.config || !this.configStore) {
|
|
1681
|
+
return { text: "当前没有加载可写入的配置。" };
|
|
1682
|
+
}
|
|
1683
|
+
const cwd = normalizePathForWorkspace(cwdInput);
|
|
1684
|
+
if (!await pathExists(cwd)) {
|
|
1685
|
+
return { text: `工作区路径不存在:${cwdInput}` };
|
|
1686
|
+
}
|
|
1687
|
+
const workspace = await this.resolveShortcutWorkspace(cwd);
|
|
1688
|
+
await this.logger.info("session.shortcut.workspace", "resolved shortcut workspace", {
|
|
1689
|
+
workspace: workspace.name,
|
|
1690
|
+
cwd: workspace.cwd,
|
|
1691
|
+
reused: workspace.reused
|
|
1692
|
+
});
|
|
1693
|
+
const baseAlias = `${workspace.name}:${agent}`;
|
|
1694
|
+
const alias = createNew ? await this.allocateUniqueSessionAlias(baseAlias, chatKey) : baseAlias;
|
|
1695
|
+
if (!createNew && await this.hasLogicalSession(alias, chatKey)) {
|
|
1696
|
+
await this.sessions.useSession(chatKey, alias);
|
|
1697
|
+
await this.logger.info("session.shortcut.reused", "reused existing logical session", {
|
|
1698
|
+
alias,
|
|
1699
|
+
workspace: workspace.name,
|
|
1700
|
+
agent
|
|
1701
|
+
});
|
|
1702
|
+
return {
|
|
1703
|
+
text: [
|
|
1704
|
+
`已切换到会话「${alias}」`,
|
|
1705
|
+
`- 复用工作区:${workspace.name}`,
|
|
1706
|
+
`- 复用会话:${alias}`
|
|
1707
|
+
].join(`
|
|
1708
|
+
`)
|
|
1709
|
+
};
|
|
1710
|
+
}
|
|
1711
|
+
const session = this.sessions.resolveSession(alias, agent, workspace.name, `${workspace.name}:${alias}`);
|
|
1712
|
+
try {
|
|
1713
|
+
await this.ensureTransportSession(session);
|
|
1714
|
+
const exists = await this.checkTransportSession(session);
|
|
1715
|
+
if (!exists) {
|
|
1716
|
+
return this.renderShortcutSessionCreationError(workspace, alias);
|
|
1717
|
+
}
|
|
1718
|
+
} catch {
|
|
1719
|
+
return this.renderShortcutSessionCreationError(workspace, alias);
|
|
1720
|
+
}
|
|
1721
|
+
await this.sessions.attachSession(alias, agent, workspace.name, session.transportSession);
|
|
1722
|
+
await this.sessions.useSession(chatKey, alias);
|
|
1723
|
+
await this.logger.info("session.shortcut.created", "created new logical session from shortcut", {
|
|
1724
|
+
alias,
|
|
1725
|
+
workspace: workspace.name,
|
|
1726
|
+
agent,
|
|
1727
|
+
workspaceReused: workspace.reused
|
|
1728
|
+
});
|
|
1729
|
+
return {
|
|
1730
|
+
text: [
|
|
1731
|
+
`已创建并切换到会话「${alias}」`,
|
|
1732
|
+
workspace.reused ? `- 复用工作区:${workspace.name}` : `- 新增工作区:${workspace.name} -> ${workspace.cwd}`,
|
|
1733
|
+
`- 新增会话:${alias}`
|
|
1734
|
+
].join(`
|
|
1735
|
+
`)
|
|
1736
|
+
};
|
|
1737
|
+
}
|
|
1738
|
+
replaceConfig(updated) {
|
|
1739
|
+
if (!this.config) {
|
|
1740
|
+
return;
|
|
1741
|
+
}
|
|
1742
|
+
this.config.transport = { ...updated.transport };
|
|
1743
|
+
this.config.agents = { ...updated.agents };
|
|
1744
|
+
this.config.workspaces = { ...updated.workspaces };
|
|
1745
|
+
}
|
|
1746
|
+
renderTransportError(session, error) {
|
|
1747
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1748
|
+
if (message.includes("No acpx session found")) {
|
|
1749
|
+
return {
|
|
1750
|
+
text: [
|
|
1751
|
+
`当前会话「${session.alias}」暂时不可用。`,
|
|
1752
|
+
`请先在微信里重新执行:/session new ${session.alias} --agent ${session.agent} --ws ${session.workspace}`,
|
|
1753
|
+
`如果你要绑定一个已有会话,再执行:/session attach ${session.alias} --agent ${session.agent} --ws ${session.workspace} --name <会话名>`
|
|
1754
|
+
].join(`
|
|
1755
|
+
`)
|
|
1756
|
+
};
|
|
1757
|
+
}
|
|
1758
|
+
throw error;
|
|
1759
|
+
}
|
|
1760
|
+
renderSessionCreationError(session, error) {
|
|
1761
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1762
|
+
if (message.includes("timed out") && message.includes("sessions new")) {
|
|
1763
|
+
return this.renderSessionCreationVerificationError(session);
|
|
1764
|
+
}
|
|
1765
|
+
throw error;
|
|
1766
|
+
}
|
|
1767
|
+
renderSessionCreationVerificationError(session) {
|
|
1768
|
+
return {
|
|
1769
|
+
text: [
|
|
1770
|
+
"当前还不能直接在微信里创建新会话。",
|
|
1771
|
+
`请先准备好一个已有会话,然后在微信里执行:/session attach ${session.alias} --agent ${session.agent} --ws ${session.workspace} --name <会话名>`
|
|
1772
|
+
].join(`
|
|
1773
|
+
`)
|
|
1774
|
+
};
|
|
1775
|
+
}
|
|
1776
|
+
async resolveShortcutWorkspace(cwd) {
|
|
1777
|
+
const existingByPath = Object.entries(this.config?.workspaces ?? {}).find(([, workspace]) => sameWorkspacePath(workspace.cwd, cwd));
|
|
1778
|
+
if (existingByPath) {
|
|
1779
|
+
return {
|
|
1780
|
+
name: existingByPath[0],
|
|
1781
|
+
cwd: existingByPath[1].cwd,
|
|
1782
|
+
reused: true
|
|
1783
|
+
};
|
|
1784
|
+
}
|
|
1785
|
+
const baseName = basename2(cwd);
|
|
1786
|
+
const workspaceName = this.allocateWorkspaceName(baseName, cwd);
|
|
1787
|
+
const updated = await this.configStore.upsertWorkspace(workspaceName, cwd);
|
|
1788
|
+
this.replaceConfig(updated);
|
|
1789
|
+
return {
|
|
1790
|
+
name: workspaceName,
|
|
1791
|
+
cwd,
|
|
1792
|
+
reused: false
|
|
1793
|
+
};
|
|
1794
|
+
}
|
|
1795
|
+
allocateWorkspaceName(baseName, cwd) {
|
|
1796
|
+
if (!this.config?.workspaces[baseName]) {
|
|
1797
|
+
return baseName;
|
|
1798
|
+
}
|
|
1799
|
+
let suffix = 2;
|
|
1800
|
+
while (this.config.workspaces[`${baseName}-${suffix}`]) {
|
|
1801
|
+
suffix += 1;
|
|
1802
|
+
}
|
|
1803
|
+
return `${baseName}-${suffix}`;
|
|
1804
|
+
}
|
|
1805
|
+
async allocateUniqueSessionAlias(baseAlias, chatKey) {
|
|
1806
|
+
if (!await this.hasLogicalSession(baseAlias, chatKey)) {
|
|
1807
|
+
return baseAlias;
|
|
1808
|
+
}
|
|
1809
|
+
let suffix = 2;
|
|
1810
|
+
while (await this.hasLogicalSession(`${baseAlias}-${suffix}`, chatKey)) {
|
|
1811
|
+
suffix += 1;
|
|
1812
|
+
}
|
|
1813
|
+
return `${baseAlias}-${suffix}`;
|
|
1814
|
+
}
|
|
1815
|
+
async hasLogicalSession(alias, chatKey) {
|
|
1816
|
+
const sessions = await this.sessions.listSessions(chatKey);
|
|
1817
|
+
return sessions.some((session) => session.alias === alias);
|
|
1818
|
+
}
|
|
1819
|
+
renderShortcutSessionCreationError(workspace, alias) {
|
|
1820
|
+
return {
|
|
1821
|
+
text: [
|
|
1822
|
+
`会话「${alias}」创建失败。`,
|
|
1823
|
+
workspace.reused ? `- 复用工作区:${workspace.name}` : `- 已新增工作区:${workspace.name} -> ${workspace.cwd}`,
|
|
1824
|
+
"- 会话未创建,请重试。"
|
|
1825
|
+
].join(`
|
|
1826
|
+
`)
|
|
1827
|
+
};
|
|
1828
|
+
}
|
|
1829
|
+
async executeCommand(chatKey, kind, startedAt, operation) {
|
|
1830
|
+
try {
|
|
1831
|
+
const response = await operation();
|
|
1832
|
+
await this.logger.info("command.completed", "completed command handling", {
|
|
1833
|
+
chatKey,
|
|
1834
|
+
kind,
|
|
1835
|
+
durationMs: Date.now() - startedAt
|
|
1836
|
+
});
|
|
1837
|
+
return response;
|
|
1838
|
+
} catch (error) {
|
|
1839
|
+
await this.logger.error("command.failed", "command handling failed", {
|
|
1840
|
+
chatKey,
|
|
1841
|
+
kind,
|
|
1842
|
+
durationMs: Date.now() - startedAt,
|
|
1843
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1844
|
+
});
|
|
1845
|
+
throw error;
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
async ensureTransportSession(session) {
|
|
1849
|
+
await this.measureTransportCall("ensure_session", session, () => this.transport.ensureSession(session));
|
|
1850
|
+
}
|
|
1851
|
+
async checkTransportSession(session) {
|
|
1852
|
+
return await this.measureTransportCall("has_session", session, () => this.transport.hasSession(session));
|
|
1853
|
+
}
|
|
1854
|
+
async promptTransportSession(session, text) {
|
|
1855
|
+
return await this.measureTransportCall("prompt", session, () => this.transport.prompt(session, text));
|
|
1856
|
+
}
|
|
1857
|
+
async cancelTransportSession(session) {
|
|
1858
|
+
return await this.measureTransportCall("cancel", session, () => this.transport.cancel(session));
|
|
1859
|
+
}
|
|
1860
|
+
async measureTransportCall(operation, session, callback) {
|
|
1861
|
+
const startedAt = Date.now();
|
|
1862
|
+
try {
|
|
1863
|
+
const result = await callback();
|
|
1864
|
+
await this.logger.info(`transport.${operation}`, "transport operation completed", {
|
|
1865
|
+
operation,
|
|
1866
|
+
agent: session.agent,
|
|
1867
|
+
workspace: session.workspace,
|
|
1868
|
+
alias: session.alias,
|
|
1869
|
+
durationMs: Date.now() - startedAt
|
|
1870
|
+
});
|
|
1871
|
+
return result;
|
|
1872
|
+
} catch (error) {
|
|
1873
|
+
await this.logger.error(`transport.${operation}.failed`, "transport operation failed", {
|
|
1874
|
+
operation,
|
|
1875
|
+
agent: session.agent,
|
|
1876
|
+
workspace: session.workspace,
|
|
1877
|
+
alias: session.alias,
|
|
1878
|
+
durationMs: Date.now() - startedAt,
|
|
1879
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1880
|
+
});
|
|
1881
|
+
throw error;
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
async function pathExists(path) {
|
|
1886
|
+
try {
|
|
1887
|
+
await access(path);
|
|
1888
|
+
return true;
|
|
1889
|
+
} catch {
|
|
1890
|
+
return false;
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
function normalizePathForWorkspace(path) {
|
|
1894
|
+
return normalize(path);
|
|
1895
|
+
}
|
|
1896
|
+
function sameWorkspacePath(left, right) {
|
|
1897
|
+
const normalizedLeft = normalizePathForWorkspace(left);
|
|
1898
|
+
const normalizedRight = normalizePathForWorkspace(right);
|
|
1899
|
+
if (process.platform === "win32") {
|
|
1900
|
+
return normalizedLeft.toLowerCase() === normalizedRight.toLowerCase();
|
|
1901
|
+
}
|
|
1902
|
+
return normalizedLeft === normalizedRight;
|
|
1903
|
+
}
|
|
1904
|
+
var init_command_router = __esm(() => {
|
|
1905
|
+
init_agent_templates();
|
|
1906
|
+
init_app_logger();
|
|
1907
|
+
});
|
|
1908
|
+
|
|
1909
|
+
// src/config/resolve-agent-command.ts
|
|
1910
|
+
function resolveAgentCommand(driver, command) {
|
|
1911
|
+
if (!command) {
|
|
1912
|
+
return;
|
|
1913
|
+
}
|
|
1914
|
+
if (driver === "codex" && isLegacyCodexCommand(command)) {
|
|
1915
|
+
return;
|
|
1916
|
+
}
|
|
1917
|
+
return command;
|
|
1918
|
+
}
|
|
1919
|
+
function isLegacyCodexCommand(command) {
|
|
1920
|
+
const normalized = command.trim().replaceAll("\\", "/").toLowerCase();
|
|
1921
|
+
return normalized === "./node_modules/.bin/codex-acp" || normalized === "./node_modules/.bin/codex-acp.exe" || normalized.endsWith("/node_modules/.bin/codex-acp") || normalized.endsWith("/node_modules/.bin/codex-acp.exe") || normalized.includes("/@zed-industries/codex-acp/bin/codex-acp.js") || normalized.includes("@zed-industries/codex-acp/bin/codex-acp.js");
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1924
|
+
// src/config/load-config.ts
|
|
1925
|
+
import { readFile as readFile3 } from "node:fs/promises";
|
|
1926
|
+
function isRecord(value) {
|
|
1927
|
+
return typeof value === "object" && value !== null;
|
|
1928
|
+
}
|
|
1929
|
+
async function loadConfig(path, options = {}) {
|
|
1930
|
+
const raw = JSON.parse(await readFile3(path, "utf8"));
|
|
1931
|
+
return parseConfig(raw, options);
|
|
1932
|
+
}
|
|
1933
|
+
function parseConfig(raw, options = {}) {
|
|
1934
|
+
if (!isRecord(raw)) {
|
|
1935
|
+
throw new Error("config must be a JSON object");
|
|
1936
|
+
}
|
|
1937
|
+
const transport = raw.transport;
|
|
1938
|
+
if (!isRecord(transport)) {
|
|
1939
|
+
throw new Error("transport must be an object");
|
|
1940
|
+
}
|
|
1941
|
+
if ("type" in transport && transport.type !== "acpx-cli" && transport.type !== "acpx-bridge") {
|
|
1942
|
+
throw new Error("transport.type must be acpx-cli or acpx-bridge");
|
|
1943
|
+
}
|
|
1944
|
+
if ("sessionInitTimeoutMs" in transport && (typeof transport.sessionInitTimeoutMs !== "number" || !Number.isFinite(transport.sessionInitTimeoutMs) || transport.sessionInitTimeoutMs <= 0)) {
|
|
1945
|
+
throw new Error("transport.sessionInitTimeoutMs must be a positive number");
|
|
1946
|
+
}
|
|
1947
|
+
if (!isRecord(raw.agents)) {
|
|
1948
|
+
throw new Error("agents must be an object");
|
|
1949
|
+
}
|
|
1950
|
+
if (!isRecord(raw.workspaces)) {
|
|
1951
|
+
throw new Error("workspaces must be an object");
|
|
1952
|
+
}
|
|
1953
|
+
const logging = raw.logging;
|
|
1954
|
+
if (logging !== undefined && !isRecord(logging)) {
|
|
1955
|
+
throw new Error("logging must be an object");
|
|
1956
|
+
}
|
|
1957
|
+
if (isRecord(logging) && "level" in logging && logging.level !== "error" && logging.level !== "info" && logging.level !== "debug") {
|
|
1958
|
+
throw new Error("logging.level must be error, info, or debug");
|
|
1959
|
+
}
|
|
1960
|
+
for (const field of ["maxSizeBytes", "maxFiles", "retentionDays"]) {
|
|
1961
|
+
if (isRecord(logging) && field in logging && (typeof logging[field] !== "number" || !Number.isFinite(logging[field]) || logging[field] <= 0)) {
|
|
1962
|
+
throw new Error(`logging.${field} must be a positive number`);
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
for (const [name, agent] of Object.entries(raw.agents)) {
|
|
1966
|
+
if (!isRecord(agent) || typeof agent.driver !== "string" || agent.driver.length === 0) {
|
|
1967
|
+
throw new Error(`agent "${name}" must define a non-empty driver`);
|
|
1968
|
+
}
|
|
1969
|
+
if ("command" in agent && (typeof agent.command !== "string" || agent.command.length === 0)) {
|
|
1970
|
+
throw new Error(`agent "${name}" command must be a non-empty string`);
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
for (const [name, workspace] of Object.entries(raw.workspaces)) {
|
|
1974
|
+
if (!isRecord(workspace) || typeof workspace.cwd !== "string" || workspace.cwd.length === 0) {
|
|
1975
|
+
throw new Error(`workspace "${name}" must define a non-empty cwd`);
|
|
1976
|
+
}
|
|
1977
|
+
if ("allowed_agents" in workspace && (!Array.isArray(workspace.allowed_agents) || workspace.allowed_agents.some((value) => typeof value !== "string"))) {
|
|
1978
|
+
throw new Error(`workspace "${name}" allowed_agents must be an array of strings`);
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
const agents = {};
|
|
1982
|
+
for (const [name, agent] of Object.entries(raw.agents)) {
|
|
1983
|
+
const driver = agent.driver;
|
|
1984
|
+
const command = typeof agent.command === "string" ? resolveAgentCommand(driver, agent.command) : undefined;
|
|
1985
|
+
agents[name] = {
|
|
1986
|
+
driver,
|
|
1987
|
+
...command ? { command } : {}
|
|
1988
|
+
};
|
|
1989
|
+
}
|
|
1990
|
+
const workspaces = {};
|
|
1991
|
+
for (const [name, workspace] of Object.entries(raw.workspaces)) {
|
|
1992
|
+
workspaces[name] = {
|
|
1993
|
+
cwd: workspace.cwd,
|
|
1994
|
+
...typeof workspace.description === "string" ? { description: workspace.description } : {}
|
|
1995
|
+
};
|
|
1996
|
+
}
|
|
1997
|
+
return {
|
|
1998
|
+
transport: {
|
|
1999
|
+
...typeof transport.command === "string" ? { command: transport.command } : {},
|
|
2000
|
+
...typeof transport.sessionInitTimeoutMs === "number" ? { sessionInitTimeoutMs: transport.sessionInitTimeoutMs } : {},
|
|
2001
|
+
type: transport.type ?? "acpx-bridge"
|
|
2002
|
+
},
|
|
2003
|
+
logging: {
|
|
2004
|
+
level: typeof logging?.level === "string" ? logging.level : options.defaultLoggingLevel ?? DEFAULT_LOGGING_CONFIG.level,
|
|
2005
|
+
maxSizeBytes: typeof logging?.maxSizeBytes === "number" ? logging.maxSizeBytes : DEFAULT_LOGGING_CONFIG.maxSizeBytes,
|
|
2006
|
+
maxFiles: typeof logging?.maxFiles === "number" ? logging.maxFiles : DEFAULT_LOGGING_CONFIG.maxFiles,
|
|
2007
|
+
retentionDays: typeof logging?.retentionDays === "number" ? logging.retentionDays : DEFAULT_LOGGING_CONFIG.retentionDays
|
|
2008
|
+
},
|
|
2009
|
+
agents,
|
|
2010
|
+
workspaces
|
|
2011
|
+
};
|
|
2012
|
+
}
|
|
2013
|
+
var DEFAULT_LOGGING_CONFIG;
|
|
2014
|
+
var init_load_config = __esm(() => {
|
|
2015
|
+
DEFAULT_LOGGING_CONFIG = {
|
|
2016
|
+
level: "info",
|
|
2017
|
+
maxSizeBytes: 2 * 1024 * 1024,
|
|
2018
|
+
maxFiles: 5,
|
|
2019
|
+
retentionDays: 7
|
|
2020
|
+
};
|
|
2021
|
+
});
|
|
2022
|
+
|
|
2023
|
+
// src/config/config-store.ts
|
|
2024
|
+
import { mkdir as mkdir6, writeFile as writeFile4 } from "node:fs/promises";
|
|
2025
|
+
import { dirname as dirname5 } from "node:path";
|
|
2026
|
+
|
|
2027
|
+
class ConfigStore {
|
|
2028
|
+
path;
|
|
2029
|
+
constructor(path) {
|
|
2030
|
+
this.path = path;
|
|
2031
|
+
}
|
|
2032
|
+
async load() {
|
|
2033
|
+
return await loadConfig(this.path);
|
|
2034
|
+
}
|
|
2035
|
+
async save(config) {
|
|
2036
|
+
await mkdir6(dirname5(this.path), { recursive: true });
|
|
2037
|
+
await writeFile4(this.path, `${JSON.stringify(config, null, 2)}
|
|
2038
|
+
`, "utf8");
|
|
2039
|
+
}
|
|
2040
|
+
async upsertWorkspace(name, cwd, description) {
|
|
2041
|
+
const config = await this.load();
|
|
2042
|
+
const workspace = {
|
|
2043
|
+
cwd,
|
|
2044
|
+
...description ? { description } : {}
|
|
2045
|
+
};
|
|
2046
|
+
config.workspaces[name] = workspace;
|
|
2047
|
+
await this.save(config);
|
|
2048
|
+
return config;
|
|
2049
|
+
}
|
|
2050
|
+
async removeWorkspace(name) {
|
|
2051
|
+
const config = await this.load();
|
|
2052
|
+
delete config.workspaces[name];
|
|
2053
|
+
await this.save(config);
|
|
2054
|
+
return config;
|
|
2055
|
+
}
|
|
2056
|
+
async upsertAgent(name, agent) {
|
|
2057
|
+
const config = await this.load();
|
|
2058
|
+
config.agents[name] = agent;
|
|
2059
|
+
await this.save(config);
|
|
2060
|
+
return config;
|
|
2061
|
+
}
|
|
2062
|
+
async removeAgent(name) {
|
|
2063
|
+
const config = await this.load();
|
|
2064
|
+
delete config.agents[name];
|
|
2065
|
+
await this.save(config);
|
|
2066
|
+
return config;
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
var init_config_store = __esm(() => {
|
|
2070
|
+
init_load_config();
|
|
2071
|
+
});
|
|
2072
|
+
|
|
2073
|
+
// src/config/ensure-config.ts
|
|
2074
|
+
import { readFile as readFile4 } from "node:fs/promises";
|
|
2075
|
+
async function ensureConfigExists(path) {
|
|
2076
|
+
try {
|
|
2077
|
+
await loadConfig(path);
|
|
2078
|
+
} catch (error) {
|
|
2079
|
+
if (!isMissingFileError2(error)) {
|
|
2080
|
+
throw error;
|
|
2081
|
+
}
|
|
2082
|
+
const store = new ConfigStore(path);
|
|
2083
|
+
await store.save(await loadDefaultConfigTemplate());
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
async function loadDefaultConfigTemplate() {
|
|
2087
|
+
const templatePath = new URL("../../config.example.json", import.meta.url);
|
|
2088
|
+
const template = JSON.parse(await readFile4(templatePath, "utf8"));
|
|
2089
|
+
return {
|
|
2090
|
+
...template,
|
|
2091
|
+
agents: Object.fromEntries(Object.entries(template.agents).map(([name, agent]) => [
|
|
2092
|
+
name,
|
|
2093
|
+
{
|
|
2094
|
+
driver: agent.driver,
|
|
2095
|
+
...resolveAgentCommand(agent.driver, agent.command) ? { command: resolveAgentCommand(agent.driver, agent.command) } : {}
|
|
2096
|
+
}
|
|
2097
|
+
])),
|
|
2098
|
+
workspaces: {}
|
|
2099
|
+
};
|
|
2100
|
+
}
|
|
2101
|
+
function isMissingFileError2(error) {
|
|
2102
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
2103
|
+
}
|
|
2104
|
+
var init_ensure_config = __esm(() => {
|
|
2105
|
+
init_config_store();
|
|
2106
|
+
init_load_config();
|
|
2107
|
+
});
|
|
2108
|
+
|
|
2109
|
+
// src/config/resolve-acpx-command.ts
|
|
2110
|
+
import { readFileSync } from "node:fs";
|
|
2111
|
+
import { dirname as dirname6, resolve } from "node:path";
|
|
2112
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
2113
|
+
function resolveAcpxCommand(options = {}) {
|
|
2114
|
+
if (options.configuredCommand) {
|
|
2115
|
+
return options.configuredCommand;
|
|
2116
|
+
}
|
|
2117
|
+
const platform = options.platform ?? process.platform;
|
|
2118
|
+
const resolvePackageJson = options.resolvePackageJson ?? ((id) => require2.resolve(id));
|
|
2119
|
+
const readPackageJson = options.readPackageJson ?? ((path) => JSON.parse(readFileSync(path, "utf8")));
|
|
2120
|
+
try {
|
|
2121
|
+
const packageJsonPath = resolvePackageJson("acpx/package.json");
|
|
2122
|
+
const pkg = readPackageJson(packageJsonPath);
|
|
2123
|
+
const binPath = typeof pkg.bin === "string" ? pkg.bin : pkg.bin && typeof pkg.bin.acpx === "string" ? pkg.bin.acpx : null;
|
|
2124
|
+
if (binPath) {
|
|
2125
|
+
if (platform === "win32") {
|
|
2126
|
+
return resolve(dirname6(packageJsonPath), "../.bin/acpx.exe");
|
|
2127
|
+
}
|
|
2128
|
+
return resolve(dirname6(packageJsonPath), binPath);
|
|
2129
|
+
}
|
|
2130
|
+
} catch {}
|
|
2131
|
+
return "acpx";
|
|
2132
|
+
}
|
|
2133
|
+
var require2;
|
|
2134
|
+
var init_resolve_acpx_command = __esm(() => {
|
|
2135
|
+
require2 = createRequire2(import.meta.url);
|
|
2136
|
+
});
|
|
2137
|
+
|
|
2138
|
+
// src/console-agent.ts
|
|
2139
|
+
class ConsoleAgent {
|
|
2140
|
+
router;
|
|
2141
|
+
logger;
|
|
2142
|
+
constructor(router, logger) {
|
|
2143
|
+
this.router = router;
|
|
2144
|
+
this.logger = logger ?? createNoopAppLogger();
|
|
2145
|
+
}
|
|
2146
|
+
async chat(request) {
|
|
2147
|
+
if (!request.text.trim()) {
|
|
2148
|
+
return { text: "Empty message." };
|
|
2149
|
+
}
|
|
2150
|
+
await this.logger.info("chat.received", "received inbound chat message", {
|
|
2151
|
+
chatKey: request.conversationId,
|
|
2152
|
+
kind: request.text.trim().startsWith("/") ? "command" : "prompt",
|
|
2153
|
+
text: summarizeText(request.text)
|
|
2154
|
+
});
|
|
2155
|
+
return await this.router.handle(request.conversationId, request.text);
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
2158
|
+
function summarizeText(text) {
|
|
2159
|
+
const trimmed = text.trim();
|
|
2160
|
+
if (trimmed.length <= 120) {
|
|
2161
|
+
return trimmed;
|
|
2162
|
+
}
|
|
2163
|
+
return `${trimmed.slice(0, 117)}...`;
|
|
2164
|
+
}
|
|
2165
|
+
var init_console_agent = __esm(() => {
|
|
2166
|
+
init_app_logger();
|
|
2167
|
+
});
|
|
2168
|
+
|
|
2169
|
+
// src/sessions/session-service.ts
|
|
2170
|
+
class SessionService {
|
|
2171
|
+
config;
|
|
2172
|
+
stateStore;
|
|
2173
|
+
state;
|
|
2174
|
+
constructor(config, stateStore, state) {
|
|
2175
|
+
this.config = config;
|
|
2176
|
+
this.stateStore = stateStore;
|
|
2177
|
+
this.state = state;
|
|
2178
|
+
}
|
|
2179
|
+
async createSession(alias, agent, workspace) {
|
|
2180
|
+
return await this.createLogicalSession(alias, agent, workspace, `${workspace}:${alias}`);
|
|
2181
|
+
}
|
|
2182
|
+
resolveSession(alias, agent, workspace, transportSession) {
|
|
2183
|
+
this.validateSession(alias, agent, workspace);
|
|
2184
|
+
return this.toResolvedSession({
|
|
2185
|
+
alias,
|
|
2186
|
+
agent,
|
|
2187
|
+
workspace,
|
|
2188
|
+
transport_session: transportSession,
|
|
2189
|
+
created_at: this.state.sessions[alias]?.created_at ?? new Date().toISOString(),
|
|
2190
|
+
last_used_at: new Date().toISOString()
|
|
2191
|
+
});
|
|
2192
|
+
}
|
|
2193
|
+
async attachSession(alias, agent, workspace, transportSession) {
|
|
2194
|
+
return await this.createLogicalSession(alias, agent, workspace, transportSession);
|
|
2195
|
+
}
|
|
2196
|
+
async useSession(chatKey, alias) {
|
|
2197
|
+
const session = this.state.sessions[alias];
|
|
2198
|
+
if (!session) {
|
|
2199
|
+
throw new Error(`session "${alias}" does not exist`);
|
|
2200
|
+
}
|
|
2201
|
+
session.last_used_at = new Date().toISOString();
|
|
2202
|
+
this.state.chat_contexts[chatKey] = { current_session: alias };
|
|
2203
|
+
await this.persist();
|
|
2204
|
+
}
|
|
2205
|
+
async getCurrentSession(chatKey) {
|
|
2206
|
+
const currentAlias = this.state.chat_contexts[chatKey]?.current_session;
|
|
2207
|
+
if (!currentAlias) {
|
|
2208
|
+
return null;
|
|
2209
|
+
}
|
|
2210
|
+
const session = this.state.sessions[currentAlias];
|
|
2211
|
+
if (!session) {
|
|
2212
|
+
return null;
|
|
2213
|
+
}
|
|
2214
|
+
session.last_used_at = new Date().toISOString();
|
|
2215
|
+
await this.persist();
|
|
2216
|
+
return this.toResolvedSession(session);
|
|
2217
|
+
}
|
|
2218
|
+
async listSessions(chatKey) {
|
|
2219
|
+
const currentAlias = this.state.chat_contexts[chatKey]?.current_session;
|
|
2220
|
+
return Object.values(this.state.sessions).map((session) => ({
|
|
2221
|
+
alias: session.alias,
|
|
2222
|
+
agent: session.agent,
|
|
2223
|
+
workspace: session.workspace,
|
|
2224
|
+
isCurrent: session.alias === currentAlias
|
|
2225
|
+
}));
|
|
2226
|
+
}
|
|
2227
|
+
toResolvedSession(session) {
|
|
2228
|
+
const agentConfig = this.config.agents[session.agent];
|
|
2229
|
+
return {
|
|
2230
|
+
alias: session.alias,
|
|
2231
|
+
agent: session.agent,
|
|
2232
|
+
agentCommand: resolveAgentCommand(agentConfig.driver, agentConfig.command),
|
|
2233
|
+
workspace: session.workspace,
|
|
2234
|
+
transportSession: session.transport_session,
|
|
2235
|
+
cwd: this.config.workspaces[session.workspace].cwd
|
|
2236
|
+
};
|
|
2237
|
+
}
|
|
2238
|
+
async persist() {
|
|
2239
|
+
await this.stateStore.save(this.state);
|
|
2240
|
+
}
|
|
2241
|
+
async createLogicalSession(alias, agent, workspace, transportSession) {
|
|
2242
|
+
this.validateSession(alias, agent, workspace);
|
|
2243
|
+
const existingSession = this.state.sessions[alias];
|
|
2244
|
+
const now = new Date().toISOString();
|
|
2245
|
+
const session = {
|
|
2246
|
+
alias,
|
|
2247
|
+
agent,
|
|
2248
|
+
workspace,
|
|
2249
|
+
transport_session: transportSession,
|
|
2250
|
+
created_at: existingSession?.created_at ?? now,
|
|
2251
|
+
last_used_at: now
|
|
2252
|
+
};
|
|
2253
|
+
this.state.sessions[alias] = session;
|
|
2254
|
+
await this.persist();
|
|
2255
|
+
return this.toResolvedSession(session);
|
|
2256
|
+
}
|
|
2257
|
+
validateSession(alias, agent, workspace) {
|
|
2258
|
+
if (!this.config.workspaces[workspace]) {
|
|
2259
|
+
throw new Error(`workspace "${workspace}" is not registered`);
|
|
2260
|
+
}
|
|
2261
|
+
if (!this.config.agents[agent]) {
|
|
2262
|
+
throw new Error(`agent "${agent}" is not registered`);
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2266
|
+
var init_session_service = () => {};
|
|
2267
|
+
|
|
2268
|
+
// src/state/types.ts
|
|
2269
|
+
function createEmptyState() {
|
|
2270
|
+
return {
|
|
2271
|
+
sessions: {},
|
|
2272
|
+
chat_contexts: {}
|
|
2273
|
+
};
|
|
2274
|
+
}
|
|
2275
|
+
|
|
2276
|
+
// src/state/state-store.ts
|
|
2277
|
+
import { mkdir as mkdir7, readFile as readFile5, writeFile as writeFile5 } from "node:fs/promises";
|
|
2278
|
+
import { dirname as dirname7 } from "node:path";
|
|
2279
|
+
|
|
2280
|
+
class StateStore {
|
|
2281
|
+
path;
|
|
2282
|
+
constructor(path) {
|
|
2283
|
+
this.path = path;
|
|
2284
|
+
}
|
|
2285
|
+
async load() {
|
|
2286
|
+
try {
|
|
2287
|
+
const content = await readFile5(this.path, "utf8");
|
|
2288
|
+
if (content.trim() === "") {
|
|
2289
|
+
return createEmptyState();
|
|
2290
|
+
}
|
|
2291
|
+
return JSON.parse(content);
|
|
2292
|
+
} catch (error) {
|
|
2293
|
+
if (error.code === "ENOENT") {
|
|
2294
|
+
return createEmptyState();
|
|
2295
|
+
}
|
|
2296
|
+
throw error;
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
async save(state) {
|
|
2300
|
+
await mkdir7(dirname7(this.path), { recursive: true });
|
|
2301
|
+
await writeFile5(this.path, JSON.stringify(state, null, 2));
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
var init_state_store = () => {};
|
|
2305
|
+
|
|
2306
|
+
// src/run-console.ts
|
|
2307
|
+
var exports_run_console = {};
|
|
2308
|
+
__export(exports_run_console, {
|
|
2309
|
+
runConsole: () => runConsole
|
|
2310
|
+
});
|
|
2311
|
+
async function runConsole(paths, deps) {
|
|
2312
|
+
const runtime = await deps.buildApp(paths);
|
|
2313
|
+
const sdk = await deps.loadWeixinSdk();
|
|
2314
|
+
const setIntervalFn = deps.setInterval ?? ((fn, delay) => setInterval(fn, delay));
|
|
2315
|
+
const clearIntervalFn = deps.clearInterval ?? ((timer) => clearInterval(timer));
|
|
2316
|
+
let heartbeatTimer = null;
|
|
2317
|
+
try {
|
|
2318
|
+
if (deps.daemonRuntime) {
|
|
2319
|
+
await deps.daemonRuntime.start({
|
|
2320
|
+
configPath: paths.configPath,
|
|
2321
|
+
statePath: paths.statePath
|
|
2322
|
+
});
|
|
2323
|
+
heartbeatTimer = setIntervalFn(() => deps.daemonRuntime?.heartbeat(), deps.heartbeatIntervalMs ?? 30000);
|
|
2324
|
+
}
|
|
2325
|
+
await sdk.start(runtime.agent);
|
|
2326
|
+
} finally {
|
|
2327
|
+
if (heartbeatTimer !== null) {
|
|
2328
|
+
clearIntervalFn(heartbeatTimer);
|
|
2329
|
+
}
|
|
2330
|
+
await runtime.dispose();
|
|
2331
|
+
if (deps.daemonRuntime) {
|
|
2332
|
+
await deps.daemonRuntime.stop();
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
|
|
2337
|
+
// src/transport/acpx-bridge/acpx-bridge-protocol.ts
|
|
2338
|
+
function encodeBridgeRequest(request) {
|
|
2339
|
+
return `${JSON.stringify(request)}
|
|
2340
|
+
`;
|
|
2341
|
+
}
|
|
2342
|
+
|
|
2343
|
+
// src/transport/acpx-bridge/acpx-bridge-client.ts
|
|
2344
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
2345
|
+
import { fileURLToPath } from "node:url";
|
|
2346
|
+
import { createInterface } from "node:readline";
|
|
2347
|
+
|
|
2348
|
+
class AcpxBridgeClient {
|
|
2349
|
+
writeLine;
|
|
2350
|
+
nextId = 1;
|
|
2351
|
+
pending = new Map;
|
|
2352
|
+
constructor(writeLine) {
|
|
2353
|
+
this.writeLine = writeLine;
|
|
2354
|
+
}
|
|
2355
|
+
request(method, params) {
|
|
2356
|
+
const id = String(this.nextId);
|
|
2357
|
+
this.nextId += 1;
|
|
2358
|
+
return awaitable((resolve2, reject) => {
|
|
2359
|
+
this.pending.set(id, { resolve: resolve2, reject });
|
|
2360
|
+
this.writeLine(encodeBridgeRequest({
|
|
2361
|
+
id,
|
|
2362
|
+
method,
|
|
2363
|
+
params
|
|
2364
|
+
}));
|
|
2365
|
+
});
|
|
2366
|
+
}
|
|
2367
|
+
handleLine(line) {
|
|
2368
|
+
const response = JSON.parse(line);
|
|
2369
|
+
const pending = this.pending.get(response.id);
|
|
2370
|
+
if (!pending) {
|
|
2371
|
+
return;
|
|
2372
|
+
}
|
|
2373
|
+
this.pending.delete(response.id);
|
|
2374
|
+
if (response.ok) {
|
|
2375
|
+
pending.resolve(response.result);
|
|
2376
|
+
return;
|
|
2377
|
+
}
|
|
2378
|
+
pending.reject(new Error(response.error.message));
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
function buildBridgeSpawnSpec(options) {
|
|
2382
|
+
if (options.execPath.endsWith("bun")) {
|
|
2383
|
+
return {
|
|
2384
|
+
command: options.execPath,
|
|
2385
|
+
args: ["run", options.bridgeEntryPath]
|
|
2386
|
+
};
|
|
2387
|
+
}
|
|
2388
|
+
return {
|
|
2389
|
+
command: options.execPath,
|
|
2390
|
+
args: [options.bridgeEntryPath]
|
|
2391
|
+
};
|
|
2392
|
+
}
|
|
2393
|
+
async function spawnAcpxBridgeClient(options = {}) {
|
|
2394
|
+
const bridgeEntryPath = options.bridgeEntryPath ?? fileURLToPath(new URL("../../bridge/bridge-main.ts", import.meta.url));
|
|
2395
|
+
const spawnSpec = buildBridgeSpawnSpec({
|
|
2396
|
+
execPath: process.execPath,
|
|
2397
|
+
bridgeEntryPath
|
|
2398
|
+
});
|
|
2399
|
+
const child = spawn2(spawnSpec.command, spawnSpec.args, {
|
|
2400
|
+
cwd: options.cwd ?? process.cwd(),
|
|
2401
|
+
env: {
|
|
2402
|
+
...process.env,
|
|
2403
|
+
WEACPX_BRIDGE_ACPX_COMMAND: options.acpxCommand ?? "acpx"
|
|
2404
|
+
},
|
|
2405
|
+
stdio: ["pipe", "pipe", "inherit"]
|
|
2406
|
+
});
|
|
2407
|
+
const client = new AcpxBridgeClient((line) => {
|
|
2408
|
+
child.stdin.write(line);
|
|
2409
|
+
});
|
|
2410
|
+
const output = createInterface({
|
|
2411
|
+
input: child.stdout,
|
|
2412
|
+
crlfDelay: Infinity
|
|
2413
|
+
});
|
|
2414
|
+
output.on("line", (line) => {
|
|
2415
|
+
client.handleLine(line);
|
|
2416
|
+
});
|
|
2417
|
+
child.on("exit", () => {
|
|
2418
|
+
output.close();
|
|
2419
|
+
});
|
|
2420
|
+
client.waitUntilReady = async () => {
|
|
2421
|
+
await client.request("ping", {});
|
|
2422
|
+
};
|
|
2423
|
+
client.dispose = async () => {
|
|
2424
|
+
try {
|
|
2425
|
+
await client.request("shutdown", {});
|
|
2426
|
+
} finally {
|
|
2427
|
+
child.stdin.end();
|
|
2428
|
+
child.kill("SIGTERM");
|
|
2429
|
+
}
|
|
2430
|
+
};
|
|
2431
|
+
await client.waitUntilReady();
|
|
2432
|
+
return client;
|
|
2433
|
+
}
|
|
2434
|
+
function awaitable(executor) {
|
|
2435
|
+
return new Promise((resolve2, reject) => {
|
|
2436
|
+
executor(resolve2, reject);
|
|
2437
|
+
});
|
|
2438
|
+
}
|
|
2439
|
+
var init_acpx_bridge_client = () => {};
|
|
2440
|
+
|
|
2441
|
+
// src/transport/acpx-bridge/acpx-bridge-transport.ts
|
|
2442
|
+
class AcpxBridgeTransport {
|
|
2443
|
+
client;
|
|
2444
|
+
constructor(client) {
|
|
2445
|
+
this.client = client;
|
|
2446
|
+
}
|
|
2447
|
+
async ensureSession(session) {
|
|
2448
|
+
await this.client.request("ensureSession", this.toParams(session));
|
|
2449
|
+
}
|
|
2450
|
+
async prompt(session, text) {
|
|
2451
|
+
return await this.client.request("prompt", {
|
|
2452
|
+
...this.toParams(session),
|
|
2453
|
+
text
|
|
2454
|
+
});
|
|
2455
|
+
}
|
|
2456
|
+
async cancel(session) {
|
|
2457
|
+
return await this.client.request("cancel", this.toParams(session));
|
|
2458
|
+
}
|
|
2459
|
+
async hasSession(session) {
|
|
2460
|
+
const result = await this.client.request("hasSession", this.toParams(session));
|
|
2461
|
+
return result.exists;
|
|
2462
|
+
}
|
|
2463
|
+
async listSessions() {
|
|
2464
|
+
return [];
|
|
2465
|
+
}
|
|
2466
|
+
async dispose() {
|
|
2467
|
+
await this.client.dispose?.();
|
|
2468
|
+
}
|
|
2469
|
+
toParams(session) {
|
|
2470
|
+
return {
|
|
2471
|
+
agent: session.agent,
|
|
2472
|
+
agentCommand: session.agentCommand,
|
|
2473
|
+
cwd: session.cwd,
|
|
2474
|
+
name: session.transportSession
|
|
2475
|
+
};
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2478
|
+
|
|
2479
|
+
// src/transport/acpx-cli/node-pty-helper.ts
|
|
2480
|
+
import { chmod as chmodFs } from "node:fs/promises";
|
|
2481
|
+
import { dirname as dirname8, join as join3 } from "node:path";
|
|
2482
|
+
function resolveNodePtyHelperPath(packageJsonPath, platform, arch) {
|
|
2483
|
+
if (platform === "win32") {
|
|
2484
|
+
return null;
|
|
2485
|
+
}
|
|
2486
|
+
return join3(dirname8(packageJsonPath), "prebuilds", `${platform}-${arch}`, "spawn-helper");
|
|
2487
|
+
}
|
|
2488
|
+
async function ensureNodePtyHelperExecutable(helperPath, chmod = chmodFs) {
|
|
2489
|
+
if (!helperPath) {
|
|
2490
|
+
return;
|
|
2491
|
+
}
|
|
2492
|
+
try {
|
|
2493
|
+
await chmod(helperPath, 493);
|
|
2494
|
+
} catch (error) {
|
|
2495
|
+
if (error.code === "ENOENT") {
|
|
2496
|
+
return;
|
|
2497
|
+
}
|
|
2498
|
+
throw error;
|
|
2499
|
+
}
|
|
2500
|
+
}
|
|
2501
|
+
var init_node_pty_helper = () => {};
|
|
2502
|
+
|
|
2503
|
+
// src/transport/acpx-cli/acpx-cli-transport.ts
|
|
2504
|
+
import { createRequire as createRequire3 } from "node:module";
|
|
2505
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
2506
|
+
import { spawn as spawnPty } from "node-pty";
|
|
2507
|
+
async function defaultRunner(command, args, options) {
|
|
2508
|
+
return await new Promise((resolve2, reject) => {
|
|
2509
|
+
const child = spawn3(command, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
2510
|
+
let stdout = "";
|
|
2511
|
+
let stderr = "";
|
|
2512
|
+
const timeoutId = options?.timeoutMs ? setTimeout(() => {
|
|
2513
|
+
child.kill("SIGTERM");
|
|
2514
|
+
reject(new Error(`acpx command timed out after ${options.timeoutMs}ms: ${renderCommandForError(args)}`));
|
|
2515
|
+
}, options.timeoutMs) : undefined;
|
|
2516
|
+
child.stdout.on("data", (chunk) => {
|
|
2517
|
+
stdout += String(chunk);
|
|
2518
|
+
});
|
|
2519
|
+
child.stderr.on("data", (chunk) => {
|
|
2520
|
+
stderr += String(chunk);
|
|
2521
|
+
});
|
|
2522
|
+
child.on("error", (error) => {
|
|
2523
|
+
if (timeoutId)
|
|
2524
|
+
clearTimeout(timeoutId);
|
|
2525
|
+
reject(error);
|
|
2526
|
+
});
|
|
2527
|
+
child.on("close", (code) => {
|
|
2528
|
+
if (timeoutId)
|
|
2529
|
+
clearTimeout(timeoutId);
|
|
2530
|
+
resolve2({ code: code ?? 1, stdout, stderr });
|
|
2531
|
+
});
|
|
2532
|
+
});
|
|
2533
|
+
}
|
|
2534
|
+
async function defaultPtyRunner(command, args, options) {
|
|
2535
|
+
const helperPath = resolveNodePtyHelperPath(require3.resolve("node-pty/package.json"), process.platform, process.arch);
|
|
2536
|
+
await ensureNodePtyHelperExecutable(helperPath);
|
|
2537
|
+
return await new Promise((resolve2, reject) => {
|
|
2538
|
+
const child = spawnPty(command, args, {
|
|
2539
|
+
name: "xterm-color",
|
|
2540
|
+
cols: 80,
|
|
2541
|
+
rows: 24,
|
|
2542
|
+
cwd: process.cwd(),
|
|
2543
|
+
env: process.env
|
|
2544
|
+
});
|
|
2545
|
+
let output = "";
|
|
2546
|
+
const timeoutId = options?.timeoutMs ? setTimeout(() => {
|
|
2547
|
+
child.kill();
|
|
2548
|
+
reject(new Error(`acpx command timed out after ${options.timeoutMs}ms: ${renderCommandForError(args)}`));
|
|
2549
|
+
}, options.timeoutMs) : undefined;
|
|
2550
|
+
child.onData((chunk) => {
|
|
2551
|
+
output += chunk;
|
|
2552
|
+
});
|
|
2553
|
+
child.onExit(({ exitCode }) => {
|
|
2554
|
+
if (timeoutId)
|
|
2555
|
+
clearTimeout(timeoutId);
|
|
2556
|
+
resolve2({ code: exitCode, stdout: output, stderr: "" });
|
|
2557
|
+
});
|
|
2558
|
+
});
|
|
2559
|
+
}
|
|
2560
|
+
|
|
2561
|
+
class AcpxCliTransport {
|
|
2562
|
+
command;
|
|
2563
|
+
sessionInitTimeoutMs;
|
|
2564
|
+
runCommand;
|
|
2565
|
+
runPtyCommand;
|
|
2566
|
+
constructor(options, runCommand = defaultRunner, runPtyCommand = defaultPtyRunner) {
|
|
2567
|
+
this.command = options.command ?? "acpx";
|
|
2568
|
+
this.sessionInitTimeoutMs = options.sessionInitTimeoutMs ?? 120000;
|
|
2569
|
+
this.runCommand = runCommand;
|
|
2570
|
+
this.runPtyCommand = runPtyCommand;
|
|
2571
|
+
}
|
|
2572
|
+
async ensureSession(session) {
|
|
2573
|
+
const args = this.buildArgs(session, [
|
|
2574
|
+
"sessions",
|
|
2575
|
+
"new",
|
|
2576
|
+
"--name",
|
|
2577
|
+
session.transportSession
|
|
2578
|
+
]);
|
|
2579
|
+
const runEnsure = session.agentCommand ? this.run : this.runWithPty;
|
|
2580
|
+
await runEnsure.call(this, args, {
|
|
2581
|
+
timeoutMs: this.sessionInitTimeoutMs
|
|
2582
|
+
});
|
|
2583
|
+
}
|
|
2584
|
+
async prompt(session, text) {
|
|
2585
|
+
const output = await this.run(this.buildPromptArgs(session, text));
|
|
2586
|
+
return { text: sanitizePromptText(extractPromptText(output)) };
|
|
2587
|
+
}
|
|
2588
|
+
async cancel(session) {
|
|
2589
|
+
const output = await this.run(this.buildArgs(session, [
|
|
2590
|
+
"cancel",
|
|
2591
|
+
"-s",
|
|
2592
|
+
session.transportSession
|
|
2593
|
+
]));
|
|
2594
|
+
return {
|
|
2595
|
+
cancelled: true,
|
|
2596
|
+
message: output.trim()
|
|
2597
|
+
};
|
|
2598
|
+
}
|
|
2599
|
+
async hasSession(session) {
|
|
2600
|
+
const result = await this.runCommand(this.command, this.buildArgs(session, [
|
|
2601
|
+
"sessions",
|
|
2602
|
+
"show",
|
|
2603
|
+
session.transportSession
|
|
2604
|
+
]));
|
|
2605
|
+
return result.code === 0;
|
|
2606
|
+
}
|
|
2607
|
+
async listSessions() {
|
|
2608
|
+
return [];
|
|
2609
|
+
}
|
|
2610
|
+
async run(args, options) {
|
|
2611
|
+
const result = await this.runCommandWithTimeout(this.runCommand, args, options);
|
|
2612
|
+
if (result.code !== 0) {
|
|
2613
|
+
const detail = normalizeCommandError(result) ?? `command failed with exit code ${result.code}`;
|
|
2614
|
+
throw new Error(detail);
|
|
2615
|
+
}
|
|
2616
|
+
return result.stdout;
|
|
2617
|
+
}
|
|
2618
|
+
async runWithPty(args, options) {
|
|
2619
|
+
const result = await this.runCommandWithTimeout(this.runPtyCommand, args, options);
|
|
2620
|
+
if (result.code !== 0) {
|
|
2621
|
+
const detail = normalizeCommandError(result) ?? `command failed with exit code ${result.code}`;
|
|
2622
|
+
throw new Error(detail);
|
|
2623
|
+
}
|
|
2624
|
+
return result.stdout;
|
|
2625
|
+
}
|
|
2626
|
+
async runCommandWithTimeout(runner, args, options) {
|
|
2627
|
+
if (!options?.timeoutMs) {
|
|
2628
|
+
return await runner(this.command, args, undefined);
|
|
2629
|
+
}
|
|
2630
|
+
let timeoutId;
|
|
2631
|
+
return await Promise.race([
|
|
2632
|
+
runner(this.command, args, options).finally(() => {
|
|
2633
|
+
if (timeoutId)
|
|
2634
|
+
clearTimeout(timeoutId);
|
|
2635
|
+
}),
|
|
2636
|
+
new Promise((_, reject) => {
|
|
2637
|
+
timeoutId = setTimeout(() => {
|
|
2638
|
+
reject(new Error(`acpx command timed out after ${options.timeoutMs}ms: ${renderCommandForError(args)}`));
|
|
2639
|
+
}, options.timeoutMs);
|
|
2640
|
+
})
|
|
2641
|
+
]);
|
|
2642
|
+
}
|
|
2643
|
+
buildArgs(session, tail) {
|
|
2644
|
+
const prefix = ["--format", "quiet", "--cwd", session.cwd];
|
|
2645
|
+
if (session.agentCommand) {
|
|
2646
|
+
return [...prefix, "--agent", session.agentCommand, ...tail];
|
|
2647
|
+
}
|
|
2648
|
+
return [...prefix, session.agent, ...tail];
|
|
2649
|
+
}
|
|
2650
|
+
buildPromptArgs(session, text) {
|
|
2651
|
+
const prefix = ["--format", "json", "--json-strict", "--cwd", session.cwd];
|
|
2652
|
+
const tail = ["prompt", "-s", session.transportSession, text];
|
|
2653
|
+
if (session.agentCommand) {
|
|
2654
|
+
return [...prefix, "--agent", session.agentCommand, ...tail];
|
|
2655
|
+
}
|
|
2656
|
+
return [...prefix, session.agent, ...tail];
|
|
2657
|
+
}
|
|
2658
|
+
}
|
|
2659
|
+
function renderCommandForError(args) {
|
|
2660
|
+
const rendered = [];
|
|
2661
|
+
for (let index = 0;index < args.length; index += 1) {
|
|
2662
|
+
const arg = args[index];
|
|
2663
|
+
if (arg === "--format") {
|
|
2664
|
+
index += 1;
|
|
2665
|
+
continue;
|
|
2666
|
+
}
|
|
2667
|
+
if (arg === "--cwd") {
|
|
2668
|
+
index += 1;
|
|
2669
|
+
continue;
|
|
2670
|
+
}
|
|
2671
|
+
rendered.push(/\s/.test(arg) || arg.includes(":") ? `"${arg}"` : arg);
|
|
2672
|
+
}
|
|
2673
|
+
return rendered.join(" ");
|
|
2674
|
+
}
|
|
2675
|
+
function extractPromptText(output) {
|
|
2676
|
+
const lines = output.split(`
|
|
2677
|
+
`).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
2678
|
+
const messageSegments = [];
|
|
2679
|
+
let currentSegment = "";
|
|
2680
|
+
for (const line of lines) {
|
|
2681
|
+
try {
|
|
2682
|
+
const event = JSON.parse(line);
|
|
2683
|
+
const isMessageChunk = event.method === "session/update" && event.params?.update?.sessionUpdate === "agent_message_chunk" && event.params.update.content?.type === "text" && typeof event.params.update.content.text === "string";
|
|
2684
|
+
if (isMessageChunk) {
|
|
2685
|
+
const chunk = event.params.update.content.text ?? "";
|
|
2686
|
+
if (chunk.length > 0) {
|
|
2687
|
+
currentSegment += chunk;
|
|
2688
|
+
}
|
|
2689
|
+
continue;
|
|
2690
|
+
}
|
|
2691
|
+
if (currentSegment.trim().length > 0) {
|
|
2692
|
+
messageSegments.push(currentSegment.trim());
|
|
2693
|
+
}
|
|
2694
|
+
currentSegment = "";
|
|
2695
|
+
} catch {
|
|
2696
|
+
if (currentSegment.trim().length > 0) {
|
|
2697
|
+
messageSegments.push(currentSegment.trim());
|
|
2698
|
+
currentSegment = "";
|
|
2699
|
+
}
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
if (currentSegment.trim().length > 0) {
|
|
2703
|
+
messageSegments.push(currentSegment.trim());
|
|
2704
|
+
}
|
|
2705
|
+
if (messageSegments.length > 0) {
|
|
2706
|
+
return messageSegments[messageSegments.length - 1];
|
|
2707
|
+
}
|
|
2708
|
+
return output.trim();
|
|
2709
|
+
}
|
|
2710
|
+
function sanitizePromptText(text) {
|
|
2711
|
+
const trimmed = text.trim();
|
|
2712
|
+
const paragraphs = trimmed.split(/\n\s*\n/);
|
|
2713
|
+
if (paragraphs.length < 2) {
|
|
2714
|
+
return trimmed;
|
|
2715
|
+
}
|
|
2716
|
+
const firstParagraph = paragraphs[0].trim().replace(/\s+/g, " ").toLowerCase();
|
|
2717
|
+
if (!looksLikeWorkflowPreamble(firstParagraph)) {
|
|
2718
|
+
return trimmed;
|
|
2719
|
+
}
|
|
2720
|
+
return paragraphs.slice(1).join(`
|
|
2721
|
+
|
|
2722
|
+
`).trim();
|
|
2723
|
+
}
|
|
2724
|
+
function looksLikeWorkflowPreamble(paragraph) {
|
|
2725
|
+
if (!paragraph.startsWith("using ")) {
|
|
2726
|
+
return false;
|
|
2727
|
+
}
|
|
2728
|
+
return paragraph.includes("using-superpowers") || paragraph.includes("repo workflow requirement") || paragraph.includes("workflow requirement") || paragraph.includes("before responding") || paragraph.includes("skill check");
|
|
2729
|
+
}
|
|
2730
|
+
function normalizeCommandError(result) {
|
|
2731
|
+
const rpcMessages = extractJsonRpcErrorMessages(result.stderr).concat(extractJsonRpcErrorMessages(result.stdout)).filter((message) => message.length > 0);
|
|
2732
|
+
const preferredMessage = [...rpcMessages].reverse().find((message) => message !== "Resource not found");
|
|
2733
|
+
if (preferredMessage) {
|
|
2734
|
+
return preferredMessage;
|
|
2735
|
+
}
|
|
2736
|
+
if (rpcMessages.length > 0) {
|
|
2737
|
+
return rpcMessages[rpcMessages.length - 1] ?? null;
|
|
2738
|
+
}
|
|
2739
|
+
return result.stderr.trim() || result.stdout.trim() || null;
|
|
2740
|
+
}
|
|
2741
|
+
function extractJsonRpcErrorMessages(output) {
|
|
2742
|
+
return output.split(`
|
|
2743
|
+
`).map((line) => line.trim()).filter((line) => line.length > 0).flatMap((line) => {
|
|
2744
|
+
try {
|
|
2745
|
+
const payload = JSON.parse(line);
|
|
2746
|
+
if (typeof payload.error?.message === "string" && payload.error.message.length > 0) {
|
|
2747
|
+
return [payload.error.message];
|
|
2748
|
+
}
|
|
2749
|
+
} catch {
|
|
2750
|
+
return [];
|
|
2751
|
+
}
|
|
2752
|
+
return [];
|
|
2753
|
+
});
|
|
2754
|
+
}
|
|
2755
|
+
var require3;
|
|
2756
|
+
var init_acpx_cli_transport = __esm(() => {
|
|
2757
|
+
init_node_pty_helper();
|
|
2758
|
+
require3 = createRequire3(import.meta.url);
|
|
2759
|
+
});
|
|
2760
|
+
|
|
2761
|
+
// src/main.ts
|
|
2762
|
+
var exports_main = {};
|
|
2763
|
+
__export(exports_main, {
|
|
2764
|
+
resolveRuntimePaths: () => resolveRuntimePaths,
|
|
2765
|
+
main: () => main2,
|
|
2766
|
+
buildApp: () => buildApp
|
|
2767
|
+
});
|
|
2768
|
+
import { homedir } from "node:os";
|
|
2769
|
+
import { dirname as dirname9, join as join4 } from "node:path";
|
|
2770
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
2771
|
+
async function buildApp(paths, deps = {}) {
|
|
2772
|
+
await ensureConfigExists(paths.configPath);
|
|
2773
|
+
const configStore = new ConfigStore(paths.configPath);
|
|
2774
|
+
const config = await loadConfig(paths.configPath, {
|
|
2775
|
+
defaultLoggingLevel: deps.defaultLoggingLevel
|
|
2776
|
+
});
|
|
2777
|
+
const logger = createAppLogger({
|
|
2778
|
+
filePath: resolveAppLogPath(paths.configPath),
|
|
2779
|
+
level: config.logging.level,
|
|
2780
|
+
maxSizeBytes: config.logging.maxSizeBytes,
|
|
2781
|
+
maxFiles: config.logging.maxFiles,
|
|
2782
|
+
retentionDays: config.logging.retentionDays,
|
|
2783
|
+
now: deps.loggerNow
|
|
2784
|
+
});
|
|
2785
|
+
await logger.cleanup();
|
|
2786
|
+
const acpxCommand = resolveAcpxCommand({ configuredCommand: config.transport.command });
|
|
2787
|
+
const stateStore = new StateStore(paths.statePath);
|
|
2788
|
+
const state = await stateStore.load();
|
|
2789
|
+
const sessions = new SessionService(config, stateStore, state);
|
|
2790
|
+
const transport = config.transport.type === "acpx-bridge" ? await (deps.createBridgeTransport?.() ?? Promise.resolve(new AcpxBridgeTransport(await spawnAcpxBridgeClient({
|
|
2791
|
+
acpxCommand,
|
|
2792
|
+
bridgeEntryPath: resolveBridgeEntryPath()
|
|
2793
|
+
})))) : deps.createCliTransport?.(acpxCommand) ?? new AcpxCliTransport({ ...config.transport, command: acpxCommand });
|
|
2794
|
+
const router = new CommandRouter(sessions, transport, config, configStore, logger);
|
|
2795
|
+
const agent = new ConsoleAgent(router, logger);
|
|
2796
|
+
return {
|
|
2797
|
+
agent,
|
|
2798
|
+
router,
|
|
2799
|
+
sessions,
|
|
2800
|
+
stateStore,
|
|
2801
|
+
configStore,
|
|
2802
|
+
logger,
|
|
2803
|
+
dispose: async () => {
|
|
2804
|
+
if ("dispose" in transport && typeof transport.dispose === "function") {
|
|
2805
|
+
await transport.dispose();
|
|
2806
|
+
}
|
|
2807
|
+
}
|
|
2808
|
+
};
|
|
2809
|
+
}
|
|
2810
|
+
async function main2() {
|
|
2811
|
+
const paths = resolveRuntimePaths();
|
|
2812
|
+
try {
|
|
2813
|
+
await runConsole(paths, {
|
|
2814
|
+
buildApp,
|
|
2815
|
+
loadWeixinSdk
|
|
2816
|
+
});
|
|
2817
|
+
} catch (error) {
|
|
2818
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2819
|
+
throw new Error([
|
|
2820
|
+
"Failed to start weacpx console.",
|
|
2821
|
+
`config: ${paths.configPath}`,
|
|
2822
|
+
`state: ${paths.statePath}`,
|
|
2823
|
+
message
|
|
2824
|
+
].join(`
|
|
2825
|
+
`));
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2828
|
+
function resolveRuntimePaths() {
|
|
2829
|
+
const home = process.env.HOME ?? homedir();
|
|
2830
|
+
if (!home) {
|
|
2831
|
+
throw new Error("Unable to resolve the current user home directory");
|
|
2832
|
+
}
|
|
2833
|
+
return {
|
|
2834
|
+
configPath: process.env.WEACPX_CONFIG ?? `${home}/.weacpx/config.json`,
|
|
2835
|
+
statePath: process.env.WEACPX_STATE ?? `${home}/.weacpx/state.json`
|
|
2836
|
+
};
|
|
2837
|
+
}
|
|
2838
|
+
function resolveBridgeEntryPath() {
|
|
2839
|
+
if (import.meta.url.includes("/dist/")) {
|
|
2840
|
+
return fileURLToPath2(new URL("./bridge/bridge-main.js", import.meta.url));
|
|
2841
|
+
}
|
|
2842
|
+
return fileURLToPath2(new URL("./bridge/bridge-main.ts", import.meta.url));
|
|
2843
|
+
}
|
|
2844
|
+
function resolveAppLogPath(configPath) {
|
|
2845
|
+
const rootDir = dirname9(configPath);
|
|
2846
|
+
const runtimeDir = join4(rootDir, "runtime");
|
|
2847
|
+
return join4(runtimeDir, "app.log");
|
|
2848
|
+
}
|
|
2849
|
+
var init_main = __esm(async () => {
|
|
2850
|
+
init_command_router();
|
|
2851
|
+
init_config_store();
|
|
2852
|
+
init_ensure_config();
|
|
2853
|
+
init_load_config();
|
|
2854
|
+
init_resolve_acpx_command();
|
|
2855
|
+
init_console_agent();
|
|
2856
|
+
init_app_logger();
|
|
2857
|
+
init_session_service();
|
|
2858
|
+
init_state_store();
|
|
2859
|
+
init_acpx_bridge_client();
|
|
2860
|
+
init_acpx_cli_transport();
|
|
2861
|
+
if (false) {}
|
|
2862
|
+
});
|
|
2863
|
+
|
|
2864
|
+
// src/cli.ts
|
|
2865
|
+
import { homedir as homedir2 } from "node:os";
|
|
2866
|
+
import { sep } from "node:path";
|
|
2867
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
2868
|
+
|
|
2869
|
+
// src/daemon/create-daemon-controller.ts
|
|
2870
|
+
import { mkdir as mkdir3, open } from "node:fs/promises";
|
|
2871
|
+
import { spawn } from "node:child_process";
|
|
2872
|
+
|
|
2873
|
+
// src/daemon/daemon-controller.ts
|
|
2874
|
+
import { mkdir as mkdir2, readFile as readFile2, rm as rm2, writeFile as writeFile2 } from "node:fs/promises";
|
|
2875
|
+
import { dirname as dirname2 } from "node:path";
|
|
2876
|
+
|
|
2877
|
+
// src/daemon/daemon-status.ts
|
|
2878
|
+
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
2879
|
+
import { dirname } from "node:path";
|
|
2880
|
+
|
|
2881
|
+
class DaemonStatusStore {
|
|
2882
|
+
path;
|
|
2883
|
+
constructor(path) {
|
|
2884
|
+
this.path = path;
|
|
2885
|
+
}
|
|
2886
|
+
async load() {
|
|
2887
|
+
try {
|
|
2888
|
+
const content = await readFile(this.path, "utf8");
|
|
2889
|
+
if (content.trim() === "") {
|
|
2890
|
+
return null;
|
|
2891
|
+
}
|
|
2892
|
+
return JSON.parse(content);
|
|
2893
|
+
} catch (error) {
|
|
2894
|
+
if (error.code === "ENOENT") {
|
|
2895
|
+
return null;
|
|
2896
|
+
}
|
|
2897
|
+
throw error;
|
|
2898
|
+
}
|
|
2899
|
+
}
|
|
2900
|
+
async save(status) {
|
|
2901
|
+
await mkdir(dirname(this.path), { recursive: true });
|
|
2902
|
+
await writeFile(this.path, JSON.stringify(status, null, 2));
|
|
2903
|
+
}
|
|
2904
|
+
async clear() {
|
|
2905
|
+
await rm(this.path, { force: true });
|
|
2906
|
+
}
|
|
2907
|
+
}
|
|
2908
|
+
|
|
2909
|
+
// src/daemon/daemon-controller.ts
|
|
2910
|
+
class DaemonController {
|
|
2911
|
+
paths;
|
|
2912
|
+
deps;
|
|
2913
|
+
statusStore;
|
|
2914
|
+
constructor(paths, deps) {
|
|
2915
|
+
this.paths = paths;
|
|
2916
|
+
this.deps = deps;
|
|
2917
|
+
this.statusStore = new DaemonStatusStore(paths.statusFile);
|
|
2918
|
+
}
|
|
2919
|
+
async getStatus() {
|
|
2920
|
+
const pid = await this.loadPid();
|
|
2921
|
+
const status = await this.statusStore.load();
|
|
2922
|
+
if (!pid) {
|
|
2923
|
+
return { state: "stopped" };
|
|
2924
|
+
}
|
|
2925
|
+
if (!this.deps.isProcessRunning(pid)) {
|
|
2926
|
+
await this.clearRuntimeFiles();
|
|
2927
|
+
return { state: "stopped", stale: true };
|
|
2928
|
+
}
|
|
2929
|
+
if (!status) {
|
|
2930
|
+
return { state: "stopped" };
|
|
2931
|
+
}
|
|
2932
|
+
return {
|
|
2933
|
+
state: "running",
|
|
2934
|
+
pid,
|
|
2935
|
+
status
|
|
2936
|
+
};
|
|
2937
|
+
}
|
|
2938
|
+
async start() {
|
|
2939
|
+
const current = await this.getStatus();
|
|
2940
|
+
if (current.state === "running") {
|
|
2941
|
+
return { state: "already-running", pid: current.pid };
|
|
2942
|
+
}
|
|
2943
|
+
const pid = await this.deps.spawnDetached();
|
|
2944
|
+
await this.writePid(pid);
|
|
2945
|
+
return { state: "started", pid };
|
|
2946
|
+
}
|
|
2947
|
+
async stop() {
|
|
2948
|
+
const pid = await this.loadPid();
|
|
2949
|
+
if (!pid) {
|
|
2950
|
+
return { state: "stopped", detail: "not-running" };
|
|
2951
|
+
}
|
|
2952
|
+
if (this.deps.isProcessRunning(pid)) {
|
|
2953
|
+
await this.deps.terminateProcess(pid);
|
|
2954
|
+
}
|
|
2955
|
+
await this.clearRuntimeFiles();
|
|
2956
|
+
return { state: "stopped", detail: "stopped" };
|
|
2957
|
+
}
|
|
2958
|
+
async loadPid() {
|
|
2959
|
+
try {
|
|
2960
|
+
const content = await readFile2(this.paths.pidFile, "utf8");
|
|
2961
|
+
const pid = Number(content.trim());
|
|
2962
|
+
return Number.isFinite(pid) && pid > 0 ? pid : null;
|
|
2963
|
+
} catch (error) {
|
|
2964
|
+
if (error.code === "ENOENT") {
|
|
2965
|
+
return null;
|
|
2966
|
+
}
|
|
2967
|
+
throw error;
|
|
2968
|
+
}
|
|
2969
|
+
}
|
|
2970
|
+
async writePid(pid) {
|
|
2971
|
+
await mkdir2(dirname2(this.paths.pidFile), { recursive: true });
|
|
2972
|
+
await writeFile2(this.paths.pidFile, `${pid}
|
|
2973
|
+
`);
|
|
2974
|
+
}
|
|
2975
|
+
async clearRuntimeFiles() {
|
|
2976
|
+
await rm2(this.paths.pidFile, { force: true });
|
|
2977
|
+
await this.statusStore.clear();
|
|
2978
|
+
}
|
|
2979
|
+
}
|
|
2980
|
+
|
|
2981
|
+
// src/daemon/create-daemon-controller.ts
|
|
2982
|
+
function createDaemonController(paths, options) {
|
|
2983
|
+
return new DaemonController(paths, {
|
|
2984
|
+
isProcessRunning: options.isProcessRunning ?? defaultIsProcessRunning,
|
|
2985
|
+
spawnDetached: async () => {
|
|
2986
|
+
await mkdir3(paths.runtimeDir, { recursive: true });
|
|
2987
|
+
const stdoutHandle = await open(paths.stdoutLog, "a");
|
|
2988
|
+
const stderrHandle = await open(paths.stderrLog, "a");
|
|
2989
|
+
try {
|
|
2990
|
+
return await (options.spawnProcess ?? defaultSpawnProcess)({
|
|
2991
|
+
command: options.processExecPath,
|
|
2992
|
+
args: [options.cliEntryPath, "run"],
|
|
2993
|
+
options: {
|
|
2994
|
+
cwd: options.cwd,
|
|
2995
|
+
detached: true,
|
|
2996
|
+
env: options.env,
|
|
2997
|
+
stdio: ["ignore", stdoutHandle.fd, stderrHandle.fd],
|
|
2998
|
+
...(options.platform ?? process.platform) === "win32" ? { windowsHide: true } : {}
|
|
2999
|
+
}
|
|
3000
|
+
});
|
|
3001
|
+
} finally {
|
|
3002
|
+
await stdoutHandle.close();
|
|
3003
|
+
await stderrHandle.close();
|
|
3004
|
+
}
|
|
3005
|
+
},
|
|
3006
|
+
terminateProcess: options.terminateProcess ?? defaultTerminateProcess
|
|
3007
|
+
});
|
|
3008
|
+
}
|
|
3009
|
+
function defaultIsProcessRunning(pid) {
|
|
3010
|
+
try {
|
|
3011
|
+
process.kill(pid, 0);
|
|
3012
|
+
return true;
|
|
3013
|
+
} catch {
|
|
3014
|
+
return false;
|
|
3015
|
+
}
|
|
3016
|
+
}
|
|
3017
|
+
async function defaultSpawnProcess(request) {
|
|
3018
|
+
const child = spawn(request.command, request.args, request.options);
|
|
3019
|
+
child.unref();
|
|
3020
|
+
return child.pid ?? 0;
|
|
3021
|
+
}
|
|
3022
|
+
async function defaultTerminateProcess(pid) {
|
|
3023
|
+
await terminateProcessTree(pid);
|
|
3024
|
+
}
|
|
3025
|
+
async function terminateProcessTree(pid, platform = process.platform, runCommand = defaultRunProcessCommand) {
|
|
3026
|
+
if (platform === "win32") {
|
|
3027
|
+
try {
|
|
3028
|
+
await runCommand("taskkill", ["/PID", String(pid), "/T", "/F"]);
|
|
3029
|
+
} catch {}
|
|
3030
|
+
return;
|
|
3031
|
+
}
|
|
3032
|
+
try {
|
|
3033
|
+
process.kill(pid, "SIGTERM");
|
|
3034
|
+
} catch {
|
|
3035
|
+
return;
|
|
3036
|
+
}
|
|
3037
|
+
const deadline = Date.now() + 5000;
|
|
3038
|
+
while (Date.now() < deadline) {
|
|
3039
|
+
if (!defaultIsProcessRunning(pid)) {
|
|
3040
|
+
return;
|
|
3041
|
+
}
|
|
3042
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
3043
|
+
}
|
|
3044
|
+
try {
|
|
3045
|
+
process.kill(pid, "SIGKILL");
|
|
3046
|
+
} catch {}
|
|
3047
|
+
}
|
|
3048
|
+
async function defaultRunProcessCommand(command, args) {
|
|
3049
|
+
return await new Promise((resolve, reject) => {
|
|
3050
|
+
const child = spawn(command, args, { stdio: "ignore" });
|
|
3051
|
+
child.on("error", reject);
|
|
3052
|
+
child.on("close", (code) => resolve(code ?? 1));
|
|
3053
|
+
});
|
|
3054
|
+
}
|
|
3055
|
+
|
|
3056
|
+
// src/daemon/daemon-files.ts
|
|
3057
|
+
import { join } from "node:path";
|
|
3058
|
+
function resolveDaemonPaths(options) {
|
|
3059
|
+
const runtimeDir = options.runtimeDir ?? join(options.home, ".weacpx", "runtime");
|
|
3060
|
+
return {
|
|
3061
|
+
runtimeDir,
|
|
3062
|
+
pidFile: join(runtimeDir, "daemon.pid"),
|
|
3063
|
+
statusFile: join(runtimeDir, "status.json"),
|
|
3064
|
+
stdoutLog: join(runtimeDir, "stdout.log"),
|
|
3065
|
+
stderrLog: join(runtimeDir, "stderr.log"),
|
|
3066
|
+
appLog: join(runtimeDir, "app.log")
|
|
3067
|
+
};
|
|
3068
|
+
}
|
|
3069
|
+
|
|
3070
|
+
// src/daemon/daemon-runtime.ts
|
|
3071
|
+
import { mkdir as mkdir4, rm as rm3, writeFile as writeFile3 } from "node:fs/promises";
|
|
3072
|
+
import { dirname as dirname3 } from "node:path";
|
|
3073
|
+
class DaemonRuntime {
|
|
3074
|
+
paths;
|
|
3075
|
+
options;
|
|
3076
|
+
statusStore;
|
|
3077
|
+
now;
|
|
3078
|
+
currentStatus = null;
|
|
3079
|
+
constructor(paths, options) {
|
|
3080
|
+
this.paths = paths;
|
|
3081
|
+
this.options = options;
|
|
3082
|
+
this.statusStore = new DaemonStatusStore(paths.statusFile);
|
|
3083
|
+
this.now = options.now ?? (() => new Date().toISOString());
|
|
3084
|
+
}
|
|
3085
|
+
async start(input) {
|
|
3086
|
+
const timestamp = this.now();
|
|
3087
|
+
this.currentStatus = {
|
|
3088
|
+
pid: this.options.pid,
|
|
3089
|
+
started_at: timestamp,
|
|
3090
|
+
heartbeat_at: timestamp,
|
|
3091
|
+
config_path: input.configPath,
|
|
3092
|
+
state_path: input.statePath,
|
|
3093
|
+
app_log: this.paths.appLog,
|
|
3094
|
+
stdout_log: this.paths.stdoutLog,
|
|
3095
|
+
stderr_log: this.paths.stderrLog
|
|
3096
|
+
};
|
|
3097
|
+
await mkdir4(dirname3(this.paths.pidFile), { recursive: true });
|
|
3098
|
+
await writeFile3(this.paths.pidFile, `${this.options.pid}
|
|
3099
|
+
`);
|
|
3100
|
+
await this.statusStore.save(this.currentStatus);
|
|
3101
|
+
}
|
|
3102
|
+
async heartbeat() {
|
|
3103
|
+
if (!this.currentStatus) {
|
|
3104
|
+
return;
|
|
3105
|
+
}
|
|
3106
|
+
this.currentStatus = {
|
|
3107
|
+
...this.currentStatus,
|
|
3108
|
+
heartbeat_at: this.now()
|
|
3109
|
+
};
|
|
3110
|
+
await this.statusStore.save(this.currentStatus);
|
|
3111
|
+
}
|
|
3112
|
+
async stop() {
|
|
3113
|
+
await rm3(this.paths.pidFile, { force: true });
|
|
3114
|
+
await this.statusStore.clear();
|
|
3115
|
+
this.currentStatus = null;
|
|
3116
|
+
}
|
|
3117
|
+
}
|
|
3118
|
+
|
|
3119
|
+
// src/cli.ts
|
|
3120
|
+
var HELP_LINES = ["用法:", "weacpx login", "weacpx run", "weacpx start", "weacpx status", "weacpx stop"];
|
|
3121
|
+
async function runCli(args, deps = {}) {
|
|
3122
|
+
const command = args[0];
|
|
3123
|
+
const print = deps.print ?? ((line) => console.log(line));
|
|
3124
|
+
const controller = deps.controller ?? createDefaultController();
|
|
3125
|
+
switch (command) {
|
|
3126
|
+
case "login":
|
|
3127
|
+
await (deps.login ?? defaultLogin)();
|
|
3128
|
+
return 0;
|
|
3129
|
+
case "run":
|
|
3130
|
+
await (deps.run ?? defaultRun)();
|
|
3131
|
+
return 0;
|
|
3132
|
+
case "start": {
|
|
3133
|
+
const result = await controller.start();
|
|
3134
|
+
if (result.state === "already-running") {
|
|
3135
|
+
print("weacpx 已在后台运行");
|
|
3136
|
+
print(`PID: ${result.pid}`);
|
|
3137
|
+
return 0;
|
|
3138
|
+
}
|
|
3139
|
+
print("weacpx 已在后台启动");
|
|
3140
|
+
print(`PID: ${result.pid}`);
|
|
3141
|
+
return 0;
|
|
3142
|
+
}
|
|
3143
|
+
case "status": {
|
|
3144
|
+
const status = await controller.getStatus();
|
|
3145
|
+
if (status.state !== "running") {
|
|
3146
|
+
print("weacpx 未运行");
|
|
3147
|
+
return 0;
|
|
3148
|
+
}
|
|
3149
|
+
print("weacpx 正在运行");
|
|
3150
|
+
print(`PID: ${status.pid}`);
|
|
3151
|
+
print(`Started: ${status.status.started_at}`);
|
|
3152
|
+
print(`Heartbeat: ${status.status.heartbeat_at}`);
|
|
3153
|
+
print(`Config: ${status.status.config_path}`);
|
|
3154
|
+
print(`State: ${status.status.state_path}`);
|
|
3155
|
+
print(`App Log: ${status.status.app_log}`);
|
|
3156
|
+
print(`Stdout: ${status.status.stdout_log}`);
|
|
3157
|
+
print(`Stderr: ${status.status.stderr_log}`);
|
|
3158
|
+
return 0;
|
|
3159
|
+
}
|
|
3160
|
+
case "stop": {
|
|
3161
|
+
const result = await controller.stop();
|
|
3162
|
+
if (result.detail === "not-running") {
|
|
3163
|
+
print("weacpx 未运行");
|
|
3164
|
+
return 0;
|
|
3165
|
+
}
|
|
3166
|
+
print("weacpx 已停止");
|
|
3167
|
+
return 0;
|
|
3168
|
+
}
|
|
3169
|
+
default:
|
|
3170
|
+
for (const line of HELP_LINES) {
|
|
3171
|
+
print(line);
|
|
3172
|
+
}
|
|
3173
|
+
return 1;
|
|
3174
|
+
}
|
|
3175
|
+
}
|
|
3176
|
+
async function defaultLogin() {
|
|
3177
|
+
const { main: main3 } = await init_login().then(() => exports_login);
|
|
3178
|
+
await main3();
|
|
3179
|
+
}
|
|
3180
|
+
async function defaultRun() {
|
|
3181
|
+
const [{ buildApp: buildApp2, resolveRuntimePaths: resolveRuntimePaths2 }, { loadWeixinSdk: loadWeixinSdk2 }, { runConsole: runConsole2 }] = await Promise.all([
|
|
3182
|
+
init_main().then(() => exports_main),
|
|
3183
|
+
Promise.resolve().then(() => exports_weixin_sdk),
|
|
3184
|
+
Promise.resolve().then(() => exports_run_console)
|
|
3185
|
+
]);
|
|
3186
|
+
const runtimePaths = resolveRuntimePaths2();
|
|
3187
|
+
const daemonPaths = resolveDaemonPaths({ home: requireHome() });
|
|
3188
|
+
const daemonRuntime = new DaemonRuntime(daemonPaths, { pid: process.pid });
|
|
3189
|
+
await runConsole2(runtimePaths, {
|
|
3190
|
+
buildApp: (paths) => buildApp2(paths, {
|
|
3191
|
+
defaultLoggingLevel: resolveCliEntryPath().includes(`${sep}src${sep}`) ? "debug" : "info"
|
|
3192
|
+
}),
|
|
3193
|
+
loadWeixinSdk: loadWeixinSdk2,
|
|
3194
|
+
daemonRuntime
|
|
3195
|
+
});
|
|
3196
|
+
}
|
|
3197
|
+
function createDefaultController() {
|
|
3198
|
+
const daemonPaths = resolveDaemonPaths({ home: requireHome() });
|
|
3199
|
+
return createDaemonController(daemonPaths, {
|
|
3200
|
+
processExecPath: process.execPath,
|
|
3201
|
+
cliEntryPath: resolveCliEntryPath(),
|
|
3202
|
+
cwd: process.cwd(),
|
|
3203
|
+
env: process.env
|
|
3204
|
+
});
|
|
3205
|
+
}
|
|
3206
|
+
function requireHome() {
|
|
3207
|
+
const home = process.env.HOME ?? homedir2();
|
|
3208
|
+
if (!home) {
|
|
3209
|
+
throw new Error("Unable to resolve the current user home directory");
|
|
3210
|
+
}
|
|
3211
|
+
return home;
|
|
3212
|
+
}
|
|
3213
|
+
function resolveCliEntryPath() {
|
|
3214
|
+
if (process.argv[1]) {
|
|
3215
|
+
return process.argv[1];
|
|
3216
|
+
}
|
|
3217
|
+
return fileURLToPath3(import.meta.url);
|
|
3218
|
+
}
|
|
3219
|
+
if (__require.main == __require.module) {
|
|
3220
|
+
process.exitCode = await runCli(process.argv.slice(2));
|
|
3221
|
+
}
|
|
3222
|
+
export {
|
|
3223
|
+
runCli
|
|
3224
|
+
};
|