@xbrowser/cli 1.11.0 → 1.13.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.
Files changed (35) hide show
  1. package/dist/{browser-XNU7JQYC.js → browser-AQ2AT2YZ.js} +2 -2
  2. package/dist/{browser-Q5APBNF6.js → browser-OMGJMSBB.js} +1 -1
  3. package/dist/{browser-V3JIWSTR.js → browser-T7OVLAEC.js} +4 -3
  4. package/dist/{cdp-driver-XFTTZI5O.js → cdp-driver-ALDNOZYD.js} +1176 -13
  5. package/dist/{cdp-driver-GD6YBBGE.js → cdp-driver-HYCLE4YP.js} +2 -1
  6. package/dist/{cdp-driver-VEK6BNN6.js → cdp-driver-VS7Z7W7Y.js} +1 -1
  7. package/dist/chunk-A6LPGFAL.js +437 -0
  8. package/dist/{chunk-JEDP4PJW.js → chunk-BY5TT6WZ.js} +912 -688
  9. package/dist/{chunk-XKQVUFUS.js → chunk-CW7L53JE.js} +2 -2
  10. package/dist/{chunk-ABXMBNQ6.js → chunk-H2A5JUK5.js} +37 -466
  11. package/dist/{chunk-BNO7OKO4.js → chunk-HBMEFSTB.js} +39 -0
  12. package/dist/chunk-HD4FX2N5.js +1255 -0
  13. package/dist/{chunk-DWDXEGVK.js → chunk-NKW4A74J.js} +9 -3
  14. package/dist/{chunk-SQHSZENE.js → chunk-OMU63E6J.js} +3 -22
  15. package/dist/{chunk-7OFM755Z.js → chunk-R2POF2GP.js} +236 -13
  16. package/dist/{chunk-WHPBNUKB.js → chunk-SH6OTPXN.js} +42 -46
  17. package/dist/{chunk-XQ4HRPDJ.js → chunk-T2CNKWPV.js} +1177 -13
  18. package/dist/{chunk-MWFHZUIY.js → chunk-TEXCXIBW.js} +1 -1
  19. package/dist/{chunk-IIM5GOD7.js → chunk-ZTHE5RBZ.js} +7 -1
  20. package/dist/cli.js +1075 -569
  21. package/dist/{daemon-client-MMDRYCF5.js → daemon-client-7D6EZNOE.js} +42 -46
  22. package/dist/{daemon-client-Y2YSZCGK.js → daemon-client-HEGAXWGK.js} +1 -1
  23. package/dist/daemon-main.js +950 -499
  24. package/dist/{human-interaction-5AO42MBA.js → human-interaction-4YR2N6R6.js} +2 -2
  25. package/dist/{human-interaction-C5OEGXGO.js → human-interaction-Y5IDH6LD.js} +1 -1
  26. package/dist/{human-interaction-ISXZTAYY.js → human-interaction-ZUTR5AC2.js} +2 -2
  27. package/dist/index.d.ts +2 -0
  28. package/dist/index.js +1086 -578
  29. package/dist/{proxy-C6CK3UH5.js → proxy-LUR4U5YF.js} +2 -1
  30. package/dist/{recovery-J2ISVGUL.js → recovery-FTZGV6VY.js} +2 -2
  31. package/dist/{recovery-ZJVVHP7N.js → recovery-L5GLDX4O.js} +1 -1
  32. package/dist/{recovery-EF33LKRJ.js → recovery-Z3RDZENA.js} +2 -2
  33. package/dist/{session-replayer-WIUYVN5J.js → session-replayer-UVH5FGY2.js} +1 -1
  34. package/package.json +1 -1
  35. package/dist/chunk-HVPUVVSA.js +0 -2194
@@ -20,7 +20,6 @@ import {
20
20
  closeEphemeralContext,
21
21
  closeSessionByName,
22
22
  createEphemeralContext,
23
- createRuleEngine,
24
23
  createSession,
25
24
  findOrRestoreSession,
26
25
  findSession,
@@ -31,8 +30,10 @@ import {
31
30
  resolveLaunchOpts,
32
31
  saveSessionDiskMeta,
33
32
  setActivePage
34
- } from "./chunk-HVPUVVSA.js";
35
- import "./chunk-XQ4HRPDJ.js";
33
+ } from "./chunk-HD4FX2N5.js";
34
+ import {
35
+ createRuleEngine
36
+ } from "./chunk-T2CNKWPV.js";
36
37
  import {
37
38
  queryJS
38
39
  } from "./chunk-3FWLW7FS.js";
@@ -45,18 +46,19 @@ import {
45
46
  formatDetectionMessage
46
47
  } from "./chunk-JKVUFP3G.js";
47
48
  import {
48
- ScreencastCapturer
49
- } from "./chunk-BNO7OKO4.js";
49
+ ScreencastCapturer,
50
+ resolveScreenshotsDir
51
+ } from "./chunk-HBMEFSTB.js";
50
52
  import "./chunk-KFQGP6VL.js";
51
53
 
52
54
  // src/daemon/daemon-main.ts
53
55
  import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, appendFileSync, unlinkSync } from "fs";
54
- import { join as join7 } from "path";
56
+ import { join as join9 } from "path";
55
57
  import { homedir as homedir7 } from "os";
56
58
  import { startHttpServer } from "@dyyz1993/xcli-core";
57
59
 
58
60
  // src/daemon/rpc-handlers.ts
