@optima-chat/browser-cli 0.15.4 → 0.17.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.
Files changed (2) hide show
  1. package/dist/index.js +24 -143
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -291,33 +291,6 @@ var BrowserAPI = class {
291
291
  async getScreenshot(sessionId) {
292
292
  return requestBinary(`/api/sessions/${sessionId}/screenshot`);
293
293
  }
294
- async runRecipeScript(name, params, pollMs = 2e3, timeoutMs = 36e4, timeoutHint) {
295
- const started = await request(`/api/scripts/${name}/run`, {
296
- method: "POST",
297
- body: JSON.stringify({ params })
298
- });
299
- const deadline = Date.now() + timeoutMs;
300
- for (; ; ) {
301
- const task = await request(`/api/script-tasks/${started.id}`);
302
- if (task.status === "completed") {
303
- if (!task.output) {
304
- throw new Error(
305
- `recipe ${name} reported completed but returned no output (task ${started.id})`
306
- );
307
- }
308
- return task.output;
309
- }
310
- if (task.status === "failed" || task.status === "needs_fallback") {
311
- throw new Error(task.error || task.output?.error || `recipe ${task.status}`);
312
- }
313
- if (Date.now() >= deadline) {
314
- throw new Error(
315
- `recipe ${name} did not finish within ${Math.round(timeoutMs / 1e3)}s (task ${started.id})` + (timeoutHint ? ` \u2014\u2014 ${timeoutHint}` : "")
316
- );
317
- }
318
- await new Promise((r) => setTimeout(r, pollMs));
319
- }
320
- }
321
294
  async fetchArtifact(artifactPath) {
322
295
  return requestBinary(artifactPath);
323
296
  }
@@ -354,10 +327,10 @@ var BrowserAPI = class {
354
327
  return this.getState(sessionId);
355
328
  }
356
329
  // Script methods
357
- async submitScript(name, code) {
330
+ async submitScript(name, files) {
358
331
  return request(`/api/scripts`, {
359
332
  method: "POST",
360
- body: JSON.stringify({ name, code })
333
+ body: JSON.stringify({ name, files })
361
334
  });
362
335
  }
363
336
  async updateScript(name, code) {
@@ -771,7 +744,7 @@ function registerTabCommands(program2) {
771
744
  }
772
745
 
773
746
  // src/commands/script.ts
774
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, statSync, writeFileSync as writeFileSync3 } from "fs";
747
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, readdirSync, statSync, writeFileSync as writeFileSync3 } from "fs";
775
748
  import { tmpdir } from "os";
776
749
  import { basename, dirname, isAbsolute, join as join3, relative, resolve } from "path";
777
750
  var POLL_INTERVAL_MS = 2e3;
@@ -898,11 +871,28 @@ function formatScriptTask(task) {
898
871
  }
899
872
  return lines.join("\n");
900
873
  }
