@optima-chat/browser-cli 0.18.1 → 0.20.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/index.js +66 -15
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -785,6 +785,34 @@ import { tmpdir } from "os";
|
|
|
785
785
|
import { basename, dirname, isAbsolute, join as join3, relative, resolve } from "path";
|
|
786
786
|
var POLL_INTERVAL_MS = 2e3;
|
|
787
787
|
var POLL_TIMEOUT_MS = 18e4;
|
|
788
|
+
var WAIT_DEFAULT_TIMEOUT_S = 90;
|
|
789
|
+
var WAIT_MAX_TIMEOUT_S = 570;
|
|
790
|
+
var EXIT_TASK_STOPPED = 2;
|
|
791
|
+
var EXIT_STILL_RUNNING = 3;
|
|
792
|
+
async function waitForScriptTask(taskId, timeoutMs, first) {
|
|
793
|
+
const deadline = Date.now() + timeoutMs;
|
|
794
|
+
let task = first ?? await api.getScriptTask(taskId);
|
|
795
|
+
while (task.status === "running") {
|
|
796
|
+
const left = deadline - Date.now();
|
|
797
|
+
if (left <= 0) return { task, timedOut: true };
|
|
798
|
+
await sleep(Math.min(POLL_INTERVAL_MS, left));
|
|
799
|
+
task = await api.getScriptTask(taskId);
|
|
800
|
+
}
|
|
801
|
+
return { task, timedOut: false };
|
|
802
|
+
}
|
|
803
|
+
function parseWaitTimeout(raw) {
|
|
804
|
+
const n = Number(raw);
|
|
805
|
+
if (!/^\d+$/.test(raw.trim()) || !Number.isInteger(n) || n < 1 || n > WAIT_MAX_TIMEOUT_S) {
|
|
806
|
+
throw new Error(`--timeout \u987B\u4E3A 1\u2013${WAIT_MAX_TIMEOUT_S} \u7684\u6574\u6570\u79D2(bash \u786C\u4E0A\u9650 600s),\u6536\u5230 ${JSON.stringify(raw)}`);
|
|
807
|
+
}
|
|
808
|
+
return n;
|
|
809
|
+
}
|
|
810
|
+
function stillRunningMessage(task, waitedS, nowMs = Date.now()) {
|
|
811
|
+
const started = task.created_at ? Date.parse(task.created_at) : Number.NaN;
|
|
812
|
+
const elapsed = Number.isFinite(started) ? `running for ${Math.max(0, Math.round((nowMs - started) / 1e3))}s` : `waited ${waitedS}s`;
|
|
813
|
+
const step = task.total_steps ? `, step ${task.current_step}/${task.total_steps}` : "";
|
|
814
|
+
return `Still running (${elapsed}${step}). Call again as a separate command: browser-cli script-task ${task.id} --wait`;
|
|
815
|
+
}
|
|
788
816
|
var REQUIRED_ARG_FLAG = {
|
|
789
817
|
ziniao_profile_id: "--ziniao-profile <id>",
|
|
790
818
|
bitbrowser_profile_id: "--bitbrowser-profile <id>",
|
|
@@ -1018,18 +1046,13 @@ function registerScriptCommands(program2) {
|
|
|
1018
1046
|
const task = await api.runScript(name, params, opts.connector, opts.ziniaoProfile, opts.bitbrowserProfile, opts.feikuaProfile, opts.chromeProfile);
|
|
1019
1047
|
console.log(`Script started (task: ${task.id})`);
|
|
1020
1048
|
if (!opts.poll) {
|
|
1021
|
-
console.log(`
|
|
1049
|
+
console.log(`Wait with: browser-cli script-task ${task.id} --wait`);
|
|
1022
1050
|
return;
|
|
1023
1051
|
}
|
|
1024
|
-
const
|
|
1025
|
-
|
|
1026
|
-
while (result.status === "running" && Date.now() < deadline) {
|
|
1027
|
-
await sleep(POLL_INTERVAL_MS);
|
|
1028
|
-
result = await api.getScriptTask(task.id);
|
|
1029
|
-
}
|
|
1030
|
-
if (result.status === "running") {
|
|
1052
|
+
const { task: result, timedOut } = await waitForScriptTask(task.id, POLL_TIMEOUT_MS, task);
|
|
1053
|
+
if (timedOut) {
|
|
1031
1054
|
console.log(`Polling timed out. Task ${task.id} still running.`);
|
|
1032
|
-
console.log(`
|
|
1055
|
+
console.log(`Wait with: browser-cli script-task ${task.id} --wait`);
|
|
1033
1056
|
return;
|
|
1034
1057
|
}
|
|
1035
1058
|
if (result.status === "needs_fallback" && result.session_id) {
|
|
@@ -1059,20 +1082,42 @@ ${downloadHint(result)}`);
|
|
|
1059
1082
|
process.exit(1);
|
|
1060
1083
|
}
|
|
1061
1084
|
});
|
|
1062
|
-
program2.command("script-task").argument("<task-id>", "Task ID").description("Check script task status").option("--download [path]", "Save the task's exported file (default: ./<filename>)").option("--json", "Print the raw task (status / error / full output) as JSON").
|
|
1085
|
+
program2.command("script-task").argument("<task-id>", "Task ID").description("Check script task status").option("--download [path]", "Save the task's exported file (default: ./<filename>)").option("--json", "Print the raw task (status / error / full output) as JSON").option(
|
|
1086
|
+
"--wait",
|
|
1087
|
+
`Block until the task finishes (completed / failed / needs_fallback), at most --timeout seconds. Exit code: 0 completed, 2 failed / needs_fallback, 3 still running (call again), 1 error`
|
|
1088
|
+
).option("--timeout <seconds>", `With --wait: max seconds to wait (default ${WAIT_DEFAULT_TIMEOUT_S}, max ${WAIT_MAX_TIMEOUT_S})`).action(async (taskId, opts) => {
|
|
1063
1089
|
try {
|
|
1064
1090
|
if (opts.json && opts.download !== void 0) {
|
|
1065
1091
|
throw new Error("--json \u4E0E --download \u53EA\u80FD\u7ED9\u4E00\u4E2A");
|
|
1066
1092
|
}
|
|
1067
|
-
|
|
1093
|
+
if (opts.timeout !== void 0 && !opts.wait) throw new Error("--timeout \u53EA\u80FD\u548C --wait \u4E00\u8D77\u7528");
|
|
1094
|
+
const timeoutS = opts.timeout === void 0 ? WAIT_DEFAULT_TIMEOUT_S : parseWaitTimeout(opts.timeout);
|
|
1095
|
+
let task;
|
|
1096
|
+
if (opts.wait) {
|
|
1097
|
+
const waited = await waitForScriptTask(taskId, timeoutS * 1e3);
|
|
1098
|
+
task = waited.task;
|
|
1099
|
+
if (waited.timedOut) {
|
|
1100
|
+
if (opts.json) console.log(JSON.stringify(task, null, 2));
|
|
1101
|
+
(opts.json ? console.error : console.log)(stillRunningMessage(task, timeoutS));
|
|
1102
|
+
process.exit(EXIT_STILL_RUNNING);
|
|
1103
|
+
return;
|
|
1104
|
+
}
|
|
1105
|
+
} else {
|
|
1106
|
+
task = await api.getScriptTask(taskId);
|
|
1107
|
+
}
|
|
1108
|
+
const stoppedExit = () => {
|
|
1109
|
+
if (opts.wait && task.status !== "completed") process.exit(EXIT_TASK_STOPPED);
|
|
1110
|
+
};
|
|
1068
1111
|
if (task.status === "needs_fallback" && task.session_id) saveSessionId(task.session_id);
|
|
1069
1112
|
if (opts.json) {
|
|
1070
1113
|
console.log(JSON.stringify(task, null, 2));
|
|
1114
|
+
stoppedExit();
|
|
1071
1115
|
return;
|
|
1072
1116
|
}
|
|
1073
1117
|
if (opts.download === void 0) {
|
|
1074
1118
|
console.log(formatScriptTask(task));
|
|
1075
1119
|
if (hasStagedArtifact(task)) console.log(downloadHint(task));
|
|
1120
|
+
stoppedExit();
|
|
1076
1121
|
return;
|
|
1077
1122
|
}
|
|
1078
1123
|
if (task.status === "running") {
|
|
@@ -1081,6 +1126,7 @@ ${downloadHint(result)}`);
|
|
|
1081
1126
|
const saved = await deliverArtifact(task, typeof opts.download === "string" ? opts.download : void 0);
|
|
1082
1127
|
if (!saved) throw new Error(`Task ${taskId} produced no file`);
|
|
1083
1128
|
console.log(`Saved -> ${saved.path}${saved.size === void 0 ? "" : ` (${saved.size} bytes)`}`);
|
|
1129
|
+
stoppedExit();
|
|
1084
1130
|
} catch (err) {
|
|
1085
1131
|
const gone = err.errorCode === "artifact_gone";
|
|
1086
1132
|
console.error(
|
|
@@ -1375,9 +1421,9 @@ function registerFeikuaCommands(program2) {
|
|
|
1375
1421
|
// src/commands/files.ts
|
|
1376
1422
|
function formatVideoList(videos) {
|
|
1377
1423
|
if (videos.length === 0) {
|
|
1378
|
-
return `No videos found.
|
|
1379
|
-
macOS/Linux: ~/Downloads/OptimaConnect/videos
|
|
1380
|
-
Windows: C:\\Users\\<\u7528\u6237\u540D>\\Downloads\\OptimaConnect\\videos
|
|
1424
|
+
return `No videos found. Videos are listed from your OptimaConnect folder:
|
|
1425
|
+
macOS/Linux: ~/Downloads/OptimaConnect/<\u5E73\u53F0>/<\u8D27\u54C1>/\u89C6\u9891/ (older files: ~/Downloads/OptimaConnect/videos)
|
|
1426
|
+
Windows: C:\\Users\\<\u7528\u6237\u540D>\\Downloads\\OptimaConnect\\<\u5E73\u53F0>\\<\u8D27\u54C1>\\\u89C6\u9891\\ (older files: ...\\OptimaConnect\\videos)
|
|
1381
1427
|
Then try again.`;
|
|
1382
1428
|
}
|
|
1383
1429
|
return videos.map((v) => {
|
|
@@ -1386,17 +1432,22 @@ Then try again.`;
|
|
|
1386
1432
|
return `${v.name} \u2014 ${mb} MB, ${time}`;
|
|
1387
1433
|
}).join("\n");
|
|
1388
1434
|
}
|
|
1435
|
+
function mediaRefLine(result) {
|
|
1436
|
+
return result.media_ref ? `media_ref: ${result.media_ref} (\u4E0A\u4F20\u53C2\u6570\u586B\u8FD9\u4E2A)` : null;
|
|
1437
|
+
}
|
|
1389
1438
|
function registerFileCommands(program2) {
|
|
1390
1439
|
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) => {
|
|
1391
1440
|
try {
|
|
1392
1441
|
const result = await api.sendFile(opts.src, opts.dest);
|
|
1393
1442
|
console.log(result.local_path);
|
|
1443
|
+
const hint = mediaRefLine(result);
|
|
1444
|
+
if (hint) console.error(hint);
|
|
1394
1445
|
} catch (err) {
|
|
1395
1446
|
console.error(formatError(err));
|
|
1396
1447
|
process.exit(1);
|
|
1397
1448
|
}
|
|
1398
1449
|
});
|
|
1399
|
-
program2.command("list-videos").description("List videos in your local
|
|
1450
|
+
program2.command("list-videos").description("List videos in your local OptimaConnect folder (requires Desktop app online)").action(async () => {
|
|
1400
1451
|
try {
|
|
1401
1452
|
const { videos } = await api.listVideos();
|
|
1402
1453
|
console.log(formatVideoList(videos));
|