opensteer 0.9.4 → 0.9.6

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 (33) hide show
  1. package/dist/{chunk-ZRF7WMS3.js → chunk-3I3A5OLB.js} +3 -3
  2. package/dist/{chunk-ZRF7WMS3.js.map → chunk-3I3A5OLB.js.map} +1 -1
  3. package/dist/{chunk-GSCQQKZZ.js → chunk-3XBQRZZC.js} +221 -6
  4. package/dist/chunk-3XBQRZZC.js.map +1 -0
  5. package/dist/{chunk-GEUHKPC2.js → chunk-BVRIPCWA.js} +878 -572
  6. package/dist/chunk-BVRIPCWA.js.map +1 -0
  7. package/dist/chunk-L4NF74KI.js +458 -0
  8. package/dist/chunk-L4NF74KI.js.map +1 -0
  9. package/dist/cli/bin.cjs +1313 -494
  10. package/dist/cli/bin.cjs.map +1 -1
  11. package/dist/cli/bin.js +235 -108
  12. package/dist/cli/bin.js.map +1 -1
  13. package/dist/index.cjs +1152 -647
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.cts +37 -460
  16. package/dist/index.d.ts +37 -460
  17. package/dist/index.js +3 -4
  18. package/dist/local-view/serve-entry.cjs +5354 -4
  19. package/dist/local-view/serve-entry.cjs.map +1 -1
  20. package/dist/local-view/serve-entry.js +1 -1
  21. package/dist/opensteer-UGA6YBRN.js +6 -0
  22. package/dist/{opensteer-PJI7VUIT.js.map → opensteer-UGA6YBRN.js.map} +1 -1
  23. package/dist/{session-control-M3JD7ZKA.js → session-control-U3L5H2ZI.js} +3 -3
  24. package/dist/{session-control-M3JD7ZKA.js.map → session-control-U3L5H2ZI.js.map} +1 -1
  25. package/package.json +7 -7
  26. package/skills/opensteer/SKILL.md +134 -94
  27. package/dist/chunk-GEUHKPC2.js.map +0 -1
  28. package/dist/chunk-GSCQQKZZ.js.map +0 -1
  29. package/dist/chunk-HQCMXRBE.js +0 -335
  30. package/dist/chunk-HQCMXRBE.js.map +0 -1
  31. package/dist/chunk-KCINASQC.js +0 -3
  32. package/dist/chunk-KCINASQC.js.map +0 -1
  33. package/dist/opensteer-PJI7VUIT.js +0 -6
package/dist/cli/bin.js CHANGED
@@ -1,8 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import '../chunk-KCINASQC.js';
3
- import { runLocalViewService } from '../chunk-ZRF7WMS3.js';
4
- import { createOpensteerSemanticRuntime, dispatchSemanticOperation, loadEnvironment, normalizeOpensteerProviderMode, resolveOpensteerRuntimeConfig, assertProviderSupportsEngine, resolveOpensteerProvider, requireCloudAppBaseUrl, CloudSessionProxy, FlowRecorderCollector, generateReplayScript, OpensteerCloudClient } from '../chunk-GEUHKPC2.js';
5
- import { OpensteerBrowserManager, stopLocalViewService, setLocalViewMode, ensureLocalViewServiceRunning, buildLocalViewSessionUrl, resolveOpensteerEngineName, resolveFilesystemWorkspacePath } from '../chunk-GSCQQKZZ.js';
2
+ import { runLocalViewService } from '../chunk-3I3A5OLB.js';
3
+ import { createOpensteerSemanticRuntime, dispatchSemanticOperation, isBrowserCoreError, loadEnvironment, normalizeOpensteerProviderMode, resolveOpensteerRuntimeConfig, assertProviderSupportsEngine, resolveOpensteerProvider, requireCloudAppBaseUrl, CloudSessionProxy, FlowRecorderCollector, generateReplayScript, OpensteerCloudClient } from '../chunk-BVRIPCWA.js';
4
+ import { isOpensteerProtocolError, OpensteerBrowserManager, stopLocalViewService, setLocalViewMode, ensureLocalViewServiceRunning, buildLocalViewSessionUrl, resolveOpensteerEngineName, resolveFilesystemWorkspacePath } from '../chunk-3XBQRZZC.js';
6
5
  import { discoverLocalCdpBrowsers, inspectCdpEndpoint, readPersistedLocalBrowserSessionRecord, isProcessRunning, buildLocalViewSessionId, pathExists, readPersistedCloudSessionRecord } from '../chunk-T5P2QGZ3.js';
7
6
  import process4 from 'process';
8
7
  import { mkdir, writeFile } from 'fs/promises';
@@ -15,13 +14,115 @@ import { fileURLToPath } from 'url';
15
14
 
16
15
  // package.json
17
16
  var package_default = {
18
- version: "0.9.4"};
17
+ version: "0.9.6"};
19
18
 
20
19
  // src/cli/env-loader.ts
21
20
  async function loadCliEnvironment(cwd) {
22
21
  loadEnvironment(cwd);
23
22
  }
24
23
 