874
+ function collectSkillDir(dir) {
875
+ const files = {};
876
+ for (const name of readdirSync(dir).sort()) {
877
+ if (!name.endsWith(".py")) continue;
878
+ if (name !== "script.py" && !name.startsWith("_")) continue;
879
+ if (!statSync(join3(dir, name)).isFile()) continue;
880
+ files[name] = readFileSync3(join3(dir, name), "utf-8");
881
+ }
882
+ if (files["script.py"] === void 0) throw new Error(`${dir} \u91CC\u6CA1\u6709 script.py`);
883
+ return files;
884
+ }
885
+ function resolveSubmitSource(opts) {
886
+ if (opts.codeFile && opts.codeDir) throw new Error("--code-file \u4E0E --code-dir \u53EA\u80FD\u7ED9\u4E00\u4E2A");
887
+ if (opts.codeDir) return collectSkillDir(opts.codeDir);
888
+ if (opts.codeFile) return { "script.py": readFileSync3(opts.codeFile, "utf-8") };
889
+ throw new Error("\u5FC5\u987B\u7ED9 --code-file \u6216 --code-dir");
890
+ }
901
891
  function registerScriptCommands(program2) {
902
- program2.command("submit-script").description("Submit a script (write to EFS)").requiredOption("--name <name>", "Script name").requiredOption("--code-file <path>", "Path to Python script file").action(async (opts) => {
892
+ program2.command("submit-script").description("Submit a script (write to EFS)").requiredOption("--name <name>", "Script name").option("--code-file <path>", "Path to Python script file (\u7B49\u4EF7\u4E8E\u300C\u53EA\u6709 script.py\u300D)").option("--code-dir <dir>", "Path to a skill directory (script.py + _*.py \u4E00\u8D77\u9001)").action(async (opts) => {
903
893
  try {
904
- const code = readFileSync3(opts.codeFile, "utf-8");
905
- const result = await api.submitScript(opts.name, code);
894
+ const files = resolveSubmitSource(opts);
895
+ const result = await api.submitScript(opts.name, files);
906
896
  console.log(`\u2713 Script ${result.name} ${result.created ? "created" : "updated"}`);
907
897
  } catch (err) {
908
898
  console.error(formatError(err));
@@ -1016,7 +1006,7 @@ Each artifact can be fetched once; if runscript printed "Saved ->", the file is
1016
1006
  });
1017
1007
  }
1018
1008
  function sleep(ms) {
1019
- return new Promise((resolve3) => setTimeout(resolve3, ms));
1009
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
1020
1010
  }
1021
1011
 
1022
1012
  // src/ziniao-doctor.ts
@@ -1230,114 +1220,6 @@ function registerChromeCommands(program2) {
1230
1220
  });
1231
1221
  }
1232
1222
 
1233
- // src/commands/recipes.ts
1234
- import { writeFileSync as writeFileSync4 } from "fs";
1235
- import { resolve as resolve2 } from "path";
1236
- var PUBLISH_TIMEOUT_MS = 18e5;
1237
- var AD_REPORT_TIMEOUT_MS = 132e4;
1238
- var PUBLISH_TIMEOUT_HINT = "\u4EFB\u52A1\u4ECD\u5728\u540E\u7AEF\u8FD0\u884C,\u89C6\u9891\u53EF\u80FD\u5DF2\u7ECF\u53D1\u51FA\u53BB\u4E86\u3002\u53D1\u5E03\u72B6\u6001\u672A\u77E5 \u2014\u2014 \u8BF7\u5230\u5356\u5BB6\u540E\u53F0\u786E\u8BA4,\u4E0D\u8981\u91CD\u8DD1\u672C\u547D\u4EE4(\u4F1A\u53D1\u51FA\u91CD\u590D\u89C6\u9891)\u3002";
1239
- async function runExport(script, params, out, timeoutMs) {
1240
- const outPath = resolve2(out);
1241
- const result = await api.runRecipeScript(script, { ...params, out_path: outPath }, 2e3, timeoutMs);
1242
- if (!result.path) {
1243
- if (!result.artifact_path) {
1244
- throw new Error(
1245
- `Script ${script} finished but delivered no file \u2014 no workspace path and no staged artifact. Nothing was written to ${outPath}.`
1246
- );
1247
- }
1248
- writeFileSync4(outPath, await api.fetchArtifact(result.artifact_path));
1249
- }
1250
- console.log(`Saved ${result.filename} -> ${outPath} (${result.size} bytes)`);
1251
- }
1252
- function registerRecipeCommands(program2) {
1253
- program2.command("export-ad-report").description("Export TikTok Shop ad data (per-creative by default) into the session workspace").requiredOption("--ziniao-profile <id>", "ZiNiao browser profile ID").option("--report-url <url>", "Ad dashboard URL (default: TikTok Shop MY local site)").option("--days <n>", "Date range: 7 / 30 / 90 / 180 / 365 (default: 7)").option(
1254
- "--scope <scope>",
1255
- "creative = per-creative rows (default) | daily = account-level daily totals"
1256
- ).option("--out <path>", "Output CSV path", "./tt_ad_report.csv").action(
1257
- async (opts) => {
1258
- try {
1259
- const params = { ziniao_profile_id: opts.ziniaoProfile };
1260
- if (opts.reportUrl) params.report_url = opts.reportUrl;
1261
- if (opts.days) params.days = opts.days;
1262
- if (opts.scope) params.scope = opts.scope;
1263
- await runExport("tiktok_video_ad_export", params, opts.out, AD_REPORT_TIMEOUT_MS);
1264
- } catch (err) {
1265
- console.error(formatError(err));
1266
- process.exit(1);
1267
- }
1268
- }
1269
- );
1270
- program2.command("export-order-report").description("Export TikTok Shop order data into the session workspace").requiredOption("--ziniao-profile <id>", "ZiNiao browser profile ID").option("--report-url <url>", "Order list URL (default: TikTok Shop MY local site)").option("--days <n>", "Export the last N days (default: 7, max 365)").option("--out <path>", "Output CSV path", "./tt_orders.csv").action(async (opts) => {
1271
- try {
1272
- const params = { ziniao_profile_id: opts.ziniaoProfile };
1273
- if (opts.reportUrl) params.report_url = opts.reportUrl;
1274
- if (opts.days) params.days = opts.days;
1275
- await runExport("tiktok_order_export", params, opts.out, 54e4);
1276
- } catch (err) {
1277
- console.error(formatError(err));
1278
- process.exit(1);
1279
- }
1280
- });
1281
- program2.command("publish-video-global").description("Publish one shoppable video to a TikTok Shop CROSS-BORDER store (one region per run)").requiredOption("--ziniao-profile <id>", "ZiNiao browser profile ID").requiredOption("--video <name>", "Video file name (from list-videos)").requiredOption("--account <username>", "Publishing account username; it also picks the region (one region per run)").requiredOption("--product-query <keyword>", "Product search keyword or product ID, narrow enough to hit exactly one product (ask the user)").requiredOption(
1282
- "--product-name <text>",
1283
- "Display name on the video: max 30 chars, letters/digits/spaces only (ask the user)"
1284
- ).requiredOption("--caption <text>", "Video caption").option("--dry-run", "Fill everything, run the pre-publish checks, then STOP before clicking publish (nothing is published; the browser window stays open for you to inspect)").option("--hashtags <text>", "Comma-separated hashtags, bare words (the page adds the # itself)").option("--publish-url <url>", "Video publish page URL (include the right ?shop_region=)").action(async (opts) => {
1285
- try {
1286
- const params = {
1287
- ziniao_profile_id: opts.ziniaoProfile,
1288
- video: opts.video,
1289
- account: opts.account,
1290
- product_query: opts.productQuery,
1291
- product_name: opts.productName,
1292
- caption: opts.caption
1293
- };
1294
- if (opts.dryRun) params.dry_run = "1";
1295
- if (opts.hashtags) params.hashtags = opts.hashtags;
1296
- if (opts.publishUrl) params.publish_url = opts.publishUrl;
1297
- const result = await api.runRecipeScript(
1298
- "tiktok_video_publish_global",
1299
- params,
1300
- 2e3,
1301
- PUBLISH_TIMEOUT_MS,
1302
- PUBLISH_TIMEOUT_HINT
1303
- );
1304
- console.log(result.detail);
1305
- } catch (err) {
1306
- console.error(formatError(err));
1307
- process.exit(1);
1308
- }
1309
- });
1310
- program2.command("publish-video").description("Publish one shoppable video to TikTok Shop").requiredOption("--ziniao-profile <id>", "ZiNiao browser profile ID").requiredOption("--video <name>", "Video file name (from list-videos)").requiredOption("--product-query <keyword>", "Product search keyword, narrow enough to hit exactly one product (ask the user)").requiredOption(
1311
- "--product-name <text>",
1312
- "Display name on the video: max 30 chars, letters/digits/spaces only (ask the user)"
1313
- ).requiredOption("--caption <text>", "Video caption").option("--account <username>", "Publishing account username (default: the shop's official account; pass a marketing sub-account to post as it)").option("--dry-run", "Fill everything, run the pre-publish checks, then STOP before clicking publish (nothing is published; the browser window stays open for you to inspect)").option("--hashtags <text>", "Comma-separated hashtags, bare words (the page adds the # itself)").option("--publish-url <url>", "Video publish page URL").action(async (opts) => {
1314
- try {
1315
- const params = {
1316
- ziniao_profile_id: opts.ziniaoProfile,
1317
- video: opts.video,
1318
- product_query: opts.productQuery,
1319
- product_name: opts.productName,
1320
- caption: opts.caption
1321
- };
1322
- if (opts.account) params.account = opts.account;
1323
- if (opts.dryRun) params.dry_run = "1";
1324
- if (opts.hashtags) params.hashtags = opts.hashtags;
1325
- if (opts.publishUrl) params.publish_url = opts.publishUrl;
1326
- const result = await api.runRecipeScript(
1327
- "tiktok_video_publish",
1328
- params,
1329
- 2e3,
1330
- PUBLISH_TIMEOUT_MS,
1331
- PUBLISH_TIMEOUT_HINT
1332
- );
1333
- console.log(result.detail);
1334
- } catch (err) {
1335
- console.error(formatError(err));
1336
- process.exit(1);
1337
- }
1338
- });
1339
- }
1340
-
1341
1223
  // src/commands/bitbrowser.ts
1342
1224
  function registerBitBrowserCommands(program2) {
1343
1225
  const bitbrowser = program2.command("bitbrowser").description("Manage BitBrowser (\u6BD4\u7279\u6D4F\u89C8\u5668) anti-detect browser");
@@ -1454,7 +1336,6 @@ registerTabCommands(program);
1454
1336
  registerScriptCommands(program);
1455
1337
  registerZiNiaoCommands(program);
1456
1338
  registerChromeCommands(program);
1457
- registerRecipeCommands(program);
1458
1339
  registerBitBrowserCommands(program);
1459
1340
  registerFeikuaCommands(program);
1460
1341
  registerFileCommands(program);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optima-chat/browser-cli",
3
- "version": "0.15.4",
3
+ "version": "0.17.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "bin": {