@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/index.js CHANGED
@@ -25,7 +25,7 @@ import {
25
25
  forwardSessionList,
26
26
  forwardViewerCheckSelector,
27
27
  version
28
- } from "./chunk-WHPBNUKB.js";
28
+ } from "./chunk-SH6OTPXN.js";
29
29
  import {
30
30
  buildViewerUrl
31
31
  } from "./chunk-3OD76SUE.js";
@@ -41,8 +41,9 @@ import {
41
41
  getMarketplaceUrl,
42
42
  loadConfig,
43
43
  resolveNpmPackageWithFallback,
44
+ resolveScreenshotsDir,
44
45
  setConfigValue
45
- } from "./chunk-DWDXEGVK.js";
46
+ } from "./chunk-NKW4A74J.js";
46
47
  import {
47
48
  getDaemonConfig,
48
49
  getDaemonProcessStatus,
@@ -91,15 +92,17 @@ import {
91
92
  resolveLaunchOpts,
92
93
  saveSessionDiskMeta,
93
94
  setActivePage
94
- } from "./chunk-XKQVUFUS.js";
95
- import "./chunk-7OFM755Z.js";
95
+ } from "./chunk-CW7L53JE.js";
96
+ import "./chunk-R2POF2GP.js";
96
97
  import "./chunk-TNEN6VQ2.js";
97
98
  import {
98
99
  errMsg
99
100
  } from "./chunk-GDKLH7ZY.js";
100
101
  import {
101
102
  CDPInterceptorProxy,
102
- advise,
103
+ advise
104
+ } from "./chunk-A6LPGFAL.js";
105
+ import {
103
106
  automationSignalsRule,
104
107
  createRuleEngine,
105
108
  domMutationRule,
@@ -110,15 +113,15 @@ import {
110
113
  mouseTrajectoryRule,
111
114
  networkAnomalyRule,
112
115
  pageLifecycleRule
113
- } from "./chunk-ABXMBNQ6.js";
116
+ } from "./chunk-H2A5JUK5.js";
114
117
  import {
115
118
  __require
116
119
  } from "./chunk-KFQGP6VL.js";
117
120
 
118
121
  // src/executor.ts
119
122
  import {
120
- ok as ok25,
121
- fail as fail11,
123
+ ok as ok28,
124
+ fail as fail13,
122
125
  isCommandResult,
123
126
  CompositeStorage as CompositeStorage2,
124
127
  TipCollector as TipCollector2,
@@ -994,29 +997,441 @@ var mouseCommand = registerCommand({
994
997
  }
995
998
  });
996
999
 
997
- // src/commands/evaluate.ts
1000
+ // src/commands/find-visual.ts
998
1001
  import { z as z7 } from "zod";
