@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
package/dist/cli.js CHANGED
@@ -22,7 +22,7 @@ import {
22
22
  forwardSessionList,
23
23
  forwardViewerCheckSelector,
24
24
  version
25
- } from "./chunk-WHPBNUKB.js";
25
+ } from "./chunk-SH6OTPXN.js";
26
26
  import {
27
27
  buildViewerUrl
28
28
  } from "./chunk-3OD76SUE.js";
@@ -59,7 +59,7 @@ import {
59
59
  resolveLaunchOpts,
60
60
  saveSessionDiskMeta,
61
61
  setActivePage
62
- } from "./chunk-JEDP4PJW.js";
62
+ } from "./chunk-BY5TT6WZ.js";
63
63
  import "./chunk-TNEN6VQ2.js";
64
64
  import {
65
65
  errMsg
@@ -75,8 +75,9 @@ import {
75
75
  getMarketplaceUrl,
76
76
  loadConfig,
77
77
  resolveNpmPackageWithFallback,
78
+ resolveScreenshotsDir,
78
79
  setConfigValue
79
- } from "./chunk-IIM5GOD7.js";
80
+ } from "./chunk-ZTHE5RBZ.js";
80
81
  import "./chunk-KFQGP6VL.js";
81
82
 
82
83
  // src/router.ts
