@optima-chat/browser-cli 0.10.1 → 0.10.2
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/index.js +95 -12
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -183,13 +183,14 @@ async function requestBinary(path) {
|
|
|
183
183
|
return Buffer.from(arrayBuffer);
|
|
184
184
|
}
|
|
185
185
|
var BrowserAPI = class {
|
|
186
|
-
async createSession(url, force, connector, ziniaoProfileId, bitbrowserProfileId) {
|
|
186
|
+
async createSession(url, force, connector, ziniaoProfileId, bitbrowserProfileId, feikuaProfileId) {
|
|
187
187
|
const body = {};
|
|
188
188
|
if (url) body.url = url;
|
|
189
189
|
if (force) body.force = true;
|
|
190
190
|
if (connector) body.connector = connector;
|
|
191
191
|
if (ziniaoProfileId) body.ziniao_profile_id = ziniaoProfileId;
|
|
192
192
|
if (bitbrowserProfileId) body.bitbrowser_profile_id = bitbrowserProfileId;
|
|
193
|
+
if (feikuaProfileId) body.feikua_profile_id = feikuaProfileId;
|
|
193
194
|
return request("/api/sessions", {
|
|
194
195
|
method: "POST",
|
|
195
196
|
body: JSON.stringify(body)
|
|
@@ -213,6 +214,11 @@ var BrowserAPI = class {
|
|
|
213
214
|
async getBitBrowserProfiles() {
|
|
214
215
|
return request("/api/user/bitbrowser-profiles");
|
|
215
216
|
}
|
|
217
|
+
// paired_count = 配对过的店铺浏览器总数(含没在运行的)。老桌面端不返回它,
|
|
218
|
+
// 所以是可选的 —— 缺失时调用方只能退回「从未配对」的指引。
|
|
219
|
+
async getFeikuaProfiles() {
|
|
220
|
+
return request("/api/user/feikua-profiles");
|
|
221
|
+
}
|
|
216
222
|
async closeSession(sessionId) {
|
|
217
223
|
await request(`/api/sessions/${sessionId}`, { method: "DELETE" });
|
|
218
224
|
}
|
|
@@ -243,8 +249,18 @@ var BrowserAPI = class {
|
|
|
243
249
|
async input(sessionId, index, text, clear = true) {
|
|
244
250
|
return this.actThenState(sessionId, "input", { index, text, clear });
|
|
245
251
|
}
|
|
246
|
-
async scroll(sessionId, down, pages = 1) {
|
|
247
|
-
return this.actThenState(sessionId, "scroll", {
|
|
252
|
+
async scroll(sessionId, down, pages = 1, index) {
|
|
253
|
+
return this.actThenState(sessionId, "scroll", {
|
|
254
|
+
down,
|
|
255
|
+
pages,
|
|
256
|
+
...index === void 0 ? {} : { index }
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
async clickTo(sessionId, selector, native = false) {
|
|
260
|
+
return this.actThenState(sessionId, "click_to", { selector, ...native ? { native } : {} });
|
|
261
|
+
}
|
|
262
|
+
async scrollTo(sessionId, selector) {
|
|
263
|
+
return this.actThenState(sessionId, "scroll_to", { selector });
|
|
248
264
|
}
|
|
249
265
|
async keys(sessionId, keys) {
|
|
250
266
|
return this.actThenState(sessionId, "keys", { keys });
|
|
@@ -342,11 +358,12 @@ var BrowserAPI = class {
|
|
|
342
358
|
async deleteScript(name) {
|
|
343
359
|
await request(`/api/scripts/${encodeURIComponent(name)}`, { method: "DELETE" });
|
|
344
360
|
}
|
|
345
|
-
async runScript(scriptName, params = {}, connector, ziniaoProfileId, bitbrowserProfileId) {
|
|
361
|
+
async runScript(scriptName, params = {}, connector, ziniaoProfileId, bitbrowserProfileId, feikuaProfileId) {
|
|
346
362
|
const payload = { params };
|
|
347
363
|
if (connector) payload.connector = connector;
|
|
348
364
|
if (ziniaoProfileId) payload.ziniao_profile_id = ziniaoProfileId;
|
|
349
365
|
if (bitbrowserProfileId) payload.bitbrowser_profile_id = bitbrowserProfileId;
|
|
366
|
+
if (feikuaProfileId) payload.feikua_profile_id = feikuaProfileId;
|
|
350
367
|
return request(`/api/scripts/${encodeURIComponent(scriptName)}/run`, {
|
|
351
368
|
method: "POST",
|
|
352
369
|
body: JSON.stringify(payload)
|
|
@@ -450,7 +467,12 @@ function formatFind(result) {
|
|
|
450
467
|
const lines = [`Found ${result.total} element(s):`, ""];
|
|
451
468
|
for (const match of result.matches) {
|
|
452
469
|
const attrs = Object.entries(match.attributes).map(([k, v]) => `${k}="${v}"`).join(" ");
|
|
453
|
-
|
|
470
|
+
const state = Object.entries(match.state ?? {}).filter(([, v]) => v).map(([k]) => k).join(" ");
|
|
471
|
+
const head = ` [${match.index}] <${match.tag} ${attrs}>`;
|
|
472
|
+
lines.push(state ? `${head} [${state}]` : head);
|
|
473
|
+
if (match.text) {
|
|
474
|
+
lines.push(` ${match.text}`);
|
|
475
|
+
}
|
|
454
476
|
}
|
|
455
477
|
return lines.join("\n");
|
|
456
478
|
}
|
|
@@ -469,9 +491,9 @@ function formatTabsInline(tabs) {
|
|
|
469
491
|
|
|
470
492
|
// src/commands/lifecycle.ts
|
|
471
493
|
function registerLifecycleCommands(program2) {
|
|
472
|
-
program2.command("launch").description("Launch a browser session").option("--url <url>", "Navigate to URL after launch").option("--force", "Close existing session before launching").option("--connector <type>", "Browser connector: chrome, ziniao, or
|
|
494
|
+
program2.command("launch").description("Launch a browser session").option("--url <url>", "Navigate to URL after launch").option("--force", "Close existing session before launching").option("--connector <type>", "Browser connector: chrome, ziniao, bitbrowser, or feikua").option("--ziniao-profile <id>", "ZiNiao browser profile ID").option("--bitbrowser-profile <id>", "BitBrowser profile ID").option("--feikua-profile <id>", "Feikua (\u98DE\u8DE8) shop browser instance ID").action(async (opts) => {
|
|
473
495
|
try {
|
|
474
|
-
const state = await api.createSession(opts.url, opts.force, opts.connector, opts.ziniaoProfile, opts.bitbrowserProfile);
|
|
496
|
+
const state = await api.createSession(opts.url, opts.force, opts.connector, opts.ziniaoProfile, opts.bitbrowserProfile, opts.feikuaProfile);
|
|
475
497
|
saveSessionId(state.session_id);
|
|
476
498
|
console.log(formatLaunch(state));
|
|
477
499
|
} catch (err) {
|
|
@@ -563,12 +585,36 @@ function registerInteractionCommands(program2) {
|
|
|
563
585
|
process.exit(1);
|
|
564
586
|
}
|
|
565
587
|
});
|
|
566
|
-
program2.command("scroll").description("Scroll the page up or down").argument("<direction>", "Scroll direction: up or down").option("--pages <n>", "Number of pages to scroll", "1").
|
|
588
|
+
program2.command("scroll").description("Scroll the page up or down").argument("<direction>", "Scroll direction: up or down").option("--pages <n>", "Number of pages to scroll", "1").option(
|
|
589
|
+
"--index <n>",
|
|
590
|
+
"Scroll inside this element instead of the window (for pages with inner scroll containers)"
|
|
591
|
+
).action(async (direction, opts) => {
|
|
567
592
|
try {
|
|
568
593
|
const sessionId = loadSessionId();
|
|
569
594
|
const down = direction.toLowerCase() === "down";
|
|
570
595
|
const pages = parseInt(opts.pages, 10);
|
|
571
|
-
const
|
|
596
|
+
const index = opts.index === void 0 ? void 0 : parseInt(opts.index, 10);
|
|
597
|
+
const state = await api.scroll(sessionId, down, pages, index);
|
|
598
|
+
console.log(formatPageState(state));
|
|
599
|
+
} catch (err) {
|
|
600
|
+
console.error(formatError(err));
|
|
601
|
+
process.exit(1);
|
|
602
|
+
}
|
|
603
|
+
});
|
|
604
|
+
program2.command("click-to").description("Click the element matching a CSS selector (for elements with no DOM index)").argument("<selector>", "CSS selector of the element to click").option("--native", "Dispatch real mouse events (for controls that ignore synthetic clicks)").action(async (selector, opts) => {
|
|
605
|
+
try {
|
|
606
|
+
const sessionId = loadSessionId();
|
|
607
|
+
const state = await api.clickTo(sessionId, selector, opts.native === true);
|
|
608
|
+
console.log(formatPageState(state));
|
|
609
|
+
} catch (err) {
|
|
610
|
+
console.error(formatError(err));
|
|
611
|
+
process.exit(1);
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
program2.command("scroll-to").description("Scroll the element matching a CSS selector into view").argument("<selector>", "CSS selector of the element to scroll into view").action(async (selector) => {
|
|
615
|
+
try {
|
|
616
|
+
const sessionId = loadSessionId();
|
|
617
|
+
const state = await api.scrollTo(sessionId, selector);
|
|
572
618
|
console.log(formatPageState(state));
|
|
573
619
|
} catch (err) {
|
|
574
620
|
console.error(formatError(err));
|
|
@@ -694,7 +740,8 @@ var POLL_INTERVAL_MS = 2e3;
|
|
|
694
740
|
var POLL_TIMEOUT_MS = 18e4;
|
|
695
741
|
var REQUIRED_ARG_FLAG = {
|
|
696
742
|
ziniao_profile_id: "--ziniao-profile <id>",
|
|
697
|
-
bitbrowser_profile_id: "--bitbrowser-profile <id>"
|
|
743
|
+
bitbrowser_profile_id: "--bitbrowser-profile <id>",
|
|
744
|
+
feikua_profile_id: "--feikua-profile <id>"
|
|
698
745
|
};
|
|
699
746
|
function formatRequiredArgs(s) {
|
|
700
747
|
if (!s.required_args?.length) return null;
|
|
@@ -810,7 +857,7 @@ function registerScriptCommands(program2) {
|
|
|
810
857
|
process.exit(1);
|
|
811
858
|
}
|
|
812
859
|
});
|
|
813
|
-
program2.command("runscript").argument("<name>", "Script name").description("Run a script (polls until complete)").option("--param <params...>", "Parameters as key=value").option("--connector <type>", "Browser connector: chrome, ziniao, or
|
|
860
|
+
program2.command("runscript").argument("<name>", "Script name").description("Run a script (polls until complete)").option("--param <params...>", "Parameters as key=value").option("--connector <type>", "Browser connector: chrome, ziniao, bitbrowser, or feikua").option("--ziniao-profile <id>", "ZiNiao browser profile ID (with --connector ziniao)").option("--bitbrowser-profile <id>", "BitBrowser profile ID (with --connector bitbrowser)").option("--feikua-profile <id>", "Feikua (\u98DE\u8DE8) shop browser instance ID (with --connector feikua)").option("--out <path>", "Where to write the script's exported file (default: ./<filename>)").option("--no-poll", "Return immediately without polling").action(async (name, opts) => {
|
|
814
861
|
try {
|
|
815
862
|
const params = {};
|
|
816
863
|
if (opts.param) {
|
|
@@ -821,7 +868,7 @@ function registerScriptCommands(program2) {
|
|
|
821
868
|
}
|
|
822
869
|
if (opts.out) params.out_path = resolve(opts.out);
|
|
823
870
|
clearSessionId();
|
|
824
|
-
const task = await api.runScript(name, params, opts.connector, opts.ziniaoProfile, opts.bitbrowserProfile);
|
|
871
|
+
const task = await api.runScript(name, params, opts.connector, opts.ziniaoProfile, opts.bitbrowserProfile, opts.feikuaProfile);
|
|
825
872
|
console.log(`Script started (task: ${task.id})`);
|
|
826
873
|
if (!opts.poll) {
|
|
827
874
|
console.log(`Poll with: browser-cli script-task ${task.id}`);
|
|
@@ -1113,6 +1160,41 @@ function registerBitBrowserCommands(program2) {
|
|
|
1113
1160
|
});
|
|
1114
1161
|
}
|
|
1115
1162
|
|
|
1163
|
+
// src/commands/feikua.ts
|
|
1164
|
+
function registerFeikuaCommands(program2) {
|
|
1165
|
+
const feikua = program2.command("feikua").description("Manage Feikua (\u98DE\u8DE8) anti-detect browser");
|
|
1166
|
+
feikua.command("profiles").description("List paired Feikua shop browsers (requires Desktop app online + extension paired)").action(async () => {
|
|
1167
|
+
try {
|
|
1168
|
+
const result = await api.getFeikuaProfiles();
|
|
1169
|
+
if (!result.connected) {
|
|
1170
|
+
console.log(formatDesktopNotConnected(result));
|
|
1171
|
+
process.exit(1);
|
|
1172
|
+
}
|
|
1173
|
+
if (result.profiles.length === 0) {
|
|
1174
|
+
if (result.paired_count) {
|
|
1175
|
+
console.log(
|
|
1176
|
+
`${result.paired_count} paired shop browser(s), but none are running. Open the Feikua shop browser you paired, then retry.`
|
|
1177
|
+
);
|
|
1178
|
+
return;
|
|
1179
|
+
}
|
|
1180
|
+
console.log(
|
|
1181
|
+
'No paired shop browsers. Open the desktop app tray and choose "Pair a Shop Browser", then follow the on-screen code.'
|
|
1182
|
+
);
|
|
1183
|
+
return;
|
|
1184
|
+
}
|
|
1185
|
+
console.log(`Feikua shop browsers (${result.profiles.length}):
|
|
1186
|
+
`);
|
|
1187
|
+
for (const p of result.profiles) {
|
|
1188
|
+
console.log(` ${p.id} ${p.name}`);
|
|
1189
|
+
}
|
|
1190
|
+
console.log("\nUse: browser-cli launch --connector feikua --feikua-profile <id> --url <url>");
|
|
1191
|
+
} catch (err) {
|
|
1192
|
+
console.error(formatError(err));
|
|
1193
|
+
process.exit(1);
|
|
1194
|
+
}
|
|
1195
|
+
});
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1116
1198
|
// src/commands/files.ts
|
|
1117
1199
|
function registerFileCommands(program2) {
|
|
1118
1200
|
program2.command("send-file").description("Send a file from your workspace to your local download folder").requiredOption("--src <path>", "Source path in your workspace (e.g. ~/output/report.pdf)").requiredOption("--dest <path>", "Destination path relative to the download folder").action(async (opts) => {
|
|
@@ -1146,5 +1228,6 @@ registerScriptCommands(program);
|
|
|
1146
1228
|
registerZiNiaoCommands(program);
|
|
1147
1229
|
registerRecipeCommands(program);
|
|
1148
1230
|
registerBitBrowserCommands(program);
|
|
1231
|
+
registerFeikuaCommands(program);
|
|
1149
1232
|
registerFileCommands(program);
|
|
1150
1233
|
program.parse();
|