@coze-arch/cli 0.0.1-alpha.db1c06 → 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.
Files changed (132) hide show
  1. package/README.md +1 -0
  2. package/lib/__templates__/expo/_npmrc +1 -0
  3. package/lib/__templates__/expo/client/components/Screen.tsx +2 -2
  4. package/lib/__templates__/expo/client/eslint.config.mjs +7 -0
  5. package/lib/__templates__/expo/client/scripts/install-missing-deps.js +10 -10
  6. package/lib/__templates__/expo/eslint-plugins/forbid-emoji/index.js +9 -0
  7. package/lib/__templates__/expo/eslint-plugins/forbid-emoji/rule.js +112 -0
  8. package/lib/__templates__/expo/eslint-plugins/forbid-emoji/tech.md +94 -0
  9. package/lib/__templates__/expo/eslint-plugins/restrict-linear-gradient/index.js +9 -0
  10. package/lib/__templates__/expo/eslint-plugins/restrict-linear-gradient/rule.js +120 -0
  11. package/lib/__templates__/expo/eslint-plugins/restrict-linear-gradient/tech.md +58 -0
  12. package/lib/__templates__/nextjs/AGENTS.md +54 -0
  13. package/lib/__templates__/nextjs/README.md +5 -0
  14. package/lib/__templates__/nextjs/eslint.config.mjs +5 -0
  15. package/lib/__templates__/nextjs/next.config.ts +1 -2
  16. package/lib/__templates__/nextjs/package.json +2 -5
  17. package/lib/__templates__/nextjs/pnpm-lock.yaml +1028 -5
  18. package/lib/__templates__/nextjs/scripts/build.sh +4 -1
  19. package/lib/__templates__/nextjs/scripts/dev.sh +8 -2
  20. package/lib/__templates__/nextjs/scripts/start.sh +7 -1
  21. package/lib/__templates__/nextjs/src/app/layout.tsx +1 -1
  22. package/lib/__templates__/nextjs/src/app/page.tsx +1 -2
  23. package/lib/__templates__/nextjs/src/server.ts +35 -0
  24. package/lib/__templates__/nextjs/tsconfig.json +1 -1
  25. package/lib/__templates__/nuxt-vue/.coze +12 -0
  26. package/lib/__templates__/nuxt-vue/AGENTS.md +42 -0
  27. package/lib/__templates__/nuxt-vue/README.md +73 -0
  28. package/lib/__templates__/nuxt-vue/_gitignore +24 -0
  29. package/lib/__templates__/nuxt-vue/_npmrc +23 -0
  30. package/lib/__templates__/nuxt-vue/app/app.vue +6 -0
  31. package/lib/__templates__/nuxt-vue/app/pages/index.vue +23 -0
  32. package/lib/__templates__/nuxt-vue/assets/css/main.css +24 -0
  33. package/lib/__templates__/nuxt-vue/nuxt.config.ts +116 -0
  34. package/lib/__templates__/nuxt-vue/package.json +35 -0
  35. package/lib/__templates__/nuxt-vue/pnpm-lock.yaml +8759 -0
  36. package/lib/__templates__/nuxt-vue/postcss.config.mjs +8 -0
  37. package/lib/__templates__/nuxt-vue/public/favicon.ico +0 -0
  38. package/lib/__templates__/nuxt-vue/public/robots.txt +2 -0
  39. package/lib/__templates__/nuxt-vue/scripts/build.sh +14 -0
  40. package/lib/__templates__/nuxt-vue/scripts/dev.sh +39 -0
  41. package/lib/__templates__/nuxt-vue/scripts/prepare.sh +14 -0
  42. package/lib/__templates__/nuxt-vue/scripts/start.sh +21 -0
  43. package/lib/__templates__/nuxt-vue/server/api/hello.ts +10 -0
  44. package/lib/__templates__/nuxt-vue/server/middleware/logger.ts +10 -0
  45. package/lib/__templates__/nuxt-vue/server/routes/health.ts +10 -0
  46. package/lib/__templates__/nuxt-vue/tailwind.config.js +13 -0
  47. package/lib/__templates__/nuxt-vue/template.config.js +87 -0
  48. package/lib/__templates__/nuxt-vue/tsconfig.json +18 -0
  49. package/lib/__templates__/taro/README.md +57 -45
  50. package/lib/__templates__/taro/config/index.ts +106 -41
  51. package/lib/__templates__/taro/config/prod.ts +4 -5
  52. package/lib/__templates__/taro/eslint.config.mjs +62 -6
  53. package/lib/__templates__/taro/package.json +19 -4
  54. package/lib/__templates__/taro/patches/@tarojs__plugin-mini-ci@4.1.9.patch +30 -0
  55. package/lib/__templates__/taro/pnpm-lock.yaml +912 -206
  56. package/lib/__templates__/taro/src/app.css +140 -36
  57. package/lib/__templates__/taro/src/components/ui/accordion.tsx +159 -0
  58. package/lib/__templates__/taro/src/components/ui/alert-dialog.tsx +260 -0
  59. package/lib/__templates__/taro/src/components/ui/alert.tsx +60 -0
  60. package/lib/__templates__/taro/src/components/ui/aspect-ratio.tsx +36 -0
  61. package/lib/__templates__/taro/src/components/ui/avatar.tsx +84 -0
  62. package/lib/__templates__/taro/src/components/ui/badge.tsx +37 -0
  63. package/lib/__templates__/taro/src/components/ui/breadcrumb.tsx +117 -0
  64. package/lib/__templates__/taro/src/components/ui/button-group.tsx +83 -0
  65. package/lib/__templates__/taro/src/components/ui/button.tsx +67 -0
  66. package/lib/__templates__/taro/src/components/ui/calendar.tsx +394 -0
  67. package/lib/__templates__/taro/src/components/ui/card.tsx +108 -0
  68. package/lib/__templates__/taro/src/components/ui/carousel.tsx +228 -0
  69. package/lib/__templates__/taro/src/components/ui/checkbox.tsx +58 -0
  70. package/lib/__templates__/taro/src/components/ui/code-block.tsx +169 -0
  71. package/lib/__templates__/taro/src/components/ui/collapsible.tsx +71 -0
  72. package/lib/__templates__/taro/src/components/ui/command.tsx +385 -0
  73. package/lib/__templates__/taro/src/components/ui/context-menu.tsx +614 -0
  74. package/lib/__templates__/taro/src/components/ui/dialog.tsx +256 -0
  75. package/lib/__templates__/taro/src/components/ui/drawer.tsx +192 -0
  76. package/lib/__templates__/taro/src/components/ui/dropdown-menu.tsx +561 -0
  77. package/lib/__templates__/taro/src/components/ui/field.tsx +228 -0
  78. package/lib/__templates__/taro/src/components/ui/hover-card.tsx +282 -0
  79. package/lib/__templates__/taro/src/components/ui/input-group.tsx +197 -0
  80. package/lib/__templates__/taro/src/components/ui/input-otp.tsx +136 -0
  81. package/lib/__templates__/taro/src/components/ui/input.tsx +56 -0
  82. package/lib/__templates__/taro/src/components/ui/label.tsx +24 -0
  83. package/lib/__templates__/taro/src/components/ui/menubar.tsx +595 -0
  84. package/lib/__templates__/taro/src/components/ui/navigation-menu.tsx +264 -0
  85. package/lib/__templates__/taro/src/components/ui/pagination.tsx +118 -0
  86. package/lib/__templates__/taro/src/components/ui/popover.tsx +291 -0
  87. package/lib/__templates__/taro/src/components/ui/portal.tsx +19 -0
  88. package/lib/__templates__/taro/src/components/ui/progress.tsx +28 -0
  89. package/lib/__templates__/taro/src/components/ui/radio-group.tsx +64 -0
  90. package/lib/__templates__/taro/src/components/ui/resizable.tsx +346 -0
  91. package/lib/__templates__/taro/src/components/ui/scroll-area.tsx +34 -0
  92. package/lib/__templates__/taro/src/components/ui/select.tsx +438 -0
  93. package/lib/__templates__/taro/src/components/ui/separator.tsx +30 -0
  94. package/lib/__templates__/taro/src/components/ui/sheet.tsx +262 -0
  95. package/lib/__templates__/taro/src/components/ui/skeleton.tsx +17 -0
  96. package/lib/__templates__/taro/src/components/ui/slider.tsx +203 -0
  97. package/lib/__templates__/taro/src/components/ui/sonner.tsx +1 -0
  98. package/lib/__templates__/taro/src/components/ui/switch.tsx +55 -0
  99. package/lib/__templates__/taro/src/components/ui/table.tsx +142 -0
  100. package/lib/__templates__/taro/src/components/ui/tabs.tsx +114 -0
  101. package/lib/__templates__/taro/src/components/ui/textarea.tsx +54 -0
  102. package/lib/__templates__/taro/src/components/ui/toast.tsx +517 -0
  103. package/lib/__templates__/taro/src/components/ui/toggle-group.tsx +120 -0
  104. package/lib/__templates__/taro/src/components/ui/toggle.tsx +77 -0
  105. package/lib/__templates__/taro/src/components/ui/tooltip.tsx +455 -0
  106. package/lib/__templates__/taro/src/lib/hooks/use-keyboard-offset.ts +37 -0
  107. package/lib/__templates__/taro/src/lib/measure.ts +115 -0
  108. package/lib/__templates__/taro/src/lib/platform.ts +12 -0
  109. package/lib/__templates__/taro/src/lib/utils.ts +6 -0
  110. package/lib/__templates__/taro/src/presets/dev-debug.ts +23 -0
  111. package/lib/__templates__/taro/src/presets/h5-container.tsx +15 -0
  112. package/lib/__templates__/taro/src/presets/h5-navbar.tsx +97 -30
  113. package/lib/__templates__/taro/src/presets/h5-styles.ts +192 -5
  114. package/lib/__templates__/taro/src/presets/index.tsx +4 -4
  115. package/lib/__templates__/templates.json +32 -0
  116. package/lib/__templates__/vite/AGENTS.md +41 -0
  117. package/lib/__templates__/vite/README.md +190 -11
  118. package/lib/__templates__/vite/_gitignore +1 -0
  119. package/lib/__templates__/vite/eslint.config.mjs +6 -1
  120. package/lib/__templates__/vite/package.json +10 -3
  121. package/lib/__templates__/vite/pnpm-lock.yaml +755 -15
  122. package/lib/__templates__/vite/scripts/build.sh +4 -1
  123. package/lib/__templates__/vite/scripts/dev.sh +9 -2
  124. package/lib/__templates__/vite/scripts/start.sh +9 -3
  125. package/lib/__templates__/vite/server/routes/index.ts +31 -0
  126. package/lib/__templates__/vite/server/server.ts +65 -0
  127. package/lib/__templates__/vite/server/vite.ts +67 -0
  128. package/lib/__templates__/vite/tsconfig.json +4 -3
  129. package/lib/__templates__/vite/vite.config.ts +4 -0
  130. package/lib/cli.js +3256 -768
  131. package/package.json +9 -3
  132. package/lib/__templates__/taro/src/presets/wx-debug.ts +0 -23
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;
2485
+ };
2486
+
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;
2496
+ };
2497
+
2498
+ /**
2499
+ * 加载模板配置文件
2500
+ * 支持 .ts 和 .js 文件(通过 sucrase 注册)
2501
+ *
2502
+ * @param templatePath - 模板目录路径
2503
+ * @returns 模板配置对象
2504
+ */
2505
+
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
+ );
2544
+ }
2545
+
2546
+ logger.verbose(`Using config file: ${configPath}`);
2547
+
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);
2550
+
2551
+ logger.verbose('Template config loaded successfully');
2552
+
2553
+ return config.default || config;
152
2554
  };
