@xbrowser/cli 1.26.0 → 1.27.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.
@@ -6057,26 +6057,193 @@ var waitForCommand = registerCommand({
6057
6057
  }
6058
6058
  });
6059
6059
 
6060
- // src/commands/tab.ts
6060
+ // src/commands/task.ts
6061
6061
  import { z as z27 } from "zod";
6062
- import { ok as ok26, fail as fail12 } from "@dyyz1993/xcli-core";
6063
- var TabParams = z27.object({
6064
- subcommand: z27.enum(["list", "new", "close", "switch"]),
6065
- url: z27.string().optional(),
6066
- index: z27.number().int().min(0).optional()
6062
+ import { ok as ok26, fail as fail12, normalizeTips as normalizeTips5 } from "@dyyz1993/xcli-core";
6063
+ var DECISION_SYSTEM = `\u4F60\u662F\u6D4F\u89C8\u5668\u4EFB\u52A1\u51B3\u7B56\u5668\u3002\u6839\u636E\u76EE\u6807\u3001\u5F53\u524D\u9875\u9762\u5143\u7D20\u6E05\u5355\uFF08\u6BCF\u9879\u5E26 @eN \u7F16\u53F7\uFF09\u548C\u5DF2\u6267\u884C\u5386\u53F2\uFF0C\u8F93\u51FA\u4E0B\u4E00\u6B65\u52A8\u4F5C\u3002
6064
+
6065
+ \u53EF\u7528\u52A8\u4F5C\uFF08\u53EA\u80FD\u8F93\u51FA\u4E00\u4E2A JSON \u5BF9\u8C61\uFF0C\u4E0D\u8981\u8F93\u51FA\u4EFB\u4F55\u5176\u4ED6\u6587\u5B57\uFF09\uFF1A
6066
+ {"action":"click","ref":"@eN","thought":"\u4E3A\u4EC0\u4E48"}
6067
+ {"action":"fill","ref":"@eN","value":"\u6587\u672C","thought":"\u4E3A\u4EC0\u4E48"}
6068
+ {"action":"select","ref":"@eN","value":"\u9009\u9879\u503C","thought":"\u4E3A\u4EC0\u4E48"}
6069
+ {"action":"press","value":"Enter","thought":"\u4E3A\u4EC0\u4E48"}
6070
+ {"action":"scroll","value":"down \u6216 up","thought":"\u4E3A\u4EC0\u4E48"}
6071
+ {"action":"done","value":"\u5B8C\u6210\u6458\u8981","thought":"\u4E3A\u4EC0\u4E48"}
6072
+ {"action":"blocked","value":"\u5361\u4F4F\u539F\u56E0","thought":"\u4E3A\u4EC0\u4E48"}
6073
+
6074
+ \u89C4\u5219\uFF1A
6075
+ 1. ref \u5FC5\u987B\u539F\u6837\u6765\u81EA\u672C\u6B21\u5143\u7D20\u6E05\u5355\uFF1B\u6E05\u5355\u91CC\u6CA1\u6709\u80FD\u63A8\u8FDB\u76EE\u6807\u7684\u5143\u7D20\u5C31\u8F93\u51FA blocked\u3002
6076
+ 2. \u76EE\u6807\u5DF2\u5B8C\u6210\uFF08\u9875\u9762\u4E0A\u80FD\u786E\u8BA4\u7ED3\u679C\uFF09\u5C31\u8F93\u51FA done\uFF0Cvalue \u5199\u5B8C\u6210\u6458\u8981\u3002
6077
+ 3. \u4E0D\u8981\u91CD\u590D\u521A\u5931\u8D25\u8FC7\u7684\u540C\u4E00\u52A8\u4F5C\uFF1B\u8FDE\u7EED\u5931\u8D25\u6362\u601D\u8DEF\u6216 blocked\u3002`;
6078
+ function extractDecision(raw) {
6079
+ let s = raw.trim().replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/, "");
6080
+ const start = s.indexOf("{");
6081
+ if (start < 0) return null;
6082
+ let depth = 0;
6083
+ for (let i = start; i < s.length; i++) {
6084
+ if (s[i] === "{") depth++;
6085
+ else if (s[i] === "}") {
6086
+ depth--;
6087
+ if (depth === 0) {
6088
+ try {
6089
+ return JSON.parse(s.slice(start, i + 1));
6090
+ } catch {
6091
+ return null;
6092
+ }
6093
+ }
6094
+ }
6095
+ }
6096
+ return null;
6097
+ }
6098
+ var taskCommand = registerCommand({
6099
+ name: "task",
6100
+ description: "Autonomous goal loop: observe refs \u2192 LLM decides \u2192 act, until done/blocked (budget-capped)",
6101
+ scope: "page",
6102
+ parameters: z27.object({
6103
+ goal: z27.string().describe("Natural-language goal"),
6104
+ maxSteps: z27.number().int().positive().max(50).optional().default(15).describe("Hard action budget"),
6105
+ timeout: z27.number().optional().default(18e4).describe("Wall clock budget in ms"),
6106
+ limit: z27.number().int().positive().max(300).optional().default(80).describe("Max targets per observe")
6107
+ }),
6108
+ result: z27.object({
6109
+ status: z27.enum(["done", "blocked", "max_steps", "error"]),
6110
+ summary: z27.string().optional(),
6111
+ steps: z27.array(z27.record(z27.unknown())),
6112
+ finalUrl: z27.string().optional(),
6113
+ usage: z27.object({ llmCalls: z27.number(), steps: z27.number() }).passthrough().optional()
6114
+ }).passthrough(),
6115
+ handler: async (p, ctx) => {
6116
+ const creds = loadVLMCredentials2();
6117
+ if (!creds) {
6118
+ return fail12("task \u9700\u8981\u51B3\u7B56\u6A21\u578B\u51ED\u8BC1\uFF1A\u8BBE\u7F6E XBROWSER_VLM_CONFIG \u6216 ~/.zcode/v2/config.json");
6119
+ }
6120
+ const model = process.env.XBROWSER_TASK_MODEL || creds.model;
6121
+ const started = Date.now();
6122
+ const steps = [];
6123
+ const history = [];
6124
+ let llmCalls = 0;
6125
+ let status = "max_steps";
6126
+ let summary = "";
6127
+ let lastError = "";
6128
+ for (let step = 1; step <= p.maxSteps; step++) {
6129
+ if (Date.now() - started > p.timeout) {
6130
+ status = "blocked";
6131
+ summary = `timeout after ${step - 1} steps`;
6132
+ break;
6133
+ }
6134
+ const obs = await observePage(ctx.page, ctx.sessionId, { limit: p.limit, includeHidden: false });
6135
+ const compact = obs.compact ?? obs.targets.map((t, i) => `@e${i + 1} [${t.tag ?? ""}] ${t.name ?? ""}`).join("\n");
6136
+ const screenHash = obs.screenHash;
6137
+ const userPayload = [
6138
+ `\u76EE\u6807\uFF1A${p.goal}`,
6139
+ `\u5F53\u524D\u9875\u9762\uFF08URL: ${obs.url ?? ""}\uFF09\uFF1A`,
6140
+ compact,
6141
+ history.length ? `
6142
+ \u5DF2\u6267\u884C\uFF08\u52FF\u91CD\u590D\u5931\u8D25\u52A8\u4F5C\uFF09\uFF1A
6143
+ ${history.slice(-6).join("\n")}` : ""
6144
+ ].filter(Boolean).join("\n");
6145
+ let raw = "";
6146
+ try {
6147
+ raw = await vlmAsk({ ...creds, model }, [{ type: "text", text: `${DECISION_SYSTEM}
6148
+
6149
+ ---
6150
+
6151
+ ${userPayload}` }], 900);
6152
+ llmCalls++;
6153
+ } catch (e) {
6154
+ lastError = e instanceof Error ? e.message : String(e);
6155
+ status = "error";
6156
+ summary = `LLM call failed: ${lastError}`;
6157
+ break;
6158
+ }
6159
+ const decision = extractDecision(raw);
6160
+ if (!decision || typeof decision.action !== "string") {
6161
+ status = "blocked";
6162
+ summary = `LLM returned unparsable decision: ${raw.slice(0, 120)}`;
6163
+ break;
6164
+ }
6165
+ const action = decision.action;
6166
+ const ref = typeof decision.ref === "string" ? decision.ref : void 0;
6167
+ const value = typeof decision.value === "string" ? decision.value : void 0;
6168
+ const thought = typeof decision.thought === "string" ? decision.thought : void 0;
6169
+ if (action === "done") {
6170
+ status = "done";
6171
+ summary = value || thought || "task done";
6172
+ steps.push({ step, thought, action: "done", ok: true, screenHash });
6173
+ break;
6174
+ }
6175
+ if (action === "blocked") {
6176
+ status = "blocked";
6177
+ summary = value || thought || "LLM judged blocked";
6178
+ steps.push({ step, thought, action: "blocked", ok: true, screenHash });
6179
+ break;
6180
+ }
6181
+ const legalRefs = new Set(obs.targets.map((t, i) => t.ref ?? `e${i + 1}`));
6182
+ const normalizedRef = ref ? legalRefs.has(ref.replace("@", "")) || legalRefs.has(ref) ? ref : void 0 : void 0;
6183
+ if (ref && !normalizedRef && action !== "scroll" && action !== "press") {
6184
+ history.push(`step${step}: ${action} ${ref} REJECTED (ref not in current list)`);
6185
+ steps.push({ step, thought, action, ref, ok: false, error: "ref not in current list", screenHash });
6186
+ continue;
6187
+ }
6188
+ const res = await actOnPage(ctx.page, ctx.sessionId, {
6189
+ action,
6190
+ ref: normalizedRef,
6191
+ value,
6192
+ selector: void 0,
6193
+ force: false,
6194
+ timeout: 1e4
6195
+ });
6196
+ const record = {
6197
+ step,
6198
+ thought,
6199
+ action,
6200
+ ref: normalizedRef,
6201
+ value,
6202
+ ok: res.success === true,
6203
+ stale: res.stale,
6204
+ error: res.success ? void 0 : res.message || res.reason,
6205
+ screenHash
6206
+ };
6207
+ steps.push(record);
6208
+ history.push(`step${step}: ${action} ${normalizedRef ?? ""} ${value ?? ""} -> ${res.success ? "ok" : `failed(${res.reason || res.message})`}`);
6209
+ const sig = (s) => `${s.action}|${s.ref ?? ""}|${s.value ?? ""}`;
6210
+ const last3 = steps.slice(-3);
6211
+ if (last3.length === 3 && new Set(last3.map(sig)).size === 1) {
6212
+ status = "blocked";
6213
+ summary = "dead loop: identical action repeated 3 times";
6214
+ break;
6215
+ }
6216
+ }
6217
+ if (status === "max_steps") summary = summary || `hit maxSteps=${p.maxSteps}`;
6218
+ const finalUrl = ctx.page ? ctx.page.url() : void 0;
6219
+ const data = { status, summary, steps, finalUrl, usage: { llmCalls, steps: steps.length } };
6220
+ if (status === "error") return fail12(summary || lastError, normalizeTips5([`completed steps: ${steps.length}`]));
6221
+ return ok26(data, normalizeTips5([
6222
+ status === "done" ? `\u2705 task done (${steps.length} steps, ${llmCalls} LLM calls)` : `\u26A0\uFE0F task ${status}: ${summary.slice(0, 80)}`
6223
+ ]));
6224
+ }
6225
+ });
6226
+
6227
+ // src/commands/tab.ts
6228
+ import { z as z28 } from "zod";
6229
+ import { ok as ok27, fail as fail13 } from "@dyyz1993/xcli-core";
6230
+ var TabParams = z28.object({
6231
+ subcommand: z28.enum(["list", "new", "close", "switch"]),
6232
+ url: z28.string().optional(),
6233
+ index: z28.number().int().min(0).optional()
6067
6234
  });
6068
6235
  var tabCommand = registerCommand({
6069
6236
  name: "tab",
6070
6237
  description: "Manage browser tabs: list, new, close, switch",
6071
6238
  scope: "page",
6072
6239
  parameters: TabParams,
6073
- result: z27.object({
6074
- success: z27.boolean(),
6075
- data: z27.unknown()
6240
+ result: z28.object({
6241
+ success: z28.boolean(),
6242
+ data: z28.unknown()
6076
6243
  }),
6077
6244
  handler: async (p, ctx) => {
6078
6245
  if (!ctx.browserContext) {
6079
- return fail12("No browser context available. Use --cdp to connect to a browser first.");
6246
+ return fail13("No browser context available. Use --cdp to connect to a browser first.");
6080
6247
  }
6081
6248
  const pages = ctx.browserContext.pages();
6082
6249
  switch (p.subcommand) {
@@ -6089,7 +6256,7 @@ var tabCommand = registerCommand({
6089
6256
  case "switch":
6090
6257
  return handleSwitch(p, pages, ctx);
6091
6258
  default:
6092
- return fail12(`Unknown subcommand: ${p.subcommand}`);
6259
+ return fail13(`Unknown subcommand: ${p.subcommand}`);
6093
6260
  }
6094
6261
  }
6095
6262
  });
@@ -6104,7 +6271,7 @@ async function handleList(pages, ctx) {
6104
6271
  if (isActive) activeIndex = i;
6105
6272
  tabs.push({ index: i, url, title, active: isActive });
6106
6273
  }
6107
- return ok26({ tabs, total: tabs.length, activeIndex });
6274
+ return ok27({ tabs, total: tabs.length, activeIndex });
6108
6275
  }
6109
6276
  async function handleNew(p, _pages, ctx) {
6110
6277
  const newPage = await ctx.browserContext.newPage();
@@ -6132,7 +6299,7 @@ async function handleNew(p, _pages, ctx) {
6132
6299
  const title = await newPage.title().catch(() => "");
6133
6300
  const allPages = ctx.browserContext.pages();
6134
6301
  const newIndex = allPages.indexOf(newPage);
6135
- return ok26({
6302
+ return ok27({
6136
6303
  index: newIndex >= 0 ? newIndex : allPages.length - 1,
6137
6304
  url: newPage.url(),
6138
6305
  title,
@@ -6143,11 +6310,11 @@ async function handleNew(p, _pages, ctx) {
6143
6310
  async function handleClose(p, ctx) {
6144
6311
  const currentPages = ctx.browserContext.pages();
6145
6312
  if (currentPages.length <= 1) {
6146
- return fail12("Cannot close the last remaining tab");
6313
+ return fail13("Cannot close the last remaining tab");
6147
6314
  }
6148
6315
  const closeIndex = p.index ?? currentPages.findIndex((pg) => pg === ctx.page);
6149
6316
  if (closeIndex < 0 || closeIndex >= currentPages.length) {
6150
- return fail12(`Invalid tab index: ${closeIndex}. Valid range: 0-${currentPages.length - 1}`);
6317
+ return fail13(`Invalid tab index: ${closeIndex}. Valid range: 0-${currentPages.length - 1}`);
6151
6318
  }
6152
6319
  const pageToClose = currentPages[closeIndex];
6153
6320
  const isActivePage = pageToClose === ctx.page;
@@ -6162,7 +6329,7 @@ async function handleClose(p, ctx) {
6162
6329
  }
6163
6330
  ctx.page = newActivePage;
6164
6331
  }
6165
- return ok26({
6332
+ return ok27({
6166
6333
  closedIndex: closeIndex,
6167
6334
  total: remainingPages.length,
6168
6335
  activeIndex: isActivePage ? closeIndex < remainingPages.length ? closeIndex : remainingPages.length - 1 : remainingPages.findIndex((pg) => pg === ctx.page)
@@ -6170,10 +6337,10 @@ async function handleClose(p, ctx) {
6170
6337
  }
6171
6338
  async function handleSwitch(p, pages, ctx) {
6172
6339
  if (p.index === void 0) {
6173
- return fail12("Parameter --index is required for switch subcommand");
6340
+ return fail13("Parameter --index is required for switch subcommand");
6174
6341
  }
6175
6342
  if (p.index < 0 || p.index >= pages.length) {
6176
- return fail12(`Invalid tab index: ${p.index}. Valid range: 0-${pages.length - 1}`);
6343
+ return fail13(`Invalid tab index: ${p.index}. Valid range: 0-${pages.length - 1}`);
6177
6344
  }
6178
6345
  const targetPage = pages[p.index];
6179
6346
  await targetPage.bringToFront().catch(() => {
@@ -6195,7 +6362,7 @@ async function handleSwitch(p, pages, ctx) {
6195
6362
  }
6196
6363
  ctx.page = targetPage;
6197
6364
  const title = await targetPage.title().catch(() => "");
6198
- return ok26({
6365
+ return ok27({
6199
6366
  index: p.index,
6200
6367
  url: targetPage.url(),
6201
6368
  title,
@@ -6204,17 +6371,17 @@ async function handleSwitch(p, pages, ctx) {
6204
6371
  }
6205
6372
 
6206
6373
  // src/commands/addinitscript.ts
6207
- import { z as z28 } from "zod";
6208
- import { ok as ok27 } from "@dyyz1993/xcli-core";
6374
+ import { z as z29 } from "zod";
6375
+ import { ok as ok28 } from "@dyyz1993/xcli-core";
6209
6376
  import { readFileSync as readFileSync4 } from "fs";
6210
- var InitScriptParams = z28.object({
6211
- script: z28.string().optional(),
6212
- file: z28.string().optional(),
6213
- stdin: z28.boolean().optional(),
6214
- name: z28.string().optional(),
6215
- list: z28.boolean().optional(),
6216
- remove: z28.union([z28.string(), z28.boolean()]).optional(),
6217
- base64: z28.string().optional()
6377
+ var InitScriptParams = z29.object({
6378
+ script: z29.string().optional(),
6379
+ file: z29.string().optional(),
6380
+ stdin: z29.boolean().optional(),
6381
+ name: z29.string().optional(),
6382
+ list: z29.boolean().optional(),
6383
+ remove: z29.union([z29.string(), z29.boolean()]).optional(),
6384
+ base64: z29.string().optional()
6218
6385
  });
6219
6386
  var registeredScripts = /* @__PURE__ */ new Map();
6220
6387
  function resolveScriptContent(params) {
@@ -6248,14 +6415,14 @@ var addInitScriptCommand = registerCommand({
6248
6415
  description: "Add an initialization script that runs on every page load",
6249
6416
  scope: "page",
6250
6417
  parameters: InitScriptParams,
6251
- result: z28.object({
6252
- scripts: z28.array(z28.object({ name: z28.string(), size: z28.number(), preview: z28.string() })).optional(),
6253
- removed: z28.string().optional(),
6254
- existed: z28.boolean().optional(),
6255
- error: z28.string().optional(),
6256
- registered: z28.string().optional(),
6257
- hint: z28.string().optional(),
6258
- executedImmediately: z28.boolean().optional()
6418
+ result: z29.object({
6419
+ scripts: z29.array(z29.object({ name: z29.string(), size: z29.number(), preview: z29.string() })).optional(),
6420
+ removed: z29.string().optional(),
6421
+ existed: z29.boolean().optional(),
6422
+ error: z29.string().optional(),
6423
+ registered: z29.string().optional(),
6424
+ hint: z29.string().optional(),
6425
+ executedImmediately: z29.boolean().optional()
6259
6426
  }).passthrough(),
6260
6427
  handler: async (params, ctx) => {
6261
6428
  if (params.list) {
@@ -6264,20 +6431,20 @@ var addInitScriptCommand = registerCommand({
6264
6431
  size: content2.length,
6265
6432
  preview: content2.slice(0, 80)
6266
6433
  }));
6267
- return ok27({ scripts });
6434
+ return ok28({ scripts });
6268
6435
  }
6269
6436
  if (params.remove && typeof params.remove === "string") {
6270
6437
  const existed = registeredScripts.delete(params.remove);
6271
- return ok27({ removed: params.remove, existed });
6438
+ return ok28({ removed: params.remove, existed });
6272
6439
  }
6273
6440
  const removeTarget = (typeof params.remove === "string" ? params.remove : null) || (params.name && !resolveScriptContent(params) ? params.name : null);
6274
6441
  if (removeTarget && !resolveScriptContent(params)) {
6275
6442
  const existed = registeredScripts.delete(removeTarget);
6276
- return ok27({ removed: removeTarget, existed });
6443
+ return ok28({ removed: removeTarget, existed });
6277
6444
  }
6278
6445
  let content = params.stdin ? await readStdin() : resolveScriptContent(params);
6279
6446
  if (!content) {
6280
- return ok27({ error: "No script content provided. Use --script, --file, --stdin, or --base64" });
6447
+ return ok28({ error: "No script content provided. Use --script, --file, --stdin, or --base64" });
6281
6448
  }
6282
6449
  const scriptName = params.name ?? `script-${Date.now()}`;
6283
6450
  registeredScripts.set(scriptName, content);
@@ -6285,45 +6452,45 @@ var addInitScriptCommand = registerCommand({
6285
6452
  try {
6286
6453
  await ctx.page.evaluate(content);
6287
6454
  } catch {
6288
- return ok27({
6455
+ return ok28({
6289
6456
  registered: scriptName,
6290
6457
  hint: "Script registered for future page loads; immediate execution skipped (page may not be ready)"
6291
6458
  });
6292
6459
  }
6293
- return ok27({ registered: scriptName, executedImmediately: true });
6460
+ return ok28({ registered: scriptName, executedImmediately: true });
6294
6461
  }
6295
6462
  });
6296
6463
  registerCommandDefinition("addinitscript", ["script"]);
6297
6464
 
6298
6465
  // src/commands/find.ts
6299
- import { z as z29 } from "zod";
6300
- import { ok as ok28, fail as fail13, normalizeTips as normalizeTips5 } from "@dyyz1993/xcli-core";
6301
- var actionSchema2 = z29.enum(["click", "fill", "type", "select", "hover", "check"]);
6466
+ import { z as z30 } from "zod";
6467
+ import { ok as ok29, fail as fail14, normalizeTips as normalizeTips6 } from "@dyyz1993/xcli-core";
6468
+ var actionSchema2 = z30.enum(["click", "fill", "type", "select", "hover", "check"]);
6302
6469
  var findCommand = registerCommand({
6303
6470
  name: "find",
6304
6471
  description: "Find elements by semantic strategy (text/role/label/placeholder/testid) and optionally perform an action",
6305
6472
  scope: "page",
6306
- parameters: z29.object({
6307
- strategy: z29.enum(["text", "role", "label", "placeholder", "testid", "alt", "title", "first", "last", "nth"]),
6308
- value: z29.string(),
6309
- name: z29.string().optional(),
6310
- exact: z29.boolean().optional().default(false),
6311
- operation: z29.string().optional().describe('Trailing operation syntax, e.g. click, fill "text", type "text"'),
6473
+ parameters: z30.object({
6474
+ strategy: z30.enum(["text", "role", "label", "placeholder", "testid", "alt", "title", "first", "last", "nth"]),
6475
+ value: z30.string(),
6476
+ name: z30.string().optional(),
6477
+ exact: z30.boolean().optional().default(false),
6478
+ operation: z30.string().optional().describe('Trailing operation syntax, e.g. click, fill "text", type "text"'),
6312
6479
  action: actionSchema2.optional().describe("Action to perform when not using trailing operation syntax"),
6313
- actionValue: z29.string().optional().describe("Value for fill/type/select when using action"),
6314
- index: z29.number().int().optional().describe("Index for nth strategy"),
6315
- click: z29.boolean().optional().default(false),
6316
- fill: z29.string().optional(),
6317
- type: z29.string().optional(),
6318
- select: z29.string().optional(),
6319
- hover: z29.boolean().optional().default(false),
6320
- check: z29.boolean().optional().default(false),
6321
- timeout: z29.number().optional().default(1e4)
6480
+ actionValue: z30.string().optional().describe("Value for fill/type/select when using action"),
6481
+ index: z30.number().int().optional().describe("Index for nth strategy"),
6482
+ click: z30.boolean().optional().default(false),
6483
+ fill: z30.string().optional(),
6484
+ type: z30.string().optional(),
6485
+ select: z30.string().optional(),
6486
+ hover: z30.boolean().optional().default(false),
6487
+ check: z30.boolean().optional().default(false),
6488
+ timeout: z30.number().optional().default(1e4)
6322
6489
  }),
6323
- result: z29.object({
6324
- matched: z29.number(),
6325
- selector: z29.string(),
6326
- action: z29.string().optional()
6490
+ result: z30.object({
6491
+ matched: z30.number(),
6492
+ selector: z30.string(),
6493
+ action: z30.string().optional()
6327
6494
  }),
6328
6495
  handler: async (p, ctx) => {
6329
6496
  const page = ctx.page;
@@ -6338,7 +6505,7 @@ var findCommand = registerCommand({
6338
6505
  });
6339
6506
  const count = await locator.count();
6340
6507
  if (count === 0) {
6341
- return fail13(`No element found with ${p.strategy}="${p.value}"`);
6508
+ return fail14(`No element found with ${p.strategy}="${p.value}"`);
6342
6509
  }
6343
6510
  const tips = [];
6344
6511
  const target = selectTarget(locator, p.strategy);
@@ -6350,15 +6517,15 @@ var findCommand = registerCommand({
6350
6517
  await target.click({ timeout: p.timeout, force: true });
6351
6518
  return okWithTips({ matched: count, selector, action: "click" }, tips);
6352
6519
  } else if (actionName === "fill") {
6353
- if (actionValue === void 0) return fail13("find fill requires a value");
6520
+ if (actionValue === void 0) return fail14("find fill requires a value");
6354
6521
  await target.fill(actionValue, { timeout: p.timeout, force: true });
6355
6522
  return okWithTips({ matched: count, selector, action: `fill("${actionValue}")` }, tips);
6356
6523
  } else if (actionName === "type") {
6357
- if (actionValue === void 0) return fail13("find type requires a value");
6524
+ if (actionValue === void 0) return fail14("find type requires a value");
6358
6525
  await target.type(actionValue, { delay: 10, timeout: p.timeout });
6359
6526
  return okWithTips({ matched: count, selector, action: `type("${actionValue}")` }, tips);
6360
6527
  } else if (actionName === "select") {
6361
- if (actionValue === void 0) return fail13("find select requires a value");
6528
+ if (actionValue === void 0) return fail14("find select requires a value");
6362
6529
  await target.selectOption(actionValue);
6363
6530
  return okWithTips({ matched: count, selector, action: `select("${actionValue}")` }, tips);
6364
6531
  } else if (actionName === "hover") {
@@ -6372,8 +6539,8 @@ var findCommand = registerCommand({
6372
6539
  }
6373
6540
  });
6374
6541
  function okWithTips(data, tips) {
6375
- const result = ok28(data);
6376
- if (tips.length > 0) result.tips = normalizeTips5(tips);
6542
+ const result = ok29(data);
6543
+ if (tips.length > 0) result.tips = normalizeTips6(tips);
6377
6544
  return result;
6378
6545
  }
6379
6546
  function parseOperation(operation) {
@@ -6471,8 +6638,8 @@ function describeSelector(strategy, value, name) {
6471
6638
  }
6472
6639
 
6473
6640
  // src/commands/visual-tag.ts
6474
- import { z as z30 } from "zod";
6475
- import { ok as ok29, fail as fail14 } from "@dyyz1993/xcli-core";
6641
+ import { z as z31 } from "zod";
6642
+ import { ok as ok30, fail as fail15 } from "@dyyz1993/xcli-core";
6476
6643
  var SAFE_SET = "ahjkmnprtw3479".split("");
6477
6644
  var TAG_SCRIPT = `
6478
6645
  (function() {
@@ -6563,49 +6730,49 @@ var visualTagCommand = registerCommand({
6563
6730
  name: "visual-tag",
6564
6731
  description: "\u5728\u9875\u9762\u4E0A\u7ED9\u53EF\u4EA4\u4E92\u5143\u7D20\u53E0\u52A0\u9632\u6DF7\u6DC6\u77ED ID \u6807\u7B7E\uFF08\u89C6\u89C9\u5B9A\u4F4D\u7528\uFF09",
6565
6732
  scope: "page",
6566
- parameters: z30.object({
6567
- action: z30.enum(["tag", "lookup", "clear", "list"]),
6568
- id: z30.string().optional().describe("lookup \u65F6\u6307\u5B9A\u8981\u67E5\u7684 ID")
6733
+ parameters: z31.object({
6734
+ action: z31.enum(["tag", "lookup", "clear", "list"]),
6735
+ id: z31.string().optional().describe("lookup \u65F6\u6307\u5B9A\u8981\u67E5\u7684 ID")
6569
6736
  }),
6570
- result: z30.object({
6571
- action: z30.string(),
6572
- tagged: z30.number().optional(),
6573
- id: z30.string().optional(),
6574
- element: z30.record(z30.unknown()).optional()
6737
+ result: z31.object({
6738
+ action: z31.string(),
6739
+ tagged: z31.number().optional(),
6740
+ id: z31.string().optional(),
6741
+ element: z31.record(z31.unknown()).optional()
6575
6742
  }),
6576
6743
  handler: async (p, ctx) => {
6577
6744
  switch (p.action) {
6578
6745
  case "tag": {
6579
6746
  const result = await ctx.page.evaluate(TAG_SCRIPT);
6580
6747
  const parsed = JSON.parse(result);
6581
- return ok29({ action: "tag", tagged: parsed.tagged });
6748
+ return ok30({ action: "tag", tagged: parsed.tagged });
6582
6749
  }
6583
6750
  case "lookup": {
6584
- if (!p.id) return fail14("lookup \u9700\u8981 --id \u53C2\u6570");
6751
+ if (!p.id) return fail15("lookup \u9700\u8981 --id \u53C2\u6570");
6585
6752
  const result = await ctx.page.evaluate(
6586
6753
  `(function() { var m = window.__xbTagMap; if (!m) return JSON.stringify({err:'no-map'}); var e = m[${JSON.stringify(p.id)}]; return e ? JSON.stringify(e) : JSON.stringify({err:'not-found'}); })()`
6587
6754
  );
6588
6755
  const parsed = JSON.parse(result);
6589
- if (parsed.err) return fail14(`ID ${p.id}: ${parsed.err}`);
6590
- return ok29({ action: "lookup", id: p.id, element: parsed });
6756
+ if (parsed.err) return fail15(`ID ${p.id}: ${parsed.err}`);
6757
+ return ok30({ action: "lookup", id: p.id, element: parsed });
6591
6758
  }
6592
6759
  case "list": {
6593
6760
  const result = await ctx.page.evaluate(
6594
6761
  `(function() { var m = window.__xbTagMap; if (!m) return '{}'; var out = {}; for (var k in m) { out[k] = m[k].text || m[k].tagName; } return JSON.stringify(out); })()`
6595
6762
  );
6596
- return ok29({ action: "list", element: JSON.parse(result) });
6763
+ return ok30({ action: "list", element: JSON.parse(result) });
6597
6764
  }
6598
6765
  case "clear": {
6599
6766
  await ctx.page.evaluate(`(function(){var o=document.getElementById('__xb-tag-overlay');if(o)o.remove();window.__xbTagMap=null;return 'cleared'})()`);
6600
- return ok29({ action: "clear" });
6767
+ return ok30({ action: "clear" });
6601
6768
  }
6602
6769
  }
6603
6770
  }
6604
6771
  });
6605
6772
 
6606
6773
  // src/commands/visual-tag-v2.ts
6607
- import { z as z31 } from "zod";
6608
- import { ok as ok30, fail as fail15 } from "@dyyz1993/xcli-core";
6774
+ import { z as z32 } from "zod";
6775
+ import { ok as ok31, fail as fail16 } from "@dyyz1993/xcli-core";
6609
6776
  var SAFE_SET2 = "ahjkmnprtw3479".split("");
6610
6777
  var TAG_V2_SCRIPT = `
6611
6778
  (function() {
@@ -6952,45 +7119,45 @@ var visualTagV2Command = registerCommand({
6952
7119
  name: "visual-tag-v2",
6953
7120
  description: "\u5206\u8272\u6807\u6CE8\u9875\u9762\u5143\u7D20\uFF08\u7EA2=\u53EF\u70B9\u51FB \u84DD=\u8F93\u5165 \u7EFF=\u56FE\u7247 \u9EC4=\u5217\u8868 \u7D2B=\u6570\u5B57 \u6A59=\u6587\u672C\uFF09",
6954
7121
  scope: "page",
6955
- parameters: z31.object({
6956
- action: z31.enum(["tag", "lookup", "clear", "stats", "by-type", "find", "export", "interact"]),
6957
- id: z31.string().optional().describe("lookup \u65F6\u6307\u5B9A\u8981\u67E5\u7684 ID"),
6958
- query: z31.string().optional().describe('find \u65F6\u6307\u5B9A\u641C\u7D22\u8BCD\uFF08\u5982 "\u627E\u6709AI\u7F16\u7A0B\u6807\u7B7E\u7684\u5361\u7247"\uFF09'),
6959
- type: z31.string().optional().describe("by-type \u65F6\u8FC7\u6EE4\u7C7B\u578B\uFF1Aclick/input/img/list/count/text"),
6960
- "interact-action": z31.enum(["click", "fill", "read"]).optional().describe("interact \u52A8\u4F5C\u7C7B\u578B"),
6961
- value: z31.string().optional().describe("fill \u65F6\u7684\u586B\u5145\u503C")
7122
+ parameters: z32.object({
7123
+ action: z32.enum(["tag", "lookup", "clear", "stats", "by-type", "find", "export", "interact"]),
7124
+ id: z32.string().optional().describe("lookup \u65F6\u6307\u5B9A\u8981\u67E5\u7684 ID"),
7125
+ query: z32.string().optional().describe('find \u65F6\u6307\u5B9A\u641C\u7D22\u8BCD\uFF08\u5982 "\u627E\u6709AI\u7F16\u7A0B\u6807\u7B7E\u7684\u5361\u7247"\uFF09'),
7126
+ type: z32.string().optional().describe("by-type \u65F6\u8FC7\u6EE4\u7C7B\u578B\uFF1Aclick/input/img/list/count/text"),
7127
+ "interact-action": z32.enum(["click", "fill", "read"]).optional().describe("interact \u52A8\u4F5C\u7C7B\u578B"),
7128
+ value: z32.string().optional().describe("fill \u65F6\u7684\u586B\u5145\u503C")
6962
7129
  }),
6963
- result: z31.object({
6964
- action: z31.string(),
6965
- total: z31.number().optional(),
6966
- byType: z31.record(z31.number()).optional(),
6967
- element: z31.record(z31.unknown()).optional()
7130
+ result: z32.object({
7131
+ action: z32.string(),
7132
+ total: z32.number().optional(),
7133
+ byType: z32.record(z32.number()).optional(),
7134
+ element: z32.record(z32.unknown()).optional()
6968
7135
  }),
6969
7136
  handler: async (p, ctx) => {
6970
7137
  switch (p.action) {
6971
7138
  case "tag": {
6972
7139
  const result = await ctx.page.evaluate(TAG_V2_SCRIPT);
6973
7140
  const parsed = JSON.parse(result);
6974
- return ok30({ action: "tag", total: parsed.total, byType: parsed.byType });
7141
+ return ok31({ action: "tag", total: parsed.total, byType: parsed.byType });
6975
7142
  }
6976
7143
  case "lookup": {
6977
- if (!p.id) return fail15("lookup \u9700\u8981 --id");
7144
+ if (!p.id) return fail16("lookup \u9700\u8981 --id");
6978
7145
  const result = await ctx.page.evaluate(
6979
7146
  `(function(){var m=window.__xbTagMap;if(!m)return JSON.stringify({err:'no-map'});var e=m[${JSON.stringify(p.id)}];return e?JSON.stringify(e):JSON.stringify({err:'not-found'})})()`
6980
7147
  );
6981
7148
  const parsed = JSON.parse(result);
6982
- if (parsed.err) return fail15(`ID ${p.id}: ${parsed.err}`);
6983
- return ok30({ action: "lookup", id: p.id, element: parsed });
7149
+ if (parsed.err) return fail16(`ID ${p.id}: ${parsed.err}`);
7150
+ return ok31({ action: "lookup", id: p.id, element: parsed });
6984
7151
  }
6985
7152
  case "by-type": {
6986
7153
  const result = await ctx.page.evaluate(
6987
7154
  `(function(){var m=window.__xbTagMap;if(!m)return'{}';var out={};for(var k in m){if(!${JSON.stringify(p.type || "")}||m[k].type===${JSON.stringify(p.type || "")}){out[k]={type:m[k].type,text:m[k].text,num:m[k].num,label:m[k].formLabel}}}return JSON.stringify(out)})()`
6988
7155
  );
6989
- return ok30({ action: "by-type", element: JSON.parse(result) });
7156
+ return ok31({ action: "by-type", element: JSON.parse(result) });
6990
7157
  }
6991
7158
  case "find": {
6992
7159
  const query = p.query;
6993
- if (!query) return fail15('find \u9700\u8981 --query \u53C2\u6570\uFF08\u5982 "\u627E\u6709AI\u7F16\u7A0B\u6807\u7B7E\u7684\u5361\u7247"\uFF09');
7160
+ if (!query) return fail16('find \u9700\u8981 --query \u53C2\u6570\uFF08\u5982 "\u627E\u6709AI\u7F16\u7A0B\u6807\u7B7E\u7684\u5361\u7247"\uFF09');
6994
7161
  const result = await ctx.page.evaluate(
6995
7162
  `(function(){
6996
7163
  var map = window.__xbTagSerializable;
@@ -7032,21 +7199,21 @@ var visualTagV2Command = registerCommand({
7032
7199
  return JSON.stringify({total:results.length, results:results.slice(0,20)});
7033
7200
  })()`
7034
7201
  );
7035
- return ok30({ action: "find", query, element: JSON.parse(result) });
7202
+ return ok31({ action: "find", query, element: JSON.parse(result) });
7036
7203
  }
7037
7204
  case "stats": {
7038
7205
  const result = await ctx.page.evaluate(
7039
7206
  `(function(){return JSON.stringify(window.__xbTagStats||{})})()`
7040
7207
  );
7041
- return ok30({ action: "stats", element: JSON.parse(result) });
7208
+ return ok31({ action: "stats", element: JSON.parse(result) });
7042
7209
  }
7043
7210
  case "interact": {
7044
7211
  const query = p.query;
7045
7212
  const pAny = p;
7046
7213
  const action = pAny["interact-action"] || pAny.interactAction || pAny.interact_action;
7047
7214
  const value = pAny.value;
7048
- if (!query) return fail15("interact \u9700\u8981 --query \u53C2\u6570");
7049
- if (!action) return fail15("interact \u9700\u8981 --interact-action \u53C2\u6570\uFF08click/fill/read\uFF09");
7215
+ if (!query) return fail16("interact \u9700\u8981 --query \u53C2\u6570");
7216
+ if (!action) return fail16("interact \u9700\u8981 --interact-action \u53C2\u6570\uFF08click/fill/read\uFF09");
7050
7217
  const findResult = await ctx.page.evaluate(
7051
7218
  `(function(){
7052
7219
  var map = window.__xbTagSerializable;
@@ -7076,11 +7243,11 @@ var visualTagV2Command = registerCommand({
7076
7243
  })()`
7077
7244
  );
7078
7245
  const parsed = JSON.parse(findResult);
7079
- if (parsed.err) return fail15(parsed.err);
7080
- if (!parsed.total || !parsed.results?.length) return fail15(`\u672A\u627E\u5230\u5339\u914D "${query}" \u7684\u5143\u7D20`);
7246
+ if (parsed.err) return fail16(parsed.err);
7247
+ if (!parsed.total || !parsed.results?.length) return fail16(`\u672A\u627E\u5230\u5339\u914D "${query}" \u7684\u5143\u7D20`);
7081
7248
  const target = parsed.results[0];
7082
7249
  if (action === "read") {
7083
- return ok30({ action: "interact", query, target, result: { read: target.text || target.label } });
7250
+ return ok31({ action: "interact", query, target, result: { read: target.text || target.label } });
7084
7251
  }
7085
7252
  if (action === "click") {
7086
7253
  const el = await ctx.page.evaluate(
@@ -7093,12 +7260,12 @@ var visualTagV2Command = registerCommand({
7093
7260
  return {x: Math.round(r.x + r.width/2), y: Math.round(r.y + r.height/2)};
7094
7261
  })()`
7095
7262
  );
7096
- if (!el) return fail15(`\u5143\u7D20 ${target.id} \u4E0D\u53EF\u4EA4\u4E92`);
7263
+ if (!el) return fail16(`\u5143\u7D20 ${target.id} \u4E0D\u53EF\u4EA4\u4E92`);
7097
7264
  await ctx.page.mouse.click(el.x, el.y, { stealth: true });
7098
- return ok30({ action: "interact", query, target, result: { clicked: true, x: el.x, y: el.y } });
7265
+ return ok31({ action: "interact", query, target, result: { clicked: true, x: el.x, y: el.y } });
7099
7266
  }
7100
7267
  if (action === "fill") {
7101
- if (!value) return fail15("fill \u9700\u8981 --value \u53C2\u6570");
7268
+ if (!value) return fail16("fill \u9700\u8981 --value \u53C2\u6570");
7102
7269
  const el = await ctx.page.evaluate(
7103
7270
  `(function(){
7104
7271
  var map = window.__xbTagMap;
@@ -7109,12 +7276,12 @@ var visualTagV2Command = registerCommand({
7109
7276
  return {x: Math.round(r.x + r.width/2), y: Math.round(r.y + r.height/2)};
7110
7277
  })()`
7111
7278
  );
7112
- if (!el) return fail15(`\u5143\u7D20 ${target.id} \u4E0D\u53EF\u4EA4\u4E92`);
7279
+ if (!el) return fail16(`\u5143\u7D20 ${target.id} \u4E0D\u53EF\u4EA4\u4E92`);
7113
7280
  await ctx.page.mouse.click(el.x, el.y, { stealth: true });
7114
7281
  await ctx.page.keyboard.type(value, { stealth: true });
7115
- return ok30({ action: "interact", query, target, result: { filled: true, value } });
7282
+ return ok31({ action: "interact", query, target, result: { filled: true, value } });
7116
7283
  }
7117
- return fail15(`\u672A\u77E5 action: ${action}`);
7284
+ return fail16(`\u672A\u77E5 action: ${action}`);
7118
7285
  }
7119
7286
  case "export": {
7120
7287
  const result = await ctx.page.evaluate(
@@ -7162,12 +7329,12 @@ var visualTagV2Command = registerCommand({
7162
7329
  })()`
7163
7330
  );
7164
7331
  const parsed = JSON.parse(result);
7165
- if (parsed.err) return fail15(String(parsed.err));
7166
- return ok30({ action: "export", element: parsed });
7332
+ if (parsed.err) return fail16(String(parsed.err));
7333
+ return ok31({ action: "export", element: parsed });
7167
7334
  }
7168
7335
  case "clear": {
7169
7336
  await ctx.page.evaluate(`(function(){var o=document.getElementById('__xb-tag-overlay');if(o)o.remove();window.__xbTagMap=null;window.__xbTagStats=null;return 'ok'})()`);
7170
- return ok30({ action: "clear" });
7337
+ return ok31({ action: "clear" });
7171
7338
  }
7172
7339
  }
7173
7340
  }
@@ -7693,12 +7860,12 @@ function classifyFailure(message) {
7693
7860
 
7694
7861
  // src/executor.ts
7695
7862
  import {
7696
- ok as ok31,
7697
- fail as fail16,
7863
+ ok as ok32,
7864
+ fail as fail17,
7698
7865
  isCommandResult,
7699
7866
  CompositeStorage as CompositeStorage2,
7700
7867
  TipCollector as TipCollector2,
7701
- normalizeTips as normalizeTips6,
7868
+ normalizeTips as normalizeTips7,
7702
7869
  configureArchiveStore,
7703
7870
  appendCommandToArchive,
7704
7871
  checkGuard,
@@ -8565,7 +8732,7 @@ async function guardCheck(commandName) {
8565
8732
  }
8566
8733
  }
8567
8734
  function errorResult(message) {
8568
- return { ...fail16(message), duration: 0 };
8735
+ return { ...fail17(message), duration: 0 };
8569
8736
  }
8570
8737
  function tipsToMessages(tips) {
8571
8738
  if (!tips || tips.length === 0) return [];
@@ -8757,7 +8924,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
8757
8924
  const cache = /* @__PURE__ */ new Map();
8758
8925
  const resolved = await resolveRefParams(session.page, params, command.selectorParams, cache, session.id);
8759
8926
  if (resolved.tips.length > 0) {
8760
- refTips = normalizeTips6(resolved.tips);
8927
+ refTips = normalizeTips7(resolved.tips);
8761
8928
  params = resolved.params;
8762
8929
  }
8763
8930
  }
@@ -8825,8 +8992,8 @@ async function executeCommand(commandName, params, sessionName = "default", extr
8825
8992
  }
8826
8993
  const merged = [
8827
8994
  ...raw.tips || [],
8828
- ...normalizeTips6(smartTips),
8829
- ...snapshotHint ? normalizeTips6([snapshotHint]) : [],
8995
+ ...normalizeTips7(smartTips),
8996
+ ...snapshotHint ? normalizeTips7([snapshotHint]) : [],
8830
8997
  ...refTips
8831
8998
  ];
8832
8999
  const isSuccess = raw.success !== false;
@@ -8841,7 +9008,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
8841
9008
  timestamp: start
8842
9009
  });
8843
9010
  if (isSuccess) {
8844
- return { ...ok31(raw.data, merged.length > 0 ? merged : raw.tips), duration, ...hookOutputs ? { hookOutputs } : {} };
9011
+ return { ...ok32(raw.data, merged.length > 0 ? merged : raw.tips), duration, ...hookOutputs ? { hookOutputs } : {} };
8845
9012
  }
8846
9013
  const fc = classifyFailure(raw.message);
8847
9014
  return {
@@ -8854,7 +9021,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
8854
9021
  ...hookOutputs ? { hookOutputs } : {}
8855
9022
  };
8856
9023
  }
8857
- const smartTipNormalized = normalizeTips6(smartTips);
9024
+ const smartTipNormalized = normalizeTips7(smartTips);
8858
9025
  recordArchive(session?.id, sessionName, {
8859
9026
  step: 0,
8860
9027
  command: commandName,
@@ -8864,7 +9031,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
8864
9031
  duration,
8865
9032
  timestamp: start
8866
9033
  });
8867
- return { ...ok31(raw, smartTipNormalized), duration, ...hookOutputs ? { hookOutputs } : {} };
9034
+ return { ...ok32(raw, smartTipNormalized), duration, ...hookOutputs ? { hookOutputs } : {} };
8868
9035
  } catch (err) {
8869
9036
  const end = Date.now();
8870
9037
  const duration = end - start;
@@ -8909,7 +9076,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
8909
9076
  duration,
8910
9077
  timestamp: start
8911
9078
  });
8912
- return { ...fail16(errorMessage), duration };
9079
+ return { ...fail17(errorMessage), duration };
8913
9080
  } finally {
8914
9081
  }
8915
9082
  }
@@ -8954,7 +9121,7 @@ async function executeChain(input, options) {
8954
9121
  results.push({
8955
9122
  command: cmdName,
8956
9123
  raw: cmdStr,
8957
- ...fail16(`Plugin "${cmdName}" requires a sub-command`),
9124
+ ...fail17(`Plugin "${cmdName}" requires a sub-command`),
8958
9125
  duration: 0
8959
9126
  });
8960
9127
  if (type === "and") {
@@ -8973,7 +9140,7 @@ async function executeChain(input, options) {
8973
9140
  results.push({
8974
9141
  command: cmdName,
8975
9142
  raw: cmdStr,
8976
- ...fail16(`Unknown command "${subCommand}" for plugin "${cmdName}"`),
9143
+ ...fail17(`Unknown command "${subCommand}" for plugin "${cmdName}"`),
8977
9144
  duration: 0
8978
9145
  });
8979
9146
  if (type === "and") {
@@ -9043,7 +9210,7 @@ async function executeChain(input, options) {
9043
9210
  success: false,
9044
9211
  data: data2,
9045
9212
  message: loginGuard.message,
9046
- tips: normalizeTips6(loginGuard.tips),
9213
+ tips: normalizeTips7(loginGuard.tips),
9047
9214
  duration: duration3
9048
9215
  });
9049
9216
  if (type === "and") {
@@ -9085,7 +9252,7 @@ async function executeChain(input, options) {
9085
9252
  results.push({
9086
9253
  command: `${cmdName} ${subCommand}`,
9087
9254
  raw: cmdStr,
9088
- ...ok31(data),
9255
+ ...ok32(data),
9089
9256
  duration: duration2,
9090
9257
  ...hookOutputs ? { hookOutputs } : {}
9091
9258
  });
@@ -9113,7 +9280,7 @@ async function executeChain(input, options) {
9113
9280
  results.push({
9114
9281
  command: `${cmdName} ${subCommand}`,
9115
9282
  raw: cmdStr,
9116
- ...fail16(errorMessage),
9283
+ ...fail17(errorMessage),
9117
9284
  duration: duration2
9118
9285
  });
9119
9286
  if (type === "and") {