24
+ // src/cli/errors.ts
25
+ var CliError = class extends Error {
26
+ type;
27
+ usage;
28
+ constructor(type, message, usage) {
29
+ super(message);
30
+ this.name = "CliError";
31
+ this.type = type;
32
+ this.usage = usage;
33
+ }
34
+ format() {
35
+ return this.usage === void 0 ? this.message : `${this.message}
36
+ Usage: ${this.usage}`;
37
+ }
38
+ };
39
+ function isCliError(value) {
40
+ return value instanceof CliError;
41
+ }
42
+ function formatCliErrorOutput(error) {
43
+ if (isCliError(error)) {
44
+ return {
45
+ success: false,
46
+ error: error.format(),
47
+ type: error.type
48
+ };
49
+ }
50
+ if (isOpensteerProtocolError(error)) {
51
+ return {
52
+ success: false,
53
+ error: error.message,
54
+ code: error.code,
55
+ retriable: error.retriable
56
+ };
57
+ }
58
+ if (isBrowserCoreError(error)) {
59
+ return {
60
+ success: false,
61
+ error: error.message,
62
+ code: error.code,
63
+ retriable: error.retriable
64
+ };
65
+ }
66
+ if (error instanceof Error && "opensteerError" in error) {
67
+ const oe = error.opensteerError;
68
+ return {
69
+ success: false,
70
+ error: oe.message,
71
+ code: oe.code,
72
+ retriable: oe.retriable
73
+ };
74
+ }
75
+ if (error instanceof SyntaxError) {
76
+ return {
77
+ success: false,
78
+ error: error.message,
79
+ type: "invalid_value"
80
+ };
81
+ }
82
+ return {
83
+ success: false,
84
+ error: error instanceof Error ? error.message : String(error)
85
+ };
86
+ }
87
+ function emitWarning(warning) {
88
+ process.stderr.write(`${JSON.stringify({ warning })}
89
+ `);
90
+ }
91
+ var CLI_USAGE_HINTS = {
92
+ "session.open": "opensteer open <url> [--workspace <id>]",
93
+ "page.goto": "opensteer goto <url>",
94
+ "page.evaluate": "opensteer evaluate <script>",
95
+ "page.add-init-script": "opensteer init-script <script>",
96
+ "dom.click": "opensteer click <element> --persist <key>",
97
+ "dom.hover": "opensteer hover <element> --persist <key>",
98
+ "dom.input": "opensteer input <element> <text> --persist <key>",
99
+ "dom.scroll": "opensteer scroll <direction> <amount> --persist <key>",
100
+ "dom.extract": "opensteer extract <template> --persist <key>",
101
+ "network.detail": "opensteer network detail <recordId>",
102
+ "session.fetch": "opensteer fetch <url>",
103
+ "captcha.solve": "opensteer captcha solve --provider <name> --api-key <key>",
104
+ "scripts.capture": "opensteer scripts capture",
105
+ "scripts.beautify": "opensteer scripts beautify <artifactId>",
106
+ "scripts.deobfuscate": "opensteer scripts deobfuscate <artifactId>",
107
+ "scripts.sandbox": "opensteer scripts sandbox <artifactId>",
108
+ "interaction.capture": "opensteer interaction capture",
109
+ "interaction.get": "opensteer interaction get <traceId>",
110
+ "interaction.replay": "opensteer interaction replay <traceId>",
111
+ "interaction.diff": "opensteer interaction diff <leftTraceId> <rightTraceId>",
112
+ "artifact.read": "opensteer artifact read <artifactId>",
113
+ "computer.click": "opensteer computer click <x> <y>",
114
+ "computer.type": "opensteer computer type <text>",
115
+ "computer.key": "opensteer computer key <key>",
116
+ "computer.scroll": "opensteer computer scroll <x> <y> --dx <n> --dy <n>",
117
+ "computer.move": "opensteer computer move <x> <y>",
118
+ "computer.drag": "opensteer computer drag <x1> <y1> <x2> <y2>",
119
+ "computer.screenshot": "opensteer computer screenshot",
120
+ exec: "opensteer exec <expression>",
121
+ record: "opensteer record --url <url> --workspace <id>",
122
+ "browser.inspect": "opensteer browser inspect <endpoint>",
123
+ "browser.clone": "opensteer browser clone --source-user-data-dir <path>"
124
+ };
125
+
25
126
  // src/cli/help.ts