153
2555
 
154
- const COLOR_CODES = {
155
- reset: '\x1b[0m',
156
- red: '\x1b[31m',
157
- yellow: '\x1b[33m',
158
- green: '\x1b[32m',
159
- cyan: '\x1b[36m',
160
- gray: '\x1b[90m',
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;
161
2576
  };
162
2577
 
163
- class Logger {
164
-
165
-
166
-
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);
167
2590
 
168
- constructor(options = {}) {
169
- this.level = this.parseLogLevel(options.level);
170
- this.useColor = _nullishCoalesce$2(options.useColor, () => ( this.isColorSupported()));
171
- this.prefix = options.prefix;
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
+ );
172
2600
  }
173
2601
 
174
- parseLogLevel(level) {
175
- if (level !== undefined) {
176
- return level;
177
- }
2602
+ return template;
2603
+ };
178
2604
 
179
- const envLevel = _optionalChain$3([process, 'access', _ => _.env, 'access', _2 => _2.LOG_LEVEL, 'optionalAccess', _3 => _3.toLowerCase, 'call', _4 => _4()]);
180
- if (envLevel && envLevel in LOG_LEVEL_MAP) {
181
- return LOG_LEVEL_MAP[envLevel];
182
- }
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}`);
183
2619
 
184
- return LogLevel.INFO;
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}`);
185
2632
  }
186
2633
 
187
- isColorSupported() {
188
- // 简单检测:Node.js 环境且支持 TTY
189
- return (
190
- typeof process !== 'undefined' &&
191
- _optionalChain$3([process, 'access', _5 => _5.stdout, 'optionalAccess', _6 => _6.isTTY]) === true &&
192
- process.env.NO_COLOR === undefined
193
- );
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;
194
2650
  }
195
2651
 
196
- colorize(text, color) {
197
- if (!this.useColor) {
198
- return text;
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);
2660
+ }
2661
+
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);
2679
+ }
2680
+ };
2681
+
2682
+ /**
2683
+ * 执行 warmup 命令的内部实现
2684
+ */
2685
+ const executeWarmup = async (options) => {
2686
+ const timer = new TimeTracker();
2687
+
2688
+ try {
2689
+ const { template: templateFilter } = options;
2690
+
2691
+ logger.info('Starting template warmup...');
2692
+ timer.logPhase('Initialization');
2693
+
2694
+ // 加载模板配置
2695
+ const configPath = getTemplatesConfigPath();
2696
+ const templatesConfig = await loadTemplatesConfig(configPath);
2697
+
2698
+ logger.verbose(
2699
+ `Found ${templatesConfig.templates.length} templates in config`,
2700
+ );
2701
+
2702
+ // 过滤模板
2703
+ const templatesToWarmup = templateFilter
2704
+ ? templatesConfig.templates.filter(t => t.name === templateFilter)
2705
+ : templatesConfig.templates;
2706
+
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
+ }
2716
+ return;
199
2717
  }
