@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.
Files changed (2) hide show
  1. package/dist/index.js +95 -12
  2. 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", { down, pages });
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
- lines.push(` [${match.index}] <${match.tag} ${attrs}>`);
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 bitbrowser").option("--ziniao-profile <id>", "ZiNiao browser profile ID").option("--bitbrowser-profile <id>", "BitBrowser profile ID").action(async (opts) => {
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").action(async (direction, opts) => {
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 state = await api.scroll(sessionId, down, pages);
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 bitbrowser").option("--ziniao-profile <id>", "ZiNiao browser profile ID (with --connector ziniao)").option("--bitbrowser-profile <id>", "BitBrowser profile ID (with --connector bitbrowser)").option("--out <path>", "Where to write the script's exported file (default: ./<filename>)").option("--no-poll", "Return immediately without polling").action(async (name, opts) => {
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();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optima-chat/browser-cli",
3
- "version": "0.10.1",
3
+ "version": "0.10.2",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "bin": {