@deepstorm/cli 0.10.2 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1187,13 +1187,13 @@ var require_ast = __commonJS({
1187
1187
  helperExpression: function helperExpression(node) {
1188
1188
  return node.type === "SubExpression" || (node.type === "MustacheStatement" || node.type === "BlockStatement") && !!(node.params && node.params.length || node.hash);
1189
1189
  },
1190
- scopedId: function scopedId(path24) {
1191
- return /^\.|this\b/.test(path24.original);
1190
+ scopedId: function scopedId(path26) {
1191
+ return /^\.|this\b/.test(path26.original);
1192
1192
  },
1193
1193
  // an ID is simple if it only has one part, and that part is not
1194
1194
  // `..` or `this`.
1195
- simpleId: function simpleId(path24) {
1196
- return path24.parts.length === 1 && !AST.helpers.scopedId(path24) && !path24.depth;
1195
+ simpleId: function simpleId(path26) {
1196
+ return path26.parts.length === 1 && !AST.helpers.scopedId(path26) && !path26.depth;
1197
1197
  }
1198
1198
  }
1199
1199
  };
@@ -2263,12 +2263,12 @@ var require_helpers2 = __commonJS({
2263
2263
  loc
2264
2264
  };
2265
2265
  }
2266
- function prepareMustache(path24, params, hash, open, strip, locInfo) {
2266
+ function prepareMustache(path26, params, hash, open, strip, locInfo) {
2267
2267
  var escapeFlag = open.charAt(3) || open.charAt(2), escaped = escapeFlag !== "{" && escapeFlag !== "&";
2268
2268
  var decorator = /\*/.test(open);
2269
2269
  return {
2270
2270
  type: decorator ? "Decorator" : "MustacheStatement",
2271
- path: path24,
2271
+ path: path26,
2272
2272
  params,
2273
2273
  hash,
2274
2274
  escaped,
@@ -2586,9 +2586,9 @@ var require_compiler = __commonJS({
2586
2586
  },
2587
2587
  DecoratorBlock: function DecoratorBlock(decorator) {
2588
2588
  var program2 = decorator.program && this.compileProgram(decorator.program);
2589
- var params = this.setupFullMustacheParams(decorator, program2, void 0), path24 = decorator.path;
2589
+ var params = this.setupFullMustacheParams(decorator, program2, void 0), path26 = decorator.path;
2590
2590
  this.useDecorators = true;
2591
- this.opcode("registerDecorator", params.length, path24.original);
2591
+ this.opcode("registerDecorator", params.length, path26.original);
2592
2592
  },
2593
2593
  PartialStatement: function PartialStatement(partial) {
2594
2594
  this.usePartial = true;
@@ -2652,46 +2652,46 @@ var require_compiler = __commonJS({
2652
2652
  }
2653
2653
  },
2654
2654
  ambiguousSexpr: function ambiguousSexpr(sexpr, program2, inverse) {
2655
- var path24 = sexpr.path, name = path24.parts[0], isBlock = program2 != null || inverse != null;
2656
- this.opcode("getContext", path24.depth);
2655
+ var path26 = sexpr.path, name = path26.parts[0], isBlock = program2 != null || inverse != null;
2656
+ this.opcode("getContext", path26.depth);
2657
2657
  this.opcode("pushProgram", program2);
2658
2658
  this.opcode("pushProgram", inverse);
2659
- path24.strict = true;
2660
- this.accept(path24);
2659
+ path26.strict = true;
2660
+ this.accept(path26);
2661
2661
  this.opcode("invokeAmbiguous", name, isBlock);
2662
2662
  },
2663
2663
  simpleSexpr: function simpleSexpr(sexpr) {
2664
- var path24 = sexpr.path;
2665
- path24.strict = true;
2666
- this.accept(path24);
2664
+ var path26 = sexpr.path;
2665
+ path26.strict = true;
2666
+ this.accept(path26);
2667
2667
  this.opcode("resolvePossibleLambda");
2668
2668
  },
2669
2669
  helperSexpr: function helperSexpr(sexpr, program2, inverse) {
2670
- var params = this.setupFullMustacheParams(sexpr, program2, inverse), path24 = sexpr.path, name = path24.parts[0];
2670
+ var params = this.setupFullMustacheParams(sexpr, program2, inverse), path26 = sexpr.path, name = path26.parts[0];
2671
2671
  if (this.options.knownHelpers[name]) {
2672
2672
  this.opcode("invokeKnownHelper", params.length, name);
2673
2673
  } else if (this.options.knownHelpersOnly) {
2674
2674
  throw new _exception2["default"]("You specified knownHelpersOnly, but used the unknown helper " + name, sexpr);
2675
2675
  } else {
2676
- path24.strict = true;
2677
- path24.falsy = true;
2678
- this.accept(path24);
2679
- this.opcode("invokeHelper", params.length, path24.original, _ast2["default"].helpers.simpleId(path24));
2676
+ path26.strict = true;
2677
+ path26.falsy = true;
2678
+ this.accept(path26);
2679
+ this.opcode("invokeHelper", params.length, path26.original, _ast2["default"].helpers.simpleId(path26));
2680
2680
  }
2681
2681
  },
2682
- PathExpression: function PathExpression(path24) {
2683
- this.addDepth(path24.depth);
2684
- this.opcode("getContext", path24.depth);
2685
- var name = path24.parts[0], scoped = _ast2["default"].helpers.scopedId(path24), blockParamId = !path24.depth && !scoped && this.blockParamIndex(name);
2682
+ PathExpression: function PathExpression(path26) {
2683
+ this.addDepth(path26.depth);
2684
+ this.opcode("getContext", path26.depth);
2685
+ var name = path26.parts[0], scoped = _ast2["default"].helpers.scopedId(path26), blockParamId = !path26.depth && !scoped && this.blockParamIndex(name);
2686
2686
  if (blockParamId) {
2687
- this.opcode("lookupBlockParam", blockParamId, path24.parts);
2687
+ this.opcode("lookupBlockParam", blockParamId, path26.parts);
2688
2688
  } else if (!name) {
2689
2689
  this.opcode("pushContext");
2690
- } else if (path24.data) {
2690
+ } else if (path26.data) {
2691
2691
  this.options.data = true;
2692
- this.opcode("lookupData", path24.depth, path24.parts, path24.strict);
2692
+ this.opcode("lookupData", path26.depth, path26.parts, path26.strict);
2693
2693
  } else {
2694
- this.opcode("lookupOnContext", path24.parts, path24.falsy, path24.strict, scoped);
2694
+ this.opcode("lookupOnContext", path26.parts, path26.falsy, path26.strict, scoped);
2695
2695
  }
2696
2696
  },
2697
2697
  StringLiteral: function StringLiteral(string) {
@@ -3041,16 +3041,16 @@ var require_util = __commonJS({
3041
3041
  }
3042
3042
  exports2.urlGenerate = urlGenerate;
3043
3043
  function normalize(aPath) {
3044
- var path24 = aPath;
3044
+ var path26 = aPath;
3045
3045
  var url = urlParse(aPath);
3046
3046
  if (url) {
3047
3047
  if (!url.path) {
3048
3048
  return aPath;
3049
3049
  }
3050
- path24 = url.path;
3050
+ path26 = url.path;
3051
3051
  }
3052
- var isAbsolute = exports2.isAbsolute(path24);
3053
- var parts = path24.split(/\/+/);
3052
+ var isAbsolute = exports2.isAbsolute(path26);
3053
+ var parts = path26.split(/\/+/);
3054
3054
  for (var part, up = 0, i = parts.length - 1; i >= 0; i--) {
3055
3055
  part = parts[i];
3056
3056
  if (part === ".") {
@@ -3067,18 +3067,18 @@ var require_util = __commonJS({
3067
3067
  }
3068
3068
  }
3069
3069
  }
3070
- path24 = parts.join("/");
3071
- if (path24 === "") {
3072
- path24 = isAbsolute ? "/" : ".";
3070
+ path26 = parts.join("/");
3071
+ if (path26 === "") {
3072
+ path26 = isAbsolute ? "/" : ".";
3073
3073
  }
3074
3074
  if (url) {
3075
- url.path = path24;
3075
+ url.path = path26;
3076
3076
  return urlGenerate(url);
3077
3077
  }
3078
- return path24;
3078
+ return path26;
3079
3079
  }
3080
3080
  exports2.normalize = normalize;
3081
- function join22(aRoot, aPath) {
3081
+ function join24(aRoot, aPath) {
3082
3082
  if (aRoot === "") {
3083
3083
  aRoot = ".";
3084
3084
  }
@@ -3110,7 +3110,7 @@ var require_util = __commonJS({
3110
3110
  }
3111
3111
  return joined;
3112
3112
  }
3113
- exports2.join = join22;
3113
+ exports2.join = join24;
3114
3114
  exports2.isAbsolute = function(aPath) {
3115
3115
  return aPath.charAt(0) === "/" || urlRegexp.test(aPath);
3116
3116
  };
@@ -3283,7 +3283,7 @@ var require_util = __commonJS({
3283
3283
  parsed.path = parsed.path.substring(0, index + 1);
3284
3284
  }
3285
3285
  }
3286
- sourceURL = join22(urlGenerate(parsed), sourceURL);
3286
+ sourceURL = join24(urlGenerate(parsed), sourceURL);
3287
3287
  }
3288
3288
  return normalize(sourceURL);
3289
3289
  }
@@ -5858,8 +5858,8 @@ var require_printer = __commonJS({
5858
5858
  return this.accept(sexpr.path) + " " + params + hash;
5859
5859
  };
5860
5860
  PrintVisitor.prototype.PathExpression = function(id) {
5861
- var path24 = id.parts.join("/");
5862
- return (id.data ? "@" : "") + "PATH:" + path24;
5861
+ var path26 = id.parts.join("/");
5862
+ return (id.data ? "@" : "") + "PATH:" + path26;
5863
5863
  };
5864
5864
  PrintVisitor.prototype.StringLiteral = function(string) {
5865
5865
  return '"' + string.value + '"';
@@ -5898,8 +5898,8 @@ var require_lib = __commonJS({
5898
5898
  handlebars.print = printer.print;
5899
5899
  module2.exports = handlebars;
5900
5900
  function extension(module3, filename) {
5901
- var fs28 = require("fs");
5902
- var templateString = fs28.readFileSync(filename, "utf8");
5901
+ var fs30 = require("fs");
5902
+ var templateString = fs30.readFileSync(filename, "utf8");
5903
5903
  module3.exports = handlebars.compile(templateString);
5904
5904
  }
5905
5905
  if (typeof require !== "undefined" && require.extensions) {
@@ -5911,8 +5911,8 @@ var require_lib = __commonJS({
5911
5911
 
5912
5912
  // src/index.ts
5913
5913
  var import_commander = require("commander");
5914
- var fs27 = __toESM(require("node:fs"));
5915
- var path23 = __toESM(require("node:path"));
5914
+ var fs29 = __toESM(require("node:fs"));
5915
+ var path25 = __toESM(require("node:path"));
5916
5916
 
5917
5917
  // src/commands/setup.ts
5918
5918
  var path9 = __toESM(require("node:path"));
@@ -10507,13 +10507,380 @@ function registerPilotCommands(program2) {
10507
10507
  (0, import_pilot.registerPilotCommands)(program2);
10508
10508
  }
10509
10509
 
10510
- // src/commands/doctor.ts
10511
- var fs25 = __toESM(require("node:fs"));
10510
+ // src/record/browser-recorder.ts
10511
+ var path22 = __toESM(require("node:path"));
10512
+ var fs26 = __toESM(require("node:fs"));
10513
+ var import_playwright = require("playwright");
10514
+
10515
+ // src/record/recorder.ts
10512
10516
  var path21 = __toESM(require("node:path"));
10517
+ var fs25 = __toESM(require("node:fs"));
10518
+ function filterNoiseEvents(events) {
10519
+ const noiseTypes = [];
10520
+ const noiseSet = new Set(noiseTypes);
10521
+ return events.filter((e) => !noiseSet.has(e.type));
10522
+ }
10523
+ function eventGroupKey(e) {
10524
+ const selector = e.data?.cssSelector || "";
10525
+ return { cssSelector: selector, type: e.type };
10526
+ }
10527
+ function dedupEvents(events) {
10528
+ if (events.length === 0) return [];
10529
+ const result = [];
10530
+ const inputElements = /* @__PURE__ */ new Map();
10531
+ for (let i = 0; i < events.length; i++) {
10532
+ const current = events[i];
10533
+ if (current.type === "input" || current.type === "select") {
10534
+ const key = eventGroupKey(current);
10535
+ const dedupKey = `${key.cssSelector}|${key.type}`;
10536
+ inputElements.set(dedupKey, current);
10537
+ continue;
10538
+ }
10539
+ if (current.type === "click") {
10540
+ const lastEvent = result.length > 0 ? result[result.length - 1] : null;
10541
+ if (lastEvent && lastEvent.type === "click") {
10542
+ const currentKey = eventGroupKey(current);
10543
+ const lastKey = eventGroupKey(lastEvent);
10544
+ const sameElement = currentKey.cssSelector === lastKey.cssSelector;
10545
+ const withinThreshold = current.ts - lastEvent.ts <= 500;
10546
+ if (sameElement && withinThreshold) {
10547
+ continue;
10548
+ }
10549
+ }
10550
+ }
10551
+ result.push(current);
10552
+ }
10553
+ inputElements.forEach((event) => {
10554
+ result.push(event);
10555
+ });
10556
+ result.sort((a, b) => a.ts - b.ts);
10557
+ return result;
10558
+ }
10559
+ function serializeRecording(data) {
10560
+ return JSON.stringify(data, null, 2);
10561
+ }
10562
+ function buildRecordingFileName(seq = 1) {
10563
+ const now = /* @__PURE__ */ new Date();
10564
+ const pad = (n) => String(n).padStart(2, "0");
10565
+ const dateTime = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
10566
+ const seqStr = seq > 1 ? `-${seq}` : "";
10567
+ return `${dateTime}${seqStr}.recording.json`;
10568
+ }
10569
+ function recordingsDir() {
10570
+ return path21.join(process.cwd(), ".deepstorm", "recordings");
10571
+ }
10572
+ function getNextSeq(datePrefix) {
10573
+ const dir = recordingsDir();
10574
+ if (!fs25.existsSync(dir)) return 1;
10575
+ const files = fs25.readdirSync(dir).filter((f) => f.startsWith(datePrefix));
10576
+ if (files.length === 0) return 1;
10577
+ let maxSeq = 0;
10578
+ for (const f of files) {
10579
+ const match = f.match(/(?:-(\d+))?\.recording\.json$/);
10580
+ if (match) {
10581
+ const s = match[1] ? parseInt(match[1], 10) : 1;
10582
+ maxSeq = Math.max(maxSeq, s);
10583
+ }
10584
+ }
10585
+ return maxSeq + 1;
10586
+ }
10587
+
10588
+ // src/record/browser-recorder.ts
10589
+ async function recordBrowserSession(url) {
10590
+ let targetUrl;
10591
+ try {
10592
+ targetUrl = new URL(url);
10593
+ } catch {
10594
+ throw new Error(`\u65E0\u6548\u7684 URL: ${url}`);
10595
+ }
10596
+ if (!await isPlaywrightAvailable()) {
10597
+ console.error("Playwright \u6D4F\u89C8\u5668\u672A\u5B89\u88C5\uFF0C\u8BF7\u6267\u884C npx playwright install");
10598
+ process.exit(1);
10599
+ }
10600
+ const startTime = Date.now();
10601
+ const browser = await import_playwright.chromium.launch({ headless: false });
10602
+ const context = await browser.newContext();
10603
+ const page = await context.newPage();
10604
+ const events = [];
10605
+ const requestMethods = /* @__PURE__ */ new Map();
10606
+ const cdpSession = await context.newCDPSession(page);
10607
+ cdpSession.on("Network.requestWillBeSent", (params) => {
10608
+ const { request, type, requestId } = params;
10609
+ requestMethods.set(requestId, request.method);
10610
+ if (type === "XHR" || type === "Fetch") {
10611
+ events.push({
10612
+ ts: Date.now() - startTime,
10613
+ type: "network",
10614
+ data: {
10615
+ url: request.url,
10616
+ method: request.method,
10617
+ type: "request"
10618
+ }
10619
+ });
10620
+ }
10621
+ });
10622
+ cdpSession.on("Network.responseReceived", (params) => {
10623
+ const { response, type, requestId } = params;
10624
+ const method = requestMethods.get(requestId) ?? "GET";
10625
+ if (type === "XHR" || type === "Fetch") {
10626
+ events.push({
10627
+ ts: Date.now() - startTime,
10628
+ type: "network",
10629
+ data: {
10630
+ url: response.url,
10631
+ method,
10632
+ statusCode: response.status,
10633
+ type: "response"
10634
+ }
10635
+ });
10636
+ }
10637
+ });
10638
+ cdpSession.on("Page.frameNavigated", (params) => {
10639
+ const { frame } = params;
10640
+ if (frame.parentId === void 0 || frame.parentId === null) {
10641
+ events.push({
10642
+ ts: Date.now() - startTime,
10643
+ type: "navigation",
10644
+ data: {
10645
+ url: frame.url,
10646
+ title: frame.name || frame.url,
10647
+ type: "full"
10648
+ }
10649
+ });
10650
+ }
10651
+ });
10652
+ cdpSession.on("Page.navigatedWithinDocument", (params) => {
10653
+ events.push({
10654
+ ts: Date.now() - startTime,
10655
+ type: "navigation",
10656
+ data: {
10657
+ url: params.url,
10658
+ type: "spa"
10659
+ }
10660
+ });
10661
+ });
10662
+ await page.exposeFunction("__deepstorm_recordEvent", (type, data) => {
10663
+ events.push({
10664
+ ts: Date.now() - startTime,
10665
+ type,
10666
+ data
10667
+ });
10668
+ });
10669
+ await page.goto(targetUrl.href);
10670
+ await page.evaluate(`(function() {
10671
+ var send = window.__deepstorm_recordEvent;
10672
+
10673
+ // Click listener (capture phase)
10674
+ document.addEventListener('click', function(e) {
10675
+ var target = e.target;
10676
+ if (!target || target === document.body || target === document.documentElement) return;
10677
+
10678
+ var info = {
10679
+ tagName: target.tagName.toLowerCase(),
10680
+ id: target.id || undefined,
10681
+ className: (target.className || '').toString().slice(0, 100) || undefined,
10682
+ textContent: (target.textContent || '').trim().slice(0, 100) || undefined
10683
+ };
10684
+
10685
+ var role = target.getAttribute('role');
10686
+ if (role) info.role = role;
10687
+ var ariaLabel = target.getAttribute('aria-label');
10688
+ if (ariaLabel) info.ariaLabel = ariaLabel;
10689
+ var placeholder = target.getAttribute('placeholder');
10690
+ if (placeholder) info.placeholder = placeholder;
10691
+ var dataTestId = target.getAttribute('data-testid');
10692
+ if (dataTestId) info.dataTestId = dataTestId;
10693
+
10694
+ info.cssSelector = buildSimpleSelector(target);
10695
+ send('click', info);
10696
+ }, true);
10697
+
10698
+ // Input listener (change event for final value)
10699
+ document.addEventListener('change', function(e) {
10700
+ var target = e.target;
10701
+ if (!target) return;
10702
+
10703
+ if (target.tagName === 'SELECT') {
10704
+ send('select', {
10705
+ tagName: 'select',
10706
+ value: target.value,
10707
+ name: target.name || undefined,
10708
+ cssSelector: buildSimpleSelector(target)
10709
+ });
10710
+ return;
10711
+ }
10712
+
10713
+ if (target.type === 'password') {
10714
+ send('input', {
10715
+ tagName: target.tagName.toLowerCase(),
10716
+ type: 'password',
10717
+ value: '****',
10718
+ name: target.name || undefined,
10719
+ placeholder: target.placeholder || undefined,
10720
+ cssSelector: buildSimpleSelector(target)
10721
+ });
10722
+ return;
10723
+ }
10724
+
10725
+ send('input', {
10726
+ tagName: target.tagName.toLowerCase(),
10727
+ type: target.type || undefined,
10728
+ value: target.value,
10729
+ name: target.name || undefined,
10730
+ placeholder: target.placeholder || undefined,
10731
+ cssSelector: buildSimpleSelector(target)
10732
+ });
10733
+ }, true);
10734
+
10735
+ // Submit listener
10736
+ document.addEventListener('submit', function(e) {
10737
+ var form = e.target;
10738
+ if (!form) return;
10739
+ send('submit', {
10740
+ cssSelector: buildSimpleSelector(form),
10741
+ id: form.id || undefined,
10742
+ action: form.action || undefined
10743
+ });
10744
+ }, true);
10745
+
10746
+ // Helper: build simple CSS selector
10747
+ function buildSimpleSelector(el) {
10748
+ if (el.id) return '#' + CSS.escape(el.id);
10749
+ var testId = el.getAttribute('data-testid');
10750
+ if (testId) return '[data-testid="' + CSS.escape(testId) + '"]';
10751
+ var tag = el.tagName.toLowerCase();
10752
+ var cls = '';
10753
+ for (var i = 0; i < el.classList.length; i++) {
10754
+ cls += '.' + CSS.escape(el.classList[i]);
10755
+ }
10756
+ if (cls) return tag + cls;
10757
+ var parent = el.parentElement;
10758
+ if (parent) {
10759
+ var siblings = Array.from(parent.children).filter(function(s) {
10760
+ return s.tagName === el.tagName;
10761
+ });
10762
+ if (siblings.length > 1) {
10763
+ var idx = siblings.indexOf(el) + 1;
10764
+ return tag + ':nth-child(' + idx + ')';
10765
+ }
10766
+ }
10767
+ return tag;
10768
+ }
10769
+ })();`);
10770
+ console.log("\u{1F3A5} \u5F55\u5236\u4E2D... \u6309 Ctrl+D \u505C\u6B62");
10771
+ const stopPromise = new Promise((resolve4) => {
10772
+ const wasRaw = process.stdin.isRaw;
10773
+ process.stdin.setRawMode?.(true);
10774
+ process.stdin.resume();
10775
+ process.stdin.on("data", (chunk) => {
10776
+ if (chunk.length === 1 && chunk[0] === 4) {
10777
+ cleanup();
10778
+ resolve4();
10779
+ }
10780
+ });
10781
+ process.stdin.on("end", () => {
10782
+ cleanup();
10783
+ resolve4();
10784
+ });
10785
+ function cleanup() {
10786
+ process.stdin.setRawMode?.(wasRaw ?? false);
10787
+ process.stdin.pause();
10788
+ }
10789
+ });
10790
+ const crashPromise = new Promise((_resolve, reject) => {
10791
+ browser.on("disconnected", () => {
10792
+ reject(new Error("\u6D4F\u89C8\u5668\u8FDE\u63A5\u65AD\u5F00"));
10793
+ });
10794
+ });
10795
+ try {
10796
+ await Promise.race([stopPromise, crashPromise]);
10797
+ } catch (err) {
10798
+ console.error("\n\u6D4F\u89C8\u5668\u8FDE\u63A5\u65AD\u5F00\uFF0C\u5F55\u5236\u5DF2\u7EC8\u6B62");
10799
+ throw err;
10800
+ } finally {
10801
+ process.stdin.setRawMode?.(false);
10802
+ process.stdin.pause();
10803
+ }
10804
+ const duration = Date.now() - startTime;
10805
+ try {
10806
+ const screenshotBuffer = await page.screenshot({ type: "jpeg", quality: 70 });
10807
+ events.push({
10808
+ ts: Date.now() - startTime,
10809
+ type: "screenshot",
10810
+ data: {
10811
+ label: "final",
10812
+ data: screenshotBuffer.toString("base64")
10813
+ }
10814
+ });
10815
+ } catch {
10816
+ }
10817
+ await browser.close();
10818
+ const filteredEvents = dedupEvents(filterNoiseEvents(events));
10819
+ const recordingData = {
10820
+ meta: {
10821
+ url: targetUrl.href,
10822
+ recordedAt: new Date(startTime).toISOString(),
10823
+ duration,
10824
+ browser: `Playwright Chromium ${browser.version()}`
10825
+ },
10826
+ events: filteredEvents
10827
+ };
10828
+ const dir = recordingsDir();
10829
+ fs26.mkdirSync(dir, { recursive: true });
10830
+ const datePrefix = buildRecordingFileName().replace(".recording.json", "");
10831
+ const seq = getNextSeq(datePrefix);
10832
+ const fileName = buildRecordingFileName(seq);
10833
+ const filePath = path22.join(dir, fileName);
10834
+ fs26.writeFileSync(filePath, serializeRecording(recordingData), "utf-8");
10835
+ console.log(`
10836
+ \u2705 \u5F55\u5236\u5B8C\u6210`);
10837
+ console.log(` \u6301\u7EED\u65F6\u95F4: ${(duration / 1e3).toFixed(1)} \u79D2`);
10838
+ console.log(` \u4E8B\u4EF6\u603B\u6570: ${filteredEvents.length}`);
10839
+ console.log(` \u6587\u4EF6\u8DEF\u5F84: ${filePath}`);
10840
+ return filePath;
10841
+ }
10842
+ async function isPlaywrightAvailable() {
10843
+ try {
10844
+ const { execSync: execSync3 } = await import("node:child_process");
10845
+ execSync3(
10846
+ "npx playwright install --dry-run 2>/dev/null || npx playwright --version 2>/dev/null",
10847
+ { stdio: "ignore" }
10848
+ );
10849
+ const browser = await import_playwright.chromium.launch({ headless: true });
10850
+ await browser.close();
10851
+ return true;
10852
+ } catch {
10853
+ return false;
10854
+ }
10855
+ }
10856
+
10857
+ // src/commands/record.ts
10858
+ function registerRecordCommand(program2) {
10859
+ const recordCmd = program2.command("record").description("\u6D4F\u89C8\u5668\u64CD\u4F5C\u5F55\u5236 \u2014 \u542F\u52A8 headed \u6D4F\u89C8\u5668\u5F55\u5236\u7528\u6237\u64CD\u4F5C\uFF0C\u8F93\u51FA .recording.json");
10860
+ recordCmd.command("start").description("\u5F00\u59CB\u5F55\u5236 \u2014 \u542F\u52A8 headed \u6D4F\u89C8\u5668\u5E76\u6253\u5F00\u6307\u5B9A URL").option("-u, --url <url>", "\u76EE\u6807 URL").action(async (options) => {
10861
+ if (!options.url) {
10862
+ console.error("\u8BF7\u4F7F\u7528 -u <url> \u6307\u5B9A\u76EE\u6807 URL");
10863
+ process.exit(1);
10864
+ }
10865
+ try {
10866
+ await recordBrowserSession(options.url);
10867
+ } catch (err) {
10868
+ console.error("\u5F55\u5236\u5931\u8D25:", err instanceof Error ? err.message : err);
10869
+ process.exit(1);
10870
+ }
10871
+ });
10872
+ recordCmd.command("stop").description("\u505C\u6B62\u5F55\u5236 \u2014 \u505C\u6B62\u5F53\u524D\u5F55\u5236\u5E76\u5199\u5165\u6587\u4EF6").action(() => {
10873
+ console.log("\u8BF7\u5728\u5F55\u5236\u7EC8\u7AEF\u6309 Ctrl+D \u505C\u6B62\u5F55\u5236");
10874
+ });
10875
+ }
10876
+
10877
+ // src/commands/doctor.ts
10878
+ var fs27 = __toESM(require("node:fs"));
10879
+ var path23 = __toESM(require("node:path"));
10513
10880
  function getInstalledSkills(targetDir) {
10514
10881
  const settingsPath = getDeepStormConfigPath(targetDir);
10515
10882
  try {
10516
- const settings = JSON.parse(fs25.readFileSync(settingsPath, "utf-8"));
10883
+ const settings = JSON.parse(fs27.readFileSync(settingsPath, "utf-8"));
10517
10884
  return settings.installedSkills || [];
10518
10885
  } catch {
10519
10886
  return [];
@@ -10530,7 +10897,7 @@ function runDoctor(targetDir) {
10530
10897
  message: `v${version}\uFF08\u8FD0\u884C deepstorm update \u68C0\u67E5\u6700\u65B0\u7248\u672C\uFF09`
10531
10898
  });
10532
10899
  let hasDeepStorm = false;
10533
- if (!fs25.existsSync(settingsPath)) {
10900
+ if (!fs27.existsSync(settingsPath)) {
10534
10901
  checks.push({
10535
10902
  name: "\u914D\u7F6E\u6587\u4EF6",
10536
10903
  status: "warn",
@@ -10538,7 +10905,7 @@ function runDoctor(targetDir) {
10538
10905
  });
10539
10906
  } else {
10540
10907
  try {
10541
- const config = JSON.parse(fs25.readFileSync(settingsPath, "utf-8"));
10908
+ const config = JSON.parse(fs27.readFileSync(settingsPath, "utf-8"));
10542
10909
  if (config && Object.keys(config).length > 0) {
10543
10910
  hasDeepStorm = true;
10544
10911
  checks.push({
@@ -10561,20 +10928,20 @@ function runDoctor(targetDir) {
10561
10928
  });
10562
10929
  }
10563
10930
  }
10564
- const skillsDir = path21.join(targetDir, ".claude", "skills");
10565
- if (fs25.existsSync(skillsDir)) {
10566
- const skillDirs = fs25.readdirSync(skillsDir).filter(
10567
- (n) => fs25.statSync(path21.join(skillsDir, n)).isDirectory()
10931
+ const skillsDir = path23.join(targetDir, ".claude", "skills");
10932
+ if (fs27.existsSync(skillsDir)) {
10933
+ const skillDirs = fs27.readdirSync(skillsDir).filter(
10934
+ (n) => fs27.statSync(path23.join(skillsDir, n)).isDirectory()
10568
10935
  );
10569
10936
  let validCount = 0;
10570
10937
  let invalidCount = 0;
10571
10938
  for (const skillName of skillDirs) {
10572
- const skillMd = path21.join(skillsDir, skillName, "SKILL.md");
10573
- if (!fs25.existsSync(skillMd)) {
10939
+ const skillMd = path23.join(skillsDir, skillName, "SKILL.md");
10940
+ if (!fs27.existsSync(skillMd)) {
10574
10941
  invalidCount++;
10575
10942
  continue;
10576
10943
  }
10577
- const content = fs25.readFileSync(skillMd, "utf-8");
10944
+ const content = fs27.readFileSync(skillMd, "utf-8");
10578
10945
  const frontmatter = parseFrontmatter(content);
10579
10946
  if (frontmatter && frontmatter.name) {
10580
10947
  validCount++;
@@ -10604,7 +10971,7 @@ function runDoctor(targetDir) {
10604
10971
  const installedSkills = getInstalledSkills(targetDir);
10605
10972
  const missingSkills = [];
10606
10973
  for (const skillId of installedSkills) {
10607
- if (!fs25.existsSync(path21.join(skillsDir, skillId))) {
10974
+ if (!fs27.existsSync(path23.join(skillsDir, skillId))) {
10608
10975
  missingSkills.push(skillId);
10609
10976
  }
10610
10977
  }
@@ -10628,10 +10995,10 @@ function runDoctor(targetDir) {
10628
10995
  message: ".claude/skills/ \u4E0D\u5B58\u5728"
10629
10996
  });
10630
10997
  }
10631
- const mcpPath = path21.join(targetDir, ".mcp.json");
10632
- if (fs25.existsSync(mcpPath)) {
10998
+ const mcpPath = path23.join(targetDir, ".mcp.json");
10999
+ if (fs27.existsSync(mcpPath)) {
10633
11000
  try {
10634
- const mcp = JSON.parse(fs25.readFileSync(mcpPath, "utf-8"));
11001
+ const mcp = JSON.parse(fs27.readFileSync(mcpPath, "utf-8"));
10635
11002
  const hasServers = mcp.mcpServers && Object.keys(mcp.mcpServers).length > 0;
10636
11003
  checks.push({
10637
11004
  name: "MCP \u670D\u52A1\u5668",
@@ -10652,12 +11019,12 @@ function runDoctor(targetDir) {
10652
11019
  message: ".mcp.json \u4E0D\u5B58\u5728"
10653
11020
  });
10654
11021
  }
10655
- if (hasDeepStorm && fs25.existsSync(settingsPath)) {
11022
+ if (hasDeepStorm && fs27.existsSync(settingsPath)) {
10656
11023
  try {
10657
- const config = JSON.parse(fs25.readFileSync(settingsPath, "utf-8"));
11024
+ const config = JSON.parse(fs27.readFileSync(settingsPath, "utf-8"));
10658
11025
  const installedMcp = config.installedMcpServers;
10659
11026
  if (installedMcp && installedMcp.length > 0) {
10660
- const mcpExists = fs25.existsSync(mcpPath);
11027
+ const mcpExists = fs27.existsSync(mcpPath);
10661
11028
  if (!mcpExists) {
10662
11029
  checks.push({
10663
11030
  name: "MCP \u4E00\u81F4\u6027",
@@ -10666,7 +11033,7 @@ function runDoctor(targetDir) {
10666
11033
  });
10667
11034
  } else {
10668
11035
  try {
10669
- const mcpConfig = JSON.parse(fs25.readFileSync(mcpPath, "utf-8"));
11036
+ const mcpConfig = JSON.parse(fs27.readFileSync(mcpPath, "utf-8"));
10670
11037
  const servers = mcpConfig.mcpServers || {};
10671
11038
  const missing = [];
10672
11039
  for (const toolName of installedMcp) {
@@ -10694,9 +11061,9 @@ function runDoctor(targetDir) {
10694
11061
  } catch {
10695
11062
  }
10696
11063
  }
10697
- if (hasDeepStorm && fs25.existsSync(settingsPath)) {
11064
+ if (hasDeepStorm && fs27.existsSync(settingsPath)) {
10698
11065
  try {
10699
- const config = JSON.parse(fs25.readFileSync(settingsPath, "utf-8"));
11066
+ const config = JSON.parse(fs27.readFileSync(settingsPath, "utf-8"));
10700
11067
  const validKeys = loadValidConfigKeys();
10701
11068
  if (validKeys.size > 0 && config) {
10702
11069
  const configKeys = [];
@@ -10749,18 +11116,18 @@ function printDoctorReport(report) {
10749
11116
  }
10750
11117
 
10751
11118
  // src/commands/uninstall.ts
10752
- var fs26 = __toESM(require("node:fs"));
10753
- var path22 = __toESM(require("node:path"));
11119
+ var fs28 = __toESM(require("node:fs"));
11120
+ var path24 = __toESM(require("node:path"));
10754
11121
  var import_prompts4 = require("@clack/prompts");
10755
11122
  async function uninstallDeepStorm(targetDir) {
10756
11123
  const settingsPath = getDeepStormConfigPath(targetDir);
10757
- if (!fs26.existsSync(settingsPath)) {
11124
+ if (!fs28.existsSync(settingsPath)) {
10758
11125
  console.log("DeepStorm \u5C1A\u672A\u914D\u7F6E\uFF0C\u65E0\u9700\u5378\u8F7D");
10759
11126
  return;
10760
11127
  }
10761
11128
  let hasDeepStorm = false;
10762
11129
  try {
10763
- const settings = JSON.parse(fs26.readFileSync(settingsPath, "utf-8"));
11130
+ const settings = JSON.parse(fs28.readFileSync(settingsPath, "utf-8"));
10764
11131
  hasDeepStorm = !!settings && Object.keys(settings).length > 0;
10765
11132
  } catch {
10766
11133
  }
@@ -10770,14 +11137,14 @@ async function uninstallDeepStorm(targetDir) {
10770
11137
  }
10771
11138
  cleanInstalled(targetDir);
10772
11139
  resetConfig(targetDir);
10773
- const templatesDir = path22.join(targetDir, ".deepstorm", "templates");
10774
- if (fs26.existsSync(templatesDir)) {
11140
+ const templatesDir = path24.join(targetDir, ".deepstorm", "templates");
11141
+ if (fs28.existsSync(templatesDir)) {
10775
11142
  const shouldDelete = await (0, import_prompts4.confirm)({
10776
11143
  message: "\u662F\u5426\u5220\u9664 .deepstorm/templates/ \u76EE\u5F55\uFF1F",
10777
11144
  initialValue: false
10778
11145
  });
10779
11146
  if (shouldDelete) {
10780
- fs26.rmSync(templatesDir, { recursive: true, force: true });
11147
+ fs28.rmSync(templatesDir, { recursive: true, force: true });
10781
11148
  console.log("\u2714 .deepstorm/templates/ \u5DF2\u5220\u9664");
10782
11149
  } else {
10783
11150
  console.log("\u4FDD\u7559 .deepstorm/templates/");
@@ -10790,9 +11157,9 @@ async function uninstallDeepStorm(targetDir) {
10790
11157
  var program = new import_commander.Command();
10791
11158
  program.name("deepstorm").description("DeepStorm CLI \u2014 \u4E00\u952E\u914D\u7F6E\u9879\u76EE\u5F00\u53D1\u73AF\u5883").version(getCliVersion(), "-v, --version", "\u8F93\u51FA\u7248\u672C\u53F7");
10792
11159
  function loadRegistry() {
10793
- const registryPath = path23.join(__dirname, "registry.json");
11160
+ const registryPath = path25.join(__dirname, "registry.json");
10794
11161
  try {
10795
- const raw = fs27.readFileSync(registryPath, "utf-8");
11162
+ const raw = fs29.readFileSync(registryPath, "utf-8");
10796
11163
  return JSON.parse(raw);
10797
11164
  } catch {
10798
11165
  return { version: "1", tools: {}, wizards: {}, skills: {} };
@@ -10806,6 +11173,7 @@ registerConfigCommand(program, registry);
10806
11173
  registerTemplateCommand(program, registry);
10807
11174
  registerUpdateCommand(program);
10808
11175
  registerPilotCommands(program);
11176
+ registerRecordCommand(program);
10809
11177
  program.command("doctor").description("\u8BCA\u65AD\u9879\u76EE DeepStorm \u914D\u7F6E\u72B6\u6001").action(() => {
10810
11178
  const report = runDoctor(process.cwd());
10811
11179
  printDoctorReport(report);