200
- return `${COLOR_CODES[color]}${text}${COLOR_CODES.reset}`;
201
- }
202
-
203
- log(options
204
-
205
-
206
2718
 
2719
+ logger.info(
2720
+ `\nWill warm up ${templatesToWarmup.length} template(s): ${templatesToWarmup.map(t => t.name).join(', ')}`,
2721
+ );
207
2722
 
2723
+ // 获取模板基础路径
2724
+ const basePath = configPath.replace(/\/templates\.json$/, '');
208
2725
 
209
- ) {
210
- if (options.level > this.level) {
211
- return;
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}`);
212
2731
  }
213
2732
 
214
- const icon = this.colorize(options.icon, options.color);
215
- const prefix = this.prefix ? `${icon} ${this.prefix}` : icon;
216
- console.log(prefix, options.message, ...(_nullishCoalesce$2(options.args, () => ( []))));
217
- }
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
+ );
218
2737
 
219
- error(message, ...args) {
220
- this.log({
221
- level: LogLevel.ERROR,
222
- icon: '✖',
223
- color: 'red',
224
- message,
225
- args,
226
- });
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);
227
2744
  }
2745
+ };
228
2746
 
229
- warn(message, ...args) {
230
- this.log({
231
- level: LogLevel.WARN,
232
- icon: '⚠',
233
- color: 'yellow',
234
- message,
235
- args,
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);
236
2757
  });
237
- }
2758
+ };
238
2759
 
239
- success(message, ...args) {
240
- this.log({
241
- level: LogLevel.SUCCESS,
242
- icon: '✓',
243
- color: 'green',
244
- message,
245
- args,
246
- });
247
- }
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
248
2762
 
249
- info(message, ...args) {
250
- this.log({
251
- level: LogLevel.INFO,
252
- icon: 'ℹ',
253
- color: 'cyan',
254
- message,
255
- args,
256
- });
257
- }
258
2763
 
259
- verbose(message, ...args) {
260
- this.log({
261
- level: LogLevel.VERBOSE,
262
- icon: '→',
263
- color: 'gray',
264
- message,
265
- args,
266
- });
267
- }
268
- }
269
2764
 
270
- // 创建 logger 实例的工厂函数
271
- const createLogger = (options = {}) =>
272
- new Logger(options);
2765
+ /**
2766
+ * 日志文件名常量
2767
+ */
2768
+ const LOG_FILE_NAME$1 = 'update.log';
273
2769
 
274
- // 导出默认实例
275
- const logger = createLogger();
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');
276
2776
 
277
2777
  /**
278
- * 时间统计工具
2778
+ * 解析日志文件路径
2779
+ * - 如果是绝对路径,直接使用
2780
+ * - 如果是相对路径,基于 getLogDir() + 相对路径
2781
+ * - 如果为空,使用 getLogDir() + LOG_FILE_NAME
279
2782
  */
280
- class TimeTracker {
281
-
282
-
2783
+ const resolveLogFilePath$1 = (logFile) => {
2784
+ if (!logFile) {
2785
+ return path.join(getLogDir$1(), LOG_FILE_NAME$1);
2786
+ }
283
2787
 
284
- constructor() {
285
- this.startTime = perf_hooks.performance.now();
286
- this.lastTime = this.startTime;
2788
+ if (path.isAbsolute(logFile)) {
2789
+ return logFile;
287
2790
  }
288
2791
 
289
- /**
290
- * 记录阶段耗时
291
- * @param phaseName 阶段名称
292
- */
293
- logPhase(phaseName) {
294
- const now = perf_hooks.performance.now();
295
- const phaseTime = now - this.lastTime;
296
- this.lastTime = now;
2792
+ return path.join(getLogDir$1(), logFile);
2793
+ };
297
2794
 
298
- logger.verbose(`⏱ ${phaseName}: ${phaseTime.toFixed(2)}ms`);
299
- }
2795
+ /**
2796
+ * 创建日志写入流
2797
+ */
2798
+ const createLogStream$1 = (logFilePath) => {
2799
+ const logDir = path.dirname(logFilePath);
300
2800
 
301
- /**
302
- * 记录总耗时
303
- */
304
- logTotal() {
305
- const totalTime = perf_hooks.performance.now() - this.startTime;
306
- logger.verbose(`⏱ Total time: ${totalTime.toFixed(2)}ms`);
2801
+ // 确保日志目录存在
2802
+ if (!fs.existsSync(logDir)) {
2803
+ fs.mkdirSync(logDir, { recursive: true });
307
2804
  }
308
2805
 
309
- /**
310
- * 获取当前耗时(不输出日志)
311
- * @returns 从开始到现在的总耗时(毫秒)
312
- */
313
- getElapsedTime() {
314
- return perf_hooks.performance.now() - this.startTime;
315
- }
316
- }
2806
+ // 使用 'w' 标志覆盖之前的日志
2807
+ return fs.createWriteStream(logFilePath, { flags: 'w' });
2808
+ };
317
2809
 
318
2810
  /**
319
- * 获取模板配置文件路径
320
- * @returns templates.json 的绝对路径
2811
+ * 格式化时间戳
321
2812
  */
322
- const getTemplatesConfigPath = () => {
323
- const configPath = path.resolve(getTemplatesDir(), 'templates.json');
324
- logger.verbose(`Templates config path: ${configPath}`);
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 getTemplatesDir = () => {
334
- const templatesDir = path.resolve(__dirname, './__templates__');
335
- logger.verbose(`Templates directory: ${templatesDir}`);
336
- logger.verbose(`Templates directory exists: ${fs.existsSync(templatesDir)}`);
337
- return templatesDir;
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();
338
2833
  };
339
2834
 
340
2835
  /**
341
- * 加载模板配置文件
342
- * 支持 .ts 和 .js 文件(通过 sucrase 注册)
343
- *
344
- * @param templatePath - 模板目录路径
345
- * @returns 模板配置对象
2836
+ * 同时输出到控制台和日志文件
346
2837
  */
347
-
348
- const loadTemplateConfig = async (
349
- templatePath,
2838
+ const logWithFile = (
2839
+ stream,
2840
+ level,
2841
+ message,
350
2842
  ) => {
351
- logger.verbose(`Loading template config from: ${templatePath}`);
352
-
353
- const tsConfigPath = path.join(templatePath, 'template.config.ts');
354
- const jsConfigPath = path.join(templatePath, 'template.config.js');
355
-
356
- logger.verbose('Checking for config files:');
357
- logger.verbose(` - TypeScript: ${tsConfigPath}`);
358
- logger.verbose(` - JavaScript: ${jsConfigPath}`);
359
-
360
- let configPath;
361
-
362
- const [tsExists, jsExists] = await Promise.all([
363
- fs$1.access(tsConfigPath).then(
364
- () => true,
365
- () => false,
366
- ),
367
- fs$1.access(jsConfigPath).then(
368
- () => true,
369
- () => false,
370
- ),
371
- ]);
372
-
373
- logger.verbose('Config file existence check:');
374
- logger.verbose(` - template.config.ts: ${tsExists}`);
375
- logger.verbose(` - template.config.js: ${jsExists}`);
376
-
377
- if (tsExists) {
378
- configPath = tsConfigPath;
379
- } else if (jsExists) {
380
- configPath = jsConfigPath;
381
- } else {
382
- throw new Error(
383
- `Template config not found in ${templatePath}.\n` +
384
- 'Expected: template.config.ts or template.config.js',
385
- );
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;
386
2857
  }
387
2858
 
388
- logger.verbose(`Using config file: ${configPath}`);
389
-
390
- // eslint-disable-next-line @typescript-eslint/no-require-imports, security/detect-non-literal-require -- Sucrase handles .ts files at runtime, path is validated above
391
- const config = require(configPath);
392
-
393
- logger.verbose('Template config loaded successfully');
394
-
395
- return config.default || config;
2859
+ // 写入日志文件(带时间戳)
2860
+ writeLogWithTimestamp(stream, `[${level.toUpperCase()}] ${message}`);
396
2861
  };
397
2862
 
2863
+ // start_aigc
398
2864
  /**
399
- * 加载模板列表配置
400
- *
401
- * @param configPath - templates.json 配置文件路径
402
- * @returns 模板列表配置
2865
+ * 构建 pnpm add 命令
403
2866
  */
404
- const loadTemplatesConfig = async (
405
- configPath,
2867
+ const buildPnpmCommand = (
2868
+ packageName,
2869
+ options
2870
+
2871
+
2872
+
2873
+
2874
+ ,
406
2875
  ) => {
407
- logger.verbose(`Loading templates config from: ${configPath}`);
2876
+ const { global, version, registry, extraArgs } = options;
408
2877
 
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) ;
2878
+ const parts = ['pnpm', 'add'];
412
2879
 
413
- logger.verbose(
414
- `Found ${config.templates.length} templates: ${config.templates.map(t => t.name).join(', ')}`,
415
- );
2880
+ // 添加全局标记
2881
+ if (global) {
2882
+ parts.push('-g');
2883
+ }
416
2884
 
417
- return config;
2885
+ // 添加包名和版本
2886
+ if (version && version !== 'latest') {
2887
+ parts.push(`${packageName}@${version}`);
2888
+ } else {
2889
+ parts.push(`${packageName}@latest`);
2890
+ }
2891
+
2892
+ // 添加 registry
2893
+ if (registry) {
2894
+ parts.push(`--registry=${registry}`);
2895
+ }
2896
+
2897
+ // 添加额外参数
2898
+ if (extraArgs.length > 0) {
2899
+ parts.push(...extraArgs);
2900
+ }
2901
+
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 findTemplate = (
428
- templatesConfig,
429
- templateName,
2909
+ const handleUpdateError = (
2910
+ error,
2911
+ packageName,
2912
+ options,
2913
+ cmdStartTime,
2914
+ logStream,
430
2915
  ) => {
431
- const template = templatesConfig.templates.find(t => t.name === templateName);
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
- if (!template) {
434
- const availableTemplates = templatesConfig.templates
435
- .map(t => t.name)
436
- .join(', ');
437
- throw new Error(
438
- `Template "${templateName}" not found.\n` +
439
- `Available templates: ${availableTemplates}\n` +
440
- 'Use --template <name> to specify a template.',
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 getTemplatePath = async (
455
- basePath,
456
- templateMetadata,
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
- // 检查是否存在 package.json
487
- const packageJsonPath = node_path.join(templatePath, 'package.json');
488
- // eslint-disable-next-line security/detect-non-literal-fs-filename
489
- if (!node_fs.existsSync(packageJsonPath)) {
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
- const result = shelljs.exec('pnpm install', {
495
- cwd: templatePath,
496
- silent: true,
497
- });
2966
+ try {
2967
+ const { global, cwd, version, registry, logFile, extraArgs } = options;
498
2968
 
499
- // 输出 stdout
500
- if (result.stdout) {
501
- process.stdout.write(result.stdout);
502
- }
2969
+ // 上报命令开始
2970
+ reportCommandStart('update', JSON.stringify({ packageName, ...options }));
503
2971
 
504
- // 输出 stderr
505
- if (result.stderr) {
506
- process.stderr.write(result.stderr);
507
- }
2972
+ // 准备日志
2973
+ const logFilePath = resolveLogFilePath$1(logFile);
508
2974
 
509
- if (result.code === 0) {
510
- logger.success(` ${templateName} warmed up successfully`);
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
- throw new Error(errorMessage);
521
- }
522
- };
2978
+ logStream = createLogStream$1(logFilePath);
523
2979
 
524
- /**
525
- * 执行 warmup 命令的内部实现
526
- */
527
- const executeWarmup = async (options) => {
528
- const timer = new TimeTracker();
2980
+ // 调试:确认流已创建
2981
+ logger.info('Log stream created successfully');
529
2982
 
530
- try {
531
- const { template: templateFilter } = options;
2983
+ logWithFile(logStream, 'info', `Updating package: ${packageName}`);
532
2984
 
533
- logger.info('Starting template warmup...');
534
- timer.logPhase('Initialization');
2985
+ // 构建命令
2986
+ const command = buildPnpmCommand(packageName, {
2987
+ global,
2988
+ version,
2989
+ registry,
2990
+ extraArgs,
2991
+ });
535
2992
 
536
- // 加载模板配置
537
- const configPath = getTemplatesConfigPath();
538
- const templatesConfig = await loadTemplatesConfig(configPath);
2993
+ // 确定工作目录
2994
+ const workingDir = cwd
2995
+ ? path.isAbsolute(cwd)
2996
+ ? cwd
2997
+ : path.join(process.cwd(), cwd)
2998
+ : process.cwd();
539
2999
 
540
- logger.verbose(
541
- `Found ${templatesConfig.templates.length} templates in config`,
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
- const templatesToWarmup = templateFilter
546
- ? templatesConfig.templates.filter(t => t.name === templateFilter)
547
- : templatesConfig.templates;
3004
+ // 记录命令开始时间
3005
+ writeLogWithTimestamp(logStream, '--- Command execution started ---');
548
3006
 
549
- if (templatesToWarmup.length === 0) {
550
- if (templateFilter) {
551
- logger.warn(`Template "${templateFilter}" not found`);
552
- logger.info(
553
- `Available templates: ${templatesConfig.templates.map(t => t.name).join(', ')}`,
554
- );
555
- } else {
556
- logger.warn('No templates found');
557
- }
558
- return;
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
- logger.info(
562
- `\nWill warm up ${templatesToWarmup.length} template(s): ${templatesToWarmup.map(t => t.name).join(', ')}`,
563
- );
3019
+ if (result.stderr) {
3020
+ process.stderr.write(result.stderr);
3021
+ writeLogWithTimestamp(logStream, result.stderr.trim());
3022
+ }
564
3023
 
565
- // 获取模板基础路径
566
- const basePath = configPath.replace(/\/templates\.json$/, '');
3024
+ // 记录命令结束时间
3025
+ writeLogWithTimestamp(logStream, '--- Command execution ended ---');
567
3026
 
568
- // 对每个模板执行 pnpm install
569
- for (const templateMetadata of templatesToWarmup) {
570
- const templatePath = await getTemplatePath(basePath, templateMetadata);
571
- warmupTemplate(templatePath, templateMetadata.name);
572
- timer.logPhase(`Warmup ${templateMetadata.name}`);
3027
+ // 检查执行结果并记录到日志
3028
+ if (result.code === 0) {
3029
+ logWithFile(logStream, 'success', 'Package updated successfully');
3030
+ logWithFile(logStream, 'info', `Log file: ${logFilePath}`);
3031
+
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
+ );
3045
+
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
+ });
573
3057
  }
574
3058
 
575
- logger.success('\n✅ All templates warmed up successfully!');
576
- logger.info(
577
- '\nNext time you run `coze init`, it will be much faster as node_modules are pre-installed.',
578
- );
579
-
580
- timer.logTotal();
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
- timer.logTotal();
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
- * 注册 warmup 命令到 program
3077
+ * 注册 update 命令到 program
591
3078
  */
592
- const registerCommand$4 = program => {
3079
+ const registerCommand$3 = program => {
593
3080
  program
594
- .command('warmup')
595
- .description('Pre-install dependencies for templates to speed up init')
596
- .option('-t, --template <name>', 'Warmup a specific template only')
597
- .action(async options => {
598
- await executeWarmup(options);
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 (options = {}) => {
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$3 = program => {
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$1 = 'dev.log';
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$1 = () =>
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$1 = (logFile) => {
3579
+ const resolveLogFilePath = (logFile) => {
1075
3580
  if (!logFile) {
1076
- return path.join(getLogDir$1(), LOG_FILE_NAME$1);
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$1(), logFile);
3588
+ return path.join(getLogDir(), logFile);
1084
3589
  };
1085
3590
 
1086
3591
  /**
1087
3592
  * 创建日志写入流
1088
3593
  */
1089
- const createLogStream$1 = (logFilePath) => {
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$1(options.logFile);
1129
- const logStream = createLogStream$1(logFilePath);
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
- logger.error(
1163
- `Command exited with code ${_nullishCoalesce$1(code, () => ( 'unknown'))}${signal ? ` and signal ${signal}` : ''}`,
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
- process.exit(code || 1);
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
- process.exit(1);
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(error instanceof Error ? error.message : String(error));
1185
- process.exit(1);
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$2 = program => {
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 阶段)
@@ -1760,7 +4306,6 @@ const detectFileConflicts = (
1760
4306
  // ABOUTME: Coordinates file system, rendering, and conflict detection layers
1761
4307
 
1762
4308
 
1763
-
1764
4309
  // start_aigc
1765
4310
  /**
1766
4311
  * 处理单个文件(准备 + 写入)
@@ -1809,7 +4354,7 @@ const processSingleFile = async (options
1809
4354
  * 1. 验证模板目录
1810
4355
  * 2. 扫描所有模板文件
1811
4356
  * 3. Dry-run:收集将要写入的文件列表(考虑 hooks 影响)
1812
- * 4. 冲突检测:检查是否有文件会被覆盖
4357
+ * 4. 冲突检测:检查是否有文件会被覆盖(可通过 force 跳过)
1813
4358
  * 5. 实际写入:渲染并写入所有文件
1814
4359
  * 6. 复制 node_modules(如果存在)
1815
4360
  *
@@ -1820,8 +4365,9 @@ const processTemplateFiles = async (options
1820
4365
 
1821
4366
 
1822
4367
 
4368
+
1823
4369
  ) => {
1824
- const { templatePath, outputPath, context, templateConfig } = options;
4370
+ const { templatePath, outputPath, context, templateConfig, force } = options;
1825
4371
  logger.verbose('Processing template files:');
1826
4372
  logger.verbose(` - Template path: ${templatePath}`);
1827
4373
  logger.verbose(` - Output path: ${outputPath}`);
@@ -1860,16 +4406,23 @@ const processTemplateFiles = async (options
1860
4406
  templateConfig,
1861
4407
  });
1862
4408
 
1863
- // 阶段 3: 冲突检测
1864
- const conflicts = detectFileConflicts(outputPath, filesToWrite);
1865
-
1866
- if (conflicts.length > 0) {
1867
- // 有冲突,抛出详细的错误信息
1868
- const conflictList = conflicts.map(f => ` - ${f}`).join('\n');
1869
- throw new Error(
1870
- `File conflicts detected in output directory: ${outputPath}\n\n` +
1871
- `The following files already exist and would be overwritten:\n${conflictList}\n\n` +
1872
- 'Please remove these files or use a different output directory.',
4409
+ // 阶段 3: 冲突检测(force 为 true 时跳过)
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 {
4424
+ logger.verbose(
4425
+ ' - Force mode enabled, skipping conflict detection. Existing files will be overwritten.',
1873
4426
  );
1874
4427
  }
1875
4428
 
@@ -1902,6 +4455,7 @@ const processTemplateFiles = async (options
1902
4455
 
1903
4456
 
1904
4457
 
4458
+
1905
4459
  /**
1906
4460
  * 加载模板元数据和路径
1907
4461
  */
@@ -2011,7 +4565,7 @@ const prepareOutputDirectory = (outputPath) => {
2011
4565
  const execute = async (
2012
4566
  options,
2013
4567
  ) => {
2014
- const { templateName, outputPath, command } = options;
4568
+ const { templateName, outputPath, command, force } = options;
2015
4569
 
2016
4570
  // 1. 加载模板
2017
4571
  const { templatePath } = await loadTemplateMetadata(templateName);
@@ -2036,6 +4590,7 @@ const execute = async (
2036
4590
  outputPath: absoluteOutputPath,
2037
4591
  context,
2038
4592
  templateConfig,
4593
+ force,
2039
4594
  });
2040
4595
 
2041
4596
  // 7. 执行 onAfterRender 钩子
@@ -2087,12 +4642,46 @@ const runPnpmInstall = (projectPath) => {
2087
4642
  };
2088
4643
 
2089
4644
  /**
2090
- * 初始化 git 仓库并创建初始提交
4645
+ * 运行 git 命令的辅助函数
4646
+ */
4647
+ const runGitCommand = (command, projectPath) => {
4648
+ logger.info(`Executing: ${command}`);
4649
+
4650
+ const result = shelljs.exec(command, {
4651
+ cwd: projectPath,
4652
+ silent: true,
4653
+ });
4654
+
4655
+ // 输出命令的结果
4656
+ if (result.stdout) {
4657
+ process.stdout.write(result.stdout);
4658
+ }
4659
+
4660
+ if (result.stderr) {
4661
+ process.stderr.write(result.stderr);
4662
+ }
4663
+
4664
+ if (result.code !== 0) {
4665
+ const errorMessage = [
4666
+ `${command} failed with exit code ${result.code}`,
4667
+ result.stderr ? `\nStderr:\n${result.stderr}` : '',
4668
+ result.stdout ? `\nStdout:\n${result.stdout}` : '',
4669
+ ]
4670
+ .filter(Boolean)
4671
+ .join('');
4672
+
4673
+ throw new Error(errorMessage);
4674
+ }
4675
+ };
4676
+
4677
+ /**
4678
+ * 初始化 git 仓库
2091
4679
  * 如果目录中已存在 .git,则跳过初始化
2092
4680
  */
2093
4681
  const runGitInit = (projectPath) => {
2094
4682
  // 检查是否已存在 .git 目录
2095
4683
  const gitDir = path.join(projectPath, '.git');
4684
+
2096
4685
  if (fs.existsSync(gitDir)) {
2097
4686
  logger.info(
2098
4687
  '\n💡 Git repository already exists, skipping git initialization',
@@ -2100,53 +4689,43 @@ const runGitInit = (projectPath) => {
2100
4689
  return;
2101
4690
  }
2102
4691
 
2103
- const runGitCommand = (command) => {
2104
- logger.info(`Executing: ${command}`);
2105
-
2106
- const result = shelljs.exec(command, {
2107
- cwd: projectPath,
2108
- silent: true,
2109
- });
2110
-
2111
- // 输出命令的结果
2112
- if (result.stdout) {
2113
- process.stdout.write(result.stdout);
2114
- }
2115
-
2116
- if (result.stderr) {
2117
- process.stderr.write(result.stderr);
2118
- }
2119
-
2120
- if (result.code !== 0) {
2121
- const errorMessage = [
2122
- `${command} failed with exit code ${result.code}`,
2123
- result.stderr ? `\nStderr:\n${result.stderr}` : '',
2124
- result.stdout ? `\nStdout:\n${result.stdout}` : '',
2125
- ]
2126
- .filter(Boolean)
2127
- .join('');
2128
-
2129
- throw new Error(errorMessage);
2130
- }
2131
- };
2132
-
2133
4692
  try {
2134
4693
  logger.info('\nInitializing git repository...');
2135
- runGitCommand('git init');
2136
-
2137
- logger.info('Adding files to git...');
2138
- runGitCommand('git add .');
2139
-
2140
- logger.info('Creating initial commit...');
2141
- runGitCommand('git commit -m "chore: initial commit"');
2142
-
4694
+ runGitCommand('git init', projectPath);
2143
4695
  logger.success('Git repository initialized successfully!');
2144
4696
  } catch (error) {
2145
4697
  // Git 初始化失败不应该导致整个流程失败
4698
+ const errorMessage = error instanceof Error ? error.message : String(error);
4699
+ logger.warn(`Git initialization failed: ${errorMessage}`);
4700
+ logger.info('You can manually initialize git later with: git init');
4701
+ }
4702
+ };
4703
+
4704
+ /**
4705
+ * 提交初始化生成的所有文件
4706
+ */
4707
+ const commitChanges = (projectPath) => {
4708
+ // 检查是否存在 .git 目录
4709
+ const gitDir = path.join(projectPath, '.git');
4710
+ if (!fs.existsSync(gitDir)) {
2146
4711
  logger.warn(
2147
- `Git initialization failed: ${error instanceof Error ? error.message : String(error)}`,
4712
+ '\n⚠️ Git repository does not exist, skipping commit. Run git init first.',
4713
+ );
4714
+ return;
4715
+ }
4716
+
4717
+ try {
4718
+ logger.info('\nCommitting initialized files...');
4719
+ runGitCommand('git add --all', projectPath);
4720
+ runGitCommand('git commit -m "chore: init env"', projectPath);
4721
+ logger.success('Changes committed successfully!');
4722
+ } catch (error) {
4723
+ // Commit 失败不应该导致整个流程失败
4724
+ const errorMessage = error instanceof Error ? error.message : String(error);
4725
+ logger.warn(`Git commit failed: ${errorMessage}`);
4726
+ logger.info(
4727
+ 'You can manually commit later with: git add --all && git commit -m "chore: init env"',
2148
4728
  );
2149
- logger.info('You can manually initialize git later with: git init');
2150
4729
  }
2151
4730
  };
2152
4731
 
@@ -2164,290 +4743,217 @@ const runDev = (projectPath) => {
2164
4743
 
2165
4744
  logger.info(`Executing: ${cliPath} dev in ${projectPath}`);
2166
4745
 
2167
- // 使用通用的后台执行函数启动开发服务器
2168
- // 调用 CLI 自己的 dev 命令
2169
- const pid = spawnDetached(process.argv[0], [cliPath, 'dev'], {
2170
- cwd: projectPath,
2171
- verbose: false, // 不输出额外的进程信息,由 logger 统一处理
2172
- });
4746
+ try {
4747
+ // 使用通用的后台执行函数启动开发服务器
4748
+ // 调用 CLI 自己的 dev 命令
4749
+ const pid = spawnDetached(process.argv[0], [cliPath, 'dev'], {
4750
+ cwd: projectPath,
4751
+ verbose: false, // 不输出额外的进程信息,由 logger 统一处理
4752
+ });
2173
4753
 
2174
- logger.success('Development server started in background!');
2175
- if (pid) {
2176
- logger.info(`Process ID: ${pid}`);
2177
- logger.info(
2178
- '\nThe dev server is running independently. You can close this terminal.',
2179
- );
2180
- logger.info(`To stop the server later, use: kill ${pid}`);
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}`);
2181
4767
  }
