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