@@ -199,8 +200,8 @@ function asZodSchema(value) {
199
200
 
200
201
  // src/executor.ts
201
202
  import {
202
- ok as ok25,
203
- fail as fail11,
203
+ ok as ok28,
204
+ fail as fail13,
204
205
  isCommandResult,
205
206
  CompositeStorage as CompositeStorage2,
206
207
  TipCollector as TipCollector2,
@@ -963,29 +964,441 @@ var mouseCommand = registerCommand({
963
964
  }
964
965
  });
965
966
 
966
- // src/commands/evaluate.ts
967
+ // src/commands/find-visual.ts
967
968
  import { z as z7 } from "zod";
968
- import { ok as ok7, normalizeTips as normalizeTips2 } from "@dyyz1993/xcli-core";
969
+ import { ok as ok7, fail as fail2 } from "@dyyz1993/xcli-core";
970
+ import { readFileSync, existsSync } from "fs";
971
+ import { homedir as homedir2 } from "os";
972
+ import { join as join2 } from "path";
973
+ function loadVLMCredentials() {
974
+ const candidates = [
975
+ process.env.XBROWSER_VLM_CONFIG,
976
+ join2(homedir2(), ".zcode", "v2", "config.json")
977
+ ].filter(Boolean);
978
+ for (const file of candidates) {
979
+ try {
980
+ if (!existsSync(file)) continue;
981
+ const cfg = JSON.parse(readFileSync(file, "utf8"));
982
+ const provider = Object.values(cfg.provider ?? {})[0];
983
+ if (provider?.options?.apiKey && provider.options.baseURL) {
984
+ const models = Object.keys(provider.models ?? {});
985
+ const vl = models.find((m) => /vl|vision/i.test(m));
986
+ const flash = models.find((m) => /flash/i.test(m) && /5\.?3/i.test(m));
987
+ return {
988
+ apiKey: provider.options.apiKey,
989
+ baseURL: provider.options.baseURL.replace(/\/$/, ""),
990
+ model: process.env.XBROWSER_VLM_MODEL || vl || flash || models.find((m) => /5\.[23]/.test(m)) || models[0]
991
+ };
992
+ }
993
+ } catch {
994
+ }
995
+ }
996
+ return null;
997
+ }
998
+ async function locateByVLM(creds, imageBase64, target, feedback) {
999
+ 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}"""
1000
+
1001
+ \u4E25\u683C\u53EA\u8F93\u51FA\u4E00\u4E2A JSON \u5BF9\u8C61\uFF08\u65E0 markdown\u3001\u65E0\u89E3\u91CA\uFF09\uFF1A
1002
+ {"label":"<\u5143\u7D20\u540D>","box":{"x":<\u5DE6\u4E0Ax>,"y":<\u5DE6\u4E0Ay>,"width":<\u5BBD>,"height":<\u9AD8>},"confidence":"high|medium|low"}
1003
+ \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
1004
+ ${feedback ? `\u6CE8\u610F\uFF1A${feedback}` : ""}`;
1005
+ const resp = await fetch(creds.baseURL + "/v1/messages", {
1006
+ method: "POST",
1007
+ headers: { "content-type": "application/json", "x-api-key": creds.apiKey, "anthropic-version": "2023-06-01" },
1008
+ body: JSON.stringify({
1009
+ model: creds.model,
1010
+ max_tokens: 2e3,
1011
+ // thinking 模型的思考块也计入配额,300 会在 text 前截断
1012
+ messages: [{
1013
+ role: "user",
1014
+ content: [
1015
+ { type: "image", source: { type: "base64", media_type: "image/png", data: imageBase64 } },
1016
+ { type: "text", text: prompt }
1017
+ ]
1018
+ }]
1019
+ }),
1020
+ signal: AbortSignal.timeout(6e4)
1021
+ });
1022
+ if (!resp.ok) throw new Error(`VLM HTTP ${resp.status}`);
1023
+ const data = await resp.json();
1024
+ const text = (data.content ?? []).filter((c) => c.type === "text").map((c) => c.text ?? "").join("");
1025
+ const m = text.match(/\{[\s\S]*\}/);
1026
+ if (!m) {
1027
+ console.error("[find-visual] VLM raw (no JSON):", text.substring(0, 200));
1028
+ return null;
1029
+ }
1030
+ const hit = JSON.parse(m[0]);
1031
+ if (!hit.label || !hit.box) return null;
1032
+ return hit;
1033
+ }
1034
+ var findVisualCommand = registerCommand({
1035
+ name: "find-visual",
1036
+ description: "Visual element location: screenshot \u2192 VLM \u2192 page coordinates (optionally click)",
1037
+ scope: "page",
1038
+ parameters: z7.object({
1039
+ 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'),
1040
+ click: z7.boolean().optional().describe("\u627E\u5230\u540E\u7ACB\u5373\u70B9\u51FB\uFF08\u771F\u5B9E\u9F20\u6807\u4E8B\u4EF6\uFF09"),
1041
+ threshold: z7.coerce.number().optional().describe("\u7F6E\u4FE1\u5EA6\u9608\u503C\uFF0C\u4F4E\u4E8E\u5219\u62A5 not-found\uFF08\u9ED8\u8BA4 0\uFF09"),
1042
+ 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")
1043
+ }),
1044
+ result: z7.object({
1045
+ found: z7.boolean(),
1046
+ element: z7.string(),
1047
+ box: z7.unknown().optional(),
1048
+ center: z7.unknown().optional(),
1049
+ confidence: z7.string().optional(),
1050
+ model: z7.string().optional(),
1051
+ clicked: z7.boolean().optional(),
1052
+ screenshot: z7.string().optional()
1053
+ }),
1054
+ handler: async (p, ctx) => {
1055
+ const page = ctx.page;
1056
+ const creds = loadVLMCredentials();
1057
+ if (!creds) {
1058
+ 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", [
1059
+ "\u4E5F\u53EF\u7528 XBROWSER_VLM_MODEL \u6307\u5B9A\u6A21\u578B\uFF08\u9ED8\u8BA4\u81EA\u52A8\u9009 VL/\u65D7\u8230\u6A21\u578B\uFF09"
1060
+ ]);
1061
+ }
1062
+ const buffer = await page.screenshot({ type: "png" });
1063
+ const imageBase64 = buffer.toString("base64");
1064
+ const imgW = buffer.readUInt32BE(16);
1065
+ const imgH = buffer.readUInt32BE(20);
1066
+ let hit = null;
1067
+ let feedback = "";
1068
+ try {
1069
+ for (let attempt = 0; attempt < 3 && !hit; attempt++) {
1070
+ const candidate = await locateByVLM(creds, imageBase64, p.element, feedback);
1071
+ if (!candidate) break;
1072
+ const b = candidate.box;
1073
+ const inBounds = b.x >= 0 && b.y >= 0 && b.x + b.width <= imgW + 2 && b.y + b.height <= imgH + 2;
1074
+ const sane = b.width > 2 && b.height > 2;
1075
+ if (inBounds && sane) {
1076
+ hit = candidate;
1077
+ break;
1078
+ }
1079
+ 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`;
1080
+ }
1081
+ } catch (e) {
1082
+ return fail2(`VLM \u8C03\u7528\u5931\u8D25: ${e instanceof Error ? e.message : String(e)}`);
1083
+ }
1084
+ if (!hit) {
1085
+ return ok7({ found: false, element: p.element, model: creds.model }, [
1086
+ `\u622A\u56FE\u4E2D\u672A\u627E\u5230: ${p.element}`
1087
+ ]);
1088
+ }
1089
+ if (p.zoom !== false && hit) {
1090
+ try {
1091
+ const pad = Math.max(20, Math.round(Math.max(hit.box.width, hit.box.height) * 0.5));
1092
+ const clip = {
1093
+ x: Math.max(0, hit.box.x - pad),
1094
+ y: Math.max(0, hit.box.y - pad),
1095
+ width: Math.min(imgW - Math.max(0, hit.box.x - pad), hit.box.width + pad * 2),
1096
+ height: Math.min(imgH - Math.max(0, hit.box.y - pad), hit.box.height + pad * 2)
1097
+ };
1098
+ if (clip.width > 8 && clip.height > 8) {
1099
+ const zoomBuf = await page.screenshot({ type: "png", clip });
1100
+ const zoomB64 = zoomBuf.toString("base64");
1101
+ const zoomW = zoomBuf.readUInt32BE(16);
1102
+ const zoomH = zoomBuf.readUInt32BE(20);
1103
+ const refined = await locateByVLM(creds, zoomB64, p.element);
1104
+ if (refined) {
1105
+ const rb = refined.box;
1106
+ 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;
1107
+ if (inZoom) {
1108
+ hit = {
1109
+ label: refined.label || hit.label,
1110
+ box: {
1111
+ x: clip.x + rb.x,
1112
+ y: clip.y + rb.y,
1113
+ width: rb.width,
1114
+ height: rb.height
1115
+ },
1116
+ confidence: refined.confidence === "low" ? hit.confidence : refined.confidence
1117
+ };
1118
+ }
1119
+ }
1120
+ }
1121
+ } catch {
1122
+ }
1123
+ }
1124
+ if (p.threshold !== void 0) {
1125
+ const rank = { high: 3, medium: 2, low: 1 };
1126
+ const thresholdName = p.threshold >= 3 ? "high" : p.threshold >= 2 ? "medium" : "low";
1127
+ if ((rank[hit.confidence ?? "low"] ?? 1) < (rank[thresholdName] ?? 1)) {
1128
+ return ok7({ found: false, element: p.element, confidence: hit.confidence, box: hit.box, model: creds.model }, [
1129
+ `\u7F6E\u4FE1\u5EA6 ${hit.confidence} \u4F4E\u4E8E\u9608\u503C ${p.threshold}`
1130
+ ]);
1131
+ }
1132
+ }
1133
+ const viewport = await page.evaluate("JSON.stringify({w: window.innerWidth, h: window.innerHeight})").then((v) => JSON.parse(String(v))).catch(() => ({ w: 1280, h: 800 }));
1134
+ const pngW = buffer.readUInt32BE(16);
1135
+ const pngH = buffer.readUInt32BE(20);
1136
+ const scaleX = viewport.w / pngW;
1137
+ const scaleY = viewport.h / pngH;
1138
+ const center = {
1139
+ x: Math.round((hit.box.x + hit.box.width / 2) * scaleX),
1140
+ y: Math.round((hit.box.y + hit.box.height / 2) * scaleY)
1141
+ };
1142
+ let clicked = false;
1143
+ if (p.click) {
1144
+ await page.mouse.click(center.x, center.y, { stealth: true });
1145
+ clicked = true;
1146
+ }
1147
+ return ok7({
1148
+ found: true,
1149
+ element: p.element,
1150
+ box: hit.box,
1151
+ center,
1152
+ confidence: hit.confidence,
1153
+ model: creds.model,
1154
+ clicked,
1155
+ screenshot: `data:image/png;base64,${imageBase64.substring(0, 64)}... (${Math.round(imageBase64.length / 1024)}KB)`
1156
+ }, [
1157
+ 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`
1158
+ ]);
1159
+ }
1160
+ });
1161
+
1162
+ // src/commands/vision-task.ts
1163
+ import { z as z8 } from "zod";
1164
+ import { ok as ok8, fail as fail3 } from "@dyyz1993/xcli-core";
1165
+ import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
1166
+ import { homedir as homedir3 } from "os";
1167
+ import { join as join3 } from "path";
1168
+ function loadVLMCredentials2() {
1169
+ const candidates = [
1170
+ process.env.XBROWSER_VLM_CONFIG,
1171
+ join3(homedir3(), ".zcode", "v2", "config.json")
1172
+ ].filter(Boolean);
1173
+ for (const file of candidates) {
1174
+ try {
1175
+ if (!existsSync2(file)) continue;
1176
+ const cfg = JSON.parse(readFileSync2(file, "utf8"));
1177
+ const provider = Object.values(cfg.provider ?? {})[0];
1178
+ if (provider?.options?.apiKey && provider.options.baseURL) {
1179
+ const models = Object.keys(provider.models ?? {});
1180
+ const vl = models.find((m) => /vl|vision/i.test(m));
1181
+ const flash = models.find((m) => /flash/i.test(m) && /5\.?3/i.test(m));
1182
+ return {
1183
+ apiKey: provider.options.apiKey,
1184
+ baseURL: provider.options.baseURL.replace(/\/$/, ""),
1185
+ model: process.env.XBROWSER_VLM_MODEL || vl || flash || models.find((m) => /5\.[23]/.test(m)) || models[0]
1186
+ };
1187
+ }
1188
+ } catch {
1189
+ }
1190
+ }
1191
+ return null;
1192
+ }
1193
+ async function vlmAsk(creds, content, maxTokens = 2e3) {
1194
+ const resp = await fetch(creds.baseURL + "/v1/messages", {
1195
+ method: "POST",
1196
+ headers: { "content-type": "application/json", "x-api-key": creds.apiKey, "anthropic-version": "2023-06-01" },
1197
+ body: JSON.stringify({ model: creds.model, max_tokens: maxTokens, messages: [{ role: "user", content }] }),
1198
+ signal: AbortSignal.timeout(9e4)
1199
+ });
1200
+ if (!resp.ok) throw new Error(`VLM HTTP ${resp.status}`);
1201
+ const data = await resp.json();
1202
+ return (data.content ?? []).filter((c) => c.type === "text").map((c) => c.text ?? "").join("");
1203
+ }
1204
+ async function vlmAskRetry(creds, content, maxTokens = 2e3) {
1205
+ let last = "";
1206
+ for (let i = 0; i < 2; i++) {
1207
+ last = await vlmAsk(creds, content, maxTokens);
1208
+ if (last.trim()) return last;
1209
+ await new Promise((r) => setTimeout(r, 1200));
1210
+ }
1211
+ return last;
1212
+ }
1213
+ function pngDims(b64) {
1214
+ const buf = Buffer.from(b64, "base64");
1215
+ return { w: buf.readUInt32BE(16), h: buf.readUInt32BE(20) };
1216
+ }
1217
+ function parseDecision(text) {
1218
+ const m = text.match(/\{[\s\S]*\}/);
1219
+ if (!m) return null;
1220
+ try {
1221
+ const j = JSON.parse(m[0]);
1222
+ if (!j.action || !j.action.type) return null;
1223
+ return j;
1224
+ } catch {
1225
+ return null;
1226
+ }
1227
+ }
1228
+ 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
1229
+
1230
+ \u4E25\u683C\u53EA\u8F93\u51FA\u4E00\u4E2A JSON\uFF08\u65E0 markdown\uFF09\uFF1A
1231
+ {"thought":"<\u7B80\u77ED\u63A8\u7406>","action":{"type":"click","x":<int>,"y":<int>}
1232
+ \u6216 {"type":"type","text":"<\u8981\u8F93\u5165\u7684\u6587\u5B57>"}\uFF08\u8F93\u5165\u524D\u987B\u5DF2\u70B9\u51FB\u805A\u7126\u8F93\u5165\u6846\uFF09
1233
+ \u6216 {"type":"key","key":"Enter"}\uFF08key \u540D\uFF1AEnter/Tab/Escape/ArrowDown\u2026\uFF09
1234
+ \u6216 {"type":"scroll","direction":"down","amount":<px>}
1235
+ \u6216 {"type":"done","summary":"<\u4EFB\u52A1\u5B8C\u6210\u6458\u8981>"}
1236
+ \u6216 {"type":"stuck","reason":"<\u65E0\u6CD5\u7EE7\u7EED\u7684\u539F\u56E0>"}}
1237
+
1238
+ \u5750\u6807\u4EE5\u622A\u56FE\u5DE6\u4E0A\u89D2\u4E3A\u539F\u70B9\u3001\u50CF\u7D20\u4E3A\u5355\u4F4D\u3002\u89C4\u5219\uFF1A
1239
+ 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
1240
+ 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
1241
+ 3. \u4EFB\u52A1\u76EE\u6807\u8FBE\u6210\u7ACB\u5373\u8F93\u51FA done\uFF1B5 \u6B65\u65E0\u8FDB\u5C55\u8F93\u51FA stuck`;
1242
+ var visionTaskCommand = registerCommand({
1243
+ name: "vision-task",
1244
+ description: "Vision-loop agent: screenshot \u2192 VLM decide \u2192 CDP act \u2192 verify, until done",
1245
+ scope: "page",
1246
+ parameters: z8.object({
1247
+ task: z8.string().describe('\u81EA\u7136\u8BED\u8A00\u4EFB\u52A1\uFF0C\u5982 "\u641C\u7D22 xbrowser \u5E76\u6253\u5F00\u7B2C\u4E00\u7BC7\u6587\u7AE0"'),
1248
+ maxSteps: z8.coerce.number().optional().describe("\u6700\u5927\u6B65\u6570\uFF08\u9ED8\u8BA4 12\uFF09"),
1249
+ settleMs: z8.coerce.number().optional().describe("\u6BCF\u6B65\u540E\u7B49\u5F85\u9875\u9762\u7A33\u5B9A\u7684\u6BEB\u79D2\u6570\uFF08\u9ED8\u8BA4 1500\uFF09")
1250
+ }),
1251
+ result: z8.object({
1252
+ done: z8.boolean(),
1253
+ steps: z8.unknown().optional(),
1254
+ summary: z8.string().optional(),
1255
+ model: z8.string().optional()
1256
+ }),
1257
+ handler: async (p, ctx) => {
1258
+ let page = ctx.page;
1259
+ const creds = loadVLMCredentials2();
1260
+ if (!creds) {
1261
+ return fail3("\u672A\u627E\u5230 VLM \u51ED\u636E\uFF1A\u9700\u8981 ~/.zcode/v2/config.json \u6216 XBROWSER_VLM_CONFIG");
1262
+ }
1263
+ const maxSteps = p.maxSteps ?? 12;
1264
+ const settleMs = p.settleMs ?? 1500;
1265
+ const steps = [];
1266
+ const history = [];
1267
+ let lastUrl = page.url();
1268
+ for (let step = 1; step <= maxSteps; step++) {
1269
+ if (page.url() === "about:blank") {
1270
+ try {
1271
+ const ctxObj = page.context?.();
1272
+ const pages = ctxObj?.pages?.() || [];
1273
+ let real;
1274
+ for (let i = pages.length - 1; i >= 0; i--) {
1275
+ const u = pages[i]?.url?.() ?? "";
1276
+ if (u && u !== "about:blank") {
1277
+ real = pages[i];
1278
+ break;
1279
+ }
1280
+ }
1281
+ if (real) {
1282
+ await real.bringToFront().catch(() => {
1283
+ });
1284
+ page = real;
1285
+ }
1286
+ } catch {
1287
+ }
1288
+ }
1289
+ const buffer = await page.screenshot({ type: "png" });
1290
+ const b64 = buffer.toString("base64");
1291
+ const { w, h } = pngDims(b64);
1292
+ const obsText = [
1293
+ `\u4EFB\u52A1\uFF1A${p.task}`,
1294
+ `\u622A\u56FE\u5C3A\u5BF8\uFF1A${w}x${h}\uFF08\u5750\u6807\u539F\u70B9\uFF1A\u5DE6\u4E0A\u89D2\uFF09`,
1295
+ `\u5F53\u524D URL\uFF1A${page.url()}`,
1296
+ history.length ? `\u5DF2\u6267\u884C\u52A8\u4F5C\uFF1A
1297
+ ${history.slice(-6).join("\n")}` : "\u5C1A\u65E0\u52A8\u4F5C\u3002"
1298
+ ].join("\n");
1299
+ const reply = await vlmAskRetry(creds, [
1300
+ { type: "image", source: { type: "base64", media_type: "image/png", data: b64 } },
1301
+ { type: "text", text: `${SYSTEM_PROMPT}
1302
+
1303
+ ${obsText}` }
1304
+ ]);
1305
+ const decision = parseDecision(reply);
1306
+ if (!decision) {
1307
+ steps.push({ n: step, thought: "(unparseable)", action: "stop", ts: Date.now() });
1308
+ return ok8({ done: false, steps, summary: `\u7B2C ${step} \u6B65 VLM \u8F93\u51FA\u65E0\u6CD5\u89E3\u6790: ${reply.substring(0, 120)}`, model: creds.model });
1309
+ }
1310
+ const a = decision.action;
1311
+ let acted = "";
1312
+ try {
1313
+ if (a.type === "done") {
1314
+ steps.push({ n: step, thought: decision.thought, action: "done", ts: Date.now() });
1315
+ return ok8({ done: true, steps, summary: a.summary, model: creds.model });
1316
+ }
1317
+ if (a.type === "stuck") {
1318
+ steps.push({ n: step, thought: decision.thought, action: "stuck", ts: Date.now() });
1319
+ return ok8({ done: false, steps, summary: `stuck: ${a.reason}`, model: creds.model });
1320
+ }
1321
+ if (a.type === "click") {
1322
+ const cx = Math.max(2, Math.min(w - 2, Math.round(a.x)));
1323
+ const cy = Math.max(2, Math.min(h - 2, Math.round(a.y)));
1324
+ await page.mouse.click(cx, cy, { stealth: true });
1325
+ acted = `click(${cx},${cy})`;
1326
+ } else if (a.type === "type") {
1327
+ await page.keyboard.type(a.text, { stealth: true });
1328
+ acted = `type("${a.text.substring(0, 30)}")`;
1329
+ } else if (a.type === "key") {
1330
+ await page.keyboard.press(a.key);
1331
+ acted = `key(${a.key})`;
1332
+ } else if (a.type === "scroll") {
1333
+ await page.mouse.wheel(0, a.direction === "down" ? a.amount : -a.amount);
1334
+ acted = `scroll(${a.direction},${a.amount})`;
1335
+ }
1336
+ } catch (e) {
1337
+ acted = `${a.type} FAILED: ${e instanceof Error ? e.message : String(e).substring(0, 80)}`;
1338
+ }
1339
+ steps.push({ n: step, thought: decision.thought, action: acted, ts: Date.now() });
1340
+ history.push(`${step}. ${acted} \u2014 ${decision.thought.substring(0, 60)}`);
1341
+ await new Promise((r) => setTimeout(r, settleMs));
1342
+ if (a.type === "click") {
1343
+ try {
1344
+ const ctxObj = page.context?.();
1345
+ const pages = ctxObj?.pages?.() || [];
1346
+ for (let i = pages.length - 1; i >= 0; i--) {
1347
+ const cand = pages[i];
1348
+ if (!cand || cand === page) continue;
1349
+ const u = cand.url?.() ?? "";
1350
+ if (!u || u === "about:blank") continue;
1351
+ if (u === page.url()) break;
1352
+ await cand.bringToFront().catch(() => {
1353
+ });
1354
+ page = cand;
1355
+ break;
1356
+ }
1357
+ } catch {
1358
+ }
1359
+ }
1360
+ const urlNow = page.url();
1361
+ if (urlNow !== lastUrl) {
1362
+ history.push(` [\u9875\u9762\u8DF3\u8F6C \u2192 ${urlNow.substring(0, 80)}]`);
1363
+ lastUrl = urlNow;
1364
+ }
1365
+ }
1366
+ return ok8({ done: false, steps, summary: `\u5DF2\u8FBE\u6700\u5927\u6B65\u6570 ${maxSteps}`, model: creds.model });
1367
+ }
1368
+ });
1369
+
1370
+ // src/commands/evaluate.ts
1371
+ import { z as z9 } from "zod";
1372
+ import { ok as ok9, normalizeTips as normalizeTips2 } from "@dyyz1993/xcli-core";
969
1373
  var evaluateCommand = registerCommand({
970
1374
  name: "eval",
971
1375
  description: "Evaluate JavaScript expression in the browser (--frame <url-substring> \u53EF\u5B9A\u5411\u5230 iframe \u4E0A\u4E0B\u6587\uFF0C\u8DE8\u57DF iframe \u4EA6\u53EF)",
972
1376
  scope: "page",
973
- parameters: z7.object({
974
- expression: z7.string(),
975
- 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")
1377
+ parameters: z9.object({
1378
+ expression: z9.string().optional(),
1379
+ 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"),
1380
+ // Internal: base64url-encoded script set by the router for `eval <js...>`,
1381
+ // so multiline/semicolon JS survives chain parsing (which splits on ';'
1382
+ // and whitespace). Not intended for direct CLI use.
1383
+ "script-b64": z9.string().optional().describe("Internal: base64url script set by the CLI router; do not pass directly")
976
1384
  }),
977
1385
  handler: async (p, ctx) => {
1386
+ const rawScript = p["script-b64"];
1387
+ const expression = rawScript ? Buffer.from(rawScript, "base64url").toString("utf8") : p.expression;
1388
+ if (!expression || !expression.trim()) {
1389
+ return { ok: false, error: "eval: expression is required" };
1390
+ }
978
1391
  const engine = createRuleEngine();
979
1392
  engine.start();
980
1393
  const decision = engine.evaluate({
981
1394
  method: "Runtime.evaluate",
982
- params: { expression: p.expression },
1395
+ params: { expression },
983
1396
  sessionId: "eval-check",
984
1397
  direction: "client\u2192browser"
985
1398
  });
986
1399
  engine.stop();
987
- const result = p.frame ? await ctx.page.evaluateInFrame(p.frame, p.expression) : await ctx.page.evaluate(p.expression);
988
- const response = ok7({ result });
1400
+ const result = p.frame ? await ctx.page.evaluateInFrame(p.frame, expression) : await ctx.page.evaluate(expression);
1401
+ const response = ok9({ result });
989
1402
  if (decision && decision.severity === "danger") {
990
1403
  response.tips = normalizeTips2([`${decision.suggestion}`]);
991
1404
  }
@@ -993,37 +1406,65 @@ var evaluateCommand = registerCommand({
993
1406
  }
994
1407
  });
995
1408
 
1409
+ // src/commands/ua.ts
1410
+ import { z as z10 } from "zod";
1411
+ import { ok as ok10 } from "@dyyz1993/xcli-core";
1412
+ function uaHookScript(ua) {
1413
+ const uaJson = JSON.stringify(ua);
1414
+ const appVersion = JSON.stringify(ua.replace(/^Mozilla\//, ""));
1415
+ 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){}`;
1416
+ }
1417
+ var uaCommand = registerCommand({
1418
+ name: "ua",
1419
+ description: "Get or override the session User-Agent (anti-bot UA detection countermeasure)",
1420
+ scope: "page",
1421
+ parameters: z10.object({
1422
+ 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')
1423
+ }),
1424
+ handler: async (p, ctx) => {
1425
+ if (!p.value) {
1426
+ const current = await ctx.page.evaluate("navigator.userAgent");
1427
+ return ok10({ userAgent: current });
1428
+ }
1429
+ 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;
1430
+ const script = uaHookScript(ua);
1431
+ await ctx.page.addInitScript(script);
1432
+ await ctx.page.evaluate(`(function(){${script}})()`);
1433
+ return ok10({ userAgent: ua, mode: "init-script" });
1434
+ }
1435
+ });
1436
+
996
1437
  // src/commands/storage.ts
997
- import { z as z8 } from "zod";
998
- import { ok as ok8, fail as fail2 } from "@dyyz1993/xcli-core";
1438
+ import { z as z11 } from "zod";
1439
+ import { ok as ok11, fail as fail4 } from "@dyyz1993/xcli-core";
999
1440
  var getCookiesCommand = registerCommand({
1000
1441
  name: "get-cookies",
1001
1442
  description: "Get all cookies for the current page",
1002
1443
  scope: "page",
1003
- result: z8.object({
1004
- cookies: z8.array(z8.record(z8.unknown()))
1444
+ result: z11.object({
1445
+ cookies: z11.array(z11.record(z11.unknown()))
1005
1446
  }),
1006
1447
  handler: async (_p, ctx) => {
1007
1448
  const cookies = await ctx.browserContext.cookies();
1008
- return ok8({ cookies });
1449
+ return ok11({ cookies });
1009
1450
  }
1010
1451
  });
1011
1452
  var setCookieCommand = registerCommand({
1012
1453
  name: "set-cookie",
1013
1454
  description: "Set a cookie",
1014
1455
  scope: "page",
1015
- parameters: z8.object({
1016
- name: z8.coerce.string(),
1017
- value: z8.coerce.string(),
1018
- domain: z8.coerce.string().optional(),
1019
- path: z8.coerce.string().optional(),
1020
- url: z8.string().optional().describe("Cookie URL (alternative to domain)"),
1021
- expires: z8.number().optional(),
1022
- httpOnly: z8.boolean().optional(),
1023
- secure: z8.boolean().optional(),
1024
- sameSite: z8.enum(["Strict", "Lax", "None"]).optional()
1456
+ parameters: z11.object({
1457
+ name: z11.coerce.string(),
1458
+ value: z11.coerce.string(),
1459
+ domain: z11.coerce.string().optional(),
1460
+ path: z11.coerce.string().optional(),
1461
+ url: z11.string().optional().describe("Cookie URL (alternative to domain)"),
1462
+ expires: z11.number().optional(),
1463
+ httpOnly: z11.boolean().optional(),
1464
+ secure: z11.boolean().optional(),
1465
+ sameSite: z11.enum(["Strict", "Lax", "None"]).optional()
1025
1466
  }),
1026
- result: z8.object({ name: z8.string() }),
1467
+ result: z11.object({ name: z11.string() }),
1027
1468
  handler: async (p, ctx) => {
1028
1469
  const cookie = { ...p };
1029
1470
  if (!cookie.domain && !cookie.url) {
@@ -1038,38 +1479,38 @@ var setCookieCommand = registerCommand({
1038
1479
  }
1039
1480
  }
1040
1481
  if (!cookie.domain && !cookie.url) {
1041
- return fail2("set-cookie requires --domain or --url, or a non-blank page URL to infer from");
1482
+ return fail4("set-cookie requires --domain or --url, or a non-blank page URL to infer from");
1042
1483
  }
1043
1484
  await ctx.browserContext.addCookies([cookie]);
1044
- return ok8({ name: p.name });
1485
+ return ok11({ name: p.name });
1045
1486
  }
1046
1487
  });
1047
1488
  var clearCookiesCommand = registerCommand({
1048
1489
  name: "clear-cookies",
1049
1490
  description: "Clear all cookies",
1050
1491
  scope: "page",
1051
- result: z8.object({ cleared: z8.boolean() }),
1492
+ result: z11.object({ cleared: z11.boolean() }),
1052
1493
  handler: async (_p, ctx) => {
1053
1494
  await ctx.browserContext.clearCookies();
1054
- return ok8({ cleared: true });
1495
+ return ok11({ cleared: true });
1055
1496
  }
1056
1497
  });
1057
1498
  var getLocalStorageCommand = registerCommand({
1058
1499
  name: "get-local-storage",
1059
1500
  description: "Get localStorage entries",
1060
1501
  scope: "page",
1061
- parameters: z8.object({
1062
- key: z8.string().optional()
1502
+ parameters: z11.object({
1503
+ key: z11.string().optional()
1063
1504
  }),
1064
- result: z8.union([
1065
- z8.object({ key: z8.string(), value: z8.string().nullable() }),
1066
- z8.object({ data: z8.record(z8.string()) })
1505
+ result: z11.union([
1506
+ z11.object({ key: z11.string(), value: z11.string().nullable() }),
1507
+ z11.object({ data: z11.record(z11.string()) })
1067
1508
  ]),
1068
1509
  handler: async (p, ctx) => {
1069
1510
  try {
1070
1511
  if (p.key) {
1071
1512
  const value = await ctx.page.evaluate((k) => localStorage.getItem(k), p.key);
1072
- return ok8({ key: p.key, value });
1513
+ return ok11({ key: p.key, value });
1073
1514
  }
1074
1515
  const data = await ctx.page.evaluate(() => {
1075
1516
  const entries = {};
@@ -1079,9 +1520,9 @@ var getLocalStorageCommand = registerCommand({
1079
1520
  }
1080
1521
  return entries;
1081
1522
  });
1082
- return ok8({ data });
1523
+ return ok11({ data });
1083
1524
  } catch (e) {
1084
- return fail2(`localStorage not accessible on this page: ${e instanceof Error ? e.message : String(e)}`);
1525
+ return fail4(`localStorage not accessible on this page: ${e instanceof Error ? e.message : String(e)}`);
1085
1526
  }
1086
1527
  }
1087
1528
  });
@@ -1089,11 +1530,11 @@ var setLocalStorageCommand = registerCommand({
1089
1530
  name: "set-local-storage",
1090
1531
  description: "Set a localStorage entry",
1091
1532
  scope: "page",
1092
- parameters: z8.object({
1093
- key: z8.string(),
1094
- value: z8.string()
1533
+ parameters: z11.object({
1534
+ key: z11.string(),
1535
+ value: z11.string()
1095
1536
  }),
1096
- result: z8.object({ key: z8.string() }),
1537
+ result: z11.object({ key: z11.string() }),
1097
1538
  handler: async (p, ctx) => {
1098
1539
  try {
1099
1540
  await ctx.page.evaluate(
@@ -1102,9 +1543,9 @@ var setLocalStorageCommand = registerCommand({
1102
1543
  },
1103
1544
  { key: p.key, value: p.value }
1104
1545
  );
1105
- return ok8({ key: p.key });
1546
+ return ok11({ key: p.key });
1106
1547
  } catch (e) {
1107
- return fail2(`localStorage not accessible on this page: ${e instanceof Error ? e.message : String(e)}`);
1548
+ return fail4(`localStorage not accessible on this page: ${e instanceof Error ? e.message : String(e)}`);
1108
1549
  }
1109
1550
  }
1110
1551
  });
@@ -1112,26 +1553,24 @@ var clearLocalStorageCommand = registerCommand({
1112
1553
  name: "clear-local-storage",
1113
1554
  description: "Clear all localStorage entries",
1114
1555
  scope: "page",
1115
- result: z8.object({ cleared: z8.boolean() }),
1556
+ result: z11.object({ cleared: z11.boolean() }),
1116
1557
  handler: async (_p, ctx) => {
1117
1558
  try {
1118
1559
  await ctx.page.evaluate(() => localStorage.clear());
1119
- return ok8({ cleared: true });
1560
+ return ok11({ cleared: true });
1120
1561
  } catch (e) {
1121
- return fail2(`localStorage not accessible on this page: ${e instanceof Error ? e.message : String(e)}`);
1562
+ return fail4(`localStorage not accessible on this page: ${e instanceof Error ? e.message : String(e)}`);
1122
1563
  }
1123
1564
  }
1124
1565
  });
1125
1566
 
1126
1567
  // src/commands/screenshot.ts
1127
- import { z as z9 } from "zod";
1128
- import { ok as ok9, fail as fail3 } from "@dyyz1993/xcli-core";
1568
+ import { z as z12 } from "zod";
1569
+ import { ok as ok12, fail as fail5 } from "@dyyz1993/xcli-core";
1129
1570
  import { writeFileSync, mkdirSync } from "fs";
1130
- import { dirname, join as join2 } from "path";
1131
- import { homedir as homedir2 } from "os";
1132
- var SCREENSHOTS_DIR = join2(homedir2(), ".xbrowser", "screenshots");
1571
+ import { dirname, join as join4 } from "path";
1133
1572
  function ensureScreenshotsDir() {
1134
- mkdirSync(SCREENSHOTS_DIR, { recursive: true });
1573
+ mkdirSync(resolveScreenshotsDir(), { recursive: true });
1135
1574
  }
1136
1575
  function ensureParentDir(filePath) {
1137
1576
  const dir = dirname(filePath);
@@ -1147,30 +1586,30 @@ function generateScreenshotPath(format) {
1147
1586
  const timestamp = Date.now();
1148
1587
  const random = Math.random().toString(36).slice(2, 8);
1149
1588
  const ext = format === "jpeg" ? "jpg" : "png";
1150
- return join2(SCREENSHOTS_DIR, `screenshot-${timestamp}-${random}.${ext}`);
1589
+ return join4(resolveScreenshotsDir(), `screenshot-${timestamp}-${random}.${ext}`);
1151
1590
  }
1152
1591
  var screenshotCommand = registerCommand({
1153
1592
  name: "screenshot",
1154
- description: "Take a screenshot of the page or element",
1593
+ 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>)",
1155
1594
  scope: "page",
1156
1595
  selectorParams: ["selector"],
1157
- parameters: z9.object({
1158
- selector: z9.string().optional(),
1159
- type: z9.enum(["png", "jpeg"]).optional(),
1160
- fullPage: z9.boolean().optional(),
1161
- output: z9.string().optional(),
1162
- base64: z9.boolean().optional().describe("Return base64 data instead of file path")
1596
+ parameters: z12.object({
1597
+ selector: z12.string().optional(),
1598
+ type: z12.enum(["png", "jpeg"]).optional(),
1599
+ fullPage: z12.boolean().optional(),
1600
+ output: z12.string().optional(),
1601
+ base64: z12.boolean().optional().describe("Return base64 data instead of file path")
1163
1602
  }),
1164
- result: z9.union([
1165
- z9.object({
1166
- data: z9.string(),
1167
- format: z9.string(),
1168
- size: z9.number()
1603
+ result: z12.union([
1604
+ z12.object({
1605
+ data: z12.string(),
1606
+ format: z12.string(),
1607
+ size: z12.number()
1169
1608
  }),
1170
- z9.object({
1171
- output: z9.string(),
1172
- format: z9.string(),
1173
- size: z9.number()
1609
+ z12.object({
1610
+ output: z12.string(),
1611
+ format: z12.string(),
1612
+ size: z12.number()
1174
1613
  })
1175
1614
  ]),
1176
1615
  handler: async (p, ctx) => {
@@ -1188,21 +1627,21 @@ var screenshotCommand = registerCommand({
1188
1627
  if (p.output) {
1189
1628
  const dirErr = ensureParentDir(p.output);
1190
1629
  if (dirErr) {
1191
- return fail3(`Cannot create directory for --output "${p.output}": ${dirErr}`);
1630
+ return fail5(`Cannot create directory for --output "${p.output}": ${dirErr}`);
1192
1631
  }
1193
1632
  try {
1194
1633
  writeFileSync(p.output, buffer, "binary");
1195
1634
  } catch (err) {
1196
- return fail3(`Failed to write screenshot to "${p.output}": ${err instanceof Error ? err.message : String(err)}`);
1635
+ return fail5(`Failed to write screenshot to "${p.output}": ${err instanceof Error ? err.message : String(err)}`);
1197
1636
  }
1198
- return ok9({
1637
+ return ok12({
1199
1638
  output: p.output,
1200
1639
  format,
1201
1640
  size: buffer.length
1202
1641
  });
1203
1642
  }
1204
1643
  if (p.base64) {
1205
- return ok9({
1644
+ return ok12({
1206
1645
  data: buffer.toString("base64"),
1207
1646
  format,
1208
1647
  size: buffer.length
@@ -1211,7 +1650,7 @@ var screenshotCommand = registerCommand({
1211
1650
  ensureScreenshotsDir();
1212
1651
  const screenshotPath = generateScreenshotPath(format);
1213
1652
  writeFileSync(screenshotPath, buffer, "binary");
1214
- return ok9({
1653
+ return ok12({
1215
1654
  output: screenshotPath,
1216
1655
  format,
1217
1656
  size: buffer.length
@@ -1220,19 +1659,19 @@ var screenshotCommand = registerCommand({
1220
1659
  });
1221
1660
 
1222
1661
  // src/commands/structure.ts
1223
- import { z as z10 } from "zod";
1224
- import { ok as ok10 } from "@dyyz1993/xcli-core";
1662
+ import { z as z13 } from "zod";
1663
+ import { ok as ok13 } from "@dyyz1993/xcli-core";
1225
1664
  var structureCommand = registerCommand({
1226
1665
  name: "structure",
1227
1666
  description: "Get the DOM structure of the page or an element",
1228
1667
  scope: "page",
1229
1668
  selectorParams: ["selector"],
1230
- parameters: z10.object({
1231
- selector: z10.string().optional(),
1232
- depth: z10.number().optional()
1669
+ parameters: z13.object({
1670
+ selector: z13.string().optional(),
1671
+ depth: z13.number().optional()
1233
1672
  }),
1234
- result: z10.object({
1235
- structure: z10.record(z10.unknown())
1673
+ result: z13.object({
1674
+ structure: z13.record(z13.unknown())
1236
1675
  }),
1237
1676
  handler: async (p, ctx) => {
1238
1677
  const structure = await ctx.page.evaluate(
@@ -1264,30 +1703,30 @@ var structureCommand = registerCommand({
1264
1703
  },
1265
1704
  { sel: p.selector || "body", maxDepth: p.depth || 5 }
1266
1705
  );
1267
- return ok10({ structure });
1706
+ return ok13({ structure });
1268
1707
  }
1269
1708
  });
1270
1709
 
1271
1710
  // src/commands/viewport.ts
1272
- import { z as z11 } from "zod";
1273
- import { ok as ok11 } from "@dyyz1993/xcli-core";
1711
+ import { z as z14 } from "zod";
1712
+ import { ok as ok14 } from "@dyyz1993/xcli-core";
1274
1713
  var setViewportCommand = registerCommand({
1275
1714
  name: "set-viewport",
1276
1715
  description: "Set the viewport size and properties",
1277
1716
  scope: "browser",
1278
- parameters: z11.object({
1279
- width: z11.coerce.number(),
1280
- height: z11.coerce.number(),
1281
- deviceScaleFactor: z11.coerce.number().optional(),
1282
- isMobile: z11.boolean().optional(),
1283
- hasTouch: z11.boolean().optional()
1717
+ parameters: z14.object({
1718
+ width: z14.coerce.number(),
1719
+ height: z14.coerce.number(),
1720
+ deviceScaleFactor: z14.coerce.number().optional(),
1721
+ isMobile: z14.boolean().optional(),
1722
+ hasTouch: z14.boolean().optional()
1284
1723
  }),
1285
- result: z11.object({
1286
- width: z11.number(),
1287
- height: z11.number(),
1288
- deviceScaleFactor: z11.number().optional(),
1289
- isMobile: z11.boolean().optional(),
1290
- hasTouch: z11.boolean().optional()
1724
+ result: z14.object({
1725
+ width: z14.number(),
1726
+ height: z14.number(),
1727
+ deviceScaleFactor: z14.number().optional(),
1728
+ isMobile: z14.boolean().optional(),
1729
+ hasTouch: z14.boolean().optional()
1291
1730
  }),
1292
1731
  handler: async (p, ctx) => {
1293
1732
  const viewport = ctx.page.viewportSize();
@@ -1300,7 +1739,7 @@ var setViewportCommand = registerCommand({
1300
1739
  ...p.isMobile !== void 0 && { isMobile: p.isMobile },
1301
1740
  ...p.hasTouch !== void 0 && { hasTouch: p.hasTouch }
1302
1741
  });
1303
- return ok11({
1742
+ return ok14({
1304
1743
  width,
1305
1744
  height,
1306
1745
  ...p.deviceScaleFactor !== void 0 && { deviceScaleFactor: p.deviceScaleFactor },
@@ -1311,17 +1750,17 @@ var setViewportCommand = registerCommand({
1311
1750
  });
1312
1751
 
1313
1752
  // src/commands/frame.ts
1314
- import { z as z12 } from "zod";
1315
- import { ok as ok12, fail as fail4 } from "@dyyz1993/xcli-core";
1753
+ import { z as z15 } from "zod";
1754
+ import { ok as ok15, fail as fail6 } from "@dyyz1993/xcli-core";
1316
1755
  var framesCommand = registerCommand({
1317
1756
  name: "frames",
1318
1757
  description: "List all frames in the current page",
1319
1758
  scope: "page",
1320
- result: z12.object({
1321
- frames: z12.array(z12.object({
1322
- index: z12.number(),
1323
- name: z12.string().nullable(),
1324
- url: z12.string()
1759
+ result: z15.object({
1760
+ frames: z15.array(z15.object({
1761
+ index: z15.number(),
1762
+ name: z15.string().nullable(),
1763
+ url: z15.string()
1325
1764
  }))
1326
1765
  }),
1327
1766
  handler: async (_p, ctx) => {
@@ -1332,21 +1771,21 @@ var framesCommand = registerCommand({
1332
1771
  name: frame.name(),
1333
1772
  url: frame.url()
1334
1773
  }));
1335
- return ok12({ frames: frameList });
1774
+ return ok15({ frames: frameList });
1336
1775
  }
1337
1776
  });
1338
1777
  var frameCommand = registerCommand({
1339
1778
  name: "frame",
1340
1779
  description: "Get frame info by index or name",
1341
1780
  scope: "page",
1342
- parameters: z12.object({
1343
- index: z12.number().int().min(0).optional(),
1344
- name: z12.string().optional()
1781
+ parameters: z15.object({
1782
+ index: z15.number().int().min(0).optional(),
1783
+ name: z15.string().optional()
1345
1784
  }),
1346
- result: z12.object({
1347
- name: z12.string().nullable(),
1348
- url: z12.string(),
1349
- error: z12.string().optional()
1785
+ result: z15.object({
1786
+ name: z15.string().nullable(),
1787
+ url: z15.string(),
1788
+ error: z15.string().optional()
1350
1789
  }),
1351
1790
  handler: async (p, ctx) => {
1352
1791
  const discover = ctx.page.discoverFrames;
@@ -1357,12 +1796,12 @@ var frameCommand = registerCommand({
1357
1796
  } else if (p.name !== void 0) {
1358
1797
  targetFrame = rawFrames.find((f) => f.name() === p.name);
1359
1798
  } else {
1360
- return fail4("Must provide index or name");
1799
+ return fail6("Must provide index or name");
1361
1800
  }
1362
1801
  if (!targetFrame) {
1363
- return fail4("Frame not found");
1802
+ return fail6("Frame not found");
1364
1803
  }
1365
- return ok12({
1804
+ return ok15({
1366
1805
  name: targetFrame.name(),
1367
1806
  url: targetFrame.url()
1368
1807
  });
@@ -1370,27 +1809,27 @@ var frameCommand = registerCommand({
1370
1809
  });
1371
1810
 
1372
1811
  // src/commands/ui-debug.ts
1373
- import { z as z13 } from "zod";
1374
- import { ok as ok13 } from "@dyyz1993/xcli-core";
1812
+ import { z as z16 } from "zod";
1813
+ import { ok as ok16 } from "@dyyz1993/xcli-core";
1375
1814
  var consoleCheckCommand = registerCommand({
1376
1815
  name: "console",
1377
1816
  description: "Collect and analyze browser console messages (errors, warnings, logs)",
1378
1817
  scope: "page",
1379
- parameters: z13.object({
1380
- url: z13.string().optional().describe("URL to navigate first (optional, uses current page if omitted)"),
1381
- duration: z13.number().optional().default(5e3).describe("How long to collect messages (ms)"),
1382
- filter: z13.enum(["all", "error", "warning", "info", "log"]).optional().default("all"),
1383
- includeStackTraces: z13.boolean().optional().default(true)
1818
+ parameters: z16.object({
1819
+ url: z16.string().optional().describe("URL to navigate first (optional, uses current page if omitted)"),
1820
+ duration: z16.number().optional().default(5e3).describe("How long to collect messages (ms)"),
1821
+ filter: z16.enum(["all", "error", "warning", "info", "log"]).optional().default("all"),
1822
+ includeStackTraces: z16.boolean().optional().default(true)
1384
1823
  }),
1385
- result: z13.object({
1386
- url: z13.string(),
1387
- duration: z13.number(),
1388
- total: z13.number(),
1389
- errors: z13.number(),
1390
- warnings: z13.number(),
1391
- messages: z13.array(z13.record(z13.unknown())),
1392
- summary: z13.string(),
1393
- passed: z13.boolean()
1824
+ result: z16.object({
1825
+ url: z16.string(),
1826
+ duration: z16.number(),
1827
+ total: z16.number(),
1828
+ errors: z16.number(),
1829
+ warnings: z16.number(),
1830
+ messages: z16.array(z16.record(z16.unknown())),
1831
+ summary: z16.string(),
1832
+ passed: z16.boolean()
1394
1833
  }),
1395
1834
  handler: async (p, ctx) => {
1396
1835
  const { page } = ctx;
@@ -1472,7 +1911,7 @@ ${a.stack || ""}`;
1472
1911
  const filtered = p.filter === "all" ? messages : messages.filter((m) => m.type === p.filter);
1473
1912
  const errorCount = messages.filter((m) => m.type === "error").length;
1474
1913
  const warnCount = messages.filter((m) => m.type === "warning").length;
1475
- return ok13({
1914
+ return ok16({
1476
1915
  url: page.url(),
1477
1916
  duration: p.duration,
1478
1917
  total: messages.length,
@@ -1488,23 +1927,23 @@ var networkCheckCommand = registerCommand({
1488
1927
  name: "net-debug",
1489
1928
  description: "Monitor and analyze network requests \u2014 capture failed requests, slow responses, status codes",
1490
1929
  scope: "page",
1491
- parameters: z13.object({
1492
- url: z13.string().optional().describe("URL to navigate first"),
1493
- duration: z13.number().optional().default(5e3).describe("How long to monitor (ms)"),
1494
- filter: z13.enum(["all", "failed", "slow", "error", "xhr", "fetch", "document", "stylesheet", "script", "image"]).optional().default("all"),
1495
- slowThreshold: z13.number().optional().default(3e3).describe('Threshold for "slow" requests (ms)')
1930
+ parameters: z16.object({
1931
+ url: z16.string().optional().describe("URL to navigate first"),
1932
+ duration: z16.number().optional().default(5e3).describe("How long to monitor (ms)"),
1933
+ filter: z16.enum(["all", "failed", "slow", "error", "xhr", "fetch", "document", "stylesheet", "script", "image"]).optional().default("all"),
1934
+ slowThreshold: z16.number().optional().default(3e3).describe('Threshold for "slow" requests (ms)')
1496
1935
  }),
1497
- result: z13.object({
1498
- url: z13.string(),
1499
- duration: z13.number(),
1500
- totalRequests: z13.number(),
1501
- failedRequests: z13.number(),
1502
- slowRequests: z13.number(),
1503
- errorRequests: z13.number(),
1504
- totalSizeKB: z13.number(),
1505
- requests: z13.array(z13.record(z13.unknown())),
1506
- summary: z13.string(),
1507
- passed: z13.boolean()
1936
+ result: z16.object({
1937
+ url: z16.string(),
1938
+ duration: z16.number(),
1939
+ totalRequests: z16.number(),
1940
+ failedRequests: z16.number(),
1941
+ slowRequests: z16.number(),
1942
+ errorRequests: z16.number(),
1943
+ totalSizeKB: z16.number(),
1944
+ requests: z16.array(z16.record(z16.unknown())),
1945
+ summary: z16.string(),
1946
+ passed: z16.boolean()
1508
1947
  }),
1509
1948
  handler: async (p, ctx) => {
1510
1949
  const { page } = ctx;
@@ -1586,7 +2025,7 @@ var networkCheckCommand = registerCommand({
1586
2025
  const slowCount = requests.filter((r) => r.duration >= p.slowThreshold).length;
1587
2026
  const errorCount = requests.filter((r) => r.error).length;
1588
2027
  const totalSize = requests.reduce((sum, r) => sum + r.size, 0);
1589
- return ok13({
2028
+ return ok16({
1590
2029
  url: page.url(),
1591
2030
  duration: p.duration,
1592
2031
  totalRequests: requests.length,
@@ -1608,17 +2047,17 @@ var perfCheckCommand = registerCommand({
1608
2047
  name: "perf",
1609
2048
  description: "Audit page performance metrics \u2014 load time, FCP, LCP, CLS, TTFB, resource sizes",
1610
2049
  scope: "page",
1611
- parameters: z13.object({
1612
- url: z13.string().optional().describe("URL to navigate (uses current page if omitted)"),
1613
- iterations: z13.number().optional().default(1).describe("Number of iterations to average")
2050
+ parameters: z16.object({
2051
+ url: z16.string().optional().describe("URL to navigate (uses current page if omitted)"),
2052
+ iterations: z16.number().optional().default(1).describe("Number of iterations to average")
1614
2053
  }),
1615
- result: z13.object({
1616
- url: z13.string(),
1617
- iterations: z13.number(),
1618
- metrics: z13.record(z13.unknown()),
1619
- allIterations: z13.array(z13.record(z13.unknown())).optional(),
1620
- passed: z13.boolean(),
1621
- summary: z13.string()
2054
+ result: z16.object({
2055
+ url: z16.string(),
2056
+ iterations: z16.number(),
2057
+ metrics: z16.record(z16.unknown()),
2058
+ allIterations: z16.array(z16.record(z16.unknown())).optional(),
2059
+ passed: z16.boolean(),
2060
+ summary: z16.string()
1622
2061
  }),
1623
2062
  handler: async (p, ctx) => {
1624
2063
  const { page } = ctx;
@@ -1680,7 +2119,7 @@ var perfCheckCommand = registerCommand({
1680
2119
  if (resourceStats) avg.resourceStats = resourceStats;
1681
2120
  return avg;
1682
2121
  })();
1683
- return ok13({
2122
+ return ok16({
1684
2123
  url: page.url(),
1685
2124
  iterations: p.iterations,
1686
2125
  metrics: avgMetrics,
@@ -1694,23 +2133,23 @@ var healthCheckCommand = registerCommand({
1694
2133
  name: "health",
1695
2134
  description: "Comprehensive page health check \u2014 broken links, missing images, console errors, SEO issues",
1696
2135
  scope: "page",
1697
- parameters: z13.object({
1698
- url: z13.string().optional().describe("URL to check"),
1699
- checkLinks: z13.boolean().optional().default(true).describe("Check for broken links"),
1700
- checkImages: z13.boolean().optional().default(true).describe("Check for missing/broken images"),
1701
- checkMeta: z13.boolean().optional().default(true).describe("Check SEO meta tags"),
1702
- maxLinks: z13.number().optional().default(50).describe("Max links to check")
2136
+ parameters: z16.object({
2137
+ url: z16.string().optional().describe("URL to check"),
2138
+ checkLinks: z16.boolean().optional().default(true).describe("Check for broken links"),
2139
+ checkImages: z16.boolean().optional().default(true).describe("Check for missing/broken images"),
2140
+ checkMeta: z16.boolean().optional().default(true).describe("Check SEO meta tags"),
2141
+ maxLinks: z16.number().optional().default(50).describe("Max links to check")
1703
2142
  }),
1704
- result: z13.object({
1705
- url: z13.string(),
1706
- title: z13.string(),
1707
- passed: z13.boolean(),
1708
- totalIssues: z13.number(),
1709
- errors: z13.number(),
1710
- warnings: z13.number(),
1711
- info: z13.number(),
1712
- issues: z13.array(z13.record(z13.unknown())),
1713
- summary: z13.string()
2143
+ result: z16.object({
2144
+ url: z16.string(),
2145
+ title: z16.string(),
2146
+ passed: z16.boolean(),
2147
+ totalIssues: z16.number(),
2148
+ errors: z16.number(),
2149
+ warnings: z16.number(),
2150
+ info: z16.number(),
2151
+ issues: z16.array(z16.record(z16.unknown())),
2152
+ summary: z16.string()
1714
2153
  }),
1715
2154
  handler: async (p, ctx) => {
1716
2155
  const { page } = ctx;
@@ -1797,7 +2236,7 @@ var healthCheckCommand = registerCommand({
1797
2236
  }, { checkLinks: p.checkLinks, checkImages: p.checkImages, checkMeta: p.checkMeta, maxLinks: p.maxLinks });
1798
2237
  const errors = result.issues.filter((i) => i.severity === "error").length;
1799
2238
  const warnings = result.issues.filter((i) => i.severity === "warning").length;
1800
- return ok13({
2239
+ return ok16({
1801
2240
  url: result.url,
1802
2241
  title: result.title,
1803
2242
  passed: errors === 0,
@@ -1812,68 +2251,66 @@ var healthCheckCommand = registerCommand({
1812
2251
  });
1813
2252
 
1814
2253
  // src/commands/actions.ts
1815
- import { z as z14 } from "zod";
1816
- import { ok as ok14 } from "@dyyz1993/xcli-core";
2254
+ import { z as z17 } from "zod";
2255
+ import { ok as ok17 } from "@dyyz1993/xcli-core";
1817
2256
  import { writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
1818
- import { join as join3 } from "path";
1819
- import { homedir as homedir3 } from "os";
1820
- var SCREENSHOTS_DIR2 = join3(homedir3(), ".xbrowser", "screenshots");
2257
+ import { join as join5 } from "path";
1821
2258
  function ensureScreenshotsDir2() {
1822
- mkdirSync2(SCREENSHOTS_DIR2, { recursive: true });
2259
+ mkdirSync2(resolveScreenshotsDir(), { recursive: true });
1823
2260
  }
1824
2261
  function generateScreenshotPath2(format) {
1825
2262
  const timestamp = Date.now();
1826
2263
  const random = Math.random().toString(36).slice(2, 8);
1827
2264
  const ext = format === "jpeg" ? "jpg" : "png";
1828
- return join3(SCREENSHOTS_DIR2, `screenshot-${timestamp}-${random}.${ext}`);
2265
+ return join5(resolveScreenshotsDir(), `screenshot-${timestamp}-${random}.${ext}`);
1829
2266
  }
1830
- var waitActionSchema = z14.object({
1831
- type: z14.literal("wait"),
1832
- milliseconds: z14.number().positive().optional(),
1833
- selector: z14.string().optional()
2267
+ var waitActionSchema = z17.object({
2268
+ type: z17.literal("wait"),
2269
+ milliseconds: z17.number().positive().optional(),
2270
+ selector: z17.string().optional()
1834
2271
  }).refine(
1835
2272
  (d) => (d.milliseconds !== void 0 || d.selector !== void 0) && !(d.milliseconds !== void 0 && d.selector !== void 0),
1836
2273
  { message: "Either 'milliseconds' or 'selector' must be provided, but not both." }
1837
2274
  );
1838
- var clickActionSchema = z14.object({
1839
- type: z14.literal("click"),
1840
- selector: z14.string(),
1841
- all: z14.boolean().optional()
2275
+ var clickActionSchema = z17.object({
2276
+ type: z17.literal("click"),
2277
+ selector: z17.string(),
2278
+ all: z17.boolean().optional()
1842
2279
  });
1843
- var screenshotActionSchema = z14.object({
1844
- type: z14.literal("screenshot"),
1845
- fullPage: z14.boolean().optional(),
1846
- quality: z14.number().min(1).max(100).optional(),
1847
- viewport: z14.object({ width: z14.number().int().positive(), height: z14.number().int().positive() }).optional(),
1848
- base64: z14.boolean().optional().describe("Return base64 data instead of file path")
2280
+ var screenshotActionSchema = z17.object({
2281
+ type: z17.literal("screenshot"),
2282
+ fullPage: z17.boolean().optional(),
2283
+ quality: z17.number().min(1).max(100).optional(),
2284
+ viewport: z17.object({ width: z17.number().int().positive(), height: z17.number().int().positive() }).optional(),
2285
+ base64: z17.boolean().optional().describe("Return base64 data instead of file path")
1849
2286
  });
1850
- var writeActionSchema = z14.object({
1851
- type: z14.literal("write"),
1852
- text: z14.string()
2287
+ var writeActionSchema = z17.object({
2288
+ type: z17.literal("write"),
2289
+ text: z17.string()
1853
2290
  });
1854
- var pressActionSchema = z14.object({
1855
- type: z14.literal("press"),
1856
- key: z14.string()
2291
+ var pressActionSchema = z17.object({
2292
+ type: z17.literal("press"),
2293
+ key: z17.string()
1857
2294
  });
1858
- var scrollActionSchema = z14.object({
1859
- type: z14.literal("scroll"),
1860
- direction: z14.enum(["up", "down"]).optional(),
1861
- selector: z14.string().optional()
2295
+ var scrollActionSchema = z17.object({
2296
+ type: z17.literal("scroll"),
2297
+ direction: z17.enum(["up", "down"]).optional(),
2298
+ selector: z17.string().optional()
1862
2299
  });
1863
- var scrapeActionSchema = z14.object({
1864
- type: z14.literal("scrape")
2300
+ var scrapeActionSchema = z17.object({
2301
+ type: z17.literal("scrape")
1865
2302
  });
1866
- var executeJavascriptActionSchema = z14.object({
1867
- type: z14.literal("executeJavascript"),
1868
- script: z14.string()
2303
+ var executeJavascriptActionSchema = z17.object({
2304
+ type: z17.literal("executeJavascript"),
2305
+ script: z17.string()
1869
2306
  });
1870
- var pdfActionSchema = z14.object({
1871
- type: z14.literal("pdf"),
1872
- landscape: z14.boolean().optional(),
1873
- scale: z14.number().optional(),
1874
- format: z14.enum(["A0", "A1", "A2", "A3", "A4", "A5", "A6", "Letter", "Legal", "Tabloid", "Ledger"]).optional()
2307
+ var pdfActionSchema = z17.object({
2308
+ type: z17.literal("pdf"),
2309
+ landscape: z17.boolean().optional(),
2310
+ scale: z17.number().optional(),
2311
+ format: z17.enum(["A0", "A1", "A2", "A3", "A4", "A5", "A6", "Letter", "Legal", "Tabloid", "Ledger"]).optional()
1875
2312
  });
1876
- var actionSchema = z14.union([
2313
+ var actionSchema = z17.union([
1877
2314
  waitActionSchema,
1878
2315
  clickActionSchema,
1879
2316
  screenshotActionSchema,
@@ -1964,11 +2401,11 @@ var actionsCommand = registerCommand({
1964
2401
  name: "actions",
1965
2402
  description: "Execute a sequence of actions (wait, click, scroll, screenshot, fill, etc.)",
1966
2403
  scope: "page",
1967
- parameters: z14.object({
1968
- url: z14.string().describe("Starting URL"),
1969
- actions: z14.array(actionSchema).max(MAX_ACTIONS).describe("Array of actions (max 50)"),
1970
- output: z14.enum(["text", "json"]).default("json").describe("Output format: text or json"),
1971
- timeout: z14.number().default(60).describe("Overall timeout in seconds (default: 60)")
2404
+ parameters: z17.object({
2405
+ url: z17.string().describe("Starting URL"),
2406
+ actions: z17.array(actionSchema).max(MAX_ACTIONS).describe("Array of actions (max 50)"),
2407
+ output: z17.enum(["text", "json"]).default("json").describe("Output format: text or json"),
2408
+ timeout: z17.number().default(60).describe("Overall timeout in seconds (default: 60)")
1972
2409
  }),
1973
2410
  handler: async (p, ctx) => {
1974
2411
  await ctx.page.goto(p.url, { waitUntil: "domcontentloaded" });
@@ -1988,14 +2425,14 @@ var actionsCommand = registerCommand({
1988
2425
  const finalUrl = ctx.page.url();
1989
2426
  const timedOut = results.length < p.actions.length;
1990
2427
  if (p.output === "text") {
1991
- return ok14({
2428
+ return ok17({
1992
2429
  title,
1993
2430
  url: finalUrl,
1994
2431
  actions: results.map((r) => JSON.stringify(r)).join("\n"),
1995
2432
  ...timedOut ? { warning: `Timed out after ${p.timeout}s, completed ${results.length}/${p.actions.length} actions` } : {}
1996
2433
  });
1997
2434
  }
1998
- return ok14({
2435
+ return ok17({
1999
2436
  title,
2000
2437
  url: finalUrl,
2001
2438
  results,
@@ -2005,8 +2442,8 @@ var actionsCommand = registerCommand({
2005
2442
  });
2006
2443
 
2007
2444
  // src/commands/scrape.ts
2008
- import { z as z15 } from "zod";
2009
- import { ok as ok15, fail as fail5 } from "@dyyz1993/xcli-core";
2445
+ import { z as z18 } from "zod";
2446
+ import { ok as ok18, fail as fail7 } from "@dyyz1993/xcli-core";
2010
2447
 
2011
2448
  // src/lib/html-to-markdown.ts
2012
2449
  import * as cheerio from "cheerio";
@@ -2391,15 +2828,15 @@ var scrapeCommand = registerCommand({
2391
2828
  description: "Scrape a page and convert to Markdown (with JS rendering)",
2392
2829
  scope: "project",
2393
2830
  selectorParams: ["selector"],
2394
- parameters: z15.object({
2395
- url: z15.string().optional(),
2396
- selector: z15.string().optional(),
2397
- timeout: z15.number().default(3e4),
2398
- format: z15.enum(["markdown", "html", "text"]).default("markdown"),
2399
- onlyMainContent: z15.boolean().default(true),
2400
- retries: z15.number().int().min(0).max(5).optional().default(2).describe("\u91CD\u8BD5\u6B21\u6570\uFF08\u9ED8\u8BA4 2\uFF09"),
2401
- waitAfterLoad: z15.number().int().optional().default(0).describe("\u9875\u9762\u52A0\u8F7D\u540E\u989D\u5916\u7B49\u5F85\u6BEB\u79D2"),
2402
- 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")
2831
+ parameters: z18.object({
2832
+ url: z18.string().optional(),
2833
+ selector: z18.string().optional(),
2834
+ timeout: z18.number().default(3e4),
2835
+ format: z18.enum(["markdown", "html", "text"]).default("markdown"),
2836
+ onlyMainContent: z18.boolean().default(true),
2837
+ retries: z18.number().int().min(0).max(5).optional().default(2).describe("\u91CD\u8BD5\u6B21\u6570\uFF08\u9ED8\u8BA4 2\uFF09"),
2838
+ waitAfterLoad: z18.number().int().optional().default(0).describe("\u9875\u9762\u52A0\u8F7D\u540E\u989D\u5916\u7B49\u5F85\u6BEB\u79D2"),
2839
+ 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")
2403
2840
  }),
2404
2841
  handler: async (p, ctx) => {
2405
2842
  const { context, page } = await createEphemeralContext(resolveLaunchOpts(ctx));
@@ -2407,7 +2844,7 @@ var scrapeCommand = registerCommand({
2407
2844
  try {
2408
2845
  const targetUrl = p.url || page.url();
2409
2846
  if (!targetUrl || targetUrl === "about:blank") {
2410
- return fail5("URL is required. Provide a URL or connect to a browser (--cdp) with a non-blank page.");
2847
+ return fail7("URL is required. Provide a URL or connect to a browser (--cdp) with a non-blank page.");
2411
2848
  }
2412
2849
  let lastError;
2413
2850
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
@@ -2477,7 +2914,7 @@ var scrapeCommand = registerCommand({
2477
2914
  } catch {
2478
2915
  }
2479
2916
  if (p.mode === "smart") {
2480
- return ok15(structured);
2917
+ return ok18(structured);
2481
2918
  }
2482
2919
  if (p.mode === "compact") {
2483
2920
  const compactData = structured.tables.length > 0 ? structured.tables.map((t) => ({
@@ -2490,9 +2927,9 @@ var scrapeCommand = registerCommand({
2490
2927
  return compact;
2491
2928
  })
2492
2929
  })) : structured.mainText?.substring(0, 500);
2493
- return ok15({ url: structured.url, title: structured.title, data: compactData });
2930
+ return ok18({ url: structured.url, title: structured.title, data: compactData });
2494
2931
  }
2495
- return ok15(structured);
2932
+ return ok18(structured);
2496
2933
  }
2497
2934
  let content;
2498
2935
  switch (p.format) {
@@ -2551,7 +2988,7 @@ var scrapeCommand = registerCommand({
2551
2988
  content = await page.innerText("body");
2552
2989
  break;
2553
2990
  }
2554
- return ok15({ content, title, url: finalUrl });
2991
+ return ok18({ content, title, url: finalUrl });
2555
2992
  } catch (err) {
2556
2993
  lastError = err instanceof Error ? err : new Error(String(err));
2557
2994
  if (attempt < maxAttempts) {
@@ -2561,20 +2998,20 @@ var scrapeCommand = registerCommand({
2561
2998
  }
2562
2999
  }
2563
3000
  }
2564
- return fail5(`Scrape failed after ${maxAttempts} attempt(s): ${lastError?.message ?? "unknown error"}`);
3001
+ return fail7(`Scrape failed after ${maxAttempts} attempt(s): ${lastError?.message ?? "unknown error"}`);
2565
3002
  } finally {
2566
3003
  await closeEphemeralContext(context);
2567
3004
  }
2568
3005
  },
2569
- result: z15.object({
2570
- url: z15.string(),
2571
- title: z15.string()
3006
+ result: z18.object({
3007
+ url: z18.string(),
3008
+ title: z18.string()
2572
3009
  }).passthrough()
2573
3010
  });
2574
3011
 
2575
3012
  // src/commands/map.ts
2576
- import { z as z16 } from "zod";
2577
- import { ok as ok16, fail as fail6 } from "@dyyz1993/xcli-core";
3013
+ import { z as z19 } from "zod";
3014
+ import { ok as ok19, fail as fail8 } from "@dyyz1993/xcli-core";
2578
3015
 
2579
3016
  // src/utils/url.ts
2580
3017
  var SKIP_EXTENSIONS = /* @__PURE__ */ new Set([
@@ -2809,21 +3246,21 @@ var mapCommand = registerCommand({
2809
3246
  name: "map",
2810
3247
  description: "Discover all URLs on a website via sitemap and page link extraction",
2811
3248
  scope: "project",
2812
- parameters: z16.object({
2813
- url: z16.string().optional(),
2814
- search: z16.string().optional(),
2815
- sitemap: z16.enum(["include", "only"]).optional(),
2816
- includeSubdomains: z16.boolean().optional(),
2817
- allowExternalLinks: z16.boolean().optional().describe("Include links to external domains"),
2818
- limit: z16.number().optional(),
2819
- verbose: z16.boolean().default(false).describe("Show progress feedback")
3249
+ parameters: z19.object({
3250
+ url: z19.string().optional(),
3251
+ search: z19.string().optional(),
3252
+ sitemap: z19.enum(["include", "only"]).optional(),
3253
+ includeSubdomains: z19.boolean().optional(),
3254
+ allowExternalLinks: z19.boolean().optional().describe("Include links to external domains"),
3255
+ limit: z19.number().optional(),
3256
+ verbose: z19.boolean().default(false).describe("Show progress feedback")
2820
3257
  }),
2821
3258
  handler: async (p, ctx) => {
2822
3259
  const { context, page } = await createEphemeralContext(resolveLaunchOpts(ctx));
2823
3260
  try {
2824
3261
  const targetUrl = p.url || page.url();
2825
3262
  if (!targetUrl || targetUrl === "about:blank") {
2826
- return fail6("URL is required. Provide a URL or connect to a browser (--cdp) with a non-blank page.");
3263
+ return fail8("URL is required. Provide a URL or connect to a browser (--cdp) with a non-blank page.");
2827
3264
  }
2828
3265
  const links = await discoverUrls(page, targetUrl, {
2829
3266
  sitemap: p.sitemap,
@@ -2834,7 +3271,7 @@ var mapCommand = registerCommand({
2834
3271
  verbose: p.verbose
2835
3272
  });
2836
3273
  const linkObjects = links.map((url) => ({ url }));
2837
- return ok16({
3274
+ return ok19({
2838
3275
  links: linkObjects,
2839
3276
  success: true
2840
3277
  });
@@ -2842,15 +3279,15 @@ var mapCommand = registerCommand({
2842
3279
  await closeEphemeralContext(context);
2843
3280
  }
2844
3281
  },
2845
- result: z16.object({
2846
- links: z16.array(z16.object({ url: z16.string() })),
2847
- success: z16.boolean()
3282
+ result: z19.object({
3283
+ links: z19.array(z19.object({ url: z19.string() })),
3284
+ success: z19.boolean()
2848
3285
  })
2849
3286
  });
2850
3287
 
2851
3288
  // src/commands/crawl.ts
2852
- import { z as z17 } from "zod";
2853
- import { ok as ok17 } from "@dyyz1993/xcli-core";
3289
+ import { z as z20 } from "zod";
3290
+ import { ok as ok20 } from "@dyyz1993/xcli-core";
2854
3291
  function stripHashAnchorQuery(url) {
2855
3292
  try {
2856
3293
  const parsed = new URL(url);
@@ -3027,21 +3464,21 @@ var crawlCommand = registerCommand({
3027
3464
  name: "crawl",
3028
3465
  description: "Crawl a website and extract content from all pages",
3029
3466
  scope: "project",
3030
- parameters: z17.object({
3031
- url: z17.string(),
3032
- limit: z17.number().default(10),
3033
- maxDepth: z17.number().default(3),
3034
- includePaths: z17.array(z17.string()).optional(),
3035
- excludePaths: z17.array(z17.string()).optional(),
3036
- allowSubdomains: z17.boolean().default(false),
3037
- allowExternalLinks: z17.boolean().default(false),
3038
- allowBackwardCrawling: z17.boolean().default(false),
3039
- enableSpa: z17.boolean().default(true).describe("Disable to skip SPA route detection"),
3040
- format: z17.enum(["markdown", "html"]).default("markdown"),
3041
- onlyMainContent: z17.boolean().default(true),
3042
- concurrency: z17.number().default(3),
3043
- retries: z17.number().default(2),
3044
- verbose: z17.boolean().default(false)
3467
+ parameters: z20.object({
3468
+ url: z20.string(),
3469
+ limit: z20.number().default(10),
3470
+ maxDepth: z20.number().default(3),
3471
+ includePaths: z20.array(z20.string()).optional(),
3472
+ excludePaths: z20.array(z20.string()).optional(),
3473
+ allowSubdomains: z20.boolean().default(false),
3474
+ allowExternalLinks: z20.boolean().default(false),
3475
+ allowBackwardCrawling: z20.boolean().default(false),
3476
+ enableSpa: z20.boolean().default(true).describe("Disable to skip SPA route detection"),
3477
+ format: z20.enum(["markdown", "html"]).default("markdown"),
3478
+ onlyMainContent: z20.boolean().default(true),
3479
+ concurrency: z20.number().default(3),
3480
+ retries: z20.number().default(2),
3481
+ verbose: z20.boolean().default(false)
3045
3482
  }),
3046
3483
  handler: async (p, ctx) => {
3047
3484
  const startUrl = new URL(p.url);
@@ -3180,7 +3617,7 @@ var crawlCommand = registerCommand({
3180
3617
  if (errorPages.length > 0) {
3181
3618
  response.errors = errorPages;
3182
3619
  }
3183
- return ok17(response);
3620
+ return ok20(response);
3184
3621
  } finally {
3185
3622
  for (const ctx2 of contexts) {
3186
3623
  await ctx2.close().catch(() => {
@@ -3190,15 +3627,15 @@ var crawlCommand = registerCommand({
3190
3627
  } catch (err) {
3191
3628
  const message = err instanceof Error ? err.message : String(err);
3192
3629
  const successPages = results.filter((r) => !isPageError(r));
3193
- return ok17({ pages: successPages, total: successPages.length, success: false, error: message });
3630
+ return ok20({ pages: successPages, total: successPages.length, success: false, error: message });
3194
3631
  }
3195
3632
  }
3196
3633
  });
3197
3634
 
3198
3635
  // src/commands/search.ts
3199
- import { z as z18 } from "zod";
3636
+ import { z as z21 } from "zod";
3200
3637
  import * as cheerio2 from "cheerio";
3201
- import { ok as ok18 } from "@dyyz1993/xcli-core";
3638
+ import { ok as ok21 } from "@dyyz1993/xcli-core";
3202
3639
  function getRecencyParams(recency) {
3203
3640
  const now = Math.floor(Date.now() / 1e3);
3204
3641
  switch (recency) {
@@ -3532,16 +3969,16 @@ var searchCommand = registerCommand({
3532
3969
  name: "search",
3533
3970
  description: "Search the web and extract results with engine fallback",
3534
3971
  scope: "project",
3535
- parameters: z18.object({
3536
- query: z18.string(),
3537
- engine: z18.string().optional(),
3538
- limit: z18.number().default(10),
3539
- full: z18.boolean().default(false),
3540
- format: z18.enum(["markdown", "json", "text"]).default("markdown"),
3541
- timeout: z18.number().default(15e3),
3542
- recency: z18.enum(["hour", "day", "week", "month", "year"]).optional().describe("Filter by time: hour/day/week/month/year"),
3543
- fallback: z18.boolean().default(false).describe("Sequential engine fallback instead of parallel"),
3544
- site: z18.string().optional().describe("Limit results to a specific site (e.g. github.com, v2ex.com)")
3972
+ parameters: z21.object({
3973
+ query: z21.string(),
3974
+ engine: z21.string().optional(),
3975
+ limit: z21.number().default(10),
3976
+ full: z21.boolean().default(false),
3977
+ format: z21.enum(["markdown", "json", "text"]).default("markdown"),
3978
+ timeout: z21.number().default(15e3),
3979
+ recency: z21.enum(["hour", "day", "week", "month", "year"]).optional().describe("Filter by time: hour/day/week/month/year"),
3980
+ fallback: z21.boolean().default(false).describe("Sequential engine fallback instead of parallel"),
3981
+ site: z21.string().optional().describe("Limit results to a specific site (e.g. github.com, v2ex.com)")
3545
3982
  }),
3546
3983
  handler: async (p, ctx) => {
3547
3984
  const { context } = await createEphemeralContext(resolveLaunchOpts(ctx));
@@ -3647,7 +4084,7 @@ ${errors.map((e) => ` - ${e.engine}: ${e.error}`).join("\n")}`
3647
4084
  lines.push(`> ${r.snippet}`);
3648
4085
  lines.push("");
3649
4086
  }
3650
- return ok18({ ...searchResult, content: lines.join("\n") });
4087
+ return ok21({ ...searchResult, content: lines.join("\n") });
3651
4088
  }
3652
4089
  if (p.format === "text") {
3653
4090
  const lines = [`Search: ${searchResult.query} (Engine: ${searchResult.engine}, Total: ${searchResult.total})`, ""];
@@ -3657,9 +4094,9 @@ ${errors.map((e) => ` - ${e.engine}: ${e.error}`).join("\n")}`
3657
4094
  lines.push(` ${r.snippet}`);
3658
4095
  lines.push("");
3659
4096
  }
3660
- return ok18({ ...searchResult, content: lines.join("\n") });
4097
+ return ok21({ ...searchResult, content: lines.join("\n") });
3661
4098
  }
3662
- return ok18(searchResult);
4099
+ return ok21(searchResult);
3663
4100
  } finally {
3664
4101
  await closeEphemeralContext(context);
3665
4102
  }
@@ -3667,8 +4104,8 @@ ${errors.map((e) => ` - ${e.engine}: ${e.error}`).join("\n")}`
3667
4104
  });
3668
4105
 
3669
4106
  // src/commands/network.ts
3670
- import { z as z19 } from "zod";
3671
- import { ok as ok19, fail as fail7 } from "@dyyz1993/xcli-core";
4107
+ import { z as z22 } from "zod";
4108
+ import { ok as ok22, fail as fail9 } from "@dyyz1993/xcli-core";
3672
4109
  function extractPath2(url) {
3673
4110
  try {
3674
4111
  const u = new URL(url);
@@ -3766,19 +4203,19 @@ var networkCommand = registerCommand({
3766
4203
  name: "network",
3767
4204
  description: "Capture and filter network responses from a URL",
3768
4205
  scope: "project",
3769
- parameters: z19.object({
3770
- url: z19.string(),
3771
- filter: z19.string().optional(),
3772
- match: z19.string().optional(),
3773
- search: z19.string().optional().describe("Search within all captured response bodies"),
3774
- console: z19.boolean().default(false),
3775
- timeout: z19.number().default(3e4),
3776
- wait: z19.number().default(3e3),
3777
- limit: z19.number().default(50),
3778
- format: z19.enum(["summary", "json"]).default("summary"),
3779
- ws: z19.boolean().optional().default(false).describe("Only show WebSocket messages"),
3780
- 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"),
3781
- duration: z19.number().int().optional().default(15e3).describe("\u76D1\u542C\u6A21\u5F0F\u7B49\u5F85\u65F6\u957F\uFF08\u6BEB\u79D2\uFF0C\u9ED8\u8BA4 15000\uFF09")
4206
+ parameters: z22.object({
4207
+ url: z22.string(),
4208
+ filter: z22.string().optional(),
4209
+ match: z22.string().optional(),
4210
+ search: z22.string().optional().describe("Search within all captured response bodies"),
4211
+ console: z22.boolean().default(false),
4212
+ timeout: z22.number().default(3e4),
4213
+ wait: z22.number().default(3e3),
4214
+ limit: z22.number().default(50),
4215
+ format: z22.enum(["summary", "json"]).default("summary"),
4216
+ ws: z22.boolean().optional().default(false).describe("Only show WebSocket messages"),
4217
+ 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"),
4218
+ duration: z22.number().int().optional().default(15e3).describe("\u76D1\u542C\u6A21\u5F0F\u7B49\u5F85\u65F6\u957F\uFF08\u6BEB\u79D2\uFF0C\u9ED8\u8BA4 15000\uFF09")
3782
4219
  }),
3783
4220
  handler: async (p, ctx) => {
3784
4221
  const startTime = Date.now();
@@ -3833,7 +4270,7 @@ var networkCommand = registerCommand({
3833
4270
  };
3834
4271
  if (p.listen) {
3835
4272
  const page2 = ctx.page;
3836
- if (!page2) return fail7("No active page. Use --cdp to connect first.");
4273
+ if (!page2) return fail9("No active page. Use --cdp to connect first.");
3837
4274
  const captures = [];
3838
4275
  const consoleMessages = [];
3839
4276
  const wsCaptures = [];
@@ -3860,7 +4297,7 @@ var networkCommand = registerCommand({
3860
4297
  page2.off("response", handler);
3861
4298
  const duration = Date.now() - startTime;
3862
4299
  if (p.ws && wsCaptures.length > 0) {
3863
- return ok19({
4300
+ return ok22({
3864
4301
  url: "[listen mode]",
3865
4302
  duration,
3866
4303
  total: captures.length,
@@ -3874,9 +4311,9 @@ var networkCommand = registerCommand({
3874
4311
  });
3875
4312
  }
3876
4313
  if (p.format === "json") {
3877
- return ok19(buildJsonOutput("[listen mode]", captures, consoleMessages, captures.length, wsCaptures));
4314
+ return ok22(buildJsonOutput("[listen mode]", captures, consoleMessages, captures.length, wsCaptures));
3878
4315
  }
3879
- return ok19(buildSummaryOutput("[listen mode]", duration, captures, consoleMessages, captures.length, wsCaptures));
4316
+ return ok22(buildSummaryOutput("[listen mode]", duration, captures, consoleMessages, captures.length, wsCaptures));
3880
4317
  }
3881
4318
  const { context, page } = await createEphemeralContext(resolveLaunchOpts(ctx));
3882
4319
  try {
@@ -3931,7 +4368,7 @@ var networkCommand = registerCommand({
3931
4368
  }
3932
4369
  const duration = Date.now() - startTime;
3933
4370
  if (p.ws && wsCaptures.length > 0) {
3934
- return ok19({
4371
+ return ok22({
3935
4372
  url: p.url,
3936
4373
  duration,
3937
4374
  total: captures.length,
@@ -3945,9 +4382,9 @@ var networkCommand = registerCommand({
3945
4382
  });
3946
4383
  }
3947
4384
  if (p.format === "json") {
3948
- return ok19(buildJsonOutput(p.url, results, consoleMessages, totalCount, wsCaptures, searchResults));
4385
+ return ok22(buildJsonOutput(p.url, results, consoleMessages, totalCount, wsCaptures, searchResults));
3949
4386
  }
3950
- return ok19(buildSummaryOutput(p.url, duration, results, consoleMessages, totalCount, wsCaptures));
4387
+ return ok22(buildSummaryOutput(p.url, duration, results, consoleMessages, totalCount, wsCaptures));
3951
4388
  } finally {
3952
4389
  await closeEphemeralContext(context);
3953
4390
  }
@@ -3955,7 +4392,7 @@ var networkCommand = registerCommand({
3955
4392
  });
3956
4393
 
3957
4394
  // src/commands/ai-search-engines.ts
3958
- import { z as z20 } from "zod";
4395
+ import { z as z23 } from "zod";
3959
4396
  var ENGINE_CONFIGS = {
3960
4397
  deepseek: {
3961
4398
  key: "deepseek",
@@ -4209,11 +4646,11 @@ var ENGINE_CONFIGS = {
4209
4646
  }
4210
4647
  };
4211
4648
  var ALL_ENGINE_KEYS = Object.keys(ENGINE_CONFIGS);
4212
- var ENGINE_KEY_ENUM = z20.enum(ALL_ENGINE_KEYS);
4649
+ var ENGINE_KEY_ENUM = z23.enum(ALL_ENGINE_KEYS);
4213
4650
 
4214
4651
  // src/commands/snapshot.ts
4215
- import { z as z21 } from "zod";
4216
- import { ok as ok20, fail as fail8, normalizeTips as normalizeTips3 } from "@dyyz1993/xcli-core";
4652
+ import { z as z24 } from "zod";
4653
+ import { ok as ok23, fail as fail10, normalizeTips as normalizeTips3 } from "@dyyz1993/xcli-core";
4217
4654
 
4218
4655
  // src/runtime/ref-store.ts
4219
4656
  var sessions = /* @__PURE__ */ new Map();
@@ -4454,8 +4891,8 @@ async function resolveRefParams(page, params, selectorKeys, cache, sessionId) {
4454
4891
  }
4455
4892
 
4456
4893
  // src/utils/site-semantics.ts
4457
- import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync, readFileSync } from "fs";
4458
- import { join as join4, dirname as dirname2 } from "path";
4894
+ import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
4895
+ import { join as join6, dirname as dirname2 } from "path";
4459
4896
  import { homedir as homedir4 } from "os";
4460
4897
  import { stringify, parse } from "yaml";
4461
4898
  import { execFile } from "child_process";
@@ -4533,10 +4970,10 @@ function inferAction(role, label) {
4533
4970
  return {};
4534
4971
  }
4535
4972
  function getSemanticsDir() {
4536
- return join4(homedir4(), ".xbrowser", "site-semantics");
4973
+ return join6(homedir4(), ".xbrowser", "site-semantics");
4537
4974
  }
4538
4975
  function getSemanticsPath(domain) {
4539
- return join4(getSemanticsDir(), `${domain}.yaml`);
4976
+ return join6(getSemanticsDir(), `${domain}.yaml`);
4540
4977
  }
4541
4978
  function extractDomain2(url) {
4542
4979
  try {
@@ -4550,9 +4987,9 @@ function saveSemantics(domain, pagePath, url, elements) {
4550
4987
  const filePath = getSemanticsPath(domain);
4551
4988
  const dir = dirname2(filePath);
4552
4989
  let site;
4553
- if (existsSync(filePath)) {
4990
+ if (existsSync3(filePath)) {
4554
4991
  try {
4555
- site = parse(readFileSync(filePath, "utf-8"));
4992
+ site = parse(readFileSync3(filePath, "utf-8"));
4556
4993
  } catch {
4557
4994
  site = { site: domain, pages: {} };
4558
4995
  }
@@ -4572,16 +5009,16 @@ function saveSemantics(domain, pagePath, url, elements) {
4572
5009
  }
4573
5010
  }
4574
5011
  site.updated_at = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
4575
- if (!existsSync(dir)) {
5012
+ if (!existsSync3(dir)) {
4576
5013
  mkdirSync3(dir, { recursive: true });
4577
5014
  }
4578
5015
  writeFileSync3(filePath, stringify(site, { lineWidth: 0 }));
4579
5016
  }
4580
5017
  function loadSemantics(domain) {
4581
5018
  const filePath = getSemanticsPath(domain);
4582
- if (!existsSync(filePath)) return null;
5019
+ if (!existsSync3(filePath)) return null;
4583
5020
  try {
4584
- return parse(readFileSync(filePath, "utf-8"));
5021
+ return parse(readFileSync3(filePath, "utf-8"));
4585
5022
  } catch {
4586
5023
  return null;
4587
5024
  }
@@ -5125,24 +5562,24 @@ var snapshotCommand = registerCommand({
5125
5562
  description: "Capture a quick page state snapshot \u2014 aria tree, visible text, or DOM summary",
5126
5563
  scope: "page",
5127
5564
  selectorParams: ["selector"],
5128
- parameters: z21.object({
5129
- type: z21.enum(["aria", "text", "dom", "all"]).default("aria").describe("Snapshot type: aria (accessibility tree), text (visible text), dom (element summary), all (combined)"),
5130
- selector: z21.string().optional().describe("Scope to a specific element"),
5131
- depth: z21.number().optional().default(6).describe("Max depth for DOM/aria tree"),
5132
- interactive: z21.boolean().optional().default(false).describe("Return interactive agent refs only"),
5133
- interactiveOnly: z21.boolean().optional().default(false).describe("Alias for interactive"),
5134
- i: z21.boolean().optional().default(false).describe("Short alias for interactive"),
5135
- compact: z21.boolean().optional().default(false).describe("Include compact xbrowser style snapshot text"),
5136
- c: z21.boolean().optional().default(false).describe("Short alias for compact"),
5137
- selectors: z21.boolean().optional().default(false).describe("Include ref to CSS selector map"),
5138
- all: z21.boolean().optional().default(false).describe("Include hidden interactive targets when using interactive snapshot")
5565
+ parameters: z24.object({
5566
+ type: z24.enum(["aria", "text", "dom", "all"]).default("aria").describe("Snapshot type: aria (accessibility tree), text (visible text), dom (element summary), all (combined)"),
5567
+ selector: z24.string().optional().describe("Scope to a specific element"),
5568
+ depth: z24.number().optional().default(6).describe("Max depth for DOM/aria tree"),
5569
+ interactive: z24.boolean().optional().default(false).describe("Return interactive agent refs only"),
5570
+ interactiveOnly: z24.boolean().optional().default(false).describe("Alias for interactive"),
5571
+ i: z24.boolean().optional().default(false).describe("Short alias for interactive"),
5572
+ compact: z24.boolean().optional().default(false).describe("Include compact xbrowser style snapshot text"),
5573
+ c: z24.boolean().optional().default(false).describe("Short alias for compact"),
5574
+ selectors: z24.boolean().optional().default(false).describe("Include ref to CSS selector map"),
5575
+ all: z24.boolean().optional().default(false).describe("Include hidden interactive targets when using interactive snapshot")
5139
5576
  }),
5140
- result: z21.object({
5141
- url: z21.string(),
5142
- title: z21.string(),
5143
- aria: z21.string().optional(),
5144
- text: z21.string().optional(),
5145
- dom: z21.record(z21.unknown()).optional()
5577
+ result: z24.object({
5578
+ url: z24.string(),
5579
+ title: z24.string(),
5580
+ aria: z24.string().optional(),
5581
+ text: z24.string().optional(),
5582
+ dom: z24.record(z24.unknown()).optional()
5146
5583
  }),
5147
5584
  handler: async (p, ctx) => {
5148
5585
  const page = ctx.page;
@@ -5156,7 +5593,7 @@ var snapshotCommand = registerCommand({
5156
5593
  if (p.compact || p.c || p.interactive || p.interactiveOnly || p.i) {
5157
5594
  observation.compact = formatObservationCompact(observation, { selectors: p.selectors });
5158
5595
  }
5159
- return ok20(observation, normalizeTips3([
5596
+ return ok23(observation, normalizeTips3([
5160
5597
  `refs refreshed for ${observation.targets.length} targets; use click @e1 or fill @e2 "text"`
5161
5598
  ]));
5162
5599
  }
@@ -5164,15 +5601,15 @@ var snapshotCommand = registerCommand({
5164
5601
  const aria = await captureAriaSnapshot(page, p.selector, p.depth);
5165
5602
  const tips = await buildRefTips(page, aria);
5166
5603
  persistSemantics(url, aria);
5167
- return ok20({ url, title, aria }, normalizeTips3(tips));
5604
+ return ok23({ url, title, aria }, normalizeTips3(tips));
5168
5605
  }
5169
5606
  if (p.type === "text") {
5170
5607
  const text = await captureTextSnapshot(page, p.selector);
5171
- return ok20({ url, title, text });
5608
+ return ok23({ url, title, text });
5172
5609
  }
5173
5610
  if (p.type === "dom") {
5174
5611
  const dom = await captureDomSnapshot(page, p.selector, p.depth ?? 6);
5175
- return ok20({ url, title, dom });
5612
+ return ok23({ url, title, dom });
5176
5613
  }
5177
5614
  if (p.type === "all") {
5178
5615
  const [aria, text, dom] = await Promise.all([
@@ -5182,9 +5619,9 @@ var snapshotCommand = registerCommand({
5182
5619
  ]);
5183
5620
  const tips = await buildRefTips(page, aria);
5184
5621
  persistSemantics(url, aria);
5185
- return ok20({ url, title, aria, text, dom }, normalizeTips3(tips));
5622
+ return ok23({ url, title, aria, text, dom }, normalizeTips3(tips));
5186
5623
  }
5187
- return fail8(`Unknown snapshot type: ${p.type}`);
5624
+ return fail10(`Unknown snapshot type: ${p.type}`);
5188
5625
  }
5189
5626
  });
5190
5627
  function persistSemantics(url, aria) {
@@ -5261,22 +5698,22 @@ async function captureDomSnapshot(page, selector, maxDepth) {
5261
5698
  }
5262
5699
 
5263
5700
  // src/commands/agent.ts
5264
- import { z as z22 } from "zod";
5265
- import { ok as ok21, normalizeTips as normalizeTips4 } from "@dyyz1993/xcli-core";
5701
+ import { z as z25 } from "zod";
5702
+ import { ok as ok24, normalizeTips as normalizeTips4 } from "@dyyz1993/xcli-core";
5266
5703
  var observeCommand = registerCommand({
5267
5704
  name: "observe",
5268
5705
  description: "Observe the current page as structured agent targets with session refs",
5269
5706
  scope: "page",
5270
- parameters: z22.object({
5271
- includeHidden: z22.boolean().optional().default(false).describe("Include hidden elements in the target list"),
5272
- limit: z22.number().int().positive().max(300).optional().default(80).describe("Maximum number of targets to return"),
5273
- compact: z22.boolean().optional().default(false).describe("Include compact xbrowser style snapshot text"),
5274
- selectors: z22.boolean().optional().default(false).describe("Include ref to stable CSS selector map")
5707
+ parameters: z25.object({
5708
+ includeHidden: z25.boolean().optional().default(false).describe("Include hidden elements in the target list"),
5709
+ limit: z25.number().int().positive().max(300).optional().default(80).describe("Maximum number of targets to return"),
5710
+ compact: z25.boolean().optional().default(false).describe("Include compact xbrowser style snapshot text"),
5711
+ selectors: z25.boolean().optional().default(false).describe("Include ref to stable CSS selector map")
5275
5712
  }),
5276
- result: z22.object({
5277
- targets: z22.array(z22.record(z22.unknown())),
5278
- selectors: z22.record(z22.unknown()).optional(),
5279
- compact: z22.string().optional()
5713
+ result: z25.object({
5714
+ targets: z25.array(z25.record(z25.unknown())),
5715
+ selectors: z25.record(z25.unknown()).optional(),
5716
+ compact: z25.string().optional()
5280
5717
  }).passthrough(),
5281
5718
  handler: async (p, ctx) => {
5282
5719
  const observation = await observePage(ctx.page, ctx.sessionId, {
@@ -5285,7 +5722,7 @@ var observeCommand = registerCommand({
5285
5722
  });
5286
5723
  if (p.selectors) observation.selectors = buildSelectorMap(observation);
5287
5724
  if (p.compact) observation.compact = formatObservationCompact(observation, { selectors: p.selectors });
5288
- return ok21(observation, normalizeTips4([
5725
+ return ok24(observation, normalizeTips4([
5289
5726
  `refs refreshed for ${observation.targets.length} targets; use act --ref @e1 --action click or click @e1`
5290
5727
  ]));
5291
5728
  }
@@ -5295,14 +5732,14 @@ var actCommand = registerCommand({
5295
5732
  description: "Perform an agent action using an observe ref or explicit selector",
5296
5733
  scope: "element",
5297
5734
  selectorParams: ["selector"],
5298
- parameters: z22.object({
5299
- action: z22.enum(["click", "fill", "type", "press", "select", "check", "hover"]).default("click"),
5300
- ref: z22.string().optional().describe("Session-scoped ref returned by observe, such as e1"),
5301
- selector: z22.string().optional().describe("CSS selector fallback when no ref is available"),
5302
- value: z22.string().optional().describe("Value for fill/type/select"),
5303
- key: z22.string().optional().describe("Key for press"),
5304
- force: z22.boolean().optional().default(false).describe("Bypass actionability checks"),
5305
- timeout: z22.number().optional().default(1e4).describe("Playwright action timeout in milliseconds")
5735
+ parameters: z25.object({
5736
+ action: z25.enum(["click", "fill", "type", "press", "select", "check", "hover"]).default("click"),
5737
+ ref: z25.string().optional().describe("Session-scoped ref returned by observe, such as e1"),
5738
+ selector: z25.string().optional().describe("CSS selector fallback when no ref is available"),
5739
+ value: z25.string().optional().describe("Value for fill/type/select"),
5740
+ key: z25.string().optional().describe("Key for press"),
5741
+ force: z25.boolean().optional().default(false).describe("Bypass actionability checks"),
5742
+ timeout: z25.number().optional().default(1e4).describe("Playwright action timeout in milliseconds")
5306
5743
  }).refine((p) => !!p.ref || !!p.selector, {
5307
5744
  message: "Either ref or selector is required"
5308
5745
  }),
@@ -5316,7 +5753,7 @@ var actCommand = registerCommand({
5316
5753
  tips: normalizeTips4(result.stale ? ["run observe again to refresh refs"] : [])
5317
5754
  };
5318
5755
  }
5319
- return ok21(result, normalizeTips4(result.stale ? ["ref screen hash changed; run observe if the next action is uncertain"] : []));
5756
+ return ok24(result, normalizeTips4(result.stale ? ["ref screen hash changed; run observe if the next action is uncertain"] : []));
5320
5757
  }
5321
5758
  });
5322
5759
  var waitForCommand = registerCommand({
@@ -5324,16 +5761,16 @@ var waitForCommand = registerCommand({
5324
5761
  description: "Wait for agent predicates such as text, URL, load state, selector state, or screen hash changes",
5325
5762
  scope: "page",
5326
5763
  selectorParams: ["selector"],
5327
- parameters: z22.object({
5328
- selector: z22.string().optional().describe("CSS selector or observe ref to wait for"),
5329
- state: z22.enum(["attached", "detached", "visible", "hidden"]).optional().default("visible"),
5330
- text: z22.string().optional().describe("Visible text to wait for"),
5331
- url: z22.string().optional().describe("URL substring or glob pattern to wait for"),
5332
- load: z22.enum(["load", "domcontentloaded", "networkidle"]).optional().describe("Load state to wait for"),
5333
- fn: z22.string().optional().describe("JavaScript predicate to wait for"),
5334
- screenHashChanged: z22.string().optional().describe("Previous screenHash from observe"),
5335
- timeout: z22.number().optional().default(3e4),
5336
- pollInterval: z22.number().optional().default(200)
5764
+ parameters: z25.object({
5765
+ selector: z25.string().optional().describe("CSS selector or observe ref to wait for"),
5766
+ state: z25.enum(["attached", "detached", "visible", "hidden"]).optional().default("visible"),
5767
+ text: z25.string().optional().describe("Visible text to wait for"),
5768
+ url: z25.string().optional().describe("URL substring or glob pattern to wait for"),
5769
+ load: z25.enum(["load", "domcontentloaded", "networkidle"]).optional().describe("Load state to wait for"),
5770
+ fn: z25.string().optional().describe("JavaScript predicate to wait for"),
5771
+ screenHashChanged: z25.string().optional().describe("Previous screenHash from observe"),
5772
+ timeout: z25.number().optional().default(3e4),
5773
+ pollInterval: z25.number().optional().default(200)
5337
5774
  }).refine((p) => [p.selector, p.text, p.url, p.load, p.fn, p.screenHashChanged].filter(Boolean).length === 1, {
5338
5775
  message: "Provide exactly one wait predicate: selector, text, url, load, fn, or screenHashChanged"
5339
5776
  }),
@@ -5347,30 +5784,30 @@ var waitForCommand = registerCommand({
5347
5784
  tips: []
5348
5785
  };
5349
5786
  }
5350
- return ok21(result);
5787
+ return ok24(result);
5351
5788
  }
5352
5789
  });
5353
5790
 
5354
5791
  // src/commands/tab.ts
5355
- import { z as z23 } from "zod";
5356
- import { ok as ok22, fail as fail9 } from "@dyyz1993/xcli-core";
5357
- var TabParams = z23.object({
5358
- subcommand: z23.enum(["list", "new", "close", "switch"]),
5359
- url: z23.string().optional(),
5360
- index: z23.number().int().min(0).optional()
5792
+ import { z as z26 } from "zod";
5793
+ import { ok as ok25, fail as fail11 } from "@dyyz1993/xcli-core";
5794
+ var TabParams = z26.object({
5795
+ subcommand: z26.enum(["list", "new", "close", "switch"]),
5796
+ url: z26.string().optional(),
5797
+ index: z26.number().int().min(0).optional()
5361
5798
  });
5362
5799
  var tabCommand = registerCommand({
5363
5800
  name: "tab",
5364
5801
  description: "Manage browser tabs: list, new, close, switch",
5365
5802
  scope: "page",
5366
5803
  parameters: TabParams,
5367
- result: z23.object({
5368
- success: z23.boolean(),
5369
- data: z23.unknown()
5804
+ result: z26.object({
5805
+ success: z26.boolean(),
5806
+ data: z26.unknown()
5370
5807
  }),
5371
5808
  handler: async (p, ctx) => {
5372
5809
  if (!ctx.browserContext) {
5373
- return fail9("No browser context available. Use --cdp to connect to a browser first.");
5810
+ return fail11("No browser context available. Use --cdp to connect to a browser first.");
5374
5811
  }
5375
5812
  const pages = ctx.browserContext.pages();
5376
5813
  switch (p.subcommand) {
@@ -5383,7 +5820,7 @@ var tabCommand = registerCommand({
5383
5820
  case "switch":
5384
5821
  return handleSwitch(p, pages, ctx);
5385
5822
  default:
5386
- return fail9(`Unknown subcommand: ${p.subcommand}`);
5823
+ return fail11(`Unknown subcommand: ${p.subcommand}`);
5387
5824
  }
5388
5825
  }
5389
5826
  });
@@ -5398,7 +5835,7 @@ async function handleList(pages, ctx) {
5398
5835
  if (isActive) activeIndex = i;
5399
5836
  tabs.push({ index: i, url, title, active: isActive });
5400
5837
  }
5401
- return ok22({ tabs, total: tabs.length, activeIndex });
5838
+ return ok25({ tabs, total: tabs.length, activeIndex });
5402
5839
  }
5403
5840
  async function handleNew(p, _pages, ctx) {
5404
5841
  const newPage = await ctx.browserContext.newPage();
@@ -5426,7 +5863,7 @@ async function handleNew(p, _pages, ctx) {
5426
5863
  const title = await newPage.title().catch(() => "");
5427
5864
  const allPages = ctx.browserContext.pages();
5428
5865
  const newIndex = allPages.indexOf(newPage);
5429
- return ok22({
5866
+ return ok25({
5430
5867
  index: newIndex >= 0 ? newIndex : allPages.length - 1,
5431
5868
  url: newPage.url(),
5432
5869
  title,
@@ -5437,11 +5874,11 @@ async function handleNew(p, _pages, ctx) {
5437
5874
  async function handleClose(p, ctx) {
5438
5875
  const currentPages = ctx.browserContext.pages();
5439
5876
  if (currentPages.length <= 1) {
5440
- return fail9("Cannot close the last remaining tab");
5877
+ return fail11("Cannot close the last remaining tab");
5441
5878
  }
5442
5879
  const closeIndex = p.index ?? currentPages.findIndex((pg) => pg === ctx.page);
5443
5880
  if (closeIndex < 0 || closeIndex >= currentPages.length) {
5444
- return fail9(`Invalid tab index: ${closeIndex}. Valid range: 0-${currentPages.length - 1}`);
5881
+ return fail11(`Invalid tab index: ${closeIndex}. Valid range: 0-${currentPages.length - 1}`);
5445
5882
  }
5446
5883
  const pageToClose = currentPages[closeIndex];
5447
5884
  const isActivePage = pageToClose === ctx.page;
@@ -5456,7 +5893,7 @@ async function handleClose(p, ctx) {
5456
5893
  }
5457
5894
  ctx.page = newActivePage;
5458
5895
  }
5459
- return ok22({
5896
+ return ok25({
5460
5897
  closedIndex: closeIndex,
5461
5898
  total: remainingPages.length,
5462
5899
  activeIndex: isActivePage ? closeIndex < remainingPages.length ? closeIndex : remainingPages.length - 1 : remainingPages.findIndex((pg) => pg === ctx.page)
@@ -5464,10 +5901,10 @@ async function handleClose(p, ctx) {
5464
5901
  }
5465
5902
  async function handleSwitch(p, pages, ctx) {
5466
5903
  if (p.index === void 0) {
5467
- return fail9("Parameter --index is required for switch subcommand");
5904
+ return fail11("Parameter --index is required for switch subcommand");
5468
5905
  }
5469
5906
  if (p.index < 0 || p.index >= pages.length) {
5470
- return fail9(`Invalid tab index: ${p.index}. Valid range: 0-${pages.length - 1}`);
5907
+ return fail11(`Invalid tab index: ${p.index}. Valid range: 0-${pages.length - 1}`);
5471
5908
  }
5472
5909
  const targetPage = pages[p.index];
5473
5910
  await targetPage.bringToFront().catch(() => {
@@ -5477,13 +5914,19 @@ async function handleSwitch(p, pages, ctx) {
5477
5914
  if (okProbe) break;
5478
5915
  await new Promise((r) => setTimeout(r, 300));
5479
5916
  }
5917
+ try {
5918
+ const mouse = targetPage.mouse;
5919
+ if (mouse) await mouse.move(1, 1).catch(() => {
5920
+ });
5921
+ } catch {
5922
+ }
5480
5923
  const session = ctx.sessionId ? getSessionById(ctx.sessionId) : void 0;
5481
5924
  if (session) {
5482
5925
  setActivePage(session, targetPage);
5483
5926
  }
5484
5927
  ctx.page = targetPage;
5485
5928
  const title = await targetPage.title().catch(() => "");
5486
- return ok22({
5929
+ return ok25({
5487
5930
  index: p.index,
5488
5931
  url: targetPage.url(),
5489
5932
  title,
@@ -5492,9 +5935,9 @@ async function handleSwitch(p, pages, ctx) {
5492
5935
  }
5493
5936
 
5494
5937
  // src/commands/addinitscript.ts
5495
- import { z as z24 } from "zod";
5496
- import { ok as ok23 } from "@dyyz1993/xcli-core";
5497
- import { readFileSync as readFileSync2 } from "fs";
5938
+ import { z as z27 } from "zod";
5939
+ import { ok as ok26 } from "@dyyz1993/xcli-core";
5940
+ import { readFileSync as readFileSync4 } from "fs";
5498
5941
 
5499
5942
  // src/chain-parser.ts
5500
5943
  import { splitCommand, parseCommandArgs } from "@dyyz1993/xcli-core";
@@ -5634,14 +6077,14 @@ registerCommandDefinition("tab", ["subcommand"]);
5634
6077
  registerCommandDefinition("mouse", ["action", "x", "y"]);
5635
6078
 
5636
6079
  // src/commands/addinitscript.ts
5637
- var InitScriptParams = z24.object({
5638
- script: z24.string().optional(),
5639
- file: z24.string().optional(),
5640
- stdin: z24.boolean().optional(),
5641
- name: z24.string().optional(),
5642
- list: z24.boolean().optional(),
5643
- remove: z24.union([z24.string(), z24.boolean()]).optional(),
5644
- base64: z24.string().optional()
6080
+ var InitScriptParams = z27.object({
6081
+ script: z27.string().optional(),
6082
+ file: z27.string().optional(),
6083
+ stdin: z27.boolean().optional(),
6084
+ name: z27.string().optional(),
6085
+ list: z27.boolean().optional(),
6086
+ remove: z27.union([z27.string(), z27.boolean()]).optional(),
6087
+ base64: z27.string().optional()
5645
6088
  });
5646
6089
  var registeredScripts = /* @__PURE__ */ new Map();
5647
6090
  function resolveScriptContent(params) {
@@ -5649,7 +6092,7 @@ function resolveScriptContent(params) {
5649
6092
  return Buffer.from(params.base64, "base64").toString("utf-8");
5650
6093
  }
5651
6094
  if (params.file) {
5652
- return readFileSync2(params.file, "utf-8");
6095
+ return readFileSync4(params.file, "utf-8");
5653
6096
  }
5654
6097
  if (params.stdin) {
5655
6098
  return null;
@@ -5675,14 +6118,14 @@ var addInitScriptCommand = registerCommand({
5675
6118
  description: "Add an initialization script that runs on every page load",
5676
6119
  scope: "page",
5677
6120
  parameters: InitScriptParams,
5678
- result: z24.object({
5679
- scripts: z24.array(z24.object({ name: z24.string(), size: z24.number(), preview: z24.string() })).optional(),
5680
- removed: z24.string().optional(),
5681
- existed: z24.boolean().optional(),
5682
- error: z24.string().optional(),
5683
- registered: z24.string().optional(),
5684
- hint: z24.string().optional(),
5685
- executedImmediately: z24.boolean().optional()
6121
+ result: z27.object({
6122
+ scripts: z27.array(z27.object({ name: z27.string(), size: z27.number(), preview: z27.string() })).optional(),
6123
+ removed: z27.string().optional(),
6124
+ existed: z27.boolean().optional(),
6125
+ error: z27.string().optional(),
6126
+ registered: z27.string().optional(),
6127
+ hint: z27.string().optional(),
6128
+ executedImmediately: z27.boolean().optional()
5686
6129
  }).passthrough(),
5687
6130
  handler: async (params, ctx) => {
5688
6131
  if (params.list) {
@@ -5691,20 +6134,20 @@ var addInitScriptCommand = registerCommand({
5691
6134
  size: content2.length,
5692
6135
  preview: content2.slice(0, 80)
5693
6136
  }));
5694
- return ok23({ scripts });
6137
+ return ok26({ scripts });
5695
6138
  }
5696
6139
  if (params.remove && typeof params.remove === "string") {
5697
6140
  const existed = registeredScripts.delete(params.remove);
5698
- return ok23({ removed: params.remove, existed });
6141
+ return ok26({ removed: params.remove, existed });
5699
6142
  }
5700
6143
  const removeTarget = (typeof params.remove === "string" ? params.remove : null) || (params.name && !resolveScriptContent(params) ? params.name : null);
5701
6144
  if (removeTarget && !resolveScriptContent(params)) {
5702
6145
  const existed = registeredScripts.delete(removeTarget);
5703
- return ok23({ removed: removeTarget, existed });
6146
+ return ok26({ removed: removeTarget, existed });
5704
6147
  }
5705
6148
  let content = params.stdin ? await readStdin() : resolveScriptContent(params);
5706
6149
  if (!content) {
5707
- return ok23({ error: "No script content provided. Use --script, --file, --stdin, or --base64" });
6150
+ return ok26({ error: "No script content provided. Use --script, --file, --stdin, or --base64" });
5708
6151
  }
5709
6152
  const scriptName = params.name ?? `script-${Date.now()}`;
5710
6153
  registeredScripts.set(scriptName, content);
@@ -5712,45 +6155,45 @@ var addInitScriptCommand = registerCommand({
5712
6155
  try {
5713
6156
  await ctx.page.evaluate(content);
5714
6157
  } catch {
5715
- return ok23({
6158
+ return ok26({
5716
6159
  registered: scriptName,
5717
6160
  hint: "Script registered for future page loads; immediate execution skipped (page may not be ready)"
5718
6161
  });
5719
6162
  }
5720
- return ok23({ registered: scriptName, executedImmediately: true });
6163
+ return ok26({ registered: scriptName, executedImmediately: true });
5721
6164
  }
5722
6165
  });
5723
6166
  registerCommandDefinition("addinitscript", ["script"]);
5724
6167
 
5725
6168
  // src/commands/find.ts
5726
- import { z as z25 } from "zod";
5727
- import { ok as ok24, fail as fail10, normalizeTips as normalizeTips5 } from "@dyyz1993/xcli-core";
5728
- var actionSchema2 = z25.enum(["click", "fill", "type", "select", "hover", "check"]);
6169
+ import { z as z28 } from "zod";
6170
+ import { ok as ok27, fail as fail12, normalizeTips as normalizeTips5 } from "@dyyz1993/xcli-core";
6171
+ var actionSchema2 = z28.enum(["click", "fill", "type", "select", "hover", "check"]);
5729
6172
  var findCommand = registerCommand({
5730
6173
  name: "find",
5731
6174
  description: "Find elements by semantic strategy (text/role/label/placeholder/testid) and optionally perform an action",
5732
6175
  scope: "page",
5733
- parameters: z25.object({
5734
- strategy: z25.enum(["text", "role", "label", "placeholder", "testid", "alt", "title", "first", "last", "nth"]),
5735
- value: z25.string(),
5736
- name: z25.string().optional(),
5737
- exact: z25.boolean().optional().default(false),
5738
- operation: z25.string().optional().describe('Trailing operation syntax, e.g. click, fill "text", type "text"'),
6176
+ parameters: z28.object({
6177
+ strategy: z28.enum(["text", "role", "label", "placeholder", "testid", "alt", "title", "first", "last", "nth"]),
6178
+ value: z28.string(),
6179
+ name: z28.string().optional(),
6180
+ exact: z28.boolean().optional().default(false),
6181
+ operation: z28.string().optional().describe('Trailing operation syntax, e.g. click, fill "text", type "text"'),
5739
6182
  action: actionSchema2.optional().describe("Action to perform when not using trailing operation syntax"),
5740
- actionValue: z25.string().optional().describe("Value for fill/type/select when using action"),
5741
- index: z25.number().int().optional().describe("Index for nth strategy"),
5742
- click: z25.boolean().optional().default(false),
5743
- fill: z25.string().optional(),
5744
- type: z25.string().optional(),
5745
- select: z25.string().optional(),
5746
- hover: z25.boolean().optional().default(false),
5747
- check: z25.boolean().optional().default(false),
5748
- timeout: z25.number().optional().default(1e4)
6183
+ actionValue: z28.string().optional().describe("Value for fill/type/select when using action"),
6184
+ index: z28.number().int().optional().describe("Index for nth strategy"),
6185
+ click: z28.boolean().optional().default(false),
6186
+ fill: z28.string().optional(),
6187
+ type: z28.string().optional(),
6188
+ select: z28.string().optional(),
6189
+ hover: z28.boolean().optional().default(false),
6190
+ check: z28.boolean().optional().default(false),
6191
+ timeout: z28.number().optional().default(1e4)
5749
6192
  }),
5750
- result: z25.object({
5751
- matched: z25.number(),
5752
- selector: z25.string(),
5753
- action: z25.string().optional()
6193
+ result: z28.object({
6194
+ matched: z28.number(),
6195
+ selector: z28.string(),
6196
+ action: z28.string().optional()
5754
6197
  }),
5755
6198
  handler: async (p, ctx) => {
5756
6199
  const page = ctx.page;
@@ -5765,7 +6208,7 @@ var findCommand = registerCommand({
5765
6208
  });
5766
6209
  const count = await locator.count();
5767
6210
  if (count === 0) {
5768
- return fail10(`No element found with ${p.strategy}="${p.value}"`);
6211
+ return fail12(`No element found with ${p.strategy}="${p.value}"`);
5769
6212
  }
5770
6213
  const tips = [];
5771
6214
  const target = selectTarget(locator, p.strategy);
@@ -5777,15 +6220,15 @@ var findCommand = registerCommand({
5777
6220
  await target.click({ timeout: p.timeout, force: true });
5778
6221
  return okWithTips({ matched: count, selector, action: "click" }, tips);
5779
6222
  } else if (actionName === "fill") {
5780
- if (actionValue === void 0) return fail10("find fill requires a value");
6223
+ if (actionValue === void 0) return fail12("find fill requires a value");
5781
6224
  await target.fill(actionValue, { timeout: p.timeout, force: true });
5782
6225
  return okWithTips({ matched: count, selector, action: `fill("${actionValue}")` }, tips);
5783
6226
  } else if (actionName === "type") {
5784
- if (actionValue === void 0) return fail10("find type requires a value");
6227
+ if (actionValue === void 0) return fail12("find type requires a value");
5785
6228
  await target.type(actionValue, { delay: 10, timeout: p.timeout });
5786
6229
  return okWithTips({ matched: count, selector, action: `type("${actionValue}")` }, tips);
5787
6230
  } else if (actionName === "select") {
5788
- if (actionValue === void 0) return fail10("find select requires a value");
6231
+ if (actionValue === void 0) return fail12("find select requires a value");
5789
6232
  await target.selectOption(actionValue);
5790
6233
  return okWithTips({ matched: count, selector, action: `select("${actionValue}")` }, tips);
5791
6234
  } else if (actionName === "hover") {
@@ -5799,7 +6242,7 @@ var findCommand = registerCommand({
5799
6242
  }
5800
6243
  });
5801
6244
  function okWithTips(data, tips) {
5802
- const result = ok24(data);
6245
+ const result = ok27(data);
5803
6246
  if (tips.length > 0) result.tips = normalizeTips5(tips);
5804
6247
  return result;
5805
6248
  }
@@ -5910,18 +6353,18 @@ import {
5910
6353
  Core
5911
6354
  } from "@dyyz1993/xcli-core";
5912
6355
  import { resolve as resolve2 } from "path";
5913
- import { existsSync as existsSync4, readdirSync } from "fs";
6356
+ import { existsSync as existsSync6, readdirSync } from "fs";
5914
6357
  import { homedir as homedir5 } from "os";
5915
6358
 
5916
6359
  // src/plugin/metadata-parser.ts
5917
- import { existsSync as existsSync2 } from "fs";
6360
+ import { existsSync as existsSync4 } from "fs";
5918
6361
  import { resolve } from "path";
5919
6362
 
5920
6363
  // src/utils/json-file.ts
5921
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "fs";
6364
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
5922
6365
  function readJsonFile(filePath, defaultValue) {
5923
6366
  try {
5924
- const content = readFileSync3(filePath, "utf-8");
6367
+ const content = readFileSync5(filePath, "utf-8");
5925
6368
  return JSON.parse(content);
5926
6369
  } catch {
5927
6370
  return defaultValue;
@@ -5933,7 +6376,7 @@ var PluginMetadataParser = class {
5933
6376
  static XBROWSER_KEYWORDS = ["xbrowser", "xbrowser-plugin"];
5934
6377
  static parseFromPackageJson(pluginPath) {
5935
6378
  const packageJsonPath = resolve(pluginPath, "package.json");
5936
- if (!existsSync2(packageJsonPath)) {
6379
+ if (!existsSync4(packageJsonPath)) {
5937
6380
  return null;
5938
6381
  }
5939
6382
  const packageJson = readJsonFile(packageJsonPath, null);
@@ -5997,20 +6440,20 @@ var PluginMetadataParser = class {
5997
6440
  };
5998
6441
 
5999
6442
  // src/plugin/ensure-deps.ts
6000
- import { existsSync as existsSync3, mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "fs";
6001
- import { join as join5 } from "path";
6443
+ import { existsSync as existsSync5, mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "fs";
6444
+ import { join as join7 } from "path";
6002
6445
  import { execSync } from "child_process";
6003
6446
  var SHARED_PLUGIN_DEPENDENCIES = {
6004
6447
  "zod": "^3.24.0",
6005
6448
  "@dyyz1993/xcli-core": "^0.12.1"
6006
6449
  };
6007
6450
  function ensurePluginDependencies(pluginsDir) {
6008
- const zodPath = join5(pluginsDir, "node_modules", "zod");
6009
- if (existsSync3(zodPath)) return;
6451
+ const zodPath = join7(pluginsDir, "node_modules", "zod");
6452
+ if (existsSync5(zodPath)) return;
6010
6453
  mkdirSync4(pluginsDir, { recursive: true });
6011
- const pkgPath = join5(pluginsDir, "package.json");
6454
+ const pkgPath = join7(pluginsDir, "package.json");
6012
6455
  let pkg = {};
6013
- if (existsSync3(pkgPath)) {
6456
+ if (existsSync5(pkgPath)) {
6014
6457
  try {
6015
6458
  pkg = readJsonFile(pkgPath, {});
6016
6459
  } catch {
@@ -6024,7 +6467,7 @@ function ensurePluginDependencies(pluginsDir) {
6024
6467
  needsInstall = true;
6025
6468
  }
6026
6469
  }
6027
- if (!needsInstall && existsSync3(join5(pluginsDir, "node_modules"))) return;
6470
+ if (!needsInstall && existsSync5(join7(pluginsDir, "node_modules"))) return;
6028
6471
  pkg.dependencies = existingDeps;
6029
6472
  pkg.private = true;
6030
6473
  pkg.description = pkg.description || "xbrowser plugins \u2014 shared dependencies";
@@ -6282,7 +6725,7 @@ var XBrowserPluginLoader = class {
6282
6725
  const loaded = [];
6283
6726
  const seen = /* @__PURE__ */ new Set();
6284
6727
  for (const dir of dirs) {
6285
- if (!existsSync4(dir)) continue;
6728
+ if (!existsSync6(dir)) continue;
6286
6729
  const entries = readdirSync(dir, { withFileTypes: true });
6287
6730
  for (const entry of entries) {
6288
6731
  if (!entry.isDirectory()) continue;
@@ -6290,12 +6733,12 @@ var XBrowserPluginLoader = class {
6290
6733
  seen.add(entry.name);
6291
6734
  const pluginDir = resolve2(dir, entry.name);
6292
6735
  let indexPath = resolve2(pluginDir, "index.js");
6293
- if (!existsSync4(indexPath)) {
6736
+ if (!existsSync6(indexPath)) {
6294
6737
  indexPath = resolve2(pluginDir, "index.ts");
6295
6738
  }
6296
- if (!existsSync4(indexPath)) continue;
6739
+ if (!existsSync6(indexPath)) continue;
6297
6740
  try {
6298
- if (!existsSync4(resolve2(pluginDir, "package.json"))) {
6741
+ if (!existsSync6(resolve2(pluginDir, "package.json"))) {
6299
6742
  console.warn(`\u26A0\uFE0F Plugin "${entry.name}" has no package.json. Use "xbrowser create ${entry.name} --template static" for proper structure.`);
6300
6743
  } else {
6301
6744
  const metadata = PluginMetadataParser.parseFromPackageJson(pluginDir);
@@ -6952,7 +7395,7 @@ async function loadHooks() {
6952
7395
 
6953
7396
  // src/executor.ts
6954
7397
  import { homedir as homedir6 } from "os";
6955
- import { join as join6 } from "path";
7398
+ import { join as join8 } from "path";
6956
7399
  function levenshtein(a, b) {
6957
7400
  const m = a.length, n = b.length;
6958
7401
  const dp = Array(m + 1).fill(null).map(() => Array(n + 1).fill(0));
@@ -6967,7 +7410,7 @@ function levenshtein(a, b) {
6967
7410
  }
6968
7411
  var NAVIGATION_COMMANDS = /* @__PURE__ */ new Set(["goto", "back", "forward", "refresh"]);
6969
7412
  var snapshotHintShown = /* @__PURE__ */ new WeakSet();
6970
- var CONFIG_DIR2 = join6(homedir6(), ".xbrowser");
7413
+ var CONFIG_DIR2 = join8(homedir6(), ".xbrowser");
6971
7414
  var storageCache = /* @__PURE__ */ new Map();
6972
7415
  function getPluginStorage(pluginName) {
6973
7416
  if (!storageCache.has(pluginName)) {
@@ -6979,7 +7422,7 @@ var archiveInitialized = false;
6979
7422
  function ensureArchiveInit() {
6980
7423
  if (!archiveInitialized) {
6981
7424
  try {
6982
- configureArchiveStore({ archiveDir: join6(homedir6(), ".xbrowser", "archives") });
7425
+ configureArchiveStore({ archiveDir: join8(homedir6(), ".xbrowser", "archives") });
6983
7426
  } catch {
6984
7427
  }
6985
7428
  archiveInitialized = true;
@@ -7003,7 +7446,7 @@ async function guardCheck(commandName) {
7003
7446
  }
7004
7447
  }
7005
7448
  function errorResult(message) {
7006
- return { ...fail11(message), duration: 0 };
7449
+ return { ...fail13(message), duration: 0 };
7007
7450
  }
7008
7451
  function tipsToMessages(tips) {
7009
7452
  if (!tips || tips.length === 0) return [];
@@ -7080,7 +7523,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7080
7523
  }
7081
7524
  let targetPageOverride = null;
7082
7525
  if (_target && extraOpts?.cdpEndpoint) {
7083
- const { findTargetPage } = await import("./browser-Q5APBNF6.js");
7526
+ const { findTargetPage } = await import("./browser-OMGJMSBB.js");
7084
7527
  targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
7085
7528
  if (!targetPageOverride) {
7086
7529
  return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
@@ -7097,8 +7540,15 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7097
7540
  params = result.data;
7098
7541
  }
7099
7542
  if (command.scope !== "cli" && !process.env.XBROWSER_DAEMON_WORKER) {
7100
- const { forwardExec } = await import("./daemon-client-Y2YSZCGK.js");
7101
- const result = await forwardExec(commandName, params, sessionName, extraOpts?.cdpEndpoint);
7543
+ const { forwardExec } = await import("./daemon-client-HEGAXWGK.js");
7544
+ const longRunning = /^(vision-task|crawl|scrape|record|replay)$/.test(commandName);
7545
+ const result = await forwardExec(
7546
+ commandName,
7547
+ params,
7548
+ sessionName,
7549
+ extraOpts?.cdpEndpoint,
7550
+ longRunning ? 20 * 6e4 : void 0
7551
+ );
7102
7552
  if (result) return result;
7103
7553
  }
7104
7554
  let session;
@@ -7266,7 +7716,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7266
7716
  timestamp: start
7267
7717
  });
7268
7718
  if (isSuccess) {
7269
- return { ...ok25(raw.data, merged.length > 0 ? merged : raw.tips), duration, ...hookOutputs ? { hookOutputs } : {} };
7719
+ return { ...ok28(raw.data, merged.length > 0 ? merged : raw.tips), duration, ...hookOutputs ? { hookOutputs } : {} };
7270
7720
  }
7271
7721
  return { success: false, data: raw.data, message: raw.message, tips: mergedOrRaw, duration, ...hookOutputs ? { hookOutputs } : {} };
7272
7722
  }
@@ -7280,14 +7730,14 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7280
7730
  duration,
7281
7731
  timestamp: start
7282
7732
  });
7283
- return { ...ok25(raw, smartTipNormalized), duration, ...hookOutputs ? { hookOutputs } : {} };
7733
+ return { ...ok28(raw, smartTipNormalized), duration, ...hookOutputs ? { hookOutputs } : {} };
7284
7734
  } catch (err) {
7285
7735
  const end = Date.now();
7286
7736
  const duration = end - start;
7287
7737
  const errorMessage = errMsg(err);
7288
7738
  if (session?.page && process.env.XBROWSER_RECOVERY && !extraOpts?._recoveryAttempted) {
7289
7739
  try {
7290
- const { attemptRecovery } = await import("./recovery-J2ISVGUL.js");
7740
+ const { attemptRecovery } = await import("./recovery-FTZGV6VY.js");
7291
7741
  const recovery = await attemptRecovery(
7292
7742
  session.page,
7293
7743
  sessionName,
@@ -7325,7 +7775,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7325
7775
  duration,
7326
7776
  timestamp: start
7327
7777
  });
7328
- return { ...fail11(errorMessage), duration };
7778
+ return { ...fail13(errorMessage), duration };
7329
7779
  } finally {
7330
7780
  }
7331
7781
  }
@@ -7366,7 +7816,7 @@ async function executeChain(input, options) {
7366
7816
  results.push({
7367
7817
  command: cmdName,
7368
7818
  raw: cmdStr,
7369
- ...fail11(`Plugin "${cmdName}" requires a sub-command`),
7819
+ ...fail13(`Plugin "${cmdName}" requires a sub-command`),
7370
7820
  duration: 0
7371
7821
  });
7372
7822
  if (type === "and") {
@@ -7385,7 +7835,7 @@ async function executeChain(input, options) {
7385
7835
  results.push({
7386
7836
  command: cmdName,
7387
7837
  raw: cmdStr,
7388
- ...fail11(`Unknown command "${subCommand}" for plugin "${cmdName}"`),
7838
+ ...fail13(`Unknown command "${subCommand}" for plugin "${cmdName}"`),
7389
7839
  duration: 0
7390
7840
  });
7391
7841
  if (type === "and") {
@@ -7489,7 +7939,7 @@ async function executeChain(input, options) {
7489
7939
  results.push({
7490
7940
  command: `${cmdName} ${subCommand}`,
7491
7941
  raw: cmdStr,
7492
- ...ok25(data),
7942
+ ...ok28(data),
7493
7943
  duration: duration2,
7494
7944
  ...hookOutputs ? { hookOutputs } : {}
7495
7945
  });
@@ -7517,7 +7967,7 @@ async function executeChain(input, options) {
7517
7967
  results.push({
7518
7968
  command: `${cmdName} ${subCommand}`,
7519
7969
  raw: cmdStr,
7520
- ...fail11(errorMessage),
7970
+ ...fail13(errorMessage),
7521
7971
  duration: duration2
7522
7972
  });
7523
7973
  if (type === "and") {
@@ -7798,25 +8248,25 @@ Run "xbrowser config list" to see current keys.`
7798
8248
 
7799
8249
  // src/plugin/installer.ts
7800
8250
  import {
7801
- existsSync as existsSync10,
8251
+ existsSync as existsSync12,
7802
8252
  readdirSync as readdirSync2,
7803
8253
  mkdirSync as mkdirSync8,
7804
8254
  rmSync as rmSync6,
7805
8255
  copyFileSync,
7806
8256
  cpSync as cpSync6,
7807
- readFileSync as readFileSync8,
8257
+ readFileSync as readFileSync10,
7808
8258
  writeFileSync as writeFileSync10
7809
8259
  } from "fs";
7810
8260
  import { resolve as resolve8, basename as basename2, dirname as dirname4 } from "path";
7811
8261
  import { homedir as homedir7 } from "os";
7812
8262
 
7813
8263
  // src/plugin/install-sources/local.ts
7814
- import { existsSync as existsSync5, cpSync, rmSync } from "fs";
8264
+ import { existsSync as existsSync7, cpSync, rmSync } from "fs";
7815
8265
  import { resolve as resolve3 } from "path";
7816
8266
  import { verifyPlugin, safeCleanup } from "@dyyz1993/xcli-core";
7817
8267
  async function installFromLocal(source, name, targetDir) {
7818
8268
  const srcPath = resolve3(source);
7819
- if (!existsSync5(srcPath)) {
8269
+ if (!existsSync7(srcPath)) {
7820
8270
  throw new Error(`Local path does not exist: ${srcPath}`);
7821
8271
  }
7822
8272
  const tmpTarget = `${targetDir}-tmp-${Date.now()}`;
@@ -7829,7 +8279,7 @@ async function installFromLocal(source, name, targetDir) {
7829
8279
  safeCleanup(tmpTarget);
7830
8280
  throw new Error(`Invalid plugin: ${verify.error}`);
7831
8281
  }
7832
- if (existsSync5(targetDir)) {
8282
+ if (existsSync7(targetDir)) {
7833
8283
  rmSync(targetDir, { recursive: true, force: true });
7834
8284
  }
7835
8285
  cpSync(tmpTarget, targetDir, { recursive: true, force: true });
@@ -7849,8 +8299,8 @@ async function installFromLocal(source, name, targetDir) {
7849
8299
  }
7850
8300
 
7851
8301
  // src/plugin/install-sources/npm.ts
7852
- import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync6, rmSync as rmSync2, cpSync as cpSync2 } from "fs";
7853
- import { resolve as resolve4, join as join7 } from "path";
8302
+ import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync6, rmSync as rmSync2, cpSync as cpSync2 } from "fs";
8303
+ import { resolve as resolve4, join as join9 } from "path";
7854
8304
  import { tmpdir } from "os";
7855
8305
  import {
7856
8306
  downloadToFile,
@@ -7923,13 +8373,13 @@ async function installFromNpm(packageName, name, targetDir) {
7923
8373
  throw new Error(`No tarball URL for ${packageName}@${latestVersion}`);
7924
8374
  }
7925
8375
  const tarballUrl = versionMeta.dist.tarball;
7926
- const tmpDir = join7(tmpdir(), `xbrowser-npm-${Date.now()}`);
8376
+ const tmpDir = join9(tmpdir(), `xbrowser-npm-${Date.now()}`);
7927
8377
  mkdirSync5(tmpDir, { recursive: true });
7928
8378
  let warnings = [];
7929
8379
  try {
7930
- const tarballPath = join7(tmpDir, `${name}.tgz`);
8380
+ const tarballPath = join9(tmpDir, `${name}.tgz`);
7931
8381
  await downloadToFile(tarballUrl, tarballPath);
7932
- const extractDir = join7(tmpDir, "extracted");
8382
+ const extractDir = join9(tmpDir, "extracted");
7933
8383
  extractTarGz(tarballPath, extractDir);
7934
8384
  flattenPackageRoot(extractDir);
7935
8385
  const verify = verifyPlugin2(extractDir, { metadataField: "xbrowser" });
@@ -7937,13 +8387,13 @@ async function installFromNpm(packageName, name, targetDir) {
7937
8387
  if (!verify.valid) {
7938
8388
  throw new Error(`Invalid npm plugin: ${verify.error}`);
7939
8389
  }
7940
- if (existsSync6(targetDir)) {
8390
+ if (existsSync8(targetDir)) {
7941
8391
  rmSync2(targetDir, { recursive: true, force: true });
7942
8392
  }
7943
8393
  cpSync2(extractDir, targetDir, { recursive: true, force: true });
7944
8394
  const pkgPath = resolve4(targetDir, "package.json");
7945
- if (existsSync6(pkgPath)) {
7946
- const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
8395
+ if (existsSync8(pkgPath)) {
8396
+ const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
7947
8397
  if (!pkg._npmSource) {
7948
8398
  pkg._npmSource = { name: packageName, version: latestVersion };
7949
8399
  writeFileSync6(pkgPath, JSON.stringify(pkg, null, 2));
@@ -7963,13 +8413,13 @@ async function installFromNpm(packageName, name, targetDir) {
7963
8413
  }
7964
8414
 
7965
8415
  // src/plugin/install-sources/git.ts
7966
- import { existsSync as existsSync7, readFileSync as readFileSync5, writeFileSync as writeFileSync7, rmSync as rmSync3, cpSync as cpSync3 } from "fs";
7967
- import { resolve as resolve5, join as join8 } from "path";
8416
+ import { existsSync as existsSync9, readFileSync as readFileSync7, writeFileSync as writeFileSync7, rmSync as rmSync3, cpSync as cpSync3 } from "fs";
8417
+ import { resolve as resolve5, join as join10 } from "path";
7968
8418
  import { tmpdir as tmpdir2 } from "os";
7969
8419
  import { execSync as execSync2 } from "child_process";
7970
8420
  import { verifyPlugin as verifyPlugin3, safeCleanup as safeCleanup3 } from "@dyyz1993/xcli-core";
7971
8421
  async function installFromGit(gitUrl, name, targetDir) {
7972
- const tmpDir = join8(tmpdir2(), `xbrowser-git-${Date.now()}`);
8422
+ const tmpDir = join10(tmpdir2(), `xbrowser-git-${Date.now()}`);
7973
8423
  let warnings = [];
7974
8424
  try {
7975
8425
  execSync2(`git clone --depth 1 "${gitUrl}" "${tmpDir}"`, { stdio: "pipe" });
@@ -7978,14 +8428,14 @@ async function installFromGit(gitUrl, name, targetDir) {
7978
8428
  if (!verify.valid) {
7979
8429
  throw new Error(`Invalid git plugin: ${verify.error}`);
7980
8430
  }
7981
- if (existsSync7(targetDir)) {
8431
+ if (existsSync9(targetDir)) {
7982
8432
  rmSync3(targetDir, { recursive: true, force: true });
7983
8433
  }
7984
8434
  cpSync3(tmpDir, targetDir, { recursive: true, force: true });
7985
8435
  rmSync3(resolve5(targetDir, ".git"), { recursive: true, force: true });
7986
8436
  const pkgPath = resolve5(targetDir, "package.json");
7987
- if (existsSync7(pkgPath)) {
7988
- const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
8437
+ if (existsSync9(pkgPath)) {
8438
+ const pkg = JSON.parse(readFileSync7(pkgPath, "utf-8"));
7989
8439
  if (!pkg._gitSource) {
7990
8440
  pkg._gitSource = { url: gitUrl };
7991
8441
  writeFileSync7(pkgPath, JSON.stringify(pkg, null, 2));
@@ -8005,8 +8455,8 @@ async function installFromGit(gitUrl, name, targetDir) {
8005
8455
  }
8006
8456
 
8007
8457
  // src/plugin/install-sources/url.ts
8008
- import { existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync8, rmSync as rmSync4, cpSync as cpSync4 } from "fs";
8009
- import { resolve as resolve6, join as join9, basename } from "path";
8458
+ import { existsSync as existsSync10, mkdirSync as mkdirSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync8, rmSync as rmSync4, cpSync as cpSync4 } from "fs";
8459
+ import { resolve as resolve6, join as join11, basename } from "path";
8010
8460
  import { tmpdir as tmpdir3 } from "os";
8011
8461
  import {
8012
8462
  downloadToFile as downloadToFile2,
@@ -8016,14 +8466,14 @@ import {
8016
8466
  safeCleanup as safeCleanup4
8017
8467
  } from "@dyyz1993/xcli-core";
8018
8468
  async function installFromUrl(url, name, targetDir) {
8019
- const tmpDir = join9(tmpdir3(), `xbrowser-url-${Date.now()}`);
8469
+ const tmpDir = join11(tmpdir3(), `xbrowser-url-${Date.now()}`);
8020
8470
  mkdirSync6(tmpDir, { recursive: true });
8021
8471
  let warnings = [];
8022
8472
  try {
8023
8473
  const fileName = basename(new URL(url).pathname) || "plugin.tar.gz";
8024
- const tarballPath = join9(tmpDir, fileName);
8474
+ const tarballPath = join11(tmpDir, fileName);
8025
8475
  await downloadToFile2(url, tarballPath);
8026
- const extractDir = join9(tmpDir, "extracted");
8476
+ const extractDir = join11(tmpDir, "extracted");
8027
8477
  extractTarGz2(tarballPath, extractDir);
8028
8478
  flattenPackageRoot2(extractDir);
8029
8479
  const verify = verifyPlugin4(extractDir, { metadataField: "xbrowser" });
@@ -8031,13 +8481,13 @@ async function installFromUrl(url, name, targetDir) {
8031
8481
  if (!verify.valid) {
8032
8482
  throw new Error(`Invalid plugin from URL: ${verify.error}`);
8033
8483
  }
8034
- if (existsSync8(targetDir)) {
8484
+ if (existsSync10(targetDir)) {
8035
8485
  rmSync4(targetDir, { recursive: true, force: true });
8036
8486
  }
8037
8487
  cpSync4(extractDir, targetDir, { recursive: true, force: true });
8038
8488
  const pkgPath = resolve6(targetDir, "package.json");
8039
- if (existsSync8(pkgPath)) {
8040
- const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
8489
+ if (existsSync10(pkgPath)) {
8490
+ const pkg = JSON.parse(readFileSync8(pkgPath, "utf-8"));
8041
8491
  if (!pkg._urlSource) {
8042
8492
  pkg._urlSource = { url };
8043
8493
  writeFileSync8(pkgPath, JSON.stringify(pkg, null, 2));
@@ -8058,14 +8508,14 @@ async function installFromUrl(url, name, targetDir) {
8058
8508
 
8059
8509
  // src/plugin/install-sources/marketplace.ts
8060
8510
  import {
8061
- existsSync as existsSync9,
8511
+ existsSync as existsSync11,
8062
8512
  mkdirSync as mkdirSync7,
8063
8513
  writeFileSync as writeFileSync9,
8064
- readFileSync as readFileSync7,
8514
+ readFileSync as readFileSync9,
8065
8515
  rmSync as rmSync5,
8066
8516
  cpSync as cpSync5
8067
8517
  } from "fs";
8068
- import { resolve as resolve7, join as join10, dirname as dirname3 } from "path";
8518
+ import { resolve as resolve7, join as join12, dirname as dirname3 } from "path";
8069
8519
  import { tmpdir as tmpdir4 } from "os";
8070
8520
  import { gunzipSync } from "zlib";
8071
8521
  import {
@@ -8090,11 +8540,11 @@ async function installFromMarketplace(pluginsDir, slug, options) {
8090
8540
  const plugin = detailData.data;
8091
8541
  const name = options?.name || String(plugin.slug || slug);
8092
8542
  const targetDir = resolve7(pluginsDir, name);
8093
- if (existsSync9(targetDir) && !options?.force) {
8543
+ if (existsSync11(targetDir) && !options?.force) {
8094
8544
  throw new Error(`Plugin "${name}" already exists. Use --force to overwrite.`);
8095
8545
  }
8096
8546
  mkdirSync7(targetDir, { recursive: true });
8097
- const tmpDir = join10(tmpdir4(), `xbrowser-marketplace-${Date.now()}`);
8547
+ const tmpDir = join12(tmpdir4(), `xbrowser-marketplace-${Date.now()}`);
8098
8548
  mkdirSync7(tmpDir, { recursive: true });
8099
8549
  const realSlug = String(plugin.slug || slug);
8100
8550
  try {
@@ -8125,7 +8575,7 @@ function isManifestArray(data) {
8125
8575
  return Array.isArray(data) && data.length > 0 && typeof data[0].path === "string" && typeof data[0].content === "string";
8126
8576
  }
8127
8577
  function extractManifestToDir(manifest, targetDir) {
8128
- if (existsSync9(targetDir)) {
8578
+ if (existsSync11(targetDir)) {
8129
8579
  rmSync5(targetDir, { recursive: true, force: true });
8130
8580
  }
8131
8581
  mkdirSync7(targetDir, { recursive: true });
@@ -8157,18 +8607,18 @@ async function downloadAndExtractMarketplaceTarball(baseUrl, slug, tmpDir, targe
8157
8607
  }
8158
8608
  if (tarballRes.status === 302 || tarballRes.headers.get("location")) {
8159
8609
  const redirectUrl = tarballRes.headers.get("location");
8160
- const tarballPath = join10(tmpDir, `${slug}.tar.gz`);
8610
+ const tarballPath = join12(tmpDir, `${slug}.tar.gz`);
8161
8611
  await downloadToFile3(redirectUrl, tarballPath);
8162
- const buffer = readFileSync7(tarballPath);
8612
+ const buffer = readFileSync9(tarballPath);
8163
8613
  const manifest = tryParseAsGzippedManifest(buffer);
8164
8614
  if (manifest) {
8165
8615
  extractManifestToDir(manifest, targetDir);
8166
8616
  return;
8167
8617
  }
8168
- const extractDir = join10(tmpDir, "extracted");
8618
+ const extractDir = join12(tmpDir, "extracted");
8169
8619
  extractTarGz3(tarballPath, extractDir);
8170
8620
  flattenPackageRoot3(extractDir);
8171
- if (existsSync9(targetDir)) {
8621
+ if (existsSync11(targetDir)) {
8172
8622
  rmSync5(targetDir, { recursive: true, force: true });
8173
8623
  }
8174
8624
  cpSync5(extractDir, targetDir, { recursive: true, force: true });
@@ -8179,13 +8629,13 @@ async function downloadAndExtractMarketplaceTarball(baseUrl, slug, tmpDir, targe
8179
8629
  extractManifestToDir(manifest, targetDir);
8180
8630
  return;
8181
8631
  }
8182
- const tarballPath = join10(tmpDir, `${slug}.tar.gz`);
8632
+ const tarballPath = join12(tmpDir, `${slug}.tar.gz`);
8183
8633
  writeFileSync9(tarballPath, buffer);
8184
8634
  try {
8185
- const extractDir = join10(tmpDir, "extracted");
8635
+ const extractDir = join12(tmpDir, "extracted");
8186
8636
  extractTarGz3(tarballPath, extractDir);
8187
8637
  flattenPackageRoot3(extractDir);
8188
- if (existsSync9(targetDir)) {
8638
+ if (existsSync11(targetDir)) {
8189
8639
  rmSync5(targetDir, { recursive: true, force: true });
8190
8640
  }
8191
8641
  cpSync5(extractDir, targetDir, { recursive: true, force: true });
@@ -8221,11 +8671,11 @@ function writeMarketplacePackageJson(plugin, slug, name, baseUrl, targetDir) {
8221
8671
  }
8222
8672
  };
8223
8673
  const pkgPath = resolve7(targetDir, "package.json");
8224
- if (!existsSync9(pkgPath)) {
8674
+ if (!existsSync11(pkgPath)) {
8225
8675
  writeFileSync9(pkgPath, JSON.stringify(packageJson, null, 2));
8226
8676
  } else {
8227
8677
  try {
8228
- const existing = JSON.parse(readFileSync7(pkgPath, "utf-8"));
8678
+ const existing = JSON.parse(readFileSync9(pkgPath, "utf-8"));
8229
8679
  const merged = {
8230
8680
  ...existing,
8231
8681
  xbrowser: { ...existing.xbrowser, ...packageJson.xbrowser },
@@ -8238,7 +8688,7 @@ function writeMarketplacePackageJson(plugin, slug, name, baseUrl, targetDir) {
8238
8688
  }
8239
8689
  }
8240
8690
  function ensureIndexFile(plugin, name, targetDir) {
8241
- if (!existsSync9(resolve7(targetDir, "index.ts")) && !existsSync9(resolve7(targetDir, "index.js"))) {
8691
+ if (!existsSync11(resolve7(targetDir, "index.ts")) && !existsSync11(resolve7(targetDir, "index.js"))) {
8242
8692
  const commands = plugin.commands || [];
8243
8693
  const commandHandlers = commands.length > 0 ? commands.map((cmd) => {
8244
8694
  return [
@@ -8292,7 +8742,7 @@ var PluginInstaller = class {
8292
8742
  const type = this.detectSourceType(source);
8293
8743
  const name = options?.name || this.deriveName(source, type);
8294
8744
  const targetDir = resolve8(this.pluginsDir, name);
8295
- if (existsSync10(targetDir) && !options?.force) {
8745
+ if (existsSync12(targetDir) && !options?.force) {
8296
8746
  throw new Error(`Plugin "${name}" already exists. Use --force to overwrite.`);
8297
8747
  }
8298
8748
  mkdirSync8(targetDir, { recursive: true });
@@ -8335,10 +8785,10 @@ var PluginInstaller = class {
8335
8785
  */
8336
8786
  async fixSharedDeps(pluginDir) {
8337
8787
  const indexPath = resolve8(pluginDir, "index.ts");
8338
- if (!existsSync10(indexPath)) return;
8788
+ if (!existsSync12(indexPath)) return;
8339
8789
  let content;
8340
8790
  try {
8341
- content = readFileSync8(indexPath, "utf8");
8791
+ content = readFileSync10(indexPath, "utf8");
8342
8792
  } catch {
8343
8793
  return;
8344
8794
  }
@@ -8353,7 +8803,7 @@ var PluginInstaller = class {
8353
8803
  const toCopy = [];
8354
8804
  for (const file of missingFiles) {
8355
8805
  const targetPath = resolve8(sharedDir, file);
8356
- if (!existsSync10(targetPath)) {
8806
+ if (!existsSync12(targetPath)) {
8357
8807
  toCopy.push(file);
8358
8808
  }
8359
8809
  }
@@ -8364,7 +8814,7 @@ var PluginInstaller = class {
8364
8814
  ];
8365
8815
  let sourceSharedDir = null;
8366
8816
  for (const dir of repoSharedDirs) {
8367
- if (existsSync10(dir)) {
8817
+ if (existsSync12(dir)) {
8368
8818
  sourceSharedDir = dir;
8369
8819
  break;
8370
8820
  }
@@ -8381,7 +8831,7 @@ var PluginInstaller = class {
8381
8831
  const content2 = await resp.text();
8382
8832
  const dst = resolve8(sharedDir, file);
8383
8833
  const dstDir = dirname4(dst);
8384
- if (!existsSync10(dstDir)) mkdirSync8(dstDir, { recursive: true });
8834
+ if (!existsSync12(dstDir)) mkdirSync8(dstDir, { recursive: true });
8385
8835
  writeFileSync10(dst, content2, "utf-8");
8386
8836
  console.log(`\u2705 Downloaded shared/${file} from GitHub`);
8387
8837
  } else {
@@ -8403,7 +8853,7 @@ var PluginInstaller = class {
8403
8853
  for (const file of toCopy) {
8404
8854
  const src = resolve8(sourceSharedDir, file);
8405
8855
  const dst = resolve8(sharedDir, file);
8406
- if (existsSync10(src)) {
8856
+ if (existsSync12(src)) {
8407
8857
  try {
8408
8858
  cpSync6(dirname4(src), dirname4(dst), { recursive: true });
8409
8859
  console.log(`\u2705 Copied shared/${file} for plugin "${basename2(pluginDir)}"`);
@@ -8453,7 +8903,7 @@ var PluginInstaller = class {
8453
8903
  */
8454
8904
  async uninstall(name) {
8455
8905
  const targetDir = resolve8(this.pluginsDir, name);
8456
- if (!existsSync10(targetDir)) {
8906
+ if (!existsSync12(targetDir)) {
8457
8907
  throw new Error(`Plugin "${name}" not found`);
8458
8908
  }
8459
8909
  rmSync6(targetDir, { recursive: true, force: true });
@@ -8464,7 +8914,7 @@ var PluginInstaller = class {
8464
8914
  * @returns Array of installed plugin information.
8465
8915
  */
8466
8916
  async list(_options) {
8467
- if (!existsSync10(this.pluginsDir)) return [];
8917
+ if (!existsSync12(this.pluginsDir)) return [];
8468
8918
  const entries = readdirSync2(this.pluginsDir, { withFileTypes: true });
8469
8919
  const plugins = [];
8470
8920
  for (const entry of entries) {
@@ -8472,7 +8922,7 @@ var PluginInstaller = class {
8472
8922
  const pluginPath = resolve8(this.pluginsDir, entry.name);
8473
8923
  const indexPath = resolve8(pluginPath, "index.ts");
8474
8924
  const indexJsPath = resolve8(pluginPath, "index.js");
8475
- if (!existsSync10(indexPath) && !existsSync10(indexJsPath)) continue;
8925
+ if (!existsSync12(indexPath) && !existsSync12(indexJsPath)) continue;
8476
8926
  const metadata = PluginMetadataParser.parseFromPackageJson(pluginPath);
8477
8927
  let source = "local";
8478
8928
  const pkg = readJsonFile(resolve8(pluginPath, "package.json"), {});
@@ -8499,10 +8949,10 @@ var PluginInstaller = class {
8499
8949
  }
8500
8950
  if (source.startsWith("file://")) {
8501
8951
  const filePath = decodeURIComponent(new URL(source).pathname);
8502
- if (existsSync10(filePath)) return "url";
8952
+ if (existsSync12(filePath)) return "url";
8503
8953
  }
8504
8954
  if (source.endsWith(".git") || source.includes("github.com/")) return "git";
8505
- if (existsSync10(resolve8(source))) return "local";
8955
+ if (existsSync12(resolve8(source))) return "local";
8506
8956
  return "npm";
8507
8957
  }
8508
8958
  deriveName(source, type) {
@@ -9431,7 +9881,7 @@ var previewBuiltin = {
9431
9881
 
9432
9882
  // src/builtins/knowledge.ts
9433
9883
  init_site_knowledge();
9434
- import { existsSync as existsSync12 } from "fs";
9884
+ import { existsSync as existsSync14 } from "fs";
9435
9885
  var knowledgeBuiltin = {
9436
9886
  name: "knowledge",
9437
9887
  description: "View LLM-readable site knowledge base (selectors, forms, APIs)",
@@ -9610,8 +10060,8 @@ var knowledgeBuiltin = {
9610
10060
  }
9611
10061
  const mdPath = getKnowledgePath(domain, "md");
9612
10062
  const jsonPath = getKnowledgePath(domain, "json");
9613
- console.log(`Markdown: ${mdPath} (${existsSync12(mdPath) ? "exists" : "not found"})`);
9614
- console.log(`JSON: ${jsonPath} (${existsSync12(jsonPath) ? "exists" : "not found"})`);
10063
+ console.log(`Markdown: ${mdPath} (${existsSync14(mdPath) ? "exists" : "not found"})`);
10064
+ console.log(`JSON: ${jsonPath} (${existsSync14(jsonPath) ? "exists" : "not found"})`);
9615
10065
  return;
9616
10066
  }
9617
10067
  console.error(`Unknown subcommand: ${subcommand}`);
@@ -9888,6 +10338,12 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
9888
10338
  output: options.output || options.o || args[0]
9889
10339
  };
9890
10340
  break;
10341
+ case "ua": {
10342
+ cmdName = "ua";
10343
+ const val = args.join(" ").trim() || options.value || "";
10344
+ params = { value: val };
10345
+ break;
10346
+ }
9891
10347
  case "eval": {
9892
10348
  cmdName = "eval";
9893
10349
  const frameVal = options.frame || void 0;
@@ -10196,14 +10652,14 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
10196
10652
 
10197
10653
  // src/cli/session-routes.ts
10198
10654
  import { homedir as homedir8 } from "os";
10199
- import { join as join12 } from "path";
10655
+ import { join as join14 } from "path";
10200
10656
  import { readdirSync as readdirSync3, rmSync as rmSync7 } from "fs";
10201
10657
  function cleanSessionFiles() {
10202
- const dir = join12(homedir8(), ".xbrowser", "sessions");
10658
+ const dir = join14(homedir8(), ".xbrowser", "sessions");
10203
10659
  let count = 0;
10204
10660
  try {
10205
10661
  for (const entry of readdirSync3(dir, { withFileTypes: true })) {
10206
- const p = join12(dir, entry.name);
10662
+ const p = join14(dir, entry.name);
10207
10663
  rmSync7(p, { recursive: true, force: true });
10208
10664
  count++;
10209
10665
  }
@@ -10607,7 +11063,7 @@ async function handlePlugin(args, options, mode) {
10607
11063
  } catch {
10608
11064
  }
10609
11065
  try {
10610
- const { daemonPing } = await import("./daemon-client-Y2YSZCGK.js");
11066
+ const { daemonPing } = await import("./daemon-client-HEGAXWGK.js");
10611
11067
  if (await daemonPing()) {
10612
11068
  await fetch("http://localhost:9224/rpc", {
10613
11069
  method: "POST",
@@ -10757,7 +11213,7 @@ function handleDaemon(args, options, mode) {
10757
11213
  }
10758
11214
 
10759
11215
  // src/cli/record-routes.ts
10760
- import { existsSync as existsSync13 } from "fs";
11216
+ import { existsSync as existsSync15 } from "fs";
10761
11217
  async function handleRecord(args, options, mode) {
10762
11218
  const sub = args[0];
10763
11219
  switch (sub) {
@@ -10788,9 +11244,9 @@ async function handleRecord(args, options, mode) {
10788
11244
  outputError(String(result.error || "Failed to stop recording"));
10789
11245
  return;
10790
11246
  }
10791
- if (output && !existsSync13(output)) {
11247
+ if (output && !existsSync15(output)) {
10792
11248
  const defaultRecording = SessionRecorder.getRecordingsDir(sessionName) + "/recording.json";
10793
- if (existsSync13(defaultRecording)) {
11249
+ if (existsSync15(defaultRecording)) {
10794
11250
  try {
10795
11251
  const { mkdirSync: mkdirSync10, copyFileSync: copyFileSync2 } = await import("fs");
10796
11252
  const { dirname: pathDirname } = await import("path");
@@ -11176,7 +11632,7 @@ async function handleGeneratePlugin(sessionName, pluginName, outputDir) {
11176
11632
  const { SessionRecorder: SessionRecorder2 } = await import("./session-recorder-SLDBENVF.js");
11177
11633
  const { readSiteKnowledge: readSiteKnowledge2, toMarkdown } = await import("./site-knowledge-SYC6VCDB.js");
11178
11634
  const { mkdirSync: mkdirSync10, writeFileSync: writeFileSync12 } = await import("fs");
11179
- const { join: join13 } = await import("path");
11635
+ const { join: join15 } = await import("path");
11180
11636
  const data = SessionRecorder2.readData(sessionName);
11181
11637
  if (!data) {
11182
11638
  outputError(`No recording found for session "${sessionName}". Run \`xbrowser record stop --session ${sessionName}\` first.`);
@@ -11188,14 +11644,14 @@ async function handleGeneratePlugin(sessionName, pluginName, outputDir) {
11188
11644
  } catch {
11189
11645
  }
11190
11646
  const finalPluginName = pluginName || domain.split(".")[0] || "my-site";
11191
- const finalOutputDir = outputDir || join13(process.cwd(), ".xcli", "plugins", finalPluginName);
11647
+ const finalOutputDir = outputDir || join15(process.cwd(), ".xcli", "plugins", finalPluginName);
11192
11648
  const knowledge = readSiteKnowledge2(domain);
11193
11649
  const knowledgeMd = knowledge ? toMarkdown(knowledge) : "";
11194
11650
  const pluginCode = generatePluginCode(finalPluginName, domain, data, knowledgeMd);
11195
- mkdirSync10(join13(finalOutputDir), { recursive: true });
11196
- writeFileSync12(join13(finalOutputDir, "index.ts"), pluginCode, "utf-8");
11651
+ mkdirSync10(join15(finalOutputDir), { recursive: true });
11652
+ writeFileSync12(join15(finalOutputDir, "index.ts"), pluginCode, "utf-8");
11197
11653
  if (knowledgeMd) {
11198
- writeFileSync12(join13(finalOutputDir, "SITE_KNOWLEDGE.md"), knowledgeMd, "utf-8");
11654
+ writeFileSync12(join15(finalOutputDir, "SITE_KNOWLEDGE.md"), knowledgeMd, "utf-8");
11199
11655
  }
11200
11656
  console.log("");
11201
11657
  console.log("=== Plugin Generated ===");
@@ -11738,7 +12194,7 @@ async function handleNetCommand(args, options, mode, sessionName) {
11738
12194
 
11739
12195
  // src/cli/test-routes.ts
11740
12196
  import { execSync as execSync3 } from "child_process";
11741
- import { readFileSync as readFileSync9 } from "fs";
12197
+ import { readFileSync as readFileSync11 } from "fs";
11742
12198
  import { resolve as resolve9 } from "path";
11743
12199
  function findPluginPath(plugin) {
11744
12200
  const candidates = [
@@ -11747,7 +12203,7 @@ function findPluginPath(plugin) {
11747
12203
  ];
11748
12204
  for (const p of candidates) {
11749
12205
  try {
11750
- readFileSync9(p, "utf-8");
12206
+ readFileSync11(p, "utf-8");
11751
12207
  return p;
11752
12208
  } catch {
11753
12209
  }
@@ -11758,7 +12214,7 @@ function extractSchema(plugin, command) {
11758
12214
  const pluginPath = findPluginPath(plugin);
11759
12215
  let src;
11760
12216
  try {
11761
- src = readFileSync9(pluginPath, "utf-8");
12217
+ src = readFileSync11(pluginPath, "utf-8");
11762
12218
  } catch {
11763
12219
  return null;
11764
12220
  }
@@ -12681,7 +13137,7 @@ function extractSessionNameFromArgv(argv) {
12681
13137
  async function handleEvalMode(argv) {
12682
13138
  const evalCommands = parseEvalFlags(argv);
12683
13139
  if (evalCommands.length === 0) return;
12684
- const chain = evalCommands.join(" ; ");
13140
+ const chain = evalCommands.map((script) => `eval --script-b64 ${Buffer.from(script, "utf8").toString("base64url")}`).join(" ; ");
12685
13141
  const cdpEndpoint = extractCdpFromArgv(argv);
12686
13142
  const sessionArgIdx = argv.indexOf("--session");
12687
13143
  const sessionName = sessionArgIdx >= 0 && argv[sessionArgIdx + 1] ? argv[sessionArgIdx + 1] : process.env.XBROWSER_SESSION || "default";
@@ -12689,6 +13145,51 @@ async function handleEvalMode(argv) {
12689
13145
  printChainResult(chainResult);
12690
13146
  if (!chainResult.success) throw new Error("Command failed");
12691
13147
  }
13148
+ function findEvalStart(argv) {
13149
+ const flagsWithValue = /* @__PURE__ */ new Set(["--session", "--cdp"]);
13150
+ for (let i = 0; i < argv.length; i++) {
13151
+ const a = argv[i];
13152
+ if (a === "-e" || a === "--eval") return -1;
13153
+ if (a.startsWith("--")) {
13154
+ if (flagsWithValue.has(a)) {
13155
+ i++;
13156
+ continue;
13157
+ }
13158
+ continue;
13159
+ }
13160
+ return a === "eval" ? i : -1;
13161
+ }
13162
+ return -1;
13163
+ }
13164
+ async function handleEvalScript(parts, argv) {
13165
+ const cdpEndpoint = extractCdpFromArgv(argv);
13166
+ const sessionName = extractSessionNameFromArgv(argv);
13167
+ let frame;
13168
+ const scriptLines = [];
13169
+ for (let i = 0; i < parts.length; i++) {
13170
+ const part = parts[i];
13171
+ if (part === "--frame" && parts[i + 1]) {
13172
+ frame = parts[i + 1];
13173
+ i++;
13174
+ continue;
13175
+ }
13176
+ if (part === "--session" || part === "--cdp") {
13177
+ i++;
13178
+ continue;
13179
+ }
13180
+ scriptLines.push(part);
13181
+ }
13182
+ const script = scriptLines.join("\n");
13183
+ if (!script.trim()) {
13184
+ console.error("Error: eval requires a script (usage: xbrowser eval <js...>)");
13185
+ throw new Error("Command failed");
13186
+ }
13187
+ const b64 = Buffer.from(script, "utf8").toString("base64url");
13188
+ const chain = frame ? `eval --frame ${frame} --script-b64 ${b64}` : `eval --script-b64 ${b64}`;
13189
+ const chainResult = await executeChain(chain, { cdpEndpoint, sessionName });
13190
+ printChainResult(chainResult);
13191
+ if (!chainResult.success) throw new Error("Command failed");
13192
+ }
12692
13193
  async function handleChainInput(input, argv) {
12693
13194
  const cdpEndpoint = argv ? extractCdpFromArgv(argv) : void 0;
12694
13195
  const hasJson = argv ? argv.some((a) => a === "--json" || a.startsWith("--json=") || a.includes(" --json") || a.startsWith("--json")) || argv.includes("-j") : false;
@@ -12726,6 +13227,11 @@ async function routeCommand(argvIn, stdinCommands) {
12726
13227
  await handleStdinMode(stdinCommands, argv);
12727
13228
  return;
12728
13229
  }
13230
+ const evalStart = findEvalStart(argv);
13231
+ if (evalStart >= 0 && argv.length > evalStart + 1) {
13232
+ await handleEvalScript(argv.slice(evalStart + 1), argv);
13233
+ return;
13234
+ }
12729
13235
  if (parseEvalFlags(argv).length > 0) {
12730
13236
  await handleEvalMode(argv);
12731
13237
  return;
@@ -13117,7 +13623,7 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
13117
13623
  }
13118
13624
  const needsBrowser = cmdEntry.scope === "page" || cmdEntry.scope === "browser";
13119
13625
  if (needsBrowser && !process.env.XBROWSER_DAEMON_WORKER) {
13120
- const { forwardPluginExec } = await import("./daemon-client-Y2YSZCGK.js");
13626
+ const { forwardPluginExec } = await import("./daemon-client-HEGAXWGK.js");
13121
13627
  const userTimeout = typeof params.timeout === "number" && params.timeout > 0 ? params.timeout * 1e3 + 3e4 : void 0;
13122
13628
  const result = await forwardPluginExec(`${command}.${subCommand}`, params, sessionName, cdpEndpoint, userTimeout);
13123
13629
  const resultData = result && typeof result === "object" && "data" in result ? result.data : void 0;
@@ -13157,7 +13663,7 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
13157
13663
  const targetPage = pages[cmdTabIndex];
13158
13664
  await targetPage.bringToFront().catch(() => {
13159
13665
  });
13160
- const { setActivePage: setActivePage2 } = await import("./browser-Q5APBNF6.js");
13666
+ const { setActivePage: setActivePage2 } = await import("./browser-OMGJMSBB.js");
13161
13667
  setActivePage2(session, targetPage);
13162
13668
  }
13163
13669
  }
@@ -13433,7 +13939,7 @@ async function main() {
13433
13939
  const command = process.argv[2];
13434
13940
  const isLongRunning = command === "preview" || command === "serve";
13435
13941
  if (!isLongRunning) {
13436
- const { ensureProcessCanExit } = await import("./browser-Q5APBNF6.js");
13942
+ const { ensureProcessCanExit } = await import("./browser-OMGJMSBB.js");
13437
13943
  await ensureProcessCanExit().catch(() => {
13438
13944
  });
13439
13945
  process.exit(process.exitCode || exitCode);