2182
4768
  };
2183
4769
 
2184
4770
  /**
2185
- * 执行 init 命令的内部实现
4771
+ * Init 命令执行上下文
2186
4772
  */
2187
- const executeInit = async (
2188
- options
2189
4773
 
2190
4774
 
2191
4775
 
2192
4776
 
2193
4777
 
2194
- ,
2195
- command,
2196
- ) => {
2197
- const timer = new TimeTracker();
2198
4778
 
2199
- try {
2200
- const {
2201
- template: templateName,
2202
- output: outputPath,
2203
- skipInstall,
2204
- skipGit,
2205
- skipDev,
2206
- } = options;
2207
-
2208
- logger.info(`Initializing project with template: ${templateName}`);
2209
- timer.logPhase('Initialization');
2210
4779
 
2211
- // 执行模板引擎,返回结果对象
2212
- const result = await execute({
2213
- templateName,
2214
- outputPath,
2215
- command,
2216
- });
2217
- const { outputPath: absoluteOutputPath, templateConfig, context } = result;
2218
4780
 
2219
- timer.logPhase('Template engine execution');
2220
- logger.success('Project created successfully!');
2221
4781
 
2222
- // 检查是否存在 package.json
2223
- const packageJsonPath = path.join(absoluteOutputPath, 'package.json');
2224
- const hasPackageJson = fs.existsSync(packageJsonPath);
2225
4782
 
2226
- // 安装依赖(始终使用 pnpm install,利用缓存机制)
2227
- if (!skipInstall) {
2228
- if (hasPackageJson) {
2229
- runPnpmInstall(absoluteOutputPath);
2230
- timer.logPhase('Dependencies installation');
2231
- } else {
2232
- logger.info(
2233
- '\n💡 No package.json found, skipping dependency installation',
2234
- );
2235
- }
2236
- }
2237
4783
 
2238
- // 执行 onComplete 钩子(在 pnpm install 之后)
2239
- await executeCompleteHook(templateConfig, context, absoluteOutputPath);
2240
- timer.logPhase('Complete hook execution');
2241
4784
 
2242
- // 如果没有跳过 git,则初始化 git 仓库
2243
- if (!skipGit) {
2244
- runGitInit(absoluteOutputPath);
2245
- timer.logPhase('Git initialization');
2246
- }
2247
4785
 
2248
- // 如果没有跳过 dev,则启动开发服务器
2249
- if (!skipDev) {
2250
- runDev(absoluteOutputPath);
2251
- timer.logPhase('Dev server startup');
2252
- } else {
2253
- // 只有跳过 dev 时才显示 Next steps
2254
- logger.info('\nNext steps:');
2255
- logger.info(` cd ${outputPath}`);
2256
- if (skipInstall && hasPackageJson) {
2257
- logger.info(' pnpm install');
2258
- }
2259
- if (skipGit) {
2260
- logger.info(
2261
- ' git init && git add . && git commit -m "initial commit"',
2262
- );
2263
- }
2264
- logger.info(' coze dev');
2265
- }
2266
4786
 
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
4787
 
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('--skip-dev', 'Skip automatic dev server start', false)
2290
- .allowUnknownOption() // 允许透传参数
2291
- .action(async (directory, options, command) => {
2292
- // 位置参数优先级高于 --output 选项
2293
- const outputPath = _nullishCoalesce(directory, () => ( options.output));
2294
- await executeInit({ ...options, output: outputPath }, command);
2295
- });
2296
- };
2297
4788
 
2298
- // ABOUTME: This file implements the update command for coze CLI
2299
- // ABOUTME: It wraps pnpm update/install to update package dependencies with logging support
2300
4789
 
2301
4790
 
2302
4791
 
2303
4792
 
2304
- /**
2305
- * 日志文件名常量
2306
- */
2307
- const LOG_FILE_NAME = 'update.log';
2308
4793
 
2309
- /**
2310
- * 获取日志目录
2311
- * 优先使用环境变量 COZE_LOG_DIR,否则使用 ~/.coze-logs
2312
- */
2313
- const getLogDir = () =>
2314
- process.env.COZE_LOG_DIR || path.join(os.homedir(), '.coze-logs');
2315
4794
 
2316
- /**
2317
- * 解析日志文件路径
2318
- * - 如果是绝对路径,直接使用
2319
- * - 如果是相对路径,基于 getLogDir() + 相对路径
2320
- * - 如果为空,使用 getLogDir() + LOG_FILE_NAME
2321
- */
2322
- const resolveLogFilePath = (logFile) => {
2323
- if (!logFile) {
2324
- return path.join(getLogDir(), LOG_FILE_NAME);
2325
- }
2326
4795
 
2327
- if (path.isAbsolute(logFile)) {
2328
- return logFile;
2329
- }
2330
4796
 
2331
- return path.join(getLogDir(), logFile);
2332
- };
4797
+
4798
+
4799
+
4800
+
4801
+
4802
+
2333
4803
 
2334
4804
  /**
2335
- * 创建日志写入流
4805
+ * 步骤1: 执行模板引擎
2336
4806
  */
2337
- const createLogStream = (logFilePath) => {
2338
- const logDir = path.dirname(logFilePath);
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
+ });
2339
4817
 
