@coze-arch/cli 0.0.1-alpha.dcc485 → 0.0.1-alpha.de4758
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/lib/cli.js +3218 -737
- package/package.json +4 -1
package/lib/cli.js
CHANGED
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
var commander = require('commander');
|
|
5
5
|
var path = require('path');
|
|
6
6
|
var fs = require('fs');
|
|
7
|
+
var debug = require('debug');
|
|
8
|
+
var node_http = require('node:http');
|
|
9
|
+
var node_https = require('node:https');
|
|
7
10
|
var node_path = require('node:path');
|
|
8
11
|
var node_fs = require('node:fs');
|
|
9
12
|
var shelljs = require('shelljs');
|
|
@@ -129,6 +132,2161 @@ const generateTemplatesHelpText = () => {
|
|
|
129
132
|
return lines.join('\n');
|
|
130
133
|
};
|
|
131
134
|
|
|
135
|
+
var DEFAULT_SIZE = 10;
|
|
136
|
+
var DEFAULT_WAIT = 1000;
|
|
137
|
+
var stringifyBatch = function (list) {
|
|
138
|
+
return JSON.stringify({
|
|
139
|
+
ev_type: 'batch',
|
|
140
|
+
list: list,
|
|
141
|
+
});
|
|
142
|
+
};
|
|
143
|
+
function createBatchSender(config) {
|
|
144
|
+
var transport = config.transport;
|
|
145
|
+
var endpoint = config.endpoint, _a = config.size, size = _a === void 0 ? DEFAULT_SIZE : _a, _b = config.wait, wait = _b === void 0 ? DEFAULT_WAIT : _b;
|
|
146
|
+
var batch = [];
|
|
147
|
+
var tid = 0;
|
|
148
|
+
var fail;
|
|
149
|
+
var success;
|
|
150
|
+
var sender = {
|
|
151
|
+
getSize: function () {
|
|
152
|
+
return size;
|
|
153
|
+
},
|
|
154
|
+
getWait: function () {
|
|
155
|
+
return wait;
|
|
156
|
+
},
|
|
157
|
+
setSize: function (v) {
|
|
158
|
+
size = v;
|
|
159
|
+
},
|
|
160
|
+
setWait: function (v) {
|
|
161
|
+
wait = v;
|
|
162
|
+
},
|
|
163
|
+
getEndpoint: function () {
|
|
164
|
+
return endpoint;
|
|
165
|
+
},
|
|
166
|
+
setEndpoint: function (v) {
|
|
167
|
+
endpoint = v;
|
|
168
|
+
},
|
|
169
|
+
send: function (e) {
|
|
170
|
+
batch.push(e);
|
|
171
|
+
if (batch.length >= size) {
|
|
172
|
+
sendBatch.call(this);
|
|
173
|
+
}
|
|
174
|
+
clearTimeout(tid);
|
|
175
|
+
tid = setTimeout(sendBatch.bind(this), wait);
|
|
176
|
+
},
|
|
177
|
+
flush: function () {
|
|
178
|
+
clearTimeout(tid);
|
|
179
|
+
sendBatch.call(this);
|
|
180
|
+
},
|
|
181
|
+
getBatchData: function () {
|
|
182
|
+
return batch.length ? stringifyBatch(batch) : '';
|
|
183
|
+
},
|
|
184
|
+
clear: function () {
|
|
185
|
+
clearTimeout(tid);
|
|
186
|
+
batch = [];
|
|
187
|
+
},
|
|
188
|
+
fail: function (cb) {
|
|
189
|
+
fail = cb;
|
|
190
|
+
},
|
|
191
|
+
success: function (cb) {
|
|
192
|
+
success = cb;
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
function sendBatch() {
|
|
196
|
+
if (!batch.length) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
var data = this.getBatchData();
|
|
200
|
+
transport.post({
|
|
201
|
+
url: endpoint,
|
|
202
|
+
data: data,
|
|
203
|
+
fail: function (err) {
|
|
204
|
+
fail && fail(err, data);
|
|
205
|
+
},
|
|
206
|
+
success: function () {
|
|
207
|
+
success && success(data);
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
batch = [];
|
|
211
|
+
}
|
|
212
|
+
return sender;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/*! *****************************************************************************
|
|
216
|
+
Copyright (c) Microsoft Corporation.
|
|
217
|
+
|
|
218
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
219
|
+
purpose with or without fee is hereby granted.
|
|
220
|
+
|
|
221
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
222
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
223
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
224
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
225
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
226
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
227
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
228
|
+
***************************************************************************** */
|
|
229
|
+
|
|
230
|
+
var __assign = function() {
|
|
231
|
+
__assign = Object.assign || function __assign(t) {
|
|
232
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
233
|
+
s = arguments[i];
|
|
234
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
|
|
235
|
+
}
|
|
236
|
+
return t;
|
|
237
|
+
};
|
|
238
|
+
return __assign.apply(this, arguments);
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
function __values(o) {
|
|
242
|
+
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
243
|
+
if (m) return m.call(o);
|
|
244
|
+
if (o && typeof o.length === "number") return {
|
|
245
|
+
next: function () {
|
|
246
|
+
if (o && i >= o.length) o = void 0;
|
|
247
|
+
return { value: o && o[i++], done: !o };
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function __read(o, n) {
|
|
254
|
+
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
255
|
+
if (!m) return o;
|
|
256
|
+
var i = m.call(o), r, ar = [], e;
|
|
257
|
+
try {
|
|
258
|
+
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
259
|
+
}
|
|
260
|
+
catch (error) { e = { error: error }; }
|
|
261
|
+
finally {
|
|
262
|
+
try {
|
|
263
|
+
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
264
|
+
}
|
|
265
|
+
finally { if (e) throw e.error; }
|
|
266
|
+
}
|
|
267
|
+
return ar;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function __spreadArray(to, from, pack) {
|
|
271
|
+
if (arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
272
|
+
if (ar || !(i in from)) {
|
|
273
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
274
|
+
ar[i] = from[i];
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
var noop$1 = function () { return ({}); };
|
|
281
|
+
function id(v) {
|
|
282
|
+
return v;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
286
|
+
function isObject(o) {
|
|
287
|
+
return typeof o === 'object' && o !== null;
|
|
288
|
+
}
|
|
289
|
+
var objProto = Object.prototype;
|
|
290
|
+
function isArray(o) {
|
|
291
|
+
return objProto.toString.call(o) === '[object Array]';
|
|
292
|
+
}
|
|
293
|
+
function isBoolean(o) {
|
|
294
|
+
return typeof o === 'boolean';
|
|
295
|
+
}
|
|
296
|
+
function isNumber(o) {
|
|
297
|
+
return typeof o === 'number';
|
|
298
|
+
}
|
|
299
|
+
function isString(o) {
|
|
300
|
+
return typeof o === 'string';
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// 检查数组中是否有元素
|
|
304
|
+
function arrayIncludes(array, value) {
|
|
305
|
+
if (!isArray(array)) {
|
|
306
|
+
return false;
|
|
307
|
+
}
|
|
308
|
+
if (array.length === 0) {
|
|
309
|
+
return false;
|
|
310
|
+
}
|
|
311
|
+
var k = 0;
|
|
312
|
+
while (k < array.length) {
|
|
313
|
+
if (array[k] === value) {
|
|
314
|
+
return true;
|
|
315
|
+
}
|
|
316
|
+
k++;
|
|
317
|
+
}
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
var arrayRemove = function (arr, e) {
|
|
321
|
+
if (!isArray(arr)) {
|
|
322
|
+
return arr;
|
|
323
|
+
}
|
|
324
|
+
var i = arr.indexOf(e);
|
|
325
|
+
if (i >= 0) {
|
|
326
|
+
var arr_ = arr.slice();
|
|
327
|
+
arr_.splice(i, 1);
|
|
328
|
+
return arr_;
|
|
329
|
+
}
|
|
330
|
+
return arr;
|
|
331
|
+
};
|
|
332
|
+
/**
|
|
333
|
+
* 按路径访问对象属性
|
|
334
|
+
* @param target 待访问对象
|
|
335
|
+
* @param property 访问属性路径
|
|
336
|
+
* @param { (target: any, property: string): any } visitor 访问器
|
|
337
|
+
*/
|
|
338
|
+
var safeVisit = function (target, path, visitor) {
|
|
339
|
+
var _a, _b;
|
|
340
|
+
var paths = path.split('.');
|
|
341
|
+
var _c = __read(paths), method = _c[0], rest = _c.slice(1);
|
|
342
|
+
while (target && rest.length > 0) {
|
|
343
|
+
target = target[method];
|
|
344
|
+
_a = rest, _b = __read(_a), method = _b[0], rest = _b.slice(1);
|
|
345
|
+
}
|
|
346
|
+
if (!target) {
|
|
347
|
+
return undefined;
|
|
348
|
+
}
|
|
349
|
+
return visitor(target, method);
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
function safeStringify(a) {
|
|
353
|
+
try {
|
|
354
|
+
return isString(a) ? a : JSON.stringify(a);
|
|
355
|
+
}
|
|
356
|
+
catch (err) {
|
|
357
|
+
return '[FAILED_TO_STRINGIFY]:' + String(err);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function createContextAgent() {
|
|
362
|
+
var context = {};
|
|
363
|
+
var stringified = {};
|
|
364
|
+
var contextAgent = {
|
|
365
|
+
set: function (k, v) {
|
|
366
|
+
context[k] = v;
|
|
367
|
+
stringified[k] = safeStringify(v);
|
|
368
|
+
return contextAgent;
|
|
369
|
+
},
|
|
370
|
+
merge: function (ctx) {
|
|
371
|
+
context = __assign(__assign({}, context), ctx);
|
|
372
|
+
Object.keys(ctx).forEach(function (key) {
|
|
373
|
+
stringified[key] = safeStringify(ctx[key]);
|
|
374
|
+
});
|
|
375
|
+
return contextAgent;
|
|
376
|
+
},
|
|
377
|
+
delete: function (k) {
|
|
378
|
+
delete context[k];
|
|
379
|
+
delete stringified[k];
|
|
380
|
+
return contextAgent;
|
|
381
|
+
},
|
|
382
|
+
clear: function () {
|
|
383
|
+
context = {};
|
|
384
|
+
stringified = {};
|
|
385
|
+
return contextAgent;
|
|
386
|
+
},
|
|
387
|
+
get: function (k) {
|
|
388
|
+
return stringified[k];
|
|
389
|
+
},
|
|
390
|
+
toString: function () {
|
|
391
|
+
return __assign({}, stringified);
|
|
392
|
+
},
|
|
393
|
+
};
|
|
394
|
+
return contextAgent;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
var getPrintString = function () {
|
|
398
|
+
// @ts-expect-error
|
|
399
|
+
if (''.padStart) {
|
|
400
|
+
return function (str, prefixLength) {
|
|
401
|
+
if (prefixLength === void 0) { prefixLength = 8; }
|
|
402
|
+
return str.padStart(prefixLength, ' ');
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
return function (str) { return str; };
|
|
406
|
+
};
|
|
407
|
+
var printString = getPrintString();
|
|
408
|
+
var errCount = 0;
|
|
409
|
+
var error = function () {
|
|
410
|
+
var args = [];
|
|
411
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
412
|
+
args[_i] = arguments[_i];
|
|
413
|
+
}
|
|
414
|
+
// eslint-disable-next-line no-console
|
|
415
|
+
console.error.apply(console, __spreadArray(['[SDK]', Date.now(), printString("" + errCount++)], __read(args), false));
|
|
416
|
+
};
|
|
417
|
+
var warnCount = 0;
|
|
418
|
+
var warn = function () {
|
|
419
|
+
var args = [];
|
|
420
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
421
|
+
args[_i] = arguments[_i];
|
|
422
|
+
}
|
|
423
|
+
// eslint-disable-next-line no-console
|
|
424
|
+
console.warn.apply(console, __spreadArray(['[SDK]', Date.now(), printString("" + warnCount++)], __read(args), false));
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
var isHitBySampleRate = function (sampleRate) {
|
|
428
|
+
if (Math.random() < Number(sampleRate)) {
|
|
429
|
+
return true;
|
|
430
|
+
}
|
|
431
|
+
return false;
|
|
432
|
+
};
|
|
433
|
+
var isHitByRandom = function (random, sampleRate) {
|
|
434
|
+
if (random < Number(sampleRate)) {
|
|
435
|
+
return true;
|
|
436
|
+
}
|
|
437
|
+
return false;
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
var runProcessors = function (fns) {
|
|
441
|
+
return function (e) {
|
|
442
|
+
var r = e;
|
|
443
|
+
for (var i = 0; i < fns.length; i++) {
|
|
444
|
+
if (r) {
|
|
445
|
+
try {
|
|
446
|
+
r = fns[i](r);
|
|
447
|
+
}
|
|
448
|
+
catch (err) {
|
|
449
|
+
error(err);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
453
|
+
break;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
return r;
|
|
457
|
+
};
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* 生成uuid
|
|
462
|
+
* stolen from https://github.com/kelektiv/node-uuid#readme uuid/v4
|
|
463
|
+
*
|
|
464
|
+
* @returns
|
|
465
|
+
*/
|
|
466
|
+
function mathRNG() {
|
|
467
|
+
var rnds = new Array(16);
|
|
468
|
+
var r = 0;
|
|
469
|
+
for (var i = 0; i < 16; i++) {
|
|
470
|
+
if ((i & 0x03) === 0) {
|
|
471
|
+
r = Math.random() * 0x100000000;
|
|
472
|
+
}
|
|
473
|
+
rnds[i] = (r >>> ((i & 0x03) << 3)) & 0xff;
|
|
474
|
+
}
|
|
475
|
+
return rnds;
|
|
476
|
+
}
|
|
477
|
+
function bytesToUuid(buf) {
|
|
478
|
+
var byteToHex = [];
|
|
479
|
+
for (var index = 0; index < 256; ++index) {
|
|
480
|
+
byteToHex[index] = (index + 0x100).toString(16).substr(1);
|
|
481
|
+
}
|
|
482
|
+
var i = 0;
|
|
483
|
+
var bth = byteToHex;
|
|
484
|
+
// join used to fix memory issue caused by concatenation: https://bugs.chromium.org/p/v8/issues/detail?id=3175#c4
|
|
485
|
+
return [
|
|
486
|
+
bth[buf[i++]],
|
|
487
|
+
bth[buf[i++]],
|
|
488
|
+
bth[buf[i++]],
|
|
489
|
+
bth[buf[i++]],
|
|
490
|
+
'-',
|
|
491
|
+
bth[buf[i++]],
|
|
492
|
+
bth[buf[i++]],
|
|
493
|
+
'-',
|
|
494
|
+
bth[buf[i++]],
|
|
495
|
+
bth[buf[i++]],
|
|
496
|
+
'-',
|
|
497
|
+
bth[buf[i++]],
|
|
498
|
+
bth[buf[i++]],
|
|
499
|
+
'-',
|
|
500
|
+
bth[buf[i++]],
|
|
501
|
+
bth[buf[i++]],
|
|
502
|
+
bth[buf[i++]],
|
|
503
|
+
bth[buf[i++]],
|
|
504
|
+
bth[buf[i++]],
|
|
505
|
+
bth[buf[i++]],
|
|
506
|
+
].join('');
|
|
507
|
+
}
|
|
508
|
+
function uuid() {
|
|
509
|
+
var rnds = mathRNG();
|
|
510
|
+
// Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
|
|
511
|
+
rnds[6] = (rnds[6] & 0x0f) | 0x40;
|
|
512
|
+
rnds[8] = (rnds[8] & 0x3f) | 0x80;
|
|
513
|
+
return bytesToUuid(rnds);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
function createDestroyAgent() {
|
|
517
|
+
var destroyed = false;
|
|
518
|
+
var data = {};
|
|
519
|
+
var removeTearDownGroup = function (tearDownGroup) {
|
|
520
|
+
tearDownGroup.length &&
|
|
521
|
+
tearDownGroup.forEach(function (v) {
|
|
522
|
+
try {
|
|
523
|
+
v();
|
|
524
|
+
// eslint-disable-next-line no-empty
|
|
525
|
+
}
|
|
526
|
+
catch (_a) { }
|
|
527
|
+
});
|
|
528
|
+
tearDownGroup.length = 0;
|
|
529
|
+
};
|
|
530
|
+
var removeByPluginName = function (pluginName) {
|
|
531
|
+
data[pluginName] &&
|
|
532
|
+
data[pluginName].forEach(function (e) {
|
|
533
|
+
removeTearDownGroup(e[1]);
|
|
534
|
+
});
|
|
535
|
+
data[pluginName] = undefined;
|
|
536
|
+
};
|
|
537
|
+
var removeByEvType = function (evType) {
|
|
538
|
+
Object.keys(data).forEach(function (k) {
|
|
539
|
+
data[k] &&
|
|
540
|
+
data[k].forEach(function (e) {
|
|
541
|
+
if (e[0] === evType) {
|
|
542
|
+
removeTearDownGroup(e[1]);
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
});
|
|
546
|
+
};
|
|
547
|
+
return {
|
|
548
|
+
/**
|
|
549
|
+
* register tearDownGroup for a single plugin.
|
|
550
|
+
*/
|
|
551
|
+
set: function (pluginName, evType, tearDownGroup) {
|
|
552
|
+
if (data[pluginName])
|
|
553
|
+
data[pluginName].push([evType, tearDownGroup]);
|
|
554
|
+
else
|
|
555
|
+
data[pluginName] = [[evType, tearDownGroup]];
|
|
556
|
+
// auto remove tearDownGroup if destroyed
|
|
557
|
+
destroyed && removeTearDownGroup(tearDownGroup);
|
|
558
|
+
},
|
|
559
|
+
has: function (pluginName) {
|
|
560
|
+
return !!data[pluginName];
|
|
561
|
+
},
|
|
562
|
+
/**
|
|
563
|
+
* remove tearDownGroup for a single plugin.
|
|
564
|
+
*/
|
|
565
|
+
remove: removeByPluginName,
|
|
566
|
+
/**
|
|
567
|
+
* remove tearDownGroup by event type
|
|
568
|
+
*/
|
|
569
|
+
removeByEvType: removeByEvType,
|
|
570
|
+
/**
|
|
571
|
+
* clear all tearDownGroup
|
|
572
|
+
*/
|
|
573
|
+
clear: function () {
|
|
574
|
+
destroyed = true;
|
|
575
|
+
Object.keys(data).forEach(function (k) {
|
|
576
|
+
removeByPluginName(k);
|
|
577
|
+
});
|
|
578
|
+
},
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// max size of preStartQueue
|
|
583
|
+
var PRESTART_QUEUE_MAX_SIZE = 500;
|
|
584
|
+
// slice size to operate preStartQueue
|
|
585
|
+
var PRESTART_QUEUE_OP_SLICE_SIZE = 50;
|
|
586
|
+
function cachePreStartData(client, preStartQueue, processed) {
|
|
587
|
+
preStartQueue.push(processed);
|
|
588
|
+
if (preStartQueue.length < PRESTART_QUEUE_MAX_SIZE) {
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
// delete some data to prevent OOM
|
|
592
|
+
var deleteData = preStartQueue.splice(0, PRESTART_QUEUE_OP_SLICE_SIZE);
|
|
593
|
+
if (client.savePreStartDataToDb) {
|
|
594
|
+
void client.savePreStartDataToDb(deleteData);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
function consumePreStartData(client) {
|
|
598
|
+
var preStartQueue = client.getPreStartQueue();
|
|
599
|
+
preStartQueue.forEach(function (e) { return client.build(e); });
|
|
600
|
+
preStartQueue.length = 0;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
var EVENTS = [
|
|
604
|
+
'init',
|
|
605
|
+
'start',
|
|
606
|
+
'config',
|
|
607
|
+
'beforeDestroy',
|
|
608
|
+
'provide',
|
|
609
|
+
'beforeReport',
|
|
610
|
+
'report',
|
|
611
|
+
'beforeBuild',
|
|
612
|
+
'build',
|
|
613
|
+
'beforeSend',
|
|
614
|
+
'send',
|
|
615
|
+
'beforeConfig',
|
|
616
|
+
];
|
|
617
|
+
|
|
618
|
+
function createClient(creationConfig) {
|
|
619
|
+
var builder = creationConfig.builder, createSender = creationConfig.createSender, createDefaultConfig = creationConfig.createDefaultConfig, createConfigManager = creationConfig.createConfigManager, userConfigNormalizer = creationConfig.userConfigNormalizer, initConfigNormalizer = creationConfig.initConfigNormalizer, validateInitConfig = creationConfig.validateInitConfig;
|
|
620
|
+
var sender;
|
|
621
|
+
var configManager;
|
|
622
|
+
var handlers = {};
|
|
623
|
+
EVENTS.forEach(function (e) { return (handlers[e] = []); });
|
|
624
|
+
var inited = false;
|
|
625
|
+
var started = false;
|
|
626
|
+
var destroyed = false;
|
|
627
|
+
// 缓存 start 之前 build 的事件
|
|
628
|
+
var preStartQueue = [];
|
|
629
|
+
// 禁止通过 provide 挂载的字段名
|
|
630
|
+
var reservedNames = [];
|
|
631
|
+
var destroyAgent = createDestroyAgent();
|
|
632
|
+
var client = {
|
|
633
|
+
getBuilder: function () { return builder; },
|
|
634
|
+
getSender: function () { return sender; },
|
|
635
|
+
getPreStartQueue: function () { return preStartQueue; },
|
|
636
|
+
init: function (c) {
|
|
637
|
+
if (inited) {
|
|
638
|
+
warn('already inited');
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
if (c && isObject(c) && validateInitConfig(c)) {
|
|
642
|
+
var defaultConfig = createDefaultConfig(c);
|
|
643
|
+
if (!defaultConfig) {
|
|
644
|
+
throw new Error('defaultConfig missing');
|
|
645
|
+
}
|
|
646
|
+
var initConfig = initConfigNormalizer(c);
|
|
647
|
+
configManager = createConfigManager(defaultConfig);
|
|
648
|
+
configManager.setConfig(initConfig);
|
|
649
|
+
configManager.onChange(function () {
|
|
650
|
+
handle('config');
|
|
651
|
+
});
|
|
652
|
+
sender = createSender(configManager.getConfig());
|
|
653
|
+
if (!sender) {
|
|
654
|
+
throw new Error('sender missing');
|
|
655
|
+
}
|
|
656
|
+
inited = true;
|
|
657
|
+
handle('init', true);
|
|
658
|
+
}
|
|
659
|
+
else {
|
|
660
|
+
throw new Error('invalid InitConfig, init failed');
|
|
661
|
+
}
|
|
662
|
+
},
|
|
663
|
+
set: function (c) {
|
|
664
|
+
if (!inited) {
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
if (c && isObject(c)) {
|
|
668
|
+
handle('beforeConfig', false, c);
|
|
669
|
+
configManager === null || configManager === void 0 ? void 0 : configManager.setConfig(c);
|
|
670
|
+
}
|
|
671
|
+
},
|
|
672
|
+
config: function (c) {
|
|
673
|
+
if (!inited) {
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
if (c && isObject(c)) {
|
|
677
|
+
handle('beforeConfig', false, c);
|
|
678
|
+
configManager === null || configManager === void 0 ? void 0 : configManager.setConfig(userConfigNormalizer(c));
|
|
679
|
+
}
|
|
680
|
+
return configManager === null || configManager === void 0 ? void 0 : configManager.getConfig();
|
|
681
|
+
},
|
|
682
|
+
provide: function (name, value) {
|
|
683
|
+
if (arrayIncludes(reservedNames, name)) {
|
|
684
|
+
warn("cannot provide " + name + ", reserved");
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
client[name] = value;
|
|
688
|
+
handle('provide', false, name);
|
|
689
|
+
},
|
|
690
|
+
start: function () {
|
|
691
|
+
if (!inited) {
|
|
692
|
+
return;
|
|
693
|
+
}
|
|
694
|
+
if (started) {
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
configManager === null || configManager === void 0 ? void 0 : configManager.onReady(function () {
|
|
698
|
+
started = true;
|
|
699
|
+
handle('start', true);
|
|
700
|
+
consumePreStartData(client);
|
|
701
|
+
});
|
|
702
|
+
},
|
|
703
|
+
report: function (data) {
|
|
704
|
+
if (!data) {
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
var preReport = runProcessors(handlers['beforeReport'])(data);
|
|
708
|
+
if (!preReport) {
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
var processed = runProcessors(handlers['report'])(preReport);
|
|
712
|
+
if (!processed) {
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
if (started) {
|
|
716
|
+
this.build(processed);
|
|
717
|
+
}
|
|
718
|
+
else {
|
|
719
|
+
cachePreStartData(client, preStartQueue, processed);
|
|
720
|
+
}
|
|
721
|
+
},
|
|
722
|
+
build: function (data) {
|
|
723
|
+
if (!started) {
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
var preBuild = runProcessors(handlers['beforeBuild'])(data);
|
|
727
|
+
if (!preBuild) {
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
var built = builder.build(preBuild);
|
|
731
|
+
if (!built) {
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
var processed = runProcessors(handlers['build'])(built);
|
|
735
|
+
if (!processed) {
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
this.send(processed);
|
|
739
|
+
},
|
|
740
|
+
send: function (data) {
|
|
741
|
+
if (!started) {
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
var processed = runProcessors(handlers['beforeSend'])(data);
|
|
745
|
+
if (processed) {
|
|
746
|
+
sender.send(processed);
|
|
747
|
+
handle('send', false, processed);
|
|
748
|
+
}
|
|
749
|
+
},
|
|
750
|
+
destroy: function () {
|
|
751
|
+
destroyAgent.clear();
|
|
752
|
+
destroyed = true;
|
|
753
|
+
preStartQueue.length = 0;
|
|
754
|
+
handle('beforeDestroy', true);
|
|
755
|
+
},
|
|
756
|
+
on: function (ev, handler) {
|
|
757
|
+
if ((ev === 'init' && inited) || (ev === 'start' && started) || (ev === 'beforeDestroy' && destroyed)) {
|
|
758
|
+
try {
|
|
759
|
+
;
|
|
760
|
+
handler();
|
|
761
|
+
}
|
|
762
|
+
catch (_err) {
|
|
763
|
+
// ignore
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
else if (handlers[ev]) {
|
|
767
|
+
handlers[ev].push(handler);
|
|
768
|
+
}
|
|
769
|
+
},
|
|
770
|
+
off: function (ev, handler) {
|
|
771
|
+
if (handlers[ev])
|
|
772
|
+
handlers[ev] = arrayRemove(handlers[ev], handler);
|
|
773
|
+
},
|
|
774
|
+
destroyAgent: destroyAgent,
|
|
775
|
+
};
|
|
776
|
+
reservedNames = Object.keys(client);
|
|
777
|
+
return client;
|
|
778
|
+
function handle(ev, once) {
|
|
779
|
+
if (once === void 0) { once = false; }
|
|
780
|
+
var args = [];
|
|
781
|
+
for (var _i = 2; _i < arguments.length; _i++) {
|
|
782
|
+
args[_i - 2] = arguments[_i];
|
|
783
|
+
}
|
|
784
|
+
handlers[ev].forEach(function (f) {
|
|
785
|
+
try {
|
|
786
|
+
f.apply(void 0, __spreadArray([], __read(args), false));
|
|
787
|
+
}
|
|
788
|
+
catch (_err) {
|
|
789
|
+
// ignore
|
|
790
|
+
}
|
|
791
|
+
});
|
|
792
|
+
if (once) {
|
|
793
|
+
handlers[ev].length = 0;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
var ContextPlugin = function (client) {
|
|
799
|
+
var contextAgent = createContextAgent();
|
|
800
|
+
client.provide('context', contextAgent);
|
|
801
|
+
client.on('report', function (ev) {
|
|
802
|
+
if (!ev.extra) {
|
|
803
|
+
ev.extra = {};
|
|
804
|
+
}
|
|
805
|
+
ev.extra.context = contextAgent.toString();
|
|
806
|
+
return ev;
|
|
807
|
+
});
|
|
808
|
+
};
|
|
809
|
+
|
|
810
|
+
function IntegrationPlugin(client, runAfterSetup) {
|
|
811
|
+
client.on('init', function () {
|
|
812
|
+
var nameList = [];
|
|
813
|
+
var applyIntegrations = function (integrations) {
|
|
814
|
+
integrations.forEach(function (integration) {
|
|
815
|
+
var integrationName = integration.name;
|
|
816
|
+
if (!arrayIncludes(nameList, integrationName)) {
|
|
817
|
+
nameList.push(integrationName);
|
|
818
|
+
integration.setup(client);
|
|
819
|
+
client.destroyAgent.set(integrationName, integrationName, [
|
|
820
|
+
function () {
|
|
821
|
+
nameList = arrayRemove(nameList, integrationName);
|
|
822
|
+
integration.tearDown && integration.tearDown();
|
|
823
|
+
},
|
|
824
|
+
]);
|
|
825
|
+
}
|
|
826
|
+
});
|
|
827
|
+
};
|
|
828
|
+
client.provide('applyIntegrations', applyIntegrations);
|
|
829
|
+
var config = client.config();
|
|
830
|
+
if (config && config.integrations) {
|
|
831
|
+
applyIntegrations(config.integrations);
|
|
832
|
+
}
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
var DevtoolsPlugin = function (client) {
|
|
837
|
+
try {
|
|
838
|
+
if (typeof window === 'object' && isObject(window) && window.__SLARDAR_DEVTOOLS_GLOBAL_HOOK__) {
|
|
839
|
+
window.__SLARDAR_DEVTOOLS_GLOBAL_HOOK__.push(client);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
catch (e) {
|
|
843
|
+
// ignore
|
|
844
|
+
}
|
|
845
|
+
};
|
|
846
|
+
|
|
847
|
+
var now = function () { return Date.now(); };
|
|
848
|
+
|
|
849
|
+
function getDefaultBrowser() {
|
|
850
|
+
if (typeof window === 'object' && isObject(window))
|
|
851
|
+
return window;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
// 获取全局注册表
|
|
855
|
+
var getGlobalRegistry = function (global) {
|
|
856
|
+
if (!global)
|
|
857
|
+
return;
|
|
858
|
+
if (!global.__SLARDAR_REGISTRY__) {
|
|
859
|
+
global.__SLARDAR_REGISTRY__ = {
|
|
860
|
+
Slardar: {
|
|
861
|
+
plugins: [],
|
|
862
|
+
errors: [],
|
|
863
|
+
subject: {},
|
|
864
|
+
},
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
return global.__SLARDAR_REGISTRY__.Slardar;
|
|
868
|
+
};
|
|
869
|
+
var reportSelfError = function () {
|
|
870
|
+
var errorInfo = [];
|
|
871
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
872
|
+
errorInfo[_i] = arguments[_i];
|
|
873
|
+
}
|
|
874
|
+
var registry = getGlobalRegistry(getDefaultBrowser());
|
|
875
|
+
if (!registry)
|
|
876
|
+
return;
|
|
877
|
+
if (!registry.errors) {
|
|
878
|
+
registry.errors = [];
|
|
879
|
+
}
|
|
880
|
+
registry.errors.push(errorInfo);
|
|
881
|
+
};
|
|
882
|
+
|
|
883
|
+
/**
|
|
884
|
+
* Special support in Perfsee for analyzing call stacks of custom events and custom metrics.
|
|
885
|
+
*/
|
|
886
|
+
// eslint-disable-next-line
|
|
887
|
+
var getStacksOnPerfsee = function (constructor) {
|
|
888
|
+
var _a;
|
|
889
|
+
// @ts-expect-error
|
|
890
|
+
if (typeof window !== 'object' || !window.__perfsee__) {
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
var obj = {};
|
|
894
|
+
(_a = Error.captureStackTrace) === null || _a === void 0 ? void 0 : _a.call(Error, obj, constructor);
|
|
895
|
+
return obj.stack;
|
|
896
|
+
};
|
|
897
|
+
|
|
898
|
+
var CUSTOM_EV_TYPE = 'custom';
|
|
899
|
+
|
|
900
|
+
var CUSTOM_EVENT_TYPE = 'event';
|
|
901
|
+
var CUSTOM_LOG_TYPE = 'log';
|
|
902
|
+
var normalizeCustomEventData = function (raw) {
|
|
903
|
+
if (!raw || !isObject(raw)) {
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
// name is required
|
|
907
|
+
if (!raw['name'] || !isString(raw['name'])) {
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
var res = {
|
|
911
|
+
name: raw['name'],
|
|
912
|
+
type: CUSTOM_EVENT_TYPE,
|
|
913
|
+
};
|
|
914
|
+
if ('metrics' in raw && isObject(raw['metrics'])) {
|
|
915
|
+
var rMetrics = raw['metrics'];
|
|
916
|
+
var metrics = {};
|
|
917
|
+
for (var k in rMetrics) {
|
|
918
|
+
if (isNumber(rMetrics[k])) {
|
|
919
|
+
metrics[k] = rMetrics[k];
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
res.metrics = metrics;
|
|
923
|
+
}
|
|
924
|
+
if ('categories' in raw && isObject(raw['categories'])) {
|
|
925
|
+
var rCategories = raw['categories'];
|
|
926
|
+
var categories = {};
|
|
927
|
+
for (var k in rCategories) {
|
|
928
|
+
categories[k] = safeStringify(rCategories[k]);
|
|
929
|
+
}
|
|
930
|
+
res.categories = categories;
|
|
931
|
+
}
|
|
932
|
+
if ('attached_log' in raw && isString(raw['attached_log'])) {
|
|
933
|
+
res.attached_log = raw['attached_log'];
|
|
934
|
+
}
|
|
935
|
+
return res;
|
|
936
|
+
};
|
|
937
|
+
var normalizeCustomLogData = function (raw) {
|
|
938
|
+
if (!raw || !isObject(raw)) {
|
|
939
|
+
return;
|
|
940
|
+
}
|
|
941
|
+
// content is required
|
|
942
|
+
if (!raw['content'] || !isString(raw['content'])) {
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
var rContent = raw['content'];
|
|
946
|
+
var res = {
|
|
947
|
+
content: safeStringify(rContent),
|
|
948
|
+
type: CUSTOM_LOG_TYPE,
|
|
949
|
+
level: 'info',
|
|
950
|
+
};
|
|
951
|
+
if ('level' in raw) {
|
|
952
|
+
res.level = raw['level'];
|
|
953
|
+
}
|
|
954
|
+
if ('extra' in raw && isObject(raw['extra'])) {
|
|
955
|
+
var rExtra = raw['extra'];
|
|
956
|
+
var metrics = {};
|
|
957
|
+
var categories = {};
|
|
958
|
+
for (var k in rExtra) {
|
|
959
|
+
if (isNumber(rExtra[k])) {
|
|
960
|
+
metrics[k] = rExtra[k];
|
|
961
|
+
}
|
|
962
|
+
else {
|
|
963
|
+
categories[k] = safeStringify(rExtra[k]);
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
res.metrics = metrics;
|
|
967
|
+
res.categories = categories;
|
|
968
|
+
}
|
|
969
|
+
if ('attached_log' in raw && isString(raw['attached_log'])) {
|
|
970
|
+
res.attached_log = raw['attached_log'];
|
|
971
|
+
}
|
|
972
|
+
return res;
|
|
973
|
+
};
|
|
974
|
+
var CustomPlugin = function (client) {
|
|
975
|
+
var sendEvent = function (data) {
|
|
976
|
+
var normalized = normalizeCustomEventData(data);
|
|
977
|
+
if (normalized) {
|
|
978
|
+
var stacks = getStacksOnPerfsee(sendEvent);
|
|
979
|
+
if (stacks) {
|
|
980
|
+
// @ts-expect-error
|
|
981
|
+
normalized.stacks = stacks;
|
|
982
|
+
}
|
|
983
|
+
client.report({
|
|
984
|
+
ev_type: CUSTOM_EV_TYPE,
|
|
985
|
+
payload: normalized,
|
|
986
|
+
extra: {
|
|
987
|
+
timestamp: now(),
|
|
988
|
+
},
|
|
989
|
+
});
|
|
990
|
+
}
|
|
991
|
+
};
|
|
992
|
+
var sendLog = function (data) {
|
|
993
|
+
var normalized = normalizeCustomLogData(data);
|
|
994
|
+
if (normalized) {
|
|
995
|
+
client.report({
|
|
996
|
+
ev_type: CUSTOM_EV_TYPE,
|
|
997
|
+
payload: normalized,
|
|
998
|
+
extra: {
|
|
999
|
+
timestamp: now(),
|
|
1000
|
+
},
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
};
|
|
1004
|
+
client.provide('sendEvent', sendEvent);
|
|
1005
|
+
client.provide('sendLog', sendLog);
|
|
1006
|
+
};
|
|
1007
|
+
|
|
1008
|
+
/* eslint-disable @typescript-eslint/prefer-for-of */
|
|
1009
|
+
var withSampleRate = function (ev, sampleRate) {
|
|
1010
|
+
var common = ev.common || {};
|
|
1011
|
+
common.sample_rate = sampleRate;
|
|
1012
|
+
ev.common = common;
|
|
1013
|
+
return ev;
|
|
1014
|
+
};
|
|
1015
|
+
var hitFnWithRandom = function (preCalc, sampleRate, isHitBySampleRate, random, isHitByRandom) {
|
|
1016
|
+
return preCalc
|
|
1017
|
+
? (function (h) { return function () {
|
|
1018
|
+
return h;
|
|
1019
|
+
}; })(isHitByRandom(random, sampleRate))
|
|
1020
|
+
: function () { return isHitBySampleRate(sampleRate); };
|
|
1021
|
+
};
|
|
1022
|
+
var parseValues = function (values, type) {
|
|
1023
|
+
return values.map(function (v) {
|
|
1024
|
+
switch (type) {
|
|
1025
|
+
case 'number':
|
|
1026
|
+
return Number(v);
|
|
1027
|
+
case 'boolean':
|
|
1028
|
+
return v === '1';
|
|
1029
|
+
case 'string': // default to string
|
|
1030
|
+
default:
|
|
1031
|
+
return String(v);
|
|
1032
|
+
}
|
|
1033
|
+
});
|
|
1034
|
+
};
|
|
1035
|
+
var checkVal = function (val, values, op) {
|
|
1036
|
+
switch (op) {
|
|
1037
|
+
case 'eq':
|
|
1038
|
+
return arrayIncludes(values, val);
|
|
1039
|
+
case 'neq':
|
|
1040
|
+
return !arrayIncludes(values, val);
|
|
1041
|
+
case 'gt':
|
|
1042
|
+
return val > values[0];
|
|
1043
|
+
case 'gte':
|
|
1044
|
+
return val >= values[0];
|
|
1045
|
+
case 'lt':
|
|
1046
|
+
return val < values[0];
|
|
1047
|
+
case 'lte':
|
|
1048
|
+
return val <= values[0];
|
|
1049
|
+
case 'regex':
|
|
1050
|
+
return Boolean(val.match(new RegExp(values.join('|'))));
|
|
1051
|
+
case 'not_regex':
|
|
1052
|
+
return !val.match(new RegExp(values.join('|')));
|
|
1053
|
+
default: {
|
|
1054
|
+
// unknown op
|
|
1055
|
+
return false;
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
};
|
|
1059
|
+
var checkFilter = function (ev, field, op, values) {
|
|
1060
|
+
var val = safeVisit(ev, field, function (t, p) {
|
|
1061
|
+
return t[p];
|
|
1062
|
+
});
|
|
1063
|
+
if (val === undefined) {
|
|
1064
|
+
return false;
|
|
1065
|
+
}
|
|
1066
|
+
var field_type = isBoolean(val) ? 'bool' : isNumber(val) ? 'number' : 'string';
|
|
1067
|
+
return checkVal(val, parseValues(values, field_type), op);
|
|
1068
|
+
};
|
|
1069
|
+
var matchFilter = function (ev, filter) {
|
|
1070
|
+
try {
|
|
1071
|
+
return filter.type === 'rule'
|
|
1072
|
+
? checkFilter(ev, filter.field, filter.op, filter.values)
|
|
1073
|
+
: filter.type === 'and'
|
|
1074
|
+
? filter.children.every(function (f) { return matchFilter(ev, f); })
|
|
1075
|
+
: filter.children.some(function (f) { return matchFilter(ev, f); });
|
|
1076
|
+
}
|
|
1077
|
+
catch (e) {
|
|
1078
|
+
reportSelfError(e);
|
|
1079
|
+
return false;
|
|
1080
|
+
}
|
|
1081
|
+
};
|
|
1082
|
+
var getHitMap = function (rules, preCalcHit, baseRate, isHitBySampleRate, random, isHitByRandom) {
|
|
1083
|
+
var hitMap = {};
|
|
1084
|
+
Object.keys(rules).forEach(function (name) {
|
|
1085
|
+
var _a = rules[name], enable = _a.enable, sample_rate = _a.sample_rate, conditional_sample_rules = _a.conditional_sample_rules;
|
|
1086
|
+
if (enable) {
|
|
1087
|
+
hitMap[name] = {
|
|
1088
|
+
enable: enable,
|
|
1089
|
+
sample_rate: sample_rate,
|
|
1090
|
+
effectiveSampleRate: sample_rate * baseRate,
|
|
1091
|
+
hit: hitFnWithRandom(preCalcHit, sample_rate, isHitBySampleRate, random, isHitByRandom),
|
|
1092
|
+
};
|
|
1093
|
+
if (conditional_sample_rules) {
|
|
1094
|
+
hitMap[name].conditional_hit_rules = conditional_sample_rules.map(function (_a) {
|
|
1095
|
+
var s = _a.sample_rate, filter = _a.filter;
|
|
1096
|
+
return ({
|
|
1097
|
+
sample_rate: s,
|
|
1098
|
+
hit: hitFnWithRandom(preCalcHit, s, isHitBySampleRate, random, isHitByRandom),
|
|
1099
|
+
effectiveSampleRate: s * baseRate,
|
|
1100
|
+
filter: filter,
|
|
1101
|
+
});
|
|
1102
|
+
});
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
else {
|
|
1106
|
+
hitMap[name] = {
|
|
1107
|
+
enable: enable,
|
|
1108
|
+
hit: function () {
|
|
1109
|
+
/* istanbul ignore next */
|
|
1110
|
+
return false;
|
|
1111
|
+
},
|
|
1112
|
+
sample_rate: 0,
|
|
1113
|
+
effectiveSampleRate: 0,
|
|
1114
|
+
};
|
|
1115
|
+
}
|
|
1116
|
+
});
|
|
1117
|
+
return hitMap;
|
|
1118
|
+
};
|
|
1119
|
+
var getSampler = function (userId, config, isHitBySampleRate, isHitByRandom, destroyFns) {
|
|
1120
|
+
if (!config)
|
|
1121
|
+
return id;
|
|
1122
|
+
// r的设计是为了允许外部传入随机数,用于彻底实现按用户采样
|
|
1123
|
+
var baseRate = config.sample_rate, include_users = config.include_users, sample_granularity = config.sample_granularity, rules = config.rules, _a = config.r, random = _a === void 0 ? Math.random() : _a;
|
|
1124
|
+
// 用户名单采样
|
|
1125
|
+
var userHit = arrayIncludes(include_users, userId);
|
|
1126
|
+
if (userHit) {
|
|
1127
|
+
return function (ev) { return withSampleRate(ev, 1); };
|
|
1128
|
+
}
|
|
1129
|
+
// should pre calculate hit
|
|
1130
|
+
var preCalcHit = sample_granularity === 'session';
|
|
1131
|
+
var baseHit = hitFnWithRandom(preCalcHit, baseRate, isHitBySampleRate, random, isHitByRandom);
|
|
1132
|
+
var hitMap = getHitMap(rules, preCalcHit, baseRate, isHitBySampleRate, random, isHitByRandom);
|
|
1133
|
+
return function (ev) {
|
|
1134
|
+
var _a;
|
|
1135
|
+
// 总采样必须命中才有后续
|
|
1136
|
+
if (!baseHit()) {
|
|
1137
|
+
preCalcHit && destroyFns[0]();
|
|
1138
|
+
return false;
|
|
1139
|
+
}
|
|
1140
|
+
// 未配置的事件类型
|
|
1141
|
+
if (!(ev.ev_type in hitMap)) {
|
|
1142
|
+
return withSampleRate(ev, baseRate);
|
|
1143
|
+
}
|
|
1144
|
+
// 忽略未开启的事件类型
|
|
1145
|
+
if (!hitMap[ev.ev_type].enable) {
|
|
1146
|
+
preCalcHit && destroyFns[1](ev.ev_type);
|
|
1147
|
+
return false;
|
|
1148
|
+
}
|
|
1149
|
+
// 跳过采样配置
|
|
1150
|
+
if ((_a = ev.common) === null || _a === void 0 ? void 0 : _a.sample_rate) {
|
|
1151
|
+
return ev;
|
|
1152
|
+
}
|
|
1153
|
+
var hitConfig = hitMap[ev.ev_type];
|
|
1154
|
+
var conditions = hitConfig.conditional_hit_rules;
|
|
1155
|
+
if (conditions) {
|
|
1156
|
+
// 先判断条件采样
|
|
1157
|
+
for (var i = 0; i < conditions.length; i++) {
|
|
1158
|
+
if (matchFilter(ev, conditions[i].filter)) {
|
|
1159
|
+
if (conditions[i].hit()) {
|
|
1160
|
+
return withSampleRate(ev, conditions[i].effectiveSampleRate);
|
|
1161
|
+
}
|
|
1162
|
+
// 条件匹配后不再搜索
|
|
1163
|
+
return false;
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
// 事件类型采样
|
|
1168
|
+
if (!hitConfig.hit()) {
|
|
1169
|
+
// not hit ev_type and no condition, destroy side effect
|
|
1170
|
+
!(conditions && conditions.length) && preCalcHit && destroyFns[1](ev.ev_type);
|
|
1171
|
+
return false;
|
|
1172
|
+
}
|
|
1173
|
+
// 事件类型默认采样已经命中
|
|
1174
|
+
return withSampleRate(ev, hitConfig.effectiveSampleRate);
|
|
1175
|
+
};
|
|
1176
|
+
};
|
|
1177
|
+
var SamplePlugin = function (client) {
|
|
1178
|
+
client.on('start', function () {
|
|
1179
|
+
var _a = client.config(), userId = _a.userId, sample = _a.sample;
|
|
1180
|
+
var destroyFns = [
|
|
1181
|
+
function () {
|
|
1182
|
+
client.destroy();
|
|
1183
|
+
},
|
|
1184
|
+
function (ev_type) {
|
|
1185
|
+
client.destroyAgent.removeByEvType(ev_type);
|
|
1186
|
+
},
|
|
1187
|
+
];
|
|
1188
|
+
var sampler = getSampler(userId, sample, isHitBySampleRate, isHitByRandom, destroyFns);
|
|
1189
|
+
client.on('build', sampler);
|
|
1190
|
+
});
|
|
1191
|
+
};
|
|
1192
|
+
|
|
1193
|
+
var builder = {
|
|
1194
|
+
build: function (e) {
|
|
1195
|
+
return {
|
|
1196
|
+
ev_type: e.ev_type,
|
|
1197
|
+
payload: e.payload,
|
|
1198
|
+
common: __assign(__assign({}, (e.extra || {})), (e.overrides || {})),
|
|
1199
|
+
};
|
|
1200
|
+
},
|
|
1201
|
+
};
|
|
1202
|
+
|
|
1203
|
+
var REPORT_DOMAIN = "mon.zijieapi.com";
|
|
1204
|
+
var SDK_VERSION = "2.1.8" ;
|
|
1205
|
+
var SDK_NAME = 'SDK_BASE';
|
|
1206
|
+
var SETTINGS_PATH = '/monitor_web/settings/browser-settings';
|
|
1207
|
+
var BATCH_REPORT_PATH = "/monitor_browser/collect/batch/";
|
|
1208
|
+
var DEFAULT_SAMPLE_CONFIG = {
|
|
1209
|
+
sample_rate: 1,
|
|
1210
|
+
include_users: [],
|
|
1211
|
+
sample_granularity: 'session',
|
|
1212
|
+
rules: {},
|
|
1213
|
+
};
|
|
1214
|
+
var DEFAULT_SENDER_SIZE = 20;
|
|
1215
|
+
var DEFAULT_SAMPLE_GRANULARITY = 'session';
|
|
1216
|
+
|
|
1217
|
+
function normalizeStrictFields(config) {
|
|
1218
|
+
var e_1, _a;
|
|
1219
|
+
var strictFields = ['userId', 'deviceId', 'sessionId', 'env'];
|
|
1220
|
+
try {
|
|
1221
|
+
for (var strictFields_1 = __values(strictFields), strictFields_1_1 = strictFields_1.next(); !strictFields_1_1.done; strictFields_1_1 = strictFields_1.next()) {
|
|
1222
|
+
var k = strictFields_1_1.value;
|
|
1223
|
+
if (!config[k]) {
|
|
1224
|
+
delete config[k];
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
1229
|
+
finally {
|
|
1230
|
+
try {
|
|
1231
|
+
if (strictFields_1_1 && !strictFields_1_1.done && (_a = strictFields_1.return)) _a.call(strictFields_1);
|
|
1232
|
+
}
|
|
1233
|
+
finally { if (e_1) throw e_1.error; }
|
|
1234
|
+
}
|
|
1235
|
+
return config;
|
|
1236
|
+
}
|
|
1237
|
+
function normalizeInitConfig(config) {
|
|
1238
|
+
return normalizeStrictFields(__assign({}, config));
|
|
1239
|
+
}
|
|
1240
|
+
function validateInitConfig(config) {
|
|
1241
|
+
return isObject(config) && 'bid' in config && 'transport' in config;
|
|
1242
|
+
}
|
|
1243
|
+
function normalizeUserConfig(config) {
|
|
1244
|
+
return normalizeStrictFields(__assign({}, config));
|
|
1245
|
+
}
|
|
1246
|
+
function parseServerConfig(serverConfig) {
|
|
1247
|
+
if (!serverConfig) {
|
|
1248
|
+
return {};
|
|
1249
|
+
}
|
|
1250
|
+
var sample = serverConfig.sample, timestamp = serverConfig.timestamp, _a = serverConfig.quota_rate, quota_rate = _a === void 0 ? 1 : _a;
|
|
1251
|
+
if (!sample) {
|
|
1252
|
+
return {};
|
|
1253
|
+
}
|
|
1254
|
+
var sample_rate = sample.sample_rate, _b = sample.sample_granularity, sample_granularity = _b === void 0 ? DEFAULT_SAMPLE_GRANULARITY : _b, include_users = sample.include_users, _c = sample.rules, rules = _c === void 0 ? [] : _c;
|
|
1255
|
+
return {
|
|
1256
|
+
sample: {
|
|
1257
|
+
include_users: include_users,
|
|
1258
|
+
sample_rate: sample_rate * quota_rate,
|
|
1259
|
+
sample_granularity: sample_granularity,
|
|
1260
|
+
rules: rules.reduce(function (prev, cur) {
|
|
1261
|
+
var name = cur.name, enable = cur.enable, sample_rate = cur.sample_rate, conditional_sample_rules = cur.conditional_sample_rules;
|
|
1262
|
+
prev[name] = {
|
|
1263
|
+
enable: enable,
|
|
1264
|
+
sample_rate: sample_rate,
|
|
1265
|
+
conditional_sample_rules: conditional_sample_rules,
|
|
1266
|
+
};
|
|
1267
|
+
return prev;
|
|
1268
|
+
}, {}),
|
|
1269
|
+
},
|
|
1270
|
+
serverTimestamp: timestamp,
|
|
1271
|
+
};
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
|
|
1275
|
+
var getReportUrl = function (domain, path) {
|
|
1276
|
+
if (path === void 0) { path = BATCH_REPORT_PATH; }
|
|
1277
|
+
return "" + (domain && domain.indexOf('//') >= 0 ? '' : 'https://') + domain + path;
|
|
1278
|
+
};
|
|
1279
|
+
var getSettingsUrl = function (domain, path) {
|
|
1280
|
+
if (path === void 0) { path = SETTINGS_PATH; }
|
|
1281
|
+
return "" + (domain && domain.indexOf('//') >= 0 ? '' : 'https://') + domain + path;
|
|
1282
|
+
};
|
|
1283
|
+
var getDefaultUserId = function () {
|
|
1284
|
+
return uuid();
|
|
1285
|
+
};
|
|
1286
|
+
var getDefaultDeviceId = function () {
|
|
1287
|
+
return uuid();
|
|
1288
|
+
};
|
|
1289
|
+
var getViewId = function (pid) { return pid + "_" + Date.now(); };
|
|
1290
|
+
var getDefaultSessionId = function () {
|
|
1291
|
+
return uuid();
|
|
1292
|
+
};
|
|
1293
|
+
|
|
1294
|
+
var createConfigManager = function (defaultConfig) {
|
|
1295
|
+
// the merged config
|
|
1296
|
+
var config = defaultConfig;
|
|
1297
|
+
// save it so we know when initConfig is set
|
|
1298
|
+
var initConfig;
|
|
1299
|
+
// save UserConfig so we can merge with priority
|
|
1300
|
+
var userConfig = {};
|
|
1301
|
+
// save the original server config, from get_setting response
|
|
1302
|
+
var serverConfig;
|
|
1303
|
+
// cache the parsed ServerConfig, used in merge
|
|
1304
|
+
var parsedServerConfig;
|
|
1305
|
+
var onReady = noop$1;
|
|
1306
|
+
// call when config changed
|
|
1307
|
+
var onChange = noop$1;
|
|
1308
|
+
return {
|
|
1309
|
+
getConfig: function () {
|
|
1310
|
+
return config;
|
|
1311
|
+
},
|
|
1312
|
+
setConfig: function (c) {
|
|
1313
|
+
userConfig = __assign(__assign({}, userConfig), (c || {}));
|
|
1314
|
+
updateConfig();
|
|
1315
|
+
if (!initConfig) {
|
|
1316
|
+
// handle init
|
|
1317
|
+
initConfig = c;
|
|
1318
|
+
if (config.useLocalConfig || !config.bid) {
|
|
1319
|
+
parsedServerConfig = {};
|
|
1320
|
+
onReady();
|
|
1321
|
+
}
|
|
1322
|
+
else {
|
|
1323
|
+
getServerConfig(config.transport, config.domain, config.bid, function (res) {
|
|
1324
|
+
serverConfig = res;
|
|
1325
|
+
handleServerConfig();
|
|
1326
|
+
});
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
return config;
|
|
1330
|
+
},
|
|
1331
|
+
onChange: function (fn) {
|
|
1332
|
+
onChange = fn;
|
|
1333
|
+
},
|
|
1334
|
+
onReady: function (fn) {
|
|
1335
|
+
onReady = fn;
|
|
1336
|
+
if (parsedServerConfig) {
|
|
1337
|
+
onReady();
|
|
1338
|
+
}
|
|
1339
|
+
},
|
|
1340
|
+
};
|
|
1341
|
+
function updateConfig() {
|
|
1342
|
+
var newConfig = __assign(__assign(__assign({}, defaultConfig), (parsedServerConfig || {})), userConfig);
|
|
1343
|
+
newConfig.sample = mergeSampleConfig(mergeSampleConfig(defaultConfig.sample, parsedServerConfig === null || parsedServerConfig === void 0 ? void 0 : parsedServerConfig.sample), userConfig.sample);
|
|
1344
|
+
config = newConfig;
|
|
1345
|
+
onChange();
|
|
1346
|
+
}
|
|
1347
|
+
function handleServerConfig() {
|
|
1348
|
+
parsedServerConfig = parseServerConfig(serverConfig);
|
|
1349
|
+
updateConfig();
|
|
1350
|
+
onReady();
|
|
1351
|
+
}
|
|
1352
|
+
};
|
|
1353
|
+
function getServerConfig(transport, domain, bid, cb) {
|
|
1354
|
+
if (!transport.get) {
|
|
1355
|
+
return cb({});
|
|
1356
|
+
}
|
|
1357
|
+
transport.get({
|
|
1358
|
+
withCredentials: true,
|
|
1359
|
+
url: getSettingsUrl(domain) + "?bid=" + bid + "&store=1",
|
|
1360
|
+
success: function (res) {
|
|
1361
|
+
cb(res.data || {});
|
|
1362
|
+
},
|
|
1363
|
+
fail: function () {
|
|
1364
|
+
cb({ sample: { sample_rate: 0.001 } });
|
|
1365
|
+
},
|
|
1366
|
+
});
|
|
1367
|
+
}
|
|
1368
|
+
function mergeSampleConfig(a, b) {
|
|
1369
|
+
if (!a || !b)
|
|
1370
|
+
return a || b;
|
|
1371
|
+
var res = __assign(__assign({}, a), b);
|
|
1372
|
+
res.include_users = __spreadArray(__spreadArray([], __read((a.include_users || [])), false), __read((b.include_users || [])), false);
|
|
1373
|
+
res.rules = __spreadArray(__spreadArray([], __read(Object.keys(a.rules || {})), false), __read(Object.keys(b.rules || {})), false).reduce(function (obj, key) {
|
|
1374
|
+
var _a, _b;
|
|
1375
|
+
if (!(key in obj)) {
|
|
1376
|
+
if (key in (a.rules || {}) && key in (b.rules || {})) {
|
|
1377
|
+
obj[key] = __assign(__assign({}, a.rules[key]), b.rules[key]);
|
|
1378
|
+
obj[key].conditional_sample_rules = __spreadArray(__spreadArray([], __read((a.rules[key].conditional_sample_rules || [])), false), __read((b.rules[key].conditional_sample_rules || [])), false);
|
|
1379
|
+
}
|
|
1380
|
+
else {
|
|
1381
|
+
obj[key] = ((_a = a.rules) === null || _a === void 0 ? void 0 : _a[key]) || ((_b = b.rules) === null || _b === void 0 ? void 0 : _b[key]);
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
return obj;
|
|
1385
|
+
}, {});
|
|
1386
|
+
return res;
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
var addEnvToSendEvent = function (ev, config) {
|
|
1390
|
+
var _a = config || {}, version = _a.version, name = _a.name;
|
|
1391
|
+
var extra = {
|
|
1392
|
+
url: '',
|
|
1393
|
+
protocol: '',
|
|
1394
|
+
domain: '',
|
|
1395
|
+
path: '',
|
|
1396
|
+
query: '',
|
|
1397
|
+
timestamp: Date.now(),
|
|
1398
|
+
sdk_version: version || SDK_VERSION,
|
|
1399
|
+
sdk_name: name || SDK_NAME,
|
|
1400
|
+
};
|
|
1401
|
+
return __assign(__assign({}, ev), { extra: __assign(__assign({}, extra), (ev.extra || {})) });
|
|
1402
|
+
};
|
|
1403
|
+
var InjectEnvPlugin = function (client) {
|
|
1404
|
+
client.on('report', function (ev) {
|
|
1405
|
+
return addEnvToSendEvent(ev, client.config());
|
|
1406
|
+
});
|
|
1407
|
+
};
|
|
1408
|
+
|
|
1409
|
+
var addConfigToReportEvent = function (ev, config) {
|
|
1410
|
+
var extra = {};
|
|
1411
|
+
extra.bid = config.bid;
|
|
1412
|
+
extra.pid = config.pid;
|
|
1413
|
+
extra.view_id = config.viewId;
|
|
1414
|
+
extra.user_id = config.userId;
|
|
1415
|
+
extra.device_id = config.deviceId;
|
|
1416
|
+
extra.session_id = config.sessionId;
|
|
1417
|
+
extra.release = config.release;
|
|
1418
|
+
extra.env = config.env;
|
|
1419
|
+
return __assign(__assign({}, ev), { extra: __assign(__assign({}, extra), (ev.extra || {})) });
|
|
1420
|
+
};
|
|
1421
|
+
var InjectConfigPlugin = function (client) {
|
|
1422
|
+
client.on('beforeBuild', function (ev) {
|
|
1423
|
+
return addConfigToReportEvent(ev, client.config());
|
|
1424
|
+
});
|
|
1425
|
+
};
|
|
1426
|
+
|
|
1427
|
+
// createSender has side effects(register onClose behaviour)
|
|
1428
|
+
// so it must be create lazily
|
|
1429
|
+
function createSender(config) {
|
|
1430
|
+
return createBatchSender(config);
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
|
|
1434
|
+
var getDefaultConfig = function (_c) { return ({
|
|
1435
|
+
bid: '',
|
|
1436
|
+
pid: '',
|
|
1437
|
+
viewId: getViewId('_'),
|
|
1438
|
+
userId: getDefaultUserId(),
|
|
1439
|
+
deviceId: getDefaultDeviceId(),
|
|
1440
|
+
sessionId: getDefaultSessionId(),
|
|
1441
|
+
domain: REPORT_DOMAIN,
|
|
1442
|
+
release: '',
|
|
1443
|
+
env: 'production',
|
|
1444
|
+
sample: DEFAULT_SAMPLE_CONFIG,
|
|
1445
|
+
plugins: {},
|
|
1446
|
+
transport: {
|
|
1447
|
+
get: noop$1,
|
|
1448
|
+
post: noop$1,
|
|
1449
|
+
},
|
|
1450
|
+
useLocalConfig: false,
|
|
1451
|
+
}); };
|
|
1452
|
+
var createMinimalClient = function (_a) {
|
|
1453
|
+
var _b = _a === void 0 ? {} : _a, _d = _b.createSender, createSender$1 = _d === void 0 ? function (config) {
|
|
1454
|
+
return createSender({
|
|
1455
|
+
size: DEFAULT_SENDER_SIZE,
|
|
1456
|
+
endpoint: getReportUrl(config.domain),
|
|
1457
|
+
transport: config.transport,
|
|
1458
|
+
});
|
|
1459
|
+
} : _d, _e = _b.builder, builder$1 = _e === void 0 ? builder : _e, _f = _b.createDefaultConfig, createDefaultConfig = _f === void 0 ? getDefaultConfig : _f;
|
|
1460
|
+
var client = createClient({
|
|
1461
|
+
validateInitConfig: validateInitConfig,
|
|
1462
|
+
initConfigNormalizer: normalizeInitConfig,
|
|
1463
|
+
userConfigNormalizer: normalizeUserConfig,
|
|
1464
|
+
createSender: createSender$1,
|
|
1465
|
+
builder: builder$1,
|
|
1466
|
+
createDefaultConfig: createDefaultConfig,
|
|
1467
|
+
createConfigManager: createConfigManager,
|
|
1468
|
+
});
|
|
1469
|
+
ContextPlugin(client);
|
|
1470
|
+
InjectConfigPlugin(client);
|
|
1471
|
+
InjectEnvPlugin(client);
|
|
1472
|
+
IntegrationPlugin(client);
|
|
1473
|
+
DevtoolsPlugin(client);
|
|
1474
|
+
return client;
|
|
1475
|
+
};
|
|
1476
|
+
var createBaseClient = function (config) {
|
|
1477
|
+
if (config === void 0) { config = {}; }
|
|
1478
|
+
var client = createMinimalClient(config);
|
|
1479
|
+
SamplePlugin(client);
|
|
1480
|
+
CustomPlugin(client);
|
|
1481
|
+
return client;
|
|
1482
|
+
};
|
|
1483
|
+
|
|
1484
|
+
var browserClient = createBaseClient();
|
|
1485
|
+
|
|
1486
|
+
/**
|
|
1487
|
+
* Node.js 环境的 HTTP Transport 实现
|
|
1488
|
+
* 使用原生 http/https 模块实现网络请求
|
|
1489
|
+
*/
|
|
1490
|
+
|
|
1491
|
+
|
|
1492
|
+
|
|
1493
|
+
const log$2 = debug('slardar:transport');
|
|
1494
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
1495
|
+
const noop = () => {};
|
|
1496
|
+
|
|
1497
|
+
|
|
1498
|
+
|
|
1499
|
+
|
|
1500
|
+
|
|
1501
|
+
|
|
1502
|
+
|
|
1503
|
+
|
|
1504
|
+
|
|
1505
|
+
|
|
1506
|
+
|
|
1507
|
+
/**
|
|
1508
|
+
* 执行 HTTP/HTTPS 请求
|
|
1509
|
+
*/
|
|
1510
|
+
function makeRequest(options) {
|
|
1511
|
+
const { url, method, data, success = noop, fail = noop, getResponseText = noop } = options;
|
|
1512
|
+
|
|
1513
|
+
log$2('Making %s request to %s', method, url);
|
|
1514
|
+
if (data) {
|
|
1515
|
+
log$2('Request data: %s', data.slice(0, 200));
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
// 检查 URL 是否有效
|
|
1519
|
+
if (!url || typeof url !== 'string') {
|
|
1520
|
+
const error = new Error(`Invalid URL: ${url}`);
|
|
1521
|
+
log$2('Invalid URL provided: %o', url);
|
|
1522
|
+
fail(error);
|
|
1523
|
+
return;
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
try {
|
|
1527
|
+
const urlObj = new URL(url);
|
|
1528
|
+
log$2('Parsed URL - protocol: %s, hostname: %s, port: %s, path: %s',
|
|
1529
|
+
urlObj.protocol, urlObj.hostname, urlObj.port, urlObj.pathname);
|
|
1530
|
+
|
|
1531
|
+
const isHttps = urlObj.protocol === 'https:';
|
|
1532
|
+
const request = isHttps ? node_https.request : node_http.request;
|
|
1533
|
+
|
|
1534
|
+
const req = request(
|
|
1535
|
+
{
|
|
1536
|
+
hostname: urlObj.hostname,
|
|
1537
|
+
port: urlObj.port,
|
|
1538
|
+
path: urlObj.pathname + urlObj.search,
|
|
1539
|
+
method,
|
|
1540
|
+
headers: {
|
|
1541
|
+
'Content-Type': 'application/json',
|
|
1542
|
+
...(data && { 'Content-Length': Buffer.byteLength(data) }),
|
|
1543
|
+
},
|
|
1544
|
+
},
|
|
1545
|
+
res => {
|
|
1546
|
+
log$2('Response callback triggered: status=%s', res.statusCode);
|
|
1547
|
+
let responseText = '';
|
|
1548
|
+
|
|
1549
|
+
res.on('data', chunk => {
|
|
1550
|
+
log$2('Response data chunk received: %s bytes', chunk.length);
|
|
1551
|
+
responseText += chunk.toString();
|
|
1552
|
+
});
|
|
1553
|
+
|
|
1554
|
+
res.on('end', () => {
|
|
1555
|
+
log$2('Response end event fired');
|
|
1556
|
+
log$2('Response received: status=%s, body=%s', res.statusCode, responseText.slice(0, 200));
|
|
1557
|
+
getResponseText(responseText);
|
|
1558
|
+
|
|
1559
|
+
try {
|
|
1560
|
+
if (res.statusCode && res.statusCode >= 400) {
|
|
1561
|
+
log$2('Request failed with status %s: %s', res.statusCode, responseText);
|
|
1562
|
+
fail(new Error(responseText || res.statusMessage || 'Request failed'));
|
|
1563
|
+
} else if (responseText) {
|
|
1564
|
+
// eslint-disable-next-line no-restricted-syntax -- Parsing trusted Slardar API responses
|
|
1565
|
+
const result = JSON.parse(responseText);
|
|
1566
|
+
log$2('Request succeeded');
|
|
1567
|
+
success(result);
|
|
1568
|
+
} else {
|
|
1569
|
+
log$2('Request succeeded with empty response');
|
|
1570
|
+
success({});
|
|
1571
|
+
}
|
|
1572
|
+
} catch (e) {
|
|
1573
|
+
log$2('Failed to parse response: %s', (e ).message);
|
|
1574
|
+
fail(e );
|
|
1575
|
+
}
|
|
1576
|
+
});
|
|
1577
|
+
},
|
|
1578
|
+
);
|
|
1579
|
+
|
|
1580
|
+
req.on('error', err => {
|
|
1581
|
+
log$2('Request error: %s', err.message);
|
|
1582
|
+
fail(err);
|
|
1583
|
+
});
|
|
1584
|
+
|
|
1585
|
+
req.on('timeout', () => {
|
|
1586
|
+
log$2('Request timeout');
|
|
1587
|
+
req.destroy();
|
|
1588
|
+
fail(new Error('Request timeout'));
|
|
1589
|
+
});
|
|
1590
|
+
|
|
1591
|
+
if (data) {
|
|
1592
|
+
req.write(data);
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
req.end();
|
|
1596
|
+
} catch (e) {
|
|
1597
|
+
log$2('Exception during request: %s', (e ).message);
|
|
1598
|
+
fail(e );
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
/**
|
|
1603
|
+
* 创建 Node.js 环境的 Transport
|
|
1604
|
+
*/
|
|
1605
|
+
function createNodeTransport() {
|
|
1606
|
+
return {
|
|
1607
|
+
get(options) {
|
|
1608
|
+
log$2('Transport GET called: %s', options.url);
|
|
1609
|
+
makeRequest({
|
|
1610
|
+
method: 'GET',
|
|
1611
|
+
...options,
|
|
1612
|
+
});
|
|
1613
|
+
},
|
|
1614
|
+
post({ url, data }) {
|
|
1615
|
+
log$2('Transport POST called: %s', url);
|
|
1616
|
+
makeRequest({
|
|
1617
|
+
method: 'POST',
|
|
1618
|
+
url,
|
|
1619
|
+
data: typeof data === 'string' ? data : JSON.stringify(data),
|
|
1620
|
+
});
|
|
1621
|
+
},
|
|
1622
|
+
};
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
function _nullishCoalesce$3(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain$6(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }/**
|
|
1626
|
+
* Slardar CLI Reporter 主类
|
|
1627
|
+
* 封装 @slardar/base 的初始化和上报逻辑
|
|
1628
|
+
*/
|
|
1629
|
+
|
|
1630
|
+
|
|
1631
|
+
|
|
1632
|
+
|
|
1633
|
+
|
|
1634
|
+
|
|
1635
|
+
// 创建 debug 实例
|
|
1636
|
+
// 使用方式: DEBUG=slardar:* your-cli-command
|
|
1637
|
+
const log$1 = debug('slardar:reporter');
|
|
1638
|
+
|
|
1639
|
+
/**
|
|
1640
|
+
* Slardar CLI Reporter
|
|
1641
|
+
* 提供简洁的 API 用于 CLI 工具的监控数据上报
|
|
1642
|
+
*/
|
|
1643
|
+
class SlardarCLIReporter {constructor() { SlardarCLIReporter.prototype.__init.call(this);SlardarCLIReporter.prototype.__init2.call(this); }
|
|
1644
|
+
__init() {this.initialized = false;}
|
|
1645
|
+
__init2() {this.client = browserClient;}
|
|
1646
|
+
|
|
1647
|
+
/**
|
|
1648
|
+
* 初始化 Slardar Reporter
|
|
1649
|
+
* @param config 初始化配置
|
|
1650
|
+
*
|
|
1651
|
+
* @example
|
|
1652
|
+
* ```typescript
|
|
1653
|
+
* reporter.setup({
|
|
1654
|
+
* bid: 'my_cli_app',
|
|
1655
|
+
* release: '1.0.0',
|
|
1656
|
+
* env: 'production',
|
|
1657
|
+
* });
|
|
1658
|
+
* ```
|
|
1659
|
+
*/
|
|
1660
|
+
setup(config) {
|
|
1661
|
+
if (this.initialized) {
|
|
1662
|
+
log$1('Already initialized, skipping setup');
|
|
1663
|
+
return;
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
try {
|
|
1667
|
+
log$1('Initializing Slardar with config:', {
|
|
1668
|
+
bid: config.bid,
|
|
1669
|
+
release: config.release,
|
|
1670
|
+
env: config.env,
|
|
1671
|
+
userId: config.userId,
|
|
1672
|
+
});
|
|
1673
|
+
|
|
1674
|
+
const transport = createNodeTransport();
|
|
1675
|
+
|
|
1676
|
+
this.client.init({
|
|
1677
|
+
bid: config.bid,
|
|
1678
|
+
transport,
|
|
1679
|
+
userId: config.userId,
|
|
1680
|
+
deviceId: config.deviceId,
|
|
1681
|
+
sessionId: config.sessionId,
|
|
1682
|
+
release: config.release,
|
|
1683
|
+
env: config.env,
|
|
1684
|
+
name: config.name,
|
|
1685
|
+
useLocalConfig: _nullishCoalesce$3(config.useLocalConfig, () => ( false)), // 默认使用服务端配置
|
|
1686
|
+
domain: config.domain,
|
|
1687
|
+
// 设置本地采样率,确保事件不被过滤
|
|
1688
|
+
sample: {
|
|
1689
|
+
event: 1, // 100% 采样自定义事件
|
|
1690
|
+
},
|
|
1691
|
+
});
|
|
1692
|
+
|
|
1693
|
+
// 添加 beforeSend 钩子来追踪事件是否被过滤
|
|
1694
|
+
this.client.on('beforeSend', (ev) => {
|
|
1695
|
+
log$1('beforeSend hook called for event: %s', (_optionalChain$6([ev, 'optionalAccess', _ => _.ev_type]) ) || 'unknown');
|
|
1696
|
+
return ev; // 必须返回 ev 才能继续上报
|
|
1697
|
+
});
|
|
1698
|
+
|
|
1699
|
+
this.client.start();
|
|
1700
|
+
this.initialized = true;
|
|
1701
|
+
log$1('Slardar initialized successfully');
|
|
1702
|
+
} catch (error) {
|
|
1703
|
+
log$1('Failed to initialize:', error);
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
/**
|
|
1708
|
+
* 检查是否已初始化
|
|
1709
|
+
*/
|
|
1710
|
+
ensureInitialized() {
|
|
1711
|
+
if (!this.initialized) {
|
|
1712
|
+
log$1('Not initialized, call setup() first');
|
|
1713
|
+
return false;
|
|
1714
|
+
}
|
|
1715
|
+
return true;
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
/**
|
|
1719
|
+
* 上报自定义事件
|
|
1720
|
+
* @param name 事件名称
|
|
1721
|
+
* @param metrics 指标数据(数值类型)
|
|
1722
|
+
* @param categories 分类维度(字符串类型)
|
|
1723
|
+
*/
|
|
1724
|
+
sendEvent(
|
|
1725
|
+
name,
|
|
1726
|
+
metrics,
|
|
1727
|
+
categories,
|
|
1728
|
+
) {
|
|
1729
|
+
if (!this.ensureInitialized()) {
|
|
1730
|
+
return;
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
try {
|
|
1734
|
+
log$1('Sending event:', { name, metrics, categories });
|
|
1735
|
+
this.client.sendEvent({
|
|
1736
|
+
name,
|
|
1737
|
+
metrics,
|
|
1738
|
+
categories,
|
|
1739
|
+
});
|
|
1740
|
+
} catch (error) {
|
|
1741
|
+
log$1('Failed to send event:', error);
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
/**
|
|
1746
|
+
* 上报 JS 错误(会显示在 Slardar JS 错误总览页面)
|
|
1747
|
+
* @param error Error 对象
|
|
1748
|
+
* @param extra 额外的上下文信息
|
|
1749
|
+
* @param source 错误来源信息
|
|
1750
|
+
*/
|
|
1751
|
+
reportError(
|
|
1752
|
+
error,
|
|
1753
|
+
extra,
|
|
1754
|
+
source,
|
|
1755
|
+
) {
|
|
1756
|
+
if (!this.ensureInitialized()) {
|
|
1757
|
+
return;
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
try {
|
|
1761
|
+
const errorEvent = {
|
|
1762
|
+
ev_type: 'js_error' ,
|
|
1763
|
+
payload: {
|
|
1764
|
+
error: {
|
|
1765
|
+
name: error.name,
|
|
1766
|
+
message: error.message,
|
|
1767
|
+
stack: error.stack,
|
|
1768
|
+
},
|
|
1769
|
+
breadcrumbs: [], // CLI 环境暂不收集面包屑
|
|
1770
|
+
extra,
|
|
1771
|
+
source: source || { type: 'manual' }, // 默认为手动捕获
|
|
1772
|
+
},
|
|
1773
|
+
};
|
|
1774
|
+
|
|
1775
|
+
log$1('Reporting JS error:', {
|
|
1776
|
+
name: error.name,
|
|
1777
|
+
message: error.message.slice(0, 100),
|
|
1778
|
+
stack: _optionalChain$6([error, 'access', _2 => _2.stack, 'optionalAccess', _3 => _3.slice, 'call', _4 => _4(0, 200)]),
|
|
1779
|
+
extra,
|
|
1780
|
+
source,
|
|
1781
|
+
});
|
|
1782
|
+
|
|
1783
|
+
// 使用 Slardar 的 js_error 事件类型,这样会显示在 JS 错误总览页面
|
|
1784
|
+
this.client.report(errorEvent);
|
|
1785
|
+
log$1('JS error reported successfully');
|
|
1786
|
+
} catch (err) {
|
|
1787
|
+
log$1('Failed to report error:', err);
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
|
|
1792
|
+
/**
|
|
1793
|
+
* 设置全局上下文
|
|
1794
|
+
*/
|
|
1795
|
+
setContext(key, value) {
|
|
1796
|
+
if (!this.ensureInitialized()) {
|
|
1797
|
+
return;
|
|
1798
|
+
}
|
|
1799
|
+
this.client.context.set(key, value);
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
/**
|
|
1803
|
+
* 批量设置全局上下文
|
|
1804
|
+
*/
|
|
1805
|
+
mergeContext(context) {
|
|
1806
|
+
if (!this.ensureInitialized()) {
|
|
1807
|
+
return;
|
|
1808
|
+
}
|
|
1809
|
+
log$1('Merging context:', context);
|
|
1810
|
+
this.client.context.merge(context);
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
/**
|
|
1814
|
+
* 删除全局上下文
|
|
1815
|
+
*/
|
|
1816
|
+
deleteContext(key) {
|
|
1817
|
+
if (!this.ensureInitialized()) {
|
|
1818
|
+
return;
|
|
1819
|
+
}
|
|
1820
|
+
this.client.context.delete(key);
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1823
|
+
/**
|
|
1824
|
+
* 清空全局上下文
|
|
1825
|
+
*/
|
|
1826
|
+
clearContext() {
|
|
1827
|
+
if (!this.ensureInitialized()) {
|
|
1828
|
+
return;
|
|
1829
|
+
}
|
|
1830
|
+
this.client.context.clear();
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
/**
|
|
1834
|
+
* 更新用户配置
|
|
1835
|
+
*/
|
|
1836
|
+
updateConfig(
|
|
1837
|
+
config
|
|
1838
|
+
|
|
1839
|
+
,
|
|
1840
|
+
) {
|
|
1841
|
+
if (!this.ensureInitialized()) {
|
|
1842
|
+
return;
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
try {
|
|
1846
|
+
log$1('Updating config:', config);
|
|
1847
|
+
this.client.config({
|
|
1848
|
+
userId: config.userId,
|
|
1849
|
+
release: config.release,
|
|
1850
|
+
sessionId: config.sessionId,
|
|
1851
|
+
env: config.env,
|
|
1852
|
+
});
|
|
1853
|
+
} catch (error) {
|
|
1854
|
+
log$1('Failed to update config:', error);
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
/**
|
|
1859
|
+
* 立即上报(不等待队列)
|
|
1860
|
+
* @param waitMs 等待时间(毫秒),默认 1500ms,用于确保设置请求完成和事件发送
|
|
1861
|
+
*/
|
|
1862
|
+
async flush(waitMs = 1500) {
|
|
1863
|
+
if (!this.ensureInitialized()) {
|
|
1864
|
+
return;
|
|
1865
|
+
}
|
|
1866
|
+
log$1('Flushing Slardar data...');
|
|
1867
|
+
this.client.getSender().flush();
|
|
1868
|
+
log$1('Waiting %dms for events to be sent...', waitMs);
|
|
1869
|
+
await new Promise(resolve => setTimeout(resolve, waitMs));
|
|
1870
|
+
log$1('Slardar data flushed');
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
/**
|
|
1874
|
+
* 获取原始 Slardar client(用于高级用法)
|
|
1875
|
+
*/
|
|
1876
|
+
getRawClient() {
|
|
1877
|
+
return this.client;
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
/**
|
|
1882
|
+
* 默认导出的单例实例
|
|
1883
|
+
*/
|
|
1884
|
+
const reporter = new SlardarCLIReporter();
|
|
1885
|
+
|
|
1886
|
+
function _optionalChain$5(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
1887
|
+
|
|
1888
|
+
|
|
1889
|
+
|
|
1890
|
+
|
|
1891
|
+
|
|
1892
|
+
/**
|
|
1893
|
+
* CLI 通用事件类型常量
|
|
1894
|
+
* 仅包含通用的 CLI 工具事件,不包含具体业务逻辑
|
|
1895
|
+
*/
|
|
1896
|
+
const CLI_EVENTS = {
|
|
1897
|
+
/** CLI 启动 */
|
|
1898
|
+
CLI_START: 'cli_start',
|
|
1899
|
+
/** CLI 命令执行 */
|
|
1900
|
+
CLI_COMMAND: 'cli_command',
|
|
1901
|
+
/** CLI 命令完成 */
|
|
1902
|
+
CLI_COMMAND_COMPLETE: 'cli_command_complete',
|
|
1903
|
+
/** CLI 错误 */
|
|
1904
|
+
CLI_ERROR: 'cli_error',
|
|
1905
|
+
/** 网络请求 */
|
|
1906
|
+
NETWORK_REQUEST: 'network_request',
|
|
1907
|
+
/** 文件操作 */
|
|
1908
|
+
FILE_OPERATION: 'file_operation',
|
|
1909
|
+
} ;
|
|
1910
|
+
|
|
1911
|
+
|
|
1912
|
+
|
|
1913
|
+
/**
|
|
1914
|
+
* 事件构建器 - 提供更友好的事件创建接口
|
|
1915
|
+
*/
|
|
1916
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1917
|
+
const EventBuilder = {
|
|
1918
|
+
/**
|
|
1919
|
+
* 构建 CLI 启动事件
|
|
1920
|
+
*/
|
|
1921
|
+
cliStart(metrics) {
|
|
1922
|
+
return {
|
|
1923
|
+
name: CLI_EVENTS.CLI_START,
|
|
1924
|
+
metrics: {
|
|
1925
|
+
duration: 0,
|
|
1926
|
+
...metrics,
|
|
1927
|
+
},
|
|
1928
|
+
};
|
|
1929
|
+
},
|
|
1930
|
+
|
|
1931
|
+
/**
|
|
1932
|
+
* 构建 CLI 命令执行事件
|
|
1933
|
+
*/
|
|
1934
|
+
cliCommand(
|
|
1935
|
+
command,
|
|
1936
|
+
options
|
|
1937
|
+
|
|
1938
|
+
|
|
1939
|
+
|
|
1940
|
+
,
|
|
1941
|
+
) {
|
|
1942
|
+
return {
|
|
1943
|
+
name: CLI_EVENTS.CLI_COMMAND,
|
|
1944
|
+
categories: {
|
|
1945
|
+
command,
|
|
1946
|
+
args: _optionalChain$5([options, 'optionalAccess', _ => _.args]),
|
|
1947
|
+
status: 'running' ,
|
|
1948
|
+
..._optionalChain$5([options, 'optionalAccess', _2 => _2.categories]),
|
|
1949
|
+
},
|
|
1950
|
+
metrics: _optionalChain$5([options, 'optionalAccess', _3 => _3.metrics]),
|
|
1951
|
+
};
|
|
1952
|
+
},
|
|
1953
|
+
|
|
1954
|
+
/**
|
|
1955
|
+
* 构建 CLI 命令完成事件
|
|
1956
|
+
*/
|
|
1957
|
+
cliCommandComplete(
|
|
1958
|
+
command,
|
|
1959
|
+
success,
|
|
1960
|
+
duration,
|
|
1961
|
+
options
|
|
1962
|
+
|
|
1963
|
+
|
|
1964
|
+
|
|
1965
|
+
,
|
|
1966
|
+
) {
|
|
1967
|
+
return {
|
|
1968
|
+
name: CLI_EVENTS.CLI_COMMAND_COMPLETE,
|
|
1969
|
+
categories: {
|
|
1970
|
+
command,
|
|
1971
|
+
args: _optionalChain$5([options, 'optionalAccess', _4 => _4.args]),
|
|
1972
|
+
status: success ? ('success' ) : ('fail' ),
|
|
1973
|
+
..._optionalChain$5([options, 'optionalAccess', _5 => _5.categories]),
|
|
1974
|
+
},
|
|
1975
|
+
metrics: {
|
|
1976
|
+
duration,
|
|
1977
|
+
...(_optionalChain$5([options, 'optionalAccess', _6 => _6.errorCode]) && { errorCode: options.errorCode }),
|
|
1978
|
+
},
|
|
1979
|
+
};
|
|
1980
|
+
},
|
|
1981
|
+
|
|
1982
|
+
/**
|
|
1983
|
+
* 构建网络请求事件
|
|
1984
|
+
*/
|
|
1985
|
+
networkRequest(
|
|
1986
|
+
url,
|
|
1987
|
+
options
|
|
1988
|
+
|
|
1989
|
+
|
|
1990
|
+
|
|
1991
|
+
|
|
1992
|
+
,
|
|
1993
|
+
) {
|
|
1994
|
+
return {
|
|
1995
|
+
name: CLI_EVENTS.NETWORK_REQUEST,
|
|
1996
|
+
categories: {
|
|
1997
|
+
url,
|
|
1998
|
+
method: _optionalChain$5([options, 'optionalAccess', _7 => _7.method]) || 'GET',
|
|
1999
|
+
statusCode: _optionalChain$5([options, 'optionalAccess', _8 => _8.statusCode, 'optionalAccess', _9 => _9.toString, 'call', _10 => _10()]),
|
|
2000
|
+
status: _optionalChain$5([options, 'optionalAccess', _11 => _11.success]) ? ('success' ) : ('fail' ),
|
|
2001
|
+
},
|
|
2002
|
+
metrics: {
|
|
2003
|
+
duration: _optionalChain$5([options, 'optionalAccess', _12 => _12.duration]),
|
|
2004
|
+
},
|
|
2005
|
+
};
|
|
2006
|
+
},
|
|
2007
|
+
|
|
2008
|
+
/**
|
|
2009
|
+
* 构建文件操作事件
|
|
2010
|
+
*/
|
|
2011
|
+
fileOperation(
|
|
2012
|
+
operation,
|
|
2013
|
+
filePath,
|
|
2014
|
+
options
|
|
2015
|
+
|
|
2016
|
+
|
|
2017
|
+
|
|
2018
|
+
,
|
|
2019
|
+
) {
|
|
2020
|
+
return {
|
|
2021
|
+
name: CLI_EVENTS.FILE_OPERATION,
|
|
2022
|
+
categories: {
|
|
2023
|
+
operation,
|
|
2024
|
+
filePath,
|
|
2025
|
+
status: _optionalChain$5([options, 'optionalAccess', _13 => _13.success]) ? ('success' ) : ('fail' ),
|
|
2026
|
+
},
|
|
2027
|
+
metrics: {
|
|
2028
|
+
duration: _optionalChain$5([options, 'optionalAccess', _14 => _14.duration]),
|
|
2029
|
+
fileSize: _optionalChain$5([options, 'optionalAccess', _15 => _15.fileSize]),
|
|
2030
|
+
},
|
|
2031
|
+
};
|
|
2032
|
+
},
|
|
2033
|
+
|
|
2034
|
+
/**
|
|
2035
|
+
* 构建错误事件
|
|
2036
|
+
*/
|
|
2037
|
+
cliError(
|
|
2038
|
+
error,
|
|
2039
|
+
context,
|
|
2040
|
+
) {
|
|
2041
|
+
return {
|
|
2042
|
+
name: CLI_EVENTS.CLI_ERROR,
|
|
2043
|
+
categories: {
|
|
2044
|
+
errorName: error.name,
|
|
2045
|
+
errorMessage: error.message,
|
|
2046
|
+
stack: error.stack || '',
|
|
2047
|
+
...context,
|
|
2048
|
+
},
|
|
2049
|
+
metrics: {
|
|
2050
|
+
errorCode: 1,
|
|
2051
|
+
},
|
|
2052
|
+
};
|
|
2053
|
+
},
|
|
2054
|
+
};
|
|
2055
|
+
|
|
2056
|
+
var name = "@coze-arch/cli";
|
|
2057
|
+
var version = "0.0.1-alpha.de4758";
|
|
2058
|
+
var description = "coze coding devtools cli";
|
|
2059
|
+
var license = "MIT";
|
|
2060
|
+
var author = "fanwenjie.fe@bytedance.com";
|
|
2061
|
+
var maintainers = [
|
|
2062
|
+
];
|
|
2063
|
+
var bin = {
|
|
2064
|
+
coze: "bin/main"
|
|
2065
|
+
};
|
|
2066
|
+
var files = [
|
|
2067
|
+
"lib",
|
|
2068
|
+
"bin",
|
|
2069
|
+
"lib/**/.npmrc",
|
|
2070
|
+
"!**/*.tsbuildinfo",
|
|
2071
|
+
"!**/*.map"
|
|
2072
|
+
];
|
|
2073
|
+
var scripts = {
|
|
2074
|
+
prebuild: "tsx scripts/prebuild.ts",
|
|
2075
|
+
build: "tsx scripts/build.ts",
|
|
2076
|
+
create: "tsx scripts/create-template.ts",
|
|
2077
|
+
lint: "eslint ./ --cache",
|
|
2078
|
+
postpublish: "bash scripts/sync-npmmirror.sh",
|
|
2079
|
+
test: "vitest --run --passWithNoTests",
|
|
2080
|
+
"test:all": "bash scripts/test-coverage.sh",
|
|
2081
|
+
"test:cov": "vitest --run --passWithNoTests --coverage",
|
|
2082
|
+
"test:e2e": "NODE_ENV=test bash scripts/e2e.sh",
|
|
2083
|
+
"test:perf": "vitest bench --run --config vitest.perf.config.ts",
|
|
2084
|
+
"test:perf:compare": "bash scripts/compare-perf.sh",
|
|
2085
|
+
"test:perf:save": "bash scripts/run-perf-with-output.sh"
|
|
2086
|
+
};
|
|
2087
|
+
var dependencies = {
|
|
2088
|
+
"@iarna/toml": "^2.2.5",
|
|
2089
|
+
ajv: "^8.17.1",
|
|
2090
|
+
"ajv-formats": "^3.0.1",
|
|
2091
|
+
"change-case": "^5.4.4",
|
|
2092
|
+
commander: "~12.1.0",
|
|
2093
|
+
debug: "^4.3.7",
|
|
2094
|
+
ejs: "^3.1.10",
|
|
2095
|
+
"js-yaml": "^4.1.0",
|
|
2096
|
+
minimist: "^1.2.5",
|
|
2097
|
+
shelljs: "^0.10.0"
|
|
2098
|
+
};
|
|
2099
|
+
var devDependencies = {
|
|
2100
|
+
"@coze-arch/cli-logger": "workspace:*",
|
|
2101
|
+
"@coze-arch/cli-slardar": "workspace:*",
|
|
2102
|
+
"@coze-arch/eslint-config": "workspace:*",
|
|
2103
|
+
"@coze-arch/monorepo-kits": "workspace:*",
|
|
2104
|
+
"@coze-arch/rollup-config": "workspace:*",
|
|
2105
|
+
"@coze-arch/ts-config": "workspace:*",
|
|
2106
|
+
"@coze-arch/vitest-config": "workspace:*",
|
|
2107
|
+
"@coze-coding/lambda": "workspace:*",
|
|
2108
|
+
"@inquirer/prompts": "^3.2.0",
|
|
2109
|
+
"@playwright/test": "~1.55.0",
|
|
2110
|
+
"@types/debug": "^4.1.12",
|
|
2111
|
+
"@types/ejs": "^3.1.5",
|
|
2112
|
+
"@types/iarna__toml": "^2.0.5",
|
|
2113
|
+
"@types/js-yaml": "^4.0.9",
|
|
2114
|
+
"@types/minimatch": "^5.1.2",
|
|
2115
|
+
"@types/minimist": "^1.2.5",
|
|
2116
|
+
"@types/node": "^24",
|
|
2117
|
+
"@types/shelljs": "^0.10.0",
|
|
2118
|
+
"@vitest/coverage-v8": "~4.0.18",
|
|
2119
|
+
"json-schema-to-typescript": "^15.0.3",
|
|
2120
|
+
minimatch: "^10.0.1",
|
|
2121
|
+
playwright: "~1.55.0",
|
|
2122
|
+
rollup: "^4.41.1",
|
|
2123
|
+
sucrase: "^3.35.0",
|
|
2124
|
+
"tree-kill": "^1.2.2",
|
|
2125
|
+
tsx: "^4.20.6",
|
|
2126
|
+
"vite-tsconfig-paths": "^4.2.1",
|
|
2127
|
+
vitest: "~4.0.18"
|
|
2128
|
+
};
|
|
2129
|
+
var publishConfig = {
|
|
2130
|
+
access: "public",
|
|
2131
|
+
registry: "https://registry.npmjs.org"
|
|
2132
|
+
};
|
|
2133
|
+
var cozePublishConfig = {
|
|
2134
|
+
bin: {
|
|
2135
|
+
coze: "bin/main"
|
|
2136
|
+
}
|
|
2137
|
+
};
|
|
2138
|
+
var packageJson = {
|
|
2139
|
+
name: name,
|
|
2140
|
+
version: version,
|
|
2141
|
+
"private": false,
|
|
2142
|
+
description: description,
|
|
2143
|
+
license: license,
|
|
2144
|
+
author: author,
|
|
2145
|
+
maintainers: maintainers,
|
|
2146
|
+
bin: bin,
|
|
2147
|
+
files: files,
|
|
2148
|
+
scripts: scripts,
|
|
2149
|
+
dependencies: dependencies,
|
|
2150
|
+
devDependencies: devDependencies,
|
|
2151
|
+
publishConfig: publishConfig,
|
|
2152
|
+
cozePublishConfig: cozePublishConfig
|
|
2153
|
+
};
|
|
2154
|
+
|
|
2155
|
+
function _optionalChain$4(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }/**
|
|
2156
|
+
* Slardar 监控初始化和上报
|
|
2157
|
+
*/
|
|
2158
|
+
|
|
2159
|
+
const log = debug('slardar:cli');
|
|
2160
|
+
|
|
2161
|
+
/**
|
|
2162
|
+
* 安全执行函数包装器
|
|
2163
|
+
* 捕获并静默处理所有错误,确保 Slardar 上报失败不影响 CLI 正常运行
|
|
2164
|
+
* 支持同步和异步函数
|
|
2165
|
+
*/
|
|
2166
|
+
function safeRun(
|
|
2167
|
+
name,
|
|
2168
|
+
fn,
|
|
2169
|
+
) {
|
|
2170
|
+
return ((...args) => {
|
|
2171
|
+
try {
|
|
2172
|
+
log('Calling Slardar function: %s', name);
|
|
2173
|
+
const result = fn(...args);
|
|
2174
|
+
|
|
2175
|
+
// 如果是 Promise,处理异步错误
|
|
2176
|
+
if (result instanceof Promise) {
|
|
2177
|
+
return result
|
|
2178
|
+
.then(res => {
|
|
2179
|
+
log('Slardar function %s completed', name);
|
|
2180
|
+
return res;
|
|
2181
|
+
})
|
|
2182
|
+
.catch(error => {
|
|
2183
|
+
log(
|
|
2184
|
+
'Slardar function %s failed: %s',
|
|
2185
|
+
name,
|
|
2186
|
+
(error ).message,
|
|
2187
|
+
);
|
|
2188
|
+
void error;
|
|
2189
|
+
});
|
|
2190
|
+
}
|
|
2191
|
+
|
|
2192
|
+
log('Slardar function %s completed', name);
|
|
2193
|
+
return result;
|
|
2194
|
+
} catch (error) {
|
|
2195
|
+
// Slardar 上报失败不应影响 CLI 正常运行,但要记录错误
|
|
2196
|
+
log('Slardar function %s failed: %s', name, (error ).message);
|
|
2197
|
+
}
|
|
2198
|
+
}) ;
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2201
|
+
/**
|
|
2202
|
+
* 初始化 Slardar Reporter
|
|
2203
|
+
*/
|
|
2204
|
+
const initSlardar = safeRun('initSlardar', () => {
|
|
2205
|
+
reporter.setup({
|
|
2206
|
+
bid: 'coze_codign_cli',
|
|
2207
|
+
release: packageJson.version,
|
|
2208
|
+
env: process.env.NODE_ENV || 'production',
|
|
2209
|
+
userId: process.env.USER,
|
|
2210
|
+
projectId: process.env.COZE_PROJECT_ID || '',
|
|
2211
|
+
useLocalConfig: false, // 启用服务端采样率配置
|
|
2212
|
+
domain: 'mon.zijieapi.com', // Node.js 环境上报域名
|
|
2213
|
+
});
|
|
2214
|
+
|
|
2215
|
+
// 设置全局上下文
|
|
2216
|
+
reporter.mergeContext({
|
|
2217
|
+
platform: process.platform,
|
|
2218
|
+
nodeVersion: process.version,
|
|
2219
|
+
cliVersion: packageJson.version,
|
|
2220
|
+
});
|
|
2221
|
+
});
|
|
2222
|
+
|
|
2223
|
+
/**
|
|
2224
|
+
* 上报命令执行
|
|
2225
|
+
*/
|
|
2226
|
+
const reportCommandStart = safeRun(
|
|
2227
|
+
'reportCommandStart',
|
|
2228
|
+
(
|
|
2229
|
+
command,
|
|
2230
|
+
args,
|
|
2231
|
+
extraCategories,
|
|
2232
|
+
) => {
|
|
2233
|
+
const event = EventBuilder.cliCommand(command, {
|
|
2234
|
+
args,
|
|
2235
|
+
categories: extraCategories,
|
|
2236
|
+
});
|
|
2237
|
+
reporter.sendEvent(event.name, event.metrics, event.categories);
|
|
2238
|
+
},
|
|
2239
|
+
);
|
|
2240
|
+
|
|
2241
|
+
/**
|
|
2242
|
+
* 上报命令完成
|
|
2243
|
+
*/
|
|
2244
|
+
const reportCommandComplete = safeRun(
|
|
2245
|
+
'reportCommandComplete',
|
|
2246
|
+
(
|
|
2247
|
+
command,
|
|
2248
|
+
success,
|
|
2249
|
+
duration,
|
|
2250
|
+
options
|
|
2251
|
+
|
|
2252
|
+
|
|
2253
|
+
|
|
2254
|
+
|
|
2255
|
+
,
|
|
2256
|
+
) => {
|
|
2257
|
+
const event = EventBuilder.cliCommandComplete(command, success, duration, {
|
|
2258
|
+
args: _optionalChain$4([options, 'optionalAccess', _ => _.args]),
|
|
2259
|
+
errorCode: _optionalChain$4([options, 'optionalAccess', _2 => _2.errorCode]),
|
|
2260
|
+
categories: {
|
|
2261
|
+
...(_optionalChain$4([options, 'optionalAccess', _3 => _3.errorMessage]) && { errorMessage: options.errorMessage }),
|
|
2262
|
+
..._optionalChain$4([options, 'optionalAccess', _4 => _4.categories]),
|
|
2263
|
+
},
|
|
2264
|
+
});
|
|
2265
|
+
reporter.sendEvent(event.name, event.metrics, event.categories);
|
|
2266
|
+
},
|
|
2267
|
+
);
|
|
2268
|
+
|
|
2269
|
+
/**
|
|
2270
|
+
* 上报错误(使用 JS 错误上报,会显示在 Slardar JS 错误总览页面)
|
|
2271
|
+
*/
|
|
2272
|
+
const reportError = safeRun(
|
|
2273
|
+
'reportError',
|
|
2274
|
+
(error, context) => {
|
|
2275
|
+
reporter.reportError(error, context, {
|
|
2276
|
+
type: 'cli',
|
|
2277
|
+
data: context,
|
|
2278
|
+
});
|
|
2279
|
+
},
|
|
2280
|
+
);
|
|
2281
|
+
|
|
2282
|
+
/**
|
|
2283
|
+
* 立即上报(在 CLI 退出前调用)
|
|
2284
|
+
* 等待 500ms 确保设置请求完成和事件发送
|
|
2285
|
+
*/
|
|
2286
|
+
const flushSlardar = safeRun('flushSlardar', async () => {
|
|
2287
|
+
await reporter.flush();
|
|
2288
|
+
});
|
|
2289
|
+
|
|
132
2290
|
function _nullishCoalesce$2(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain$3(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }var LogLevel; (function (LogLevel) {
|
|
133
2291
|
const ERROR = 0; LogLevel[LogLevel["ERROR"] = ERROR] = "ERROR";
|
|
134
2292
|
const WARN = 1; LogLevel[LogLevel["WARN"] = WARN] = "WARN";
|
|
@@ -143,459 +2301,804 @@ function _nullishCoalesce$2(lhs, rhsFn) { if (lhs != null) { return lhs; } else
|
|
|
143
2301
|
|
|
144
2302
|
|
|
145
2303
|
|
|
146
|
-
const LOG_LEVEL_MAP = {
|
|
147
|
-
error: LogLevel.ERROR,
|
|
148
|
-
warn: LogLevel.WARN,
|
|
149
|
-
success: LogLevel.SUCCESS,
|
|
150
|
-
info: LogLevel.INFO,
|
|
151
|
-
verbose: LogLevel.VERBOSE,
|
|
2304
|
+
const LOG_LEVEL_MAP = {
|
|
2305
|
+
error: LogLevel.ERROR,
|
|
2306
|
+
warn: LogLevel.WARN,
|
|
2307
|
+
success: LogLevel.SUCCESS,
|
|
2308
|
+
info: LogLevel.INFO,
|
|
2309
|
+
verbose: LogLevel.VERBOSE,
|
|
2310
|
+
};
|
|
2311
|
+
|
|
2312
|
+
const COLOR_CODES = {
|
|
2313
|
+
reset: '\x1b[0m',
|
|
2314
|
+
red: '\x1b[31m',
|
|
2315
|
+
yellow: '\x1b[33m',
|
|
2316
|
+
green: '\x1b[32m',
|
|
2317
|
+
cyan: '\x1b[36m',
|
|
2318
|
+
gray: '\x1b[90m',
|
|
2319
|
+
};
|
|
2320
|
+
|
|
2321
|
+
class Logger {
|
|
2322
|
+
|
|
2323
|
+
|
|
2324
|
+
|
|
2325
|
+
|
|
2326
|
+
constructor(options = {}) {
|
|
2327
|
+
this.level = this.parseLogLevel(options.level);
|
|
2328
|
+
this.useColor = _nullishCoalesce$2(options.useColor, () => ( this.isColorSupported()));
|
|
2329
|
+
this.prefix = options.prefix;
|
|
2330
|
+
}
|
|
2331
|
+
|
|
2332
|
+
parseLogLevel(level) {
|
|
2333
|
+
if (level !== undefined) {
|
|
2334
|
+
return level;
|
|
2335
|
+
}
|
|
2336
|
+
|
|
2337
|
+
const envLevel = _optionalChain$3([process, 'access', _ => _.env, 'access', _2 => _2.LOG_LEVEL, 'optionalAccess', _3 => _3.toLowerCase, 'call', _4 => _4()]);
|
|
2338
|
+
if (envLevel && envLevel in LOG_LEVEL_MAP) {
|
|
2339
|
+
return LOG_LEVEL_MAP[envLevel];
|
|
2340
|
+
}
|
|
2341
|
+
|
|
2342
|
+
return LogLevel.INFO;
|
|
2343
|
+
}
|
|
2344
|
+
|
|
2345
|
+
isColorSupported() {
|
|
2346
|
+
// 简单检测:Node.js 环境且支持 TTY
|
|
2347
|
+
return (
|
|
2348
|
+
typeof process !== 'undefined' &&
|
|
2349
|
+
_optionalChain$3([process, 'access', _5 => _5.stdout, 'optionalAccess', _6 => _6.isTTY]) === true &&
|
|
2350
|
+
process.env.NO_COLOR === undefined
|
|
2351
|
+
);
|
|
2352
|
+
}
|
|
2353
|
+
|
|
2354
|
+
colorize(text, color) {
|
|
2355
|
+
if (!this.useColor) {
|
|
2356
|
+
return text;
|
|
2357
|
+
}
|
|
2358
|
+
return `${COLOR_CODES[color]}${text}${COLOR_CODES.reset}`;
|
|
2359
|
+
}
|
|
2360
|
+
|
|
2361
|
+
log(options
|
|
2362
|
+
|
|
2363
|
+
|
|
2364
|
+
|
|
2365
|
+
|
|
2366
|
+
|
|
2367
|
+
) {
|
|
2368
|
+
if (options.level > this.level) {
|
|
2369
|
+
return;
|
|
2370
|
+
}
|
|
2371
|
+
|
|
2372
|
+
const icon = this.colorize(options.icon, options.color);
|
|
2373
|
+
const prefix = this.prefix ? `${icon} ${this.prefix}` : icon;
|
|
2374
|
+
console.log(prefix, options.message, ...(_nullishCoalesce$2(options.args, () => ( []))));
|
|
2375
|
+
}
|
|
2376
|
+
|
|
2377
|
+
error(message, ...args) {
|
|
2378
|
+
this.log({
|
|
2379
|
+
level: LogLevel.ERROR,
|
|
2380
|
+
icon: '✖',
|
|
2381
|
+
color: 'red',
|
|
2382
|
+
message,
|
|
2383
|
+
args,
|
|
2384
|
+
});
|
|
2385
|
+
}
|
|
2386
|
+
|
|
2387
|
+
warn(message, ...args) {
|
|
2388
|
+
this.log({
|
|
2389
|
+
level: LogLevel.WARN,
|
|
2390
|
+
icon: '⚠',
|
|
2391
|
+
color: 'yellow',
|
|
2392
|
+
message,
|
|
2393
|
+
args,
|
|
2394
|
+
});
|
|
2395
|
+
}
|
|
2396
|
+
|
|
2397
|
+
success(message, ...args) {
|
|
2398
|
+
this.log({
|
|
2399
|
+
level: LogLevel.SUCCESS,
|
|
2400
|
+
icon: '✓',
|
|
2401
|
+
color: 'green',
|
|
2402
|
+
message,
|
|
2403
|
+
args,
|
|
2404
|
+
});
|
|
2405
|
+
}
|
|
2406
|
+
|
|
2407
|
+
info(message, ...args) {
|
|
2408
|
+
this.log({
|
|
2409
|
+
level: LogLevel.INFO,
|
|
2410
|
+
icon: 'ℹ',
|
|
2411
|
+
color: 'cyan',
|
|
2412
|
+
message,
|
|
2413
|
+
args,
|
|
2414
|
+
});
|
|
2415
|
+
}
|
|
2416
|
+
|
|
2417
|
+
verbose(message, ...args) {
|
|
2418
|
+
this.log({
|
|
2419
|
+
level: LogLevel.VERBOSE,
|
|
2420
|
+
icon: '→',
|
|
2421
|
+
color: 'gray',
|
|
2422
|
+
message,
|
|
2423
|
+
args,
|
|
2424
|
+
});
|
|
2425
|
+
}
|
|
2426
|
+
}
|
|
2427
|
+
|
|
2428
|
+
// 创建 logger 实例的工厂函数
|
|
2429
|
+
const createLogger = (options = {}) =>
|
|
2430
|
+
new Logger(options);
|
|
2431
|
+
|
|
2432
|
+
// 导出默认实例
|
|
2433
|
+
const logger = createLogger();
|
|
2434
|
+
|
|
2435
|
+
/**
|
|
2436
|
+
* 时间统计工具
|
|
2437
|
+
*/
|
|
2438
|
+
class TimeTracker {
|
|
2439
|
+
|
|
2440
|
+
|
|
2441
|
+
|
|
2442
|
+
constructor() {
|
|
2443
|
+
this.startTime = perf_hooks.performance.now();
|
|
2444
|
+
this.lastTime = this.startTime;
|
|
2445
|
+
}
|
|
2446
|
+
|
|
2447
|
+
/**
|
|
2448
|
+
* 记录阶段耗时
|
|
2449
|
+
* @param phaseName 阶段名称
|
|
2450
|
+
*/
|
|
2451
|
+
logPhase(phaseName) {
|
|
2452
|
+
const now = perf_hooks.performance.now();
|
|
2453
|
+
const phaseTime = now - this.lastTime;
|
|
2454
|
+
this.lastTime = now;
|
|
2455
|
+
|
|
2456
|
+
logger.verbose(`⏱ ${phaseName}: ${phaseTime.toFixed(2)}ms`);
|
|
2457
|
+
}
|
|
2458
|
+
|
|
2459
|
+
/**
|
|
2460
|
+
* 记录总耗时
|
|
2461
|
+
*/
|
|
2462
|
+
logTotal() {
|
|
2463
|
+
const totalTime = perf_hooks.performance.now() - this.startTime;
|
|
2464
|
+
logger.verbose(`⏱ Total time: ${totalTime.toFixed(2)}ms`);
|
|
2465
|
+
}
|
|
2466
|
+
|
|
2467
|
+
/**
|
|
2468
|
+
* 获取当前耗时(不输出日志)
|
|
2469
|
+
* @returns 从开始到现在的总耗时(毫秒)
|
|
2470
|
+
*/
|
|
2471
|
+
getElapsedTime() {
|
|
2472
|
+
return perf_hooks.performance.now() - this.startTime;
|
|
2473
|
+
}
|
|
2474
|
+
}
|
|
2475
|
+
|
|
2476
|
+
/**
|
|
2477
|
+
* 获取模板配置文件路径
|
|
2478
|
+
* @returns templates.json 的绝对路径
|
|
2479
|
+
*/
|
|
2480
|
+
const getTemplatesConfigPath = () => {
|
|
2481
|
+
const configPath = path.resolve(getTemplatesDir(), 'templates.json');
|
|
2482
|
+
logger.verbose(`Templates config path: ${configPath}`);
|
|
2483
|
+
logger.verbose(`Config file exists: ${fs.existsSync(configPath)}`);
|
|
2484
|
+
return configPath;
|
|
152
2485
|
};
|
|
153
2486
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
2487
|
+
/**
|
|
2488
|
+
* 获取模板目录路径
|
|
2489
|
+
* @returns __templates__ 目录的绝对路径
|
|
2490
|
+
*/
|
|
2491
|
+
const getTemplatesDir = () => {
|
|
2492
|
+
const templatesDir = path.resolve(__dirname, './__templates__');
|
|
2493
|
+
logger.verbose(`Templates directory: ${templatesDir}`);
|
|
2494
|
+
logger.verbose(`Templates directory exists: ${fs.existsSync(templatesDir)}`);
|
|
2495
|
+
return templatesDir;
|
|
161
2496
|
};
|
|
162
2497
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
2498
|
+
/**
|
|
2499
|
+
* 加载模板配置文件
|
|
2500
|
+
* 支持 .ts 和 .js 文件(通过 sucrase 注册)
|
|
2501
|
+
*
|
|
2502
|
+
* @param templatePath - 模板目录路径
|
|
2503
|
+
* @returns 模板配置对象
|
|
2504
|
+
*/
|
|
167
2505
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
2506
|
+
const loadTemplateConfig = async (
|
|
2507
|
+
templatePath,
|
|
2508
|
+
) => {
|
|
2509
|
+
logger.verbose(`Loading template config from: ${templatePath}`);
|
|
2510
|
+
|
|
2511
|
+
const tsConfigPath = path.join(templatePath, 'template.config.ts');
|
|
2512
|
+
const jsConfigPath = path.join(templatePath, 'template.config.js');
|
|
2513
|
+
|
|
2514
|
+
logger.verbose('Checking for config files:');
|
|
2515
|
+
logger.verbose(` - TypeScript: ${tsConfigPath}`);
|
|
2516
|
+
logger.verbose(` - JavaScript: ${jsConfigPath}`);
|
|
2517
|
+
|
|
2518
|
+
let configPath;
|
|
2519
|
+
|
|
2520
|
+
const [tsExists, jsExists] = await Promise.all([
|
|
2521
|
+
fs$1.access(tsConfigPath).then(
|
|
2522
|
+
() => true,
|
|
2523
|
+
() => false,
|
|
2524
|
+
),
|
|
2525
|
+
fs$1.access(jsConfigPath).then(
|
|
2526
|
+
() => true,
|
|
2527
|
+
() => false,
|
|
2528
|
+
),
|
|
2529
|
+
]);
|
|
2530
|
+
|
|
2531
|
+
logger.verbose('Config file existence check:');
|
|
2532
|
+
logger.verbose(` - template.config.ts: ${tsExists}`);
|
|
2533
|
+
logger.verbose(` - template.config.js: ${jsExists}`);
|
|
2534
|
+
|
|
2535
|
+
if (tsExists) {
|
|
2536
|
+
configPath = tsConfigPath;
|
|
2537
|
+
} else if (jsExists) {
|
|
2538
|
+
configPath = jsConfigPath;
|
|
2539
|
+
} else {
|
|
2540
|
+
throw new Error(
|
|
2541
|
+
`Template config not found in ${templatePath}.\n` +
|
|
2542
|
+
'Expected: template.config.ts or template.config.js',
|
|
2543
|
+
);
|
|
172
2544
|
}
|
|
173
2545
|
|
|
174
|
-
|
|
175
|
-
if (level !== undefined) {
|
|
176
|
-
return level;
|
|
177
|
-
}
|
|
2546
|
+
logger.verbose(`Using config file: ${configPath}`);
|
|
178
2547
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
return LOG_LEVEL_MAP[envLevel];
|
|
182
|
-
}
|
|
2548
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports, security/detect-non-literal-require -- Sucrase handles .ts files at runtime, path is validated above
|
|
2549
|
+
const config = require(configPath);
|
|
183
2550
|
|
|
184
|
-
|
|
2551
|
+
logger.verbose('Template config loaded successfully');
|
|
2552
|
+
|
|
2553
|
+
return config.default || config;
|
|
2554
|
+
};
|
|
2555
|
+
|
|
2556
|
+
/**
|
|
2557
|
+
* 加载模板列表配置
|
|
2558
|
+
*
|
|
2559
|
+
* @param configPath - templates.json 配置文件路径
|
|
2560
|
+
* @returns 模板列表配置
|
|
2561
|
+
*/
|
|
2562
|
+
const loadTemplatesConfig = async (
|
|
2563
|
+
configPath,
|
|
2564
|
+
) => {
|
|
2565
|
+
logger.verbose(`Loading templates config from: ${configPath}`);
|
|
2566
|
+
|
|
2567
|
+
const content = await fs$1.readFile(configPath, 'utf-8');
|
|
2568
|
+
// eslint-disable-next-line no-restricted-syntax -- Static config file loaded at build time, safeJsonParse not needed
|
|
2569
|
+
const config = JSON.parse(content) ;
|
|
2570
|
+
|
|
2571
|
+
logger.verbose(
|
|
2572
|
+
`Found ${config.templates.length} templates: ${config.templates.map(t => t.name).join(', ')}`,
|
|
2573
|
+
);
|
|
2574
|
+
|
|
2575
|
+
return config;
|
|
2576
|
+
};
|
|
2577
|
+
|
|
2578
|
+
/**
|
|
2579
|
+
* 根据模板名称查找模板元信息
|
|
2580
|
+
*
|
|
2581
|
+
* @param templatesConfig - 模板列表配置
|
|
2582
|
+
* @param templateName - 模板名称
|
|
2583
|
+
* @returns 模板元信息
|
|
2584
|
+
*/
|
|
2585
|
+
const findTemplate = (
|
|
2586
|
+
templatesConfig,
|
|
2587
|
+
templateName,
|
|
2588
|
+
) => {
|
|
2589
|
+
const template = templatesConfig.templates.find(t => t.name === templateName);
|
|
2590
|
+
|
|
2591
|
+
if (!template) {
|
|
2592
|
+
const availableTemplates = templatesConfig.templates
|
|
2593
|
+
.map(t => t.name)
|
|
2594
|
+
.join(', ');
|
|
2595
|
+
throw new Error(
|
|
2596
|
+
`Template "${templateName}" not found.\n` +
|
|
2597
|
+
`Available templates: ${availableTemplates}\n` +
|
|
2598
|
+
'Use --template <name> to specify a template.',
|
|
2599
|
+
);
|
|
2600
|
+
}
|
|
2601
|
+
|
|
2602
|
+
return template;
|
|
2603
|
+
};
|
|
2604
|
+
|
|
2605
|
+
/**
|
|
2606
|
+
* 获取模板的完整路径
|
|
2607
|
+
*
|
|
2608
|
+
* @param basePath - 模板目录(templates.json 所在目录)
|
|
2609
|
+
* @param templateMetadata - 模板元信息
|
|
2610
|
+
* @returns 模板完整路径
|
|
2611
|
+
*/
|
|
2612
|
+
const getTemplatePath = async (
|
|
2613
|
+
basePath,
|
|
2614
|
+
templateMetadata,
|
|
2615
|
+
) => {
|
|
2616
|
+
logger.verbose('Resolving template path:');
|
|
2617
|
+
logger.verbose(` - Base path: ${basePath}`);
|
|
2618
|
+
logger.verbose(` - Template location: ${templateMetadata.location}`);
|
|
2619
|
+
|
|
2620
|
+
// location 是相对于 templates.json 文件的路径
|
|
2621
|
+
const templatePath = path.join(basePath, templateMetadata.location);
|
|
2622
|
+
|
|
2623
|
+
logger.verbose(` - Resolved path: ${templatePath}`);
|
|
2624
|
+
|
|
2625
|
+
try {
|
|
2626
|
+
await fs$1.access(templatePath);
|
|
2627
|
+
logger.verbose(' - Template directory exists: ✓');
|
|
2628
|
+
// eslint-disable-next-line @coze-arch/use-error-in-catch -- Error handling done in throw statement
|
|
2629
|
+
} catch (e) {
|
|
2630
|
+
logger.error(' - Template directory does not exist: ✗');
|
|
2631
|
+
throw new Error(`Template directory not found: ${templatePath}`);
|
|
2632
|
+
}
|
|
2633
|
+
|
|
2634
|
+
return templatePath;
|
|
2635
|
+
};
|
|
2636
|
+
|
|
2637
|
+
/**
|
|
2638
|
+
* 对单个模板执行 pnpm install
|
|
2639
|
+
*/
|
|
2640
|
+
const warmupTemplate = (templatePath, templateName) => {
|
|
2641
|
+
logger.info(`\nWarming up template: ${templateName}`);
|
|
2642
|
+
logger.info(` Path: ${templatePath}`);
|
|
2643
|
+
|
|
2644
|
+
// 检查是否存在 package.json
|
|
2645
|
+
const packageJsonPath = node_path.join(templatePath, 'package.json');
|
|
2646
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
|
2647
|
+
if (!node_fs.existsSync(packageJsonPath)) {
|
|
2648
|
+
logger.info(` ⊘ Skipping ${templateName} (no package.json found)`);
|
|
2649
|
+
return;
|
|
2650
|
+
}
|
|
2651
|
+
|
|
2652
|
+
const result = shelljs.exec('pnpm install', {
|
|
2653
|
+
cwd: templatePath,
|
|
2654
|
+
silent: true,
|
|
2655
|
+
});
|
|
2656
|
+
|
|
2657
|
+
// 输出 stdout
|
|
2658
|
+
if (result.stdout) {
|
|
2659
|
+
process.stdout.write(result.stdout);
|
|
185
2660
|
}
|
|
186
2661
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
);
|
|
2662
|
+
// 输出 stderr
|
|
2663
|
+
if (result.stderr) {
|
|
2664
|
+
process.stderr.write(result.stderr);
|
|
2665
|
+
}
|
|
2666
|
+
|
|
2667
|
+
if (result.code === 0) {
|
|
2668
|
+
logger.success(` ✓ ${templateName} warmed up successfully`);
|
|
2669
|
+
} else {
|
|
2670
|
+
const errorMessage = [
|
|
2671
|
+
`pnpm install failed for ${templateName} with exit code ${result.code}`,
|
|
2672
|
+
result.stderr ? `\nStderr:\n${result.stderr}` : '',
|
|
2673
|
+
result.stdout ? `\nStdout:\n${result.stdout}` : '',
|
|
2674
|
+
]
|
|
2675
|
+
.filter(Boolean)
|
|
2676
|
+
.join('');
|
|
2677
|
+
|
|
2678
|
+
throw new Error(errorMessage);
|
|
194
2679
|
}
|
|
2680
|
+
};
|
|
195
2681
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
}
|
|
2682
|
+
/**
|
|
2683
|
+
* 执行 warmup 命令的内部实现
|
|
2684
|
+
*/
|
|
2685
|
+
const executeWarmup = async (options) => {
|
|
2686
|
+
const timer = new TimeTracker();
|
|
202
2687
|
|
|
203
|
-
|
|
2688
|
+
try {
|
|
2689
|
+
const { template: templateFilter } = options;
|
|
204
2690
|
|
|
2691
|
+
logger.info('Starting template warmup...');
|
|
2692
|
+
timer.logPhase('Initialization');
|
|
205
2693
|
|
|
2694
|
+
// 加载模板配置
|
|
2695
|
+
const configPath = getTemplatesConfigPath();
|
|
2696
|
+
const templatesConfig = await loadTemplatesConfig(configPath);
|
|
206
2697
|
|
|
2698
|
+
logger.verbose(
|
|
2699
|
+
`Found ${templatesConfig.templates.length} templates in config`,
|
|
2700
|
+
);
|
|
207
2701
|
|
|
2702
|
+
// 过滤模板
|
|
2703
|
+
const templatesToWarmup = templateFilter
|
|
2704
|
+
? templatesConfig.templates.filter(t => t.name === templateFilter)
|
|
2705
|
+
: templatesConfig.templates;
|
|
208
2706
|
|
|
209
|
-
) {
|
|
210
|
-
|
|
2707
|
+
if (templatesToWarmup.length === 0) {
|
|
2708
|
+
if (templateFilter) {
|
|
2709
|
+
logger.warn(`Template "${templateFilter}" not found`);
|
|
2710
|
+
logger.info(
|
|
2711
|
+
`Available templates: ${templatesConfig.templates.map(t => t.name).join(', ')}`,
|
|
2712
|
+
);
|
|
2713
|
+
} else {
|
|
2714
|
+
logger.warn('No templates found');
|
|
2715
|
+
}
|
|
211
2716
|
return;
|
|
212
2717
|
}
|
|
213
2718
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
}
|
|
2719
|
+
logger.info(
|
|
2720
|
+
`\nWill warm up ${templatesToWarmup.length} template(s): ${templatesToWarmup.map(t => t.name).join(', ')}`,
|
|
2721
|
+
);
|
|
218
2722
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
level: LogLevel.ERROR,
|
|
222
|
-
icon: '✖',
|
|
223
|
-
color: 'red',
|
|
224
|
-
message,
|
|
225
|
-
args,
|
|
226
|
-
});
|
|
227
|
-
}
|
|
2723
|
+
// 获取模板基础路径
|
|
2724
|
+
const basePath = configPath.replace(/\/templates\.json$/, '');
|
|
228
2725
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
args,
|
|
236
|
-
});
|
|
237
|
-
}
|
|
2726
|
+
// 对每个模板执行 pnpm install
|
|
2727
|
+
for (const templateMetadata of templatesToWarmup) {
|
|
2728
|
+
const templatePath = await getTemplatePath(basePath, templateMetadata);
|
|
2729
|
+
warmupTemplate(templatePath, templateMetadata.name);
|
|
2730
|
+
timer.logPhase(`Warmup ${templateMetadata.name}`);
|
|
2731
|
+
}
|
|
238
2732
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
color: 'green',
|
|
244
|
-
message,
|
|
245
|
-
args,
|
|
246
|
-
});
|
|
247
|
-
}
|
|
2733
|
+
logger.success('\n✅ All templates warmed up successfully!');
|
|
2734
|
+
logger.info(
|
|
2735
|
+
'\nNext time you run `coze init`, it will be much faster as node_modules are pre-installed.',
|
|
2736
|
+
);
|
|
248
2737
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
args,
|
|
256
|
-
});
|
|
2738
|
+
timer.logTotal();
|
|
2739
|
+
} catch (error) {
|
|
2740
|
+
timer.logTotal();
|
|
2741
|
+
logger.error('Failed to warmup templates:');
|
|
2742
|
+
logger.error(error instanceof Error ? error.message : String(error));
|
|
2743
|
+
process.exit(1);
|
|
257
2744
|
}
|
|
2745
|
+
};
|
|
258
2746
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
2747
|
+
/**
|
|
2748
|
+
* 注册 warmup 命令到 program
|
|
2749
|
+
*/
|
|
2750
|
+
const registerCommand$4 = program => {
|
|
2751
|
+
program
|
|
2752
|
+
.command('warmup')
|
|
2753
|
+
.description('Pre-install dependencies for templates to speed up init')
|
|
2754
|
+
.option('-t, --template <name>', 'Warmup a specific template only')
|
|
2755
|
+
.action(async options => {
|
|
2756
|
+
await executeWarmup(options);
|
|
266
2757
|
});
|
|
267
|
-
|
|
268
|
-
|
|
2758
|
+
};
|
|
2759
|
+
|
|
2760
|
+
// ABOUTME: This file implements the update command for coze CLI
|
|
2761
|
+
// ABOUTME: It wraps pnpm update/install to update package dependencies with logging support
|
|
269
2762
|
|
|
270
|
-
// 创建 logger 实例的工厂函数
|
|
271
|
-
const createLogger = (options = {}) =>
|
|
272
|
-
new Logger(options);
|
|
273
2763
|
|
|
274
|
-
// 导出默认实例
|
|
275
|
-
const logger = createLogger();
|
|
276
2764
|
|
|
277
2765
|
/**
|
|
278
|
-
*
|
|
2766
|
+
* 日志文件名常量
|
|
279
2767
|
*/
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
constructor() {
|
|
285
|
-
this.startTime = perf_hooks.performance.now();
|
|
286
|
-
this.lastTime = this.startTime;
|
|
287
|
-
}
|
|
2768
|
+
const LOG_FILE_NAME$1 = 'update.log';
|
|
288
2769
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
const phaseTime = now - this.lastTime;
|
|
296
|
-
this.lastTime = now;
|
|
2770
|
+
/**
|
|
2771
|
+
* 获取日志目录
|
|
2772
|
+
* 优先使用环境变量 COZE_LOG_DIR,否则使用 ~/.coze-logs
|
|
2773
|
+
*/
|
|
2774
|
+
const getLogDir$1 = () =>
|
|
2775
|
+
process.env.COZE_LOG_DIR || path.join(os.homedir(), '.coze-logs');
|
|
297
2776
|
|
|
298
|
-
|
|
2777
|
+
/**
|
|
2778
|
+
* 解析日志文件路径
|
|
2779
|
+
* - 如果是绝对路径,直接使用
|
|
2780
|
+
* - 如果是相对路径,基于 getLogDir() + 相对路径
|
|
2781
|
+
* - 如果为空,使用 getLogDir() + LOG_FILE_NAME
|
|
2782
|
+
*/
|
|
2783
|
+
const resolveLogFilePath$1 = (logFile) => {
|
|
2784
|
+
if (!logFile) {
|
|
2785
|
+
return path.join(getLogDir$1(), LOG_FILE_NAME$1);
|
|
299
2786
|
}
|
|
300
2787
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
*/
|
|
304
|
-
logTotal() {
|
|
305
|
-
const totalTime = perf_hooks.performance.now() - this.startTime;
|
|
306
|
-
logger.verbose(`⏱ Total time: ${totalTime.toFixed(2)}ms`);
|
|
2788
|
+
if (path.isAbsolute(logFile)) {
|
|
2789
|
+
return logFile;
|
|
307
2790
|
}
|
|
308
2791
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
2792
|
+
return path.join(getLogDir$1(), logFile);
|
|
2793
|
+
};
|
|
2794
|
+
|
|
2795
|
+
/**
|
|
2796
|
+
* 创建日志写入流
|
|
2797
|
+
*/
|
|
2798
|
+
const createLogStream$1 = (logFilePath) => {
|
|
2799
|
+
const logDir = path.dirname(logFilePath);
|
|
2800
|
+
|
|
2801
|
+
// 确保日志目录存在
|
|
2802
|
+
if (!fs.existsSync(logDir)) {
|
|
2803
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
315
2804
|
}
|
|
316
|
-
|
|
2805
|
+
|
|
2806
|
+
// 使用 'w' 标志覆盖之前的日志
|
|
2807
|
+
return fs.createWriteStream(logFilePath, { flags: 'w' });
|
|
2808
|
+
};
|
|
317
2809
|
|
|
318
2810
|
/**
|
|
319
|
-
*
|
|
320
|
-
* @returns templates.json 的绝对路径
|
|
2811
|
+
* 格式化时间戳
|
|
321
2812
|
*/
|
|
322
|
-
const
|
|
323
|
-
const
|
|
324
|
-
|
|
325
|
-
logger.verbose(`Config file exists: ${fs.existsSync(configPath)}`);
|
|
326
|
-
return configPath;
|
|
2813
|
+
const formatTimestamp = () => {
|
|
2814
|
+
const now = new Date();
|
|
2815
|
+
return now.toISOString();
|
|
327
2816
|
};
|
|
328
2817
|
|
|
329
2818
|
/**
|
|
330
|
-
*
|
|
331
|
-
* @returns __templates__ 目录的绝对路径
|
|
2819
|
+
* 写入带时间戳的日志
|
|
332
2820
|
*/
|
|
333
|
-
const
|
|
334
|
-
const
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
2821
|
+
const writeLogWithTimestamp = (stream, message) => {
|
|
2822
|
+
const timestamp = formatTimestamp();
|
|
2823
|
+
const lines = message.split('\n');
|
|
2824
|
+
lines.forEach(line => {
|
|
2825
|
+
if (line) {
|
|
2826
|
+
stream.write(`[${timestamp}] ${line}\n`);
|
|
2827
|
+
} else {
|
|
2828
|
+
stream.write('\n');
|
|
2829
|
+
}
|
|
2830
|
+
});
|
|
2831
|
+
// 确保数据写入磁盘
|
|
2832
|
+
stream.uncork();
|
|
2833
|
+
};
|
|
2834
|
+
|
|
2835
|
+
/**
|
|
2836
|
+
* 同时输出到控制台和日志文件
|
|
2837
|
+
*/
|
|
2838
|
+
const logWithFile = (
|
|
2839
|
+
stream,
|
|
2840
|
+
level,
|
|
2841
|
+
message,
|
|
2842
|
+
) => {
|
|
2843
|
+
// 输出到控制台
|
|
2844
|
+
switch (level) {
|
|
2845
|
+
case 'info':
|
|
2846
|
+
logger.info(message);
|
|
2847
|
+
break;
|
|
2848
|
+
case 'success':
|
|
2849
|
+
logger.success(message);
|
|
2850
|
+
break;
|
|
2851
|
+
case 'error':
|
|
2852
|
+
logger.error(message);
|
|
2853
|
+
break;
|
|
2854
|
+
default:
|
|
2855
|
+
logger.info(message);
|
|
2856
|
+
break;
|
|
2857
|
+
}
|
|
2858
|
+
|
|
2859
|
+
// 写入日志文件(带时间戳)
|
|
2860
|
+
writeLogWithTimestamp(stream, `[${level.toUpperCase()}] ${message}`);
|
|
338
2861
|
};
|
|
339
2862
|
|
|
2863
|
+
// start_aigc
|
|
340
2864
|
/**
|
|
341
|
-
*
|
|
342
|
-
* 支持 .ts 和 .js 文件(通过 sucrase 注册)
|
|
343
|
-
*
|
|
344
|
-
* @param templatePath - 模板目录路径
|
|
345
|
-
* @returns 模板配置对象
|
|
2865
|
+
* 构建 pnpm add 命令
|
|
346
2866
|
*/
|
|
2867
|
+
const buildPnpmCommand = (
|
|
2868
|
+
packageName,
|
|
2869
|
+
options
|
|
347
2870
|
|
|
348
|
-
const loadTemplateConfig = async (
|
|
349
|
-
templatePath,
|
|
350
|
-
) => {
|
|
351
|
-
logger.verbose(`Loading template config from: ${templatePath}`);
|
|
352
2871
|
|
|
353
|
-
const tsConfigPath = path.join(templatePath, 'template.config.ts');
|
|
354
|
-
const jsConfigPath = path.join(templatePath, 'template.config.js');
|
|
355
2872
|
|
|
356
|
-
logger.verbose('Checking for config files:');
|
|
357
|
-
logger.verbose(` - TypeScript: ${tsConfigPath}`);
|
|
358
|
-
logger.verbose(` - JavaScript: ${jsConfigPath}`);
|
|
359
2873
|
|
|
360
|
-
|
|
2874
|
+
,
|
|
2875
|
+
) => {
|
|
2876
|
+
const { global, version, registry, extraArgs } = options;
|
|
361
2877
|
|
|
362
|
-
const [
|
|
363
|
-
fs$1.access(tsConfigPath).then(
|
|
364
|
-
() => true,
|
|
365
|
-
() => false,
|
|
366
|
-
),
|
|
367
|
-
fs$1.access(jsConfigPath).then(
|
|
368
|
-
() => true,
|
|
369
|
-
() => false,
|
|
370
|
-
),
|
|
371
|
-
]);
|
|
2878
|
+
const parts = ['pnpm', 'add'];
|
|
372
2879
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
2880
|
+
// 添加全局标记
|
|
2881
|
+
if (global) {
|
|
2882
|
+
parts.push('-g');
|
|
2883
|
+
}
|
|
376
2884
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
configPath = jsConfigPath;
|
|
2885
|
+
// 添加包名和版本
|
|
2886
|
+
if (version && version !== 'latest') {
|
|
2887
|
+
parts.push(`${packageName}@${version}`);
|
|
381
2888
|
} else {
|
|
382
|
-
|
|
383
|
-
`Template config not found in ${templatePath}.\n` +
|
|
384
|
-
'Expected: template.config.ts or template.config.js',
|
|
385
|
-
);
|
|
2889
|
+
parts.push(`${packageName}@latest`);
|
|
386
2890
|
}
|
|
387
2891
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
logger.verbose('Template config loaded successfully');
|
|
394
|
-
|
|
395
|
-
return config.default || config;
|
|
396
|
-
};
|
|
397
|
-
|
|
398
|
-
/**
|
|
399
|
-
* 加载模板列表配置
|
|
400
|
-
*
|
|
401
|
-
* @param configPath - templates.json 配置文件路径
|
|
402
|
-
* @returns 模板列表配置
|
|
403
|
-
*/
|
|
404
|
-
const loadTemplatesConfig = async (
|
|
405
|
-
configPath,
|
|
406
|
-
) => {
|
|
407
|
-
logger.verbose(`Loading templates config from: ${configPath}`);
|
|
408
|
-
|
|
409
|
-
const content = await fs$1.readFile(configPath, 'utf-8');
|
|
410
|
-
// eslint-disable-next-line no-restricted-syntax -- Static config file loaded at build time, safeJsonParse not needed
|
|
411
|
-
const config = JSON.parse(content) ;
|
|
2892
|
+
// 添加 registry
|
|
2893
|
+
if (registry) {
|
|
2894
|
+
parts.push(`--registry=${registry}`);
|
|
2895
|
+
}
|
|
412
2896
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
2897
|
+
// 添加额外参数
|
|
2898
|
+
if (extraArgs.length > 0) {
|
|
2899
|
+
parts.push(...extraArgs);
|
|
2900
|
+
}
|
|
416
2901
|
|
|
417
|
-
return
|
|
2902
|
+
return parts.join(' ');
|
|
418
2903
|
};
|
|
2904
|
+
// end_aigc
|
|
419
2905
|
|
|
420
2906
|
/**
|
|
421
|
-
*
|
|
422
|
-
*
|
|
423
|
-
* @param templatesConfig - 模板列表配置
|
|
424
|
-
* @param templateName - 模板名称
|
|
425
|
-
* @returns 模板元信息
|
|
2907
|
+
* 处理更新失败的错误
|
|
426
2908
|
*/
|
|
427
|
-
const
|
|
428
|
-
|
|
429
|
-
|
|
2909
|
+
const handleUpdateError = (
|
|
2910
|
+
error,
|
|
2911
|
+
packageName,
|
|
2912
|
+
options,
|
|
2913
|
+
cmdStartTime,
|
|
2914
|
+
logStream,
|
|
430
2915
|
) => {
|
|
431
|
-
const
|
|
2916
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
2917
|
+
logger.error('Failed to update package:');
|
|
2918
|
+
logger.error(err.message);
|
|
2919
|
+
|
|
2920
|
+
// 上报错误
|
|
2921
|
+
reportError(err, {
|
|
2922
|
+
command: 'update',
|
|
2923
|
+
packageName,
|
|
2924
|
+
type: 'execution_error',
|
|
2925
|
+
});
|
|
2926
|
+
reportCommandComplete('update', false, Date.now() - cmdStartTime, {
|
|
2927
|
+
args: JSON.stringify({ packageName, ...options }),
|
|
2928
|
+
errorCode: 1,
|
|
2929
|
+
errorMessage: err.message,
|
|
2930
|
+
});
|
|
432
2931
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
);
|
|
2932
|
+
// 写入错误到日志文件
|
|
2933
|
+
if (logStream) {
|
|
2934
|
+
writeLogWithTimestamp(logStream, `[ERROR] ${err.message}`);
|
|
2935
|
+
// 等待流关闭后再退出
|
|
2936
|
+
logStream.end(() => {
|
|
2937
|
+
flushSlardar().then(() => {
|
|
2938
|
+
process.exit(1);
|
|
2939
|
+
});
|
|
2940
|
+
});
|
|
2941
|
+
} else {
|
|
2942
|
+
flushSlardar().then(() => {
|
|
2943
|
+
process.exit(1);
|
|
2944
|
+
});
|
|
442
2945
|
}
|
|
443
|
-
|
|
444
|
-
return template;
|
|
445
2946
|
};
|
|
446
2947
|
|
|
2948
|
+
// start_aigc
|
|
447
2949
|
/**
|
|
448
|
-
*
|
|
449
|
-
*
|
|
450
|
-
* @param basePath - 模板目录(templates.json 所在目录)
|
|
451
|
-
* @param templateMetadata - 模板元信息
|
|
452
|
-
* @returns 模板完整路径
|
|
2950
|
+
* 执行 update 命令的内部实现
|
|
453
2951
|
*/
|
|
454
|
-
const
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
) => {
|
|
458
|
-
logger.verbose('Resolving template path:');
|
|
459
|
-
logger.verbose(` - Base path: ${basePath}`);
|
|
460
|
-
logger.verbose(` - Template location: ${templateMetadata.location}`);
|
|
2952
|
+
const executeUpdate = (
|
|
2953
|
+
packageName,
|
|
2954
|
+
options
|
|
461
2955
|
|
|
462
|
-
// location 是相对于 templates.json 文件的路径
|
|
463
|
-
const templatePath = path.join(basePath, templateMetadata.location);
|
|
464
2956
|
|
|
465
|
-
logger.verbose(` - Resolved path: ${templatePath}`);
|
|
466
2957
|
|
|
467
|
-
try {
|
|
468
|
-
await fs$1.access(templatePath);
|
|
469
|
-
logger.verbose(' - Template directory exists: ✓');
|
|
470
|
-
// eslint-disable-next-line @coze-arch/use-error-in-catch -- Error handling done in throw statement
|
|
471
|
-
} catch (e) {
|
|
472
|
-
logger.error(' - Template directory does not exist: ✗');
|
|
473
|
-
throw new Error(`Template directory not found: ${templatePath}`);
|
|
474
|
-
}
|
|
475
2958
|
|
|
476
|
-
return templatePath;
|
|
477
|
-
};
|
|
478
2959
|
|
|
479
|
-
/**
|
|
480
|
-
* 对单个模板执行 pnpm install
|
|
481
|
-
*/
|
|
482
|
-
const warmupTemplate = (templatePath, templateName) => {
|
|
483
|
-
logger.info(`\nWarming up template: ${templateName}`);
|
|
484
|
-
logger.info(` Path: ${templatePath}`);
|
|
485
2960
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
logger.info(` ⊘ Skipping ${templateName} (no package.json found)`);
|
|
491
|
-
return;
|
|
492
|
-
}
|
|
2961
|
+
,
|
|
2962
|
+
) => {
|
|
2963
|
+
const cmdStartTime = Date.now();
|
|
2964
|
+
let logStream = null;
|
|
493
2965
|
|
|
494
|
-
|
|
495
|
-
cwd
|
|
496
|
-
silent: true,
|
|
497
|
-
});
|
|
2966
|
+
try {
|
|
2967
|
+
const { global, cwd, version, registry, logFile, extraArgs } = options;
|
|
498
2968
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
process.stdout.write(result.stdout);
|
|
502
|
-
}
|
|
2969
|
+
// 上报命令开始
|
|
2970
|
+
reportCommandStart('update', JSON.stringify({ packageName, ...options }));
|
|
503
2971
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
process.stderr.write(result.stderr);
|
|
507
|
-
}
|
|
2972
|
+
// 准备日志
|
|
2973
|
+
const logFilePath = resolveLogFilePath$1(logFile);
|
|
508
2974
|
|
|
509
|
-
|
|
510
|
-
logger.
|
|
511
|
-
} else {
|
|
512
|
-
const errorMessage = [
|
|
513
|
-
`pnpm install failed for ${templateName} with exit code ${result.code}`,
|
|
514
|
-
result.stderr ? `\nStderr:\n${result.stderr}` : '',
|
|
515
|
-
result.stdout ? `\nStdout:\n${result.stdout}` : '',
|
|
516
|
-
]
|
|
517
|
-
.filter(Boolean)
|
|
518
|
-
.join('');
|
|
2975
|
+
// 调试:确认日志路径
|
|
2976
|
+
logger.info(`Log file path resolved to: ${logFilePath}`);
|
|
519
2977
|
|
|
520
|
-
|
|
521
|
-
}
|
|
522
|
-
};
|
|
2978
|
+
logStream = createLogStream$1(logFilePath);
|
|
523
2979
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
*/
|
|
527
|
-
const executeWarmup = async (options) => {
|
|
528
|
-
const timer = new TimeTracker();
|
|
2980
|
+
// 调试:确认流已创建
|
|
2981
|
+
logger.info('Log stream created successfully');
|
|
529
2982
|
|
|
530
|
-
|
|
531
|
-
const { template: templateFilter } = options;
|
|
2983
|
+
logWithFile(logStream, 'info', `Updating package: ${packageName}`);
|
|
532
2984
|
|
|
533
|
-
|
|
534
|
-
|
|
2985
|
+
// 构建命令
|
|
2986
|
+
const command = buildPnpmCommand(packageName, {
|
|
2987
|
+
global,
|
|
2988
|
+
version,
|
|
2989
|
+
registry,
|
|
2990
|
+
extraArgs,
|
|
2991
|
+
});
|
|
535
2992
|
|
|
536
|
-
//
|
|
537
|
-
const
|
|
538
|
-
|
|
2993
|
+
// 确定工作目录
|
|
2994
|
+
const workingDir = cwd
|
|
2995
|
+
? path.isAbsolute(cwd)
|
|
2996
|
+
? cwd
|
|
2997
|
+
: path.join(process.cwd(), cwd)
|
|
2998
|
+
: process.cwd();
|
|
539
2999
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
);
|
|
3000
|
+
logWithFile(logStream, 'info', `Executing: ${command}`);
|
|
3001
|
+
logWithFile(logStream, 'info', `Working directory: ${workingDir}`);
|
|
3002
|
+
logWithFile(logStream, 'info', `Log file: ${logFilePath}`);
|
|
543
3003
|
|
|
544
|
-
//
|
|
545
|
-
|
|
546
|
-
? templatesConfig.templates.filter(t => t.name === templateFilter)
|
|
547
|
-
: templatesConfig.templates;
|
|
3004
|
+
// 记录命令开始时间
|
|
3005
|
+
writeLogWithTimestamp(logStream, '--- Command execution started ---');
|
|
548
3006
|
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
3007
|
+
// 同步执行命令
|
|
3008
|
+
const result = shelljs.exec(command, {
|
|
3009
|
+
cwd: workingDir,
|
|
3010
|
+
silent: true, // 使用 silent 来捕获输出
|
|
3011
|
+
});
|
|
3012
|
+
|
|
3013
|
+
// 将输出写入控制台和日志文件(带时间戳)
|
|
3014
|
+
if (result.stdout) {
|
|
3015
|
+
process.stdout.write(result.stdout);
|
|
3016
|
+
writeLogWithTimestamp(logStream, result.stdout.trim());
|
|
559
3017
|
}
|
|
560
3018
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
3019
|
+
if (result.stderr) {
|
|
3020
|
+
process.stderr.write(result.stderr);
|
|
3021
|
+
writeLogWithTimestamp(logStream, result.stderr.trim());
|
|
3022
|
+
}
|
|
564
3023
|
|
|
565
|
-
//
|
|
566
|
-
|
|
3024
|
+
// 记录命令结束时间
|
|
3025
|
+
writeLogWithTimestamp(logStream, '--- Command execution ended ---');
|
|
567
3026
|
|
|
568
|
-
//
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
timer.logPhase(`Warmup ${templateMetadata.name}`);
|
|
573
|
-
}
|
|
3027
|
+
// 检查执行结果并记录到日志
|
|
3028
|
+
if (result.code === 0) {
|
|
3029
|
+
logWithFile(logStream, 'success', 'Package updated successfully');
|
|
3030
|
+
logWithFile(logStream, 'info', `Log file: ${logFilePath}`);
|
|
574
3031
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
3032
|
+
// 上报命令成功
|
|
3033
|
+
reportCommandComplete('update', true, Date.now() - cmdStartTime, {
|
|
3034
|
+
args: JSON.stringify({ packageName, ...options }),
|
|
3035
|
+
});
|
|
3036
|
+
// flush 由 main 函数统一处理
|
|
3037
|
+
} else {
|
|
3038
|
+
const errorMessage = `Command exited with code ${result.code}`;
|
|
3039
|
+
logWithFile(logStream, 'error', errorMessage);
|
|
3040
|
+
logWithFile(
|
|
3041
|
+
logStream,
|
|
3042
|
+
'error',
|
|
3043
|
+
`Check log file for details: ${logFilePath}`,
|
|
3044
|
+
);
|
|
579
3045
|
|
|
580
|
-
|
|
3046
|
+
// 上报命令失败
|
|
3047
|
+
reportError(new Error(errorMessage), {
|
|
3048
|
+
command: 'update',
|
|
3049
|
+
packageName,
|
|
3050
|
+
exitCode: String(result.code),
|
|
3051
|
+
});
|
|
3052
|
+
reportCommandComplete('update', false, Date.now() - cmdStartTime, {
|
|
3053
|
+
args: JSON.stringify({ packageName, ...options }),
|
|
3054
|
+
errorCode: result.code || 1,
|
|
3055
|
+
errorMessage,
|
|
3056
|
+
});
|
|
3057
|
+
}
|
|
3058
|
+
|
|
3059
|
+
// 关闭日志流并等待写入完成
|
|
3060
|
+
logStream.end(() => {
|
|
3061
|
+
// 流关闭后再退出进程
|
|
3062
|
+
if (result.code !== 0) {
|
|
3063
|
+
// flush 后再退出
|
|
3064
|
+
flushSlardar().then(() => {
|
|
3065
|
+
process.exit(result.code || 1);
|
|
3066
|
+
});
|
|
3067
|
+
}
|
|
3068
|
+
// 成功时 flush 由 main 函数统一处理
|
|
3069
|
+
});
|
|
581
3070
|
} catch (error) {
|
|
582
|
-
|
|
583
|
-
logger.error('Failed to warmup templates:');
|
|
584
|
-
logger.error(error instanceof Error ? error.message : String(error));
|
|
585
|
-
process.exit(1);
|
|
3071
|
+
handleUpdateError(error, packageName, options, cmdStartTime, logStream);
|
|
586
3072
|
}
|
|
587
3073
|
};
|
|
3074
|
+
// end_aigc
|
|
588
3075
|
|
|
589
3076
|
/**
|
|
590
|
-
* 注册
|
|
3077
|
+
* 注册 update 命令到 program
|
|
591
3078
|
*/
|
|
592
|
-
const registerCommand$
|
|
3079
|
+
const registerCommand$3 = program => {
|
|
593
3080
|
program
|
|
594
|
-
.command('
|
|
595
|
-
.description('
|
|
596
|
-
.option('-
|
|
597
|
-
.
|
|
598
|
-
|
|
3081
|
+
.command('update <package>')
|
|
3082
|
+
.description('Update a package dependency')
|
|
3083
|
+
.option('-g, --global', 'Update package globally', false)
|
|
3084
|
+
.option('-c, --cwd <path>', 'Working directory for the update')
|
|
3085
|
+
.option(
|
|
3086
|
+
'--to <version>',
|
|
3087
|
+
'Version to update to (default: latest)',
|
|
3088
|
+
'latest',
|
|
3089
|
+
)
|
|
3090
|
+
.option('--registry <url>', 'Registry URL to use for the update')
|
|
3091
|
+
.option('--log-file <path>', 'Log file path')
|
|
3092
|
+
.allowUnknownOption() // 允许透传参数给 pnpm
|
|
3093
|
+
.action((packageName, options, command) => {
|
|
3094
|
+
// 收集所有未知选项作为额外参数
|
|
3095
|
+
const extraArgs = command.args.slice(1);
|
|
3096
|
+
|
|
3097
|
+
executeUpdate(packageName, {
|
|
3098
|
+
...options,
|
|
3099
|
+
version: options.to, // 将 --to 映射到 version
|
|
3100
|
+
extraArgs,
|
|
3101
|
+
});
|
|
599
3102
|
});
|
|
600
3103
|
};
|
|
601
3104
|
|
|
@@ -979,7 +3482,9 @@ const rules = [
|
|
|
979
3482
|
/**
|
|
980
3483
|
* 执行 fix 命令的内部实现
|
|
981
3484
|
*/
|
|
982
|
-
const executeFix = async (
|
|
3485
|
+
const executeFix = async (
|
|
3486
|
+
options = {},
|
|
3487
|
+
) => {
|
|
983
3488
|
try {
|
|
984
3489
|
const cwd = process.cwd();
|
|
985
3490
|
const projectFolder = options.directory
|
|
@@ -1037,7 +3542,7 @@ const executeFix = async (options = {}) => {
|
|
|
1037
3542
|
/**
|
|
1038
3543
|
* 注册 fix 命令到 program
|
|
1039
3544
|
*/
|
|
1040
|
-
const registerCommand$
|
|
3545
|
+
const registerCommand$2 = program => {
|
|
1041
3546
|
program
|
|
1042
3547
|
.command('fix')
|
|
1043
3548
|
.description(
|
|
@@ -1056,13 +3561,13 @@ function _nullishCoalesce$1(lhs, rhsFn) { if (lhs != null) { return lhs; } else
|
|
|
1056
3561
|
/**
|
|
1057
3562
|
* 日志文件名常量
|
|
1058
3563
|
*/
|
|
1059
|
-
const LOG_FILE_NAME
|
|
3564
|
+
const LOG_FILE_NAME = 'dev.log';
|
|
1060
3565
|
|
|
1061
3566
|
/**
|
|
1062
3567
|
* 获取日志目录
|
|
1063
3568
|
* 优先使用环境变量 COZE_LOG_DIR,否则使用 ~/.coze-logs
|
|
1064
3569
|
*/
|
|
1065
|
-
const getLogDir
|
|
3570
|
+
const getLogDir = () =>
|
|
1066
3571
|
process.env.COZE_LOG_DIR || path.join(os.homedir(), '.coze-logs');
|
|
1067
3572
|
|
|
1068
3573
|
/**
|
|
@@ -1071,22 +3576,22 @@ const getLogDir$1 = () =>
|
|
|
1071
3576
|
* - 如果是相对路径,基于 getLogDir() + 相对路径
|
|
1072
3577
|
* - 如果为空,使用 getLogDir() + LOG_FILE_NAME
|
|
1073
3578
|
*/
|
|
1074
|
-
const resolveLogFilePath
|
|
3579
|
+
const resolveLogFilePath = (logFile) => {
|
|
1075
3580
|
if (!logFile) {
|
|
1076
|
-
return path.join(getLogDir
|
|
3581
|
+
return path.join(getLogDir(), LOG_FILE_NAME);
|
|
1077
3582
|
}
|
|
1078
3583
|
|
|
1079
3584
|
if (path.isAbsolute(logFile)) {
|
|
1080
3585
|
return logFile;
|
|
1081
3586
|
}
|
|
1082
3587
|
|
|
1083
|
-
return path.join(getLogDir
|
|
3588
|
+
return path.join(getLogDir(), logFile);
|
|
1084
3589
|
};
|
|
1085
3590
|
|
|
1086
3591
|
/**
|
|
1087
3592
|
* 创建日志写入流
|
|
1088
3593
|
*/
|
|
1089
|
-
const createLogStream
|
|
3594
|
+
const createLogStream = (logFilePath) => {
|
|
1090
3595
|
const logDir = path.dirname(logFilePath);
|
|
1091
3596
|
|
|
1092
3597
|
// 确保日志目录存在
|
|
@@ -1105,9 +3610,14 @@ const executeRun = async (
|
|
|
1105
3610
|
commandName,
|
|
1106
3611
|
options = {},
|
|
1107
3612
|
) => {
|
|
3613
|
+
const cmdStartTime = Date.now();
|
|
3614
|
+
|
|
1108
3615
|
try {
|
|
1109
3616
|
logger.info(`Running ${commandName} command...`);
|
|
1110
3617
|
|
|
3618
|
+
// 上报命令开始
|
|
3619
|
+
reportCommandStart(commandName, JSON.stringify(options));
|
|
3620
|
+
|
|
1111
3621
|
// 1. 对于 build 命令,先执行 fix 以确保项目配置正确
|
|
1112
3622
|
if (['dev', 'build'].includes(commandName)) {
|
|
1113
3623
|
logger.info('\n🔧 Running fix command before build...\n');
|
|
@@ -1125,8 +3635,8 @@ const executeRun = async (
|
|
|
1125
3635
|
const commandArgs = getCommandConfig(config, commandName);
|
|
1126
3636
|
|
|
1127
3637
|
// 3. 准备日志
|
|
1128
|
-
const logFilePath = resolveLogFilePath
|
|
1129
|
-
const logStream = createLogStream
|
|
3638
|
+
const logFilePath = resolveLogFilePath(options.logFile);
|
|
3639
|
+
const logStream = createLogStream(logFilePath);
|
|
1130
3640
|
|
|
1131
3641
|
// 4. 执行命令
|
|
1132
3642
|
const commandString = commandArgs.join(' ');
|
|
@@ -1159,14 +3669,34 @@ const executeRun = async (
|
|
|
1159
3669
|
logStream.end();
|
|
1160
3670
|
|
|
1161
3671
|
if (code !== 0) {
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
);
|
|
3672
|
+
const errorMessage = `Command exited with code ${_nullishCoalesce$1(code, () => ( 'unknown'))}${signal ? ` and signal ${signal}` : ''}`;
|
|
3673
|
+
logger.error(errorMessage);
|
|
1165
3674
|
logger.error(`Check log file for details: ${logFilePath}`);
|
|
1166
|
-
|
|
3675
|
+
|
|
3676
|
+
// 上报命令失败
|
|
3677
|
+
reportError(new Error(errorMessage), {
|
|
3678
|
+
command: commandName,
|
|
3679
|
+
exitCode: String(_nullishCoalesce$1(code, () => ( 'unknown'))),
|
|
3680
|
+
signal: _nullishCoalesce$1(signal, () => ( 'none')),
|
|
3681
|
+
logFile: logFilePath,
|
|
3682
|
+
});
|
|
3683
|
+
reportCommandComplete(commandName, false, Date.now() - cmdStartTime, {
|
|
3684
|
+
args: JSON.stringify(options),
|
|
3685
|
+
errorCode: _nullishCoalesce$1(code, () => ( 1)),
|
|
3686
|
+
errorMessage,
|
|
3687
|
+
});
|
|
3688
|
+
flushSlardar().then(() => {
|
|
3689
|
+
process.exit(code || 1);
|
|
3690
|
+
});
|
|
1167
3691
|
} else {
|
|
1168
3692
|
logger.success('Command completed successfully');
|
|
1169
3693
|
logger.info(`Log file: ${logFilePath}`);
|
|
3694
|
+
|
|
3695
|
+
// 上报命令成功
|
|
3696
|
+
reportCommandComplete(commandName, true, Date.now() - cmdStartTime, {
|
|
3697
|
+
args: JSON.stringify(options),
|
|
3698
|
+
});
|
|
3699
|
+
// flush 由 main 函数统一处理
|
|
1170
3700
|
}
|
|
1171
3701
|
});
|
|
1172
3702
|
|
|
@@ -1177,19 +3707,46 @@ const executeRun = async (
|
|
|
1177
3707
|
logger.error(`Stack trace:\n${error.stack}`);
|
|
1178
3708
|
}
|
|
1179
3709
|
logStream.end();
|
|
1180
|
-
|
|
3710
|
+
|
|
3711
|
+
// 上报错误
|
|
3712
|
+
reportError(error, {
|
|
3713
|
+
command: commandName,
|
|
3714
|
+
type: 'child_process_error',
|
|
3715
|
+
});
|
|
3716
|
+
reportCommandComplete(commandName, false, Date.now() - cmdStartTime, {
|
|
3717
|
+
args: JSON.stringify(options),
|
|
3718
|
+
errorCode: 1,
|
|
3719
|
+
errorMessage: error.message,
|
|
3720
|
+
});
|
|
3721
|
+
flushSlardar().then(() => {
|
|
3722
|
+
process.exit(1);
|
|
3723
|
+
});
|
|
1181
3724
|
});
|
|
1182
3725
|
} catch (error) {
|
|
3726
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
1183
3727
|
logger.error(`Failed to run ${commandName} command:`);
|
|
1184
|
-
logger.error(
|
|
1185
|
-
|
|
3728
|
+
logger.error(err.message);
|
|
3729
|
+
|
|
3730
|
+
// 上报错误
|
|
3731
|
+
reportError(err, {
|
|
3732
|
+
command: commandName,
|
|
3733
|
+
type: 'execution_error',
|
|
3734
|
+
});
|
|
3735
|
+
reportCommandComplete(commandName, false, Date.now() - cmdStartTime, {
|
|
3736
|
+
args: JSON.stringify(options),
|
|
3737
|
+
errorCode: 1,
|
|
3738
|
+
errorMessage: err.message,
|
|
3739
|
+
});
|
|
3740
|
+
flushSlardar().then(() => {
|
|
3741
|
+
process.exit(1);
|
|
3742
|
+
});
|
|
1186
3743
|
}
|
|
1187
3744
|
};
|
|
1188
3745
|
|
|
1189
3746
|
/**
|
|
1190
3747
|
* 注册 dev/build/start 命令到 program
|
|
1191
3748
|
*/
|
|
1192
|
-
const registerCommand$
|
|
3749
|
+
const registerCommand$1 = program => {
|
|
1193
3750
|
// dev 命令
|
|
1194
3751
|
program
|
|
1195
3752
|
.command('dev')
|
|
@@ -1521,12 +4078,6 @@ const convertDotfileName = (filePath) => {
|
|
|
1521
4078
|
// ABOUTME: Handles file content rendering, hook execution, and file writing
|
|
1522
4079
|
|
|
1523
4080
|
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
4081
|
// start_aigc
|
|
1531
4082
|
/**
|
|
1532
4083
|
* 执行文件渲染钩子
|
|
@@ -1677,11 +4228,6 @@ const writeRenderedFile = async (options
|
|
|
1677
4228
|
// ABOUTME: Provides dry-run file collection and conflict checking
|
|
1678
4229
|
|
|
1679
4230
|
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
4231
|
// start_aigc
|
|
1686
4232
|
/**
|
|
1687
4233
|
* 收集所有将要写入的文件路径(dry-run 阶段)
|
|
@@ -1722,13 +4268,44 @@ const collectFilesToRender = async (options
|
|
|
1722
4268
|
|
|
1723
4269
|
return filesToWrite;
|
|
1724
4270
|
};
|
|
4271
|
+
|
|
4272
|
+
/**
|
|
4273
|
+
* 检测文件冲突
|
|
4274
|
+
*
|
|
4275
|
+
* @param outputPath - 输出目录路径
|
|
4276
|
+
* @param filesToWrite - 将要写入的文件路径列表
|
|
4277
|
+
* @returns 冲突的文件路径列表
|
|
4278
|
+
*/
|
|
4279
|
+
const detectFileConflicts = (
|
|
4280
|
+
outputPath,
|
|
4281
|
+
filesToWrite,
|
|
4282
|
+
) => {
|
|
4283
|
+
logger.verbose('\nChecking for file conflicts...');
|
|
4284
|
+
|
|
4285
|
+
const conflicts = [];
|
|
4286
|
+
|
|
4287
|
+
for (const file of filesToWrite) {
|
|
4288
|
+
const fullPath = path.join(outputPath, file);
|
|
4289
|
+
if (fs.existsSync(fullPath)) {
|
|
4290
|
+
conflicts.push(file);
|
|
4291
|
+
logger.verbose(` ⚠ Conflict detected: ${file}`);
|
|
4292
|
+
}
|
|
4293
|
+
}
|
|
4294
|
+
|
|
4295
|
+
if (conflicts.length === 0) {
|
|
4296
|
+
logger.verbose(' ✓ No conflicts detected');
|
|
4297
|
+
} else {
|
|
4298
|
+
logger.verbose(` ⚠ ${conflicts.length} conflicts detected`);
|
|
4299
|
+
}
|
|
4300
|
+
|
|
4301
|
+
return conflicts;
|
|
4302
|
+
};
|
|
1725
4303
|
// end_aigc
|
|
1726
4304
|
|
|
1727
4305
|
// ABOUTME: Main file processing orchestration for template rendering
|
|
1728
4306
|
// ABOUTME: Coordinates file system, rendering, and conflict detection layers
|
|
1729
4307
|
|
|
1730
4308
|
|
|
1731
|
-
|
|
1732
4309
|
// start_aigc
|
|
1733
4310
|
/**
|
|
1734
4311
|
* 处理单个文件(准备 + 写入)
|
|
@@ -1790,7 +4367,7 @@ const processTemplateFiles = async (options
|
|
|
1790
4367
|
|
|
1791
4368
|
|
|
1792
4369
|
) => {
|
|
1793
|
-
const { templatePath, outputPath, context, templateConfig} = options;
|
|
4370
|
+
const { templatePath, outputPath, context, templateConfig, force } = options;
|
|
1794
4371
|
logger.verbose('Processing template files:');
|
|
1795
4372
|
logger.verbose(` - Template path: ${templatePath}`);
|
|
1796
4373
|
logger.verbose(` - Output path: ${outputPath}`);
|
|
@@ -1822,7 +4399,7 @@ const processTemplateFiles = async (options
|
|
|
1822
4399
|
}
|
|
1823
4400
|
|
|
1824
4401
|
// 阶段 2: Dry-run - 收集所有将要写入的文件
|
|
1825
|
-
await collectFilesToRender({
|
|
4402
|
+
const filesToWrite = await collectFilesToRender({
|
|
1826
4403
|
files,
|
|
1827
4404
|
templatePath,
|
|
1828
4405
|
context,
|
|
@@ -1830,7 +4407,20 @@ const processTemplateFiles = async (options
|
|
|
1830
4407
|
});
|
|
1831
4408
|
|
|
1832
4409
|
// 阶段 3: 冲突检测(force 为 true 时跳过)
|
|
1833
|
-
{
|
|
4410
|
+
if (!force) {
|
|
4411
|
+
const conflicts = detectFileConflicts(outputPath, filesToWrite);
|
|
4412
|
+
|
|
4413
|
+
if (conflicts.length > 0) {
|
|
4414
|
+
// 有冲突,抛出详细的错误信息
|
|
4415
|
+
const conflictList = conflicts.map(f => ` - ${f}`).join('\n');
|
|
4416
|
+
throw new Error(
|
|
4417
|
+
`File conflicts detected in output directory: ${outputPath}\n\n` +
|
|
4418
|
+
`The following files already exist and would be overwritten:\n${conflictList}\n\n` +
|
|
4419
|
+
'Please remove these files or use a different output directory.\n' +
|
|
4420
|
+
'Or use --force to overwrite existing files.',
|
|
4421
|
+
);
|
|
4422
|
+
}
|
|
4423
|
+
} else {
|
|
1834
4424
|
logger.verbose(
|
|
1835
4425
|
' - Force mode enabled, skipping conflict detection. Existing files will be overwritten.',
|
|
1836
4426
|
);
|
|
@@ -1975,7 +4565,7 @@ const prepareOutputDirectory = (outputPath) => {
|
|
|
1975
4565
|
const execute = async (
|
|
1976
4566
|
options,
|
|
1977
4567
|
) => {
|
|
1978
|
-
const { templateName, outputPath, command} = options;
|
|
4568
|
+
const { templateName, outputPath, command, force } = options;
|
|
1979
4569
|
|
|
1980
4570
|
// 1. 加载模板
|
|
1981
4571
|
const { templatePath } = await loadTemplateMetadata(templateName);
|
|
@@ -1999,7 +4589,9 @@ const execute = async (
|
|
|
1999
4589
|
templatePath,
|
|
2000
4590
|
outputPath: absoluteOutputPath,
|
|
2001
4591
|
context,
|
|
2002
|
-
templateConfig
|
|
4592
|
+
templateConfig,
|
|
4593
|
+
force,
|
|
4594
|
+
});
|
|
2003
4595
|
|
|
2004
4596
|
// 7. 执行 onAfterRender 钩子
|
|
2005
4597
|
await executeAfterRenderHook(templateConfig, context, absoluteOutputPath);
|
|
@@ -2089,6 +4681,7 @@ const runGitCommand = (command, projectPath) => {
|
|
|
2089
4681
|
const runGitInit = (projectPath) => {
|
|
2090
4682
|
// 检查是否已存在 .git 目录
|
|
2091
4683
|
const gitDir = path.join(projectPath, '.git');
|
|
4684
|
+
|
|
2092
4685
|
if (fs.existsSync(gitDir)) {
|
|
2093
4686
|
logger.info(
|
|
2094
4687
|
'\n💡 Git repository already exists, skipping git initialization',
|
|
@@ -2102,9 +4695,8 @@ const runGitInit = (projectPath) => {
|
|
|
2102
4695
|
logger.success('Git repository initialized successfully!');
|
|
2103
4696
|
} catch (error) {
|
|
2104
4697
|
// Git 初始化失败不应该导致整个流程失败
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
);
|
|
4698
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4699
|
+
logger.warn(`Git initialization failed: ${errorMessage}`);
|
|
2108
4700
|
logger.info('You can manually initialize git later with: git init');
|
|
2109
4701
|
}
|
|
2110
4702
|
};
|
|
@@ -2129,9 +4721,8 @@ const commitChanges = (projectPath) => {
|
|
|
2129
4721
|
logger.success('Changes committed successfully!');
|
|
2130
4722
|
} catch (error) {
|
|
2131
4723
|
// Commit 失败不应该导致整个流程失败
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
);
|
|
4724
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4725
|
+
logger.warn(`Git commit failed: ${errorMessage}`);
|
|
2135
4726
|
logger.info(
|
|
2136
4727
|
'You can manually commit later with: git add --all && git commit -m "chore: init env"',
|
|
2137
4728
|
);
|
|
@@ -2152,28 +4743,33 @@ const runDev = (projectPath) => {
|
|
|
2152
4743
|
|
|
2153
4744
|
logger.info(`Executing: ${cliPath} dev in ${projectPath}`);
|
|
2154
4745
|
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
4746
|
+
try {
|
|
4747
|
+
// 使用通用的后台执行函数启动开发服务器
|
|
4748
|
+
// 调用 CLI 自己的 dev 命令
|
|
4749
|
+
const pid = spawnDetached(process.argv[0], [cliPath, 'dev'], {
|
|
4750
|
+
cwd: projectPath,
|
|
4751
|
+
verbose: false, // 不输出额外的进程信息,由 logger 统一处理
|
|
4752
|
+
});
|
|
2161
4753
|
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
4754
|
+
if (pid) {
|
|
4755
|
+
logger.success('Development server started in background!');
|
|
4756
|
+
logger.info(`Process ID: ${pid}`);
|
|
4757
|
+
logger.info(
|
|
4758
|
+
'\nThe dev server is running independently. You can close this terminal.',
|
|
4759
|
+
);
|
|
4760
|
+
logger.info(`To stop the server later, use: kill ${pid}`);
|
|
4761
|
+
} else {
|
|
4762
|
+
logger.warn('Failed to get process ID for dev server');
|
|
4763
|
+
}
|
|
4764
|
+
} catch (error) {
|
|
4765
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4766
|
+
logger.error(`Failed to start dev server: ${errorMessage}`);
|
|
2169
4767
|
}
|
|
2170
4768
|
};
|
|
2171
4769
|
|
|
2172
4770
|
/**
|
|
2173
|
-
*
|
|
4771
|
+
* Init 命令执行上下文
|
|
2174
4772
|
*/
|
|
2175
|
-
const executeInit = async (
|
|
2176
|
-
options
|
|
2177
4773
|
|
|
2178
4774
|
|
|
2179
4775
|
|
|
@@ -2181,280 +4777,183 @@ const executeInit = async (
|
|
|
2181
4777
|
|
|
2182
4778
|
|
|
2183
4779
|
|
|
2184
|
-
,
|
|
2185
|
-
command,
|
|
2186
|
-
) => {
|
|
2187
|
-
const timer = new TimeTracker();
|
|
2188
4780
|
|
|
2189
|
-
try {
|
|
2190
|
-
const {
|
|
2191
|
-
template: templateName,
|
|
2192
|
-
output: outputPath,
|
|
2193
|
-
skipInstall,
|
|
2194
|
-
skipGit,
|
|
2195
|
-
skipCommit,
|
|
2196
|
-
skipDev,
|
|
2197
|
-
force,
|
|
2198
|
-
} = options;
|
|
2199
|
-
|
|
2200
|
-
logger.info(`Initializing project with template: ${templateName}`);
|
|
2201
|
-
timer.logPhase('Initialization');
|
|
2202
4781
|
|
|
2203
|
-
// 执行模板引擎,返回结果对象
|
|
2204
|
-
const result = await execute({
|
|
2205
|
-
templateName,
|
|
2206
|
-
outputPath,
|
|
2207
|
-
command,
|
|
2208
|
-
force,
|
|
2209
|
-
});
|
|
2210
|
-
const { outputPath: absoluteOutputPath, templateConfig, context } = result;
|
|
2211
4782
|
|
|
2212
|
-
timer.logPhase('Template engine execution');
|
|
2213
|
-
logger.success('Project created successfully!');
|
|
2214
4783
|
|
|
2215
|
-
// 检查是否存在 package.json
|
|
2216
|
-
const packageJsonPath = path.join(absoluteOutputPath, 'package.json');
|
|
2217
|
-
const hasPackageJson = fs.existsSync(packageJsonPath);
|
|
2218
4784
|
|
|
2219
|
-
// 安装依赖(始终使用 pnpm install,利用缓存机制)
|
|
2220
|
-
if (!skipInstall) {
|
|
2221
|
-
if (hasPackageJson) {
|
|
2222
|
-
runPnpmInstall(absoluteOutputPath);
|
|
2223
|
-
timer.logPhase('Dependencies installation');
|
|
2224
|
-
} else {
|
|
2225
|
-
logger.info(
|
|
2226
|
-
'\n💡 No package.json found, skipping dependency installation',
|
|
2227
|
-
);
|
|
2228
|
-
}
|
|
2229
|
-
}
|
|
2230
4785
|
|
|
2231
|
-
// 执行 onComplete 钩子(在 pnpm install 之后)
|
|
2232
|
-
await executeCompleteHook(templateConfig, context, absoluteOutputPath);
|
|
2233
|
-
timer.logPhase('Complete hook execution');
|
|
2234
4786
|
|
|
2235
|
-
// 如果没有跳过 git,则初始化 git 仓库
|
|
2236
|
-
if (!skipGit) {
|
|
2237
|
-
runGitInit(absoluteOutputPath);
|
|
2238
|
-
timer.logPhase('Git initialization');
|
|
2239
|
-
}
|
|
2240
4787
|
|
|
2241
|
-
// 如果没有跳过 commit,则提交初始化生成的文件
|
|
2242
|
-
if (!skipCommit) {
|
|
2243
|
-
commitChanges(absoluteOutputPath);
|
|
2244
|
-
timer.logPhase('Git commit');
|
|
2245
|
-
}
|
|
2246
4788
|
|
|
2247
|
-
// 如果没有跳过 dev,则启动开发服务器
|
|
2248
|
-
if (!skipDev) {
|
|
2249
|
-
runDev(absoluteOutputPath);
|
|
2250
|
-
timer.logPhase('Dev server startup');
|
|
2251
|
-
} else {
|
|
2252
|
-
// 只有跳过 dev 时才显示 Next steps
|
|
2253
|
-
logger.info('\nNext steps:');
|
|
2254
|
-
logger.info(` cd ${outputPath}`);
|
|
2255
|
-
if (skipInstall && hasPackageJson) {
|
|
2256
|
-
logger.info(' pnpm install');
|
|
2257
|
-
}
|
|
2258
|
-
if (skipGit) {
|
|
2259
|
-
logger.info(' git init');
|
|
2260
|
-
}
|
|
2261
|
-
if (skipCommit) {
|
|
2262
|
-
logger.info(' git add --all && git commit -m "chore: init env"');
|
|
2263
|
-
}
|
|
2264
|
-
logger.info(' coze dev');
|
|
2265
|
-
}
|
|
2266
4789
|
|
|
2267
|
-
// 输出总耗时
|
|
2268
|
-
timer.logTotal();
|
|
2269
|
-
} catch (error) {
|
|
2270
|
-
timer.logTotal();
|
|
2271
|
-
logger.error('Failed to initialize project:');
|
|
2272
|
-
logger.error(error instanceof Error ? error.message : String(error));
|
|
2273
|
-
process.exit(1);
|
|
2274
|
-
}
|
|
2275
|
-
};
|
|
2276
4790
|
|
|
2277
|
-
/**
|
|
2278
|
-
* 注册 init 命令到 program
|
|
2279
|
-
*/
|
|
2280
|
-
const registerCommand$1 = program => {
|
|
2281
|
-
program
|
|
2282
|
-
.command('init')
|
|
2283
|
-
.description('Initialize a new project from a template')
|
|
2284
|
-
.argument('[directory]', 'Output directory for the project')
|
|
2285
|
-
.requiredOption('-t, --template <name>', 'Template name')
|
|
2286
|
-
.option('-o, --output <path>', 'Output directory', process.cwd())
|
|
2287
|
-
.option('--skip-install', 'Skip automatic pnpm install', false)
|
|
2288
|
-
.option('--skip-git', 'Skip automatic git initialization', false)
|
|
2289
|
-
.option(
|
|
2290
|
-
'--skip-commit',
|
|
2291
|
-
'Skip automatic git commit after initialization',
|
|
2292
|
-
false,
|
|
2293
|
-
)
|
|
2294
|
-
.option('--skip-dev', 'Skip automatic dev server start', false)
|
|
2295
|
-
.allowUnknownOption() // 允许透传参数
|
|
2296
|
-
.action(async (directory, options, command) => {
|
|
2297
|
-
// 位置参数优先级高于 --output 选项
|
|
2298
|
-
const outputPath = _nullishCoalesce(directory, () => ( options.output));
|
|
2299
|
-
// Always use force mode - overwrite existing files without conflict check
|
|
2300
|
-
const force = true;
|
|
2301
|
-
await executeInit({ ...options, output: outputPath, force }, command);
|
|
2302
|
-
});
|
|
2303
|
-
};
|
|
2304
4791
|
|
|
2305
|
-
// ABOUTME: This file implements the update command for coze CLI
|
|
2306
|
-
// ABOUTME: It wraps pnpm update/install to update package dependencies with logging support
|
|
2307
4792
|
|
|
2308
4793
|
|
|
2309
4794
|
|
|
2310
4795
|
|
|
2311
|
-
/**
|
|
2312
|
-
* 日志文件名常量
|
|
2313
|
-
*/
|
|
2314
|
-
const LOG_FILE_NAME = 'update.log';
|
|
2315
4796
|
|
|
2316
|
-
/**
|
|
2317
|
-
* 获取日志目录
|
|
2318
|
-
* 优先使用环境变量 COZE_LOG_DIR,否则使用 ~/.coze-logs
|
|
2319
|
-
*/
|
|
2320
|
-
const getLogDir = () =>
|
|
2321
|
-
process.env.COZE_LOG_DIR || path.join(os.homedir(), '.coze-logs');
|
|
2322
4797
|
|
|
2323
|
-
/**
|
|
2324
|
-
* 解析日志文件路径
|
|
2325
|
-
* - 如果是绝对路径,直接使用
|
|
2326
|
-
* - 如果是相对路径,基于 getLogDir() + 相对路径
|
|
2327
|
-
* - 如果为空,使用 getLogDir() + LOG_FILE_NAME
|
|
2328
|
-
*/
|
|
2329
|
-
const resolveLogFilePath = (logFile) => {
|
|
2330
|
-
if (!logFile) {
|
|
2331
|
-
return path.join(getLogDir(), LOG_FILE_NAME);
|
|
2332
|
-
}
|
|
2333
4798
|
|
|
2334
|
-
if (path.isAbsolute(logFile)) {
|
|
2335
|
-
return logFile;
|
|
2336
|
-
}
|
|
2337
4799
|
|
|
2338
|
-
|
|
2339
|
-
|
|
4800
|
+
|
|
4801
|
+
|
|
4802
|
+
|
|
2340
4803
|
|
|
2341
4804
|
/**
|
|
2342
|
-
*
|
|
4805
|
+
* 步骤1: 执行模板引擎
|
|
2343
4806
|
*/
|
|
2344
|
-
const
|
|
2345
|
-
|
|
4807
|
+
const executeTemplateEngineStep = async ctx => {
|
|
4808
|
+
logger.info(`Initializing project with template: ${ctx.templateName}`);
|
|
4809
|
+
ctx.timer.logPhase('Initialization');
|
|
4810
|
+
|
|
4811
|
+
const result = await execute({
|
|
4812
|
+
templateName: ctx.templateName,
|
|
4813
|
+
outputPath: ctx.outputPath,
|
|
4814
|
+
command: ctx.command,
|
|
4815
|
+
force: _nullishCoalesce(ctx.options.force, () => ( false)),
|
|
4816
|
+
});
|
|
2346
4817
|
|
|
2347
|
-
//
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
4818
|
+
// 保存结果到上下文
|
|
4819
|
+
ctx.absoluteOutputPath = result.outputPath;
|
|
4820
|
+
ctx.templateConfig = result.templateConfig;
|
|
4821
|
+
ctx.context = result.context;
|
|
2351
4822
|
|
|
2352
|
-
|
|
2353
|
-
|
|
4823
|
+
ctx.timer.logPhase('Template engine execution');
|
|
4824
|
+
logger.success('Project created successfully!');
|
|
2354
4825
|
};
|
|
2355
4826
|
|
|
2356
4827
|
/**
|
|
2357
|
-
*
|
|
4828
|
+
* 步骤2: 检查 package.json 是否存在
|
|
2358
4829
|
*/
|
|
2359
|
-
const
|
|
2360
|
-
|
|
2361
|
-
|
|
4830
|
+
const checkPackageJsonStep = ctx => {
|
|
4831
|
+
if (!ctx.absoluteOutputPath) {
|
|
4832
|
+
return;
|
|
4833
|
+
}
|
|
4834
|
+
|
|
4835
|
+
const packageJsonPath = path.join(ctx.absoluteOutputPath, 'package.json');
|
|
4836
|
+
ctx.hasPackageJson = fs.existsSync(packageJsonPath);
|
|
2362
4837
|
};
|
|
2363
4838
|
|
|
2364
4839
|
/**
|
|
2365
|
-
*
|
|
4840
|
+
* 步骤3: 安装依赖
|
|
2366
4841
|
*/
|
|
2367
|
-
const
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
4842
|
+
const installDependenciesStep = ctx => {
|
|
4843
|
+
if (!ctx.absoluteOutputPath) {
|
|
4844
|
+
return;
|
|
4845
|
+
}
|
|
4846
|
+
|
|
4847
|
+
const { skipInstall } = ctx.options;
|
|
4848
|
+
|
|
4849
|
+
if (!skipInstall) {
|
|
4850
|
+
if (ctx.hasPackageJson) {
|
|
4851
|
+
runPnpmInstall(ctx.absoluteOutputPath);
|
|
4852
|
+
ctx.timer.logPhase('Dependencies installation');
|
|
2373
4853
|
} else {
|
|
2374
|
-
|
|
4854
|
+
logger.info(
|
|
4855
|
+
'\n💡 No package.json found, skipping dependency installation',
|
|
4856
|
+
);
|
|
2375
4857
|
}
|
|
2376
|
-
}
|
|
2377
|
-
// 确保数据写入磁盘
|
|
2378
|
-
stream.uncork();
|
|
4858
|
+
}
|
|
2379
4859
|
};
|
|
2380
4860
|
|
|
2381
4861
|
/**
|
|
2382
|
-
*
|
|
4862
|
+
* 步骤4: 执行完成钩子
|
|
2383
4863
|
*/
|
|
2384
|
-
const
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
message,
|
|
2388
|
-
) => {
|
|
2389
|
-
// 输出到控制台
|
|
2390
|
-
switch (level) {
|
|
2391
|
-
case 'info':
|
|
2392
|
-
logger.info(message);
|
|
2393
|
-
break;
|
|
2394
|
-
case 'success':
|
|
2395
|
-
logger.success(message);
|
|
2396
|
-
break;
|
|
2397
|
-
case 'error':
|
|
2398
|
-
logger.error(message);
|
|
2399
|
-
break;
|
|
2400
|
-
default:
|
|
2401
|
-
logger.info(message);
|
|
2402
|
-
break;
|
|
4864
|
+
const executeCompleteHookStep = async ctx => {
|
|
4865
|
+
if (!ctx.absoluteOutputPath || !ctx.templateConfig || !ctx.context) {
|
|
4866
|
+
return;
|
|
2403
4867
|
}
|
|
2404
4868
|
|
|
2405
|
-
|
|
2406
|
-
|
|
4869
|
+
await executeCompleteHook(
|
|
4870
|
+
ctx.templateConfig,
|
|
4871
|
+
ctx.context,
|
|
4872
|
+
ctx.absoluteOutputPath,
|
|
4873
|
+
);
|
|
4874
|
+
|
|
4875
|
+
ctx.timer.logPhase('Complete hook execution');
|
|
2407
4876
|
};
|
|
2408
4877
|
|
|
2409
|
-
// start_aigc
|
|
2410
4878
|
/**
|
|
2411
|
-
*
|
|
4879
|
+
* 步骤5: 初始化 Git 仓库
|
|
2412
4880
|
*/
|
|
2413
|
-
const
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
4881
|
+
const gitInitStep = ctx => {
|
|
4882
|
+
if (!ctx.absoluteOutputPath) {
|
|
4883
|
+
return;
|
|
4884
|
+
}
|
|
2417
4885
|
|
|
4886
|
+
const { skipGit } = ctx.options;
|
|
2418
4887
|
|
|
4888
|
+
if (!skipGit) {
|
|
4889
|
+
runGitInit(ctx.absoluteOutputPath);
|
|
4890
|
+
ctx.timer.logPhase('Git initialization');
|
|
4891
|
+
}
|
|
4892
|
+
};
|
|
2419
4893
|
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
4894
|
+
/**
|
|
4895
|
+
* 步骤6: 提交 Git 变更
|
|
4896
|
+
*/
|
|
4897
|
+
const gitCommitStep = ctx => {
|
|
4898
|
+
if (!ctx.absoluteOutputPath) {
|
|
4899
|
+
return;
|
|
4900
|
+
}
|
|
2423
4901
|
|
|
2424
|
-
const
|
|
4902
|
+
const { skipCommit } = ctx.options;
|
|
2425
4903
|
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
4904
|
+
if (!skipCommit) {
|
|
4905
|
+
commitChanges(ctx.absoluteOutputPath);
|
|
4906
|
+
ctx.timer.logPhase('Git commit');
|
|
2429
4907
|
}
|
|
4908
|
+
};
|
|
2430
4909
|
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
4910
|
+
/**
|
|
4911
|
+
* 步骤7: 启动开发服务器
|
|
4912
|
+
*/
|
|
4913
|
+
const startDevServerStep = ctx => {
|
|
4914
|
+
if (!ctx.absoluteOutputPath) {
|
|
4915
|
+
return;
|
|
2436
4916
|
}
|
|
2437
4917
|
|
|
2438
|
-
|
|
2439
|
-
if (registry) {
|
|
2440
|
-
parts.push(`--registry=${registry}`);
|
|
2441
|
-
}
|
|
4918
|
+
const { skipDev, skipInstall, skipGit, skipCommit } = ctx.options;
|
|
2442
4919
|
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
4920
|
+
if (!skipDev) {
|
|
4921
|
+
runDev(ctx.absoluteOutputPath);
|
|
4922
|
+
ctx.timer.logPhase('Dev server startup');
|
|
4923
|
+
} else {
|
|
4924
|
+
// 只有跳过 dev 时才显示 Next steps
|
|
4925
|
+
logger.info('\nNext steps:');
|
|
4926
|
+
logger.info(` cd ${ctx.outputPath}`);
|
|
4927
|
+
if (skipInstall && ctx.hasPackageJson) {
|
|
4928
|
+
logger.info(' pnpm install');
|
|
4929
|
+
}
|
|
4930
|
+
if (skipGit) {
|
|
4931
|
+
logger.info(' git init');
|
|
4932
|
+
}
|
|
4933
|
+
if (skipCommit) {
|
|
4934
|
+
logger.info(' git add --all && git commit -m "chore: init env"');
|
|
4935
|
+
}
|
|
4936
|
+
logger.info(' coze dev');
|
|
2446
4937
|
}
|
|
2447
|
-
|
|
2448
|
-
return parts.join(' ');
|
|
2449
4938
|
};
|
|
2450
|
-
// end_aigc
|
|
2451
4939
|
|
|
2452
|
-
// start_aigc
|
|
2453
4940
|
/**
|
|
2454
|
-
*
|
|
4941
|
+
* Init 命令的所有执行步骤
|
|
2455
4942
|
*/
|
|
2456
|
-
const
|
|
2457
|
-
|
|
4943
|
+
const initSteps = [
|
|
4944
|
+
executeTemplateEngineStep,
|
|
4945
|
+
checkPackageJsonStep,
|
|
4946
|
+
installDependenciesStep,
|
|
4947
|
+
executeCompleteHookStep,
|
|
4948
|
+
gitInitStep,
|
|
4949
|
+
gitCommitStep,
|
|
4950
|
+
startDevServerStep,
|
|
4951
|
+
];
|
|
4952
|
+
|
|
4953
|
+
/**
|
|
4954
|
+
* 执行 init 命令的内部实现
|
|
4955
|
+
*/
|
|
4956
|
+
const executeInit = async (
|
|
2458
4957
|
options
|
|
2459
4958
|
|
|
2460
4959
|
|
|
@@ -2462,155 +4961,132 @@ const executeUpdate = (
|
|
|
2462
4961
|
|
|
2463
4962
|
|
|
2464
4963
|
|
|
4964
|
+
|
|
2465
4965
|
,
|
|
4966
|
+
command,
|
|
2466
4967
|
) => {
|
|
2467
|
-
|
|
4968
|
+
const timer = new TimeTracker();
|
|
4969
|
+
const cmdStartTime = Date.now();
|
|
4970
|
+
|
|
4971
|
+
// 初始化执行上下文
|
|
4972
|
+
const ctx = {
|
|
4973
|
+
options,
|
|
4974
|
+
command,
|
|
4975
|
+
timer,
|
|
4976
|
+
cmdStartTime,
|
|
4977
|
+
templateName: options.template,
|
|
4978
|
+
outputPath: options.output,
|
|
4979
|
+
};
|
|
2468
4980
|
|
|
2469
4981
|
try {
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
logger.info(`Log file path resolved to: ${logFilePath}`);
|
|
2477
|
-
|
|
2478
|
-
logStream = createLogStream(logFilePath);
|
|
4982
|
+
// 上报命令开始
|
|
4983
|
+
reportCommandStart(
|
|
4984
|
+
'init',
|
|
4985
|
+
JSON.stringify({ template: ctx.templateName, output: ctx.outputPath }),
|
|
4986
|
+
{ template: ctx.templateName },
|
|
4987
|
+
);
|
|
2479
4988
|
|
|
2480
|
-
//
|
|
2481
|
-
|
|
4989
|
+
// 逐个执行步骤
|
|
4990
|
+
for (const step of initSteps) {
|
|
4991
|
+
await step(ctx);
|
|
4992
|
+
}
|
|
2482
4993
|
|
|
2483
|
-
|
|
4994
|
+
// 输出总耗时
|
|
4995
|
+
timer.logTotal();
|
|
2484
4996
|
|
|
2485
|
-
//
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
registry,
|
|
2490
|
-
extraArgs,
|
|
4997
|
+
// 上报命令成功
|
|
4998
|
+
reportCommandComplete('init', true, Date.now() - cmdStartTime, {
|
|
4999
|
+
args: JSON.stringify(options),
|
|
5000
|
+
categories: { template: ctx.templateName },
|
|
2491
5001
|
});
|
|
5002
|
+
// flush 由 main 函数统一处理
|
|
5003
|
+
} catch (error) {
|
|
5004
|
+
timer.logTotal();
|
|
2492
5005
|
|
|
2493
|
-
//
|
|
2494
|
-
const
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
:
|
|
2499
|
-
|
|
2500
|
-
logWithFile(logStream, 'info', `Executing: ${command}`);
|
|
2501
|
-
logWithFile(logStream, 'info', `Working directory: ${workingDir}`);
|
|
2502
|
-
logWithFile(logStream, 'info', `Log file: ${logFilePath}`);
|
|
2503
|
-
|
|
2504
|
-
// 记录命令开始时间
|
|
2505
|
-
writeLogWithTimestamp(logStream, '--- Command execution started ---');
|
|
2506
|
-
|
|
2507
|
-
// 同步执行命令
|
|
2508
|
-
const result = shelljs.exec(command, {
|
|
2509
|
-
cwd: workingDir,
|
|
2510
|
-
silent: true, // 使用 silent 来捕获输出
|
|
5006
|
+
// 上报错误
|
|
5007
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
5008
|
+
reportError(err, {
|
|
5009
|
+
command: 'init',
|
|
5010
|
+
template: options.template,
|
|
5011
|
+
output: options.output,
|
|
2511
5012
|
});
|
|
2512
5013
|
|
|
2513
|
-
//
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
if (result.stderr) {
|
|
2520
|
-
process.stderr.write(result.stderr);
|
|
2521
|
-
writeLogWithTimestamp(logStream, result.stderr.trim());
|
|
2522
|
-
}
|
|
2523
|
-
|
|
2524
|
-
// 记录命令结束时间
|
|
2525
|
-
writeLogWithTimestamp(logStream, '--- Command execution ended ---');
|
|
2526
|
-
|
|
2527
|
-
// 检查执行结果并记录到日志
|
|
2528
|
-
if (result.code === 0) {
|
|
2529
|
-
logWithFile(logStream, 'success', 'Package updated successfully');
|
|
2530
|
-
logWithFile(logStream, 'info', `Log file: ${logFilePath}`);
|
|
2531
|
-
} else {
|
|
2532
|
-
logWithFile(
|
|
2533
|
-
logStream,
|
|
2534
|
-
'error',
|
|
2535
|
-
`Command exited with code ${result.code}`,
|
|
2536
|
-
);
|
|
2537
|
-
logWithFile(
|
|
2538
|
-
logStream,
|
|
2539
|
-
'error',
|
|
2540
|
-
`Check log file for details: ${logFilePath}`,
|
|
2541
|
-
);
|
|
2542
|
-
}
|
|
2543
|
-
|
|
2544
|
-
// 关闭日志流并等待写入完成
|
|
2545
|
-
logStream.end(() => {
|
|
2546
|
-
// 流关闭后再退出进程
|
|
2547
|
-
if (result.code !== 0) {
|
|
2548
|
-
process.exit(result.code || 1);
|
|
2549
|
-
}
|
|
5014
|
+
// 上报命令失败
|
|
5015
|
+
reportCommandComplete('init', false, Date.now() - cmdStartTime, {
|
|
5016
|
+
args: JSON.stringify(options),
|
|
5017
|
+
errorCode: 1,
|
|
5018
|
+
errorMessage: err.message,
|
|
5019
|
+
categories: { template: options.template },
|
|
2550
5020
|
});
|
|
2551
|
-
|
|
2552
|
-
logger.error('Failed to update package:');
|
|
2553
|
-
logger.error(error instanceof Error ? error.message : String(error));
|
|
5021
|
+
await flushSlardar();
|
|
2554
5022
|
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
logStream,
|
|
2559
|
-
`[ERROR] ${error instanceof Error ? error.message : String(error)}`,
|
|
2560
|
-
);
|
|
2561
|
-
// 等待流关闭后再退出
|
|
2562
|
-
logStream.end(() => {
|
|
2563
|
-
process.exit(1);
|
|
2564
|
-
});
|
|
2565
|
-
} else {
|
|
2566
|
-
process.exit(1);
|
|
2567
|
-
}
|
|
5023
|
+
logger.error('Failed to initialize project:');
|
|
5024
|
+
logger.error(err.message);
|
|
5025
|
+
process.exit(1);
|
|
2568
5026
|
}
|
|
2569
5027
|
};
|
|
2570
|
-
// end_aigc
|
|
2571
5028
|
|
|
2572
5029
|
/**
|
|
2573
|
-
* 注册
|
|
5030
|
+
* 注册 init 命令到 program
|
|
2574
5031
|
*/
|
|
2575
5032
|
const registerCommand = program => {
|
|
2576
5033
|
program
|
|
2577
|
-
.command('
|
|
2578
|
-
.description('
|
|
2579
|
-
.
|
|
2580
|
-
.
|
|
5034
|
+
.command('init')
|
|
5035
|
+
.description('Initialize a new project from a template')
|
|
5036
|
+
.argument('[directory]', 'Output directory for the project')
|
|
5037
|
+
.requiredOption('-t, --template <name>', 'Template name')
|
|
5038
|
+
.option('-o, --output <path>', 'Output directory', process.cwd())
|
|
5039
|
+
.option('--skip-install', 'Skip automatic pnpm install', false)
|
|
5040
|
+
.option('--skip-git', 'Skip automatic git initialization', false)
|
|
2581
5041
|
.option(
|
|
2582
|
-
'--
|
|
2583
|
-
'
|
|
2584
|
-
|
|
5042
|
+
'--skip-commit',
|
|
5043
|
+
'Skip automatic git commit after initialization',
|
|
5044
|
+
false,
|
|
2585
5045
|
)
|
|
2586
|
-
.option('--
|
|
2587
|
-
.
|
|
2588
|
-
.
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
...options,
|
|
2595
|
-
version: options.to, // 将 --to 映射到 version
|
|
2596
|
-
extraArgs,
|
|
2597
|
-
});
|
|
5046
|
+
.option('--skip-dev', 'Skip automatic dev server start', false)
|
|
5047
|
+
.allowUnknownOption() // 允许透传参数
|
|
5048
|
+
.action(async (directory, options, command) => {
|
|
5049
|
+
// 位置参数优先级高于 --output 选项
|
|
5050
|
+
const outputPath = _nullishCoalesce(directory, () => ( options.output));
|
|
5051
|
+
// Always use force mode - overwrite existing files without conflict check
|
|
5052
|
+
const force = true;
|
|
5053
|
+
await executeInit({ ...options, output: outputPath, force }, command);
|
|
2598
5054
|
});
|
|
2599
5055
|
};
|
|
2600
5056
|
|
|
2601
|
-
var version = "0.0.1-alpha.dcc485";
|
|
2602
|
-
var packageJson = {
|
|
2603
|
-
version: version};
|
|
2604
|
-
|
|
2605
5057
|
const commands = [
|
|
5058
|
+
registerCommand,
|
|
2606
5059
|
registerCommand$1,
|
|
2607
|
-
registerCommand$2,
|
|
2608
5060
|
registerCommand$4,
|
|
5061
|
+
registerCommand$2,
|
|
2609
5062
|
registerCommand$3,
|
|
2610
|
-
registerCommand,
|
|
2611
5063
|
];
|
|
2612
5064
|
|
|
2613
|
-
const main = () => {
|
|
5065
|
+
const main = async () => {
|
|
5066
|
+
// 初始化 Slardar 监控
|
|
5067
|
+
initSlardar();
|
|
5068
|
+
|
|
5069
|
+
// 监听未捕获的异常
|
|
5070
|
+
process.on('uncaughtException', async (error) => {
|
|
5071
|
+
reportError(error, {
|
|
5072
|
+
type: 'uncaughtException',
|
|
5073
|
+
context: 'global',
|
|
5074
|
+
});
|
|
5075
|
+
await flushSlardar();
|
|
5076
|
+
process.exit(1);
|
|
5077
|
+
});
|
|
5078
|
+
|
|
5079
|
+
// 监听未处理的 Promise rejection
|
|
5080
|
+
process.on('unhandledRejection', async (reason) => {
|
|
5081
|
+
const error = reason instanceof Error ? reason : new Error(String(reason));
|
|
5082
|
+
reportError(error, {
|
|
5083
|
+
type: 'unhandledRejection',
|
|
5084
|
+
context: 'global',
|
|
5085
|
+
});
|
|
5086
|
+
await flushSlardar();
|
|
5087
|
+
process.exit(1);
|
|
5088
|
+
});
|
|
5089
|
+
|
|
2614
5090
|
const program = new commander.Command();
|
|
2615
5091
|
|
|
2616
5092
|
program
|
|
@@ -2625,7 +5101,12 @@ const main = () => {
|
|
|
2625
5101
|
// 在 help 输出中添加模板信息
|
|
2626
5102
|
program.addHelpText('after', generateTemplatesHelpText());
|
|
2627
5103
|
|
|
2628
|
-
|
|
5104
|
+
// 使用 parseAsync 等待命令执行完成
|
|
5105
|
+
await program.parseAsync(process.argv);
|
|
5106
|
+
|
|
5107
|
+
// 统一在命令执行完成后 flush
|
|
5108
|
+
// 注意:如果命令中调用了 process.exit(),不会执行到这里
|
|
5109
|
+
await flushSlardar();
|
|
2629
5110
|
};
|
|
2630
5111
|
|
|
2631
5112
|
main();
|