999
- import { ok as ok7, normalizeTips as normalizeTips2 } from "@dyyz1993/xcli-core";
1002
+ import { ok as ok7, fail as fail2 } from "@dyyz1993/xcli-core";
1003
+ import { readFileSync, existsSync } from "fs";
1004
+ import { homedir as homedir2 } from "os";
1005
+ import { join as join2 } from "path";
1006
+ function loadVLMCredentials() {
1007
+ const candidates = [
1008
+ process.env.XBROWSER_VLM_CONFIG,
1009
+ join2(homedir2(), ".zcode", "v2", "config.json")
1010
+ ].filter(Boolean);
1011
+ for (const file of candidates) {
1012
+ try {
1013
+ if (!existsSync(file)) continue;
1014
+ const cfg = JSON.parse(readFileSync(file, "utf8"));
1015
+ const provider = Object.values(cfg.provider ?? {})[0];
1016
+ if (provider?.options?.apiKey && provider.options.baseURL) {
1017
+ const models = Object.keys(provider.models ?? {});
1018
+ const vl = models.find((m) => /vl|vision/i.test(m));
1019
+ const flash = models.find((m) => /flash/i.test(m) && /5\.?3/i.test(m));
1020
+ return {
1021
+ apiKey: provider.options.apiKey,
1022
+ baseURL: provider.options.baseURL.replace(/\/$/, ""),
1023
+ model: process.env.XBROWSER_VLM_MODEL || vl || flash || models.find((m) => /5\.[23]/.test(m)) || models[0]
1024
+ };
1025
+ }
1026
+ } catch {
1027
+ }
1028
+ }
1029
+ return null;
1030
+ }
1031
+ async function locateByVLM(creds, imageBase64, target, feedback) {
1032
+ 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}"""
1033
+
1034
+ \u4E25\u683C\u53EA\u8F93\u51FA\u4E00\u4E2A JSON \u5BF9\u8C61\uFF08\u65E0 markdown\u3001\u65E0\u89E3\u91CA\uFF09\uFF1A
1035
+ {"label":"<\u5143\u7D20\u540D>","box":{"x":<\u5DE6\u4E0Ax>,"y":<\u5DE6\u4E0Ay>,"width":<\u5BBD>,"height":<\u9AD8>},"confidence":"high|medium|low"}
1036
+ \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
1037
+ ${feedback ? `\u6CE8\u610F\uFF1A${feedback}` : ""}`;
1038
+ const resp = await fetch(creds.baseURL + "/v1/messages", {
1039
+ method: "POST",
1040
+ headers: { "content-type": "application/json", "x-api-key": creds.apiKey, "anthropic-version": "2023-06-01" },
1041
+ body: JSON.stringify({
1042
+ model: creds.model,
1043
+ max_tokens: 2e3,
1044
+ // thinking 模型的思考块也计入配额,300 会在 text 前截断
1045
+ messages: [{
1046
+ role: "user",
1047
+ content: [
1048
+ { type: "image", source: { type: "base64", media_type: "image/png", data: imageBase64 } },
1049
+ { type: "text", text: prompt }
1050
+ ]
1051
+ }]
1052
+ }),
1053
+ signal: AbortSignal.timeout(6e4)
1054
+ });
1055
+ if (!resp.ok) throw new Error(`VLM HTTP ${resp.status}`);
1056
+ const data = await resp.json();
1057
+ const text = (data.content ?? []).filter((c) => c.type === "text").map((c) => c.text ?? "").join("");
1058
+ const m = text.match(/\{[\s\S]*\}/);
1059
+ if (!m) {
1060
+ console.error("[find-visual] VLM raw (no JSON):", text.substring(0, 200));
1061
+ return null;
1062
+ }
1063
+ const hit = JSON.parse(m[0]);
1064
+ if (!hit.label || !hit.box) return null;
1065
+ return hit;
1066
+ }
1067
+ var findVisualCommand = registerCommand({
1068
+ name: "find-visual",
1069
+ description: "Visual element location: screenshot \u2192 VLM \u2192 page coordinates (optionally click)",
1070
+ scope: "page",
1071
+ parameters: z7.object({
1072
+ 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'),
1073
+ click: z7.boolean().optional().describe("\u627E\u5230\u540E\u7ACB\u5373\u70B9\u51FB\uFF08\u771F\u5B9E\u9F20\u6807\u4E8B\u4EF6\uFF09"),
1074
+ threshold: z7.coerce.number().optional().describe("\u7F6E\u4FE1\u5EA6\u9608\u503C\uFF0C\u4F4E\u4E8E\u5219\u62A5 not-found\uFF08\u9ED8\u8BA4 0\uFF09"),
1075
+ 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")
1076
+ }),
1077
+ result: z7.object({
1078
+ found: z7.boolean(),
1079
+ element: z7.string(),
1080
+ box: z7.unknown().optional(),
1081
+ center: z7.unknown().optional(),
1082
+ confidence: z7.string().optional(),
1083
+ model: z7.string().optional(),
1084
+ clicked: z7.boolean().optional(),
1085
+ screenshot: z7.string().optional()
1086
+ }),
1087
+ handler: async (p, ctx) => {
1088
+ const page = ctx.page;
1089
+ const creds = loadVLMCredentials();
1090
+ if (!creds) {
1091
+ 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", [
1092
+ "\u4E5F\u53EF\u7528 XBROWSER_VLM_MODEL \u6307\u5B9A\u6A21\u578B\uFF08\u9ED8\u8BA4\u81EA\u52A8\u9009 VL/\u65D7\u8230\u6A21\u578B\uFF09"
1093
+ ]);
1094
+ }
1095
+ const buffer = await page.screenshot({ type: "png" });
1096
+ const imageBase64 = buffer.toString("base64");
1097
+ const imgW = buffer.readUInt32BE(16);
1098
+ const imgH = buffer.readUInt32BE(20);
1099
+ let hit = null;
1100
+ let feedback = "";
1101
+ try {
1102
+ for (let attempt = 0; attempt < 3 && !hit; attempt++) {
1103
+ const candidate = await locateByVLM(creds, imageBase64, p.element, feedback);
1104
+ if (!candidate) break;
1105
+ const b = candidate.box;
1106
+ const inBounds = b.x >= 0 && b.y >= 0 && b.x + b.width <= imgW + 2 && b.y + b.height <= imgH + 2;
1107
+ const sane = b.width > 2 && b.height > 2;
1108
+ if (inBounds && sane) {
1109
+ hit = candidate;
1110
+ break;
1111
+ }
1112
+ 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`;
1113
+ }
1114
+ } catch (e) {
1115
+ return fail2(`VLM \u8C03\u7528\u5931\u8D25: ${e instanceof Error ? e.message : String(e)}`);
1116
+ }
1117
+ if (!hit) {
1118
+ return ok7({ found: false, element: p.element, model: creds.model }, [
1119
+ `\u622A\u56FE\u4E2D\u672A\u627E\u5230: ${p.element}`
1120
+ ]);
1121
+ }
1122
+ if (p.zoom !== false && hit) {
1123
+ try {
1124
+ const pad = Math.max(20, Math.round(Math.max(hit.box.width, hit.box.height) * 0.5));
1125
+ const clip = {
1126
+ x: Math.max(0, hit.box.x - pad),
1127
+ y: Math.max(0, hit.box.y - pad),
1128
+ width: Math.min(imgW - Math.max(0, hit.box.x - pad), hit.box.width + pad * 2),
1129
+ height: Math.min(imgH - Math.max(0, hit.box.y - pad), hit.box.height + pad * 2)
1130
+ };
1131
+ if (clip.width > 8 && clip.height > 8) {
1132
+ const zoomBuf = await page.screenshot({ type: "png", clip });
1133
+ const zoomB64 = zoomBuf.toString("base64");
1134
+ const zoomW = zoomBuf.readUInt32BE(16);
1135
+ const zoomH = zoomBuf.readUInt32BE(20);
1136
+ const refined = await locateByVLM(creds, zoomB64, p.element);
1137
+ if (refined) {
1138
+ const rb = refined.box;
1139
+ 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;
1140
+ if (inZoom) {
1141
+ hit = {
1142
+ label: refined.label || hit.label,
1143
+ box: {
1144
+ x: clip.x + rb.x,
1145
+ y: clip.y + rb.y,
1146
+ width: rb.width,
1147
+ height: rb.height
1148
+ },
1149
+ confidence: refined.confidence === "low" ? hit.confidence : refined.confidence
1150
+ };
1151
+ }
1152
+ }
1153
+ }
1154
+ } catch {
1155
+ }
1156
+ }
1157
+ if (p.threshold !== void 0) {
1158
+ const rank = { high: 3, medium: 2, low: 1 };
1159
+ const thresholdName = p.threshold >= 3 ? "high" : p.threshold >= 2 ? "medium" : "low";
1160
+ if ((rank[hit.confidence ?? "low"] ?? 1) < (rank[thresholdName] ?? 1)) {
1161
+ return ok7({ found: false, element: p.element, confidence: hit.confidence, box: hit.box, model: creds.model }, [
1162
+ `\u7F6E\u4FE1\u5EA6 ${hit.confidence} \u4F4E\u4E8E\u9608\u503C ${p.threshold}`
1163
+ ]);
1164
+ }
1165
+ }
1166
+ const viewport = await page.evaluate("JSON.stringify({w: window.innerWidth, h: window.innerHeight})").then((v) => JSON.parse(String(v))).catch(() => ({ w: 1280, h: 800 }));
1167
+ const pngW = buffer.readUInt32BE(16);
1168
+ const pngH = buffer.readUInt32BE(20);
1169
+ const scaleX = viewport.w / pngW;
1170
+ const scaleY = viewport.h / pngH;
1171
+ const center = {
1172
+ x: Math.round((hit.box.x + hit.box.width / 2) * scaleX),
1173
+ y: Math.round((hit.box.y + hit.box.height / 2) * scaleY)
1174
+ };
1175
+ let clicked = false;
1176
+ if (p.click) {
1177
+ await page.mouse.click(center.x, center.y, { stealth: true });
1178
+ clicked = true;
1179
+ }
1180
+ return ok7({
1181
+ found: true,
1182
+ element: p.element,
1183
+ box: hit.box,
1184
+ center,
1185
+ confidence: hit.confidence,
1186
+ model: creds.model,
1187
+ clicked,
1188
+ screenshot: `data:image/png;base64,${imageBase64.substring(0, 64)}... (${Math.round(imageBase64.length / 1024)}KB)`
1189
+ }, [
1190
+ 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`
1191
+ ]);
1192
+ }
1193
+ });
1194
+
1195
+ // src/commands/vision-task.ts
1196
+ import { z as z8 } from "zod";
1197
+ import { ok as ok8, fail as fail3 } from "@dyyz1993/xcli-core";
1198
+ import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
1199
+ import { homedir as homedir3 } from "os";
1200
+ import { join as join3 } from "path";
1201
+ function loadVLMCredentials2() {
1202
+ const candidates = [
1203
+ process.env.XBROWSER_VLM_CONFIG,
1204
+ join3(homedir3(), ".zcode", "v2", "config.json")
1205
+ ].filter(Boolean);
1206
+ for (const file of candidates) {
1207
+ try {
1208
+ if (!existsSync2(file)) continue;
1209
+ const cfg = JSON.parse(readFileSync2(file, "utf8"));
1210
+ const provider = Object.values(cfg.provider ?? {})[0];
1211
+ if (provider?.options?.apiKey && provider.options.baseURL) {
1212
+ const models = Object.keys(provider.models ?? {});
1213
+ const vl = models.find((m) => /vl|vision/i.test(m));
1214
+ const flash = models.find((m) => /flash/i.test(m) && /5\.?3/i.test(m));
1215
+ return {
1216
+ apiKey: provider.options.apiKey,
1217
+ baseURL: provider.options.baseURL.replace(/\/$/, ""),
1218
+ model: process.env.XBROWSER_VLM_MODEL || vl || flash || models.find((m) => /5\.[23]/.test(m)) || models[0]
1219
+ };
1220
+ }
1221
+ } catch {
1222
+ }
1223
+ }
1224
+ return null;
1225
+ }
1226
+ async function vlmAsk(creds, content, maxTokens = 2e3) {
1227
+ const resp = await fetch(creds.baseURL + "/v1/messages", {
1228
+ method: "POST",
1229
+ headers: { "content-type": "application/json", "x-api-key": creds.apiKey, "anthropic-version": "2023-06-01" },
1230
+ body: JSON.stringify({ model: creds.model, max_tokens: maxTokens, messages: [{ role: "user", content }] }),
1231
+ signal: AbortSignal.timeout(9e4)
1232
+ });
1233
+ if (!resp.ok) throw new Error(`VLM HTTP ${resp.status}`);
1234
+ const data = await resp.json();
1235
+ return (data.content ?? []).filter((c) => c.type === "text").map((c) => c.text ?? "").join("");
1236
+ }
1237
+ async function vlmAskRetry(creds, content, maxTokens = 2e3) {
1238
+ let last = "";
1239
+ for (let i = 0; i < 2; i++) {
1240
+ last = await vlmAsk(creds, content, maxTokens);
1241
+ if (last.trim()) return last;
1242
+ await new Promise((r) => setTimeout(r, 1200));
1243
+ }
1244
+ return last;
1245
+ }
1246
+ function pngDims(b64) {
1247
+ const buf = Buffer.from(b64, "base64");
1248
+ return { w: buf.readUInt32BE(16), h: buf.readUInt32BE(20) };
1249
+ }
1250
+ function parseDecision(text) {
1251
+ const m = text.match(/\{[\s\S]*\}/);
1252
+ if (!m) return null;
1253
+ try {
1254
+ const j = JSON.parse(m[0]);
1255
+ if (!j.action || !j.action.type) return null;
1256
+ return j;
1257
+ } catch {
1258
+ return null;
1259
+ }
1260
+ }
1261
+ 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
1262
+
1263
+ \u4E25\u683C\u53EA\u8F93\u51FA\u4E00\u4E2A JSON\uFF08\u65E0 markdown\uFF09\uFF1A
1264
+ {"thought":"<\u7B80\u77ED\u63A8\u7406>","action":{"type":"click","x":<int>,"y":<int>}
1265
+ \u6216 {"type":"type","text":"<\u8981\u8F93\u5165\u7684\u6587\u5B57>"}\uFF08\u8F93\u5165\u524D\u987B\u5DF2\u70B9\u51FB\u805A\u7126\u8F93\u5165\u6846\uFF09
1266
+ \u6216 {"type":"key","key":"Enter"}\uFF08key \u540D\uFF1AEnter/Tab/Escape/ArrowDown\u2026\uFF09
1267
+ \u6216 {"type":"scroll","direction":"down","amount":<px>}
1268
+ \u6216 {"type":"done","summary":"<\u4EFB\u52A1\u5B8C\u6210\u6458\u8981>"}
1269
+ \u6216 {"type":"stuck","reason":"<\u65E0\u6CD5\u7EE7\u7EED\u7684\u539F\u56E0>"}}
1270
+
1271
+ \u5750\u6807\u4EE5\u622A\u56FE\u5DE6\u4E0A\u89D2\u4E3A\u539F\u70B9\u3001\u50CF\u7D20\u4E3A\u5355\u4F4D\u3002\u89C4\u5219\uFF1A
1272
+ 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
1273
+ 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
1274
+ 3. \u4EFB\u52A1\u76EE\u6807\u8FBE\u6210\u7ACB\u5373\u8F93\u51FA done\uFF1B5 \u6B65\u65E0\u8FDB\u5C55\u8F93\u51FA stuck`;
1275
+ var visionTaskCommand = registerCommand({
1276
+ name: "vision-task",
1277
+ description: "Vision-loop agent: screenshot \u2192 VLM decide \u2192 CDP act \u2192 verify, until done",
1278
+ scope: "page",
1279
+ parameters: z8.object({
1280
+ task: z8.string().describe('\u81EA\u7136\u8BED\u8A00\u4EFB\u52A1\uFF0C\u5982 "\u641C\u7D22 xbrowser \u5E76\u6253\u5F00\u7B2C\u4E00\u7BC7\u6587\u7AE0"'),
1281
+ maxSteps: z8.coerce.number().optional().describe("\u6700\u5927\u6B65\u6570\uFF08\u9ED8\u8BA4 12\uFF09"),
1282
+ settleMs: z8.coerce.number().optional().describe("\u6BCF\u6B65\u540E\u7B49\u5F85\u9875\u9762\u7A33\u5B9A\u7684\u6BEB\u79D2\u6570\uFF08\u9ED8\u8BA4 1500\uFF09")
1283
+ }),
1284
+ result: z8.object({
1285
+ done: z8.boolean(),
1286
+ steps: z8.unknown().optional(),
1287
+ summary: z8.string().optional(),
1288
+ model: z8.string().optional()
1289
+ }),
1290
+ handler: async (p, ctx) => {
1291
+ let page = ctx.page;
1292
+ const creds = loadVLMCredentials2();
1293
+ if (!creds) {
1294
+ return fail3("\u672A\u627E\u5230 VLM \u51ED\u636E\uFF1A\u9700\u8981 ~/.zcode/v2/config.json \u6216 XBROWSER_VLM_CONFIG");
1295
+ }
1296
+ const maxSteps = p.maxSteps ?? 12;
1297
+ const settleMs = p.settleMs ?? 1500;
1298
+ const steps = [];
1299
+ const history = [];
1300
+ let lastUrl = page.url();
1301
+ for (let step = 1; step <= maxSteps; step++) {
1302
+ if (page.url() === "about:blank") {
1303
+ try {
1304
+ const ctxObj = page.context?.();
1305
+ const pages = ctxObj?.pages?.() || [];
1306
+ let real;
1307
+ for (let i = pages.length - 1; i >= 0; i--) {
1308
+ const u = pages[i]?.url?.() ?? "";
1309
+ if (u && u !== "about:blank") {
1310
+ real = pages[i];
1311
+ break;
1312
+ }
1313
+ }
1314
+ if (real) {
1315
+ await real.bringToFront().catch(() => {
1316
+ });
1317
+ page = real;
1318
+ }
1319
+ } catch {
1320
+ }
1321
+ }
1322
+ const buffer = await page.screenshot({ type: "png" });
1323
+ const b64 = buffer.toString("base64");
1324
+ const { w, h } = pngDims(b64);
1325
+ const obsText = [
1326
+ `\u4EFB\u52A1\uFF1A${p.task}`,
1327
+ `\u622A\u56FE\u5C3A\u5BF8\uFF1A${w}x${h}\uFF08\u5750\u6807\u539F\u70B9\uFF1A\u5DE6\u4E0A\u89D2\uFF09`,
1328
+ `\u5F53\u524D URL\uFF1A${page.url()}`,
1329
+ history.length ? `\u5DF2\u6267\u884C\u52A8\u4F5C\uFF1A
1330
+ ${history.slice(-6).join("\n")}` : "\u5C1A\u65E0\u52A8\u4F5C\u3002"
1331
+ ].join("\n");
1332
+ const reply = await vlmAskRetry(creds, [
1333
+ { type: "image", source: { type: "base64", media_type: "image/png", data: b64 } },
1334
+ { type: "text", text: `${SYSTEM_PROMPT}
1335
+
1336
+ ${obsText}` }
1337
+ ]);
1338
+ const decision = parseDecision(reply);
1339
+ if (!decision) {
1340
+ steps.push({ n: step, thought: "(unparseable)", action: "stop", ts: Date.now() });
1341
+ return ok8({ done: false, steps, summary: `\u7B2C ${step} \u6B65 VLM \u8F93\u51FA\u65E0\u6CD5\u89E3\u6790: ${reply.substring(0, 120)}`, model: creds.model });
1342
+ }
1343
+ const a = decision.action;
1344
+ let acted = "";
1345
+ try {
1346
+ if (a.type === "done") {
1347
+ steps.push({ n: step, thought: decision.thought, action: "done", ts: Date.now() });
1348
+ return ok8({ done: true, steps, summary: a.summary, model: creds.model });
1349
+ }
1350
+ if (a.type === "stuck") {
1351
+ steps.push({ n: step, thought: decision.thought, action: "stuck", ts: Date.now() });
1352
+ return ok8({ done: false, steps, summary: `stuck: ${a.reason}`, model: creds.model });
1353
+ }
1354
+ if (a.type === "click") {
1355
+ const cx = Math.max(2, Math.min(w - 2, Math.round(a.x)));
1356
+ const cy = Math.max(2, Math.min(h - 2, Math.round(a.y)));
1357
+ await page.mouse.click(cx, cy, { stealth: true });
1358
+ acted = `click(${cx},${cy})`;
1359
+ } else if (a.type === "type") {
1360
+ await page.keyboard.type(a.text, { stealth: true });
1361
+ acted = `type("${a.text.substring(0, 30)}")`;
1362
+ } else if (a.type === "key") {
1363
+ await page.keyboard.press(a.key);
1364
+ acted = `key(${a.key})`;
1365
+ } else if (a.type === "scroll") {
1366
+ await page.mouse.wheel(0, a.direction === "down" ? a.amount : -a.amount);
1367
+ acted = `scroll(${a.direction},${a.amount})`;
1368
+ }
1369
+ } catch (e) {
1370
+ acted = `${a.type} FAILED: ${e instanceof Error ? e.message : String(e).substring(0, 80)}`;
1371
+ }
1372
+ steps.push({ n: step, thought: decision.thought, action: acted, ts: Date.now() });
1373
+ history.push(`${step}. ${acted} \u2014 ${decision.thought.substring(0, 60)}`);
1374
+ await new Promise((r) => setTimeout(r, settleMs));
1375
+ if (a.type === "click") {
1376
+ try {
1377
+ const ctxObj = page.context?.();
1378
+ const pages = ctxObj?.pages?.() || [];
1379
+ for (let i = pages.length - 1; i >= 0; i--) {
1380
+ const cand = pages[i];
1381
+ if (!cand || cand === page) continue;
1382
+ const u = cand.url?.() ?? "";
1383
+ if (!u || u === "about:blank") continue;
1384
+ if (u === page.url()) break;
1385
+ await cand.bringToFront().catch(() => {
1386
+ });
1387
+ page = cand;
1388
+ break;
1389
+ }
1390
+ } catch {
1391
+ }
1392
+ }
1393
+ const urlNow = page.url();
1394
+ if (urlNow !== lastUrl) {
1395
+ history.push(` [\u9875\u9762\u8DF3\u8F6C \u2192 ${urlNow.substring(0, 80)}]`);
1396
+ lastUrl = urlNow;
1397
+ }
1398
+ }
1399
+ return ok8({ done: false, steps, summary: `\u5DF2\u8FBE\u6700\u5927\u6B65\u6570 ${maxSteps}`, model: creds.model });
1400
+ }
1401
+ });
1402
+
1403
+ // src/commands/evaluate.ts
1404
+ import { z as z9 } from "zod";
1405
+ import { ok as ok9, normalizeTips as normalizeTips2 } from "@dyyz1993/xcli-core";
1000
1406
  var evaluateCommand = registerCommand({
1001
1407
  name: "eval",
1002
1408
  description: "Evaluate JavaScript expression in the browser (--frame <url-substring> \u53EF\u5B9A\u5411\u5230 iframe \u4E0A\u4E0B\u6587\uFF0C\u8DE8\u57DF iframe \u4EA6\u53EF)",
1003
1409
  scope: "page",
1004
- parameters: z7.object({
1005
- expression: z7.string(),
1006
- 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")
1410
+ parameters: z9.object({
1411
+ expression: z9.string().optional(),
1412
+ 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"),
1413
+ // Internal: base64url-encoded script set by the router for `eval <js...>`,
1414
+ // so multiline/semicolon JS survives chain parsing (which splits on ';'
1415
+ // and whitespace). Not intended for direct CLI use.
1416
+ "script-b64": z9.string().optional().describe("Internal: base64url script set by the CLI router; do not pass directly")
1007
1417
  }),
1008
1418
  handler: async (p, ctx) => {
1419
+ const rawScript = p["script-b64"];
1420
+ const expression = rawScript ? Buffer.from(rawScript, "base64url").toString("utf8") : p.expression;
1421
+ if (!expression || !expression.trim()) {
1422
+ return { ok: false, error: "eval: expression is required" };
1423
+ }
1009
1424
  const engine = createRuleEngine();
1010
1425
  engine.start();
1011
1426
  const decision = engine.evaluate({
1012
1427
  method: "Runtime.evaluate",
1013
- params: { expression: p.expression },
1428
+ params: { expression },
1014
1429
  sessionId: "eval-check",
1015
1430
  direction: "client\u2192browser"
1016
1431
  });
1017
1432
  engine.stop();
1018
- const result = p.frame ? await ctx.page.evaluateInFrame(p.frame, p.expression) : await ctx.page.evaluate(p.expression);
1019
- const response = ok7({ result });
1433
+ const result = p.frame ? await ctx.page.evaluateInFrame(p.frame, expression) : await ctx.page.evaluate(expression);
1434
+ const response = ok9({ result });
1020
1435
  if (decision && decision.severity === "danger") {
1021
1436
  response.tips = normalizeTips2([`${decision.suggestion}`]);
1022
1437
  }
@@ -1024,37 +1439,65 @@ var evaluateCommand = registerCommand({
1024
1439
  }
1025
1440
  });
1026
1441
 
1442
+ // src/commands/ua.ts
1443
+ import { z as z10 } from "zod";
1444
+ import { ok as ok10 } from "@dyyz1993/xcli-core";
1445
+ function uaHookScript(ua) {
1446
+ const uaJson = JSON.stringify(ua);
1447
+ const appVersion = JSON.stringify(ua.replace(/^Mozilla\//, ""));
1448
+ 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){}`;
1449
+ }
1450
+ var uaCommand = registerCommand({
1451
+ name: "ua",
1452
+ description: "Get or override the session User-Agent (anti-bot UA detection countermeasure)",
1453
+ scope: "page",
1454
+ parameters: z10.object({
1455
+ 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')
1456
+ }),
1457
+ handler: async (p, ctx) => {
1458
+ if (!p.value) {
1459
+ const current = await ctx.page.evaluate("navigator.userAgent");
1460
+ return ok10({ userAgent: current });
1461
+ }
1462
+ 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;
1463
+ const script = uaHookScript(ua);
1464
+ await ctx.page.addInitScript(script);
1465
+ await ctx.page.evaluate(`(function(){${script}})()`);
1466
+ return ok10({ userAgent: ua, mode: "init-script" });
1467
+ }
1468
+ });
1469
+
1027
1470
  // src/commands/storage.ts
1028
- import { z as z8 } from "zod";
1029
- import { ok as ok8, fail as fail2 } from "@dyyz1993/xcli-core";
1471
+ import { z as z11 } from "zod";
1472
+ import { ok as ok11, fail as fail4 } from "@dyyz1993/xcli-core";
1030
1473
  var getCookiesCommand = registerCommand({
1031
1474
  name: "get-cookies",
1032
1475
  description: "Get all cookies for the current page",
1033
1476
  scope: "page",
1034
- result: z8.object({
1035
- cookies: z8.array(z8.record(z8.unknown()))
1477
+ result: z11.object({
1478
+ cookies: z11.array(z11.record(z11.unknown()))
1036
1479
  }),
1037
1480
  handler: async (_p, ctx) => {
1038
1481
  const cookies = await ctx.browserContext.cookies();
1039
- return ok8({ cookies });
1482
+ return ok11({ cookies });
1040
1483
  }
1041
1484
  });
1042
1485
  var setCookieCommand = registerCommand({
1043
1486
  name: "set-cookie",
1044
1487
  description: "Set a cookie",
1045
1488
  scope: "page",
1046
- parameters: z8.object({
1047
- name: z8.coerce.string(),
1048
- value: z8.coerce.string(),
1049
- domain: z8.coerce.string().optional(),
1050
- path: z8.coerce.string().optional(),
1051
- url: z8.string().optional().describe("Cookie URL (alternative to domain)"),
1052
- expires: z8.number().optional(),
1053
- httpOnly: z8.boolean().optional(),
1054
- secure: z8.boolean().optional(),
1055
- sameSite: z8.enum(["Strict", "Lax", "None"]).optional()
1489
+ parameters: z11.object({
1490
+ name: z11.coerce.string(),
1491
+ value: z11.coerce.string(),
1492
+ domain: z11.coerce.string().optional(),
1493
+ path: z11.coerce.string().optional(),
1494
+ url: z11.string().optional().describe("Cookie URL (alternative to domain)"),
1495
+ expires: z11.number().optional(),
1496
+ httpOnly: z11.boolean().optional(),
1497
+ secure: z11.boolean().optional(),
1498
+ sameSite: z11.enum(["Strict", "Lax", "None"]).optional()
1056
1499
  }),
1057
- result: z8.object({ name: z8.string() }),
1500
+ result: z11.object({ name: z11.string() }),
1058
1501
  handler: async (p, ctx) => {
1059
1502
  const cookie = { ...p };
1060
1503
  if (!cookie.domain && !cookie.url) {
@@ -1069,38 +1512,38 @@ var setCookieCommand = registerCommand({
1069
1512
  }
1070
1513
  }
1071
1514
  if (!cookie.domain && !cookie.url) {
1072
- return fail2("set-cookie requires --domain or --url, or a non-blank page URL to infer from");
1515
+ return fail4("set-cookie requires --domain or --url, or a non-blank page URL to infer from");
1073
1516
  }
1074
1517
  await ctx.browserContext.addCookies([cookie]);
1075
- return ok8({ name: p.name });
1518
+ return ok11({ name: p.name });
1076
1519
  }
1077
1520
  });
1078
1521
  var clearCookiesCommand = registerCommand({
1079
1522
  name: "clear-cookies",
1080
1523
  description: "Clear all cookies",
1081
1524
  scope: "page",
1082
- result: z8.object({ cleared: z8.boolean() }),
1525
+ result: z11.object({ cleared: z11.boolean() }),
1083
1526
  handler: async (_p, ctx) => {
1084
1527
  await ctx.browserContext.clearCookies();
1085
- return ok8({ cleared: true });
1528
+ return ok11({ cleared: true });
1086
1529
  }
1087
1530
  });
1088
1531
  var getLocalStorageCommand = registerCommand({
1089
1532
  name: "get-local-storage",
1090
1533
  description: "Get localStorage entries",
1091
1534
  scope: "page",
1092
- parameters: z8.object({
1093
- key: z8.string().optional()
1535
+ parameters: z11.object({
1536
+ key: z11.string().optional()
1094
1537
  }),
1095
- result: z8.union([
1096
- z8.object({ key: z8.string(), value: z8.string().nullable() }),
1097
- z8.object({ data: z8.record(z8.string()) })
1538
+ result: z11.union([
1539
+ z11.object({ key: z11.string(), value: z11.string().nullable() }),
1540
+ z11.object({ data: z11.record(z11.string()) })
1098
1541
  ]),
1099
1542
  handler: async (p, ctx) => {
1100
1543
  try {
1101
1544
  if (p.key) {
1102
1545
  const value = await ctx.page.evaluate((k) => localStorage.getItem(k), p.key);
1103
- return ok8({ key: p.key, value });
1546
+ return ok11({ key: p.key, value });
1104
1547
  }
1105
1548
  const data = await ctx.page.evaluate(() => {
1106
1549
  const entries = {};
@@ -1110,9 +1553,9 @@ var getLocalStorageCommand = registerCommand({
1110
1553
  }
1111
1554
  return entries;
1112
1555
  });
1113
- return ok8({ data });
1556
+ return ok11({ data });
1114
1557
  } catch (e) {
1115
- return fail2(`localStorage not accessible on this page: ${e instanceof Error ? e.message : String(e)}`);
1558
+ return fail4(`localStorage not accessible on this page: ${e instanceof Error ? e.message : String(e)}`);
1116
1559
  }
1117
1560
  }
1118
1561
  });
@@ -1120,11 +1563,11 @@ var setLocalStorageCommand = registerCommand({
1120
1563
  name: "set-local-storage",
1121
1564
  description: "Set a localStorage entry",
1122
1565
  scope: "page",
1123
- parameters: z8.object({
1124
- key: z8.string(),
1125
- value: z8.string()
1566
+ parameters: z11.object({
1567
+ key: z11.string(),
1568
+ value: z11.string()
1126
1569
  }),
1127
- result: z8.object({ key: z8.string() }),
1570
+ result: z11.object({ key: z11.string() }),
1128
1571
  handler: async (p, ctx) => {
1129
1572
  try {
1130
1573
  await ctx.page.evaluate(
@@ -1133,9 +1576,9 @@ var setLocalStorageCommand = registerCommand({
1133
1576
  },
1134
1577
  { key: p.key, value: p.value }
1135
1578
  );
1136
- return ok8({ key: p.key });
1579
+ return ok11({ key: p.key });
1137
1580
  } catch (e) {
1138
- return fail2(`localStorage not accessible on this page: ${e instanceof Error ? e.message : String(e)}`);
1581
+ return fail4(`localStorage not accessible on this page: ${e instanceof Error ? e.message : String(e)}`);
1139
1582
  }
1140
1583
  }
1141
1584
  });
@@ -1143,26 +1586,24 @@ var clearLocalStorageCommand = registerCommand({
1143
1586
  name: "clear-local-storage",
1144
1587
  description: "Clear all localStorage entries",
1145
1588
  scope: "page",
1146
- result: z8.object({ cleared: z8.boolean() }),
1589
+ result: z11.object({ cleared: z11.boolean() }),
1147
1590
  handler: async (_p, ctx) => {
1148
1591
  try {
1149
1592
  await ctx.page.evaluate(() => localStorage.clear());
1150
- return ok8({ cleared: true });
1593
+ return ok11({ cleared: true });
1151
1594
  } catch (e) {
1152
- return fail2(`localStorage not accessible on this page: ${e instanceof Error ? e.message : String(e)}`);
1595
+ return fail4(`localStorage not accessible on this page: ${e instanceof Error ? e.message : String(e)}`);
1153
1596
  }
1154
1597
  }
1155
1598
  });
1156
1599
 
1157
1600
  // src/commands/screenshot.ts
1158
- import { z as z9 } from "zod";
1159
- import { ok as ok9, fail as fail3 } from "@dyyz1993/xcli-core";
1601
+ import { z as z12 } from "zod";
1602
+ import { ok as ok12, fail as fail5 } from "@dyyz1993/xcli-core";
1160
1603
  import { writeFileSync, mkdirSync } from "fs";
1161
- import { dirname, join as join2 } from "path";
1162
- import { homedir as homedir2 } from "os";
1163
- var SCREENSHOTS_DIR = join2(homedir2(), ".xbrowser", "screenshots");
1604
+ import { dirname, join as join4 } from "path";
1164
1605
  function ensureScreenshotsDir() {
1165
- mkdirSync(SCREENSHOTS_DIR, { recursive: true });
1606
+ mkdirSync(resolveScreenshotsDir(), { recursive: true });
1166
1607
  }
1167
1608
  function ensureParentDir(filePath) {
1168
1609
  const dir = dirname(filePath);
@@ -1178,30 +1619,30 @@ function generateScreenshotPath(format) {
1178
1619
  const timestamp = Date.now();
1179
1620
  const random = Math.random().toString(36).slice(2, 8);
1180
1621
  const ext = format === "jpeg" ? "jpg" : "png";
1181
- return join2(SCREENSHOTS_DIR, `screenshot-${timestamp}-${random}.${ext}`);
1622
+ return join4(resolveScreenshotsDir(), `screenshot-${timestamp}-${random}.${ext}`);
1182
1623
  }
1183
1624
  var screenshotCommand = registerCommand({
1184
1625
  name: "screenshot",
1185
- description: "Take a screenshot of the page or element",
1626
+ 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>)",
1186
1627
  scope: "page",
1187
1628
  selectorParams: ["selector"],
1188
- parameters: z9.object({
1189
- selector: z9.string().optional(),
1190
- type: z9.enum(["png", "jpeg"]).optional(),
1191
- fullPage: z9.boolean().optional(),
1192
- output: z9.string().optional(),
1193
- base64: z9.boolean().optional().describe("Return base64 data instead of file path")
1629
+ parameters: z12.object({
1630
+ selector: z12.string().optional(),
1631
+ type: z12.enum(["png", "jpeg"]).optional(),
1632
+ fullPage: z12.boolean().optional(),
1633
+ output: z12.string().optional(),
1634
+ base64: z12.boolean().optional().describe("Return base64 data instead of file path")
1194
1635
  }),
1195
- result: z9.union([
1196
- z9.object({
1197
- data: z9.string(),
1198
- format: z9.string(),
1199
- size: z9.number()
1636
+ result: z12.union([
1637
+ z12.object({
1638
+ data: z12.string(),
1639
+ format: z12.string(),
1640
+ size: z12.number()
1200
1641
  }),
1201
- z9.object({
1202
- output: z9.string(),
1203
- format: z9.string(),
1204
- size: z9.number()
1642
+ z12.object({
1643
+ output: z12.string(),
1644
+ format: z12.string(),
1645
+ size: z12.number()
1205
1646
  })
1206
1647
  ]),
1207
1648
  handler: async (p, ctx) => {
@@ -1219,21 +1660,21 @@ var screenshotCommand = registerCommand({
1219
1660
  if (p.output) {
1220
1661
  const dirErr = ensureParentDir(p.output);
1221
1662
  if (dirErr) {
1222
- return fail3(`Cannot create directory for --output "${p.output}": ${dirErr}`);
1663
+ return fail5(`Cannot create directory for --output "${p.output}": ${dirErr}`);
1223
1664
  }
1224
1665
  try {
1225
1666
  writeFileSync(p.output, buffer, "binary");
1226
1667
  } catch (err) {
1227
- return fail3(`Failed to write screenshot to "${p.output}": ${err instanceof Error ? err.message : String(err)}`);
1668
+ return fail5(`Failed to write screenshot to "${p.output}": ${err instanceof Error ? err.message : String(err)}`);
1228
1669
  }
1229
- return ok9({
1670
+ return ok12({
1230
1671
  output: p.output,
1231
1672
  format,
1232
1673
  size: buffer.length
1233
1674
  });
1234
1675
  }
1235
1676
  if (p.base64) {
1236
- return ok9({
1677
+ return ok12({
1237
1678
  data: buffer.toString("base64"),
1238
1679
  format,
1239
1680
  size: buffer.length
@@ -1242,7 +1683,7 @@ var screenshotCommand = registerCommand({
1242
1683
  ensureScreenshotsDir();
1243
1684
  const screenshotPath = generateScreenshotPath(format);
1244
1685
  writeFileSync(screenshotPath, buffer, "binary");
1245
- return ok9({
1686
+ return ok12({
1246
1687
  output: screenshotPath,
1247
1688
  format,
1248
1689
  size: buffer.length
@@ -1251,19 +1692,19 @@ var screenshotCommand = registerCommand({
1251
1692
  });
1252
1693
 
1253
1694
  // src/commands/structure.ts
1254
- import { z as z10 } from "zod";
1255
- import { ok as ok10 } from "@dyyz1993/xcli-core";
1695
+ import { z as z13 } from "zod";
1696
+ import { ok as ok13 } from "@dyyz1993/xcli-core";
1256
1697
  var structureCommand = registerCommand({
1257
1698
  name: "structure",
1258
1699
  description: "Get the DOM structure of the page or an element",
1259
1700
  scope: "page",
1260
1701
  selectorParams: ["selector"],
1261
- parameters: z10.object({
1262
- selector: z10.string().optional(),
1263
- depth: z10.number().optional()
1702
+ parameters: z13.object({
1703
+ selector: z13.string().optional(),
1704
+ depth: z13.number().optional()
1264
1705
  }),
1265
- result: z10.object({
1266
- structure: z10.record(z10.unknown())
1706
+ result: z13.object({
1707
+ structure: z13.record(z13.unknown())
1267
1708
  }),
1268
1709
  handler: async (p, ctx) => {
1269
1710
  const structure = await ctx.page.evaluate(
@@ -1295,30 +1736,30 @@ var structureCommand = registerCommand({
1295
1736
  },
1296
1737
  { sel: p.selector || "body", maxDepth: p.depth || 5 }
1297
1738
  );
1298
- return ok10({ structure });
1739
+ return ok13({ structure });
1299
1740
  }
1300
1741
  });
1301
1742
 
1302
1743
  // src/commands/viewport.ts
1303
- import { z as z11 } from "zod";
1304
- import { ok as ok11 } from "@dyyz1993/xcli-core";
1744
+ import { z as z14 } from "zod";
1745
+ import { ok as ok14 } from "@dyyz1993/xcli-core";
1305
1746
  var setViewportCommand = registerCommand({
1306
1747
  name: "set-viewport",
1307
1748
  description: "Set the viewport size and properties",
1308
1749
  scope: "browser",
1309
- parameters: z11.object({
1310
- width: z11.coerce.number(),
1311
- height: z11.coerce.number(),
1312
- deviceScaleFactor: z11.coerce.number().optional(),
1313
- isMobile: z11.boolean().optional(),
1314
- hasTouch: z11.boolean().optional()
1750
+ parameters: z14.object({
1751
+ width: z14.coerce.number(),
1752
+ height: z14.coerce.number(),
1753
+ deviceScaleFactor: z14.coerce.number().optional(),
1754
+ isMobile: z14.boolean().optional(),
1755
+ hasTouch: z14.boolean().optional()
1315
1756
  }),
1316
- result: z11.object({
1317
- width: z11.number(),
1318
- height: z11.number(),
1319
- deviceScaleFactor: z11.number().optional(),
1320
- isMobile: z11.boolean().optional(),
1321
- hasTouch: z11.boolean().optional()
1757
+ result: z14.object({
1758
+ width: z14.number(),
1759
+ height: z14.number(),
1760
+ deviceScaleFactor: z14.number().optional(),
1761
+ isMobile: z14.boolean().optional(),
1762
+ hasTouch: z14.boolean().optional()
1322
1763
  }),
1323
1764
  handler: async (p, ctx) => {
1324
1765
  const viewport = ctx.page.viewportSize();
@@ -1331,7 +1772,7 @@ var setViewportCommand = registerCommand({
1331
1772
  ...p.isMobile !== void 0 && { isMobile: p.isMobile },
1332
1773
  ...p.hasTouch !== void 0 && { hasTouch: p.hasTouch }
1333
1774
  });
1334
- return ok11({
1775
+ return ok14({
1335
1776
  width,
1336
1777
  height,
1337
1778
  ...p.deviceScaleFactor !== void 0 && { deviceScaleFactor: p.deviceScaleFactor },
@@ -1342,17 +1783,17 @@ var setViewportCommand = registerCommand({
1342
1783
  });
1343
1784
 
1344
1785
  // src/commands/frame.ts
1345
- import { z as z12 } from "zod";
1346
- import { ok as ok12, fail as fail4 } from "@dyyz1993/xcli-core";
1786
+ import { z as z15 } from "zod";
1787
+ import { ok as ok15, fail as fail6 } from "@dyyz1993/xcli-core";
1347
1788
  var framesCommand = registerCommand({
1348
1789
  name: "frames",
1349
1790
  description: "List all frames in the current page",
1350
1791
  scope: "page",
1351
- result: z12.object({
1352
- frames: z12.array(z12.object({
1353
- index: z12.number(),
1354
- name: z12.string().nullable(),
1355
- url: z12.string()
1792
+ result: z15.object({
1793
+ frames: z15.array(z15.object({
1794
+ index: z15.number(),
1795
+ name: z15.string().nullable(),
1796
+ url: z15.string()
1356
1797
  }))
1357
1798
  }),
1358
1799
  handler: async (_p, ctx) => {
@@ -1363,21 +1804,21 @@ var framesCommand = registerCommand({
1363
1804
  name: frame.name(),
1364
1805
  url: frame.url()
1365
1806
  }));
1366
- return ok12({ frames: frameList });
1807
+ return ok15({ frames: frameList });
1367
1808
  }
1368
1809
  });
1369
1810
  var frameCommand = registerCommand({
1370
1811
  name: "frame",
1371
1812
  description: "Get frame info by index or name",
1372
1813
  scope: "page",
1373
- parameters: z12.object({
1374
- index: z12.number().int().min(0).optional(),
1375
- name: z12.string().optional()
1814
+ parameters: z15.object({
1815
+ index: z15.number().int().min(0).optional(),
1816
+ name: z15.string().optional()
1376
1817
  }),
1377
- result: z12.object({
1378
- name: z12.string().nullable(),
1379
- url: z12.string(),
1380
- error: z12.string().optional()
1818
+ result: z15.object({
1819
+ name: z15.string().nullable(),
1820
+ url: z15.string(),
1821
+ error: z15.string().optional()
1381
1822
  }),
1382
1823
  handler: async (p, ctx) => {
1383
1824
  const discover = ctx.page.discoverFrames;
@@ -1388,12 +1829,12 @@ var frameCommand = registerCommand({
1388
1829
  } else if (p.name !== void 0) {
1389
1830
  targetFrame = rawFrames.find((f) => f.name() === p.name);
1390
1831
  } else {
1391
- return fail4("Must provide index or name");
1832
+ return fail6("Must provide index or name");
1392
1833
  }
1393
1834
  if (!targetFrame) {
1394
- return fail4("Frame not found");
1835
+ return fail6("Frame not found");
1395
1836
  }
1396
- return ok12({
1837
+ return ok15({
1397
1838
  name: targetFrame.name(),
1398
1839
  url: targetFrame.url()
1399
1840
  });
@@ -1401,27 +1842,27 @@ var frameCommand = registerCommand({
1401
1842
  });
1402
1843
 
1403
1844
  // src/commands/ui-debug.ts
1404
- import { z as z13 } from "zod";
1405
- import { ok as ok13 } from "@dyyz1993/xcli-core";
1845
+ import { z as z16 } from "zod";
1846
+ import { ok as ok16 } from "@dyyz1993/xcli-core";
1406
1847
  var consoleCheckCommand = registerCommand({
1407
1848
  name: "console",
1408
1849
  description: "Collect and analyze browser console messages (errors, warnings, logs)",
1409
1850
  scope: "page",
1410
- parameters: z13.object({
1411
- url: z13.string().optional().describe("URL to navigate first (optional, uses current page if omitted)"),
1412
- duration: z13.number().optional().default(5e3).describe("How long to collect messages (ms)"),
1413
- filter: z13.enum(["all", "error", "warning", "info", "log"]).optional().default("all"),
1414
- includeStackTraces: z13.boolean().optional().default(true)
1851
+ parameters: z16.object({
1852
+ url: z16.string().optional().describe("URL to navigate first (optional, uses current page if omitted)"),
1853
+ duration: z16.number().optional().default(5e3).describe("How long to collect messages (ms)"),
1854
+ filter: z16.enum(["all", "error", "warning", "info", "log"]).optional().default("all"),
1855
+ includeStackTraces: z16.boolean().optional().default(true)
1415
1856
  }),
1416
- result: z13.object({
1417
- url: z13.string(),
1418
- duration: z13.number(),
1419
- total: z13.number(),
1420
- errors: z13.number(),
1421
- warnings: z13.number(),
1422
- messages: z13.array(z13.record(z13.unknown())),
1423
- summary: z13.string(),
1424
- passed: z13.boolean()
1857
+ result: z16.object({
1858
+ url: z16.string(),
1859
+ duration: z16.number(),
1860
+ total: z16.number(),
1861
+ errors: z16.number(),
1862
+ warnings: z16.number(),
1863
+ messages: z16.array(z16.record(z16.unknown())),
1864
+ summary: z16.string(),
1865
+ passed: z16.boolean()
1425
1866
  }),
1426
1867
  handler: async (p, ctx) => {
1427
1868
  const { page } = ctx;
@@ -1503,7 +1944,7 @@ ${a.stack || ""}`;
1503
1944
  const filtered = p.filter === "all" ? messages : messages.filter((m) => m.type === p.filter);
1504
1945
  const errorCount = messages.filter((m) => m.type === "error").length;
1505
1946
  const warnCount = messages.filter((m) => m.type === "warning").length;
1506
- return ok13({
1947
+ return ok16({
1507
1948
  url: page.url(),
1508
1949
  duration: p.duration,
1509
1950
  total: messages.length,
@@ -1519,23 +1960,23 @@ var networkCheckCommand = registerCommand({
1519
1960
  name: "net-debug",
1520
1961
  description: "Monitor and analyze network requests \u2014 capture failed requests, slow responses, status codes",
1521
1962
  scope: "page",
1522
- parameters: z13.object({
1523
- url: z13.string().optional().describe("URL to navigate first"),
1524
- duration: z13.number().optional().default(5e3).describe("How long to monitor (ms)"),
1525
- filter: z13.enum(["all", "failed", "slow", "error", "xhr", "fetch", "document", "stylesheet", "script", "image"]).optional().default("all"),
1526
- slowThreshold: z13.number().optional().default(3e3).describe('Threshold for "slow" requests (ms)')
1963
+ parameters: z16.object({
1964
+ url: z16.string().optional().describe("URL to navigate first"),
1965
+ duration: z16.number().optional().default(5e3).describe("How long to monitor (ms)"),
1966
+ filter: z16.enum(["all", "failed", "slow", "error", "xhr", "fetch", "document", "stylesheet", "script", "image"]).optional().default("all"),
1967
+ slowThreshold: z16.number().optional().default(3e3).describe('Threshold for "slow" requests (ms)')
1527
1968
  }),
1528
- result: z13.object({
1529
- url: z13.string(),
1530
- duration: z13.number(),
1531
- totalRequests: z13.number(),
1532
- failedRequests: z13.number(),
1533
- slowRequests: z13.number(),
1534
- errorRequests: z13.number(),
1535
- totalSizeKB: z13.number(),
1536
- requests: z13.array(z13.record(z13.unknown())),
1537
- summary: z13.string(),
1538
- passed: z13.boolean()
1969
+ result: z16.object({
1970
+ url: z16.string(),
1971
+ duration: z16.number(),
1972
+ totalRequests: z16.number(),
1973
+ failedRequests: z16.number(),
1974
+ slowRequests: z16.number(),
1975
+ errorRequests: z16.number(),
1976
+ totalSizeKB: z16.number(),
1977
+ requests: z16.array(z16.record(z16.unknown())),
1978
+ summary: z16.string(),
1979
+ passed: z16.boolean()
1539
1980
  }),
1540
1981
  handler: async (p, ctx) => {
1541
1982
  const { page } = ctx;
@@ -1617,7 +2058,7 @@ var networkCheckCommand = registerCommand({
1617
2058
  const slowCount = requests.filter((r) => r.duration >= p.slowThreshold).length;
1618
2059
  const errorCount = requests.filter((r) => r.error).length;
1619
2060
  const totalSize = requests.reduce((sum, r) => sum + r.size, 0);
1620
- return ok13({
2061
+ return ok16({
1621
2062
  url: page.url(),
1622
2063
  duration: p.duration,
1623
2064
  totalRequests: requests.length,
@@ -1639,17 +2080,17 @@ var perfCheckCommand = registerCommand({
1639
2080
  name: "perf",
1640
2081
  description: "Audit page performance metrics \u2014 load time, FCP, LCP, CLS, TTFB, resource sizes",
1641
2082
  scope: "page",
1642
- parameters: z13.object({
1643
- url: z13.string().optional().describe("URL to navigate (uses current page if omitted)"),
1644
- iterations: z13.number().optional().default(1).describe("Number of iterations to average")
2083
+ parameters: z16.object({
2084
+ url: z16.string().optional().describe("URL to navigate (uses current page if omitted)"),
2085
+ iterations: z16.number().optional().default(1).describe("Number of iterations to average")
1645
2086
  }),
1646
- result: z13.object({
1647
- url: z13.string(),
1648
- iterations: z13.number(),
1649
- metrics: z13.record(z13.unknown()),
1650
- allIterations: z13.array(z13.record(z13.unknown())).optional(),
1651
- passed: z13.boolean(),
1652
- summary: z13.string()
2087
+ result: z16.object({
2088
+ url: z16.string(),
2089
+ iterations: z16.number(),
2090
+ metrics: z16.record(z16.unknown()),
2091
+ allIterations: z16.array(z16.record(z16.unknown())).optional(),
2092
+ passed: z16.boolean(),
2093
+ summary: z16.string()
1653
2094
  }),
1654
2095
  handler: async (p, ctx) => {
1655
2096
  const { page } = ctx;
@@ -1711,7 +2152,7 @@ var perfCheckCommand = registerCommand({
1711
2152
  if (resourceStats) avg.resourceStats = resourceStats;
1712
2153
  return avg;
1713
2154
  })();
1714
- return ok13({
2155
+ return ok16({
1715
2156
  url: page.url(),
1716
2157
  iterations: p.iterations,
1717
2158
  metrics: avgMetrics,
@@ -1725,23 +2166,23 @@ var healthCheckCommand = registerCommand({
1725
2166
  name: "health",
1726
2167
  description: "Comprehensive page health check \u2014 broken links, missing images, console errors, SEO issues",
1727
2168
  scope: "page",
1728
- parameters: z13.object({
1729
- url: z13.string().optional().describe("URL to check"),
1730
- checkLinks: z13.boolean().optional().default(true).describe("Check for broken links"),
1731
- checkImages: z13.boolean().optional().default(true).describe("Check for missing/broken images"),
1732
- checkMeta: z13.boolean().optional().default(true).describe("Check SEO meta tags"),
1733
- maxLinks: z13.number().optional().default(50).describe("Max links to check")
2169
+ parameters: z16.object({
2170
+ url: z16.string().optional().describe("URL to check"),
2171
+ checkLinks: z16.boolean().optional().default(true).describe("Check for broken links"),
2172
+ checkImages: z16.boolean().optional().default(true).describe("Check for missing/broken images"),
2173
+ checkMeta: z16.boolean().optional().default(true).describe("Check SEO meta tags"),
2174
+ maxLinks: z16.number().optional().default(50).describe("Max links to check")
1734
2175
  }),
1735
- result: z13.object({
1736
- url: z13.string(),
1737
- title: z13.string(),
1738
- passed: z13.boolean(),
1739
- totalIssues: z13.number(),
1740
- errors: z13.number(),
1741
- warnings: z13.number(),
1742
- info: z13.number(),
1743
- issues: z13.array(z13.record(z13.unknown())),
1744
- summary: z13.string()
2176
+ result: z16.object({
2177
+ url: z16.string(),
2178
+ title: z16.string(),
2179
+ passed: z16.boolean(),
2180
+ totalIssues: z16.number(),
2181
+ errors: z16.number(),
2182
+ warnings: z16.number(),
2183
+ info: z16.number(),
2184
+ issues: z16.array(z16.record(z16.unknown())),
2185
+ summary: z16.string()
1745
2186
  }),
1746
2187
  handler: async (p, ctx) => {
1747
2188
  const { page } = ctx;
@@ -1828,7 +2269,7 @@ var healthCheckCommand = registerCommand({
1828
2269
  }, { checkLinks: p.checkLinks, checkImages: p.checkImages, checkMeta: p.checkMeta, maxLinks: p.maxLinks });
1829
2270
  const errors = result.issues.filter((i) => i.severity === "error").length;
1830
2271
  const warnings = result.issues.filter((i) => i.severity === "warning").length;
1831
- return ok13({
2272
+ return ok16({
1832
2273
  url: result.url,
1833
2274
  title: result.title,
1834
2275
  passed: errors === 0,
@@ -1843,68 +2284,66 @@ var healthCheckCommand = registerCommand({
1843
2284
  });
1844
2285
 
1845
2286
  // src/commands/actions.ts
1846
- import { z as z14 } from "zod";
1847
- import { ok as ok14 } from "@dyyz1993/xcli-core";
2287
+ import { z as z17 } from "zod";
2288
+ import { ok as ok17 } from "@dyyz1993/xcli-core";
1848
2289
  import { writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
1849
- import { join as join3 } from "path";
1850
- import { homedir as homedir3 } from "os";
1851
- var SCREENSHOTS_DIR2 = join3(homedir3(), ".xbrowser", "screenshots");
2290
+ import { join as join5 } from "path";
1852
2291
  function ensureScreenshotsDir2() {
1853
- mkdirSync2(SCREENSHOTS_DIR2, { recursive: true });
2292
+ mkdirSync2(resolveScreenshotsDir(), { recursive: true });
1854
2293
  }
1855
2294
  function generateScreenshotPath2(format) {
1856
2295
  const timestamp = Date.now();
1857
2296
  const random = Math.random().toString(36).slice(2, 8);
1858
2297
  const ext = format === "jpeg" ? "jpg" : "png";
1859
- return join3(SCREENSHOTS_DIR2, `screenshot-${timestamp}-${random}.${ext}`);
2298
+ return join5(resolveScreenshotsDir(), `screenshot-${timestamp}-${random}.${ext}`);
1860
2299
  }
1861
- var waitActionSchema = z14.object({
1862
- type: z14.literal("wait"),
1863
- milliseconds: z14.number().positive().optional(),
1864
- selector: z14.string().optional()
2300
+ var waitActionSchema = z17.object({
2301
+ type: z17.literal("wait"),
2302
+ milliseconds: z17.number().positive().optional(),
2303
+ selector: z17.string().optional()
1865
2304
  }).refine(
1866
2305
  (d) => (d.milliseconds !== void 0 || d.selector !== void 0) && !(d.milliseconds !== void 0 && d.selector !== void 0),
1867
2306
  { message: "Either 'milliseconds' or 'selector' must be provided, but not both." }
1868
2307
  );
1869
- var clickActionSchema = z14.object({
1870
- type: z14.literal("click"),
1871
- selector: z14.string(),
1872
- all: z14.boolean().optional()
2308
+ var clickActionSchema = z17.object({
2309
+ type: z17.literal("click"),
2310
+ selector: z17.string(),
2311
+ all: z17.boolean().optional()
1873
2312
  });
1874
- var screenshotActionSchema = z14.object({
1875
- type: z14.literal("screenshot"),
1876
- fullPage: z14.boolean().optional(),
1877
- quality: z14.number().min(1).max(100).optional(),
1878
- viewport: z14.object({ width: z14.number().int().positive(), height: z14.number().int().positive() }).optional(),
1879
- base64: z14.boolean().optional().describe("Return base64 data instead of file path")
2313
+ var screenshotActionSchema = z17.object({
2314
+ type: z17.literal("screenshot"),
2315
+ fullPage: z17.boolean().optional(),
2316
+ quality: z17.number().min(1).max(100).optional(),
2317
+ viewport: z17.object({ width: z17.number().int().positive(), height: z17.number().int().positive() }).optional(),
2318
+ base64: z17.boolean().optional().describe("Return base64 data instead of file path")
1880
2319
  });
1881
- var writeActionSchema = z14.object({
1882
- type: z14.literal("write"),
1883
- text: z14.string()
2320
+ var writeActionSchema = z17.object({
2321
+ type: z17.literal("write"),
2322
+ text: z17.string()
1884
2323
  });
1885
- var pressActionSchema = z14.object({
1886
- type: z14.literal("press"),
1887
- key: z14.string()
2324
+ var pressActionSchema = z17.object({
2325
+ type: z17.literal("press"),
2326
+ key: z17.string()
1888
2327
  });
1889
- var scrollActionSchema = z14.object({
1890
- type: z14.literal("scroll"),
1891
- direction: z14.enum(["up", "down"]).optional(),
1892
- selector: z14.string().optional()
2328
+ var scrollActionSchema = z17.object({
2329
+ type: z17.literal("scroll"),
2330
+ direction: z17.enum(["up", "down"]).optional(),
2331
+ selector: z17.string().optional()
1893
2332
  });
1894
- var scrapeActionSchema = z14.object({
1895
- type: z14.literal("scrape")
2333
+ var scrapeActionSchema = z17.object({
2334
+ type: z17.literal("scrape")
1896
2335
  });
1897
- var executeJavascriptActionSchema = z14.object({
1898
- type: z14.literal("executeJavascript"),
1899
- script: z14.string()
2336
+ var executeJavascriptActionSchema = z17.object({
2337
+ type: z17.literal("executeJavascript"),
2338
+ script: z17.string()
1900
2339
  });
1901
- var pdfActionSchema = z14.object({
1902
- type: z14.literal("pdf"),
1903
- landscape: z14.boolean().optional(),
1904
- scale: z14.number().optional(),
1905
- format: z14.enum(["A0", "A1", "A2", "A3", "A4", "A5", "A6", "Letter", "Legal", "Tabloid", "Ledger"]).optional()
2340
+ var pdfActionSchema = z17.object({
2341
+ type: z17.literal("pdf"),
2342
+ landscape: z17.boolean().optional(),
2343
+ scale: z17.number().optional(),
2344
+ format: z17.enum(["A0", "A1", "A2", "A3", "A4", "A5", "A6", "Letter", "Legal", "Tabloid", "Ledger"]).optional()
1906
2345
  });
1907
- var actionSchema = z14.union([
2346
+ var actionSchema = z17.union([
1908
2347
  waitActionSchema,
1909
2348
  clickActionSchema,
1910
2349
  screenshotActionSchema,
@@ -1995,11 +2434,11 @@ var actionsCommand = registerCommand({
1995
2434
  name: "actions",
1996
2435
  description: "Execute a sequence of actions (wait, click, scroll, screenshot, fill, etc.)",
1997
2436
  scope: "page",
1998
- parameters: z14.object({
1999
- url: z14.string().describe("Starting URL"),
2000
- actions: z14.array(actionSchema).max(MAX_ACTIONS).describe("Array of actions (max 50)"),
2001
- output: z14.enum(["text", "json"]).default("json").describe("Output format: text or json"),
2002
- timeout: z14.number().default(60).describe("Overall timeout in seconds (default: 60)")
2437
+ parameters: z17.object({
2438
+ url: z17.string().describe("Starting URL"),
2439
+ actions: z17.array(actionSchema).max(MAX_ACTIONS).describe("Array of actions (max 50)"),
2440
+ output: z17.enum(["text", "json"]).default("json").describe("Output format: text or json"),
2441
+ timeout: z17.number().default(60).describe("Overall timeout in seconds (default: 60)")
2003
2442
  }),
2004
2443
  handler: async (p, ctx) => {
2005
2444
  await ctx.page.goto(p.url, { waitUntil: "domcontentloaded" });
@@ -2019,14 +2458,14 @@ var actionsCommand = registerCommand({
2019
2458
  const finalUrl = ctx.page.url();
2020
2459
  const timedOut = results.length < p.actions.length;
2021
2460
  if (p.output === "text") {
2022
- return ok14({
2461
+ return ok17({
2023
2462
  title,
2024
2463
  url: finalUrl,
2025
2464
  actions: results.map((r) => JSON.stringify(r)).join("\n"),
2026
2465
  ...timedOut ? { warning: `Timed out after ${p.timeout}s, completed ${results.length}/${p.actions.length} actions` } : {}
2027
2466
  });
2028
2467
  }
2029
- return ok14({
2468
+ return ok17({
2030
2469
  title,
2031
2470
  url: finalUrl,
2032
2471
  results,
@@ -2036,8 +2475,8 @@ var actionsCommand = registerCommand({
2036
2475
  });
2037
2476
 
2038
2477
  // src/commands/scrape.ts
2039
- import { z as z15 } from "zod";
2040
- import { ok as ok15, fail as fail5 } from "@dyyz1993/xcli-core";
2478
+ import { z as z18 } from "zod";
2479
+ import { ok as ok18, fail as fail7 } from "@dyyz1993/xcli-core";
2041
2480
 
2042
2481
  // src/lib/html-to-markdown.ts
2043
2482
  import * as cheerio from "cheerio";
@@ -2422,15 +2861,15 @@ var scrapeCommand = registerCommand({
2422
2861
  description: "Scrape a page and convert to Markdown (with JS rendering)",
2423
2862
  scope: "project",
2424
2863
  selectorParams: ["selector"],
2425
- parameters: z15.object({
2426
- url: z15.string().optional(),
2427
- selector: z15.string().optional(),
2428
- timeout: z15.number().default(3e4),
2429
- format: z15.enum(["markdown", "html", "text"]).default("markdown"),
2430
- onlyMainContent: z15.boolean().default(true),
2431
- retries: z15.number().int().min(0).max(5).optional().default(2).describe("\u91CD\u8BD5\u6B21\u6570\uFF08\u9ED8\u8BA4 2\uFF09"),
2432
- waitAfterLoad: z15.number().int().optional().default(0).describe("\u9875\u9762\u52A0\u8F7D\u540E\u989D\u5916\u7B49\u5F85\u6BEB\u79D2"),
2433
- 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")
2864
+ parameters: z18.object({
2865
+ url: z18.string().optional(),
2866
+ selector: z18.string().optional(),
2867
+ timeout: z18.number().default(3e4),
2868
+ format: z18.enum(["markdown", "html", "text"]).default("markdown"),
2869
+ onlyMainContent: z18.boolean().default(true),
2870
+ retries: z18.number().int().min(0).max(5).optional().default(2).describe("\u91CD\u8BD5\u6B21\u6570\uFF08\u9ED8\u8BA4 2\uFF09"),
2871
+ waitAfterLoad: z18.number().int().optional().default(0).describe("\u9875\u9762\u52A0\u8F7D\u540E\u989D\u5916\u7B49\u5F85\u6BEB\u79D2"),
2872
+ 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")
2434
2873
  }),
2435
2874
  handler: async (p, ctx) => {
2436
2875
  const { context, page } = await createEphemeralContext(resolveLaunchOpts(ctx));
@@ -2438,7 +2877,7 @@ var scrapeCommand = registerCommand({
2438
2877
  try {
2439
2878
  const targetUrl = p.url || page.url();
2440
2879
  if (!targetUrl || targetUrl === "about:blank") {
2441
- return fail5("URL is required. Provide a URL or connect to a browser (--cdp) with a non-blank page.");
2880
+ return fail7("URL is required. Provide a URL or connect to a browser (--cdp) with a non-blank page.");
2442
2881
  }
2443
2882
  let lastError;
2444
2883
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
@@ -2508,7 +2947,7 @@ var scrapeCommand = registerCommand({
2508
2947
  } catch {
2509
2948
  }
2510
2949
  if (p.mode === "smart") {
2511
- return ok15(structured);
2950
+ return ok18(structured);
2512
2951
  }
2513
2952
  if (p.mode === "compact") {
2514
2953
  const compactData = structured.tables.length > 0 ? structured.tables.map((t) => ({
@@ -2521,9 +2960,9 @@ var scrapeCommand = registerCommand({
2521
2960
  return compact;
2522
2961
  })
2523
2962
  })) : structured.mainText?.substring(0, 500);
2524
- return ok15({ url: structured.url, title: structured.title, data: compactData });
2963
+ return ok18({ url: structured.url, title: structured.title, data: compactData });
2525
2964
  }
2526
- return ok15(structured);
2965
+ return ok18(structured);
2527
2966
  }
2528
2967
  let content;
2529
2968
  switch (p.format) {
@@ -2582,7 +3021,7 @@ var scrapeCommand = registerCommand({
2582
3021
  content = await page.innerText("body");
2583
3022
  break;
2584
3023
  }
2585
- return ok15({ content, title, url: finalUrl });
3024
+ return ok18({ content, title, url: finalUrl });
2586
3025
  } catch (err) {
2587
3026
  lastError = err instanceof Error ? err : new Error(String(err));
2588
3027
  if (attempt < maxAttempts) {
@@ -2592,20 +3031,20 @@ var scrapeCommand = registerCommand({
2592
3031
  }
2593
3032
  }
2594
3033
  }
2595
- return fail5(`Scrape failed after ${maxAttempts} attempt(s): ${lastError?.message ?? "unknown error"}`);
3034
+ return fail7(`Scrape failed after ${maxAttempts} attempt(s): ${lastError?.message ?? "unknown error"}`);
2596
3035
  } finally {
2597
3036
  await closeEphemeralContext(context);
2598
3037
  }
2599
3038
  },
2600
- result: z15.object({
2601
- url: z15.string(),
2602
- title: z15.string()
3039
+ result: z18.object({
3040
+ url: z18.string(),
3041
+ title: z18.string()
2603
3042
  }).passthrough()
2604
3043
  });
2605
3044
 
2606
3045
  // src/commands/map.ts
2607
- import { z as z16 } from "zod";
2608
- import { ok as ok16, fail as fail6 } from "@dyyz1993/xcli-core";
3046
+ import { z as z19 } from "zod";
3047
+ import { ok as ok19, fail as fail8 } from "@dyyz1993/xcli-core";
2609
3048
 
2610
3049
  // src/utils/url.ts
2611
3050
  var SKIP_EXTENSIONS = /* @__PURE__ */ new Set([
@@ -2840,21 +3279,21 @@ var mapCommand = registerCommand({
2840
3279
  name: "map",
2841
3280
  description: "Discover all URLs on a website via sitemap and page link extraction",
2842
3281
  scope: "project",
2843
- parameters: z16.object({
2844
- url: z16.string().optional(),
2845
- search: z16.string().optional(),
2846
- sitemap: z16.enum(["include", "only"]).optional(),
2847
- includeSubdomains: z16.boolean().optional(),
2848
- allowExternalLinks: z16.boolean().optional().describe("Include links to external domains"),
2849
- limit: z16.number().optional(),
2850
- verbose: z16.boolean().default(false).describe("Show progress feedback")
3282
+ parameters: z19.object({
3283
+ url: z19.string().optional(),
3284
+ search: z19.string().optional(),
3285
+ sitemap: z19.enum(["include", "only"]).optional(),
3286
+ includeSubdomains: z19.boolean().optional(),
3287
+ allowExternalLinks: z19.boolean().optional().describe("Include links to external domains"),
3288
+ limit: z19.number().optional(),
3289
+ verbose: z19.boolean().default(false).describe("Show progress feedback")
2851
3290
  }),
2852
3291
  handler: async (p, ctx) => {
2853
3292
  const { context, page } = await createEphemeralContext(resolveLaunchOpts(ctx));
2854
3293
  try {
2855
3294
  const targetUrl = p.url || page.url();
2856
3295
  if (!targetUrl || targetUrl === "about:blank") {
2857
- return fail6("URL is required. Provide a URL or connect to a browser (--cdp) with a non-blank page.");
3296
+ return fail8("URL is required. Provide a URL or connect to a browser (--cdp) with a non-blank page.");
2858
3297
  }
2859
3298
  const links = await discoverUrls(page, targetUrl, {
2860
3299
  sitemap: p.sitemap,
@@ -2865,7 +3304,7 @@ var mapCommand = registerCommand({
2865
3304
  verbose: p.verbose
2866
3305
  });
2867
3306
  const linkObjects = links.map((url) => ({ url }));
2868
- return ok16({
3307
+ return ok19({
2869
3308
  links: linkObjects,
2870
3309
  success: true
2871
3310
  });
@@ -2873,15 +3312,15 @@ var mapCommand = registerCommand({
2873
3312
  await closeEphemeralContext(context);
2874
3313
  }
2875
3314
  },
2876
- result: z16.object({
2877
- links: z16.array(z16.object({ url: z16.string() })),
2878
- success: z16.boolean()
3315
+ result: z19.object({
3316
+ links: z19.array(z19.object({ url: z19.string() })),
3317
+ success: z19.boolean()
2879
3318
  })
2880
3319
  });
2881
3320
 
2882
3321
  // src/commands/crawl.ts
2883
- import { z as z17 } from "zod";
2884
- import { ok as ok17 } from "@dyyz1993/xcli-core";
3322
+ import { z as z20 } from "zod";
3323
+ import { ok as ok20 } from "@dyyz1993/xcli-core";
2885
3324
  function stripHashAnchorQuery(url) {
2886
3325
  try {
2887
3326
  const parsed = new URL(url);
@@ -3058,21 +3497,21 @@ var crawlCommand = registerCommand({
3058
3497
  name: "crawl",
3059
3498
  description: "Crawl a website and extract content from all pages",
3060
3499
  scope: "project",
3061
- parameters: z17.object({
3062
- url: z17.string(),
3063
- limit: z17.number().default(10),
3064
- maxDepth: z17.number().default(3),
3065
- includePaths: z17.array(z17.string()).optional(),
3066
- excludePaths: z17.array(z17.string()).optional(),
3067
- allowSubdomains: z17.boolean().default(false),
3068
- allowExternalLinks: z17.boolean().default(false),
3069
- allowBackwardCrawling: z17.boolean().default(false),
3070
- enableSpa: z17.boolean().default(true).describe("Disable to skip SPA route detection"),
3071
- format: z17.enum(["markdown", "html"]).default("markdown"),
3072
- onlyMainContent: z17.boolean().default(true),
3073
- concurrency: z17.number().default(3),
3074
- retries: z17.number().default(2),
3075
- verbose: z17.boolean().default(false)
3500
+ parameters: z20.object({
3501
+ url: z20.string(),
3502
+ limit: z20.number().default(10),
3503
+ maxDepth: z20.number().default(3),
3504
+ includePaths: z20.array(z20.string()).optional(),
3505
+ excludePaths: z20.array(z20.string()).optional(),
3506
+ allowSubdomains: z20.boolean().default(false),
3507
+ allowExternalLinks: z20.boolean().default(false),
3508
+ allowBackwardCrawling: z20.boolean().default(false),
3509
+ enableSpa: z20.boolean().default(true).describe("Disable to skip SPA route detection"),
3510
+ format: z20.enum(["markdown", "html"]).default("markdown"),
3511
+ onlyMainContent: z20.boolean().default(true),
3512
+ concurrency: z20.number().default(3),
3513
+ retries: z20.number().default(2),
3514
+ verbose: z20.boolean().default(false)
3076
3515
  }),
3077
3516
  handler: async (p, ctx) => {
3078
3517
  const startUrl = new URL(p.url);
@@ -3211,7 +3650,7 @@ var crawlCommand = registerCommand({
3211
3650
  if (errorPages.length > 0) {
3212
3651
  response.errors = errorPages;
3213
3652
  }
3214
- return ok17(response);
3653
+ return ok20(response);
3215
3654
  } finally {
3216
3655
  for (const ctx2 of contexts) {
3217
3656
  await ctx2.close().catch(() => {
@@ -3221,15 +3660,15 @@ var crawlCommand = registerCommand({
3221
3660
  } catch (err) {
3222
3661
  const message = err instanceof Error ? err.message : String(err);
3223
3662
  const successPages = results.filter((r) => !isPageError(r));
3224
- return ok17({ pages: successPages, total: successPages.length, success: false, error: message });
3663
+ return ok20({ pages: successPages, total: successPages.length, success: false, error: message });
3225
3664
  }
3226
3665
  }
3227
3666
  });
3228
3667
 
3229
3668
  // src/commands/search.ts
3230
- import { z as z18 } from "zod";
3669
+ import { z as z21 } from "zod";
3231
3670
  import * as cheerio2 from "cheerio";
3232
- import { ok as ok18 } from "@dyyz1993/xcli-core";
3671
+ import { ok as ok21 } from "@dyyz1993/xcli-core";
3233
3672
  function getRecencyParams(recency) {
3234
3673
  const now = Math.floor(Date.now() / 1e3);
3235
3674
  switch (recency) {
@@ -3563,16 +4002,16 @@ var searchCommand = registerCommand({
3563
4002
  name: "search",
3564
4003
  description: "Search the web and extract results with engine fallback",
3565
4004
  scope: "project",
3566
- parameters: z18.object({
3567
- query: z18.string(),
3568
- engine: z18.string().optional(),
3569
- limit: z18.number().default(10),
3570
- full: z18.boolean().default(false),
3571
- format: z18.enum(["markdown", "json", "text"]).default("markdown"),
3572
- timeout: z18.number().default(15e3),
3573
- recency: z18.enum(["hour", "day", "week", "month", "year"]).optional().describe("Filter by time: hour/day/week/month/year"),
3574
- fallback: z18.boolean().default(false).describe("Sequential engine fallback instead of parallel"),
3575
- site: z18.string().optional().describe("Limit results to a specific site (e.g. github.com, v2ex.com)")
4005
+ parameters: z21.object({
4006
+ query: z21.string(),
4007
+ engine: z21.string().optional(),
4008
+ limit: z21.number().default(10),
4009
+ full: z21.boolean().default(false),
4010
+ format: z21.enum(["markdown", "json", "text"]).default("markdown"),
4011
+ timeout: z21.number().default(15e3),
4012
+ recency: z21.enum(["hour", "day", "week", "month", "year"]).optional().describe("Filter by time: hour/day/week/month/year"),
4013
+ fallback: z21.boolean().default(false).describe("Sequential engine fallback instead of parallel"),
4014
+ site: z21.string().optional().describe("Limit results to a specific site (e.g. github.com, v2ex.com)")
3576
4015
  }),
3577
4016
  handler: async (p, ctx) => {
3578
4017
  const { context } = await createEphemeralContext(resolveLaunchOpts(ctx));
@@ -3678,7 +4117,7 @@ ${errors.map((e) => ` - ${e.engine}: ${e.error}`).join("\n")}`
3678
4117
  lines.push(`> ${r.snippet}`);
3679
4118
  lines.push("");
3680
4119
  }
3681
- return ok18({ ...searchResult, content: lines.join("\n") });
4120
+ return ok21({ ...searchResult, content: lines.join("\n") });
3682
4121
  }
3683
4122
  if (p.format === "text") {
3684
4123
  const lines = [`Search: ${searchResult.query} (Engine: ${searchResult.engine}, Total: ${searchResult.total})`, ""];
@@ -3688,9 +4127,9 @@ ${errors.map((e) => ` - ${e.engine}: ${e.error}`).join("\n")}`
3688
4127
  lines.push(` ${r.snippet}`);
3689
4128
  lines.push("");
3690
4129
  }
3691
- return ok18({ ...searchResult, content: lines.join("\n") });
4130
+ return ok21({ ...searchResult, content: lines.join("\n") });
3692
4131
  }
3693
- return ok18(searchResult);
4132
+ return ok21(searchResult);
3694
4133
  } finally {
3695
4134
  await closeEphemeralContext(context);
3696
4135
  }
@@ -3698,8 +4137,8 @@ ${errors.map((e) => ` - ${e.engine}: ${e.error}`).join("\n")}`
3698
4137
  });
3699
4138
 
3700
4139
  // src/commands/network.ts
3701
- import { z as z19 } from "zod";
3702
- import { ok as ok19, fail as fail7 } from "@dyyz1993/xcli-core";
4140
+ import { z as z22 } from "zod";
4141
+ import { ok as ok22, fail as fail9 } from "@dyyz1993/xcli-core";
3703
4142
  function extractPath2(url) {
3704
4143
  try {
3705
4144
  const u = new URL(url);
@@ -3797,19 +4236,19 @@ var networkCommand = registerCommand({
3797
4236
  name: "network",
3798
4237
  description: "Capture and filter network responses from a URL",
3799
4238
  scope: "project",
3800
- parameters: z19.object({
3801
- url: z19.string(),
3802
- filter: z19.string().optional(),
3803
- match: z19.string().optional(),
3804
- search: z19.string().optional().describe("Search within all captured response bodies"),
3805
- console: z19.boolean().default(false),
3806
- timeout: z19.number().default(3e4),
3807
- wait: z19.number().default(3e3),
3808
- limit: z19.number().default(50),
3809
- format: z19.enum(["summary", "json"]).default("summary"),
3810
- ws: z19.boolean().optional().default(false).describe("Only show WebSocket messages"),
3811
- 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"),
3812
- duration: z19.number().int().optional().default(15e3).describe("\u76D1\u542C\u6A21\u5F0F\u7B49\u5F85\u65F6\u957F\uFF08\u6BEB\u79D2\uFF0C\u9ED8\u8BA4 15000\uFF09")
4239
+ parameters: z22.object({
4240
+ url: z22.string(),
4241
+ filter: z22.string().optional(),
4242
+ match: z22.string().optional(),
4243
+ search: z22.string().optional().describe("Search within all captured response bodies"),
4244
+ console: z22.boolean().default(false),
4245
+ timeout: z22.number().default(3e4),
4246
+ wait: z22.number().default(3e3),
4247
+ limit: z22.number().default(50),
4248
+ format: z22.enum(["summary", "json"]).default("summary"),
4249
+ ws: z22.boolean().optional().default(false).describe("Only show WebSocket messages"),
4250
+ 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"),
4251
+ duration: z22.number().int().optional().default(15e3).describe("\u76D1\u542C\u6A21\u5F0F\u7B49\u5F85\u65F6\u957F\uFF08\u6BEB\u79D2\uFF0C\u9ED8\u8BA4 15000\uFF09")
3813
4252
  }),
3814
4253
  handler: async (p, ctx) => {
3815
4254
  const startTime = Date.now();
@@ -3864,7 +4303,7 @@ var networkCommand = registerCommand({
3864
4303
  };
3865
4304
  if (p.listen) {
3866
4305
  const page2 = ctx.page;
3867
- if (!page2) return fail7("No active page. Use --cdp to connect first.");
4306
+ if (!page2) return fail9("No active page. Use --cdp to connect first.");
3868
4307
  const captures = [];
3869
4308
  const consoleMessages = [];
3870
4309
  const wsCaptures = [];
@@ -3891,7 +4330,7 @@ var networkCommand = registerCommand({
3891
4330
  page2.off("response", handler);
3892
4331
  const duration = Date.now() - startTime;
3893
4332
  if (p.ws && wsCaptures.length > 0) {
3894
- return ok19({
4333
+ return ok22({
3895
4334
  url: "[listen mode]",
3896
4335
  duration,
3897
4336
  total: captures.length,
@@ -3905,9 +4344,9 @@ var networkCommand = registerCommand({
3905
4344
  });
3906
4345
  }
3907
4346
  if (p.format === "json") {
3908
- return ok19(buildJsonOutput("[listen mode]", captures, consoleMessages, captures.length, wsCaptures));
4347
+ return ok22(buildJsonOutput("[listen mode]", captures, consoleMessages, captures.length, wsCaptures));
3909
4348
  }
3910
- return ok19(buildSummaryOutput("[listen mode]", duration, captures, consoleMessages, captures.length, wsCaptures));
4349
+ return ok22(buildSummaryOutput("[listen mode]", duration, captures, consoleMessages, captures.length, wsCaptures));
3911
4350
  }
3912
4351
  const { context, page } = await createEphemeralContext(resolveLaunchOpts(ctx));
3913
4352
  try {
@@ -3962,7 +4401,7 @@ var networkCommand = registerCommand({
3962
4401
  }
3963
4402
  const duration = Date.now() - startTime;
3964
4403
  if (p.ws && wsCaptures.length > 0) {
3965
- return ok19({
4404
+ return ok22({
3966
4405
  url: p.url,
3967
4406
  duration,
3968
4407
  total: captures.length,
@@ -3976,9 +4415,9 @@ var networkCommand = registerCommand({
3976
4415
  });
3977
4416
  }
3978
4417
  if (p.format === "json") {
3979
- return ok19(buildJsonOutput(p.url, results, consoleMessages, totalCount, wsCaptures, searchResults));
4418
+ return ok22(buildJsonOutput(p.url, results, consoleMessages, totalCount, wsCaptures, searchResults));
3980
4419
  }
3981
- return ok19(buildSummaryOutput(p.url, duration, results, consoleMessages, totalCount, wsCaptures));
4420
+ return ok22(buildSummaryOutput(p.url, duration, results, consoleMessages, totalCount, wsCaptures));
3982
4421
  } finally {
3983
4422
  await closeEphemeralContext(context);
3984
4423
  }
@@ -3986,7 +4425,7 @@ var networkCommand = registerCommand({
3986
4425
  });
3987
4426
 
3988
4427
  // src/commands/ai-search-engines.ts
3989
- import { z as z20 } from "zod";
4428
+ import { z as z23 } from "zod";
3990
4429
  var ENGINE_CONFIGS = {
3991
4430
  deepseek: {
3992
4431
  key: "deepseek",
@@ -4240,7 +4679,7 @@ var ENGINE_CONFIGS = {
4240
4679
  }
4241
4680
  };
4242
4681
  var ALL_ENGINE_KEYS = Object.keys(ENGINE_CONFIGS);
4243
- var ENGINE_KEY_ENUM = z20.enum(ALL_ENGINE_KEYS);
4682
+ var ENGINE_KEY_ENUM = z23.enum(ALL_ENGINE_KEYS);
4244
4683
  function getEngineConfig(key) {
4245
4684
  return ENGINE_CONFIGS[key];
4246
4685
  }
@@ -4520,8 +4959,8 @@ function parseMarkdownResults(rawText) {
4520
4959
  }
4521
4960
 
4522
4961
  // src/commands/snapshot.ts
4523
- import { z as z21 } from "zod";
4524
- import { ok as ok20, fail as fail8, normalizeTips as normalizeTips3 } from "@dyyz1993/xcli-core";
4962
+ import { z as z24 } from "zod";
4963
+ import { ok as ok23, fail as fail10, normalizeTips as normalizeTips3 } from "@dyyz1993/xcli-core";
4525
4964
 
4526
4965
  // src/runtime/ref-store.ts
4527
4966
  var sessions = /* @__PURE__ */ new Map();
@@ -4762,8 +5201,8 @@ async function resolveRefParams(page, params, selectorKeys, cache, sessionId) {
4762
5201
  }
4763
5202
 
4764
5203
  // src/utils/site-semantics.ts
4765
- import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync, readFileSync } from "fs";
4766
- import { join as join4, dirname as dirname2 } from "path";
5204
+ import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
5205
+ import { join as join6, dirname as dirname2 } from "path";
4767
5206
  import { homedir as homedir4 } from "os";
4768
5207
  import { stringify, parse } from "yaml";
4769
5208
  import { execFile } from "child_process";
@@ -4841,10 +5280,10 @@ function inferAction(role, label) {
4841
5280
  return {};
4842
5281
  }
4843
5282
  function getSemanticsDir() {
4844
- return join4(homedir4(), ".xbrowser", "site-semantics");
5283
+ return join6(homedir4(), ".xbrowser", "site-semantics");
4845
5284
  }
4846
5285
  function getSemanticsPath(domain) {
4847
- return join4(getSemanticsDir(), `${domain}.yaml`);
5286
+ return join6(getSemanticsDir(), `${domain}.yaml`);
4848
5287
  }
4849
5288
  function extractDomain2(url) {
4850
5289
  try {
@@ -4858,9 +5297,9 @@ function saveSemantics(domain, pagePath, url, elements) {
4858
5297
  const filePath = getSemanticsPath(domain);
4859
5298
  const dir = dirname2(filePath);
4860
5299
  let site;
4861
- if (existsSync(filePath)) {
5300
+ if (existsSync3(filePath)) {
4862
5301
  try {
4863
- site = parse(readFileSync(filePath, "utf-8"));
5302
+ site = parse(readFileSync3(filePath, "utf-8"));
4864
5303
  } catch {
4865
5304
  site = { site: domain, pages: {} };
4866
5305
  }
@@ -4880,16 +5319,16 @@ function saveSemantics(domain, pagePath, url, elements) {
4880
5319
  }
4881
5320
  }
4882
5321
  site.updated_at = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
4883
- if (!existsSync(dir)) {
5322
+ if (!existsSync3(dir)) {
4884
5323
  mkdirSync3(dir, { recursive: true });
4885
5324
  }
4886
5325
  writeFileSync3(filePath, stringify(site, { lineWidth: 0 }));
4887
5326
  }
4888
5327
  function loadSemantics(domain) {
4889
5328
  const filePath = getSemanticsPath(domain);
4890
- if (!existsSync(filePath)) return null;
5329
+ if (!existsSync3(filePath)) return null;
4891
5330
  try {
4892
- return parse(readFileSync(filePath, "utf-8"));
5331
+ return parse(readFileSync3(filePath, "utf-8"));
4893
5332
  } catch {
4894
5333
  return null;
4895
5334
  }
@@ -5433,24 +5872,24 @@ var snapshotCommand = registerCommand({
5433
5872
  description: "Capture a quick page state snapshot \u2014 aria tree, visible text, or DOM summary",
5434
5873
  scope: "page",
5435
5874
  selectorParams: ["selector"],
5436
- parameters: z21.object({
5437
- type: z21.enum(["aria", "text", "dom", "all"]).default("aria").describe("Snapshot type: aria (accessibility tree), text (visible text), dom (element summary), all (combined)"),
5438
- selector: z21.string().optional().describe("Scope to a specific element"),
5439
- depth: z21.number().optional().default(6).describe("Max depth for DOM/aria tree"),
5440
- interactive: z21.boolean().optional().default(false).describe("Return interactive agent refs only"),
5441
- interactiveOnly: z21.boolean().optional().default(false).describe("Alias for interactive"),
5442
- i: z21.boolean().optional().default(false).describe("Short alias for interactive"),
5443
- compact: z21.boolean().optional().default(false).describe("Include compact xbrowser style snapshot text"),
5444
- c: z21.boolean().optional().default(false).describe("Short alias for compact"),
5445
- selectors: z21.boolean().optional().default(false).describe("Include ref to CSS selector map"),
5446
- all: z21.boolean().optional().default(false).describe("Include hidden interactive targets when using interactive snapshot")
5875
+ parameters: z24.object({
5876
+ type: z24.enum(["aria", "text", "dom", "all"]).default("aria").describe("Snapshot type: aria (accessibility tree), text (visible text), dom (element summary), all (combined)"),
5877
+ selector: z24.string().optional().describe("Scope to a specific element"),
5878
+ depth: z24.number().optional().default(6).describe("Max depth for DOM/aria tree"),
5879
+ interactive: z24.boolean().optional().default(false).describe("Return interactive agent refs only"),
5880
+ interactiveOnly: z24.boolean().optional().default(false).describe("Alias for interactive"),
5881
+ i: z24.boolean().optional().default(false).describe("Short alias for interactive"),
5882
+ compact: z24.boolean().optional().default(false).describe("Include compact xbrowser style snapshot text"),
5883
+ c: z24.boolean().optional().default(false).describe("Short alias for compact"),
5884
+ selectors: z24.boolean().optional().default(false).describe("Include ref to CSS selector map"),
5885
+ all: z24.boolean().optional().default(false).describe("Include hidden interactive targets when using interactive snapshot")
5447
5886
  }),
5448
- result: z21.object({
5449
- url: z21.string(),
5450
- title: z21.string(),
5451
- aria: z21.string().optional(),
5452
- text: z21.string().optional(),
5453
- dom: z21.record(z21.unknown()).optional()
5887
+ result: z24.object({
5888
+ url: z24.string(),
5889
+ title: z24.string(),
5890
+ aria: z24.string().optional(),
5891
+ text: z24.string().optional(),
5892
+ dom: z24.record(z24.unknown()).optional()
5454
5893
  }),
5455
5894
  handler: async (p, ctx) => {
5456
5895
  const page = ctx.page;
@@ -5464,7 +5903,7 @@ var snapshotCommand = registerCommand({
5464
5903
  if (p.compact || p.c || p.interactive || p.interactiveOnly || p.i) {
5465
5904
  observation.compact = formatObservationCompact(observation, { selectors: p.selectors });
5466
5905
  }
5467
- return ok20(observation, normalizeTips3([
5906
+ return ok23(observation, normalizeTips3([
5468
5907
  `refs refreshed for ${observation.targets.length} targets; use click @e1 or fill @e2 "text"`
5469
5908
  ]));
5470
5909
  }
@@ -5472,15 +5911,15 @@ var snapshotCommand = registerCommand({
5472
5911
  const aria = await captureAriaSnapshot(page, p.selector, p.depth);
5473
5912
  const tips = await buildRefTips(page, aria);
5474
5913
  persistSemantics(url, aria);
5475
- return ok20({ url, title, aria }, normalizeTips3(tips));
5914
+ return ok23({ url, title, aria }, normalizeTips3(tips));
5476
5915
  }
5477
5916
  if (p.type === "text") {
5478
5917
  const text = await captureTextSnapshot(page, p.selector);
5479
- return ok20({ url, title, text });
5918
+ return ok23({ url, title, text });
5480
5919
  }
5481
5920
  if (p.type === "dom") {
5482
5921
  const dom = await captureDomSnapshot(page, p.selector, p.depth ?? 6);
5483
- return ok20({ url, title, dom });
5922
+ return ok23({ url, title, dom });
5484
5923
  }
5485
5924
  if (p.type === "all") {
5486
5925
  const [aria, text, dom] = await Promise.all([
@@ -5490,9 +5929,9 @@ var snapshotCommand = registerCommand({
5490
5929
  ]);
5491
5930
  const tips = await buildRefTips(page, aria);
5492
5931
  persistSemantics(url, aria);
5493
- return ok20({ url, title, aria, text, dom }, normalizeTips3(tips));
5932
+ return ok23({ url, title, aria, text, dom }, normalizeTips3(tips));
5494
5933
  }
5495
- return fail8(`Unknown snapshot type: ${p.type}`);
5934
+ return fail10(`Unknown snapshot type: ${p.type}`);
5496
5935
  }
5497
5936
  });
5498
5937
  function persistSemantics(url, aria) {
@@ -5569,22 +6008,22 @@ async function captureDomSnapshot(page, selector, maxDepth) {
5569
6008
  }
5570
6009
 
5571
6010
  // src/commands/agent.ts
5572
- import { z as z22 } from "zod";
5573
- import { ok as ok21, normalizeTips as normalizeTips4 } from "@dyyz1993/xcli-core";
6011
+ import { z as z25 } from "zod";
6012
+ import { ok as ok24, normalizeTips as normalizeTips4 } from "@dyyz1993/xcli-core";
5574
6013
  var observeCommand = registerCommand({
5575
6014
  name: "observe",
5576
6015
  description: "Observe the current page as structured agent targets with session refs",
5577
6016
  scope: "page",
5578
- parameters: z22.object({
5579
- includeHidden: z22.boolean().optional().default(false).describe("Include hidden elements in the target list"),
5580
- limit: z22.number().int().positive().max(300).optional().default(80).describe("Maximum number of targets to return"),
5581
- compact: z22.boolean().optional().default(false).describe("Include compact xbrowser style snapshot text"),
5582
- selectors: z22.boolean().optional().default(false).describe("Include ref to stable CSS selector map")
6017
+ parameters: z25.object({
6018
+ includeHidden: z25.boolean().optional().default(false).describe("Include hidden elements in the target list"),
6019
+ limit: z25.number().int().positive().max(300).optional().default(80).describe("Maximum number of targets to return"),
6020
+ compact: z25.boolean().optional().default(false).describe("Include compact xbrowser style snapshot text"),
6021
+ selectors: z25.boolean().optional().default(false).describe("Include ref to stable CSS selector map")
5583
6022
  }),
5584
- result: z22.object({
5585
- targets: z22.array(z22.record(z22.unknown())),
5586
- selectors: z22.record(z22.unknown()).optional(),
5587
- compact: z22.string().optional()
6023
+ result: z25.object({
6024
+ targets: z25.array(z25.record(z25.unknown())),
6025
+ selectors: z25.record(z25.unknown()).optional(),
6026
+ compact: z25.string().optional()
5588
6027
  }).passthrough(),
5589
6028
  handler: async (p, ctx) => {
5590
6029
  const observation = await observePage(ctx.page, ctx.sessionId, {
@@ -5593,7 +6032,7 @@ var observeCommand = registerCommand({
5593
6032
  });
5594
6033
  if (p.selectors) observation.selectors = buildSelectorMap(observation);
5595
6034
  if (p.compact) observation.compact = formatObservationCompact(observation, { selectors: p.selectors });
5596
- return ok21(observation, normalizeTips4([
6035
+ return ok24(observation, normalizeTips4([
5597
6036
  `refs refreshed for ${observation.targets.length} targets; use act --ref @e1 --action click or click @e1`
5598
6037
  ]));
5599
6038
  }
@@ -5603,14 +6042,14 @@ var actCommand = registerCommand({
5603
6042
  description: "Perform an agent action using an observe ref or explicit selector",
5604
6043
  scope: "element",
5605
6044
  selectorParams: ["selector"],
5606
- parameters: z22.object({
5607
- action: z22.enum(["click", "fill", "type", "press", "select", "check", "hover"]).default("click"),
5608
- ref: z22.string().optional().describe("Session-scoped ref returned by observe, such as e1"),
5609
- selector: z22.string().optional().describe("CSS selector fallback when no ref is available"),
5610
- value: z22.string().optional().describe("Value for fill/type/select"),
5611
- key: z22.string().optional().describe("Key for press"),
5612
- force: z22.boolean().optional().default(false).describe("Bypass actionability checks"),
5613
- timeout: z22.number().optional().default(1e4).describe("Playwright action timeout in milliseconds")
6045
+ parameters: z25.object({
6046
+ action: z25.enum(["click", "fill", "type", "press", "select", "check", "hover"]).default("click"),
6047
+ ref: z25.string().optional().describe("Session-scoped ref returned by observe, such as e1"),
6048
+ selector: z25.string().optional().describe("CSS selector fallback when no ref is available"),
6049
+ value: z25.string().optional().describe("Value for fill/type/select"),
6050
+ key: z25.string().optional().describe("Key for press"),
6051
+ force: z25.boolean().optional().default(false).describe("Bypass actionability checks"),
6052
+ timeout: z25.number().optional().default(1e4).describe("Playwright action timeout in milliseconds")
5614
6053
  }).refine((p) => !!p.ref || !!p.selector, {
5615
6054
  message: "Either ref or selector is required"
5616
6055
  }),
@@ -5624,7 +6063,7 @@ var actCommand = registerCommand({
5624
6063
  tips: normalizeTips4(result.stale ? ["run observe again to refresh refs"] : [])
5625
6064
  };
5626
6065
  }
5627
- return ok21(result, normalizeTips4(result.stale ? ["ref screen hash changed; run observe if the next action is uncertain"] : []));
6066
+ return ok24(result, normalizeTips4(result.stale ? ["ref screen hash changed; run observe if the next action is uncertain"] : []));
5628
6067
  }
5629
6068
  });
5630
6069
  var waitForCommand = registerCommand({
@@ -5632,16 +6071,16 @@ var waitForCommand = registerCommand({
5632
6071
  description: "Wait for agent predicates such as text, URL, load state, selector state, or screen hash changes",
5633
6072
  scope: "page",
5634
6073
  selectorParams: ["selector"],
5635
- parameters: z22.object({
5636
- selector: z22.string().optional().describe("CSS selector or observe ref to wait for"),
5637
- state: z22.enum(["attached", "detached", "visible", "hidden"]).optional().default("visible"),
5638
- text: z22.string().optional().describe("Visible text to wait for"),
5639
- url: z22.string().optional().describe("URL substring or glob pattern to wait for"),
5640
- load: z22.enum(["load", "domcontentloaded", "networkidle"]).optional().describe("Load state to wait for"),
5641
- fn: z22.string().optional().describe("JavaScript predicate to wait for"),
5642
- screenHashChanged: z22.string().optional().describe("Previous screenHash from observe"),
5643
- timeout: z22.number().optional().default(3e4),
5644
- pollInterval: z22.number().optional().default(200)
6074
+ parameters: z25.object({
6075
+ selector: z25.string().optional().describe("CSS selector or observe ref to wait for"),
6076
+ state: z25.enum(["attached", "detached", "visible", "hidden"]).optional().default("visible"),
6077
+ text: z25.string().optional().describe("Visible text to wait for"),
6078
+ url: z25.string().optional().describe("URL substring or glob pattern to wait for"),
6079
+ load: z25.enum(["load", "domcontentloaded", "networkidle"]).optional().describe("Load state to wait for"),
6080
+ fn: z25.string().optional().describe("JavaScript predicate to wait for"),
6081
+ screenHashChanged: z25.string().optional().describe("Previous screenHash from observe"),
6082
+ timeout: z25.number().optional().default(3e4),
6083
+ pollInterval: z25.number().optional().default(200)
5645
6084
  }).refine((p) => [p.selector, p.text, p.url, p.load, p.fn, p.screenHashChanged].filter(Boolean).length === 1, {
5646
6085
  message: "Provide exactly one wait predicate: selector, text, url, load, fn, or screenHashChanged"
5647
6086
  }),
@@ -5655,30 +6094,30 @@ var waitForCommand = registerCommand({
5655
6094
  tips: []
5656
6095
  };
5657
6096
  }
5658
- return ok21(result);
6097
+ return ok24(result);
5659
6098
  }
5660
6099
  });
5661
6100
 
5662
6101
  // src/commands/tab.ts
5663
- import { z as z23 } from "zod";
5664
- import { ok as ok22, fail as fail9 } from "@dyyz1993/xcli-core";
5665
- var TabParams = z23.object({
5666
- subcommand: z23.enum(["list", "new", "close", "switch"]),
5667
- url: z23.string().optional(),
5668
- index: z23.number().int().min(0).optional()
6102
+ import { z as z26 } from "zod";
6103
+ import { ok as ok25, fail as fail11 } from "@dyyz1993/xcli-core";
6104
+ var TabParams = z26.object({
6105
+ subcommand: z26.enum(["list", "new", "close", "switch"]),
6106
+ url: z26.string().optional(),
6107
+ index: z26.number().int().min(0).optional()
5669
6108
  });
5670
6109
  var tabCommand = registerCommand({
5671
6110
  name: "tab",
5672
6111
  description: "Manage browser tabs: list, new, close, switch",
5673
6112
  scope: "page",
5674
6113
  parameters: TabParams,
5675
- result: z23.object({
5676
- success: z23.boolean(),
5677
- data: z23.unknown()
6114
+ result: z26.object({
6115
+ success: z26.boolean(),
6116
+ data: z26.unknown()
5678
6117
  }),
5679
6118
  handler: async (p, ctx) => {
5680
6119
  if (!ctx.browserContext) {
5681
- return fail9("No browser context available. Use --cdp to connect to a browser first.");
6120
+ return fail11("No browser context available. Use --cdp to connect to a browser first.");
5682
6121
  }
5683
6122
  const pages = ctx.browserContext.pages();
5684
6123
  switch (p.subcommand) {
@@ -5691,7 +6130,7 @@ var tabCommand = registerCommand({
5691
6130
  case "switch":
5692
6131
  return handleSwitch(p, pages, ctx);
5693
6132
  default:
5694
- return fail9(`Unknown subcommand: ${p.subcommand}`);
6133
+ return fail11(`Unknown subcommand: ${p.subcommand}`);
5695
6134
  }
5696
6135
  }
5697
6136
  });
@@ -5706,7 +6145,7 @@ async function handleList(pages, ctx) {
5706
6145
  if (isActive) activeIndex = i;
5707
6146
  tabs.push({ index: i, url, title, active: isActive });
5708
6147
  }
5709
- return ok22({ tabs, total: tabs.length, activeIndex });
6148
+ return ok25({ tabs, total: tabs.length, activeIndex });
5710
6149
  }
5711
6150
  async function handleNew(p, _pages, ctx) {
5712
6151
  const newPage = await ctx.browserContext.newPage();
@@ -5734,7 +6173,7 @@ async function handleNew(p, _pages, ctx) {
5734
6173
  const title = await newPage.title().catch(() => "");
5735
6174
  const allPages = ctx.browserContext.pages();
5736
6175
  const newIndex = allPages.indexOf(newPage);
5737
- return ok22({
6176
+ return ok25({
5738
6177
  index: newIndex >= 0 ? newIndex : allPages.length - 1,
5739
6178
  url: newPage.url(),
5740
6179
  title,
@@ -5745,11 +6184,11 @@ async function handleNew(p, _pages, ctx) {
5745
6184
  async function handleClose(p, ctx) {
5746
6185
  const currentPages = ctx.browserContext.pages();
5747
6186
  if (currentPages.length <= 1) {
5748
- return fail9("Cannot close the last remaining tab");
6187
+ return fail11("Cannot close the last remaining tab");
5749
6188
  }
5750
6189
  const closeIndex = p.index ?? currentPages.findIndex((pg) => pg === ctx.page);
5751
6190
  if (closeIndex < 0 || closeIndex >= currentPages.length) {
5752
- return fail9(`Invalid tab index: ${closeIndex}. Valid range: 0-${currentPages.length - 1}`);
6191
+ return fail11(`Invalid tab index: ${closeIndex}. Valid range: 0-${currentPages.length - 1}`);
5753
6192
  }
5754
6193
  const pageToClose = currentPages[closeIndex];
5755
6194
  const isActivePage = pageToClose === ctx.page;
@@ -5764,7 +6203,7 @@ async function handleClose(p, ctx) {
5764
6203
  }
5765
6204
  ctx.page = newActivePage;
5766
6205
  }
5767
- return ok22({
6206
+ return ok25({
5768
6207
  closedIndex: closeIndex,
5769
6208
  total: remainingPages.length,
5770
6209
  activeIndex: isActivePage ? closeIndex < remainingPages.length ? closeIndex : remainingPages.length - 1 : remainingPages.findIndex((pg) => pg === ctx.page)
@@ -5772,10 +6211,10 @@ async function handleClose(p, ctx) {
5772
6211
  }
5773
6212
  async function handleSwitch(p, pages, ctx) {
5774
6213
  if (p.index === void 0) {
5775
- return fail9("Parameter --index is required for switch subcommand");
6214
+ return fail11("Parameter --index is required for switch subcommand");
5776
6215
  }
5777
6216
  if (p.index < 0 || p.index >= pages.length) {
5778
- return fail9(`Invalid tab index: ${p.index}. Valid range: 0-${pages.length - 1}`);
6217
+ return fail11(`Invalid tab index: ${p.index}. Valid range: 0-${pages.length - 1}`);
5779
6218
  }
5780
6219
  const targetPage = pages[p.index];
5781
6220
  await targetPage.bringToFront().catch(() => {
@@ -5785,13 +6224,19 @@ async function handleSwitch(p, pages, ctx) {
5785
6224
  if (okProbe) break;
5786
6225
  await new Promise((r) => setTimeout(r, 300));
5787
6226
  }
6227
+ try {
6228
+ const mouse = targetPage.mouse;
6229
+ if (mouse) await mouse.move(1, 1).catch(() => {
6230
+ });
6231
+ } catch {
6232
+ }
5788
6233
  const session = ctx.sessionId ? getSessionById(ctx.sessionId) : void 0;
5789
6234
  if (session) {
5790
6235
  setActivePage(session, targetPage);
5791
6236
  }
5792
6237
  ctx.page = targetPage;
5793
6238
  const title = await targetPage.title().catch(() => "");
5794
- return ok22({
6239
+ return ok25({
5795
6240
  index: p.index,
5796
6241
  url: targetPage.url(),
5797
6242
  title,
@@ -5800,9 +6245,9 @@ async function handleSwitch(p, pages, ctx) {
5800
6245
  }
5801
6246
 
5802
6247
  // src/commands/addinitscript.ts
5803
- import { z as z24 } from "zod";
5804
- import { ok as ok23 } from "@dyyz1993/xcli-core";
5805
- import { readFileSync as readFileSync2 } from "fs";
6248
+ import { z as z27 } from "zod";
6249
+ import { ok as ok26 } from "@dyyz1993/xcli-core";
6250
+ import { readFileSync as readFileSync4 } from "fs";
5806
6251
 
5807
6252
  // src/chain-parser.ts
5808
6253
  import { splitCommand, parseCommandArgs } from "@dyyz1993/xcli-core";
@@ -5942,14 +6387,14 @@ registerCommandDefinition("tab", ["subcommand"]);
5942
6387
  registerCommandDefinition("mouse", ["action", "x", "y"]);
5943
6388
 
5944
6389
  // src/commands/addinitscript.ts
5945
- var InitScriptParams = z24.object({
5946
- script: z24.string().optional(),
5947
- file: z24.string().optional(),
5948
- stdin: z24.boolean().optional(),
5949
- name: z24.string().optional(),
5950
- list: z24.boolean().optional(),
5951
- remove: z24.union([z24.string(), z24.boolean()]).optional(),
5952
- base64: z24.string().optional()
6390
+ var InitScriptParams = z27.object({
6391
+ script: z27.string().optional(),
6392
+ file: z27.string().optional(),
6393
+ stdin: z27.boolean().optional(),
6394
+ name: z27.string().optional(),
6395
+ list: z27.boolean().optional(),
6396
+ remove: z27.union([z27.string(), z27.boolean()]).optional(),
6397
+ base64: z27.string().optional()
5953
6398
  });
5954
6399
  var registeredScripts = /* @__PURE__ */ new Map();
5955
6400
  function resolveScriptContent(params) {
@@ -5957,7 +6402,7 @@ function resolveScriptContent(params) {
5957
6402
  return Buffer.from(params.base64, "base64").toString("utf-8");
5958
6403
  }
5959
6404
  if (params.file) {
5960
- return readFileSync2(params.file, "utf-8");
6405
+ return readFileSync4(params.file, "utf-8");
5961
6406
  }
5962
6407
  if (params.stdin) {
5963
6408
  return null;
@@ -5983,14 +6428,14 @@ var addInitScriptCommand = registerCommand({
5983
6428
  description: "Add an initialization script that runs on every page load",
5984
6429
  scope: "page",
5985
6430
  parameters: InitScriptParams,
5986
- result: z24.object({
5987
- scripts: z24.array(z24.object({ name: z24.string(), size: z24.number(), preview: z24.string() })).optional(),
5988
- removed: z24.string().optional(),
5989
- existed: z24.boolean().optional(),
5990
- error: z24.string().optional(),
5991
- registered: z24.string().optional(),
5992
- hint: z24.string().optional(),
5993
- executedImmediately: z24.boolean().optional()
6431
+ result: z27.object({
6432
+ scripts: z27.array(z27.object({ name: z27.string(), size: z27.number(), preview: z27.string() })).optional(),
6433
+ removed: z27.string().optional(),
6434
+ existed: z27.boolean().optional(),
6435
+ error: z27.string().optional(),
6436
+ registered: z27.string().optional(),
6437
+ hint: z27.string().optional(),
6438
+ executedImmediately: z27.boolean().optional()
5994
6439
  }).passthrough(),
5995
6440
  handler: async (params, ctx) => {
5996
6441
  if (params.list) {
@@ -5999,20 +6444,20 @@ var addInitScriptCommand = registerCommand({
5999
6444
  size: content2.length,
6000
6445
  preview: content2.slice(0, 80)
6001
6446
  }));
6002
- return ok23({ scripts });
6447
+ return ok26({ scripts });
6003
6448
  }
6004
6449
  if (params.remove && typeof params.remove === "string") {
6005
6450
  const existed = registeredScripts.delete(params.remove);
6006
- return ok23({ removed: params.remove, existed });
6451
+ return ok26({ removed: params.remove, existed });
6007
6452
  }
6008
6453
  const removeTarget = (typeof params.remove === "string" ? params.remove : null) || (params.name && !resolveScriptContent(params) ? params.name : null);
6009
6454
  if (removeTarget && !resolveScriptContent(params)) {
6010
6455
  const existed = registeredScripts.delete(removeTarget);
6011
- return ok23({ removed: removeTarget, existed });
6456
+ return ok26({ removed: removeTarget, existed });
6012
6457
  }
6013
6458
  let content = params.stdin ? await readStdin() : resolveScriptContent(params);
6014
6459
  if (!content) {
6015
- return ok23({ error: "No script content provided. Use --script, --file, --stdin, or --base64" });
6460
+ return ok26({ error: "No script content provided. Use --script, --file, --stdin, or --base64" });
6016
6461
  }
6017
6462
  const scriptName = params.name ?? `script-${Date.now()}`;
6018
6463
  registeredScripts.set(scriptName, content);
@@ -6020,45 +6465,45 @@ var addInitScriptCommand = registerCommand({
6020
6465
  try {
6021
6466
  await ctx.page.evaluate(content);
6022
6467
  } catch {
6023
- return ok23({
6468
+ return ok26({
6024
6469
  registered: scriptName,
6025
6470
  hint: "Script registered for future page loads; immediate execution skipped (page may not be ready)"
6026
6471
  });
6027
6472
  }
6028
- return ok23({ registered: scriptName, executedImmediately: true });
6473
+ return ok26({ registered: scriptName, executedImmediately: true });
6029
6474
  }
6030
6475
  });
6031
6476
  registerCommandDefinition("addinitscript", ["script"]);
6032
6477
 
6033
6478
  // src/commands/find.ts
6034
- import { z as z25 } from "zod";
6035
- import { ok as ok24, fail as fail10, normalizeTips as normalizeTips5 } from "@dyyz1993/xcli-core";
6036
- var actionSchema2 = z25.enum(["click", "fill", "type", "select", "hover", "check"]);
6479
+ import { z as z28 } from "zod";
6480
+ import { ok as ok27, fail as fail12, normalizeTips as normalizeTips5 } from "@dyyz1993/xcli-core";
6481
+ var actionSchema2 = z28.enum(["click", "fill", "type", "select", "hover", "check"]);
6037
6482
  var findCommand = registerCommand({
6038
6483
  name: "find",
6039
6484
  description: "Find elements by semantic strategy (text/role/label/placeholder/testid) and optionally perform an action",
6040
6485
  scope: "page",
6041
- parameters: z25.object({
6042
- strategy: z25.enum(["text", "role", "label", "placeholder", "testid", "alt", "title", "first", "last", "nth"]),
6043
- value: z25.string(),
6044
- name: z25.string().optional(),
6045
- exact: z25.boolean().optional().default(false),
6046
- operation: z25.string().optional().describe('Trailing operation syntax, e.g. click, fill "text", type "text"'),
6486
+ parameters: z28.object({
6487
+ strategy: z28.enum(["text", "role", "label", "placeholder", "testid", "alt", "title", "first", "last", "nth"]),
6488
+ value: z28.string(),
6489
+ name: z28.string().optional(),
6490
+ exact: z28.boolean().optional().default(false),
6491
+ operation: z28.string().optional().describe('Trailing operation syntax, e.g. click, fill "text", type "text"'),
6047
6492
  action: actionSchema2.optional().describe("Action to perform when not using trailing operation syntax"),
6048
- actionValue: z25.string().optional().describe("Value for fill/type/select when using action"),
6049
- index: z25.number().int().optional().describe("Index for nth strategy"),
6050
- click: z25.boolean().optional().default(false),
6051
- fill: z25.string().optional(),
6052
- type: z25.string().optional(),
6053
- select: z25.string().optional(),
6054
- hover: z25.boolean().optional().default(false),
6055
- check: z25.boolean().optional().default(false),
6056
- timeout: z25.number().optional().default(1e4)
6493
+ actionValue: z28.string().optional().describe("Value for fill/type/select when using action"),
6494
+ index: z28.number().int().optional().describe("Index for nth strategy"),
6495
+ click: z28.boolean().optional().default(false),
6496
+ fill: z28.string().optional(),
6497
+ type: z28.string().optional(),
6498
+ select: z28.string().optional(),
6499
+ hover: z28.boolean().optional().default(false),
6500
+ check: z28.boolean().optional().default(false),
6501
+ timeout: z28.number().optional().default(1e4)
6057
6502
  }),
6058
- result: z25.object({
6059
- matched: z25.number(),
6060
- selector: z25.string(),
6061
- action: z25.string().optional()
6503
+ result: z28.object({
6504
+ matched: z28.number(),
6505
+ selector: z28.string(),
6506
+ action: z28.string().optional()
6062
6507
  }),
6063
6508
  handler: async (p, ctx) => {
6064
6509
  const page = ctx.page;
@@ -6073,7 +6518,7 @@ var findCommand = registerCommand({
6073
6518
  });
6074
6519
  const count = await locator.count();
6075
6520
  if (count === 0) {
6076
- return fail10(`No element found with ${p.strategy}="${p.value}"`);
6521
+ return fail12(`No element found with ${p.strategy}="${p.value}"`);
6077
6522
  }
6078
6523
  const tips = [];
6079
6524
  const target = selectTarget(locator, p.strategy);
@@ -6085,15 +6530,15 @@ var findCommand = registerCommand({
6085
6530
  await target.click({ timeout: p.timeout, force: true });
6086
6531
  return okWithTips({ matched: count, selector, action: "click" }, tips);
6087
6532
  } else if (actionName === "fill") {
6088
- if (actionValue === void 0) return fail10("find fill requires a value");
6533
+ if (actionValue === void 0) return fail12("find fill requires a value");
6089
6534
  await target.fill(actionValue, { timeout: p.timeout, force: true });
6090
6535
  return okWithTips({ matched: count, selector, action: `fill("${actionValue}")` }, tips);
6091
6536
  } else if (actionName === "type") {
6092
- if (actionValue === void 0) return fail10("find type requires a value");
6537
+ if (actionValue === void 0) return fail12("find type requires a value");
6093
6538
  await target.type(actionValue, { delay: 10, timeout: p.timeout });
6094
6539
  return okWithTips({ matched: count, selector, action: `type("${actionValue}")` }, tips);
6095
6540
  } else if (actionName === "select") {
6096
- if (actionValue === void 0) return fail10("find select requires a value");
6541
+ if (actionValue === void 0) return fail12("find select requires a value");
6097
6542
  await target.selectOption(actionValue);
6098
6543
  return okWithTips({ matched: count, selector, action: `select("${actionValue}")` }, tips);
6099
6544
  } else if (actionName === "hover") {
@@ -6107,7 +6552,7 @@ var findCommand = registerCommand({
6107
6552
  }
6108
6553
  });
6109
6554
  function okWithTips(data, tips) {
6110
- const result = ok24(data);
6555
+ const result = ok27(data);
6111
6556
  if (tips.length > 0) result.tips = normalizeTips5(tips);
6112
6557
  return result;
6113
6558
  }
@@ -6231,7 +6676,7 @@ function attachWaitForHuman(ctx, getOrCreateWSServer) {
6231
6676
  if (!ctx.page) {
6232
6677
  throw new Error("waitForHuman requires an active page");
6233
6678
  }
6234
- const { HumanInteractionManager: HumanInteractionManager2 } = await import("./human-interaction-C5OEGXGO.js");
6679
+ const { HumanInteractionManager: HumanInteractionManager2 } = await import("./human-interaction-Y5IDH6LD.js");
6235
6680
  const wsServer2 = await getOrCreateWSServer(ctx.browserContext);
6236
6681
  const manager = new HumanInteractionManager2(wsServer2, ctx.page);
6237
6682
  return manager.waitForHuman(options);
@@ -6255,18 +6700,18 @@ import {
6255
6700
  Core
6256
6701
  } from "@dyyz1993/xcli-core";
6257
6702
  import { resolve as resolve2 } from "path";
6258
- import { existsSync as existsSync4, readdirSync } from "fs";
6703
+ import { existsSync as existsSync6, readdirSync } from "fs";
6259
6704
  import { homedir as homedir5 } from "os";
6260
6705
 
6261
6706
  // src/plugin/metadata-parser.ts
6262
- import { existsSync as existsSync2 } from "fs";
6707
+ import { existsSync as existsSync4 } from "fs";
6263
6708
  import { resolve } from "path";
6264
6709
 
6265
6710
  // src/utils/json-file.ts
6266
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "fs";
6711
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
6267
6712
  function readJsonFile(filePath, defaultValue) {
6268
6713
  try {
6269
- const content = readFileSync3(filePath, "utf-8");
6714
+ const content = readFileSync5(filePath, "utf-8");
6270
6715
  return JSON.parse(content);
6271
6716
  } catch {
6272
6717
  return defaultValue;
@@ -6278,7 +6723,7 @@ var PluginMetadataParser = class {
6278
6723
  static XBROWSER_KEYWORDS = ["xbrowser", "xbrowser-plugin"];
6279
6724
  static parseFromPackageJson(pluginPath) {
6280
6725
  const packageJsonPath = resolve(pluginPath, "package.json");
6281
- if (!existsSync2(packageJsonPath)) {
6726
+ if (!existsSync4(packageJsonPath)) {
6282
6727
  return null;
6283
6728
  }
6284
6729
  const packageJson = readJsonFile(packageJsonPath, null);
@@ -6342,20 +6787,20 @@ var PluginMetadataParser = class {
6342
6787
  };
6343
6788
 
6344
6789
  // src/plugin/ensure-deps.ts
6345
- import { existsSync as existsSync3, mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "fs";
6346
- import { join as join5 } from "path";
6790
+ import { existsSync as existsSync5, mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "fs";
6791
+ import { join as join7 } from "path";
6347
6792
  import { execSync } from "child_process";
6348
6793
  var SHARED_PLUGIN_DEPENDENCIES = {
6349
6794
  "zod": "^3.24.0",
6350
6795
  "@dyyz1993/xcli-core": "^0.12.1"
6351
6796
  };
6352
6797
  function ensurePluginDependencies(pluginsDir) {
6353
- const zodPath = join5(pluginsDir, "node_modules", "zod");
6354
- if (existsSync3(zodPath)) return;
6798
+ const zodPath = join7(pluginsDir, "node_modules", "zod");
6799
+ if (existsSync5(zodPath)) return;
6355
6800
  mkdirSync4(pluginsDir, { recursive: true });
6356
- const pkgPath = join5(pluginsDir, "package.json");
6801
+ const pkgPath = join7(pluginsDir, "package.json");
6357
6802
  let pkg = {};
6358
- if (existsSync3(pkgPath)) {
6803
+ if (existsSync5(pkgPath)) {
6359
6804
  try {
6360
6805
  pkg = readJsonFile(pkgPath, {});
6361
6806
  } catch {
@@ -6369,7 +6814,7 @@ function ensurePluginDependencies(pluginsDir) {
6369
6814
  needsInstall = true;
6370
6815
  }
6371
6816
  }
6372
- if (!needsInstall && existsSync3(join5(pluginsDir, "node_modules"))) return;
6817
+ if (!needsInstall && existsSync5(join7(pluginsDir, "node_modules"))) return;
6373
6818
  pkg.dependencies = existingDeps;
6374
6819
  pkg.private = true;
6375
6820
  pkg.description = pkg.description || "xbrowser plugins \u2014 shared dependencies";
@@ -6627,7 +7072,7 @@ var XBrowserPluginLoader = class {
6627
7072
  const loaded = [];
6628
7073
  const seen = /* @__PURE__ */ new Set();
6629
7074
  for (const dir of dirs) {
6630
- if (!existsSync4(dir)) continue;
7075
+ if (!existsSync6(dir)) continue;
6631
7076
  const entries = readdirSync(dir, { withFileTypes: true });
6632
7077
  for (const entry of entries) {
6633
7078
  if (!entry.isDirectory()) continue;
@@ -6635,12 +7080,12 @@ var XBrowserPluginLoader = class {
6635
7080
  seen.add(entry.name);
6636
7081
  const pluginDir = resolve2(dir, entry.name);
6637
7082
  let indexPath = resolve2(pluginDir, "index.js");
6638
- if (!existsSync4(indexPath)) {
7083
+ if (!existsSync6(indexPath)) {
6639
7084
  indexPath = resolve2(pluginDir, "index.ts");
6640
7085
  }
6641
- if (!existsSync4(indexPath)) continue;
7086
+ if (!existsSync6(indexPath)) continue;
6642
7087
  try {
6643
- if (!existsSync4(resolve2(pluginDir, "package.json"))) {
7088
+ if (!existsSync6(resolve2(pluginDir, "package.json"))) {
6644
7089
  console.warn(`\u26A0\uFE0F Plugin "${entry.name}" has no package.json. Use "xbrowser create ${entry.name} --template static" for proper structure.`);
6645
7090
  } else {
6646
7091
  const metadata = PluginMetadataParser.parseFromPackageJson(pluginDir);
@@ -7297,7 +7742,7 @@ async function loadHooks() {
7297
7742
 
7298
7743
  // src/executor.ts
7299
7744
  import { homedir as homedir6 } from "os";
7300
- import { join as join6 } from "path";
7745
+ import { join as join8 } from "path";
7301
7746
  function levenshtein(a, b) {
7302
7747
  const m = a.length, n = b.length;
7303
7748
  const dp = Array(m + 1).fill(null).map(() => Array(n + 1).fill(0));
@@ -7312,7 +7757,7 @@ function levenshtein(a, b) {
7312
7757
  }
7313
7758
  var NAVIGATION_COMMANDS = /* @__PURE__ */ new Set(["goto", "back", "forward", "refresh"]);
7314
7759
  var snapshotHintShown = /* @__PURE__ */ new WeakSet();
7315
- var CONFIG_DIR2 = join6(homedir6(), ".xbrowser");
7760
+ var CONFIG_DIR2 = join8(homedir6(), ".xbrowser");
7316
7761
  var storageCache = /* @__PURE__ */ new Map();
7317
7762
  function getPluginStorage(pluginName) {
7318
7763
  if (!storageCache.has(pluginName)) {
@@ -7324,7 +7769,7 @@ var archiveInitialized = false;
7324
7769
  function ensureArchiveInit() {
7325
7770
  if (!archiveInitialized) {
7326
7771
  try {
7327
- configureArchiveStore({ archiveDir: join6(homedir6(), ".xbrowser", "archives") });
7772
+ configureArchiveStore({ archiveDir: join8(homedir6(), ".xbrowser", "archives") });
7328
7773
  } catch {
7329
7774
  }
7330
7775
  archiveInitialized = true;
@@ -7348,7 +7793,7 @@ async function guardCheck(commandName) {
7348
7793
  }
7349
7794
  }
7350
7795
  function errorResult(message) {
7351
- return { ...fail11(message), duration: 0 };
7796
+ return { ...fail13(message), duration: 0 };
7352
7797
  }
7353
7798
  function tipsToMessages(tips) {
7354
7799
  if (!tips || tips.length === 0) return [];
@@ -7428,7 +7873,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7428
7873
  }
7429
7874
  let targetPageOverride = null;
7430
7875
  if (_target && extraOpts?.cdpEndpoint) {
7431
- const { findTargetPage } = await import("./browser-V3JIWSTR.js");
7876
+ const { findTargetPage } = await import("./browser-T7OVLAEC.js");
7432
7877
  targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
7433
7878
  if (!targetPageOverride) {
7434
7879
  return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
@@ -7445,8 +7890,15 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7445
7890
  params = result.data;
7446
7891
  }
7447
7892
  if (command.scope !== "cli" && !process.env.XBROWSER_DAEMON_WORKER) {
7448
- const { forwardExec } = await import("./daemon-client-Y2YSZCGK.js");
7449
- const result = await forwardExec(commandName, params, sessionName, extraOpts?.cdpEndpoint);
7893
+ const { forwardExec } = await import("./daemon-client-HEGAXWGK.js");
7894
+ const longRunning = /^(vision-task|crawl|scrape|record|replay)$/.test(commandName);
7895
+ const result = await forwardExec(
7896
+ commandName,
7897
+ params,
7898
+ sessionName,
7899
+ extraOpts?.cdpEndpoint,
7900
+ longRunning ? 20 * 6e4 : void 0
7901
+ );
7450
7902
  if (result) return result;
7451
7903
  }
7452
7904
  let session;
@@ -7614,7 +8066,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7614
8066
  timestamp: start
7615
8067
  });
7616
8068
  if (isSuccess) {
7617
- return { ...ok25(raw.data, merged.length > 0 ? merged : raw.tips), duration, ...hookOutputs ? { hookOutputs } : {} };
8069
+ return { ...ok28(raw.data, merged.length > 0 ? merged : raw.tips), duration, ...hookOutputs ? { hookOutputs } : {} };
7618
8070
  }
7619
8071
  return { success: false, data: raw.data, message: raw.message, tips: mergedOrRaw, duration, ...hookOutputs ? { hookOutputs } : {} };
7620
8072
  }
@@ -7628,14 +8080,14 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7628
8080
  duration,
7629
8081
  timestamp: start
7630
8082
  });
7631
- return { ...ok25(raw, smartTipNormalized), duration, ...hookOutputs ? { hookOutputs } : {} };
8083
+ return { ...ok28(raw, smartTipNormalized), duration, ...hookOutputs ? { hookOutputs } : {} };
7632
8084
  } catch (err) {
7633
8085
  const end = Date.now();
7634
8086
  const duration = end - start;
7635
8087
  const errorMessage = errMsg(err);
7636
8088
  if (session?.page && process.env.XBROWSER_RECOVERY && !extraOpts?._recoveryAttempted) {
7637
8089
  try {
7638
- const { attemptRecovery } = await import("./recovery-ZJVVHP7N.js");
8090
+ const { attemptRecovery } = await import("./recovery-L5GLDX4O.js");
7639
8091
  const recovery = await attemptRecovery(
7640
8092
  session.page,
7641
8093
  sessionName,
@@ -7673,7 +8125,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7673
8125
  duration,
7674
8126
  timestamp: start
7675
8127
  });
7676
- return { ...fail11(errorMessage), duration };
8128
+ return { ...fail13(errorMessage), duration };
7677
8129
  } finally {
7678
8130
  }
7679
8131
  }
@@ -7714,7 +8166,7 @@ async function executeChain(input, options) {
7714
8166
  results.push({
7715
8167
  command: cmdName,
7716
8168
  raw: cmdStr,
7717
- ...fail11(`Plugin "${cmdName}" requires a sub-command`),
8169
+ ...fail13(`Plugin "${cmdName}" requires a sub-command`),
7718
8170
  duration: 0
7719
8171
  });
7720
8172
  if (type === "and") {
@@ -7733,7 +8185,7 @@ async function executeChain(input, options) {
7733
8185
  results.push({
7734
8186
  command: cmdName,
7735
8187
  raw: cmdStr,
7736
- ...fail11(`Unknown command "${subCommand}" for plugin "${cmdName}"`),
8188
+ ...fail13(`Unknown command "${subCommand}" for plugin "${cmdName}"`),
7737
8189
  duration: 0
7738
8190
  });
7739
8191
  if (type === "and") {
@@ -7837,7 +8289,7 @@ async function executeChain(input, options) {
7837
8289
  results.push({
7838
8290
  command: `${cmdName} ${subCommand}`,
7839
8291
  raw: cmdStr,
7840
- ...ok25(data),
8292
+ ...ok28(data),
7841
8293
  duration: duration2,
7842
8294
  ...hookOutputs ? { hookOutputs } : {}
7843
8295
  });
@@ -7865,7 +8317,7 @@ async function executeChain(input, options) {
7865
8317
  results.push({
7866
8318
  command: `${cmdName} ${subCommand}`,
7867
8319
  raw: cmdStr,
7868
- ...fail11(errorMessage),
8320
+ ...fail13(errorMessage),
7869
8321
  duration: duration2
7870
8322
  });
7871
8323
  if (type === "and") {
@@ -8179,25 +8631,25 @@ Run "xbrowser config list" to see current keys.`
8179
8631
 
8180
8632
  // src/plugin/installer.ts
8181
8633
  import {
8182
- existsSync as existsSync10,
8634
+ existsSync as existsSync12,
8183
8635
  readdirSync as readdirSync2,
8184
8636
  mkdirSync as mkdirSync8,
8185
8637
  rmSync as rmSync6,
8186
8638
  copyFileSync,
8187
8639
  cpSync as cpSync6,
8188
- readFileSync as readFileSync8,
8640
+ readFileSync as readFileSync10,
8189
8641
  writeFileSync as writeFileSync10
8190
8642
  } from "fs";
8191
8643
  import { resolve as resolve8, basename as basename2, dirname as dirname4 } from "path";
8192
8644
  import { homedir as homedir7 } from "os";
8193
8645
 
8194
8646
  // src/plugin/install-sources/local.ts
8195
- import { existsSync as existsSync5, cpSync, rmSync } from "fs";
8647
+ import { existsSync as existsSync7, cpSync, rmSync } from "fs";
8196
8648
  import { resolve as resolve3 } from "path";
8197
8649
  import { verifyPlugin, safeCleanup } from "@dyyz1993/xcli-core";
8198
8650
  async function installFromLocal(source, name, targetDir) {
8199
8651
  const srcPath = resolve3(source);
8200
- if (!existsSync5(srcPath)) {
8652
+ if (!existsSync7(srcPath)) {
8201
8653
  throw new Error(`Local path does not exist: ${srcPath}`);
8202
8654
  }
8203
8655
  const tmpTarget = `${targetDir}-tmp-${Date.now()}`;
@@ -8210,7 +8662,7 @@ async function installFromLocal(source, name, targetDir) {
8210
8662
  safeCleanup(tmpTarget);
8211
8663
  throw new Error(`Invalid plugin: ${verify.error}`);
8212
8664
  }
8213
- if (existsSync5(targetDir)) {
8665
+ if (existsSync7(targetDir)) {
8214
8666
  rmSync(targetDir, { recursive: true, force: true });
8215
8667
  }
8216
8668
  cpSync(tmpTarget, targetDir, { recursive: true, force: true });
@@ -8230,8 +8682,8 @@ async function installFromLocal(source, name, targetDir) {
8230
8682
  }
8231
8683
 
8232
8684
  // src/plugin/install-sources/npm.ts
8233
- import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync6, rmSync as rmSync2, cpSync as cpSync2 } from "fs";
8234
- import { resolve as resolve4, join as join7 } from "path";
8685
+ import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync6, rmSync as rmSync2, cpSync as cpSync2 } from "fs";
8686
+ import { resolve as resolve4, join as join9 } from "path";
8235
8687
  import { tmpdir } from "os";
8236
8688
  import {
8237
8689
  downloadToFile,
@@ -8304,13 +8756,13 @@ async function installFromNpm(packageName, name, targetDir) {
8304
8756
  throw new Error(`No tarball URL for ${packageName}@${latestVersion}`);
8305
8757
  }
8306
8758
  const tarballUrl = versionMeta.dist.tarball;
8307
- const tmpDir = join7(tmpdir(), `xbrowser-npm-${Date.now()}`);
8759
+ const tmpDir = join9(tmpdir(), `xbrowser-npm-${Date.now()}`);
8308
8760
  mkdirSync5(tmpDir, { recursive: true });
8309
8761
  let warnings = [];
8310
8762
  try {
8311
- const tarballPath = join7(tmpDir, `${name}.tgz`);
8763
+ const tarballPath = join9(tmpDir, `${name}.tgz`);
8312
8764
  await downloadToFile(tarballUrl, tarballPath);
8313
- const extractDir = join7(tmpDir, "extracted");
8765
+ const extractDir = join9(tmpDir, "extracted");
8314
8766
  extractTarGz(tarballPath, extractDir);
8315
8767
  flattenPackageRoot(extractDir);
8316
8768
  const verify = verifyPlugin2(extractDir, { metadataField: "xbrowser" });
@@ -8318,13 +8770,13 @@ async function installFromNpm(packageName, name, targetDir) {
8318
8770
  if (!verify.valid) {
8319
8771
  throw new Error(`Invalid npm plugin: ${verify.error}`);
8320
8772
  }
8321
- if (existsSync6(targetDir)) {
8773
+ if (existsSync8(targetDir)) {
8322
8774
  rmSync2(targetDir, { recursive: true, force: true });
8323
8775
  }
8324
8776
  cpSync2(extractDir, targetDir, { recursive: true, force: true });
8325
8777
  const pkgPath = resolve4(targetDir, "package.json");
8326
- if (existsSync6(pkgPath)) {
8327
- const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
8778
+ if (existsSync8(pkgPath)) {
8779
+ const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
8328
8780
  if (!pkg._npmSource) {
8329
8781
  pkg._npmSource = { name: packageName, version: latestVersion };
8330
8782
  writeFileSync6(pkgPath, JSON.stringify(pkg, null, 2));
@@ -8344,13 +8796,13 @@ async function installFromNpm(packageName, name, targetDir) {
8344
8796
  }
8345
8797
 
8346
8798
  // src/plugin/install-sources/git.ts
8347
- import { existsSync as existsSync7, readFileSync as readFileSync5, writeFileSync as writeFileSync7, rmSync as rmSync3, cpSync as cpSync3 } from "fs";
8348
- import { resolve as resolve5, join as join8 } from "path";
8799
+ import { existsSync as existsSync9, readFileSync as readFileSync7, writeFileSync as writeFileSync7, rmSync as rmSync3, cpSync as cpSync3 } from "fs";
8800
+ import { resolve as resolve5, join as join10 } from "path";
8349
8801
  import { tmpdir as tmpdir2 } from "os";
8350
8802
  import { execSync as execSync2 } from "child_process";
8351
8803
  import { verifyPlugin as verifyPlugin3, safeCleanup as safeCleanup3 } from "@dyyz1993/xcli-core";
8352
8804
  async function installFromGit(gitUrl, name, targetDir) {
8353
- const tmpDir = join8(tmpdir2(), `xbrowser-git-${Date.now()}`);
8805
+ const tmpDir = join10(tmpdir2(), `xbrowser-git-${Date.now()}`);
8354
8806
  let warnings = [];
8355
8807
  try {
8356
8808
  execSync2(`git clone --depth 1 "${gitUrl}" "${tmpDir}"`, { stdio: "pipe" });
@@ -8359,14 +8811,14 @@ async function installFromGit(gitUrl, name, targetDir) {
8359
8811
  if (!verify.valid) {
8360
8812
  throw new Error(`Invalid git plugin: ${verify.error}`);
8361
8813
  }
8362
- if (existsSync7(targetDir)) {
8814
+ if (existsSync9(targetDir)) {
8363
8815
  rmSync3(targetDir, { recursive: true, force: true });
8364
8816
  }
8365
8817
  cpSync3(tmpDir, targetDir, { recursive: true, force: true });
8366
8818
  rmSync3(resolve5(targetDir, ".git"), { recursive: true, force: true });
8367
8819
  const pkgPath = resolve5(targetDir, "package.json");
8368
- if (existsSync7(pkgPath)) {
8369
- const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
8820
+ if (existsSync9(pkgPath)) {
8821
+ const pkg = JSON.parse(readFileSync7(pkgPath, "utf-8"));
8370
8822
  if (!pkg._gitSource) {
8371
8823
  pkg._gitSource = { url: gitUrl };
8372
8824
  writeFileSync7(pkgPath, JSON.stringify(pkg, null, 2));
@@ -8386,8 +8838,8 @@ async function installFromGit(gitUrl, name, targetDir) {
8386
8838
  }
8387
8839
 
8388
8840
  // src/plugin/install-sources/url.ts
8389
- import { existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync8, rmSync as rmSync4, cpSync as cpSync4 } from "fs";
8390
- import { resolve as resolve6, join as join9, basename } from "path";
8841
+ import { existsSync as existsSync10, mkdirSync as mkdirSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync8, rmSync as rmSync4, cpSync as cpSync4 } from "fs";
8842
+ import { resolve as resolve6, join as join11, basename } from "path";
8391
8843
  import { tmpdir as tmpdir3 } from "os";
8392
8844
  import {
8393
8845
  downloadToFile as downloadToFile2,
@@ -8397,14 +8849,14 @@ import {
8397
8849
  safeCleanup as safeCleanup4
8398
8850
  } from "@dyyz1993/xcli-core";
8399
8851
  async function installFromUrl(url, name, targetDir) {
8400
- const tmpDir = join9(tmpdir3(), `xbrowser-url-${Date.now()}`);
8852
+ const tmpDir = join11(tmpdir3(), `xbrowser-url-${Date.now()}`);
8401
8853
  mkdirSync6(tmpDir, { recursive: true });
8402
8854
  let warnings = [];
8403
8855
  try {
8404
8856
  const fileName = basename(new URL(url).pathname) || "plugin.tar.gz";
8405
- const tarballPath = join9(tmpDir, fileName);
8857
+ const tarballPath = join11(tmpDir, fileName);
8406
8858
  await downloadToFile2(url, tarballPath);
8407
- const extractDir = join9(tmpDir, "extracted");
8859
+ const extractDir = join11(tmpDir, "extracted");
8408
8860
  extractTarGz2(tarballPath, extractDir);
8409
8861
  flattenPackageRoot2(extractDir);
8410
8862
  const verify = verifyPlugin4(extractDir, { metadataField: "xbrowser" });
@@ -8412,13 +8864,13 @@ async function installFromUrl(url, name, targetDir) {
8412
8864
  if (!verify.valid) {
8413
8865
  throw new Error(`Invalid plugin from URL: ${verify.error}`);
8414
8866
  }
8415
- if (existsSync8(targetDir)) {
8867
+ if (existsSync10(targetDir)) {
8416
8868
  rmSync4(targetDir, { recursive: true, force: true });
8417
8869
  }
8418
8870
  cpSync4(extractDir, targetDir, { recursive: true, force: true });
8419
8871
  const pkgPath = resolve6(targetDir, "package.json");
8420
- if (existsSync8(pkgPath)) {
8421
- const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
8872
+ if (existsSync10(pkgPath)) {
8873
+ const pkg = JSON.parse(readFileSync8(pkgPath, "utf-8"));
8422
8874
  if (!pkg._urlSource) {
8423
8875
  pkg._urlSource = { url };
8424
8876
  writeFileSync8(pkgPath, JSON.stringify(pkg, null, 2));
@@ -8439,14 +8891,14 @@ async function installFromUrl(url, name, targetDir) {
8439
8891
 
8440
8892
  // src/plugin/install-sources/marketplace.ts
8441
8893
  import {
8442
- existsSync as existsSync9,
8894
+ existsSync as existsSync11,
8443
8895
  mkdirSync as mkdirSync7,
8444
8896
  writeFileSync as writeFileSync9,
8445
- readFileSync as readFileSync7,
8897
+ readFileSync as readFileSync9,
8446
8898
  rmSync as rmSync5,
8447
8899
  cpSync as cpSync5
8448
8900
  } from "fs";
8449
- import { resolve as resolve7, join as join10, dirname as dirname3 } from "path";
8901
+ import { resolve as resolve7, join as join12, dirname as dirname3 } from "path";
8450
8902
  import { tmpdir as tmpdir4 } from "os";
8451
8903
  import { gunzipSync } from "zlib";
8452
8904
  import {
@@ -8471,11 +8923,11 @@ async function installFromMarketplace(pluginsDir, slug, options) {
8471
8923
  const plugin = detailData.data;
8472
8924
  const name = options?.name || String(plugin.slug || slug);
8473
8925
  const targetDir = resolve7(pluginsDir, name);
8474
- if (existsSync9(targetDir) && !options?.force) {
8926
+ if (existsSync11(targetDir) && !options?.force) {
8475
8927
  throw new Error(`Plugin "${name}" already exists. Use --force to overwrite.`);
8476
8928
  }
8477
8929
  mkdirSync7(targetDir, { recursive: true });
8478
- const tmpDir = join10(tmpdir4(), `xbrowser-marketplace-${Date.now()}`);
8930
+ const tmpDir = join12(tmpdir4(), `xbrowser-marketplace-${Date.now()}`);
8479
8931
  mkdirSync7(tmpDir, { recursive: true });
8480
8932
  const realSlug = String(plugin.slug || slug);
8481
8933
  try {
@@ -8506,7 +8958,7 @@ function isManifestArray(data) {
8506
8958
  return Array.isArray(data) && data.length > 0 && typeof data[0].path === "string" && typeof data[0].content === "string";
8507
8959
  }
8508
8960
  function extractManifestToDir(manifest, targetDir) {
8509
- if (existsSync9(targetDir)) {
8961
+ if (existsSync11(targetDir)) {
8510
8962
  rmSync5(targetDir, { recursive: true, force: true });
8511
8963
  }
8512
8964
  mkdirSync7(targetDir, { recursive: true });
@@ -8538,18 +8990,18 @@ async function downloadAndExtractMarketplaceTarball(baseUrl, slug, tmpDir, targe
8538
8990
  }
8539
8991
  if (tarballRes.status === 302 || tarballRes.headers.get("location")) {
8540
8992
  const redirectUrl = tarballRes.headers.get("location");
8541
- const tarballPath = join10(tmpDir, `${slug}.tar.gz`);
8993
+ const tarballPath = join12(tmpDir, `${slug}.tar.gz`);
8542
8994
  await downloadToFile3(redirectUrl, tarballPath);
8543
- const buffer = readFileSync7(tarballPath);
8995
+ const buffer = readFileSync9(tarballPath);
8544
8996
  const manifest = tryParseAsGzippedManifest(buffer);
8545
8997
  if (manifest) {
8546
8998
  extractManifestToDir(manifest, targetDir);
8547
8999
  return;
8548
9000
  }
8549
- const extractDir = join10(tmpDir, "extracted");
9001
+ const extractDir = join12(tmpDir, "extracted");
8550
9002
  extractTarGz3(tarballPath, extractDir);
8551
9003
  flattenPackageRoot3(extractDir);
8552
- if (existsSync9(targetDir)) {
9004
+ if (existsSync11(targetDir)) {
8553
9005
  rmSync5(targetDir, { recursive: true, force: true });
8554
9006
  }
8555
9007
  cpSync5(extractDir, targetDir, { recursive: true, force: true });
@@ -8560,13 +9012,13 @@ async function downloadAndExtractMarketplaceTarball(baseUrl, slug, tmpDir, targe
8560
9012
  extractManifestToDir(manifest, targetDir);
8561
9013
  return;
8562
9014
  }
8563
- const tarballPath = join10(tmpDir, `${slug}.tar.gz`);
9015
+ const tarballPath = join12(tmpDir, `${slug}.tar.gz`);
8564
9016
  writeFileSync9(tarballPath, buffer);
8565
9017
  try {
8566
- const extractDir = join10(tmpDir, "extracted");
9018
+ const extractDir = join12(tmpDir, "extracted");
8567
9019
  extractTarGz3(tarballPath, extractDir);
8568
9020
  flattenPackageRoot3(extractDir);
8569
- if (existsSync9(targetDir)) {
9021
+ if (existsSync11(targetDir)) {
8570
9022
  rmSync5(targetDir, { recursive: true, force: true });
8571
9023
  }
8572
9024
  cpSync5(extractDir, targetDir, { recursive: true, force: true });
@@ -8602,11 +9054,11 @@ function writeMarketplacePackageJson(plugin, slug, name, baseUrl, targetDir) {
8602
9054
  }
8603
9055
  };
8604
9056
  const pkgPath = resolve7(targetDir, "package.json");
8605
- if (!existsSync9(pkgPath)) {
9057
+ if (!existsSync11(pkgPath)) {
8606
9058
  writeFileSync9(pkgPath, JSON.stringify(packageJson, null, 2));
8607
9059
  } else {
8608
9060
  try {
8609
- const existing = JSON.parse(readFileSync7(pkgPath, "utf-8"));
9061
+ const existing = JSON.parse(readFileSync9(pkgPath, "utf-8"));
8610
9062
  const merged = {
8611
9063
  ...existing,
8612
9064
  xbrowser: { ...existing.xbrowser, ...packageJson.xbrowser },
@@ -8619,7 +9071,7 @@ function writeMarketplacePackageJson(plugin, slug, name, baseUrl, targetDir) {
8619
9071
  }
8620
9072
  }
8621
9073
  function ensureIndexFile(plugin, name, targetDir) {
8622
- if (!existsSync9(resolve7(targetDir, "index.ts")) && !existsSync9(resolve7(targetDir, "index.js"))) {
9074
+ if (!existsSync11(resolve7(targetDir, "index.ts")) && !existsSync11(resolve7(targetDir, "index.js"))) {
8623
9075
  const commands = plugin.commands || [];
8624
9076
  const commandHandlers = commands.length > 0 ? commands.map((cmd) => {
8625
9077
  return [
@@ -8673,7 +9125,7 @@ var PluginInstaller = class {
8673
9125
  const type = this.detectSourceType(source);
8674
9126
  const name = options?.name || this.deriveName(source, type);
8675
9127
  const targetDir = resolve8(this.pluginsDir, name);
8676
- if (existsSync10(targetDir) && !options?.force) {
9128
+ if (existsSync12(targetDir) && !options?.force) {
8677
9129
  throw new Error(`Plugin "${name}" already exists. Use --force to overwrite.`);
8678
9130
  }
8679
9131
  mkdirSync8(targetDir, { recursive: true });
@@ -8716,10 +9168,10 @@ var PluginInstaller = class {
8716
9168
  */
8717
9169
  async fixSharedDeps(pluginDir) {
8718
9170
  const indexPath = resolve8(pluginDir, "index.ts");
8719
- if (!existsSync10(indexPath)) return;
9171
+ if (!existsSync12(indexPath)) return;
8720
9172
  let content;
8721
9173
  try {
8722
- content = readFileSync8(indexPath, "utf8");
9174
+ content = readFileSync10(indexPath, "utf8");
8723
9175
  } catch {
8724
9176
  return;
8725
9177
  }
@@ -8734,7 +9186,7 @@ var PluginInstaller = class {
8734
9186
  const toCopy = [];
8735
9187
  for (const file of missingFiles) {
8736
9188
  const targetPath = resolve8(sharedDir, file);
8737
- if (!existsSync10(targetPath)) {
9189
+ if (!existsSync12(targetPath)) {
8738
9190
  toCopy.push(file);
8739
9191
  }
8740
9192
  }
@@ -8745,7 +9197,7 @@ var PluginInstaller = class {
8745
9197
  ];
8746
9198
  let sourceSharedDir = null;
8747
9199
  for (const dir of repoSharedDirs) {
8748
- if (existsSync10(dir)) {
9200
+ if (existsSync12(dir)) {
8749
9201
  sourceSharedDir = dir;
8750
9202
  break;
8751
9203
  }
@@ -8762,7 +9214,7 @@ var PluginInstaller = class {
8762
9214
  const content2 = await resp.text();
8763
9215
  const dst = resolve8(sharedDir, file);
8764
9216
  const dstDir = dirname4(dst);
8765
- if (!existsSync10(dstDir)) mkdirSync8(dstDir, { recursive: true });
9217
+ if (!existsSync12(dstDir)) mkdirSync8(dstDir, { recursive: true });
8766
9218
  writeFileSync10(dst, content2, "utf-8");
8767
9219
  console.log(`\u2705 Downloaded shared/${file} from GitHub`);
8768
9220
  } else {
@@ -8784,7 +9236,7 @@ var PluginInstaller = class {
8784
9236
  for (const file of toCopy) {
8785
9237
  const src = resolve8(sourceSharedDir, file);
8786
9238
  const dst = resolve8(sharedDir, file);
8787
- if (existsSync10(src)) {
9239
+ if (existsSync12(src)) {
8788
9240
  try {
8789
9241
  cpSync6(dirname4(src), dirname4(dst), { recursive: true });
8790
9242
  console.log(`\u2705 Copied shared/${file} for plugin "${basename2(pluginDir)}"`);
@@ -8834,7 +9286,7 @@ var PluginInstaller = class {
8834
9286
  */
8835
9287
  async uninstall(name) {
8836
9288
  const targetDir = resolve8(this.pluginsDir, name);
8837
- if (!existsSync10(targetDir)) {
9289
+ if (!existsSync12(targetDir)) {
8838
9290
  throw new Error(`Plugin "${name}" not found`);
8839
9291
  }
8840
9292
  rmSync6(targetDir, { recursive: true, force: true });
@@ -8845,7 +9297,7 @@ var PluginInstaller = class {
8845
9297
  * @returns Array of installed plugin information.
8846
9298
  */
8847
9299
  async list(_options) {
8848
- if (!existsSync10(this.pluginsDir)) return [];
9300
+ if (!existsSync12(this.pluginsDir)) return [];
8849
9301
  const entries = readdirSync2(this.pluginsDir, { withFileTypes: true });
8850
9302
  const plugins = [];
8851
9303
  for (const entry of entries) {
@@ -8853,7 +9305,7 @@ var PluginInstaller = class {
8853
9305
  const pluginPath = resolve8(this.pluginsDir, entry.name);
8854
9306
  const indexPath = resolve8(pluginPath, "index.ts");
8855
9307
  const indexJsPath = resolve8(pluginPath, "index.js");
8856
- if (!existsSync10(indexPath) && !existsSync10(indexJsPath)) continue;
9308
+ if (!existsSync12(indexPath) && !existsSync12(indexJsPath)) continue;
8857
9309
  const metadata = PluginMetadataParser.parseFromPackageJson(pluginPath);
8858
9310
  let source = "local";
8859
9311
  const pkg = readJsonFile(resolve8(pluginPath, "package.json"), {});
@@ -8880,10 +9332,10 @@ var PluginInstaller = class {
8880
9332
  }
8881
9333
  if (source.startsWith("file://")) {
8882
9334
  const filePath = decodeURIComponent(new URL(source).pathname);
8883
- if (existsSync10(filePath)) return "url";
9335
+ if (existsSync12(filePath)) return "url";
8884
9336
  }
8885
9337
  if (source.endsWith(".git") || source.includes("github.com/")) return "git";
8886
- if (existsSync10(resolve8(source))) return "local";
9338
+ if (existsSync12(resolve8(source))) return "local";
8887
9339
  return "npm";
8888
9340
  }
8889
9341
  deriveName(source, type) {
@@ -9812,7 +10264,7 @@ var previewBuiltin = {
9812
10264
 
9813
10265
  // src/builtins/knowledge.ts
9814
10266
  init_site_knowledge();
9815
- import { existsSync as existsSync12 } from "fs";
10267
+ import { existsSync as existsSync14 } from "fs";
9816
10268
  var knowledgeBuiltin = {
9817
10269
  name: "knowledge",
9818
10270
  description: "View LLM-readable site knowledge base (selectors, forms, APIs)",
@@ -9991,8 +10443,8 @@ var knowledgeBuiltin = {
9991
10443
  }
9992
10444
  const mdPath = getKnowledgePath(domain, "md");
9993
10445
  const jsonPath = getKnowledgePath(domain, "json");
9994
- console.log(`Markdown: ${mdPath} (${existsSync12(mdPath) ? "exists" : "not found"})`);
9995
- console.log(`JSON: ${jsonPath} (${existsSync12(jsonPath) ? "exists" : "not found"})`);
10446
+ console.log(`Markdown: ${mdPath} (${existsSync14(mdPath) ? "exists" : "not found"})`);
10447
+ console.log(`JSON: ${jsonPath} (${existsSync14(jsonPath) ? "exists" : "not found"})`);
9996
10448
  return;
9997
10449
  }
9998
10450
  console.error(`Unknown subcommand: ${subcommand}`);
@@ -10274,6 +10726,12 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
10274
10726
  output: options.output || options.o || args[0]
10275
10727
  };
10276
10728
  break;
10729
+ case "ua": {
10730
+ cmdName = "ua";
10731
+ const val = args.join(" ").trim() || options.value || "";
10732
+ params = { value: val };
10733
+ break;
10734
+ }
10277
10735
  case "eval": {
10278
10736
  cmdName = "eval";
10279
10737
  const frameVal = options.frame || void 0;
@@ -10582,14 +11040,14 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
10582
11040
 
10583
11041
  // src/cli/session-routes.ts
10584
11042
  import { homedir as homedir8 } from "os";
10585
- import { join as join12 } from "path";
11043
+ import { join as join14 } from "path";
10586
11044
  import { readdirSync as readdirSync3, rmSync as rmSync7 } from "fs";
10587
11045
  function cleanSessionFiles() {
10588
- const dir = join12(homedir8(), ".xbrowser", "sessions");
11046
+ const dir = join14(homedir8(), ".xbrowser", "sessions");
10589
11047
  let count = 0;
10590
11048
  try {
10591
11049
  for (const entry of readdirSync3(dir, { withFileTypes: true })) {
10592
- const p = join12(dir, entry.name);
11050
+ const p = join14(dir, entry.name);
10593
11051
  rmSync7(p, { recursive: true, force: true });
10594
11052
  count++;
10595
11053
  }
@@ -10993,7 +11451,7 @@ async function handlePlugin(args, options, mode) {
10993
11451
  } catch {
10994
11452
  }
10995
11453
  try {
10996
- const { daemonPing } = await import("./daemon-client-Y2YSZCGK.js");
11454
+ const { daemonPing } = await import("./daemon-client-HEGAXWGK.js");
10997
11455
  if (await daemonPing()) {
10998
11456
  await fetch("http://localhost:9224/rpc", {
10999
11457
  method: "POST",
@@ -11143,7 +11601,7 @@ function handleDaemon(args, options, mode) {
11143
11601
  }
11144
11602
 
11145
11603
  // src/cli/record-routes.ts
11146
- import { existsSync as existsSync13 } from "fs";
11604
+ import { existsSync as existsSync15 } from "fs";
11147
11605
  async function handleRecord(args, options, mode) {
11148
11606
  const sub = args[0];
11149
11607
  switch (sub) {
@@ -11174,9 +11632,9 @@ async function handleRecord(args, options, mode) {
11174
11632
  outputError(String(result.error || "Failed to stop recording"));
11175
11633
  return;
11176
11634
  }
11177
- if (output && !existsSync13(output)) {
11635
+ if (output && !existsSync15(output)) {
11178
11636
  const defaultRecording = SessionRecorder.getRecordingsDir(sessionName) + "/recording.json";
11179
- if (existsSync13(defaultRecording)) {
11637
+ if (existsSync15(defaultRecording)) {
11180
11638
  try {
11181
11639
  const { mkdirSync: mkdirSync11, copyFileSync: copyFileSync2 } = await import("fs");
11182
11640
  const { dirname: pathDirname } = await import("path");
@@ -11562,7 +12020,7 @@ async function handleGeneratePlugin(sessionName, pluginName, outputDir) {
11562
12020
  const { SessionRecorder: SessionRecorder2 } = await import("./session-recorder-SLDBENVF.js");
11563
12021
  const { readSiteKnowledge: readSiteKnowledge2, toMarkdown } = await import("./site-knowledge-SYC6VCDB.js");
11564
12022
  const { mkdirSync: mkdirSync11, writeFileSync: writeFileSync13 } = await import("fs");
11565
- const { join: join14 } = await import("path");
12023
+ const { join: join16 } = await import("path");
11566
12024
  const data = SessionRecorder2.readData(sessionName);
11567
12025
  if (!data) {
11568
12026
  outputError(`No recording found for session "${sessionName}". Run \`xbrowser record stop --session ${sessionName}\` first.`);
@@ -11574,14 +12032,14 @@ async function handleGeneratePlugin(sessionName, pluginName, outputDir) {
11574
12032
  } catch {
11575
12033
  }
11576
12034
  const finalPluginName = pluginName || domain.split(".")[0] || "my-site";
11577
- const finalOutputDir = outputDir || join14(process.cwd(), ".xcli", "plugins", finalPluginName);
12035
+ const finalOutputDir = outputDir || join16(process.cwd(), ".xcli", "plugins", finalPluginName);
11578
12036
  const knowledge = readSiteKnowledge2(domain);
11579
12037
  const knowledgeMd = knowledge ? toMarkdown(knowledge) : "";
11580
12038
  const pluginCode = generatePluginCode(finalPluginName, domain, data, knowledgeMd);
11581
- mkdirSync11(join14(finalOutputDir), { recursive: true });
11582
- writeFileSync13(join14(finalOutputDir, "index.ts"), pluginCode, "utf-8");
12039
+ mkdirSync11(join16(finalOutputDir), { recursive: true });
12040
+ writeFileSync13(join16(finalOutputDir, "index.ts"), pluginCode, "utf-8");
11583
12041
  if (knowledgeMd) {
11584
- writeFileSync13(join14(finalOutputDir, "SITE_KNOWLEDGE.md"), knowledgeMd, "utf-8");
12042
+ writeFileSync13(join16(finalOutputDir, "SITE_KNOWLEDGE.md"), knowledgeMd, "utf-8");
11585
12043
  }
11586
12044
  console.log("");
11587
12045
  console.log("=== Plugin Generated ===");
@@ -12124,7 +12582,7 @@ async function handleNetCommand(args, options, mode, sessionName) {
12124
12582
 
12125
12583
  // src/cli/test-routes.ts
12126
12584
  import { execSync as execSync3 } from "child_process";
12127
- import { readFileSync as readFileSync9 } from "fs";
12585
+ import { readFileSync as readFileSync11 } from "fs";
12128
12586
  import { resolve as resolve9 } from "path";
12129
12587
  function findPluginPath(plugin) {
12130
12588
  const candidates = [
@@ -12133,7 +12591,7 @@ function findPluginPath(plugin) {
12133
12591
  ];
12134
12592
  for (const p of candidates) {
12135
12593
  try {
12136
- readFileSync9(p, "utf-8");
12594
+ readFileSync11(p, "utf-8");
12137
12595
  return p;
12138
12596
  } catch {
12139
12597
  }
@@ -12144,7 +12602,7 @@ function extractSchema(plugin, command) {
12144
12602
  const pluginPath = findPluginPath(plugin);
12145
12603
  let src;
12146
12604
  try {
12147
- src = readFileSync9(pluginPath, "utf-8");
12605
+ src = readFileSync11(pluginPath, "utf-8");
12148
12606
  } catch {
12149
12607
  return null;
12150
12608
  }
@@ -13067,7 +13525,7 @@ function extractSessionNameFromArgv(argv) {
13067
13525
  async function handleEvalMode(argv) {
13068
13526
  const evalCommands = parseEvalFlags(argv);
13069
13527
  if (evalCommands.length === 0) return;
13070
- const chain = evalCommands.join(" ; ");
13528
+ const chain = evalCommands.map((script) => `eval --script-b64 ${Buffer.from(script, "utf8").toString("base64url")}`).join(" ; ");
13071
13529
  const cdpEndpoint = extractCdpFromArgv(argv);
13072
13530
  const sessionArgIdx = argv.indexOf("--session");
13073
13531
  const sessionName = sessionArgIdx >= 0 && argv[sessionArgIdx + 1] ? argv[sessionArgIdx + 1] : process.env.XBROWSER_SESSION || "default";
@@ -13075,6 +13533,51 @@ async function handleEvalMode(argv) {
13075
13533
  printChainResult(chainResult);
13076
13534
  if (!chainResult.success) throw new Error("Command failed");
13077
13535
  }
13536
+ function findEvalStart(argv) {
13537
+ const flagsWithValue = /* @__PURE__ */ new Set(["--session", "--cdp"]);
13538
+ for (let i = 0; i < argv.length; i++) {
13539
+ const a = argv[i];
13540
+ if (a === "-e" || a === "--eval") return -1;
13541
+ if (a.startsWith("--")) {
13542
+ if (flagsWithValue.has(a)) {
13543
+ i++;
13544
+ continue;
13545
+ }
13546
+ continue;
13547
+ }
13548
+ return a === "eval" ? i : -1;
13549
+ }
13550
+ return -1;
13551
+ }
13552
+ async function handleEvalScript(parts, argv) {
13553
+ const cdpEndpoint = extractCdpFromArgv(argv);
13554
+ const sessionName = extractSessionNameFromArgv(argv);
13555
+ let frame;
13556
+ const scriptLines = [];
13557
+ for (let i = 0; i < parts.length; i++) {
13558
+ const part = parts[i];
13559
+ if (part === "--frame" && parts[i + 1]) {
13560
+ frame = parts[i + 1];
13561
+ i++;
13562
+ continue;
13563
+ }
13564
+ if (part === "--session" || part === "--cdp") {
13565
+ i++;
13566
+ continue;
13567
+ }
13568
+ scriptLines.push(part);
13569
+ }
13570
+ const script = scriptLines.join("\n");
13571
+ if (!script.trim()) {
13572
+ console.error("Error: eval requires a script (usage: xbrowser eval <js...>)");
13573
+ throw new Error("Command failed");
13574
+ }
13575
+ const b64 = Buffer.from(script, "utf8").toString("base64url");
13576
+ const chain = frame ? `eval --frame ${frame} --script-b64 ${b64}` : `eval --script-b64 ${b64}`;
13577
+ const chainResult = await executeChain(chain, { cdpEndpoint, sessionName });
13578
+ printChainResult(chainResult);
13579
+ if (!chainResult.success) throw new Error("Command failed");
13580
+ }
13078
13581
  async function handleChainInput(input, argv) {
13079
13582
  const cdpEndpoint = argv ? extractCdpFromArgv(argv) : void 0;
13080
13583
  const hasJson = argv ? argv.some((a) => a === "--json" || a.startsWith("--json=") || a.includes(" --json") || a.startsWith("--json")) || argv.includes("-j") : false;
@@ -13112,6 +13615,11 @@ async function routeCommand(argvIn, stdinCommands) {
13112
13615
  await handleStdinMode(stdinCommands, argv);
13113
13616
  return;
13114
13617
  }
13618
+ const evalStart = findEvalStart(argv);
13619
+ if (evalStart >= 0 && argv.length > evalStart + 1) {
13620
+ await handleEvalScript(argv.slice(evalStart + 1), argv);
13621
+ return;
13622
+ }
13115
13623
  if (parseEvalFlags(argv).length > 0) {
13116
13624
  await handleEvalMode(argv);
13117
13625
  return;
@@ -13503,7 +14011,7 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
13503
14011
  }
13504
14012
  const needsBrowser = cmdEntry.scope === "page" || cmdEntry.scope === "browser";
13505
14013
  if (needsBrowser && !process.env.XBROWSER_DAEMON_WORKER) {
13506
- const { forwardPluginExec } = await import("./daemon-client-Y2YSZCGK.js");
14014
+ const { forwardPluginExec } = await import("./daemon-client-HEGAXWGK.js");
13507
14015
  const userTimeout = typeof params.timeout === "number" && params.timeout > 0 ? params.timeout * 1e3 + 3e4 : void 0;
13508
14016
  const result = await forwardPluginExec(`${command}.${subCommand}`, params, sessionName, cdpEndpoint, userTimeout);
13509
14017
  const resultData = result && typeof result === "object" && "data" in result ? result.data : void 0;
@@ -13543,7 +14051,7 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
13543
14051
  const targetPage = pages[cmdTabIndex];
13544
14052
  await targetPage.bringToFront().catch(() => {
13545
14053
  });
13546
- const { setActivePage: setActivePage2 } = await import("./browser-V3JIWSTR.js");
14054
+ const { setActivePage: setActivePage2 } = await import("./browser-T7OVLAEC.js");
13547
14055
  setActivePage2(session, targetPage);
13548
14056
  }
13549
14057
  }
@@ -15116,12 +15624,12 @@ var FileListHandler = class {
15116
15624
  const msg = ctx.message;
15117
15625
  try {
15118
15626
  const { readdirSync: readdirSync4, statSync } = await import("fs");
15119
- const { join: join14, resolve: resolve10 } = await import("path");
15627
+ const { join: join16, resolve: resolve10 } = await import("path");
15120
15628
  const targetPath = resolve10(msg.path);
15121
15629
  const entries = readdirSync4(targetPath);
15122
15630
  const files = entries.map((name) => {
15123
15631
  try {
15124
- const stat = statSync(join14(targetPath, name));
15632
+ const stat = statSync(join16(targetPath, name));
15125
15633
  return { name, isDir: stat.isDirectory(), size: stat.size, modified: stat.mtime.toISOString() };
15126
15634
  } catch {
15127
15635
  return { name, isDir: false, size: 0, modified: "" };
@@ -15138,10 +15646,10 @@ var FileDownloadHandler = class {
15138
15646
  async handle(ctx) {
15139
15647
  const msg = ctx.message;
15140
15648
  try {
15141
- const { readFileSync: readFileSync11 } = await import("fs");
15649
+ const { readFileSync: readFileSync13 } = await import("fs");
15142
15650
  const { resolve: resolve10, basename: basename3 } = await import("path");
15143
15651
  const targetPath = resolve10(msg.path);
15144
- const data = readFileSync11(targetPath);
15652
+ const data = readFileSync13(targetPath);
15145
15653
  const base64 = data.toString("base64");
15146
15654
  const ext = targetPath.split(".").pop()?.toLowerCase() || "";
15147
15655
  const mimeMap = {
@@ -16716,7 +17224,7 @@ var DataCollector = class {
16716
17224
  return results;
16717
17225
  }
16718
17226
  async createBrowserContext() {
16719
- const { launch } = await import("./cdp-driver-GD6YBBGE.js");
17227
+ const { launch } = await import("./cdp-driver-HYCLE4YP.js");
16720
17228
  const { browser } = await launch({
16721
17229
  headless: true,
16722
17230
  args: ["--no-sandbox", "--disable-setuid-sandbox"]
@@ -16756,7 +17264,7 @@ var DataCollector = class {
16756
17264
 
16757
17265
  // src/cdp-interceptor/index.ts
16758
17266
  async function createCDPInterceptor(config) {
16759
- const { CDPInterceptorProxy: CDPInterceptorProxy2 } = await import("./proxy-C6CK3UH5.js");
17267
+ const { CDPInterceptorProxy: CDPInterceptorProxy2 } = await import("./proxy-LUR4U5YF.js");
16760
17268
  const proxy = new CDPInterceptorProxy2(config);
16761
17269
  await proxy.start();
16762
17270
  return proxy;