2340
- // 确保日志目录存在
2341
- if (!fs.existsSync(logDir)) {
2342
- fs.mkdirSync(logDir, { recursive: true });
2343
- }
4818
+ // 保存结果到上下文
4819
+ ctx.absoluteOutputPath = result.outputPath;
4820
+ ctx.templateConfig = result.templateConfig;
4821
+ ctx.context = result.context;
2344
4822
 
2345
- // 使用 'w' 标志覆盖之前的日志
2346
- return fs.createWriteStream(logFilePath, { flags: 'w' });
4823
+ ctx.timer.logPhase('Template engine execution');
4824
+ logger.success('Project created successfully!');
2347
4825
  };
2348
4826
 
2349
4827
  /**
2350
- * 格式化时间戳
4828
+ * 步骤2: 检查 package.json 是否存在
2351
4829
  */
2352
- const formatTimestamp = () => {
2353
- const now = new Date();
2354
- return now.toISOString();
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);
2355
4837
  };
2356
4838
 
2357
4839
  /**
2358
- * 写入带时间戳的日志
4840
+ * 步骤3: 安装依赖
2359
4841
  */
2360
- const writeLogWithTimestamp = (stream, message) => {
2361
- const timestamp = formatTimestamp();
2362
- const lines = message.split('\n');
2363
- lines.forEach(line => {
2364
- if (line) {
2365
- stream.write(`[${timestamp}] ${line}\n`);
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');
2366
4853
  } else {
2367
- stream.write('\n');
4854
+ logger.info(
4855
+ '\n💡 No package.json found, skipping dependency installation',
4856
+ );
2368
4857
  }
2369
- });
2370
- // 确保数据写入磁盘
2371
- stream.uncork();
4858
+ }
2372
4859
  };
