@optima-chat/browser-cli 0.15.0 → 0.15.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 +75 -13
  2. 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 { basename, dirname, resolve } from "path";
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 target = resolve(out ?? `./${basename(output.filename ?? "artifact")}`);
787
- const content = await api.fetchArtifact(output.artifact_path);
802
+ const name = basename(output.filename ?? "") || "artifact";
803
+ const target = resolveArtifactTarget(out, name);
788
804
  mkdirSync2(dirname(target), { recursive: true });
789
- writeFileSync3(target, content);
790
- return target;
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 !!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") {
@@ -918,15 +960,21 @@ function registerScriptCommands(program2) {
918
960
  saveSessionId(result.session_id);
919
961
  }
920
962
  console.log(formatScriptTask(result));
963
+ if (result.status !== "completed" && hasStagedArtifact(result)) {
964
+ console.log(downloadHint(result));
965
+ }
921
966
  if (result.status === "completed") {
922
967
  try {
923
968
  const saved = await deliverArtifact(result, opts.out);
924
- if (saved) console.log(`Saved -> ${saved}`);
969
+ if (saved) console.log(`Saved -> ${saved.path}`);
925
970
  } catch (err) {
926
971
  const staged = result.output?.artifact_path;
927
972
  console.error(`${formatError(err)}
928
973
  Script completed; only saving the file failed.`);
929
- if (staged) console.error(`The artifact is still at ${staged}.`);
974
+ if (staged && !(err instanceof ArtifactLostError)) {
975
+ console.error(`The artifact is still at ${artifactUrl(staged)}.
976
+ ${downloadHint(result)}`);
977
+ }
930
978
  process.exit(1);
931
979
  }
932
980
  }
@@ -935,12 +983,26 @@ Script completed; only saving the file failed.`);
935
983
  process.exit(1);
936
984
  }
937
985
  });
938
- program2.command("script-task").argument("<task-id>", "Task ID").description("Check script task status").action(async (taskId) => {
986
+ 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
987
  try {
940
988
  const task = await api.getScriptTask(taskId);
941
- console.log(formatScriptTask(task));
989
+ if (opts.download === void 0) {
990
+ console.log(formatScriptTask(task));
991
+ if (hasStagedArtifact(task)) console.log(downloadHint(task));
992
+ return;
993
+ }
994
+ if (task.status === "running") {
995
+ throw new Error(`Task ${taskId} is ${task.status} \u2014 nothing to download yet`);
996
+ }
997
+ const saved = await deliverArtifact(task, typeof opts.download === "string" ? opts.download : void 0);
998
+ if (!saved) throw new Error(`Task ${taskId} produced no file`);
999
+ console.log(`Saved -> ${saved.path}${saved.size === void 0 ? "" : ` (${saved.size} bytes)`}`);
942
1000
  } catch (err) {
943
- console.error(formatError(err));
1001
+ const gone = err.errorCode === "artifact_gone";
1002
+ console.error(
1003
+ gone ? `${formatError(err)}
1004
+ Each artifact can be fetched once; if runscript printed "Saved ->", the file is already local.` : formatError(err)
1005
+ );
944
1006
  process.exit(1);
945
1007
  }
946
1008
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optima-chat/browser-cli",
3
- "version": "0.15.0",
3
+ "version": "0.15.2",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "bin": {