@optima-chat/browser-cli 0.15.0 → 0.15.1
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 +72 -13
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -436,9 +436,13 @@ function clearSessionId(onlyIfEquals) {
|
|
|
436
436
|
}
|
|
437
437
|
|
|
438
438
|
// src/formatter.ts
|
|
439
|
+
var URL_STALE_WARNING = "\u26A0 URL/title may be stale: the live page probe failed, values above are cached. Verify with a screenshot before concluding a navigation failed.";
|
|
439
440
|
function formatPageState(state) {
|
|
440
441
|
const lines = [];
|
|
441
442
|
lines.push(`Page: ${state.title} (${state.url})`);
|
|
443
|
+
if (state.url_stale) {
|
|
444
|
+
lines.push(URL_STALE_WARNING);
|
|
445
|
+
}
|
|
442
446
|
lines.push("");
|
|
443
447
|
lines.push("DOM:");
|
|
444
448
|
lines.push(state.dom);
|
|
@@ -478,6 +482,9 @@ function formatTabs(tabList) {
|
|
|
478
482
|
for (const tab of tabList.tabs) {
|
|
479
483
|
lines.push(` [${tab.tab_id}] ${tab.title} (${tab.url})`);
|
|
480
484
|
}
|
|
485
|
+
if (tabList.url_stale) {
|
|
486
|
+
lines.push(URL_STALE_WARNING);
|
|
487
|
+
}
|
|
481
488
|
return lines.join("\n");
|
|
482
489
|
}
|
|
483
490
|
function formatSearch(result) {
|
|
@@ -764,8 +771,9 @@ function registerTabCommands(program2) {
|
|
|
764
771
|
}
|
|
765
772
|
|
|
766
773
|
// src/commands/script.ts
|
|
767
|
-
import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
768
|
-
import {
|
|
774
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, statSync, writeFileSync as writeFileSync3 } from "fs";
|
|
775
|
+
import { tmpdir } from "os";
|
|
776
|
+
import { basename, dirname, join as join3, resolve } from "path";
|
|
769
777
|
var POLL_INTERVAL_MS = 2e3;
|
|
770
778
|
var POLL_TIMEOUT_MS = 18e4;
|
|
771
779
|
var REQUIRED_ARG_FLAG = {
|
|
@@ -778,16 +786,50 @@ function formatRequiredArgs(s) {
|
|
|
778
786
|
const flags = s.required_args.map((a) => REQUIRED_ARG_FLAG[a] ?? `--param ${a}=<value>`).join(" ");
|
|
779
787
|
return ` requires: ${flags}`;
|
|
780
788
|
}
|
|
789
|
+
var ArtifactLostError = class extends Error {
|
|
790
|
+
};
|
|
791
|
+
var errMsg = (err) => err instanceof Error ? err.message : String(err);
|
|
792
|
+
function resolveArtifactTarget(out, name) {
|
|
793
|
+
if (!out) return resolve(`./${name}`);
|
|
794
|
+
const wantsDir = /[\\/]$/.test(out) || existsSync3(out) && statSync(out).isDirectory();
|
|
795
|
+
return wantsDir ? resolve(out, name) : resolve(out);
|
|
796
|
+
}
|
|
781
797
|
async function deliverArtifact(task, out) {
|
|
782
798
|
const output = task.output;
|
|
783
799
|
if (!output) return null;
|
|
784
|
-
if (output.path) return output.path;
|
|
800
|
+
if (output.path) return { path: output.path, size: output.size };
|
|
785
801
|
if (!output.artifact_path) return null;
|
|
786
|
-
const
|
|
787
|
-
const
|
|
802
|
+
const name = basename(output.filename ?? "") || "artifact";
|
|
803
|
+
const target = resolveArtifactTarget(out, name);
|
|
788
804
|
mkdirSync2(dirname(target), { recursive: true });
|
|
789
|
-
|
|
790
|
-
|
|
805
|
+
const content = await api.fetchArtifact(output.artifact_path);
|
|
806
|
+
try {
|
|
807
|
+
writeFileSync3(target, content);
|
|
808
|
+
} catch (err) {
|
|
809
|
+
const fallback = join3(tmpdir(), name);
|
|
810
|
+
try {
|
|
811
|
+
writeFileSync3(fallback, content);
|
|
812
|
+
} catch (err2) {
|
|
813
|
+
throw new ArtifactLostError(
|
|
814
|
+
`Could not write ${target} (${errMsg(err)}) nor ${fallback} (${errMsg(err2)}); the artifact was already fetched and is no longer on the server`
|
|
815
|
+
);
|
|
816
|
+
}
|
|
817
|
+
console.error(`Could not write ${target} (${errMsg(err)}); saved to ${fallback} instead`);
|
|
818
|
+
return { path: fallback, size: content.length };
|
|
819
|
+
}
|
|
820
|
+
return { path: target, size: content.length };
|
|
821
|
+
}
|
|
822
|
+
function artifactUrl(artifactPath) {
|
|
823
|
+
return `${BROWSER_API_URL}${artifactPath}`;
|
|
824
|
+
}
|
|
825
|
+
function hasStagedArtifact(task) {
|
|
826
|
+
const output = task.output;
|
|
827
|
+
return task.status === "completed" && !!output?.artifact_path;
|
|
828
|
+
}
|
|
829
|
+
function downloadHint(task) {
|
|
830
|
+
const filename = task.output?.filename;
|
|
831
|
+
const dest = filename ? ` ./${basename(filename)}` : "";
|
|
832
|
+
return `Download with: browser-cli script-task ${task.id} --download${dest}`;
|
|
791
833
|
}
|
|
792
834
|
function formatScriptList(scripts) {
|
|
793
835
|
if (scripts.length === 0) return "No scripts registered.";
|
|
@@ -810,7 +852,7 @@ function formatScriptTask(task) {
|
|
|
810
852
|
lines.push("\u2713 Script completed");
|
|
811
853
|
if (task.output) {
|
|
812
854
|
for (const [k, v] of Object.entries(task.output)) {
|
|
813
|
-
lines.push(`${k}: ${v}`);
|
|
855
|
+
lines.push(k === "artifact_path" ? `${k}: ${artifactUrl(String(v))}` : `${k}: ${v}`);
|
|
814
856
|
}
|
|
815
857
|
}
|
|
816
858
|
} else if (task.status === "needs_fallback") {
|
|
@@ -921,12 +963,15 @@ function registerScriptCommands(program2) {
|
|
|
921
963
|
if (result.status === "completed") {
|
|
922
964
|
try {
|
|
923
965
|
const saved = await deliverArtifact(result, opts.out);
|
|
924
|
-
if (saved) console.log(`Saved -> ${saved}`);
|
|
966
|
+
if (saved) console.log(`Saved -> ${saved.path}`);
|
|
925
967
|
} catch (err) {
|
|
926
968
|
const staged = result.output?.artifact_path;
|
|
927
969
|
console.error(`${formatError(err)}
|
|
928
970
|
Script completed; only saving the file failed.`);
|
|
929
|
-
if (staged
|
|
971
|
+
if (staged && !(err instanceof ArtifactLostError)) {
|
|
972
|
+
console.error(`The artifact is still at ${artifactUrl(staged)}.
|
|
973
|
+
${downloadHint(result)}`);
|
|
974
|
+
}
|
|
930
975
|
process.exit(1);
|
|
931
976
|
}
|
|
932
977
|
}
|
|
@@ -935,12 +980,26 @@ Script completed; only saving the file failed.`);
|
|
|
935
980
|
process.exit(1);
|
|
936
981
|
}
|
|
937
982
|
});
|
|
938
|
-
program2.command("script-task").argument("<task-id>", "Task ID").description("Check script task status").action(async (taskId) => {
|
|
983
|
+
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>)").action(async (taskId, opts) => {
|
|
939
984
|
try {
|
|
940
985
|
const task = await api.getScriptTask(taskId);
|
|
941
|
-
|
|
986
|
+
if (opts.download === void 0) {
|
|
987
|
+
console.log(formatScriptTask(task));
|
|
988
|
+
if (hasStagedArtifact(task)) console.log(downloadHint(task));
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
if (task.status !== "completed") {
|
|
992
|
+
throw new Error(`Task ${taskId} is ${task.status} \u2014 nothing to download yet`);
|
|
993
|
+
}
|
|
994
|
+
const saved = await deliverArtifact(task, typeof opts.download === "string" ? opts.download : void 0);
|
|
995
|
+
if (!saved) throw new Error(`Task ${taskId} produced no file`);
|
|
996
|
+
console.log(`Saved -> ${saved.path}${saved.size === void 0 ? "" : ` (${saved.size} bytes)`}`);
|
|
942
997
|
} catch (err) {
|
|
943
|
-
|
|
998
|
+
const gone = err.errorCode === "artifact_gone";
|
|
999
|
+
console.error(
|
|
1000
|
+
gone ? `${formatError(err)}
|
|
1001
|
+
Each artifact can be fetched once; if runscript printed "Saved ->", the file is already local.` : formatError(err)
|
|
1002
|
+
);
|
|
944
1003
|
process.exit(1);
|
|
945
1004
|
}
|
|
946
1005
|
});
|