2373
4860
 
2374
4861
  /**
2375
- * 同时输出到控制台和日志文件
4862
+ * 步骤4: 执行完成钩子
2376
4863
  */
2377
- const logWithFile = (
2378
- stream,
2379
- level,
2380
- message,
2381
- ) => {
2382
- // 输出到控制台
2383
- switch (level) {
2384
- case 'info':
2385
- logger.info(message);
2386
- break;
2387
- case 'success':
2388
- logger.success(message);
2389
- break;
2390
- case 'error':
2391
- logger.error(message);
2392
- break;
2393
- default:
2394
- logger.info(message);
2395
- break;
4864
+ const executeCompleteHookStep = async ctx => {
4865
+ if (!ctx.absoluteOutputPath || !ctx.templateConfig || !ctx.context) {
4866
+ return;
2396
4867
  }
2397
4868
 
2398
- // 写入日志文件(带时间戳)
2399
- writeLogWithTimestamp(stream, `[${level.toUpperCase()}] ${message}`);
4869
+ await executeCompleteHook(
4870
+ ctx.templateConfig,
4871
+ ctx.context,
4872
+ ctx.absoluteOutputPath,
4873
+ );
4874
+
4875
+ ctx.timer.logPhase('Complete hook execution');
2400
4876
  };
2401
4877
 
2402
- // start_aigc
2403
4878
  /**
2404
- * 构建 pnpm add 命令
4879
+ * 步骤5: 初始化 Git 仓库
2405
4880
  */
2406
- const buildPnpmCommand = (
2407
- packageName,
2408
- options
2409
-
4881
+ const gitInitStep = ctx => {
4882
+ if (!ctx.absoluteOutputPath) {
4883
+ return;
4884
+ }
2410
4885
 
4886
+ const { skipGit } = ctx.options;
2411
4887
 
4888
+ if (!skipGit) {
4889
+ runGitInit(ctx.absoluteOutputPath);
4890
+ ctx.timer.logPhase('Git initialization');
4891
+ }
4892
+ };
2412
4893
 
2413
- ,
2414
- ) => {
2415
- const { global, version, registry, extraArgs } = options;
4894
+ /**
4895
+ * 步骤6: 提交 Git 变更
4896
+ */
4897
+ const gitCommitStep = ctx => {
4898
+ if (!ctx.absoluteOutputPath) {
4899
+ return;
4900
+ }
2416
4901
 
2417
- const parts = ['pnpm', 'add'];
4902
+ const { skipCommit } = ctx.options;
2418
4903
 
2419
- // 添加全局标记
2420
- if (global) {
2421
- parts.push('-g');
4904
+ if (!skipCommit) {
4905
+ commitChanges(ctx.absoluteOutputPath);
4906
+ ctx.timer.logPhase('Git commit');
2422
4907
  }
4908
+ };
2423
4909
 
2424
- // 添加包名和版本
2425
- if (version && version !== 'latest') {
2426
- parts.push(`${packageName}@${version}`);
2427
- } else {
2428
- parts.push(`${packageName}@latest`);
4910
+ /**
4911
+ * 步骤7: 启动开发服务器
4912
+ */
4913
+ const startDevServerStep = ctx => {
4914
+ if (!ctx.absoluteOutputPath) {
4915
+ return;
2429
4916
  }
2430
4917
 
2431
- // 添加 registry
2432
- if (registry) {
2433
- parts.push(`--registry=${registry}`);
2434
- }
4918
+ const { skipDev, skipInstall, skipGit, skipCommit } = ctx.options;
2435
4919
 
2436
- // 添加额外参数
2437
- if (extraArgs.length > 0) {
2438
- parts.push(...extraArgs);
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');
2439
4937
  }
2440
-
2441
- return parts.join(' ');
2442
4938
  };
2443
- // end_aigc
2444
4939
 
2445
- // start_aigc
2446
4940
  /**
2447
- * 执行 update 命令的内部实现
4941
+ * Init 命令的所有执行步骤
2448
4942
  */
2449
- const executeUpdate = (
2450
- packageName,
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 (
2451
4957
  options
2452
4958
 
2453
4959
 
@@ -2455,155 +4961,132 @@ const executeUpdate = (
2455
4961
 
2456
4962
 
2457
4963
 
4964
+
2458
4965
  ,
4966
+ command,
2459
4967
  ) => {
2460
- let logStream = null;
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
+ };
2461
4980
 
2462
4981
  try {
2463
- const { global, cwd, version, registry, logFile, extraArgs } = options;
2464
-
2465
- // 准备日志
2466
- const logFilePath = resolveLogFilePath(logFile);
2467
-
2468
- // 调试:确认日志路径
2469
- logger.info(`Log file path resolved to: ${logFilePath}`);
2470
-
2471
- logStream = createLogStream(logFilePath);
4982
+ // 上报命令开始
4983
+ reportCommandStart(
4984
+ 'init',
4985
+ JSON.stringify({ template: ctx.templateName, output: ctx.outputPath }),
4986
+ { template: ctx.templateName },
4987
+ );
2472
4988
 
2473
- // 调试:确认流已创建
2474
- logger.info('Log stream created successfully');
4989
+ // 逐个执行步骤
4990
+ for (const step of initSteps) {
4991
+ await step(ctx);
4992
+ }
2475
4993
 
2476
- logWithFile(logStream, 'info', `Updating package: ${packageName}`);
4994
+ // 输出总耗时
4995
+ timer.logTotal();
2477
4996
 
2478
- // 构建命令
2479
- const command = buildPnpmCommand(packageName, {
2480
- global,
2481
- version,
2482
- registry,
2483
- extraArgs,
4997
+ // 上报命令成功
4998
+ reportCommandComplete('init', true, Date.now() - cmdStartTime, {
4999
+ args: JSON.stringify(options),
5000
+ categories: { template: ctx.templateName },
2484
5001
  });
5002
+ // flush 由 main 函数统一处理
5003
+ } catch (error) {
5004
+ timer.logTotal();
2485
5005
 
2486
- // 确定工作目录
2487
- const workingDir = cwd
2488
- ? path.isAbsolute(cwd)
2489
- ? cwd
2490
- : path.join(process.cwd(), cwd)
2491
- : process.cwd();
2492
-
2493
- logWithFile(logStream, 'info', `Executing: ${command}`);
2494
- logWithFile(logStream, 'info', `Working directory: ${workingDir}`);
2495
- logWithFile(logStream, 'info', `Log file: ${logFilePath}`);
2496
-
2497
- // 记录命令开始时间
2498
- writeLogWithTimestamp(logStream, '--- Command execution started ---');
2499
-
2500
- // 同步执行命令
2501
- const result = shelljs.exec(command, {
2502
- cwd: workingDir,
2503
- 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,
2504
5012
  });
2505
5013
 
2506
- // 将输出写入控制台和日志文件(带时间戳)
2507
- if (result.stdout) {
2508
- process.stdout.write(result.stdout);
2509
- writeLogWithTimestamp(logStream, result.stdout.trim());
2510
- }
2511
-
2512
- if (result.stderr) {
2513
- process.stderr.write(result.stderr);
2514
- writeLogWithTimestamp(logStream, result.stderr.trim());
2515
- }
2516
-
2517
- // 记录命令结束时间
2518
- writeLogWithTimestamp(logStream, '--- Command execution ended ---');
2519
-
2520
- // 检查执行结果并记录到日志
2521
- if (result.code === 0) {
2522
- logWithFile(logStream, 'success', 'Package updated successfully');
2523
- logWithFile(logStream, 'info', `Log file: ${logFilePath}`);
2524
- } else {
2525
- logWithFile(
2526
- logStream,
2527
- 'error',
2528
- `Command exited with code ${result.code}`,
2529
- );
2530
- logWithFile(
2531
- logStream,
2532
- 'error',
2533
- `Check log file for details: ${logFilePath}`,
2534
- );
2535
- }
2536
-
2537
- // 关闭日志流并等待写入完成
2538
- logStream.end(() => {
2539
- // 流关闭后再退出进程
2540
- if (result.code !== 0) {
2541
- process.exit(result.code || 1);
2542
- }
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 },
2543
5020
  });
2544
- } catch (error) {
2545
- logger.error('Failed to update package:');
2546
- logger.error(error instanceof Error ? error.message : String(error));
5021
+ await flushSlardar();
2547
5022
 
2548
- // 写入错误到日志文件
2549
- if (logStream) {
2550
- writeLogWithTimestamp(
2551
- logStream,
2552
- `[ERROR] ${error instanceof Error ? error.message : String(error)}`,
2553
- );
2554
- // 等待流关闭后再退出
2555
- logStream.end(() => {
2556
- process.exit(1);
2557
- });
2558
- } else {
2559
- process.exit(1);
2560
- }
5023
+ logger.error('Failed to initialize project:');
5024
+ logger.error(err.message);
5025
+ process.exit(1);
2561
5026
  }
2562
5027
  };
2563
- // end_aigc
2564
5028
 
2565
5029
  /**
2566
- * 注册 update 命令到 program
5030
+ * 注册 init 命令到 program
2567
5031
  */
2568
5032
  const registerCommand = program => {
2569
5033
  program
2570
- .command('update <package>')
2571
- .description('Update a package dependency')
2572
- .option('-g, --global', 'Update package globally', false)
2573
- .option('-c, --cwd <path>', 'Working directory for the update')
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)
2574
5041
  .option(
2575
- '--to <version>',
2576
- 'Version to update to (default: latest)',
2577
- 'latest',
5042
+ '--skip-commit',
5043
+ 'Skip automatic git commit after initialization',
5044
+ false,
2578
5045
  )
2579
- .option('--registry <url>', 'Registry URL to use for the update')
2580
- .option('--log-file <path>', 'Log file path')
2581
- .allowUnknownOption() // 允许透传参数给 pnpm
2582
- .action((packageName, options, command) => {
2583
- // 收集所有未知选项作为额外参数
2584
- const extraArgs = command.args.slice(1);
2585
-
2586
- executeUpdate(packageName, {
2587
- ...options,
2588
- version: options.to, // 将 --to 映射到 version
2589
- extraArgs,
2590
- });
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);
2591
5054
  });
2592
5055
  };
2593
5056
 
2594
- var version = "0.0.1-alpha.db1c06";
2595
- var packageJson = {
2596
- version: version};
2597
-
2598
5057
  const commands = [
5058
+ registerCommand,
2599
5059
  registerCommand$1,
2600
- registerCommand$2,
2601
5060
  registerCommand$4,
5061
+ registerCommand$2,
2602
5062
  registerCommand$3,
2603
- registerCommand,
2604
5063
  ];
2605
5064
 
2606
- 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
+
2607
5090
  const program = new commander.Command();
2608
5091
 
2609
5092
  program
@@ -2618,7 +5101,12 @@ const main = () => {
2618
5101
  // 在 help 输出中添加模板信息
2619
5102
  program.addHelpText('after', generateTemplatesHelpText());
2620
5103
 
2621
- program.parse(process.argv);
5104
+ // 使用 parseAsync 等待命令执行完成
5105
+ await program.parseAsync(process.argv);
5106
+
5107
+ // 统一在命令执行完成后 flush
5108
+ // 注意:如果命令中调用了 process.exit(),不会执行到这里
5109
+ await flushSlardar();
2622
5110
  };
2623
5111
 
2624
5112
  main();