26
127
  function getHelpText() {
27
128
  return `Opensteer CLI
@@ -40,11 +141,11 @@ Navigation:
40
141
 
41
142
  DOM:
42
143
  snapshot [action|extraction]
43
- click <element> [--button left|middle|right] [--persist <key>] [--capture-network <label>]
44
- hover <element> [--persist <key>] [--capture-network <label>]
45
- input <element> <text> [--press-enter] [--persist <key>] [--capture-network <label>]
46
- scroll <direction> <amount> [--element <n>] [--persist <key>] [--capture-network <label>]
47
- extract <schema> [--persist <key>]
144
+ click <element> --persist <key> [--button left|middle|right] [--capture-network <label>]
145
+ hover <element> --persist <key> [--capture-network <label>]
146
+ input <element> <text> --persist <key> [--press-enter] [--capture-network <label>]
147
+ scroll <direction> <amount> --persist <key> [--element <n>] [--capture-network <label>]
148
+ extract <template> --persist <key>
48
149
  evaluate <script>
49
150
  init-script <script>
50
151
 
@@ -280,7 +381,7 @@ function parseCommandLine(argv) {
280
381
  const key = token.slice(2, separator === -1 ? void 0 : separator);
281
382
  const spec = CLI_OPTION_SPECS[key];
282
383
  if (spec === void 0) {
283
- throw new Error(`Unknown option: --${key}.`);
384
+ throw new CliError("unknown_option", `Unknown option: --${key}.`);
284
385
  }
285
386
  if (separator !== -1) {
286
387
  rawOptions.set(key, [...rawOptions.get(key) ?? [], token.slice(separator + 1)]);
@@ -309,7 +410,8 @@ function parseCommandLine(argv) {
309
410
  continue;
310
411
  }
311
412
  if (next === void 0 || next.startsWith("--")) {
312
- throw new Error(
413
+ throw new CliError(
414
+ "missing_arguments",
313
415
  `Option "--${key}" requires a value.${next?.startsWith("--") === true ? ` Use "--${key}=<value>" when the value begins with "--".` : ``}`
314
416
  );
315
417
  }
@@ -355,10 +457,14 @@ function parseCommandLine(argv) {
355
457
  const autoLocalView = readOptionalBoolean(rawOptions, "auto");
356
458
  const noAutoLocalView = readOptionalBoolean(rawOptions, "no-auto");
357
459
  if (autoLocalView === true && noAutoLocalView === true) {
358
- throw new Error('Options "--auto" and "--no-auto" cannot be combined.');
460
+ throw new CliError("invalid_option", 'Options "--auto" and "--no-auto" cannot be combined.');
359
461
  }
360
462
  if (command[0] !== "view" && (autoLocalView !== void 0 || noAutoLocalView !== void 0)) {
361
- throw new Error('Options "--auto" and "--no-auto" are only supported with "view".');
463
+ throw new CliError(
464
+ "invalid_option",
465
+ 'Options "--auto" and "--no-auto" are only supported with "view".',
466
+ "opensteer view --auto"
467
+ );
362
468
  }
363
469
  const global = readOptionalBoolean(rawOptions, "global");
364
470
  const yes = readOptionalBoolean(rawOptions, "yes");
@@ -414,7 +520,7 @@ function parseKeyValueList(values) {
414
520
  values.map((entry) => {
415
521
  const separator = entry.indexOf("=");
416
522
  if (separator <= 0) {
417
- throw new Error(`Expected NAME=VALUE, received "${entry}".`);
523
+ throw new CliError("invalid_value", `Expected NAME=VALUE, received "${entry}".`);
418
524
  }
419
525
  return [entry.slice(0, separator), entry.slice(separator + 1)];
420
526
  })
@@ -445,7 +551,7 @@ function readOptionalBoolean(options, name) {
445
551
  if (value === "false") {
446
552
  return false;
447
553
  }
448
- throw new Error(`Option "--${name}" must be true or false.`);
554
+ throw new CliError("invalid_value", `Option "--${name}" must be true or false.`);
449
555
  }
450
556
  function readOptionalNumber(options, name) {
451
557
  const value = readSingle(options, name);
@@ -454,13 +560,20 @@ function readOptionalNumber(options, name) {
454
560
  }
455
561
  const parsed = Number(value);
456
562
  if (!Number.isFinite(parsed)) {
457
- throw new Error(`Option "--${name}" must be a number.`);
563
+ throw new CliError("invalid_value", `Option "--${name}" must be a number.`);
458
564
  }
459
565
  return parsed;
460
566
  }
461
567
  function readJsonValue(options, name) {
462
568
  const value = readSingle(options, name);
463
- return value === void 0 ? void 0 : JSON.parse(value);
569
+ if (value === void 0) {
570
+ return void 0;
571
+ }
572
+ try {
573
+ return JSON.parse(value);
574
+ } catch {
575
+ throw new CliError("invalid_value", `Option "--${name}" contains invalid JSON.`);
576
+ }
464
577
  }
465
578
  function readJsonObject(options, name) {
466
579
  const parsed = readJsonValue(options, name);
@@ -468,7 +581,7 @@ function readJsonObject(options, name) {
468
581
  return void 0;
469
582
  }
470
583
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
471
- throw new Error(`Option "--${name}" must be a JSON object.`);
584
+ throw new CliError("invalid_value", `Option "--${name}" must be a JSON object.`);
472
585
  }
473
586
  return parsed;
474
587
  }
@@ -478,7 +591,7 @@ function readJsonArray(options, name) {
478
591
  return void 0;
479
592
  }
480
593
  if (!Array.isArray(parsed)) {
481
- throw new Error(`Option "--${name}" must be a JSON array.`);
594
+ throw new CliError("invalid_value", `Option "--${name}" must be a JSON array.`);
482
595
  }
483
596
  return parsed;
484
597
  }
@@ -498,7 +611,7 @@ async function buildOperationInput(operation, parsed, runtime) {
498
611
  case "session.open": {
499
612
  const url = parsed.rest[0];
500
613
  if (url === void 0) {
501
- throw new Error("open requires a URL.");
614
+ throw new CliError("missing_arguments", "open requires a URL.", CLI_USAGE_HINTS[operation]);
502
615
  }
503
616
  return {
504
617
  url,
@@ -531,7 +644,7 @@ async function buildOperationInput(operation, parsed, runtime) {
531
644
  }
532
645
  case "page.goto": {
533
646
  if (parsed.rest[0] === void 0) {
534
- throw new Error("goto requires a URL.");
647
+ throw new CliError("missing_arguments", "goto requires a URL.", CLI_USAGE_HINTS[operation]);
535
648
  }
536
649
  const captureNetwork = readSingle(parsed.rawOptions, "capture-network");
537
650
  return {
@@ -543,14 +656,14 @@ async function buildOperationInput(operation, parsed, runtime) {
543
656
  return parsed.rest[0] === void 0 ? {} : { mode: parsed.rest[0] };
544
657
  case "page.evaluate":
545
658
  if (parsed.rest[0] === void 0) {
546
- throw new Error("evaluate requires a script.");
659
+ throw new CliError("missing_arguments", "evaluate requires a script.", CLI_USAGE_HINTS[operation]);
547
660
  }
548
661
  return {
549
662
  script: joinRest(parsed.rest, 0)
550
663
  };
551
664
  case "page.add-init-script":
552
665
  if (parsed.rest[0] === void 0) {
553
- throw new Error("init-script requires a script.");
666
+ throw new CliError("missing_arguments", "init-script requires a script.", CLI_USAGE_HINTS[operation]);
554
667
  }
555
668
  return {
556
669
  script: joinRest(parsed.rest, 0)
@@ -566,7 +679,7 @@ async function buildOperationInput(operation, parsed, runtime) {
566
679
  return buildElementTargetInput(parsed, "hover");
567
680
  case "dom.input": {
568
681
  if (parsed.rest[1] === void 0) {
569
- throw new Error("input requires an element number and text.");
682
+ throw new CliError("missing_arguments", "input requires an element number and text.", CLI_USAGE_HINTS[operation]);
570
683
  }
571
684
  const pressEnter = readOptionalBoolean(parsed.rawOptions, "press-enter");
572
685
  return {
@@ -591,18 +704,18 @@ async function buildOperationInput(operation, parsed, runtime) {
591
704
  },
592
705
  direction,
593
706
  amount,
594
- ...persist === void 0 ? {} : { persist },
707
+ persist,
595
708
  ...captureNetwork === void 0 ? {} : { captureNetwork }
596
709
  };
597
710
  }
598
711
  case "dom.extract": {
599
712
  if (parsed.rest[0] === void 0) {
600
- throw new Error("extract requires a schema.");
713
+ throw new CliError("missing_arguments", "extract requires a template.", CLI_USAGE_HINTS[operation]);
601
714
  }
602
- const persist = readExtractPersistKey(parsed);
715
+ const persist = readPersistKey(parsed, "extract");
603
716
  return {
604
- schema: parseRequiredJsonObjectArgument(joinRest(parsed.rest, 0), "extract schema"),
605
- ...persist === void 0 ? {} : { persist }
717
+ persist,
718
+ template: parseRequiredJsonObjectArgument(joinRest(parsed.rest, 0), "extract template")
606
719
  };
607
720
  }
608
721
  case "network.query": {
@@ -633,7 +746,7 @@ async function buildOperationInput(operation, parsed, runtime) {
633
746
  }
634
747
  case "network.detail": {
635
748
  if (parsed.rest[0] === void 0) {
636
- throw new Error("network detail requires a record id.");
749
+ throw new CliError("missing_arguments", "network detail requires a record id.", CLI_USAGE_HINTS[operation]);
637
750
  }
638
751
  const probeFlag = readOptionalBoolean(parsed.rawOptions, "probe");
639
752
  return {
@@ -644,7 +757,7 @@ async function buildOperationInput(operation, parsed, runtime) {
644
757
  case "session.fetch": {
645
758
  const url = parsed.rest[0];
646
759
  if (url === void 0) {
647
- throw new Error("fetch requires a URL.");
760
+ throw new CliError("missing_arguments", "fetch requires a URL.", CLI_USAGE_HINTS[operation]);
648
761
  }
649
762
  const bodyJson = readJsonValue(parsed.rawOptions, "body");
650
763
  const bodyText = readSingle(parsed.rawOptions, "body-text");
@@ -652,7 +765,7 @@ async function buildOperationInput(operation, parsed, runtime) {
652
765
  const query = parseKeyValueList(parsed.rawOptions.get("query"));
653
766
  const headers = parseKeyValueList(parsed.rawOptions.get("header"));
654
767
  if (bodyJson !== void 0 && bodyText !== void 0) {
655
- throw new Error('Use either "--body" or "--body-text", not both.');
768
+ throw new CliError("invalid_option", 'Use either "--body" or "--body-text", not both.');
656
769
  }
657
770
  const transport = readSingle(parsed.rawOptions, "transport");
658
771
  const cookies = readOptionalBoolean(parsed.rawOptions, "cookies");
@@ -683,7 +796,7 @@ async function buildOperationInput(operation, parsed, runtime) {
683
796
  const siteKey = readSingle(parsed.rawOptions, "site-key");
684
797
  const pageUrl = readSingle(parsed.rawOptions, "page-url");
685
798
  if (provider === void 0 || apiKey === void 0) {
686
- throw new Error('captcha solve requires "--provider" and "--api-key".');
799
+ throw new CliError("missing_arguments", 'captcha solve requires "--provider" and "--api-key".', CLI_USAGE_HINTS[operation]);
687
800
  }
688
801
  return {
689
802
  provider: readCaptchaProvider(provider),
@@ -713,7 +826,7 @@ async function buildOperationInput(operation, parsed, runtime) {
713
826
  case "scripts.beautify":
714
827
  case "scripts.deobfuscate": {
715
828
  if (parsed.rest[0] === void 0) {
716
- throw new Error(`${parsed.command.join(" ")} requires an artifact id.`);
829
+ throw new CliError("missing_arguments", `${parsed.command.join(" ")} requires an artifact id.`, CLI_USAGE_HINTS[operation]);
717
830
  }
718
831
  const persist = readOptionalBoolean(parsed.rawOptions, "persist");
719
832
  return {
@@ -723,7 +836,7 @@ async function buildOperationInput(operation, parsed, runtime) {
723
836
  }
724
837
  case "scripts.sandbox":
725
838
  if (parsed.rest[0] === void 0) {
726
- throw new Error("scripts sandbox requires an artifact id.");
839
+ throw new CliError("missing_arguments", "scripts sandbox requires an artifact id.", CLI_USAGE_HINTS[operation]);
727
840
  }
728
841
  {
729
842
  const fidelity = readSingle(parsed.rawOptions, "fidelity");
@@ -772,14 +885,14 @@ async function buildOperationInput(operation, parsed, runtime) {
772
885
  case "interaction.get":
773
886
  case "interaction.replay":
774
887
  if (parsed.rest[0] === void 0) {
775
- throw new Error(`${parsed.command.join(" ")} requires a trace id.`);
888
+ throw new CliError("missing_arguments", `${parsed.command.join(" ")} requires a trace id.`, CLI_USAGE_HINTS[operation]);
776
889
  }
777
890
  return {
778
891
  traceId: parsed.rest[0]
779
892
  };
780
893
  case "interaction.diff":
781
894
  if (parsed.rest[0] === void 0 || parsed.rest[1] === void 0) {
782
- throw new Error("interaction diff requires two trace ids.");
895
+ throw new CliError("missing_arguments", "interaction diff requires two trace ids.", CLI_USAGE_HINTS[operation]);
783
896
  }
784
897
  return {
785
898
  leftTraceId: parsed.rest[0],
@@ -787,7 +900,7 @@ async function buildOperationInput(operation, parsed, runtime) {
787
900
  };
788
901
  case "artifact.read":
789
902
  if (parsed.rest[0] === void 0) {
790
- throw new Error("artifact read requires an artifact id.");
903
+ throw new CliError("missing_arguments", "artifact read requires an artifact id.", CLI_USAGE_HINTS[operation]);
791
904
  }
792
905
  return {
793
906
  artifactId: parsed.rest[0]
@@ -795,7 +908,8 @@ async function buildOperationInput(operation, parsed, runtime) {
795
908
  case "session.close":
796
909
  return {};
797
910
  default:
798
- throw new Error(
911
+ throw new CliError(
912
+ "unsupported_operation",
799
913
  `${operation} does not have a direct CLI input shape. Use a supported command or the SDK.`
800
914
  );
801
915
  }
@@ -812,7 +926,7 @@ function buildElementTargetInput(parsed, verb) {
812
926
  kind: "element",
813
927
  element
814
928
  },
815
- ...persist === void 0 ? {} : { persist },
929
+ persist,
816
930
  ...captureNetwork === void 0 ? {} : { captureNetwork }
817
931
  };
818
932
  }
@@ -842,7 +956,7 @@ function buildComputerExecuteInput(parsed) {
842
956
  }
843
957
  case "type":
844
958
  if (parsed.rest[0] === void 0) {
845
- throw new Error("computer type requires text.");
959
+ throw new CliError("missing_arguments", "computer type requires text.");
846
960
  }
847
961
  return {
848
962
  action: {
@@ -853,7 +967,7 @@ function buildComputerExecuteInput(parsed) {
853
967
  };
854
968
  case "key": {
855
969
  if (parsed.rest[0] === void 0) {
856
- throw new Error("computer key requires a key.");
970
+ throw new CliError("missing_arguments", "computer key requires a key.");
857
971
  }
858
972
  const modifiers = readKeyModifiers(readSingle(parsed.rawOptions, "modifiers"));
859
973
  return {
@@ -869,7 +983,7 @@ function buildComputerExecuteInput(parsed) {
869
983
  const dx = readOptionalNumber(parsed.rawOptions, "dx");
870
984
  const dy = readOptionalNumber(parsed.rawOptions, "dy");
871
985
  if (dx === void 0 || dy === void 0) {
872
- throw new Error('computer scroll requires "--dx" and "--dy".');
986
+ throw new CliError("missing_arguments", 'computer scroll requires "--dx" and "--dy".');
873
987
  }
874
988
  return {
875
989
  action: {
@@ -924,49 +1038,50 @@ function buildComputerExecuteInput(parsed) {
924
1038
  }
925
1039
  };
926
1040
  default:
927
- throw new Error(`Unknown computer command: ${parsed.command.join(" ")}`);
1041
+ throw new CliError("unknown_command", `Unknown computer command: ${parsed.command.join(" ")}`);
928
1042
  }
929
1043
  }
930
1044
  async function resolvePageRefByIndex(runtime, index) {
931
1045
  const pages = (await runtime.listPages({})).pages;
932
1046
  const page = pages[index - 1];
933
1047
  if (page === void 0) {
934
- throw new Error(`tab ${String(index)} does not exist.`);
1048
+ throw new CliError("invalid_value", `tab ${String(index)} does not exist.`);
935
1049
  }
936
1050
  return page.pageRef;
937
1051
  }
938
1052
  function readRequiredPositiveInteger(value, message) {
939
1053
  const parsed = readRequiredNumber(value, message);
940
1054
  if (!Number.isInteger(parsed) || parsed < 1) {
941
- throw new Error(message);
1055
+ throw new CliError("missing_arguments", message);
942
1056
  }
943
1057
  return parsed;
944
1058
  }
945
1059
  function readRequiredNumber(value, message) {
946
1060
  if (value === void 0) {
947
- throw new Error(message);
1061
+ throw new CliError("missing_arguments", message);
948
1062
  }
949
1063
  const parsed = Number(value);
950
1064
  if (!Number.isFinite(parsed)) {
951
- throw new Error(message);
1065
+ throw new CliError("missing_arguments", message);
952
1066
  }
953
1067
  return parsed;
954
1068
  }
955
1069
  function readSingleDirection(value) {
956
1070
  if (value === void 0 || !SCROLL_DIRECTIONS.has(value)) {
957
- throw new Error("scroll requires a direction: up, down, left, or right.");
1071
+ throw new CliError("missing_arguments", "scroll requires a direction: up, down, left, or right.");
958
1072
  }
959
1073
  return value;
960
1074
  }
961
1075
  function readClickButton(value) {
962
1076
  if (value === void 0 || !CLICK_BUTTONS.has(value)) {
963
- throw new Error('Expected "--button" to be one of: left, middle, right.');
1077
+ throw new CliError("invalid_value", 'Expected "--button" to be one of: left, middle, right.');
964
1078
  }
965
1079
  return value;
966
1080
  }
967
1081
  function readFetchTransport(value) {
968
1082
  if (value === void 0 || !FETCH_TRANSPORTS.has(value)) {
969
- throw new Error(
1083
+ throw new CliError(
1084
+ "invalid_value",
970
1085
  'Expected "--transport" to be one of: auto, direct, matched-tls, context, page.'
971
1086
  );
972
1087
  }
@@ -974,31 +1089,31 @@ function readFetchTransport(value) {
974
1089
  }
975
1090
  function readCaptchaProvider(value) {
976
1091
  if (value === void 0 || !CAPTCHA_PROVIDERS.has(value)) {
977
- throw new Error('Expected "--provider" to be one of: 2captcha, capsolver.');
1092
+ throw new CliError("invalid_value", 'Expected "--provider" to be one of: 2captcha, capsolver.');
978
1093
  }
979
1094
  return value;
980
1095
  }
981
1096
  function readCaptchaType(value) {
982
1097
  if (value === void 0 || !CAPTCHA_TYPES.has(value)) {
983
- throw new Error('Expected "--type" to be one of: recaptcha-v2, hcaptcha, turnstile.');
1098
+ throw new CliError("invalid_value", 'Expected "--type" to be one of: recaptcha-v2, hcaptcha, turnstile.');
984
1099
  }
985
1100
  return value;
986
1101
  }
987
1102
  function readSandboxFidelity(value) {
988
1103
  if (value === void 0 || !SANDBOX_FIDELITIES.has(value)) {
989
- throw new Error('Expected "--fidelity" to be one of: minimal, standard, full.');
1104
+ throw new CliError("invalid_value", 'Expected "--fidelity" to be one of: minimal, standard, full.');
990
1105
  }
991
1106
  return value;
992
1107
  }
993
1108
  function readSandboxClockMode(value) {
994
1109
  if (value === void 0 || !SANDBOX_CLOCK_MODES.has(value)) {
995
- throw new Error('Expected "--clock" to be one of: real, manual.');
1110
+ throw new CliError("invalid_value", 'Expected "--clock" to be one of: real, manual.');
996
1111
  }
997
1112
  return value;
998
1113
  }
999
1114
  function readScreenshotFormat(value) {
1000
1115
  if (value === void 0 || !SCREENSHOT_FORMATS.has(value)) {
1001
- throw new Error('Expected "--format" to be one of: png, jpeg, webp.');
1116
+ throw new CliError("invalid_value", 'Expected "--format" to be one of: png, jpeg, webp.');
1002
1117
  }
1003
1118
  return value;
1004
1119
  }
@@ -1009,7 +1124,7 @@ function readKeyModifiers(value) {
1009
1124
  }
1010
1125
  for (const modifier of modifiers) {
1011
1126
  if (!KEY_MODIFIERS.has(modifier)) {
1012
- throw new Error('Expected "--modifiers" to contain only: Shift, Control, Alt, Meta.');
1127
+ throw new CliError("invalid_value", 'Expected "--modifiers" to contain only: Shift, Control, Alt, Meta.');
1013
1128
  }
1014
1129
  }
1015
1130
  return [...new Set(modifiers)];
@@ -1017,30 +1132,22 @@ function readKeyModifiers(value) {
1017
1132
  function readPersistKey(parsed, verb) {
1018
1133
  const value = readSingle(parsed.rawOptions, "persist");
1019
1134
  if (value === void 0) {
1020
- return void 0;
1135
+ throw new CliError("missing_arguments", `${verb} requires "--persist <key>".`);
1021
1136
  }
1022
1137
  if (value === "true" || value === "false") {
1023
- throw new Error(`${verb} requires "--persist <key>" when using --persist.`);
1024
- }
1025
- if (verb === "scroll" && readOptionalNumber(parsed.rawOptions, "element") === void 0) {
1026
- throw new Error('scroll requires "--element <n>" when using "--persist <key>".');
1027
- }
1028
- return value;
1029
- }
1030
- function readExtractPersistKey(parsed) {
1031
- const value = readSingle(parsed.rawOptions, "persist");
1032
- if (value === void 0) {
1033
- return void 0;
1034
- }
1035
- if (value === "true" || value === "false") {
1036
- throw new Error('extract requires "--persist <key>" when using --persist.');
1138
+ throw new CliError("missing_arguments", `${verb} requires "--persist <key>".`);
1037
1139
  }
1038
1140
  return value;
1039
1141
  }
1040
1142
  function parseRequiredJsonObjectArgument(value, label) {
1041
- const parsed = JSON.parse(value);
1143
+ let parsed;
1144
+ try {
1145
+ parsed = JSON.parse(value);
1146
+ } catch {
1147
+ throw new CliError("invalid_value", `${label} contains invalid JSON.`);
1148
+ }
1042
1149
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1043
- throw new Error(`${label} must be a JSON object.`);
1150
+ throw new CliError("invalid_value", `${label} must be a JSON object.`);
1044
1151
  }
1045
1152
  return parsed;
1046
1153
  }
@@ -2196,7 +2303,7 @@ async function handleViewCommand(parsed, options = {}) {
2196
2303
  return;
2197
2304
  }
2198
2305
  if (subcommand !== void 0) {
2199
- throw new Error(`Unknown view command: view ${subcommand}`);
2306
+ throw new CliError("unknown_command", `Unknown view command: view ${subcommand}`);
2200
2307
  }
2201
2308
  if (parsed.options.localViewMode !== void 0) {
2202
2309
  const preference = await setLocalViewMode(parsed.options.localViewMode);
@@ -2244,7 +2351,10 @@ async function resolveWorkspaceSessionId(input) {
2244
2351
  }
2245
2352
  function assertNoViewPreferenceFlag(parsed) {
2246
2353
  if (parsed.options.localViewMode !== void 0) {
2247
- throw new Error("View preference flags cannot be combined with this subcommand.");
2354
+ throw new CliError(
2355
+ "invalid_option",
2356
+ "View preference flags cannot be combined with this subcommand."
2357
+ );
2248
2358
  }
2249
2359
  }
2250
2360
  function writeViewOutput(parsed, value) {
@@ -2329,10 +2439,10 @@ async function main() {
2329
2439
  }
2330
2440
  const operation = resolveOperation(parsed.command);
2331
2441
  if (!operation) {
2332
- throw new Error(`Unknown command: ${parsed.command.join(" ")}`);
2442
+ throw new CliError("unknown_command", `Unknown command: ${parsed.command.join(" ")}`);
2333
2443
  }
2334
2444
  if (parsed.options.workspace === void 0) {
2335
- throw new Error('Stateful commands require "--workspace <id>" or OPENSTEER_WORKSPACE.');
2445
+ throw new CliError("missing_workspace", 'Stateful commands require "--workspace <id>" or OPENSTEER_WORKSPACE.');
2336
2446
  }
2337
2447
  const { engineName, provider, runtimeProvider } = resolveCliRuntimeSelection(parsed);
2338
2448
  if (operation === "session.close") {
@@ -2354,28 +2464,43 @@ async function main() {
2354
2464
  let renderOperation = operation;
2355
2465
  try {
2356
2466
  const input = await buildOperationInput(operation, parsed, runtime);
2357
- result = await dispatchSemanticOperation(runtime, operation, input);
2467
+ const rawResult = await dispatchSemanticOperation(runtime, operation, input);
2358
2468
  if (parsed.command[0] === "tab" && operation !== "page.list") {
2359
2469
  renderOperation = "page.list";
2360
2470
  result = await runtime.listPages({});
2471
+ } else {
2472
+ result = rawResult;
2473
+ }
2474
+ let warning;
2475
+ if (result !== null && typeof result === "object" && "warning" in result) {
2476
+ const { warning: w, ...rest } = result;
2477
+ if (typeof w === "string") {
2478
+ warning = w;
2479
+ result = rest;
2480
+ }
2361
2481
  }
2362
2482
  process4.stdout.write(renderOperationOutput(renderOperation, result, input));
2483
+ if (warning !== void 0) {
2484
+ emitWarning(warning);
2485
+ }
2363
2486
  } finally {
2364
2487
  await runtime.disconnect().catch(() => void 0);
2365
2488
  }
2366
2489
  }
2367
2490
  async function handleExecCommand(parsed) {
2368
2491
  if (parsed.options.workspace === void 0) {
2369
- throw new Error('exec requires "--workspace <id>" or OPENSTEER_WORKSPACE.');
2492
+ throw new CliError("missing_workspace", 'exec requires "--workspace <id>" or OPENSTEER_WORKSPACE.');
2370
2493
  }
2371
2494
  const expression = parsed.rest.join(" ");
2372
2495
  if (!expression) {
2373
- throw new Error(
2374
- `exec requires an expression. Example: exec "await this.evaluate('document.title')"`
2496
+ throw new CliError(
2497
+ "missing_arguments",
2498
+ "exec requires an expression.",
2499
+ "opensteer exec <expression>"
2375
2500
  );
2376
2501
  }
2377
2502
  const { engineName, runtimeProvider } = resolveCliRuntimeSelection(parsed);
2378
- const { Opensteer } = await import('../opensteer-PJI7VUIT.js');
2503
+ const { Opensteer } = await import('../opensteer-UGA6YBRN.js');
2379
2504
  const opensteer = new Opensteer({
2380
2505
  workspace: parsed.options.workspace,
2381
2506
  rootDir: process4.cwd(),
@@ -2407,8 +2532,10 @@ async function handleBrowserCommand(parsed) {
2407
2532
  if (subcommand === "inspect") {
2408
2533
  const endpoint = parsed.options.attachEndpoint ?? parsed.rest[0];
2409
2534
  if (!endpoint) {
2410
- throw new Error(
2411
- 'browser inspect requires "--attach-endpoint <url>" or a positional endpoint.'
2535
+ throw new CliError(
2536
+ "missing_arguments",
2537
+ 'browser inspect requires "--attach-endpoint <url>" or a positional endpoint.',
2538
+ "opensteer browser inspect <endpoint>"
2412
2539
  );
2413
2540
  }
2414
2541
  const result = await inspectCdpEndpoint({
@@ -2421,7 +2548,8 @@ async function handleBrowserCommand(parsed) {
2421
2548
  return;
2422
2549
  }
2423
2550
  if (parsed.options.workspace === void 0) {
2424
- throw new Error(
2551
+ throw new CliError(
2552
+ "missing_workspace",
2425
2553
  'Browser workspace commands require "--workspace <id>" or OPENSTEER_WORKSPACE.'
2426
2554
  );
2427
2555
  }
@@ -2444,7 +2572,11 @@ async function handleBrowserCommand(parsed) {
2444
2572
  case "clone": {
2445
2573
  const sourceUserDataDir = parsed.options.sourceUserDataDir;
2446
2574
  if (!sourceUserDataDir) {
2447
- throw new Error('browser clone requires "--source-user-data-dir <path>".');
2575
+ throw new CliError(
2576
+ "missing_arguments",
2577
+ 'browser clone requires "--source-user-data-dir <path>".',
2578
+ "opensteer browser clone --source-user-data-dir <path>"
2579
+ );
2448
2580
  }
2449
2581
  const result = await manager.clonePersistentBrowser({
2450
2582
  sourceUserDataDir,
@@ -2467,7 +2599,7 @@ async function handleBrowserCommand(parsed) {
2467
2599
  return;
2468
2600
  }
2469
2601
  default:
2470
- throw new Error(`Unknown browser command: ${parsed.command.join(" ")}`);
2602
+ throw new CliError("unknown_command", `Unknown browser command: ${parsed.command.join(" ")}`);
2471
2603
  }
2472
2604
  }
2473
2605
  async function handleCloseCommand(parsed, engineName, providerMode, runtimeProvider) {
@@ -2500,23 +2632,27 @@ async function handleCloseCommand(parsed, engineName, providerMode, runtimeProvi
2500
2632
  }
2501
2633
  async function handleRecordCommandEntry(parsed) {
2502
2634
  if (parsed.options.workspace === void 0) {
2503
- throw new Error('record requires "--workspace <id>" or OPENSTEER_WORKSPACE.');
2635
+ throw new CliError("missing_workspace", 'record requires "--workspace <id>" or OPENSTEER_WORKSPACE.');
2504
2636
  }
2505
2637
  const url = parsed.options.url ?? parsed.rest[0];
2506
2638
  if (url === void 0) {
2507
- throw new Error('record requires "--url <value>" or a positional URL.');
2639
+ throw new CliError(
2640
+ "missing_arguments",
2641
+ 'record requires "--url <value>" or a positional URL.',
2642
+ "opensteer record --url <url> --workspace <id>"
2643
+ );
2508
2644
  }
2509
2645
  const provider = resolveCliProvider(parsed);
2510
2646
  assertCloudCliOptionsMatchProvider(parsed, provider.mode);
2511
2647
  const engineName = resolveCliEngineName(parsed);
2512
2648
  if (engineName !== "playwright") {
2513
- throw new Error("record requires engine=playwright.");
2649
+ throw new CliError("config_conflict", "record requires engine=playwright.");
2514
2650
  }
2515
2651
  const rootDir = process4.cwd();
2516
2652
  const recordBrowser = parsed.options.browser;
2517
2653
  if (provider.mode === "cloud") {
2518
2654
  if (typeof recordBrowser === "object") {
2519
- throw new Error('record does not support browser.mode="attach".');
2655
+ throw new CliError("config_conflict", 'record does not support browser.mode="attach".');
2520
2656
  }
2521
2657
  const runtimeProvider = buildCliRuntimeProvider(parsed, provider.mode);
2522
2658
  const runtimeConfig = resolveOpensteerRuntimeConfig({
@@ -2536,10 +2672,10 @@ async function handleRecordCommandEntry(parsed) {
2536
2672
  return;
2537
2673
  }
2538
2674
  if (parsed.options.launch?.headless === true) {
2539
- throw new Error('record requires a headed browser. Remove "--headless true".');
2675
+ throw new CliError("config_conflict", 'record requires a headed browser. Remove "--headless true".');
2540
2676
  }
2541
2677
  if (typeof recordBrowser === "object") {
2542
- throw new Error('record does not support browser.mode="attach".');
2678
+ throw new CliError("config_conflict", 'record does not support browser.mode="attach".');
2543
2679
  }
2544
2680
  const launch = {
2545
2681
  ...parsed.options.launch ?? {},
@@ -2608,7 +2744,7 @@ function resolveCliBootstrapAction(argv) {
2608
2744
  }
2609
2745
  function buildCliBrowserProfile(parsed) {
2610
2746
  if (parsed.options.cloudProfileReuseIfActive === true && parsed.options.cloudProfileId === void 0) {
2611
- throw new Error('"--cloud-profile-reuse-if-active" requires "--cloud-profile-id <id>".');
2747
+ throw new CliError("invalid_option", '"--cloud-profile-reuse-if-active" requires "--cloud-profile-id <id>".');
2612
2748
  }
2613
2749
  return parsed.options.cloudProfileId === void 0 ? void 0 : {
2614
2750
  profileId: parsed.options.cloudProfileId,
@@ -2669,7 +2805,8 @@ function buildCliRuntimeProvider(parsed, providerMode) {
2669
2805
  }
2670
2806
  function assertCloudCliOptionsMatchProvider(parsed, providerMode) {
2671
2807
  if (providerMode !== "cloud" && (parsed.options.cloudBaseUrl !== void 0 || parsed.options.cloudApiKey !== void 0 || parsed.options.cloudAppBaseUrl !== void 0 || parsed.options.cloudProfileId !== void 0 || parsed.options.cloudProfileReuseIfActive === true)) {
2672
- throw new Error(
2808
+ throw new CliError(
2809
+ "config_conflict",
2673
2810
  'Cloud-specific options require provider=cloud. Set "--provider cloud" or OPENSTEER_PROVIDER=cloud.'
2674
2811
  );
2675
2812
  }
@@ -2703,17 +2840,7 @@ function printVersion() {
2703
2840
  `);
2704
2841
  }
2705
2842
  main().catch((error) => {
2706
- const payload = error instanceof Error ? {
2707
- error: {
2708
- name: error.name,
2709
- message: error.message
2710
- }
2711
- } : {
2712
- error: {
2713
- name: "Error",
2714
- message: String(error)
2715
- }
2716
- };
2843
+ const payload = formatCliErrorOutput(error);
2717
2844
  process4.stderr.write(`${JSON.stringify(payload)}
2718
2845
  `);
2719
2846
  process4.exitCode = 1;