59
- import { readFileSync as readFileSync5 } from "fs";
61
+ import { readFileSync as readFileSync7 } from "fs";
60
62
  import {
61
63
  createSessionMeta,
62
64
  removeSession
@@ -64,8 +66,8 @@ import {
64
66
 
65
67
  // src/executor.ts
66
68
  import {
67
- ok as ok25,
68
- fail as fail11,
69
+ ok as ok28,
70
+ fail as fail13,
69
71
  isCommandResult,
70
72
  CompositeStorage as CompositeStorage2,
71
73
  TipCollector as TipCollector2,
@@ -922,29 +924,441 @@ var mouseCommand = registerCommand({
922
924
  }
923
925
  });
924
926
 
925
- // src/commands/evaluate.ts
927
+ // src/commands/find-visual.ts
926
928
  import { z as z7 } from "zod";
927
- import { ok as ok7, normalizeTips as normalizeTips2 } from "@dyyz1993/xcli-core";
929
+ import { ok as ok7, fail as fail2 } from "@dyyz1993/xcli-core";
930
+ import { readFileSync, existsSync } from "fs";
931
+ import { homedir as homedir2 } from "os";
932
+ import { join as join2 } from "path";
933
+ function loadVLMCredentials() {
934
+ const candidates = [
935
+ process.env.XBROWSER_VLM_CONFIG,
936
+ join2(homedir2(), ".zcode", "v2", "config.json")
937
+ ].filter(Boolean);
938
+ for (const file of candidates) {
939
+ try {
940
+ if (!existsSync(file)) continue;
941
+ const cfg = JSON.parse(readFileSync(file, "utf8"));
942
+ const provider = Object.values(cfg.provider ?? {})[0];
943
+ if (provider?.options?.apiKey && provider.options.baseURL) {
944
+ const models = Object.keys(provider.models ?? {});
945
+ const vl = models.find((m) => /vl|vision/i.test(m));
946
+ const flash = models.find((m) => /flash/i.test(m) && /5\.?3/i.test(m));
947
+ return {
948
+ apiKey: provider.options.apiKey,
949
+ baseURL: provider.options.baseURL.replace(/\/$/, ""),
950
+ model: process.env.XBROWSER_VLM_MODEL || vl || flash || models.find((m) => /5\.[23]/.test(m)) || models[0]
951
+ };
952
+ }
953
+ } catch {
954
+ }
955
+ }
956
+ return null;
957
+ }
958
+ async function locateByVLM(creds, imageBase64, target, feedback) {
959
+ const prompt = `\u4F60\u5728\u4E00\u5F20\u7F51\u9875\u622A\u56FE\u4E0A\u5B9A\u4F4D UI \u5143\u7D20\u3002\u76EE\u6807\u5143\u7D20\u63CF\u8FF0\uFF1A"""${target}"""
960
+
961
+ \u4E25\u683C\u53EA\u8F93\u51FA\u4E00\u4E2A JSON \u5BF9\u8C61\uFF08\u65E0 markdown\u3001\u65E0\u89E3\u91CA\uFF09\uFF1A
962
+ {"label":"<\u5143\u7D20\u540D>","box":{"x":<\u5DE6\u4E0Ax>,"y":<\u5DE6\u4E0Ay>,"width":<\u5BBD>,"height":<\u9AD8>},"confidence":"high|medium|low"}
963
+ \u5750\u6807\u4EE5\u622A\u56FE\u5DE6\u4E0A\u89D2\u4E3A\u539F\u70B9\u3001\u5355\u4F4D\u4E3A\u50CF\u7D20\u3002\u627E\u4E0D\u5230\u5143\u7D20\u5219\u8F93\u51FA {"label":null}\u3002
964
+ ${feedback ? `\u6CE8\u610F\uFF1A${feedback}` : ""}`;
965
+ const resp = await fetch(creds.baseURL + "/v1/messages", {
966
+ method: "POST",
967
+ headers: { "content-type": "application/json", "x-api-key": creds.apiKey, "anthropic-version": "2023-06-01" },
968
+ body: JSON.stringify({
969
+ model: creds.model,
970
+ max_tokens: 2e3,
971
+ // thinking 模型的思考块也计入配额,300 会在 text 前截断
972
+ messages: [{
973
+ role: "user",
974
+ content: [
975
+ { type: "image", source: { type: "base64", media_type: "image/png", data: imageBase64 } },
976
+ { type: "text", text: prompt }
977
+ ]
978
+ }]
979
+ }),
980
+ signal: AbortSignal.timeout(6e4)
981
+ });
982
+ if (!resp.ok) throw new Error(`VLM HTTP ${resp.status}`);
983
+ const data = await resp.json();
984
+ const text = (data.content ?? []).filter((c) => c.type === "text").map((c) => c.text ?? "").join("");
985
+ const m = text.match(/\{[\s\S]*\}/);
986
+ if (!m) {
987
+ console.error("[find-visual] VLM raw (no JSON):", text.substring(0, 200));
988
+ return null;
989
+ }
990
+ const hit = JSON.parse(m[0]);
991
+ if (!hit.label || !hit.box) return null;
992
+ return hit;
993
+ }
994
+ var findVisualCommand = registerCommand({
995
+ name: "find-visual",
996
+ description: "Visual element location: screenshot \u2192 VLM \u2192 page coordinates (optionally click)",
997
+ scope: "page",
998
+ parameters: z7.object({
999
+ element: z7.string().describe('\u5143\u7D20\u7684\u81EA\u7136\u8BED\u8A00\u63CF\u8FF0\uFF0C\u5982 "\u7EA2\u8272\u7684\u767B\u5F55\u6309\u94AE"\uFF08\u6CE8\u610F\uFF1A--target \u662F\u5168\u5C40\u9875\u9762\u8DEF\u7531\u53C2\u6570\uFF0C\u6B64\u5904\u7528 --element\uFF09'),
1000
+ click: z7.boolean().optional().describe("\u627E\u5230\u540E\u7ACB\u5373\u70B9\u51FB\uFF08\u771F\u5B9E\u9F20\u6807\u4E8B\u4EF6\uFF09"),
1001
+ threshold: z7.coerce.number().optional().describe("\u7F6E\u4FE1\u5EA6\u9608\u503C\uFF0C\u4F4E\u4E8E\u5219\u62A5 not-found\uFF08\u9ED8\u8BA4 0\uFF09"),
1002
+ zoom: z7.boolean().optional().describe("\u4E8C\u6BB5\u5F0F\u653E\u5927\u7CBE\u4FEE\uFF08\u9ED8\u8BA4\u5F00\uFF09\u2014\u2014\u7C97\u6846\u533A\u57DF\u4E8C\u6B21\u622A\u56FE\u8BA9 VLM \u7CBE\u786E\u5B9A\u4F4D")
1003
+ }),
1004
+ result: z7.object({
1005
+ found: z7.boolean(),
1006
+ element: z7.string(),
1007
+ box: z7.unknown().optional(),
1008
+ center: z7.unknown().optional(),
1009
+ confidence: z7.string().optional(),
1010
+ model: z7.string().optional(),
1011
+ clicked: z7.boolean().optional(),
1012
+ screenshot: z7.string().optional()
1013
+ }),
1014
+ handler: async (p, ctx) => {
1015
+ const page = ctx.page;
1016
+ const creds = loadVLMCredentials();
1017
+ if (!creds) {
1018
+ return fail2("\u672A\u627E\u5230 VLM \u51ED\u636E\uFF1A\u9700\u8981 ~/.zcode/v2/config.json \u7684 provider \u914D\u7F6E\u6216 XBROWSER_VLM_CONFIG \u73AF\u5883\u53D8\u91CF", [
1019
+ "\u4E5F\u53EF\u7528 XBROWSER_VLM_MODEL \u6307\u5B9A\u6A21\u578B\uFF08\u9ED8\u8BA4\u81EA\u52A8\u9009 VL/\u65D7\u8230\u6A21\u578B\uFF09"
1020
+ ]);
1021
+ }
1022
+ const buffer = await page.screenshot({ type: "png" });
1023
+ const imageBase64 = buffer.toString("base64");
1024
+ const imgW = buffer.readUInt32BE(16);
1025
+ const imgH = buffer.readUInt32BE(20);
1026
+ let hit = null;
1027
+ let feedback = "";
1028
+ try {
1029
+ for (let attempt = 0; attempt < 3 && !hit; attempt++) {
1030
+ const candidate = await locateByVLM(creds, imageBase64, p.element, feedback);
1031
+ if (!candidate) break;
1032
+ const b = candidate.box;
1033
+ const inBounds = b.x >= 0 && b.y >= 0 && b.x + b.width <= imgW + 2 && b.y + b.height <= imgH + 2;
1034
+ const sane = b.width > 2 && b.height > 2;
1035
+ if (inBounds && sane) {
1036
+ hit = candidate;
1037
+ break;
1038
+ }
1039
+ feedback = `\u4E0A\u6B21\u8F93\u51FA box=${JSON.stringify(b)} \u65E0\u6548\uFF08${!inBounds ? "\u8D85\u51FA\u56FE\u50CF\u8FB9\u754C" : "\u5C3A\u5BF8\u8FC7\u5C0F"}\uFF09\u3002\u8BF7\u57FA\u4E8E\u622A\u56FE\u91CD\u65B0\u4ED4\u7EC6\u5B9A\u4F4D\uFF0C\u5750\u6807\u539F\u70B9\u5728\u56FE\u50CF\u5DE6\u4E0A\u89D2\u3002`;
1040
+ }
1041
+ } catch (e) {
1042
+ return fail2(`VLM \u8C03\u7528\u5931\u8D25: ${e instanceof Error ? e.message : String(e)}`);
1043
+ }
1044
+ if (!hit) {
1045
+ return ok7({ found: false, element: p.element, model: creds.model }, [
1046
+ `\u622A\u56FE\u4E2D\u672A\u627E\u5230: ${p.element}`
1047
+ ]);
1048
+ }
1049
+ if (p.zoom !== false && hit) {
1050
+ try {
1051
+ const pad = Math.max(20, Math.round(Math.max(hit.box.width, hit.box.height) * 0.5));
1052
+ const clip = {
1053
+ x: Math.max(0, hit.box.x - pad),
1054
+ y: Math.max(0, hit.box.y - pad),
1055
+ width: Math.min(imgW - Math.max(0, hit.box.x - pad), hit.box.width + pad * 2),
1056
+ height: Math.min(imgH - Math.max(0, hit.box.y - pad), hit.box.height + pad * 2)
1057
+ };
1058
+ if (clip.width > 8 && clip.height > 8) {
1059
+ const zoomBuf = await page.screenshot({ type: "png", clip });
1060
+ const zoomB64 = zoomBuf.toString("base64");
1061
+ const zoomW = zoomBuf.readUInt32BE(16);
1062
+ const zoomH = zoomBuf.readUInt32BE(20);
1063
+ const refined = await locateByVLM(creds, zoomB64, p.element);
1064
+ if (refined) {
1065
+ const rb = refined.box;
1066
+ const inZoom = rb.x >= 0 && rb.y >= 0 && rb.x + rb.width <= zoomW + 2 && rb.y + rb.height <= zoomH + 2 && rb.width > 2 && rb.height > 2;
1067
+ if (inZoom) {
1068
+ hit = {
1069
+ label: refined.label || hit.label,
1070
+ box: {
1071
+ x: clip.x + rb.x,
1072
+ y: clip.y + rb.y,
1073
+ width: rb.width,
1074
+ height: rb.height
1075
+ },
1076
+ confidence: refined.confidence === "low" ? hit.confidence : refined.confidence
1077
+ };
1078
+ }
1079
+ }
1080
+ }
1081
+ } catch {
1082
+ }
1083
+ }
1084
+ if (p.threshold !== void 0) {
1085
+ const rank = { high: 3, medium: 2, low: 1 };
1086
+ const thresholdName = p.threshold >= 3 ? "high" : p.threshold >= 2 ? "medium" : "low";
1087
+ if ((rank[hit.confidence ?? "low"] ?? 1) < (rank[thresholdName] ?? 1)) {
1088
+ return ok7({ found: false, element: p.element, confidence: hit.confidence, box: hit.box, model: creds.model }, [
1089
+ `\u7F6E\u4FE1\u5EA6 ${hit.confidence} \u4F4E\u4E8E\u9608\u503C ${p.threshold}`
1090
+ ]);
1091
+ }
1092
+ }
1093
+ const viewport = await page.evaluate("JSON.stringify({w: window.innerWidth, h: window.innerHeight})").then((v) => JSON.parse(String(v))).catch(() => ({ w: 1280, h: 800 }));
1094
+ const pngW = buffer.readUInt32BE(16);
1095
+ const pngH = buffer.readUInt32BE(20);
1096
+ const scaleX = viewport.w / pngW;
1097
+ const scaleY = viewport.h / pngH;
1098
+ const center = {
1099
+ x: Math.round((hit.box.x + hit.box.width / 2) * scaleX),
1100
+ y: Math.round((hit.box.y + hit.box.height / 2) * scaleY)
1101
+ };
1102
+ let clicked = false;
1103
+ if (p.click) {
1104
+ await page.mouse.click(center.x, center.y, { stealth: true });
1105
+ clicked = true;
1106
+ }
1107
+ return ok7({
1108
+ found: true,
1109
+ element: p.element,
1110
+ box: hit.box,
1111
+ center,
1112
+ confidence: hit.confidence,
1113
+ model: creds.model,
1114
+ clicked,
1115
+ screenshot: `data:image/png;base64,${imageBase64.substring(0, 64)}... (${Math.round(imageBase64.length / 1024)}KB)`
1116
+ }, [
1117
+ clicked ? `\u5DF2\u70B9\u51FB (${center.x}, ${center.y}) \u2014 ${hit.label} [${hit.confidence}]` : `\u5B9A\u4F4D\u5230 "${hit.label}" \u4E2D\u5FC3 (${center.x}, ${center.y}) [${hit.confidence}] \u2014 \u52A0 --click \u6267\u884C\u70B9\u51FB`
1118
+ ]);
1119
+ }
1120
+ });
1121
+
1122
+ // src/commands/vision-task.ts
1123
+ import { z as z8 } from "zod";
1124
+ import { ok as ok8, fail as fail3 } from "@dyyz1993/xcli-core";
1125
+ import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
1126
+ import { homedir as homedir3 } from "os";
1127
+ import { join as join3 } from "path";
1128
+ function loadVLMCredentials2() {
1129
+ const candidates = [
1130
+ process.env.XBROWSER_VLM_CONFIG,
1131
+ join3(homedir3(), ".zcode", "v2", "config.json")
1132
+ ].filter(Boolean);
1133
+ for (const file of candidates) {
1134
+ try {
1135
+ if (!existsSync2(file)) continue;
1136
+ const cfg = JSON.parse(readFileSync2(file, "utf8"));
1137
+ const provider = Object.values(cfg.provider ?? {})[0];
1138
+ if (provider?.options?.apiKey && provider.options.baseURL) {
1139
+ const models = Object.keys(provider.models ?? {});
1140
+ const vl = models.find((m) => /vl|vision/i.test(m));
1141
+ const flash = models.find((m) => /flash/i.test(m) && /5\.?3/i.test(m));
1142
+ return {
1143
+ apiKey: provider.options.apiKey,
1144
+ baseURL: provider.options.baseURL.replace(/\/$/, ""),
1145
+ model: process.env.XBROWSER_VLM_MODEL || vl || flash || models.find((m) => /5\.[23]/.test(m)) || models[0]
1146
+ };
1147
+ }
1148
+ } catch {
1149
+ }
1150
+ }
1151
+ return null;
1152
+ }
1153
+ async function vlmAsk(creds, content, maxTokens = 2e3) {
1154
+ const resp = await fetch(creds.baseURL + "/v1/messages", {
1155
+ method: "POST",
1156
+ headers: { "content-type": "application/json", "x-api-key": creds.apiKey, "anthropic-version": "2023-06-01" },
1157
+ body: JSON.stringify({ model: creds.model, max_tokens: maxTokens, messages: [{ role: "user", content }] }),
1158
+ signal: AbortSignal.timeout(9e4)
1159
+ });
1160
+ if (!resp.ok) throw new Error(`VLM HTTP ${resp.status}`);
1161
+ const data = await resp.json();
1162
+ return (data.content ?? []).filter((c) => c.type === "text").map((c) => c.text ?? "").join("");
1163
+ }
1164
+ async function vlmAskRetry(creds, content, maxTokens = 2e3) {
1165
+ let last = "";
1166
+ for (let i = 0; i < 2; i++) {
1167
+ last = await vlmAsk(creds, content, maxTokens);
1168
+ if (last.trim()) return last;
1169
+ await new Promise((r) => setTimeout(r, 1200));
1170
+ }
1171
+ return last;
1172
+ }
1173
+ function pngDims(b64) {
1174
+ const buf = Buffer.from(b64, "base64");
1175
+ return { w: buf.readUInt32BE(16), h: buf.readUInt32BE(20) };
1176
+ }
1177
+ function parseDecision(text) {
1178
+ const m = text.match(/\{[\s\S]*\}/);
1179
+ if (!m) return null;
1180
+ try {
1181
+ const j = JSON.parse(m[0]);
1182
+ if (!j.action || !j.action.type) return null;
1183
+ return j;
1184
+ } catch {
1185
+ return null;
1186
+ }
1187
+ }
1188
+ var SYSTEM_PROMPT = `\u4F60\u662F\u6D4F\u89C8\u5668\u89C6\u89C9\u64CD\u4F5C Agent\u3002\u6839\u636E\u622A\u56FE\u548C\u4EFB\u52A1\u5386\u53F2\uFF0C\u51B3\u5B9A\u4E0B\u4E00\u4E2A\u52A8\u4F5C\u3002
1189
+
1190
+ \u4E25\u683C\u53EA\u8F93\u51FA\u4E00\u4E2A JSON\uFF08\u65E0 markdown\uFF09\uFF1A
1191
+ {"thought":"<\u7B80\u77ED\u63A8\u7406>","action":{"type":"click","x":<int>,"y":<int>}
1192
+ \u6216 {"type":"type","text":"<\u8981\u8F93\u5165\u7684\u6587\u5B57>"}\uFF08\u8F93\u5165\u524D\u987B\u5DF2\u70B9\u51FB\u805A\u7126\u8F93\u5165\u6846\uFF09
1193
+ \u6216 {"type":"key","key":"Enter"}\uFF08key \u540D\uFF1AEnter/Tab/Escape/ArrowDown\u2026\uFF09
1194
+ \u6216 {"type":"scroll","direction":"down","amount":<px>}
1195
+ \u6216 {"type":"done","summary":"<\u4EFB\u52A1\u5B8C\u6210\u6458\u8981>"}
1196
+ \u6216 {"type":"stuck","reason":"<\u65E0\u6CD5\u7EE7\u7EED\u7684\u539F\u56E0>"}}
1197
+
1198
+ \u5750\u6807\u4EE5\u622A\u56FE\u5DE6\u4E0A\u89D2\u4E3A\u539F\u70B9\u3001\u50CF\u7D20\u4E3A\u5355\u4F4D\u3002\u89C4\u5219\uFF1A
1199
+ 1. \u6BCF\u6B21\u53EA\u505A\u4E00\u4E2A\u52A8\u4F5C\uFF1B\u8F93\u5165\u6587\u5B57\u524D\u786E\u8BA4\u8F93\u5165\u6846\u5DF2\u805A\u7126\uFF08\u4E0A\u4E00\u6B65\u662F click \u8F93\u5165\u6846\uFF09
1200
+ 2. \u9875\u9762\u65E0\u660E\u663E\u53D8\u5316\u65F6\u6362\u7B56\u7565\uFF0C\u4E0D\u8981\u91CD\u590D\u540C\u4E00\u52A8\u4F5C\u8D85\u8FC7 2 \u6B21
1201
+ 3. \u4EFB\u52A1\u76EE\u6807\u8FBE\u6210\u7ACB\u5373\u8F93\u51FA done\uFF1B5 \u6B65\u65E0\u8FDB\u5C55\u8F93\u51FA stuck`;
1202
+ var visionTaskCommand = registerCommand({
1203
+ name: "vision-task",
1204
+ description: "Vision-loop agent: screenshot \u2192 VLM decide \u2192 CDP act \u2192 verify, until done",
1205
+ scope: "page",
1206
+ parameters: z8.object({
1207
+ task: z8.string().describe('\u81EA\u7136\u8BED\u8A00\u4EFB\u52A1\uFF0C\u5982 "\u641C\u7D22 xbrowser \u5E76\u6253\u5F00\u7B2C\u4E00\u7BC7\u6587\u7AE0"'),
1208
+ maxSteps: z8.coerce.number().optional().describe("\u6700\u5927\u6B65\u6570\uFF08\u9ED8\u8BA4 12\uFF09"),
1209
+ settleMs: z8.coerce.number().optional().describe("\u6BCF\u6B65\u540E\u7B49\u5F85\u9875\u9762\u7A33\u5B9A\u7684\u6BEB\u79D2\u6570\uFF08\u9ED8\u8BA4 1500\uFF09")
1210
+ }),
1211
+ result: z8.object({
1212
+ done: z8.boolean(),
1213
+ steps: z8.unknown().optional(),
1214
+ summary: z8.string().optional(),
1215
+ model: z8.string().optional()
1216
+ }),
1217
+ handler: async (p, ctx) => {
1218
+ let page = ctx.page;
1219
+ const creds = loadVLMCredentials2();
1220
+ if (!creds) {
1221
+ return fail3("\u672A\u627E\u5230 VLM \u51ED\u636E\uFF1A\u9700\u8981 ~/.zcode/v2/config.json \u6216 XBROWSER_VLM_CONFIG");
1222
+ }
1223
+ const maxSteps = p.maxSteps ?? 12;
1224
+ const settleMs = p.settleMs ?? 1500;
1225
+ const steps = [];
1226
+ const history = [];
1227
+ let lastUrl = page.url();
1228
+ for (let step = 1; step <= maxSteps; step++) {
1229
+ if (page.url() === "about:blank") {
1230
+ try {
1231
+ const ctxObj = page.context?.();
1232
+ const pages = ctxObj?.pages?.() || [];
1233
+ let real;
1234
+ for (let i = pages.length - 1; i >= 0; i--) {
1235
+ const u = pages[i]?.url?.() ?? "";
1236
+ if (u && u !== "about:blank") {
1237
+ real = pages[i];
1238
+ break;
1239
+ }
1240
+ }
1241
+ if (real) {
1242
+ await real.bringToFront().catch(() => {
1243
+ });
1244
+ page = real;
1245
+ }
1246
+ } catch {
1247
+ }
1248
+ }
1249
+ const buffer = await page.screenshot({ type: "png" });
1250
+ const b64 = buffer.toString("base64");
1251
+ const { w, h } = pngDims(b64);
1252
+ const obsText = [
1253
+ `\u4EFB\u52A1\uFF1A${p.task}`,
1254
+ `\u622A\u56FE\u5C3A\u5BF8\uFF1A${w}x${h}\uFF08\u5750\u6807\u539F\u70B9\uFF1A\u5DE6\u4E0A\u89D2\uFF09`,
1255
+ `\u5F53\u524D URL\uFF1A${page.url()}`,
1256
+ history.length ? `\u5DF2\u6267\u884C\u52A8\u4F5C\uFF1A
1257
+ ${history.slice(-6).join("\n")}` : "\u5C1A\u65E0\u52A8\u4F5C\u3002"
1258
+ ].join("\n");
1259
+ const reply = await vlmAskRetry(creds, [
1260
+ { type: "image", source: { type: "base64", media_type: "image/png", data: b64 } },
1261
+ { type: "text", text: `${SYSTEM_PROMPT}
1262
+
1263
+ ${obsText}` }
1264
+ ]);
1265
+ const decision = parseDecision(reply);
1266
+ if (!decision) {
1267
+ steps.push({ n: step, thought: "(unparseable)", action: "stop", ts: Date.now() });
1268
+ return ok8({ done: false, steps, summary: `\u7B2C ${step} \u6B65 VLM \u8F93\u51FA\u65E0\u6CD5\u89E3\u6790: ${reply.substring(0, 120)}`, model: creds.model });
1269
+ }
1270
+ const a = decision.action;
1271
+ let acted = "";
1272
+ try {
1273
+ if (a.type === "done") {
1274
+ steps.push({ n: step, thought: decision.thought, action: "done", ts: Date.now() });
1275
+ return ok8({ done: true, steps, summary: a.summary, model: creds.model });
1276
+ }
1277
+ if (a.type === "stuck") {
1278
+ steps.push({ n: step, thought: decision.thought, action: "stuck", ts: Date.now() });
1279
+ return ok8({ done: false, steps, summary: `stuck: ${a.reason}`, model: creds.model });
1280
+ }
1281
+ if (a.type === "click") {
1282
+ const cx = Math.max(2, Math.min(w - 2, Math.round(a.x)));
1283
+ const cy = Math.max(2, Math.min(h - 2, Math.round(a.y)));
1284
+ await page.mouse.click(cx, cy, { stealth: true });
1285
+ acted = `click(${cx},${cy})`;
1286
+ } else if (a.type === "type") {
1287
+ await page.keyboard.type(a.text, { stealth: true });
1288
+ acted = `type("${a.text.substring(0, 30)}")`;
1289
+ } else if (a.type === "key") {
1290
+ await page.keyboard.press(a.key);
1291
+ acted = `key(${a.key})`;
1292
+ } else if (a.type === "scroll") {
1293
+ await page.mouse.wheel(0, a.direction === "down" ? a.amount : -a.amount);
1294
+ acted = `scroll(${a.direction},${a.amount})`;
1295
+ }
1296
+ } catch (e) {
1297
+ acted = `${a.type} FAILED: ${e instanceof Error ? e.message : String(e).substring(0, 80)}`;
1298
+ }
1299
+ steps.push({ n: step, thought: decision.thought, action: acted, ts: Date.now() });
1300
+ history.push(`${step}. ${acted} \u2014 ${decision.thought.substring(0, 60)}`);
1301
+ await new Promise((r) => setTimeout(r, settleMs));
1302
+ if (a.type === "click") {
1303
+ try {
1304
+ const ctxObj = page.context?.();
1305
+ const pages = ctxObj?.pages?.() || [];
1306
+ for (let i = pages.length - 1; i >= 0; i--) {
1307
+ const cand = pages[i];
1308
+ if (!cand || cand === page) continue;
1309
+ const u = cand.url?.() ?? "";
1310
+ if (!u || u === "about:blank") continue;
1311
+ if (u === page.url()) break;
1312
+ await cand.bringToFront().catch(() => {
1313
+ });
1314
+ page = cand;
1315
+ break;
1316
+ }
1317
+ } catch {
1318
+ }
1319
+ }
1320
+ const urlNow = page.url();
1321
+ if (urlNow !== lastUrl) {
1322
+ history.push(` [\u9875\u9762\u8DF3\u8F6C \u2192 ${urlNow.substring(0, 80)}]`);
1323
+ lastUrl = urlNow;
1324
+ }
1325
+ }
1326
+ return ok8({ done: false, steps, summary: `\u5DF2\u8FBE\u6700\u5927\u6B65\u6570 ${maxSteps}`, model: creds.model });
1327
+ }
1328
+ });
1329
+
1330
+ // src/commands/evaluate.ts
1331
+ import { z as z9 } from "zod";
1332
+ import { ok as ok9, normalizeTips as normalizeTips2 } from "@dyyz1993/xcli-core";
928
1333
  var evaluateCommand = registerCommand({
929
1334
  name: "eval",
930
1335
  description: "Evaluate JavaScript expression in the browser (--frame <url-substring> \u53EF\u5B9A\u5411\u5230 iframe \u4E0A\u4E0B\u6587\uFF0C\u8DE8\u57DF iframe \u4EA6\u53EF)",
931
1336
  scope: "page",
932
- parameters: z7.object({
933
- expression: z7.string(),
934
- frame: z7.string().optional().describe("\u5728 URL \u542B\u6B64\u5B50\u4E32\u7684 iframe \u91CC\u6267\u884C\uFF08\u540C\u8FDB\u7A0B\u7528 contextId\uFF0C\u8DE8\u57DF OOPIF \u7528 Target auto-attach\uFF09")
1337
+ parameters: z9.object({
1338
+ expression: z9.string().optional(),
1339
+ frame: z9.string().optional().describe("\u5728 URL \u542B\u6B64\u5B50\u4E32\u7684 iframe \u91CC\u6267\u884C\uFF08\u540C\u8FDB\u7A0B\u7528 contextId\uFF0C\u8DE8\u57DF OOPIF \u7528 Target auto-attach\uFF09"),
1340
+ // Internal: base64url-encoded script set by the router for `eval <js...>`,
1341
+ // so multiline/semicolon JS survives chain parsing (which splits on ';'
1342
+ // and whitespace). Not intended for direct CLI use.
1343
+ "script-b64": z9.string().optional().describe("Internal: base64url script set by the CLI router; do not pass directly")
935
1344
  }),
936
1345
  handler: async (p, ctx) => {
1346
+ const rawScript = p["script-b64"];
1347
+ const expression = rawScript ? Buffer.from(rawScript, "base64url").toString("utf8") : p.expression;
1348
+ if (!expression || !expression.trim()) {
1349
+ return { ok: false, error: "eval: expression is required" };
1350
+ }
937
1351
  const engine = createRuleEngine();
938
1352
  engine.start();
939
1353
  const decision = engine.evaluate({
940
1354
  method: "Runtime.evaluate",
941
- params: { expression: p.expression },
1355
+ params: { expression },
942
1356
  sessionId: "eval-check",
943
1357
  direction: "client\u2192browser"
944
1358
  });
945
1359
  engine.stop();
946
- const result = p.frame ? await ctx.page.evaluateInFrame(p.frame, p.expression) : await ctx.page.evaluate(p.expression);
947
- const response = ok7({ result });
1360
+ const result = p.frame ? await ctx.page.evaluateInFrame(p.frame, expression) : await ctx.page.evaluate(expression);
1361
+ const response = ok9({ result });
948
1362
  if (decision && decision.severity === "danger") {
949
1363
  response.tips = normalizeTips2([`${decision.suggestion}`]);
950
1364
  }
@@ -952,37 +1366,65 @@ var evaluateCommand = registerCommand({
952
1366
  }
953
1367
  });
954
1368
 
1369
+ // src/commands/ua.ts
1370
+ import { z as z10 } from "zod";
1371
+ import { ok as ok10 } from "@dyyz1993/xcli-core";
1372
+ function uaHookScript(ua) {
1373
+ const uaJson = JSON.stringify(ua);
1374
+ const appVersion = JSON.stringify(ua.replace(/^Mozilla\//, ""));
1375
+ return `try{Object.defineProperty(navigator,'userAgent',{get:()=>${uaJson},configurable:true});Object.defineProperty(navigator,'appVersion',{get:()=>${appVersion},configurable:true});Object.defineProperty(navigator,'vendor',{get:()=>'Google Inc.',configurable:true});}catch(e){}`;
1376
+ }
1377
+ var uaCommand = registerCommand({
1378
+ name: "ua",
1379
+ description: "Get or override the session User-Agent (anti-bot UA detection countermeasure)",
1380
+ scope: "page",
1381
+ parameters: z10.object({
1382
+ value: z10.string().optional().describe('\u65B0\u7684 User-Agent\uFF1B\u7559\u7A7A\u5219\u8FD4\u56DE\u5F53\u524D UA\uFF1B"reset" \u6062\u590D\u5E38\u89C1 Chrome UA')
1383
+ }),
1384
+ handler: async (p, ctx) => {
1385
+ if (!p.value) {
1386
+ const current = await ctx.page.evaluate("navigator.userAgent");
1387
+ return ok10({ userAgent: current });
1388
+ }
1389
+ const ua = p.value.toLowerCase() === "reset" ? "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36" : p.value;
1390
+ const script = uaHookScript(ua);
1391
+ await ctx.page.addInitScript(script);
1392
+ await ctx.page.evaluate(`(function(){${script}})()`);
1393
+ return ok10({ userAgent: ua, mode: "init-script" });
1394
+ }
1395
+ });
1396
+
955
1397
  // src/commands/storage.ts
956
- import { z as z8 } from "zod";
957
- import { ok as ok8, fail as fail2 } from "@dyyz1993/xcli-core";
1398
+ import { z as z11 } from "zod";
1399
+ import { ok as ok11, fail as fail4 } from "@dyyz1993/xcli-core";
958
1400
  var getCookiesCommand = registerCommand({
959
1401
  name: "get-cookies",
960
1402
  description: "Get all cookies for the current page",
961
1403
  scope: "page",
962
- result: z8.object({
963
- cookies: z8.array(z8.record(z8.unknown()))
1404
+ result: z11.object({
1405
+ cookies: z11.array(z11.record(z11.unknown()))
964
1406
  }),
965
1407
  handler: async (_p, ctx) => {
966
1408
  const cookies = await ctx.browserContext.cookies();
967
- return ok8({ cookies });
1409
+ return ok11({ cookies });
968
1410
  }
969
1411
  });
970
1412
  var setCookieCommand = registerCommand({
971
1413
  name: "set-cookie",
972
1414
  description: "Set a cookie",
973
1415
  scope: "page",
974
- parameters: z8.object({
975
- name: z8.coerce.string(),
976
- value: z8.coerce.string(),
977
- domain: z8.coerce.string().optional(),
978
- path: z8.coerce.string().optional(),
979
- url: z8.string().optional().describe("Cookie URL (alternative to domain)"),
980
- expires: z8.number().optional(),
981
- httpOnly: z8.boolean().optional(),
982
- secure: z8.boolean().optional(),
983
- sameSite: z8.enum(["Strict", "Lax", "None"]).optional()
1416
+ parameters: z11.object({
1417
+ name: z11.coerce.string(),
1418
+ value: z11.coerce.string(),
1419
+ domain: z11.coerce.string().optional(),
1420
+ path: z11.coerce.string().optional(),
1421
+ url: z11.string().optional().describe("Cookie URL (alternative to domain)"),
1422
+ expires: z11.number().optional(),
1423
+ httpOnly: z11.boolean().optional(),
1424
+ secure: z11.boolean().optional(),
1425
+ sameSite: z11.enum(["Strict", "Lax", "None"]).optional()
984
1426
  }),
985
- result: z8.object({ name: z8.string() }),
1427
+ result: z11.object({ name: z11.string() }),
986
1428
  handler: async (p, ctx) => {
987
1429
  const cookie = { ...p };
988
1430
  if (!cookie.domain && !cookie.url) {
@@ -997,38 +1439,38 @@ var setCookieCommand = registerCommand({
997
1439
  }
998
1440
  }
999
1441
  if (!cookie.domain && !cookie.url) {
1000
- return fail2("set-cookie requires --domain or --url, or a non-blank page URL to infer from");
1442
+ return fail4("set-cookie requires --domain or --url, or a non-blank page URL to infer from");
1001
1443
  }
1002
1444
  await ctx.browserContext.addCookies([cookie]);
1003
- return ok8({ name: p.name });
1445
+ return ok11({ name: p.name });
1004
1446
  }
1005
1447
  });
1006
1448
  var clearCookiesCommand = registerCommand({
1007
1449
  name: "clear-cookies",
1008
1450
  description: "Clear all cookies",
1009
1451
  scope: "page",
1010
- result: z8.object({ cleared: z8.boolean() }),
1452
+ result: z11.object({ cleared: z11.boolean() }),
1011
1453
  handler: async (_p, ctx) => {
1012
1454
  await ctx.browserContext.clearCookies();
1013
- return ok8({ cleared: true });
1455
+ return ok11({ cleared: true });
1014
1456
  }
1015
1457
  });
1016
1458
  var getLocalStorageCommand = registerCommand({
1017
1459
  name: "get-local-storage",
1018
1460
  description: "Get localStorage entries",
1019
1461
  scope: "page",
1020
- parameters: z8.object({
1021
- key: z8.string().optional()
1462
+ parameters: z11.object({
1463
+ key: z11.string().optional()
1022
1464
  }),
1023
- result: z8.union([
1024
- z8.object({ key: z8.string(), value: z8.string().nullable() }),
1025
- z8.object({ data: z8.record(z8.string()) })
1465
+ result: z11.union([
1466
+ z11.object({ key: z11.string(), value: z11.string().nullable() }),
1467
+ z11.object({ data: z11.record(z11.string()) })
1026
1468
  ]),
1027
1469
  handler: async (p, ctx) => {
1028
1470
  try {
1029
1471
  if (p.key) {
1030
1472
  const value = await ctx.page.evaluate((k) => localStorage.getItem(k), p.key);
1031
- return ok8({ key: p.key, value });
1473
+ return ok11({ key: p.key, value });
1032
1474
  }
1033
1475
  const data = await ctx.page.evaluate(() => {
1034
1476
  const entries = {};
@@ -1038,9 +1480,9 @@ var getLocalStorageCommand = registerCommand({
1038
1480
  }
1039
1481
  return entries;
1040
1482
  });
1041
- return ok8({ data });
1483
+ return ok11({ data });
1042
1484
  } catch (e) {
1043
- return fail2(`localStorage not accessible on this page: ${e instanceof Error ? e.message : String(e)}`);
1485
+ return fail4(`localStorage not accessible on this page: ${e instanceof Error ? e.message : String(e)}`);
1044
1486
  }
1045
1487
  }
1046
1488
  });
@@ -1048,11 +1490,11 @@ var setLocalStorageCommand = registerCommand({
1048
1490
  name: "set-local-storage",
1049
1491
  description: "Set a localStorage entry",
1050
1492
  scope: "page",
1051
- parameters: z8.object({
1052
- key: z8.string(),
1053
- value: z8.string()
1493
+ parameters: z11.object({
1494
+ key: z11.string(),
1495
+ value: z11.string()
1054
1496
  }),
1055
- result: z8.object({ key: z8.string() }),
1497
+ result: z11.object({ key: z11.string() }),
1056
1498
  handler: async (p, ctx) => {
1057
1499
  try {
1058
1500
  await ctx.page.evaluate(
@@ -1061,9 +1503,9 @@ var setLocalStorageCommand = registerCommand({
1061
1503
  },
1062
1504
  { key: p.key, value: p.value }
1063
1505
  );
1064
- return ok8({ key: p.key });
1506
+ return ok11({ key: p.key });
1065
1507
  } catch (e) {
1066
- return fail2(`localStorage not accessible on this page: ${e instanceof Error ? e.message : String(e)}`);
1508
+ return fail4(`localStorage not accessible on this page: ${e instanceof Error ? e.message : String(e)}`);
1067
1509
  }
1068
1510
  }
1069
1511
  });
@@ -1071,26 +1513,24 @@ var clearLocalStorageCommand = registerCommand({
1071
1513
  name: "clear-local-storage",
1072
1514
  description: "Clear all localStorage entries",
1073
1515
  scope: "page",
1074
- result: z8.object({ cleared: z8.boolean() }),
1516
+ result: z11.object({ cleared: z11.boolean() }),
1075
1517
  handler: async (_p, ctx) => {
1076
1518
  try {
1077
1519
  await ctx.page.evaluate(() => localStorage.clear());
1078
- return ok8({ cleared: true });
1520
+ return ok11({ cleared: true });
1079
1521
  } catch (e) {
1080
- return fail2(`localStorage not accessible on this page: ${e instanceof Error ? e.message : String(e)}`);
1522
+ return fail4(`localStorage not accessible on this page: ${e instanceof Error ? e.message : String(e)}`);
1081
1523
  }
1082
1524
  }
1083
1525
  });
1084
1526
 
1085
1527
  // src/commands/screenshot.ts
1086
- import { z as z9 } from "zod";
1087
- import { ok as ok9, fail as fail3 } from "@dyyz1993/xcli-core";
1528
+ import { z as z12 } from "zod";
1529
+ import { ok as ok12, fail as fail5 } from "@dyyz1993/xcli-core";
1088
1530
  import { writeFileSync, mkdirSync } from "fs";
1089
- import { dirname, join as join2 } from "path";
1090
- import { homedir as homedir2 } from "os";
1091
- var SCREENSHOTS_DIR = join2(homedir2(), ".xbrowser", "screenshots");
1531
+ import { dirname, join as join4 } from "path";
1092
1532
  function ensureScreenshotsDir() {
1093
- mkdirSync(SCREENSHOTS_DIR, { recursive: true });
1533
+ mkdirSync(resolveScreenshotsDir(), { recursive: true });
1094
1534
  }
1095
1535
  function ensureParentDir(filePath) {
1096
1536
  const dir = dirname(filePath);
@@ -1106,30 +1546,30 @@ function generateScreenshotPath(format) {
1106
1546
  const timestamp = Date.now();
1107
1547
  const random = Math.random().toString(36).slice(2, 8);
1108
1548
  const ext = format === "jpeg" ? "jpg" : "png";
1109
- return join2(SCREENSHOTS_DIR, `screenshot-${timestamp}-${random}.${ext}`);
1549
+ return join4(resolveScreenshotsDir(), `screenshot-${timestamp}-${random}.${ext}`);
1110
1550
  }
1111
1551
  var screenshotCommand = registerCommand({
1112
1552
  name: "screenshot",
1113
- description: "Take a screenshot of the page or element",
1553
+ description: "Take a screenshot of the page or element (auto-saved to the OS temp dir unless --output is given; persistent override: xbrowser config set screenshots.dir <path>)",
1114
1554
  scope: "page",
1115
1555
  selectorParams: ["selector"],
1116
- parameters: z9.object({
1117
- selector: z9.string().optional(),
1118
- type: z9.enum(["png", "jpeg"]).optional(),
1119
- fullPage: z9.boolean().optional(),
1120
- output: z9.string().optional(),
1121
- base64: z9.boolean().optional().describe("Return base64 data instead of file path")
1556
+ parameters: z12.object({
1557
+ selector: z12.string().optional(),
1558
+ type: z12.enum(["png", "jpeg"]).optional(),
1559
+ fullPage: z12.boolean().optional(),
1560
+ output: z12.string().optional(),
1561
+ base64: z12.boolean().optional().describe("Return base64 data instead of file path")
1122
1562
  }),
1123
- result: z9.union([
1124
- z9.object({
1125
- data: z9.string(),
1126
- format: z9.string(),
1127
- size: z9.number()
1563
+ result: z12.union([
1564
+ z12.object({
1565
+ data: z12.string(),
1566
+ format: z12.string(),
1567
+ size: z12.number()
1128
1568
  }),
1129
- z9.object({
1130
- output: z9.string(),
1131
- format: z9.string(),
1132
- size: z9.number()
1569
+ z12.object({
1570
+ output: z12.string(),
1571
+ format: z12.string(),
1572
+ size: z12.number()
1133
1573
  })
1134
1574
  ]),
1135
1575
  handler: async (p, ctx) => {
@@ -1147,21 +1587,21 @@ var screenshotCommand = registerCommand({
1147
1587
  if (p.output) {
1148
1588
  const dirErr = ensureParentDir(p.output);
1149
1589
  if (dirErr) {
1150
- return fail3(`Cannot create directory for --output "${p.output}": ${dirErr}`);
1590
+ return fail5(`Cannot create directory for --output "${p.output}": ${dirErr}`);
1151
1591
  }
1152
1592
  try {
1153
1593
  writeFileSync(p.output, buffer, "binary");
1154
1594
  } catch (err) {
1155
- return fail3(`Failed to write screenshot to "${p.output}": ${err instanceof Error ? err.message : String(err)}`);
1595
+ return fail5(`Failed to write screenshot to "${p.output}": ${err instanceof Error ? err.message : String(err)}`);
1156
1596
  }
1157
- return ok9({
1597
+ return ok12({
1158
1598
  output: p.output,
1159
1599
  format,
1160
1600
  size: buffer.length
1161
1601
  });
1162
1602
  }
1163
1603
  if (p.base64) {
1164
- return ok9({
1604
+ return ok12({
1165
1605
  data: buffer.toString("base64"),
1166
1606
  format,
1167
1607
  size: buffer.length
@@ -1170,7 +1610,7 @@ var screenshotCommand = registerCommand({
1170
1610
  ensureScreenshotsDir();
1171
1611
  const screenshotPath = generateScreenshotPath(format);
1172
1612
  writeFileSync(screenshotPath, buffer, "binary");
1173
- return ok9({
1613
+ return ok12({
1174
1614
  output: screenshotPath,
1175
1615
  format,
1176
1616
  size: buffer.length
@@ -1179,19 +1619,19 @@ var screenshotCommand = registerCommand({
1179
1619
  });
1180
1620
 
1181
1621
  // src/commands/structure.ts
1182
- import { z as z10 } from "zod";
1183
- import { ok as ok10 } from "@dyyz1993/xcli-core";
1622
+ import { z as z13 } from "zod";
1623
+ import { ok as ok13 } from "@dyyz1993/xcli-core";
1184
1624
  var structureCommand = registerCommand({
1185
1625
  name: "structure",
1186
1626
  description: "Get the DOM structure of the page or an element",
1187
1627
  scope: "page",
1188
1628
  selectorParams: ["selector"],
1189
- parameters: z10.object({
1190
- selector: z10.string().optional(),
1191
- depth: z10.number().optional()
1629
+ parameters: z13.object({
1630
+ selector: z13.string().optional(),
1631
+ depth: z13.number().optional()
1192
1632
  }),
1193
- result: z10.object({
1194
- structure: z10.record(z10.unknown())
1633
+ result: z13.object({
1634
+ structure: z13.record(z13.unknown())
1195
1635
  }),
1196
1636
  handler: async (p, ctx) => {
1197
1637
  const structure = await ctx.page.evaluate(
@@ -1223,30 +1663,30 @@ var structureCommand = registerCommand({
1223
1663
  },
1224
1664
  { sel: p.selector || "body", maxDepth: p.depth || 5 }
1225
1665
  );
1226
- return ok10({ structure });
1666
+ return ok13({ structure });
1227
1667
  }
1228
1668
  });
1229
1669
 
1230
1670
  // src/commands/viewport.ts
1231
- import { z as z11 } from "zod";
1232
- import { ok as ok11 } from "@dyyz1993/xcli-core";
1671
+ import { z as z14 } from "zod";
1672
+ import { ok as ok14 } from "@dyyz1993/xcli-core";
1233
1673
  var setViewportCommand = registerCommand({
1234
1674
  name: "set-viewport",
1235
1675
  description: "Set the viewport size and properties",
1236
1676
  scope: "browser",
1237
- parameters: z11.object({
1238
- width: z11.coerce.number(),
1239
- height: z11.coerce.number(),
1240
- deviceScaleFactor: z11.coerce.number().optional(),
1241
- isMobile: z11.boolean().optional(),
1242
- hasTouch: z11.boolean().optional()
1677
+ parameters: z14.object({
1678
+ width: z14.coerce.number(),
1679
+ height: z14.coerce.number(),
1680
+ deviceScaleFactor: z14.coerce.number().optional(),
1681
+ isMobile: z14.boolean().optional(),
1682
+ hasTouch: z14.boolean().optional()
1243
1683
  }),
1244
- result: z11.object({
1245
- width: z11.number(),
1246
- height: z11.number(),
1247
- deviceScaleFactor: z11.number().optional(),
1248
- isMobile: z11.boolean().optional(),
1249
- hasTouch: z11.boolean().optional()
1684
+ result: z14.object({
1685
+ width: z14.number(),
1686
+ height: z14.number(),
1687
+ deviceScaleFactor: z14.number().optional(),
1688
+ isMobile: z14.boolean().optional(),
1689
+ hasTouch: z14.boolean().optional()
1250
1690
  }),
1251
1691
  handler: async (p, ctx) => {
1252
1692
  const viewport = ctx.page.viewportSize();
@@ -1259,7 +1699,7 @@ var setViewportCommand = registerCommand({
1259
1699
  ...p.isMobile !== void 0 && { isMobile: p.isMobile },
1260
1700
  ...p.hasTouch !== void 0 && { hasTouch: p.hasTouch }
1261
1701
  });
1262
- return ok11({
1702
+ return ok14({
1263
1703
  width,
1264
1704
  height,
1265
1705
  ...p.deviceScaleFactor !== void 0 && { deviceScaleFactor: p.deviceScaleFactor },
@@ -1270,17 +1710,17 @@ var setViewportCommand = registerCommand({
1270
1710
  });
1271
1711
 
1272
1712
  // src/commands/frame.ts
1273
- import { z as z12 } from "zod";
1274
- import { ok as ok12, fail as fail4 } from "@dyyz1993/xcli-core";
1713
+ import { z as z15 } from "zod";
1714
+ import { ok as ok15, fail as fail6 } from "@dyyz1993/xcli-core";
1275
1715
  var framesCommand = registerCommand({
1276
1716
  name: "frames",
1277
1717
  description: "List all frames in the current page",
1278
1718
  scope: "page",
1279
- result: z12.object({
1280
- frames: z12.array(z12.object({
1281
- index: z12.number(),
1282
- name: z12.string().nullable(),
1283
- url: z12.string()
1719
+ result: z15.object({
1720
+ frames: z15.array(z15.object({
1721
+ index: z15.number(),
1722
+ name: z15.string().nullable(),
1723
+ url: z15.string()
1284
1724
  }))
1285
1725
  }),
1286
1726
  handler: async (_p, ctx) => {
@@ -1291,21 +1731,21 @@ var framesCommand = registerCommand({
1291
1731
  name: frame.name(),
1292
1732
  url: frame.url()
1293
1733
  }));
1294
- return ok12({ frames: frameList });
1734
+ return ok15({ frames: frameList });
1295
1735
  }
1296
1736
  });
1297
1737
  var frameCommand = registerCommand({
1298
1738
  name: "frame",
1299
1739
  description: "Get frame info by index or name",
1300
1740
  scope: "page",
1301
- parameters: z12.object({
1302
- index: z12.number().int().min(0).optional(),
1303
- name: z12.string().optional()
1741
+ parameters: z15.object({
1742
+ index: z15.number().int().min(0).optional(),
1743
+ name: z15.string().optional()
1304
1744
  }),
1305
- result: z12.object({
1306
- name: z12.string().nullable(),
1307
- url: z12.string(),
1308
- error: z12.string().optional()
1745
+ result: z15.object({
1746
+ name: z15.string().nullable(),
1747
+ url: z15.string(),
1748
+ error: z15.string().optional()
1309
1749
  }),
1310
1750
  handler: async (p, ctx) => {
1311
1751
  const discover = ctx.page.discoverFrames;
@@ -1316,12 +1756,12 @@ var frameCommand = registerCommand({
1316
1756
  } else if (p.name !== void 0) {
1317
1757
  targetFrame = rawFrames.find((f) => f.name() === p.name);
1318
1758
  } else {
1319
- return fail4("Must provide index or name");
1759
+ return fail6("Must provide index or name");
1320
1760
  }
1321
1761
  if (!targetFrame) {
1322
- return fail4("Frame not found");
1762
+ return fail6("Frame not found");
1323
1763
  }
1324
- return ok12({
1764
+ return ok15({
1325
1765
  name: targetFrame.name(),
1326
1766
  url: targetFrame.url()
1327
1767
  });
@@ -1329,27 +1769,27 @@ var frameCommand = registerCommand({
1329
1769
  });
1330
1770
 
1331
1771
  // src/commands/ui-debug.ts
1332
- import { z as z13 } from "zod";
1333
- import { ok as ok13 } from "@dyyz1993/xcli-core";
1772
+ import { z as z16 } from "zod";
1773
+ import { ok as ok16 } from "@dyyz1993/xcli-core";
1334
1774
  var consoleCheckCommand = registerCommand({
1335
1775
  name: "console",
1336
1776
  description: "Collect and analyze browser console messages (errors, warnings, logs)",
1337
1777
  scope: "page",
1338
- parameters: z13.object({
1339
- url: z13.string().optional().describe("URL to navigate first (optional, uses current page if omitted)"),
1340
- duration: z13.number().optional().default(5e3).describe("How long to collect messages (ms)"),
1341
- filter: z13.enum(["all", "error", "warning", "info", "log"]).optional().default("all"),
1342
- includeStackTraces: z13.boolean().optional().default(true)
1778
+ parameters: z16.object({
1779
+ url: z16.string().optional().describe("URL to navigate first (optional, uses current page if omitted)"),
1780
+ duration: z16.number().optional().default(5e3).describe("How long to collect messages (ms)"),
1781
+ filter: z16.enum(["all", "error", "warning", "info", "log"]).optional().default("all"),
1782
+ includeStackTraces: z16.boolean().optional().default(true)
1343
1783
  }),
1344
- result: z13.object({
1345
- url: z13.string(),
1346
- duration: z13.number(),
1347
- total: z13.number(),
1348
- errors: z13.number(),
1349
- warnings: z13.number(),
1350
- messages: z13.array(z13.record(z13.unknown())),
1351
- summary: z13.string(),
1352
- passed: z13.boolean()
1784
+ result: z16.object({
1785
+ url: z16.string(),
1786
+ duration: z16.number(),
1787
+ total: z16.number(),
1788
+ errors: z16.number(),
1789
+ warnings: z16.number(),
1790
+ messages: z16.array(z16.record(z16.unknown())),
1791
+ summary: z16.string(),
1792
+ passed: z16.boolean()
1353
1793
  }),
1354
1794
  handler: async (p, ctx) => {
1355
1795
  const { page } = ctx;
@@ -1431,7 +1871,7 @@ ${a.stack || ""}`;
1431
1871
  const filtered = p.filter === "all" ? messages : messages.filter((m) => m.type === p.filter);
1432
1872
  const errorCount = messages.filter((m) => m.type === "error").length;
1433
1873
  const warnCount = messages.filter((m) => m.type === "warning").length;
1434
- return ok13({
1874
+ return ok16({
1435
1875
  url: page.url(),
1436
1876
  duration: p.duration,
1437
1877
  total: messages.length,
@@ -1447,23 +1887,23 @@ var networkCheckCommand = registerCommand({
1447
1887
  name: "net-debug",
1448
1888
  description: "Monitor and analyze network requests \u2014 capture failed requests, slow responses, status codes",
1449
1889
  scope: "page",
1450
- parameters: z13.object({
1451
- url: z13.string().optional().describe("URL to navigate first"),
1452
- duration: z13.number().optional().default(5e3).describe("How long to monitor (ms)"),
1453
- filter: z13.enum(["all", "failed", "slow", "error", "xhr", "fetch", "document", "stylesheet", "script", "image"]).optional().default("all"),
1454
- slowThreshold: z13.number().optional().default(3e3).describe('Threshold for "slow" requests (ms)')
1890
+ parameters: z16.object({
1891
+ url: z16.string().optional().describe("URL to navigate first"),
1892
+ duration: z16.number().optional().default(5e3).describe("How long to monitor (ms)"),
1893
+ filter: z16.enum(["all", "failed", "slow", "error", "xhr", "fetch", "document", "stylesheet", "script", "image"]).optional().default("all"),
1894
+ slowThreshold: z16.number().optional().default(3e3).describe('Threshold for "slow" requests (ms)')
1455
1895
  }),
1456
- result: z13.object({
1457
- url: z13.string(),
1458
- duration: z13.number(),
1459
- totalRequests: z13.number(),
1460
- failedRequests: z13.number(),
1461
- slowRequests: z13.number(),
1462
- errorRequests: z13.number(),
1463
- totalSizeKB: z13.number(),
1464
- requests: z13.array(z13.record(z13.unknown())),
1465
- summary: z13.string(),
1466
- passed: z13.boolean()
1896
+ result: z16.object({
1897
+ url: z16.string(),
1898
+ duration: z16.number(),
1899
+ totalRequests: z16.number(),
1900
+ failedRequests: z16.number(),
1901
+ slowRequests: z16.number(),
1902
+ errorRequests: z16.number(),
1903
+ totalSizeKB: z16.number(),
1904
+ requests: z16.array(z16.record(z16.unknown())),
1905
+ summary: z16.string(),
1906
+ passed: z16.boolean()
1467
1907
  }),
1468
1908
  handler: async (p, ctx) => {
1469
1909
  const { page } = ctx;
@@ -1545,7 +1985,7 @@ var networkCheckCommand = registerCommand({
1545
1985
  const slowCount = requests.filter((r) => r.duration >= p.slowThreshold).length;
1546
1986
  const errorCount = requests.filter((r) => r.error).length;
1547
1987
  const totalSize = requests.reduce((sum, r) => sum + r.size, 0);
1548
- return ok13({
1988
+ return ok16({
1549
1989
  url: page.url(),
1550
1990
  duration: p.duration,
1551
1991
  totalRequests: requests.length,
@@ -1567,17 +2007,17 @@ var perfCheckCommand = registerCommand({
1567
2007
  name: "perf",
1568
2008
  description: "Audit page performance metrics \u2014 load time, FCP, LCP, CLS, TTFB, resource sizes",
1569
2009
  scope: "page",
1570
- parameters: z13.object({
1571
- url: z13.string().optional().describe("URL to navigate (uses current page if omitted)"),
1572
- iterations: z13.number().optional().default(1).describe("Number of iterations to average")
2010
+ parameters: z16.object({
2011
+ url: z16.string().optional().describe("URL to navigate (uses current page if omitted)"),
2012
+ iterations: z16.number().optional().default(1).describe("Number of iterations to average")
1573
2013
  }),
1574
- result: z13.object({
1575
- url: z13.string(),
1576
- iterations: z13.number(),
1577
- metrics: z13.record(z13.unknown()),
1578
- allIterations: z13.array(z13.record(z13.unknown())).optional(),
1579
- passed: z13.boolean(),
1580
- summary: z13.string()
2014
+ result: z16.object({
2015
+ url: z16.string(),
2016
+ iterations: z16.number(),
2017
+ metrics: z16.record(z16.unknown()),
2018
+ allIterations: z16.array(z16.record(z16.unknown())).optional(),
2019
+ passed: z16.boolean(),
2020
+ summary: z16.string()
1581
2021
  }),
1582
2022
  handler: async (p, ctx) => {
1583
2023
  const { page } = ctx;
@@ -1639,7 +2079,7 @@ var perfCheckCommand = registerCommand({
1639
2079
  if (resourceStats) avg.resourceStats = resourceStats;
1640
2080
  return avg;
1641
2081
  })();
1642
- return ok13({
2082
+ return ok16({
1643
2083
  url: page.url(),
1644
2084
  iterations: p.iterations,
1645
2085
  metrics: avgMetrics,
@@ -1653,23 +2093,23 @@ var healthCheckCommand = registerCommand({
1653
2093
  name: "health",
1654
2094
  description: "Comprehensive page health check \u2014 broken links, missing images, console errors, SEO issues",
1655
2095
  scope: "page",
1656
- parameters: z13.object({
1657
- url: z13.string().optional().describe("URL to check"),
1658
- checkLinks: z13.boolean().optional().default(true).describe("Check for broken links"),
1659
- checkImages: z13.boolean().optional().default(true).describe("Check for missing/broken images"),
1660
- checkMeta: z13.boolean().optional().default(true).describe("Check SEO meta tags"),
1661
- maxLinks: z13.number().optional().default(50).describe("Max links to check")
2096
+ parameters: z16.object({
2097
+ url: z16.string().optional().describe("URL to check"),
2098
+ checkLinks: z16.boolean().optional().default(true).describe("Check for broken links"),
2099
+ checkImages: z16.boolean().optional().default(true).describe("Check for missing/broken images"),
2100
+ checkMeta: z16.boolean().optional().default(true).describe("Check SEO meta tags"),
2101
+ maxLinks: z16.number().optional().default(50).describe("Max links to check")
1662
2102
  }),
1663
- result: z13.object({
1664
- url: z13.string(),
1665
- title: z13.string(),
1666
- passed: z13.boolean(),
1667
- totalIssues: z13.number(),
1668
- errors: z13.number(),
1669
- warnings: z13.number(),
1670
- info: z13.number(),
1671
- issues: z13.array(z13.record(z13.unknown())),
1672
- summary: z13.string()
2103
+ result: z16.object({
2104
+ url: z16.string(),
2105
+ title: z16.string(),
2106
+ passed: z16.boolean(),
2107
+ totalIssues: z16.number(),
2108
+ errors: z16.number(),
2109
+ warnings: z16.number(),
2110
+ info: z16.number(),
2111
+ issues: z16.array(z16.record(z16.unknown())),
2112
+ summary: z16.string()
1673
2113
  }),
1674
2114
  handler: async (p, ctx) => {
1675
2115
  const { page } = ctx;
@@ -1756,7 +2196,7 @@ var healthCheckCommand = registerCommand({
1756
2196
  }, { checkLinks: p.checkLinks, checkImages: p.checkImages, checkMeta: p.checkMeta, maxLinks: p.maxLinks });
1757
2197
  const errors = result.issues.filter((i) => i.severity === "error").length;
1758
2198
  const warnings = result.issues.filter((i) => i.severity === "warning").length;
1759
- return ok13({
2199
+ return ok16({
1760
2200
  url: result.url,
1761
2201
  title: result.title,
1762
2202
  passed: errors === 0,
@@ -1771,68 +2211,66 @@ var healthCheckCommand = registerCommand({
1771
2211
  });
1772
2212
 
1773
2213
  // src/commands/actions.ts
1774
- import { z as z14 } from "zod";
1775
- import { ok as ok14 } from "@dyyz1993/xcli-core";
2214
+ import { z as z17 } from "zod";
2215
+ import { ok as ok17 } from "@dyyz1993/xcli-core";
1776
2216
  import { writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
1777
- import { join as join3 } from "path";
1778
- import { homedir as homedir3 } from "os";
1779
- var SCREENSHOTS_DIR2 = join3(homedir3(), ".xbrowser", "screenshots");
2217
+ import { join as join5 } from "path";
1780
2218
  function ensureScreenshotsDir2() {
1781
- mkdirSync2(SCREENSHOTS_DIR2, { recursive: true });
2219
+ mkdirSync2(resolveScreenshotsDir(), { recursive: true });
1782
2220
  }
1783
2221
  function generateScreenshotPath2(format) {
1784
2222
  const timestamp = Date.now();
1785
2223
  const random = Math.random().toString(36).slice(2, 8);
1786
2224
  const ext = format === "jpeg" ? "jpg" : "png";
1787
- return join3(SCREENSHOTS_DIR2, `screenshot-${timestamp}-${random}.${ext}`);
2225
+ return join5(resolveScreenshotsDir(), `screenshot-${timestamp}-${random}.${ext}`);
1788
2226
  }
1789
- var waitActionSchema = z14.object({
1790
- type: z14.literal("wait"),
1791
- milliseconds: z14.number().positive().optional(),
1792
- selector: z14.string().optional()
2227
+ var waitActionSchema = z17.object({
2228
+ type: z17.literal("wait"),
2229
+ milliseconds: z17.number().positive().optional(),
2230
+ selector: z17.string().optional()
1793
2231
  }).refine(
1794
2232
  (d) => (d.milliseconds !== void 0 || d.selector !== void 0) && !(d.milliseconds !== void 0 && d.selector !== void 0),
1795
2233
  { message: "Either 'milliseconds' or 'selector' must be provided, but not both." }
1796
2234
  );
1797
- var clickActionSchema = z14.object({
1798
- type: z14.literal("click"),
1799
- selector: z14.string(),
1800
- all: z14.boolean().optional()
2235
+ var clickActionSchema = z17.object({
2236
+ type: z17.literal("click"),
2237
+ selector: z17.string(),
2238
+ all: z17.boolean().optional()
1801
2239
  });
1802
- var screenshotActionSchema = z14.object({
1803
- type: z14.literal("screenshot"),
1804
- fullPage: z14.boolean().optional(),
1805
- quality: z14.number().min(1).max(100).optional(),
1806
- viewport: z14.object({ width: z14.number().int().positive(), height: z14.number().int().positive() }).optional(),
1807
- base64: z14.boolean().optional().describe("Return base64 data instead of file path")
2240
+ var screenshotActionSchema = z17.object({
2241
+ type: z17.literal("screenshot"),
2242
+ fullPage: z17.boolean().optional(),
2243
+ quality: z17.number().min(1).max(100).optional(),
2244
+ viewport: z17.object({ width: z17.number().int().positive(), height: z17.number().int().positive() }).optional(),
2245
+ base64: z17.boolean().optional().describe("Return base64 data instead of file path")
1808
2246
  });
1809
- var writeActionSchema = z14.object({
1810
- type: z14.literal("write"),
1811
- text: z14.string()
2247
+ var writeActionSchema = z17.object({
2248
+ type: z17.literal("write"),
2249
+ text: z17.string()
1812
2250
  });
1813
- var pressActionSchema = z14.object({
1814
- type: z14.literal("press"),
1815
- key: z14.string()
2251
+ var pressActionSchema = z17.object({
2252
+ type: z17.literal("press"),
2253
+ key: z17.string()
1816
2254
  });
1817
- var scrollActionSchema = z14.object({
1818
- type: z14.literal("scroll"),
1819
- direction: z14.enum(["up", "down"]).optional(),
1820
- selector: z14.string().optional()
2255
+ var scrollActionSchema = z17.object({
2256
+ type: z17.literal("scroll"),
2257
+ direction: z17.enum(["up", "down"]).optional(),
2258
+ selector: z17.string().optional()
1821
2259
  });
1822
- var scrapeActionSchema = z14.object({
1823
- type: z14.literal("scrape")
2260
+ var scrapeActionSchema = z17.object({
2261
+ type: z17.literal("scrape")
1824
2262
  });
1825
- var executeJavascriptActionSchema = z14.object({
1826
- type: z14.literal("executeJavascript"),
1827
- script: z14.string()
2263
+ var executeJavascriptActionSchema = z17.object({
2264
+ type: z17.literal("executeJavascript"),
2265
+ script: z17.string()
1828
2266
  });
1829
- var pdfActionSchema = z14.object({
1830
- type: z14.literal("pdf"),
1831
- landscape: z14.boolean().optional(),
1832
- scale: z14.number().optional(),
1833
- format: z14.enum(["A0", "A1", "A2", "A3", "A4", "A5", "A6", "Letter", "Legal", "Tabloid", "Ledger"]).optional()
2267
+ var pdfActionSchema = z17.object({
2268
+ type: z17.literal("pdf"),
2269
+ landscape: z17.boolean().optional(),
2270
+ scale: z17.number().optional(),
2271
+ format: z17.enum(["A0", "A1", "A2", "A3", "A4", "A5", "A6", "Letter", "Legal", "Tabloid", "Ledger"]).optional()
1834
2272
  });
1835
- var actionSchema = z14.union([
2273
+ var actionSchema = z17.union([
1836
2274
  waitActionSchema,
1837
2275
  clickActionSchema,
1838
2276
  screenshotActionSchema,
@@ -1923,11 +2361,11 @@ var actionsCommand = registerCommand({
1923
2361
  name: "actions",
1924
2362
  description: "Execute a sequence of actions (wait, click, scroll, screenshot, fill, etc.)",
1925
2363
  scope: "page",
1926
- parameters: z14.object({
1927
- url: z14.string().describe("Starting URL"),
1928
- actions: z14.array(actionSchema).max(MAX_ACTIONS).describe("Array of actions (max 50)"),
1929
- output: z14.enum(["text", "json"]).default("json").describe("Output format: text or json"),
1930
- timeout: z14.number().default(60).describe("Overall timeout in seconds (default: 60)")
2364
+ parameters: z17.object({
2365
+ url: z17.string().describe("Starting URL"),
2366
+ actions: z17.array(actionSchema).max(MAX_ACTIONS).describe("Array of actions (max 50)"),
2367
+ output: z17.enum(["text", "json"]).default("json").describe("Output format: text or json"),
2368
+ timeout: z17.number().default(60).describe("Overall timeout in seconds (default: 60)")
1931
2369
  }),
1932
2370
  handler: async (p, ctx) => {
1933
2371
  await ctx.page.goto(p.url, { waitUntil: "domcontentloaded" });
@@ -1947,14 +2385,14 @@ var actionsCommand = registerCommand({
1947
2385
  const finalUrl = ctx.page.url();
1948
2386
  const timedOut = results.length < p.actions.length;
1949
2387
  if (p.output === "text") {
1950
- return ok14({
2388
+ return ok17({
1951
2389
  title,
1952
2390
  url: finalUrl,
1953
2391
  actions: results.map((r) => JSON.stringify(r)).join("\n"),
1954
2392
  ...timedOut ? { warning: `Timed out after ${p.timeout}s, completed ${results.length}/${p.actions.length} actions` } : {}
1955
2393
  });
1956
2394
  }
1957
- return ok14({
2395
+ return ok17({
1958
2396
  title,
1959
2397
  url: finalUrl,
1960
2398
  results,
@@ -1964,8 +2402,8 @@ var actionsCommand = registerCommand({
1964
2402
  });
1965
2403
 
1966
2404
  // src/commands/scrape.ts
1967
- import { z as z15 } from "zod";
1968
- import { ok as ok15, fail as fail5 } from "@dyyz1993/xcli-core";
2405
+ import { z as z18 } from "zod";
2406
+ import { ok as ok18, fail as fail7 } from "@dyyz1993/xcli-core";
1969
2407
 
1970
2408
  // src/lib/html-to-markdown.ts
1971
2409
  import * as cheerio from "cheerio";
@@ -2350,15 +2788,15 @@ var scrapeCommand = registerCommand({
2350
2788
  description: "Scrape a page and convert to Markdown (with JS rendering)",
2351
2789
  scope: "project",
2352
2790
  selectorParams: ["selector"],
2353
- parameters: z15.object({
2354
- url: z15.string().optional(),
2355
- selector: z15.string().optional(),
2356
- timeout: z15.number().default(3e4),
2357
- format: z15.enum(["markdown", "html", "text"]).default("markdown"),
2358
- onlyMainContent: z15.boolean().default(true),
2359
- retries: z15.number().int().min(0).max(5).optional().default(2).describe("\u91CD\u8BD5\u6B21\u6570\uFF08\u9ED8\u8BA4 2\uFF09"),
2360
- waitAfterLoad: z15.number().int().optional().default(0).describe("\u9875\u9762\u52A0\u8F7D\u540E\u989D\u5916\u7B49\u5F85\u6BEB\u79D2"),
2361
- mode: z15.enum(["raw", "clean", "compact", "smart"]).default("raw").describe("\u8F93\u51FA\u6A21\u5F0F\uFF1Araw\uFF08\u9ED8\u8BA4\uFF09/ clean\uFF08\u7ED3\u6784\u5316\uFF09/ compact\uFF08\u7CBE\u7B80\uFF09/ smart\uFF08\u7ED3\u6784\u5316\uFF0C\u7559\u7ED9 agent \u589E\u5F3A\uFF09")
2791
+ parameters: z18.object({
2792
+ url: z18.string().optional(),
2793
+ selector: z18.string().optional(),
2794
+ timeout: z18.number().default(3e4),
2795
+ format: z18.enum(["markdown", "html", "text"]).default("markdown"),
2796
+ onlyMainContent: z18.boolean().default(true),
2797
+ retries: z18.number().int().min(0).max(5).optional().default(2).describe("\u91CD\u8BD5\u6B21\u6570\uFF08\u9ED8\u8BA4 2\uFF09"),
2798
+ waitAfterLoad: z18.number().int().optional().default(0).describe("\u9875\u9762\u52A0\u8F7D\u540E\u989D\u5916\u7B49\u5F85\u6BEB\u79D2"),
2799
+ mode: z18.enum(["raw", "clean", "compact", "smart"]).default("raw").describe("\u8F93\u51FA\u6A21\u5F0F\uFF1Araw\uFF08\u9ED8\u8BA4\uFF09/ clean\uFF08\u7ED3\u6784\u5316\uFF09/ compact\uFF08\u7CBE\u7B80\uFF09/ smart\uFF08\u7ED3\u6784\u5316\uFF0C\u7559\u7ED9 agent \u589E\u5F3A\uFF09")
2362
2800
  }),
2363
2801
  handler: async (p, ctx) => {
2364
2802
  const { context, page } = await createEphemeralContext(resolveLaunchOpts(ctx));
@@ -2366,7 +2804,7 @@ var scrapeCommand = registerCommand({
2366
2804
  try {
2367
2805
  const targetUrl = p.url || page.url();
2368
2806
  if (!targetUrl || targetUrl === "about:blank") {
2369
- return fail5("URL is required. Provide a URL or connect to a browser (--cdp) with a non-blank page.");
2807
+ return fail7("URL is required. Provide a URL or connect to a browser (--cdp) with a non-blank page.");
2370
2808
  }
2371
2809
  let lastError;
2372
2810
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
@@ -2436,7 +2874,7 @@ var scrapeCommand = registerCommand({
2436
2874
  } catch {
2437
2875
  }
2438
2876
  if (p.mode === "smart") {
2439
- return ok15(structured);
2877
+ return ok18(structured);
2440
2878
  }
2441
2879
  if (p.mode === "compact") {
2442
2880
  const compactData = structured.tables.length > 0 ? structured.tables.map((t) => ({
@@ -2449,9 +2887,9 @@ var scrapeCommand = registerCommand({
2449
2887
  return compact;
2450
2888
  })
2451
2889
  })) : structured.mainText?.substring(0, 500);
2452
- return ok15({ url: structured.url, title: structured.title, data: compactData });
2890
+ return ok18({ url: structured.url, title: structured.title, data: compactData });
2453
2891
  }
2454
- return ok15(structured);
2892
+ return ok18(structured);
2455
2893
  }
2456
2894
  let content;
2457
2895
  switch (p.format) {
@@ -2510,7 +2948,7 @@ var scrapeCommand = registerCommand({
2510
2948
  content = await page.innerText("body");
2511
2949
  break;
2512
2950
  }
2513
- return ok15({ content, title, url: finalUrl });
2951
+ return ok18({ content, title, url: finalUrl });
2514
2952
  } catch (err) {
2515
2953
  lastError = err instanceof Error ? err : new Error(String(err));
2516
2954
  if (attempt < maxAttempts) {
@@ -2520,20 +2958,20 @@ var scrapeCommand = registerCommand({
2520
2958
  }
2521
2959
  }
2522
2960
  }
2523
- return fail5(`Scrape failed after ${maxAttempts} attempt(s): ${lastError?.message ?? "unknown error"}`);
2961
+ return fail7(`Scrape failed after ${maxAttempts} attempt(s): ${lastError?.message ?? "unknown error"}`);
2524
2962
  } finally {
2525
2963
  await closeEphemeralContext(context);
2526
2964
  }
2527
2965
  },
2528
- result: z15.object({
2529
- url: z15.string(),
2530
- title: z15.string()
2966
+ result: z18.object({
2967
+ url: z18.string(),
2968
+ title: z18.string()
2531
2969
  }).passthrough()
2532
2970
  });
2533
2971
 
2534
2972
  // src/commands/map.ts
2535
- import { z as z16 } from "zod";
2536
- import { ok as ok16, fail as fail6 } from "@dyyz1993/xcli-core";
2973
+ import { z as z19 } from "zod";
2974
+ import { ok as ok19, fail as fail8 } from "@dyyz1993/xcli-core";
2537
2975
 
2538
2976
  // src/utils/url.ts
2539
2977
  var SKIP_EXTENSIONS = /* @__PURE__ */ new Set([
@@ -2768,21 +3206,21 @@ var mapCommand = registerCommand({
2768
3206
  name: "map",
2769
3207
  description: "Discover all URLs on a website via sitemap and page link extraction",
2770
3208
  scope: "project",
2771
- parameters: z16.object({
2772
- url: z16.string().optional(),
2773
- search: z16.string().optional(),
2774
- sitemap: z16.enum(["include", "only"]).optional(),
2775
- includeSubdomains: z16.boolean().optional(),
2776
- allowExternalLinks: z16.boolean().optional().describe("Include links to external domains"),
2777
- limit: z16.number().optional(),
2778
- verbose: z16.boolean().default(false).describe("Show progress feedback")
3209
+ parameters: z19.object({
3210
+ url: z19.string().optional(),
3211
+ search: z19.string().optional(),
3212
+ sitemap: z19.enum(["include", "only"]).optional(),
3213
+ includeSubdomains: z19.boolean().optional(),
3214
+ allowExternalLinks: z19.boolean().optional().describe("Include links to external domains"),
3215
+ limit: z19.number().optional(),
3216
+ verbose: z19.boolean().default(false).describe("Show progress feedback")
2779
3217
  }),
2780
3218
  handler: async (p, ctx) => {
2781
3219
  const { context, page } = await createEphemeralContext(resolveLaunchOpts(ctx));
2782
3220
  try {
2783
3221
  const targetUrl = p.url || page.url();
2784
3222
  if (!targetUrl || targetUrl === "about:blank") {
2785
- return fail6("URL is required. Provide a URL or connect to a browser (--cdp) with a non-blank page.");
3223
+ return fail8("URL is required. Provide a URL or connect to a browser (--cdp) with a non-blank page.");
2786
3224
  }
2787
3225
  const links = await discoverUrls(page, targetUrl, {
2788
3226
  sitemap: p.sitemap,
@@ -2793,7 +3231,7 @@ var mapCommand = registerCommand({
2793
3231
  verbose: p.verbose
2794
3232
  });
2795
3233
  const linkObjects = links.map((url) => ({ url }));
2796
- return ok16({
3234
+ return ok19({
2797
3235
  links: linkObjects,
2798
3236
  success: true
2799
3237
  });
@@ -2801,15 +3239,15 @@ var mapCommand = registerCommand({
2801
3239
  await closeEphemeralContext(context);
2802
3240
  }
2803
3241
  },
2804
- result: z16.object({
2805
- links: z16.array(z16.object({ url: z16.string() })),
2806
- success: z16.boolean()
3242
+ result: z19.object({
3243
+ links: z19.array(z19.object({ url: z19.string() })),
3244
+ success: z19.boolean()
2807
3245
  })
2808
3246
  });
2809
3247
 
2810
3248
  // src/commands/crawl.ts
2811
- import { z as z17 } from "zod";
2812
- import { ok as ok17 } from "@dyyz1993/xcli-core";
3249
+ import { z as z20 } from "zod";
3250
+ import { ok as ok20 } from "@dyyz1993/xcli-core";
2813
3251
  function stripHashAnchorQuery(url) {
2814
3252
  try {
2815
3253
  const parsed = new URL(url);
@@ -2986,21 +3424,21 @@ var crawlCommand = registerCommand({
2986
3424
  name: "crawl",
2987
3425
  description: "Crawl a website and extract content from all pages",
2988
3426
  scope: "project",
2989
- parameters: z17.object({
2990
- url: z17.string(),
2991
- limit: z17.number().default(10),
2992
- maxDepth: z17.number().default(3),
2993
- includePaths: z17.array(z17.string()).optional(),
2994
- excludePaths: z17.array(z17.string()).optional(),
2995
- allowSubdomains: z17.boolean().default(false),
2996
- allowExternalLinks: z17.boolean().default(false),
2997
- allowBackwardCrawling: z17.boolean().default(false),
2998
- enableSpa: z17.boolean().default(true).describe("Disable to skip SPA route detection"),
2999
- format: z17.enum(["markdown", "html"]).default("markdown"),
3000
- onlyMainContent: z17.boolean().default(true),
3001
- concurrency: z17.number().default(3),
3002
- retries: z17.number().default(2),
3003
- verbose: z17.boolean().default(false)
3427
+ parameters: z20.object({
3428
+ url: z20.string(),
3429
+ limit: z20.number().default(10),
3430
+ maxDepth: z20.number().default(3),
3431
+ includePaths: z20.array(z20.string()).optional(),
3432
+ excludePaths: z20.array(z20.string()).optional(),
3433
+ allowSubdomains: z20.boolean().default(false),
3434
+ allowExternalLinks: z20.boolean().default(false),
3435
+ allowBackwardCrawling: z20.boolean().default(false),
3436
+ enableSpa: z20.boolean().default(true).describe("Disable to skip SPA route detection"),
3437
+ format: z20.enum(["markdown", "html"]).default("markdown"),
3438
+ onlyMainContent: z20.boolean().default(true),
3439
+ concurrency: z20.number().default(3),
3440
+ retries: z20.number().default(2),
3441
+ verbose: z20.boolean().default(false)
3004
3442
  }),
3005
3443
  handler: async (p, ctx) => {
3006
3444
  const startUrl = new URL(p.url);
@@ -3139,7 +3577,7 @@ var crawlCommand = registerCommand({
3139
3577
  if (errorPages.length > 0) {
3140
3578
  response.errors = errorPages;
3141
3579
  }
3142
- return ok17(response);
3580
+ return ok20(response);
3143
3581
  } finally {
3144
3582
  for (const ctx2 of contexts) {
3145
3583
  await ctx2.close().catch(() => {
@@ -3149,15 +3587,15 @@ var crawlCommand = registerCommand({
3149
3587
  } catch (err) {
3150
3588
  const message = err instanceof Error ? err.message : String(err);
3151
3589
  const successPages = results.filter((r) => !isPageError(r));
3152
- return ok17({ pages: successPages, total: successPages.length, success: false, error: message });
3590
+ return ok20({ pages: successPages, total: successPages.length, success: false, error: message });
3153
3591
  }
3154
3592
  }
3155
3593
  });
3156
3594
 
3157
3595
  // src/commands/search.ts
3158
- import { z as z18 } from "zod";
3596
+ import { z as z21 } from "zod";
3159
3597
  import * as cheerio2 from "cheerio";
3160
- import { ok as ok18 } from "@dyyz1993/xcli-core";
3598
+ import { ok as ok21 } from "@dyyz1993/xcli-core";
3161
3599
  function getRecencyParams(recency) {
3162
3600
  const now = Math.floor(Date.now() / 1e3);
3163
3601
  switch (recency) {
@@ -3491,16 +3929,16 @@ var searchCommand = registerCommand({
3491
3929
  name: "search",
3492
3930
  description: "Search the web and extract results with engine fallback",
3493
3931
  scope: "project",
3494
- parameters: z18.object({
3495
- query: z18.string(),
3496
- engine: z18.string().optional(),
3497
- limit: z18.number().default(10),
3498
- full: z18.boolean().default(false),
3499
- format: z18.enum(["markdown", "json", "text"]).default("markdown"),
3500
- timeout: z18.number().default(15e3),
3501
- recency: z18.enum(["hour", "day", "week", "month", "year"]).optional().describe("Filter by time: hour/day/week/month/year"),
3502
- fallback: z18.boolean().default(false).describe("Sequential engine fallback instead of parallel"),
3503
- site: z18.string().optional().describe("Limit results to a specific site (e.g. github.com, v2ex.com)")
3932
+ parameters: z21.object({
3933
+ query: z21.string(),
3934
+ engine: z21.string().optional(),
3935
+ limit: z21.number().default(10),
3936
+ full: z21.boolean().default(false),
3937
+ format: z21.enum(["markdown", "json", "text"]).default("markdown"),
3938
+ timeout: z21.number().default(15e3),
3939
+ recency: z21.enum(["hour", "day", "week", "month", "year"]).optional().describe("Filter by time: hour/day/week/month/year"),
3940
+ fallback: z21.boolean().default(false).describe("Sequential engine fallback instead of parallel"),
3941
+ site: z21.string().optional().describe("Limit results to a specific site (e.g. github.com, v2ex.com)")
3504
3942
  }),
3505
3943
  handler: async (p, ctx) => {
3506
3944
  const { context } = await createEphemeralContext(resolveLaunchOpts(ctx));
@@ -3606,7 +4044,7 @@ ${errors.map((e) => ` - ${e.engine}: ${e.error}`).join("\n")}`
3606
4044
  lines.push(`> ${r.snippet}`);
3607
4045
  lines.push("");
3608
4046
  }
3609
- return ok18({ ...searchResult, content: lines.join("\n") });
4047
+ return ok21({ ...searchResult, content: lines.join("\n") });
3610
4048
  }
3611
4049
  if (p.format === "text") {
3612
4050
  const lines = [`Search: ${searchResult.query} (Engine: ${searchResult.engine}, Total: ${searchResult.total})`, ""];
@@ -3616,9 +4054,9 @@ ${errors.map((e) => ` - ${e.engine}: ${e.error}`).join("\n")}`
3616
4054
  lines.push(` ${r.snippet}`);
3617
4055
  lines.push("");
3618
4056
  }
3619
- return ok18({ ...searchResult, content: lines.join("\n") });
4057
+ return ok21({ ...searchResult, content: lines.join("\n") });
3620
4058
  }
3621
- return ok18(searchResult);
4059
+ return ok21(searchResult);
3622
4060
  } finally {
3623
4061
  await closeEphemeralContext(context);
3624
4062
  }
@@ -3626,8 +4064,8 @@ ${errors.map((e) => ` - ${e.engine}: ${e.error}`).join("\n")}`
3626
4064
  });
3627
4065
 
3628
4066
  // src/commands/network.ts
3629
- import { z as z19 } from "zod";
3630
- import { ok as ok19, fail as fail7 } from "@dyyz1993/xcli-core";
4067
+ import { z as z22 } from "zod";
4068
+ import { ok as ok22, fail as fail9 } from "@dyyz1993/xcli-core";
3631
4069
  function extractPath2(url) {
3632
4070
  try {
3633
4071
  const u = new URL(url);
@@ -3725,19 +4163,19 @@ var networkCommand = registerCommand({
3725
4163
  name: "network",
3726
4164
  description: "Capture and filter network responses from a URL",
3727
4165
  scope: "project",
3728
- parameters: z19.object({
3729
- url: z19.string(),
3730
- filter: z19.string().optional(),
3731
- match: z19.string().optional(),
3732
- search: z19.string().optional().describe("Search within all captured response bodies"),
3733
- console: z19.boolean().default(false),
3734
- timeout: z19.number().default(3e4),
3735
- wait: z19.number().default(3e3),
3736
- limit: z19.number().default(50),
3737
- format: z19.enum(["summary", "json"]).default("summary"),
3738
- ws: z19.boolean().optional().default(false).describe("Only show WebSocket messages"),
3739
- listen: z19.boolean().optional().default(false).describe("\u76D1\u542C\u6A21\u5F0F\uFF1A\u4F7F\u7528\u73B0\u6709 session \u7684 page\uFF0C\u7B49\u5F85\u6307\u5B9A\u65F6\u95F4\u6355\u83B7\u8BF7\u6C42"),
3740
- duration: z19.number().int().optional().default(15e3).describe("\u76D1\u542C\u6A21\u5F0F\u7B49\u5F85\u65F6\u957F\uFF08\u6BEB\u79D2\uFF0C\u9ED8\u8BA4 15000\uFF09")
4166
+ parameters: z22.object({
4167
+ url: z22.string(),
4168
+ filter: z22.string().optional(),
4169
+ match: z22.string().optional(),
4170
+ search: z22.string().optional().describe("Search within all captured response bodies"),
4171
+ console: z22.boolean().default(false),
4172
+ timeout: z22.number().default(3e4),
4173
+ wait: z22.number().default(3e3),
4174
+ limit: z22.number().default(50),
4175
+ format: z22.enum(["summary", "json"]).default("summary"),
4176
+ ws: z22.boolean().optional().default(false).describe("Only show WebSocket messages"),
4177
+ listen: z22.boolean().optional().default(false).describe("\u76D1\u542C\u6A21\u5F0F\uFF1A\u4F7F\u7528\u73B0\u6709 session \u7684 page\uFF0C\u7B49\u5F85\u6307\u5B9A\u65F6\u95F4\u6355\u83B7\u8BF7\u6C42"),
4178
+ duration: z22.number().int().optional().default(15e3).describe("\u76D1\u542C\u6A21\u5F0F\u7B49\u5F85\u65F6\u957F\uFF08\u6BEB\u79D2\uFF0C\u9ED8\u8BA4 15000\uFF09")
3741
4179
  }),
3742
4180
  handler: async (p, ctx) => {
3743
4181
  const startTime = Date.now();
@@ -3792,7 +4230,7 @@ var networkCommand = registerCommand({
3792
4230
  };
3793
4231
  if (p.listen) {
3794
4232
  const page2 = ctx.page;
3795
- if (!page2) return fail7("No active page. Use --cdp to connect first.");
4233
+ if (!page2) return fail9("No active page. Use --cdp to connect first.");
3796
4234
  const captures = [];
3797
4235
  const consoleMessages = [];
3798
4236
  const wsCaptures = [];
@@ -3819,7 +4257,7 @@ var networkCommand = registerCommand({
3819
4257
  page2.off("response", handler);
3820
4258
  const duration = Date.now() - startTime;
3821
4259
  if (p.ws && wsCaptures.length > 0) {
3822
- return ok19({
4260
+ return ok22({
3823
4261
  url: "[listen mode]",
3824
4262
  duration,
3825
4263
  total: captures.length,
@@ -3833,9 +4271,9 @@ var networkCommand = registerCommand({
3833
4271
  });
3834
4272
  }
3835
4273
  if (p.format === "json") {
3836
- return ok19(buildJsonOutput("[listen mode]", captures, consoleMessages, captures.length, wsCaptures));
4274
+ return ok22(buildJsonOutput("[listen mode]", captures, consoleMessages, captures.length, wsCaptures));
3837
4275
  }
3838
- return ok19(buildSummaryOutput("[listen mode]", duration, captures, consoleMessages, captures.length, wsCaptures));
4276
+ return ok22(buildSummaryOutput("[listen mode]", duration, captures, consoleMessages, captures.length, wsCaptures));
3839
4277
  }
3840
4278
  const { context, page } = await createEphemeralContext(resolveLaunchOpts(ctx));
3841
4279
  try {
@@ -3890,7 +4328,7 @@ var networkCommand = registerCommand({
3890
4328
  }
3891
4329
  const duration = Date.now() - startTime;
3892
4330
  if (p.ws && wsCaptures.length > 0) {
3893
- return ok19({
4331
+ return ok22({
3894
4332
  url: p.url,
3895
4333
  duration,
3896
4334
  total: captures.length,
@@ -3904,9 +4342,9 @@ var networkCommand = registerCommand({
3904
4342
  });
3905
4343
  }
3906
4344
  if (p.format === "json") {
3907
- return ok19(buildJsonOutput(p.url, results, consoleMessages, totalCount, wsCaptures, searchResults));
4345
+ return ok22(buildJsonOutput(p.url, results, consoleMessages, totalCount, wsCaptures, searchResults));
3908
4346
  }
3909
- return ok19(buildSummaryOutput(p.url, duration, results, consoleMessages, totalCount, wsCaptures));
4347
+ return ok22(buildSummaryOutput(p.url, duration, results, consoleMessages, totalCount, wsCaptures));
3910
4348
  } finally {
3911
4349
  await closeEphemeralContext(context);
3912
4350
  }
@@ -3914,7 +4352,7 @@ var networkCommand = registerCommand({
3914
4352
  });
3915
4353
 
3916
4354
  // src/commands/ai-search-engines.ts
3917
- import { z as z20 } from "zod";
4355
+ import { z as z23 } from "zod";
3918
4356
  var ENGINE_CONFIGS = {
3919
4357
  deepseek: {
3920
4358
  key: "deepseek",
@@ -4168,11 +4606,11 @@ var ENGINE_CONFIGS = {
4168
4606
  }
4169
4607
  };
4170
4608
  var ALL_ENGINE_KEYS = Object.keys(ENGINE_CONFIGS);
4171
- var ENGINE_KEY_ENUM = z20.enum(ALL_ENGINE_KEYS);
4609
+ var ENGINE_KEY_ENUM = z23.enum(ALL_ENGINE_KEYS);
4172
4610
 
4173
4611
  // src/commands/snapshot.ts
4174
- import { z as z21 } from "zod";
4175
- import { ok as ok20, fail as fail8, normalizeTips as normalizeTips3 } from "@dyyz1993/xcli-core";
4612
+ import { z as z24 } from "zod";
4613
+ import { ok as ok23, fail as fail10, normalizeTips as normalizeTips3 } from "@dyyz1993/xcli-core";
4176
4614
 
4177
4615
  // src/runtime/ref-store.ts
4178
4616
  var sessions = /* @__PURE__ */ new Map();
@@ -4413,8 +4851,8 @@ async function resolveRefParams(page, params, selectorKeys, cache, sessionId) {
4413
4851
  }
4414
4852
 
4415
4853
  // src/utils/site-semantics.ts
4416
- import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync, readFileSync } from "fs";
4417
- import { join as join4, dirname as dirname2 } from "path";
4854
+ import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
4855
+ import { join as join6, dirname as dirname2 } from "path";
4418
4856
  import { homedir as homedir4 } from "os";
4419
4857
  import { stringify, parse } from "yaml";
4420
4858
  import { execFile } from "child_process";
@@ -4492,10 +4930,10 @@ function inferAction(role, label) {
4492
4930
  return {};
4493
4931
  }
4494
4932
  function getSemanticsDir() {
4495
- return join4(homedir4(), ".xbrowser", "site-semantics");
4933
+ return join6(homedir4(), ".xbrowser", "site-semantics");
4496
4934
  }
4497
4935
  function getSemanticsPath(domain) {
4498
- return join4(getSemanticsDir(), `${domain}.yaml`);
4936
+ return join6(getSemanticsDir(), `${domain}.yaml`);
4499
4937
  }
4500
4938
  function extractDomain2(url) {
4501
4939
  try {
@@ -4509,9 +4947,9 @@ function saveSemantics(domain, pagePath, url, elements) {
4509
4947
  const filePath = getSemanticsPath(domain);
4510
4948
  const dir = dirname2(filePath);
4511
4949
  let site;
4512
- if (existsSync(filePath)) {
4950
+ if (existsSync3(filePath)) {
4513
4951
  try {
4514
- site = parse(readFileSync(filePath, "utf-8"));
4952
+ site = parse(readFileSync3(filePath, "utf-8"));
4515
4953
  } catch {
4516
4954
  site = { site: domain, pages: {} };
4517
4955
  }
@@ -4531,16 +4969,16 @@ function saveSemantics(domain, pagePath, url, elements) {
4531
4969
  }
4532
4970
  }
4533
4971
  site.updated_at = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
4534
- if (!existsSync(dir)) {
4972
+ if (!existsSync3(dir)) {
4535
4973
  mkdirSync3(dir, { recursive: true });
4536
4974
  }
4537
4975
  writeFileSync3(filePath, stringify(site, { lineWidth: 0 }));
4538
4976
  }
4539
4977
  function loadSemantics(domain) {
4540
4978
  const filePath = getSemanticsPath(domain);
4541
- if (!existsSync(filePath)) return null;
4979
+ if (!existsSync3(filePath)) return null;
4542
4980
  try {
4543
- return parse(readFileSync(filePath, "utf-8"));
4981
+ return parse(readFileSync3(filePath, "utf-8"));
4544
4982
  } catch {
4545
4983
  return null;
4546
4984
  }
@@ -5084,24 +5522,24 @@ var snapshotCommand = registerCommand({
5084
5522
  description: "Capture a quick page state snapshot \u2014 aria tree, visible text, or DOM summary",
5085
5523
  scope: "page",
5086
5524
  selectorParams: ["selector"],
5087
- parameters: z21.object({
5088
- type: z21.enum(["aria", "text", "dom", "all"]).default("aria").describe("Snapshot type: aria (accessibility tree), text (visible text), dom (element summary), all (combined)"),
5089
- selector: z21.string().optional().describe("Scope to a specific element"),
5090
- depth: z21.number().optional().default(6).describe("Max depth for DOM/aria tree"),
5091
- interactive: z21.boolean().optional().default(false).describe("Return interactive agent refs only"),
5092
- interactiveOnly: z21.boolean().optional().default(false).describe("Alias for interactive"),
5093
- i: z21.boolean().optional().default(false).describe("Short alias for interactive"),
5094
- compact: z21.boolean().optional().default(false).describe("Include compact xbrowser style snapshot text"),
5095
- c: z21.boolean().optional().default(false).describe("Short alias for compact"),
5096
- selectors: z21.boolean().optional().default(false).describe("Include ref to CSS selector map"),
5097
- all: z21.boolean().optional().default(false).describe("Include hidden interactive targets when using interactive snapshot")
5525
+ parameters: z24.object({
5526
+ type: z24.enum(["aria", "text", "dom", "all"]).default("aria").describe("Snapshot type: aria (accessibility tree), text (visible text), dom (element summary), all (combined)"),
5527
+ selector: z24.string().optional().describe("Scope to a specific element"),
5528
+ depth: z24.number().optional().default(6).describe("Max depth for DOM/aria tree"),
5529
+ interactive: z24.boolean().optional().default(false).describe("Return interactive agent refs only"),
5530
+ interactiveOnly: z24.boolean().optional().default(false).describe("Alias for interactive"),
5531
+ i: z24.boolean().optional().default(false).describe("Short alias for interactive"),
5532
+ compact: z24.boolean().optional().default(false).describe("Include compact xbrowser style snapshot text"),
5533
+ c: z24.boolean().optional().default(false).describe("Short alias for compact"),
5534
+ selectors: z24.boolean().optional().default(false).describe("Include ref to CSS selector map"),
5535
+ all: z24.boolean().optional().default(false).describe("Include hidden interactive targets when using interactive snapshot")
5098
5536
  }),
5099
- result: z21.object({
5100
- url: z21.string(),
5101
- title: z21.string(),
5102
- aria: z21.string().optional(),
5103
- text: z21.string().optional(),
5104
- dom: z21.record(z21.unknown()).optional()
5537
+ result: z24.object({
5538
+ url: z24.string(),
5539
+ title: z24.string(),
5540
+ aria: z24.string().optional(),
5541
+ text: z24.string().optional(),
5542
+ dom: z24.record(z24.unknown()).optional()
5105
5543
  }),
5106
5544
  handler: async (p, ctx) => {
5107
5545
  const page = ctx.page;
@@ -5115,7 +5553,7 @@ var snapshotCommand = registerCommand({
5115
5553
  if (p.compact || p.c || p.interactive || p.interactiveOnly || p.i) {
5116
5554
  observation.compact = formatObservationCompact(observation, { selectors: p.selectors });
5117
5555
  }
5118
- return ok20(observation, normalizeTips3([
5556
+ return ok23(observation, normalizeTips3([
5119
5557
  `refs refreshed for ${observation.targets.length} targets; use click @e1 or fill @e2 "text"`
5120
5558
  ]));
5121
5559
  }
@@ -5123,15 +5561,15 @@ var snapshotCommand = registerCommand({
5123
5561
  const aria = await captureAriaSnapshot(page, p.selector, p.depth);
5124
5562
  const tips = await buildRefTips(page, aria);
5125
5563
  persistSemantics(url, aria);
5126
- return ok20({ url, title, aria }, normalizeTips3(tips));
5564
+ return ok23({ url, title, aria }, normalizeTips3(tips));
5127
5565
  }
5128
5566
  if (p.type === "text") {
5129
5567
  const text = await captureTextSnapshot(page, p.selector);
5130
- return ok20({ url, title, text });
5568
+ return ok23({ url, title, text });
5131
5569
  }
5132
5570
  if (p.type === "dom") {
5133
5571
  const dom = await captureDomSnapshot(page, p.selector, p.depth ?? 6);
5134
- return ok20({ url, title, dom });
5572
+ return ok23({ url, title, dom });
5135
5573
  }
5136
5574
  if (p.type === "all") {
5137
5575
  const [aria, text, dom] = await Promise.all([
@@ -5141,9 +5579,9 @@ var snapshotCommand = registerCommand({
5141
5579
  ]);
5142
5580
  const tips = await buildRefTips(page, aria);
5143
5581
  persistSemantics(url, aria);
5144
- return ok20({ url, title, aria, text, dom }, normalizeTips3(tips));
5582
+ return ok23({ url, title, aria, text, dom }, normalizeTips3(tips));
5145
5583
  }
5146
- return fail8(`Unknown snapshot type: ${p.type}`);
5584
+ return fail10(`Unknown snapshot type: ${p.type}`);
5147
5585
  }
5148
5586
  });
5149
5587
  function persistSemantics(url, aria) {
@@ -5220,22 +5658,22 @@ async function captureDomSnapshot(page, selector, maxDepth) {
5220
5658
  }
5221
5659
 
5222
5660
  // src/commands/agent.ts
5223
- import { z as z22 } from "zod";
5224
- import { ok as ok21, normalizeTips as normalizeTips4 } from "@dyyz1993/xcli-core";
5661
+ import { z as z25 } from "zod";
5662
+ import { ok as ok24, normalizeTips as normalizeTips4 } from "@dyyz1993/xcli-core";
5225
5663
  var observeCommand = registerCommand({
5226
5664
  name: "observe",
5227
5665
  description: "Observe the current page as structured agent targets with session refs",
5228
5666
  scope: "page",
5229
- parameters: z22.object({
5230
- includeHidden: z22.boolean().optional().default(false).describe("Include hidden elements in the target list"),
5231
- limit: z22.number().int().positive().max(300).optional().default(80).describe("Maximum number of targets to return"),
5232
- compact: z22.boolean().optional().default(false).describe("Include compact xbrowser style snapshot text"),
5233
- selectors: z22.boolean().optional().default(false).describe("Include ref to stable CSS selector map")
5667
+ parameters: z25.object({
5668
+ includeHidden: z25.boolean().optional().default(false).describe("Include hidden elements in the target list"),
5669
+ limit: z25.number().int().positive().max(300).optional().default(80).describe("Maximum number of targets to return"),
5670
+ compact: z25.boolean().optional().default(false).describe("Include compact xbrowser style snapshot text"),
5671
+ selectors: z25.boolean().optional().default(false).describe("Include ref to stable CSS selector map")
5234
5672
  }),
5235
- result: z22.object({
5236
- targets: z22.array(z22.record(z22.unknown())),
5237
- selectors: z22.record(z22.unknown()).optional(),
5238
- compact: z22.string().optional()
5673
+ result: z25.object({
5674
+ targets: z25.array(z25.record(z25.unknown())),
5675
+ selectors: z25.record(z25.unknown()).optional(),
5676
+ compact: z25.string().optional()
5239
5677
  }).passthrough(),
5240
5678
  handler: async (p, ctx) => {
5241
5679
  const observation = await observePage(ctx.page, ctx.sessionId, {
@@ -5244,7 +5682,7 @@ var observeCommand = registerCommand({
5244
5682
  });
5245
5683
  if (p.selectors) observation.selectors = buildSelectorMap(observation);
5246
5684
  if (p.compact) observation.compact = formatObservationCompact(observation, { selectors: p.selectors });
5247
- return ok21(observation, normalizeTips4([
5685
+ return ok24(observation, normalizeTips4([
5248
5686
  `refs refreshed for ${observation.targets.length} targets; use act --ref @e1 --action click or click @e1`
5249
5687
  ]));
5250
5688
  }
@@ -5254,14 +5692,14 @@ var actCommand = registerCommand({
5254
5692
  description: "Perform an agent action using an observe ref or explicit selector",
5255
5693
  scope: "element",
5256
5694
  selectorParams: ["selector"],
5257
- parameters: z22.object({
5258
- action: z22.enum(["click", "fill", "type", "press", "select", "check", "hover"]).default("click"),
5259
- ref: z22.string().optional().describe("Session-scoped ref returned by observe, such as e1"),
5260
- selector: z22.string().optional().describe("CSS selector fallback when no ref is available"),
5261
- value: z22.string().optional().describe("Value for fill/type/select"),
5262
- key: z22.string().optional().describe("Key for press"),
5263
- force: z22.boolean().optional().default(false).describe("Bypass actionability checks"),
5264
- timeout: z22.number().optional().default(1e4).describe("Playwright action timeout in milliseconds")
5695
+ parameters: z25.object({
5696
+ action: z25.enum(["click", "fill", "type", "press", "select", "check", "hover"]).default("click"),
5697
+ ref: z25.string().optional().describe("Session-scoped ref returned by observe, such as e1"),
5698
+ selector: z25.string().optional().describe("CSS selector fallback when no ref is available"),
5699
+ value: z25.string().optional().describe("Value for fill/type/select"),
5700
+ key: z25.string().optional().describe("Key for press"),
5701
+ force: z25.boolean().optional().default(false).describe("Bypass actionability checks"),
5702
+ timeout: z25.number().optional().default(1e4).describe("Playwright action timeout in milliseconds")
5265
5703
  }).refine((p) => !!p.ref || !!p.selector, {
5266
5704
  message: "Either ref or selector is required"
5267
5705
  }),
@@ -5275,7 +5713,7 @@ var actCommand = registerCommand({
5275
5713
  tips: normalizeTips4(result.stale ? ["run observe again to refresh refs"] : [])
5276
5714
  };
5277
5715
  }
5278
- return ok21(result, normalizeTips4(result.stale ? ["ref screen hash changed; run observe if the next action is uncertain"] : []));
5716
+ return ok24(result, normalizeTips4(result.stale ? ["ref screen hash changed; run observe if the next action is uncertain"] : []));
5279
5717
  }
5280
5718
  });
5281
5719
  var waitForCommand = registerCommand({
@@ -5283,16 +5721,16 @@ var waitForCommand = registerCommand({
5283
5721
  description: "Wait for agent predicates such as text, URL, load state, selector state, or screen hash changes",
5284
5722
  scope: "page",
5285
5723
  selectorParams: ["selector"],
5286
- parameters: z22.object({
5287
- selector: z22.string().optional().describe("CSS selector or observe ref to wait for"),
5288
- state: z22.enum(["attached", "detached", "visible", "hidden"]).optional().default("visible"),
5289
- text: z22.string().optional().describe("Visible text to wait for"),
5290
- url: z22.string().optional().describe("URL substring or glob pattern to wait for"),
5291
- load: z22.enum(["load", "domcontentloaded", "networkidle"]).optional().describe("Load state to wait for"),
5292
- fn: z22.string().optional().describe("JavaScript predicate to wait for"),
5293
- screenHashChanged: z22.string().optional().describe("Previous screenHash from observe"),
5294
- timeout: z22.number().optional().default(3e4),
5295
- pollInterval: z22.number().optional().default(200)
5724
+ parameters: z25.object({
5725
+ selector: z25.string().optional().describe("CSS selector or observe ref to wait for"),
5726
+ state: z25.enum(["attached", "detached", "visible", "hidden"]).optional().default("visible"),
5727
+ text: z25.string().optional().describe("Visible text to wait for"),
5728
+ url: z25.string().optional().describe("URL substring or glob pattern to wait for"),
5729
+ load: z25.enum(["load", "domcontentloaded", "networkidle"]).optional().describe("Load state to wait for"),
5730
+ fn: z25.string().optional().describe("JavaScript predicate to wait for"),
5731
+ screenHashChanged: z25.string().optional().describe("Previous screenHash from observe"),
5732
+ timeout: z25.number().optional().default(3e4),
5733
+ pollInterval: z25.number().optional().default(200)
5296
5734
  }).refine((p) => [p.selector, p.text, p.url, p.load, p.fn, p.screenHashChanged].filter(Boolean).length === 1, {
5297
5735
  message: "Provide exactly one wait predicate: selector, text, url, load, fn, or screenHashChanged"
5298
5736
  }),
@@ -5306,30 +5744,30 @@ var waitForCommand = registerCommand({
5306
5744
  tips: []
5307
5745
  };
5308
5746
  }
5309
- return ok21(result);
5747
+ return ok24(result);
5310
5748
  }
5311
5749
  });
5312
5750
 
5313
5751
  // src/commands/tab.ts
5314
- import { z as z23 } from "zod";
5315
- import { ok as ok22, fail as fail9 } from "@dyyz1993/xcli-core";
5316
- var TabParams = z23.object({
5317
- subcommand: z23.enum(["list", "new", "close", "switch"]),
5318
- url: z23.string().optional(),
5319
- index: z23.number().int().min(0).optional()
5752
+ import { z as z26 } from "zod";
5753
+ import { ok as ok25, fail as fail11 } from "@dyyz1993/xcli-core";
5754
+ var TabParams = z26.object({
5755
+ subcommand: z26.enum(["list", "new", "close", "switch"]),
5756
+ url: z26.string().optional(),
5757
+ index: z26.number().int().min(0).optional()
5320
5758
  });
5321
5759
  var tabCommand = registerCommand({
5322
5760
  name: "tab",
5323
5761
  description: "Manage browser tabs: list, new, close, switch",
5324
5762
  scope: "page",
5325
5763
  parameters: TabParams,
5326
- result: z23.object({
5327
- success: z23.boolean(),
5328
- data: z23.unknown()
5764
+ result: z26.object({
5765
+ success: z26.boolean(),
5766
+ data: z26.unknown()
5329
5767
  }),
5330
5768
  handler: async (p, ctx) => {
5331
5769
  if (!ctx.browserContext) {
5332
- return fail9("No browser context available. Use --cdp to connect to a browser first.");
5770
+ return fail11("No browser context available. Use --cdp to connect to a browser first.");
5333
5771
  }
5334
5772
  const pages = ctx.browserContext.pages();
5335
5773
  switch (p.subcommand) {
@@ -5342,7 +5780,7 @@ var tabCommand = registerCommand({
5342
5780
  case "switch":
5343
5781
  return handleSwitch(p, pages, ctx);
5344
5782
  default:
5345
- return fail9(`Unknown subcommand: ${p.subcommand}`);
5783
+ return fail11(`Unknown subcommand: ${p.subcommand}`);
5346
5784
  }
5347
5785
  }
5348
5786
  });
@@ -5357,7 +5795,7 @@ async function handleList(pages, ctx) {
5357
5795
  if (isActive) activeIndex = i;
5358
5796
  tabs.push({ index: i, url, title, active: isActive });
5359
5797
  }
5360
- return ok22({ tabs, total: tabs.length, activeIndex });
5798
+ return ok25({ tabs, total: tabs.length, activeIndex });
5361
5799
  }
5362
5800
  async function handleNew(p, _pages, ctx) {
5363
5801
  const newPage = await ctx.browserContext.newPage();
@@ -5385,7 +5823,7 @@ async function handleNew(p, _pages, ctx) {
5385
5823
  const title = await newPage.title().catch(() => "");
5386
5824
  const allPages = ctx.browserContext.pages();
5387
5825
  const newIndex = allPages.indexOf(newPage);
5388
- return ok22({
5826
+ return ok25({
5389
5827
  index: newIndex >= 0 ? newIndex : allPages.length - 1,
5390
5828
  url: newPage.url(),
5391
5829
  title,
@@ -5396,11 +5834,11 @@ async function handleNew(p, _pages, ctx) {
5396
5834
  async function handleClose(p, ctx) {
5397
5835
  const currentPages = ctx.browserContext.pages();
5398
5836
  if (currentPages.length <= 1) {
5399
- return fail9("Cannot close the last remaining tab");
5837
+ return fail11("Cannot close the last remaining tab");
5400
5838
  }
5401
5839
  const closeIndex = p.index ?? currentPages.findIndex((pg) => pg === ctx.page);
5402
5840
  if (closeIndex < 0 || closeIndex >= currentPages.length) {
5403
- return fail9(`Invalid tab index: ${closeIndex}. Valid range: 0-${currentPages.length - 1}`);
5841
+ return fail11(`Invalid tab index: ${closeIndex}. Valid range: 0-${currentPages.length - 1}`);
5404
5842
  }
5405
5843
  const pageToClose = currentPages[closeIndex];
5406
5844
  const isActivePage = pageToClose === ctx.page;
@@ -5415,7 +5853,7 @@ async function handleClose(p, ctx) {
5415
5853
  }
5416
5854
  ctx.page = newActivePage;
5417
5855
  }
5418
- return ok22({
5856
+ return ok25({
5419
5857
  closedIndex: closeIndex,
5420
5858
  total: remainingPages.length,
5421
5859
  activeIndex: isActivePage ? closeIndex < remainingPages.length ? closeIndex : remainingPages.length - 1 : remainingPages.findIndex((pg) => pg === ctx.page)
@@ -5423,10 +5861,10 @@ async function handleClose(p, ctx) {
5423
5861
  }
5424
5862
  async function handleSwitch(p, pages, ctx) {
5425
5863
  if (p.index === void 0) {
5426
- return fail9("Parameter --index is required for switch subcommand");
5864
+ return fail11("Parameter --index is required for switch subcommand");
5427
5865
  }
5428
5866
  if (p.index < 0 || p.index >= pages.length) {
5429
- return fail9(`Invalid tab index: ${p.index}. Valid range: 0-${pages.length - 1}`);
5867
+ return fail11(`Invalid tab index: ${p.index}. Valid range: 0-${pages.length - 1}`);
5430
5868
  }
5431
5869
  const targetPage = pages[p.index];
5432
5870
  await targetPage.bringToFront().catch(() => {
@@ -5436,13 +5874,19 @@ async function handleSwitch(p, pages, ctx) {
5436
5874
  if (okProbe) break;
5437
5875
  await new Promise((r) => setTimeout(r, 300));
5438
5876
  }
5877
+ try {
5878
+ const mouse = targetPage.mouse;
5879
+ if (mouse) await mouse.move(1, 1).catch(() => {
5880
+ });
5881
+ } catch {
5882
+ }
5439
5883
  const session = ctx.sessionId ? getSessionById(ctx.sessionId) : void 0;
5440
5884
  if (session) {
5441
5885
  setActivePage(session, targetPage);
5442
5886
  }
5443
5887
  ctx.page = targetPage;
5444
5888
  const title = await targetPage.title().catch(() => "");
5445
- return ok22({
5889
+ return ok25({
5446
5890
  index: p.index,
5447
5891
  url: targetPage.url(),
5448
5892
  title,
@@ -5451,9 +5895,9 @@ async function handleSwitch(p, pages, ctx) {
5451
5895
  }
5452
5896
 
5453
5897
  // src/commands/addinitscript.ts
5454
- import { z as z24 } from "zod";
5455
- import { ok as ok23 } from "@dyyz1993/xcli-core";
5456
- import { readFileSync as readFileSync2 } from "fs";
5898
+ import { z as z27 } from "zod";
5899
+ import { ok as ok26 } from "@dyyz1993/xcli-core";
5900
+ import { readFileSync as readFileSync4 } from "fs";
5457
5901
 
5458
5902
  // src/chain-parser.ts
5459
5903
  import { splitCommand, parseCommandArgs } from "@dyyz1993/xcli-core";
@@ -5593,14 +6037,14 @@ registerCommandDefinition("tab", ["subcommand"]);
5593
6037
  registerCommandDefinition("mouse", ["action", "x", "y"]);
5594
6038
 
5595
6039
  // src/commands/addinitscript.ts
5596
- var InitScriptParams = z24.object({
5597
- script: z24.string().optional(),
5598
- file: z24.string().optional(),
5599
- stdin: z24.boolean().optional(),
5600
- name: z24.string().optional(),
5601
- list: z24.boolean().optional(),
5602
- remove: z24.union([z24.string(), z24.boolean()]).optional(),
5603
- base64: z24.string().optional()
6040
+ var InitScriptParams = z27.object({
6041
+ script: z27.string().optional(),
6042
+ file: z27.string().optional(),
6043
+ stdin: z27.boolean().optional(),
6044
+ name: z27.string().optional(),
6045
+ list: z27.boolean().optional(),
6046
+ remove: z27.union([z27.string(), z27.boolean()]).optional(),
6047
+ base64: z27.string().optional()
5604
6048
  });
5605
6049
  var registeredScripts = /* @__PURE__ */ new Map();
5606
6050
  function resolveScriptContent(params) {
@@ -5608,7 +6052,7 @@ function resolveScriptContent(params) {
5608
6052
  return Buffer.from(params.base64, "base64").toString("utf-8");
5609
6053
  }
5610
6054
  if (params.file) {
5611
- return readFileSync2(params.file, "utf-8");
6055
+ return readFileSync4(params.file, "utf-8");
5612
6056
  }
5613
6057
  if (params.stdin) {
5614
6058
  return null;
@@ -5634,14 +6078,14 @@ var addInitScriptCommand = registerCommand({
5634
6078
  description: "Add an initialization script that runs on every page load",
5635
6079
  scope: "page",
5636
6080
  parameters: InitScriptParams,
5637
- result: z24.object({
5638
- scripts: z24.array(z24.object({ name: z24.string(), size: z24.number(), preview: z24.string() })).optional(),
5639
- removed: z24.string().optional(),
5640
- existed: z24.boolean().optional(),
5641
- error: z24.string().optional(),
5642
- registered: z24.string().optional(),
5643
- hint: z24.string().optional(),
5644
- executedImmediately: z24.boolean().optional()
6081
+ result: z27.object({
6082
+ scripts: z27.array(z27.object({ name: z27.string(), size: z27.number(), preview: z27.string() })).optional(),
6083
+ removed: z27.string().optional(),
6084
+ existed: z27.boolean().optional(),
6085
+ error: z27.string().optional(),
6086
+ registered: z27.string().optional(),
6087
+ hint: z27.string().optional(),
6088
+ executedImmediately: z27.boolean().optional()
5645
6089
  }).passthrough(),
5646
6090
  handler: async (params, ctx) => {
5647
6091
  if (params.list) {
@@ -5650,20 +6094,20 @@ var addInitScriptCommand = registerCommand({
5650
6094
  size: content2.length,
5651
6095
  preview: content2.slice(0, 80)
5652
6096
  }));
5653
- return ok23({ scripts });
6097
+ return ok26({ scripts });
5654
6098
  }
5655
6099
  if (params.remove && typeof params.remove === "string") {
5656
6100
  const existed = registeredScripts.delete(params.remove);
5657
- return ok23({ removed: params.remove, existed });
6101
+ return ok26({ removed: params.remove, existed });
5658
6102
  }
5659
6103
  const removeTarget = (typeof params.remove === "string" ? params.remove : null) || (params.name && !resolveScriptContent(params) ? params.name : null);
5660
6104
  if (removeTarget && !resolveScriptContent(params)) {
5661
6105
  const existed = registeredScripts.delete(removeTarget);
5662
- return ok23({ removed: removeTarget, existed });
6106
+ return ok26({ removed: removeTarget, existed });
5663
6107
  }
5664
6108
  let content = params.stdin ? await readStdin() : resolveScriptContent(params);
5665
6109
  if (!content) {
5666
- return ok23({ error: "No script content provided. Use --script, --file, --stdin, or --base64" });
6110
+ return ok26({ error: "No script content provided. Use --script, --file, --stdin, or --base64" });
5667
6111
  }
5668
6112
  const scriptName = params.name ?? `script-${Date.now()}`;
5669
6113
  registeredScripts.set(scriptName, content);
@@ -5671,45 +6115,45 @@ var addInitScriptCommand = registerCommand({
5671
6115
  try {
5672
6116
  await ctx.page.evaluate(content);
5673
6117
  } catch {
5674
- return ok23({
6118
+ return ok26({
5675
6119
  registered: scriptName,
5676
6120
  hint: "Script registered for future page loads; immediate execution skipped (page may not be ready)"
5677
6121
  });
5678
6122
  }
5679
- return ok23({ registered: scriptName, executedImmediately: true });
6123
+ return ok26({ registered: scriptName, executedImmediately: true });
5680
6124
  }
5681
6125
  });
5682
6126
  registerCommandDefinition("addinitscript", ["script"]);
5683
6127
 
5684
6128
  // src/commands/find.ts
5685
- import { z as z25 } from "zod";
5686
- import { ok as ok24, fail as fail10, normalizeTips as normalizeTips5 } from "@dyyz1993/xcli-core";
5687
- var actionSchema2 = z25.enum(["click", "fill", "type", "select", "hover", "check"]);
6129
+ import { z as z28 } from "zod";
6130
+ import { ok as ok27, fail as fail12, normalizeTips as normalizeTips5 } from "@dyyz1993/xcli-core";
6131
+ var actionSchema2 = z28.enum(["click", "fill", "type", "select", "hover", "check"]);
5688
6132
  var findCommand = registerCommand({
5689
6133
  name: "find",
5690
6134
  description: "Find elements by semantic strategy (text/role/label/placeholder/testid) and optionally perform an action",
5691
6135
  scope: "page",
5692
- parameters: z25.object({
5693
- strategy: z25.enum(["text", "role", "label", "placeholder", "testid", "alt", "title", "first", "last", "nth"]),
5694
- value: z25.string(),
5695
- name: z25.string().optional(),
5696
- exact: z25.boolean().optional().default(false),
5697
- operation: z25.string().optional().describe('Trailing operation syntax, e.g. click, fill "text", type "text"'),
6136
+ parameters: z28.object({
6137
+ strategy: z28.enum(["text", "role", "label", "placeholder", "testid", "alt", "title", "first", "last", "nth"]),
6138
+ value: z28.string(),
6139
+ name: z28.string().optional(),
6140
+ exact: z28.boolean().optional().default(false),
6141
+ operation: z28.string().optional().describe('Trailing operation syntax, e.g. click, fill "text", type "text"'),
5698
6142
  action: actionSchema2.optional().describe("Action to perform when not using trailing operation syntax"),
5699
- actionValue: z25.string().optional().describe("Value for fill/type/select when using action"),
5700
- index: z25.number().int().optional().describe("Index for nth strategy"),
5701
- click: z25.boolean().optional().default(false),
5702
- fill: z25.string().optional(),
5703
- type: z25.string().optional(),
5704
- select: z25.string().optional(),
5705
- hover: z25.boolean().optional().default(false),
5706
- check: z25.boolean().optional().default(false),
5707
- timeout: z25.number().optional().default(1e4)
6143
+ actionValue: z28.string().optional().describe("Value for fill/type/select when using action"),
6144
+ index: z28.number().int().optional().describe("Index for nth strategy"),
6145
+ click: z28.boolean().optional().default(false),
6146
+ fill: z28.string().optional(),
6147
+ type: z28.string().optional(),
6148
+ select: z28.string().optional(),
6149
+ hover: z28.boolean().optional().default(false),
6150
+ check: z28.boolean().optional().default(false),
6151
+ timeout: z28.number().optional().default(1e4)
5708
6152
  }),
5709
- result: z25.object({
5710
- matched: z25.number(),
5711
- selector: z25.string(),
5712
- action: z25.string().optional()
6153
+ result: z28.object({
6154
+ matched: z28.number(),
6155
+ selector: z28.string(),
6156
+ action: z28.string().optional()
5713
6157
  }),
5714
6158
  handler: async (p, ctx) => {
5715
6159
  const page = ctx.page;
@@ -5724,7 +6168,7 @@ var findCommand = registerCommand({
5724
6168
  });
5725
6169
  const count = await locator.count();
5726
6170
  if (count === 0) {
5727
- return fail10(`No element found with ${p.strategy}="${p.value}"`);
6171
+ return fail12(`No element found with ${p.strategy}="${p.value}"`);
5728
6172
  }
5729
6173
  const tips = [];
5730
6174
  const target = selectTarget(locator, p.strategy);
@@ -5736,15 +6180,15 @@ var findCommand = registerCommand({
5736
6180
  await target.click({ timeout: p.timeout, force: true });
5737
6181
  return okWithTips({ matched: count, selector, action: "click" }, tips);
5738
6182
  } else if (actionName === "fill") {
5739
- if (actionValue === void 0) return fail10("find fill requires a value");
6183
+ if (actionValue === void 0) return fail12("find fill requires a value");
5740
6184
  await target.fill(actionValue, { timeout: p.timeout, force: true });
5741
6185
  return okWithTips({ matched: count, selector, action: `fill("${actionValue}")` }, tips);
5742
6186
  } else if (actionName === "type") {
5743
- if (actionValue === void 0) return fail10("find type requires a value");
6187
+ if (actionValue === void 0) return fail12("find type requires a value");
5744
6188
  await target.type(actionValue, { delay: 10, timeout: p.timeout });
5745
6189
  return okWithTips({ matched: count, selector, action: `type("${actionValue}")` }, tips);
5746
6190
  } else if (actionName === "select") {
5747
- if (actionValue === void 0) return fail10("find select requires a value");
6191
+ if (actionValue === void 0) return fail12("find select requires a value");
5748
6192
  await target.selectOption(actionValue);
5749
6193
  return okWithTips({ matched: count, selector, action: `select("${actionValue}")` }, tips);
5750
6194
  } else if (actionName === "hover") {
@@ -5758,7 +6202,7 @@ var findCommand = registerCommand({
5758
6202
  }
5759
6203
  });
5760
6204
  function okWithTips(data, tips) {
5761
- const result = ok24(data);
6205
+ const result = ok27(data);
5762
6206
  if (tips.length > 0) result.tips = normalizeTips5(tips);
5763
6207
  return result;
5764
6208
  }
@@ -6469,7 +6913,7 @@ async function loadHooks() {
6469
6913
 
6470
6914
  // src/executor.ts
6471
6915
  import { homedir as homedir5 } from "os";
6472
- import { join as join5 } from "path";
6916
+ import { join as join7 } from "path";
6473
6917
  function levenshtein(a, b) {
6474
6918
  const m = a.length, n = b.length;
6475
6919
  const dp = Array(m + 1).fill(null).map(() => Array(n + 1).fill(0));
@@ -6484,7 +6928,7 @@ function levenshtein(a, b) {
6484
6928
  }
6485
6929
  var NAVIGATION_COMMANDS = /* @__PURE__ */ new Set(["goto", "back", "forward", "refresh"]);
6486
6930
  var snapshotHintShown = /* @__PURE__ */ new WeakSet();
6487
- var CONFIG_DIR2 = join5(homedir5(), ".xbrowser");
6931
+ var CONFIG_DIR2 = join7(homedir5(), ".xbrowser");
6488
6932
  var storageCache = /* @__PURE__ */ new Map();
6489
6933
  function getPluginStorage(pluginName) {
6490
6934
  if (!storageCache.has(pluginName)) {
@@ -6496,7 +6940,7 @@ var archiveInitialized = false;
6496
6940
  function ensureArchiveInit() {
6497
6941
  if (!archiveInitialized) {
6498
6942
  try {
6499
- configureArchiveStore({ archiveDir: join5(homedir5(), ".xbrowser", "archives") });
6943
+ configureArchiveStore({ archiveDir: join7(homedir5(), ".xbrowser", "archives") });
6500
6944
  } catch {
6501
6945
  }
6502
6946
  archiveInitialized = true;
@@ -6520,7 +6964,7 @@ async function guardCheck(commandName) {
6520
6964
  }
6521
6965
  }
6522
6966
  function errorResult(message) {
6523
- return { ...fail11(message), duration: 0 };
6967
+ return { ...fail13(message), duration: 0 };
6524
6968
  }
6525
6969
  function tipsToMessages(tips) {
6526
6970
  if (!tips || tips.length === 0) return [];
@@ -6597,7 +7041,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
6597
7041
  }
6598
7042
  let targetPageOverride = null;
6599
7043
  if (_target && extraOpts?.cdpEndpoint) {
6600
- const { findTargetPage } = await import("./browser-XNU7JQYC.js");
7044
+ const { findTargetPage } = await import("./browser-AQ2AT2YZ.js");
6601
7045
  targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
6602
7046
  if (!targetPageOverride) {
6603
7047
  return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
@@ -6614,8 +7058,15 @@ async function executeCommand(commandName, params, sessionName = "default", extr
6614
7058
  params = result.data;
6615
7059
  }
6616
7060
  if (command.scope !== "cli" && !process.env.XBROWSER_DAEMON_WORKER) {
6617
- const { forwardExec } = await import("./daemon-client-MMDRYCF5.js");
6618
- const result = await forwardExec(commandName, params, sessionName, extraOpts?.cdpEndpoint);
7061
+ const { forwardExec } = await import("./daemon-client-7D6EZNOE.js");
7062
+ const longRunning = /^(vision-task|crawl|scrape|record|replay)$/.test(commandName);
7063
+ const result = await forwardExec(
7064
+ commandName,
7065
+ params,
7066
+ sessionName,
7067
+ extraOpts?.cdpEndpoint,
7068
+ longRunning ? 20 * 6e4 : void 0
7069
+ );
6619
7070
  if (result) return result;
6620
7071
  }
6621
7072
  let session;
@@ -6783,7 +7234,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
6783
7234
  timestamp: start
6784
7235
  });
6785
7236
  if (isSuccess) {
6786
- return { ...ok25(raw.data, merged.length > 0 ? merged : raw.tips), duration, ...hookOutputs ? { hookOutputs } : {} };
7237
+ return { ...ok28(raw.data, merged.length > 0 ? merged : raw.tips), duration, ...hookOutputs ? { hookOutputs } : {} };
6787
7238
  }
6788
7239
  return { success: false, data: raw.data, message: raw.message, tips: mergedOrRaw, duration, ...hookOutputs ? { hookOutputs } : {} };
6789
7240
  }
@@ -6797,14 +7248,14 @@ async function executeCommand(commandName, params, sessionName = "default", extr
6797
7248
  duration,
6798
7249
  timestamp: start
6799
7250
  });
6800
- return { ...ok25(raw, smartTipNormalized), duration, ...hookOutputs ? { hookOutputs } : {} };
7251
+ return { ...ok28(raw, smartTipNormalized), duration, ...hookOutputs ? { hookOutputs } : {} };
6801
7252
  } catch (err) {
6802
7253
  const end = Date.now();
6803
7254
  const duration = end - start;
6804
7255
  const errorMessage = errMsg(err);
6805
7256
  if (session?.page && process.env.XBROWSER_RECOVERY && !extraOpts?._recoveryAttempted) {
6806
7257
  try {
6807
- const { attemptRecovery } = await import("./recovery-EF33LKRJ.js");
7258
+ const { attemptRecovery } = await import("./recovery-Z3RDZENA.js");
6808
7259
  const recovery = await attemptRecovery(
6809
7260
  session.page,
6810
7261
  sessionName,
@@ -6842,7 +7293,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
6842
7293
  duration,
6843
7294
  timestamp: start
6844
7295
  });
6845
- return { ...fail11(errorMessage), duration };
7296
+ return { ...fail13(errorMessage), duration };
6846
7297
  } finally {
6847
7298
  }
6848
7299
  }
@@ -6883,7 +7334,7 @@ async function executeChain(input, options) {
6883
7334
  results.push({
6884
7335
  command: cmdName,
6885
7336
  raw: cmdStr,
6886
- ...fail11(`Plugin "${cmdName}" requires a sub-command`),
7337
+ ...fail13(`Plugin "${cmdName}" requires a sub-command`),
6887
7338
  duration: 0
6888
7339
  });
6889
7340
  if (type === "and") {
@@ -6902,7 +7353,7 @@ async function executeChain(input, options) {
6902
7353
  results.push({
6903
7354
  command: cmdName,
6904
7355
  raw: cmdStr,
6905
- ...fail11(`Unknown command "${subCommand}" for plugin "${cmdName}"`),
7356
+ ...fail13(`Unknown command "${subCommand}" for plugin "${cmdName}"`),
6906
7357
  duration: 0
6907
7358
  });
6908
7359
  if (type === "and") {
@@ -7006,7 +7457,7 @@ async function executeChain(input, options) {
7006
7457
  results.push({
7007
7458
  command: `${cmdName} ${subCommand}`,
7008
7459
  raw: cmdStr,
7009
- ...ok25(data),
7460
+ ...ok28(data),
7010
7461
  duration: duration2,
7011
7462
  ...hookOutputs ? { hookOutputs } : {}
7012
7463
  });
@@ -7034,7 +7485,7 @@ async function executeChain(input, options) {
7034
7485
  results.push({
7035
7486
  command: `${cmdName} ${subCommand}`,
7036
7487
  raw: cmdStr,
7037
- ...fail11(errorMessage),
7488
+ ...fail13(errorMessage),
7038
7489
  duration: duration2
7039
7490
  });
7040
7491
  if (type === "and") {
@@ -7338,10 +7789,10 @@ async function replayEntry(entry, options = {}) {
7338
7789
  }
7339
7790
 
7340
7791
  // src/daemon/feedback-store.ts
7341
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4 } from "fs";
7342
- import { join as join6 } from "path";
7792
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4 } from "fs";
7793
+ import { join as join8 } from "path";
7343
7794
  import { homedir as homedir6 } from "os";
7344
- var FEEDBACK_FILE = join6(homedir6(), ".xbrowser", "feedback.json");
7795
+ var FEEDBACK_FILE = join8(homedir6(), ".xbrowser", "feedback.json");
7345
7796
  var FeedbackStore = class {
7346
7797
  entries = [];
7347
7798
  constructor() {
@@ -7349,7 +7800,7 @@ var FeedbackStore = class {
7349
7800
  }
7350
7801
  load() {
7351
7802
  try {
7352
- const data = readFileSync3(FEEDBACK_FILE, "utf8");
7803
+ const data = readFileSync5(FEEDBACK_FILE, "utf8");
7353
7804
  this.entries = JSON.parse(data);
7354
7805
  } catch {
7355
7806
  this.entries = [];
@@ -7357,7 +7808,7 @@ var FeedbackStore = class {
7357
7808
  }
7358
7809
  save() {
7359
7810
  try {
7360
- mkdirSync4(join6(homedir6(), ".xbrowser"), { recursive: true });
7811
+ mkdirSync4(join8(homedir6(), ".xbrowser"), { recursive: true });
7361
7812
  writeFileSync4(FEEDBACK_FILE, JSON.stringify(this.entries, null, 2));
7362
7813
  } catch {
7363
7814
  }
@@ -7841,7 +8292,7 @@ function createRPCHandler() {
7841
8292
  return result;
7842
8293
  } catch (err) {
7843
8294
  const errorMessage = errMsg(err);
7844
- const { attemptRecovery } = await import("./recovery-EF33LKRJ.js");
8295
+ const { attemptRecovery } = await import("./recovery-Z3RDZENA.js");
7845
8296
  const recovery = await attemptRecovery(
7846
8297
  session?.page,
7847
8298
  sessionName,
@@ -8381,7 +8832,7 @@ function createRPCHandler() {
8381
8832
  let rawContent;
8382
8833
  let parsed;
8383
8834
  try {
8384
- rawContent = readFileSync5(file, "utf8");
8835
+ rawContent = readFileSync7(file, "utf8");
8385
8836
  try {
8386
8837
  parsed = JSON.parse(rawContent);
8387
8838
  } catch {
@@ -8406,7 +8857,7 @@ function createRPCHandler() {
8406
8857
  if (isNewFormat) {
8407
8858
  try {
8408
8859
  const replayErrors = [];
8409
- const { SessionReplayer } = await import("./session-replayer-WIUYVN5J.js");
8860
+ const { SessionReplayer } = await import("./session-replayer-UVH5FGY2.js");
8410
8861
  const replayer = new SessionReplayer({
8411
8862
  page: session.page,
8412
8863
  stepDelay: slowMo * 500,
@@ -8456,10 +8907,10 @@ function createRPCHandler() {
8456
8907
  const errors = Array.isArray(input.errors) ? input.errors : [];
8457
8908
  const eventsPlayed = typeof input.eventsPlayed === "number" ? input.eventsPlayed : 0;
8458
8909
  const totalEvents = typeof input.totalEvents === "number" ? input.totalEvents : eventsPlayed + errors.length;
8459
- const ok26 = input.ok ?? errors.length === 0;
8910
+ const ok29 = input.ok ?? errors.length === 0;
8460
8911
  return {
8461
- ok: ok26,
8462
- success: ok26,
8912
+ ok: ok29,
8913
+ success: ok29,
8463
8914
  duration: typeof input.duration === "number" ? input.duration : 0,
8464
8915
  eventsPlayed,
8465
8916
  totalEvents,
@@ -9458,12 +9909,12 @@ var FileListHandler = class {
9458
9909
  const msg = ctx.message;
9459
9910
  try {
9460
9911
  const { readdirSync, statSync } = await import("fs");
9461
- const { join: join8, resolve } = await import("path");
9912
+ const { join: join10, resolve } = await import("path");
9462
9913
  const targetPath = resolve(msg.path);
9463
9914
  const entries = readdirSync(targetPath);
9464
9915
  const files = entries.map((name) => {
9465
9916
  try {
9466
- const stat = statSync(join8(targetPath, name));
9917
+ const stat = statSync(join10(targetPath, name));
9467
9918
  return { name, isDir: stat.isDirectory(), size: stat.size, modified: stat.mtime.toISOString() };
9468
9919
  } catch {
9469
9920
  return { name, isDir: false, size: 0, modified: "" };
@@ -9480,10 +9931,10 @@ var FileDownloadHandler = class {
9480
9931
  async handle(ctx) {
9481
9932
  const msg = ctx.message;
9482
9933
  try {
9483
- const { readFileSync: readFileSync6 } = await import("fs");
9934
+ const { readFileSync: readFileSync8 } = await import("fs");
9484
9935
  const { resolve, basename } = await import("path");
9485
9936
  const targetPath = resolve(msg.path);
9486
- const data = readFileSync6(targetPath);
9937
+ const data = readFileSync8(targetPath);
9487
9938
  const base64 = data.toString("base64");
9488
9939
  const ext = targetPath.split(".").pop()?.toLowerCase() || "";
9489
9940
  const mimeMap = {
@@ -11473,8 +11924,8 @@ connectWS();
11473
11924
  }
11474
11925
 
11475
11926
  // src/daemon/daemon-main.ts
11476
- var CONFIG_DIR3 = join7(homedir7(), ".xbrowser");
11477
- var LOG_FILE = join7(CONFIG_DIR3, "daemon.log");
11927
+ var CONFIG_DIR3 = join9(homedir7(), ".xbrowser");
11928
+ var LOG_FILE = join9(CONFIG_DIR3, "daemon.log");
11478
11929
  function log(msg) {
11479
11930
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").substring(0, 19);
11480
11931
  const line = `[DAEMON ${ts}] ${msg}
@@ -11506,7 +11957,7 @@ async function main() {
11506
11957
  if (err.code === "EADDRINUSE") {
11507
11958
  log(`Port ${daemonPort} already in use \u2014 another daemon instance likely won the startup race. Exiting gracefully.`);
11508
11959
  try {
11509
- unlinkSync(join7(CONFIG_DIR3, "daemon.json"));
11960
+ unlinkSync(join9(CONFIG_DIR3, "daemon.json"));
11510
11961
  } catch {
11511
11962
  }
11512
11963
  process.exit(0);
@@ -11548,7 +11999,7 @@ async function main() {
11548
11999
  });
11549
12000
  });
11550
12001
  mkdirSync5(CONFIG_DIR3, { recursive: true });
11551
- writeFileSync5(join7(CONFIG_DIR3, "daemon.json"), JSON.stringify({
12002
+ writeFileSync5(join9(CONFIG_DIR3, "daemon.json"), JSON.stringify({
11552
12003
  port: daemonPort,
11553
12004
  pid: process.pid,
11554
12005
  startedAt: Date.now()