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