opendevbrowser 0.0.26 → 0.0.28

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 (102) hide show
  1. package/README.md +7 -10
  2. package/dist/browser/canvas-manager.d.ts.map +1 -1
  3. package/dist/canvas/document-store.d.ts.map +1 -1
  4. package/dist/canvas/guidance.d.ts +22 -0
  5. package/dist/canvas/guidance.d.ts.map +1 -0
  6. package/dist/canvas/types.d.ts +3 -0
  7. package/dist/canvas/types.d.ts.map +1 -1
  8. package/dist/{chunk-GTTYIAI7.js → chunk-I5ZCOZZV.js} +316 -94
  9. package/dist/chunk-I5ZCOZZV.js.map +1 -0
  10. package/dist/{chunk-AVQL6WAS.js → chunk-T3VVHJTK.js} +2384 -544
  11. package/dist/chunk-T3VVHJTK.js.map +1 -0
  12. package/dist/cli/args.d.ts.map +1 -1
  13. package/dist/cli/commands/daemon.d.ts +2 -0
  14. package/dist/cli/commands/daemon.d.ts.map +1 -1
  15. package/dist/cli/commands/devtools/console-poll.d.ts.map +1 -1
  16. package/dist/cli/commands/devtools/network-poll.d.ts.map +1 -1
  17. package/dist/cli/commands/inspiredesign.d.ts +2 -0
  18. package/dist/cli/commands/inspiredesign.d.ts.map +1 -1
  19. package/dist/cli/commands/macro-resolve.d.ts +2 -0
  20. package/dist/cli/commands/macro-resolve.d.ts.map +1 -1
  21. package/dist/cli/commands/product-video.d.ts +2 -0
  22. package/dist/cli/commands/product-video.d.ts.map +1 -1
  23. package/dist/cli/commands/research.d.ts +2 -0
  24. package/dist/cli/commands/research.d.ts.map +1 -1
  25. package/dist/cli/commands/update.d.ts.map +1 -1
  26. package/dist/cli/daemon-autostart.d.ts +6 -2
  27. package/dist/cli/daemon-autostart.d.ts.map +1 -1
  28. package/dist/cli/daemon-commands.d.ts.map +1 -1
  29. package/dist/cli/help.d.ts.map +1 -1
  30. package/dist/cli/index.js +516 -86
  31. package/dist/cli/index.js.map +1 -1
  32. package/dist/cli/utils/parse.d.ts.map +1 -1
  33. package/dist/cli/utils/workflow-message.d.ts +3 -0
  34. package/dist/cli/utils/workflow-message.d.ts.map +1 -1
  35. package/dist/daemon-fingerprint.json +1 -1
  36. package/dist/index.js +39 -15
  37. package/dist/index.js.map +1 -1
  38. package/dist/inspiredesign/brief-expansion.d.ts +3 -0
  39. package/dist/inspiredesign/brief-expansion.d.ts.map +1 -1
  40. package/dist/{providers/inspiredesign-capture-mode.d.ts → inspiredesign/capture-mode.d.ts} +2 -2
  41. package/dist/inspiredesign/capture-mode.d.ts.map +1 -0
  42. package/dist/{providers/inspiredesign-capture.d.ts → inspiredesign/capture.d.ts} +3 -3
  43. package/dist/inspiredesign/capture.d.ts.map +1 -0
  44. package/dist/{providers/inspiredesign-contract.d.ts → inspiredesign/contract.d.ts} +70 -6
  45. package/dist/inspiredesign/contract.d.ts.map +1 -0
  46. package/dist/inspiredesign/handoff.d.ts +12 -10
  47. package/dist/inspiredesign/handoff.d.ts.map +1 -1
  48. package/dist/inspiredesign/reference-pattern-board.d.ts +74 -0
  49. package/dist/inspiredesign/reference-pattern-board.d.ts.map +1 -0
  50. package/dist/macros/execute-runtime.d.ts +2 -1
  51. package/dist/macros/execute-runtime.d.ts.map +1 -1
  52. package/dist/macros/execute.d.ts +4 -2
  53. package/dist/macros/execute.d.ts.map +1 -1
  54. package/dist/opendevbrowser.js +39 -15
  55. package/dist/opendevbrowser.js.map +1 -1
  56. package/dist/providers/browser-fallback.d.ts +7 -0
  57. package/dist/providers/browser-fallback.d.ts.map +1 -1
  58. package/dist/providers/community/index.d.ts.map +1 -1
  59. package/dist/providers/index.d.ts.map +1 -1
  60. package/dist/providers/renderer.d.ts +1 -1
  61. package/dist/providers/renderer.d.ts.map +1 -1
  62. package/dist/providers/research-compiler.d.ts.map +1 -1
  63. package/dist/providers/runtime-bundle.d.ts +1 -1
  64. package/dist/providers/runtime-bundle.d.ts.map +1 -1
  65. package/dist/providers/runtime-factory.d.ts.map +1 -1
  66. package/dist/providers/shopping/index.d.ts.map +1 -1
  67. package/dist/providers/social/platform.d.ts.map +1 -1
  68. package/dist/providers/social/search-quality.d.ts.map +1 -1
  69. package/dist/providers/social/youtube.d.ts.map +1 -1
  70. package/dist/providers/workflow-handoff.d.ts +18 -1
  71. package/dist/providers/workflow-handoff.d.ts.map +1 -1
  72. package/dist/providers/workflows.d.ts +10 -5
  73. package/dist/providers/workflows.d.ts.map +1 -1
  74. package/dist/{providers-T2FQJCF6.js → providers-QF2RFB4J.js} +2 -2
  75. package/dist/public-surface/generated-manifest.d.ts +8 -8
  76. package/dist/public-surface/generated-manifest.d.ts.map +1 -1
  77. package/dist/public-surface/source.d.ts +13 -12
  78. package/dist/public-surface/source.d.ts.map +1 -1
  79. package/dist/relay/protocol.d.ts +14 -2
  80. package/dist/relay/protocol.d.ts.map +1 -1
  81. package/dist/tools/inspiredesign_run.d.ts.map +1 -1
  82. package/dist/tools/macro_resolve.d.ts.map +1 -1
  83. package/dist/tools/product_video_run.d.ts.map +1 -1
  84. package/dist/tools/research_run.d.ts.map +1 -1
  85. package/dist/tools/shopping_run.d.ts.map +1 -1
  86. package/extension/dist/canvas/canvas-runtime.js +13 -6
  87. package/extension/dist/services/ConnectionManager.js +8 -4
  88. package/extension/manifest.json +1 -1
  89. package/package.json +1 -1
  90. package/skills/opendevbrowser-best-practices/SKILL.md +6 -6
  91. package/skills/opendevbrowser-best-practices/assets/templates/skill-runtime-pack-matrix.json +1 -1
  92. package/skills/opendevbrowser-design-agent/SKILL.md +5 -0
  93. package/skills/opendevbrowser-design-agent/artifacts/design-contract-playbook.md +6 -1
  94. package/skills/opendevbrowser-design-agent/assets/templates/design-contract.v1.json +15 -1
  95. package/skills/opendevbrowser-design-agent/assets/templates/inspiredesign-advanced-brief.v1.json +72 -33
  96. package/skills/opendevbrowser-design-agent/assets/templates/reference-pattern-board.v1.json +2 -0
  97. package/dist/chunk-AVQL6WAS.js.map +0 -1
  98. package/dist/chunk-GTTYIAI7.js.map +0 -1
  99. package/dist/providers/inspiredesign-capture-mode.d.ts.map +0 -1
  100. package/dist/providers/inspiredesign-capture.d.ts.map +0 -1
  101. package/dist/providers/inspiredesign-contract.d.ts.map +0 -1
  102. /package/dist/{providers-T2FQJCF6.js.map → providers-QF2RFB4J.js.map} +0 -0
package/dist/cli/index.js CHANGED
@@ -41,7 +41,7 @@ import {
41
41
  resolveExitCode,
42
42
  startDaemon,
43
43
  toCliError
44
- } from "../chunk-GTTYIAI7.js";
44
+ } from "../chunk-I5ZCOZZV.js";
45
45
  import "../chunk-STGGGVYT.js";
46
46
  import {
47
47
  createNoOpSkillRemovalResult,
@@ -74,7 +74,7 @@ import {
74
74
  setDefaultLogSink,
75
75
  stderrSink,
76
76
  summarizePrimaryProviderIssue
77
- } from "../chunk-AVQL6WAS.js";
77
+ } from "../chunk-T3VVHJTK.js";
78
78
  import "../chunk-FUSXMW3G.js";
79
79
 
80
80
  // src/cli/args.ts
@@ -143,6 +143,17 @@ function parseTransport(args) {
143
143
  }
144
144
  var VALID_FLAG_SET = new Set(VALID_FLAGS);
145
145
  var VALID_EQUALS_FLAG_SET = new Set(VALID_EQUALS_FLAGS);
146
+ var SIGNED_VALUE_FLAG_SET = /* @__PURE__ */ new Set(["--dy"]);
147
+ var SIGNED_INTEGER_VALUE = /^-\d+$/;
148
+ function shouldSkipValueToken(flag, value) {
149
+ if (!VALID_EQUALS_FLAG_SET.has(flag) || value === void 0) {
150
+ return false;
151
+ }
152
+ if (!value.startsWith("-")) {
153
+ return true;
154
+ }
155
+ return SIGNED_VALUE_FLAG_SET.has(flag) && SIGNED_INTEGER_VALUE.test(value);
156
+ }
146
157
  function parseArgs(argv) {
147
158
  let args = expandShortFlags(argv.slice(2));
148
159
  let commandOverride = null;
@@ -237,7 +248,8 @@ function parseArgs(argv) {
237
248
  } else if (noPrompt) {
238
249
  mode = "global";
239
250
  }
240
- for (const arg of args) {
251
+ for (let index = 0; index < args.length; index += 1) {
252
+ const arg = args[index] ?? "";
241
253
  if (arg.startsWith("--") && !VALID_FLAG_SET.has(arg)) {
242
254
  if (arg.includes("=")) {
243
255
  const baseFlag = arg.split("=", 2)[0] ?? "";
@@ -250,6 +262,9 @@ function parseArgs(argv) {
250
262
  if (arg.startsWith("-") && !arg.startsWith("--") && !SHORT_FLAGS[arg]) {
251
263
  throw createUsageError(`Unknown flag: ${arg}`);
252
264
  }
265
+ if (shouldSkipValueToken(arg, args[index + 1])) {
266
+ index += 1;
267
+ }
253
268
  }
254
269
  return {
255
270
  command: commandOverride ?? "install",
@@ -441,7 +456,7 @@ var HELP_FLAG_GROUPS = [
441
456
  { flag: "--include-all-images", description: "Include all discovered product images." },
442
457
  { flag: "--include-copy", description: "Include product marketing copy metadata." },
443
458
  { flag: "--use-cookies", description: "Enable or disable provider cookie injection for workflow runs; a bare flag means true.", example: "opendevbrowser shopping run --query 'usb hub' --use-cookies" },
444
- { flag: "--browser-mode", description: "Shopping browser-recovery preference: auto, extension, or managed. Use managed for deterministic reruns and extension only when relay-backed auth/session state is required.", example: "opendevbrowser shopping run --query 'wireless ergonomic mouse' --providers shopping/bestbuy,shopping/ebay --browser-mode managed" },
459
+ { flag: "--browser-mode", description: "Provider browser transport mode: auto, extension, or managed. extension reuses relay-backed signed-in browser state; managed runs a deterministic managed browser.", example: "opendevbrowser research run --topic 'browser automation on X' --source-selection social --browser-mode extension --use-cookies --challenge-automation-mode browser_with_helper" },
445
460
  { flag: "--challenge-automation-mode", description: "Per-run challenge automation mode for workflow runs and macro-resolve execute: off, browser, or browser_with_helper. Precedence is run > session > config, and the helper remains browser-scoped only.", example: `opendevbrowser macro-resolve --expression '@community.search("openai")' --execute --challenge-automation-mode browser_with_helper` },
446
461
  { flag: "--cookie-policy-override", description: "Per-run workflow cookie policy override: off, auto, or required.", example: "opendevbrowser research run --topic 'agent workflows' --cookie-policy-override required" },
447
462
  { flag: "--cookie-policy", description: "Alias of --cookie-policy-override." },
@@ -554,6 +569,7 @@ var HELP_ONBOARDING_ENTRIES = [
554
569
  ];
555
570
  var HELP_REFERENCE_ENTRIES = [
556
571
  { label: "src/cli/onboarding-metadata.json", description: "Canonical first-contact onboarding metadata shared by help, nudges, and proof lanes." },
572
+ { label: "src/providers/workflow-handoff.ts", description: "Central next-step advisory builders for workflow follow-through." },
557
573
  { label: "src/inspiredesign/handoff.ts", description: "Shared inspiredesign follow-through commands, artifact names, and Canvas continuation guidance." },
558
574
  { label: "src/public-surface/source.ts", description: "Authoritative command, usage, flag, and tool surface metadata." },
559
575
  { label: "src/public-surface/generated-manifest.ts", description: "Checked-in generated public-surface snapshot consumed by help and parity tests." },
@@ -929,45 +945,292 @@ function installLocal(withConfig = false) {
929
945
  import * as fs4 from "fs";
930
946
  import * as path2 from "path";
931
947
  import * as os2 from "os";
948
+ import { randomUUID } from "crypto";
932
949
  var PLUGIN_NAME = "opendevbrowser";
950
+ var CACHE_MANIFEST = "package.json";
951
+ var CACHE_LOCKFILE = "package-lock.json";
952
+ var CACHE_UPDATE_LOCK = ".opendevbrowser-update.lock";
953
+ var CACHE_LOCK_STALE_MS = 30 * 60 * 1e3;
954
+ var DEPENDENCY_SECTIONS = [
955
+ "dependencies",
956
+ "devDependencies",
957
+ "optionalDependencies",
958
+ "peerDependencies"
959
+ ];
933
960
  function getCacheDir() {
934
961
  return process.env.OPENCODE_CACHE_DIR || path2.join(os2.homedir(), ".cache", "opencode");
935
962
  }
936
- function rmdir(dirPath) {
937
- const cacheDir = getCacheDir();
963
+ function assertCacheChild(targetPath, cacheDir) {
938
964
  const resolvedCache = path2.resolve(cacheDir);
939
- const resolvedPath = path2.resolve(dirPath);
940
- if (!resolvedPath.startsWith(resolvedCache + path2.sep) || resolvedPath === resolvedCache) {
941
- throw new Error(`Security: refusing to delete path outside cache directory: ${dirPath}`);
965
+ const resolvedPath = path2.resolve(targetPath);
966
+ const relativePath = path2.relative(resolvedCache, resolvedPath);
967
+ if (!relativePath || relativePath.startsWith("..") || path2.isAbsolute(relativePath)) {
968
+ throw new Error(`Security: refusing to modify path outside cache directory: ${targetPath}`);
969
+ }
970
+ }
971
+ function isMissingPathError(error) {
972
+ return error instanceof Error && "code" in error && error.code === "ENOENT";
973
+ }
974
+ function isExistingPathError(error) {
975
+ return error instanceof Error && "code" in error && error.code === "EEXIST";
976
+ }
977
+ function assertNoSymlinkCachePath(targetPath, cacheDir) {
978
+ assertCacheChild(targetPath, cacheDir);
979
+ const resolvedCache = path2.resolve(cacheDir);
980
+ const relativeSegments = path2.relative(resolvedCache, path2.resolve(targetPath)).split(path2.sep).filter(Boolean);
981
+ for (const candidate of [resolvedCache, ...relativeSegments.map((_, index) => path2.join(resolvedCache, ...relativeSegments.slice(0, index + 1)))]) {
982
+ try {
983
+ if (fs4.lstatSync(candidate).isSymbolicLink()) {
984
+ throw new Error(`Security: refusing to modify symlinked cache path: ${candidate}`);
985
+ }
986
+ } catch (error) {
987
+ if (isMissingPathError(error)) {
988
+ continue;
989
+ }
990
+ throw error;
991
+ }
992
+ }
993
+ }
994
+ function cacheDirectoryExists(cacheDir) {
995
+ try {
996
+ const stat = fs4.lstatSync(cacheDir);
997
+ if (stat.isSymbolicLink()) {
998
+ throw new Error(`Security: refusing to modify symlinked cache path: ${cacheDir}`);
999
+ }
1000
+ if (!stat.isDirectory()) {
1001
+ throw new Error(`${cacheDir} must be a directory`);
1002
+ }
1003
+ return true;
1004
+ } catch (error) {
1005
+ if (isMissingPathError(error)) {
1006
+ return false;
1007
+ }
1008
+ throw error;
1009
+ }
1010
+ }
1011
+ function removePathIfExists(targetPath, cacheDir) {
1012
+ assertNoSymlinkCachePath(targetPath, cacheDir);
1013
+ if (!fs4.existsSync(targetPath)) {
1014
+ return false;
1015
+ }
1016
+ fs4.rmSync(targetPath, { recursive: true, force: true });
1017
+ return true;
1018
+ }
1019
+ function cacheFileExists(targetPath, cacheDir) {
1020
+ assertNoSymlinkCachePath(targetPath, cacheDir);
1021
+ try {
1022
+ const stat = fs4.lstatSync(targetPath);
1023
+ if (stat.isSymbolicLink()) {
1024
+ throw new Error(`Security: refusing to modify symlinked cache manifest: ${targetPath}`);
1025
+ }
1026
+ return stat.isFile();
1027
+ } catch (error) {
1028
+ if (isMissingPathError(error)) {
1029
+ return false;
1030
+ }
1031
+ throw error;
1032
+ }
1033
+ }
1034
+ function writeManifestAtomic(manifestPath, cacheDir, manifest) {
1035
+ const tempPath = `${manifestPath}.${process.pid}.tmp`;
1036
+ assertNoSymlinkCachePath(tempPath, cacheDir);
1037
+ let fd = null;
1038
+ try {
1039
+ fd = fs4.openSync(tempPath, "wx");
1040
+ fs4.writeFileSync(fd, `${JSON.stringify(manifest, null, 2)}
1041
+ `, "utf8");
1042
+ fs4.closeSync(fd);
1043
+ fd = null;
1044
+ fs4.renameSync(tempPath, manifestPath);
1045
+ } catch (error) {
1046
+ if (fd !== null) {
1047
+ fs4.closeSync(fd);
1048
+ }
1049
+ fs4.rmSync(tempPath, { force: true });
1050
+ throw error;
1051
+ }
1052
+ }
1053
+ function withCacheMutationLock(cacheDir, action) {
1054
+ const lockPath = path2.join(cacheDir, CACHE_UPDATE_LOCK);
1055
+ assertNoSymlinkCachePath(lockPath, cacheDir);
1056
+ let lock = null;
1057
+ try {
1058
+ lock = openCacheMutationLock(lockPath);
1059
+ return action();
1060
+ } catch (error) {
1061
+ if (isExistingPathError(error)) {
1062
+ throw new Error("another update is already running for this OpenCode cache");
1063
+ }
1064
+ throw error;
1065
+ } finally {
1066
+ if (lock !== null) {
1067
+ fs4.closeSync(lock.fd);
1068
+ removeOwnedCacheMutationLock(lockPath, lock.token);
1069
+ }
1070
+ }
1071
+ }
1072
+ function openCacheMutationLock(lockPath) {
1073
+ try {
1074
+ return writeCacheMutationLock(lockPath);
1075
+ } catch (error) {
1076
+ if (!isExistingPathError(error) || !isStaleCacheMutationLock(lockPath)) {
1077
+ throw error;
1078
+ }
1079
+ removeStaleCacheMutationLock(lockPath);
1080
+ return writeCacheMutationLock(lockPath);
1081
+ }
1082
+ }
1083
+ function writeCacheMutationLock(lockPath) {
1084
+ const fd = fs4.openSync(lockPath, "wx");
1085
+ const token = randomUUID();
1086
+ try {
1087
+ const lock = { pid: process.pid, createdAt: Date.now(), token };
1088
+ fs4.writeFileSync(fd, `${JSON.stringify(lock)}
1089
+ `, "utf8");
1090
+ return { fd, token };
1091
+ } catch (error) {
1092
+ fs4.closeSync(fd);
1093
+ fs4.rmSync(lockPath, { force: true });
1094
+ throw error;
1095
+ }
1096
+ }
1097
+ function isStaleCacheMutationLock(lockPath) {
1098
+ const lock = readCacheMutationLock(lockPath);
1099
+ if (!lock) {
1100
+ return isLegacyCacheMutationLockStale(lockPath);
1101
+ }
1102
+ if (Date.now() - lock.createdAt < CACHE_LOCK_STALE_MS) {
1103
+ return false;
1104
+ }
1105
+ return !isProcessRunning(lock.pid);
1106
+ }
1107
+ function isLegacyCacheMutationLockStale(lockPath) {
1108
+ try {
1109
+ const pid = readLegacyCacheMutationLockPid(lockPath);
1110
+ if (pid !== null && isProcessRunning(pid)) {
1111
+ return false;
1112
+ }
1113
+ return Date.now() - fs4.statSync(lockPath).mtimeMs >= CACHE_LOCK_STALE_MS;
1114
+ } catch {
1115
+ return false;
942
1116
  }
943
- if (fs4.existsSync(dirPath)) {
944
- fs4.rmSync(dirPath, { recursive: true, force: true });
1117
+ }
1118
+ function readLegacyCacheMutationLockPid(lockPath) {
1119
+ const raw = fs4.readFileSync(lockPath, "utf8").trim();
1120
+ if (!/^\d+$/.test(raw)) {
1121
+ return null;
945
1122
  }
1123
+ const pid = Number(raw);
1124
+ return Number.isSafeInteger(pid) && pid > 0 ? pid : null;
1125
+ }
1126
+ function readCacheMutationLock(lockPath) {
1127
+ try {
1128
+ const parsed = JSON.parse(fs4.readFileSync(lockPath, "utf8"));
1129
+ const { pid, createdAt, token } = parsed;
1130
+ const hasToken = token === void 0 || typeof token === "string" && token.length > 0;
1131
+ if (typeof pid === "number" && typeof createdAt === "number" && hasToken && Number.isInteger(pid) && Number.isFinite(createdAt)) {
1132
+ return token === void 0 ? { pid, createdAt } : { pid, createdAt, token };
1133
+ }
1134
+ } catch {
1135
+ return null;
1136
+ }
1137
+ return null;
1138
+ }
1139
+ function isProcessRunning(pid) {
1140
+ try {
1141
+ process.kill(pid, 0);
1142
+ return true;
1143
+ } catch (error) {
1144
+ return error instanceof Error && "code" in error && error.code === "EPERM";
1145
+ }
1146
+ }
1147
+ function createExistingLockError() {
1148
+ const error = new Error("cache update lock changed before stale cleanup");
1149
+ error.code = "EEXIST";
1150
+ return error;
1151
+ }
1152
+ function removeStaleCacheMutationLock(lockPath) {
1153
+ const content = fs4.readFileSync(lockPath, "utf8");
1154
+ if (!isStaleCacheMutationLock(lockPath) || fs4.readFileSync(lockPath, "utf8") !== content) {
1155
+ throw createExistingLockError();
1156
+ }
1157
+ fs4.rmSync(lockPath, { force: true });
1158
+ }
1159
+ function removeOwnedCacheMutationLock(lockPath, token) {
1160
+ const lock = readCacheMutationLock(lockPath);
1161
+ if (lock?.token === token) {
1162
+ fs4.rmSync(lockPath, { force: true });
1163
+ }
1164
+ }
1165
+ function preflightCacheMutationPaths(cacheDir, paths) {
1166
+ for (const targetPath of paths) {
1167
+ assertNoSymlinkCachePath(targetPath, cacheDir);
1168
+ }
1169
+ }
1170
+ function isJsonObject(value) {
1171
+ return value !== null && typeof value === "object" && !Array.isArray(value);
1172
+ }
1173
+ function removeDependencyPin(manifest, section) {
1174
+ const dependencies = manifest[section];
1175
+ if (!isJsonObject(dependencies) || !(PLUGIN_NAME in dependencies)) {
1176
+ return false;
1177
+ }
1178
+ delete dependencies[PLUGIN_NAME];
1179
+ if (Object.keys(dependencies).length === 0) {
1180
+ delete manifest[section];
1181
+ }
1182
+ return true;
1183
+ }
1184
+ function removeManifestPin(cacheDir) {
1185
+ const manifestPath = path2.join(cacheDir, CACHE_MANIFEST);
1186
+ if (!cacheFileExists(manifestPath, cacheDir)) {
1187
+ return false;
1188
+ }
1189
+ const manifest = JSON.parse(fs4.readFileSync(manifestPath, "utf8"));
1190
+ if (!isJsonObject(manifest)) {
1191
+ throw new Error(`${CACHE_MANIFEST} must contain a JSON object`);
1192
+ }
1193
+ const removed = DEPENDENCY_SECTIONS.map((section) => removeDependencyPin(manifest, section)).some(Boolean);
1194
+ if (removed) {
1195
+ writeManifestAtomic(manifestPath, cacheDir, manifest);
1196
+ }
1197
+ return removed;
946
1198
  }
947
1199
  function runUpdate() {
948
1200
  const cacheDir = getCacheDir();
949
1201
  const nodeModulesDir = path2.join(cacheDir, "node_modules");
950
1202
  const pluginCacheDir = path2.join(nodeModulesDir, PLUGIN_NAME);
1203
+ const lockfilePath = path2.join(cacheDir, CACHE_LOCKFILE);
951
1204
  try {
952
- if (!fs4.existsSync(pluginCacheDir)) {
953
- if (fs4.existsSync(nodeModulesDir)) {
954
- rmdir(nodeModulesDir);
955
- return {
956
- success: true,
957
- message: "Cleared OpenCode plugin cache. The latest version will be installed on next run.",
958
- cleared: true
959
- };
960
- }
1205
+ if (!cacheDirectoryExists(cacheDir)) {
1206
+ return {
1207
+ success: true,
1208
+ message: "No cached plugin found. OpenCode will install the latest version on next run.",
1209
+ cleared: false
1210
+ };
1211
+ }
1212
+ preflightCacheMutationPaths(cacheDir, [
1213
+ path2.join(cacheDir, CACHE_MANIFEST),
1214
+ pluginCacheDir,
1215
+ lockfilePath,
1216
+ path2.join(cacheDir, CACHE_UPDATE_LOCK)
1217
+ ]);
1218
+ const cleared = withCacheMutationLock(cacheDir, () => {
1219
+ const manifestPinRemoved = removeManifestPin(cacheDir);
1220
+ const packageRemoved = removePathIfExists(pluginCacheDir, cacheDir);
1221
+ const lockfileRemoved = removePathIfExists(lockfilePath, cacheDir);
1222
+ return packageRemoved || manifestPinRemoved || lockfileRemoved;
1223
+ });
1224
+ if (!cleared) {
961
1225
  return {
962
1226
  success: true,
963
1227
  message: "No cached plugin found. OpenCode will install the latest version on next run.",
964
1228
  cleared: false
965
1229
  };
966
1230
  }
967
- rmdir(pluginCacheDir);
968
1231
  return {
969
1232
  success: true,
970
- message: "Cache cleared. OpenCode will install the latest version on next run.",
1233
+ message: "Cache repaired. OpenCode will install the latest version on next run.",
971
1234
  cleared: true
972
1235
  };
973
1236
  } catch (error) {
@@ -1056,7 +1319,24 @@ function findInstalledConfigs() {
1056
1319
  import { spawnSync } from "child_process";
1057
1320
 
1058
1321
  // src/cli/utils/parse.ts
1322
+ var SIGNED_INTEGER_PATTERN = /^-?\d+$/;
1323
+ var UNSIGNED_INTEGER_PATTERN = /^\d+$/;
1324
+ var SIGNED_DECIMAL_PATTERN = /^-?(?:\d+|\d+\.\d+|\.\d+)$/;
1325
+ var UNSIGNED_DECIMAL_PATTERN = /^(?:\d+|\d+\.\d+|\.\d+)$/;
1326
+ function allowsNegative(options) {
1327
+ return typeof options.min !== "number" || options.min < 0;
1328
+ }
1329
+ function decimalPattern(options) {
1330
+ const signed = allowsNegative(options);
1331
+ if (options.integer ?? true) {
1332
+ return signed ? SIGNED_INTEGER_PATTERN : UNSIGNED_INTEGER_PATTERN;
1333
+ }
1334
+ return signed ? SIGNED_DECIMAL_PATTERN : UNSIGNED_DECIMAL_PATTERN;
1335
+ }
1059
1336
  function parseNumberFlag(value, flag, options = {}) {
1337
+ if (value.trim() === "" || value !== value.trim() || !decimalPattern(options).test(value)) {
1338
+ throw createUsageError(`Invalid ${flag}: ${value}`);
1339
+ }
1060
1340
  const parsed = Number(value);
1061
1341
  if (!Number.isFinite(parsed)) {
1062
1342
  throw createUsageError(`Invalid ${flag}: ${value}`);
@@ -1927,9 +2207,9 @@ ${nativeMessage}${staleNote}` : `${baseMessage}${staleNote}`;
1927
2207
 
1928
2208
  // src/cli/daemon-autostart.ts
1929
2209
  import { execFileSync as execFileSync2 } from "child_process";
1930
- import { existsSync as existsSync5, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "fs";
2210
+ import { existsSync as existsSync5, mkdirSync as mkdirSync2, statSync as statSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync5 } from "fs";
1931
2211
  import { homedir as homedir4, tmpdir } from "os";
1932
- import { dirname as dirname3, isAbsolute, join as join5, relative, resolve as resolve3 } from "path";
2212
+ import { dirname as dirname3, isAbsolute as isAbsolute2, join as join5, relative as relative2, resolve as resolve3 } from "path";
1933
2213
  import { fileURLToPath as fileURLToPath2 } from "url";
1934
2214
  var MAC_LABEL = "com.opendevbrowser.daemon";
1935
2215
  var WIN_TASK_NAME = "OpenDevBrowser Daemon";
@@ -1942,7 +2222,8 @@ var defaultDeps = () => ({
1942
2222
  homedir: homedir4,
1943
2223
  existsSync: existsSync5,
1944
2224
  mkdirSync: mkdirSync2,
1945
- writeFileSync: writeFileSync4,
2225
+ statSync: statSync2,
2226
+ writeFileSync: writeFileSync5,
1946
2227
  unlinkSync: unlinkSync2,
1947
2228
  execFileSync: execFileSync2,
1948
2229
  transientEntrypointRoots: void 0
@@ -1951,6 +2232,17 @@ var NPX_CACHE_SEGMENT_PATTERN = /[\\/]_npx(?:[\\/]|$)/;
1951
2232
  var formatCommand = (programArguments) => {
1952
2233
  return programArguments.map((value) => `"${value}"`).join(" ");
1953
2234
  };
2235
+ var resolveMacWorkingDirectory = (home) => {
2236
+ return join5(home, ".cache", "opendevbrowser");
2237
+ };
2238
+ var pathIsDirectory = (path5, deps) => {
2239
+ if (!deps.existsSync(path5)) return false;
2240
+ try {
2241
+ return deps.statSync(path5).isDirectory();
2242
+ } catch {
2243
+ return false;
2244
+ }
2245
+ };
1954
2246
  var resolveCliPathFromModule = (moduleUrl, exists) => {
1955
2247
  const modulePath = fileURLToPath2(moduleUrl);
1956
2248
  const candidate = resolve3(dirname3(modulePath), "..", "index.js");
@@ -1966,8 +2258,8 @@ var normalizeComparisonPath = (value, platform) => {
1966
2258
  var isPathInsideRoot = (candidate, root, platform) => {
1967
2259
  const normalizedCandidate = normalizeComparisonPath(candidate, platform);
1968
2260
  const normalizedRoot = normalizeComparisonPath(root, platform);
1969
- const relation = relative(normalizedRoot, normalizedCandidate);
1970
- return relation === "" || !relation.startsWith("..") && !isAbsolute(relation);
2261
+ const relation = relative2(normalizedRoot, normalizedCandidate);
2262
+ return relation === "" || !relation.startsWith("..") && !isAbsolute2(relation);
1971
2263
  };
1972
2264
  var getTransientEntrypointRoots = (deps) => {
1973
2265
  const configuredRoots = deps.transientEntrypointRoots;
@@ -2013,18 +2305,20 @@ var resolveCliEntrypoint = (deps = {}) => {
2013
2305
  var getLaunchAgentPath = (home = homedir4()) => {
2014
2306
  return join5(home, "Library", "LaunchAgents", `${MAC_LABEL}.plist`);
2015
2307
  };
2308
+ var escapePlistString = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
2016
2309
  var buildLaunchAgentPlist = (entrypoint, options = {}) => {
2017
2310
  const label = options.label ?? MAC_LABEL;
2018
2311
  const stdoutPath = options.stdoutPath ?? join5(homedir4(), "Library", "Logs", "opendevbrowser-daemon.log");
2019
2312
  const stderrPath = options.stderrPath ?? join5(homedir4(), "Library", "Logs", "opendevbrowser-daemon.err.log");
2020
- const programArgs = [entrypoint.nodePath, ...entrypoint.args].map((value) => ` <string>${value}</string>`).join("\n");
2313
+ const workingDirectory = options.workingDirectory ?? resolveMacWorkingDirectory(homedir4());
2314
+ const programArgs = [entrypoint.nodePath, ...entrypoint.args].map((value) => ` <string>${escapePlistString(value)}</string>`).join("\n");
2021
2315
  return [
2022
2316
  '<?xml version="1.0" encoding="UTF-8"?>',
2023
2317
  '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
2024
2318
  '<plist version="1.0">',
2025
2319
  "<dict>",
2026
2320
  ` <key>Label</key>`,
2027
- ` <string>${label}</string>`,
2321
+ ` <string>${escapePlistString(label)}</string>`,
2028
2322
  " <key>ProgramArguments</key>",
2029
2323
  " <array>",
2030
2324
  programArgs,
@@ -2033,10 +2327,12 @@ var buildLaunchAgentPlist = (entrypoint, options = {}) => {
2033
2327
  " <true/>",
2034
2328
  " <key>KeepAlive</key>",
2035
2329
  " <true/>",
2330
+ " <key>WorkingDirectory</key>",
2331
+ ` <string>${escapePlistString(workingDirectory)}</string>`,
2036
2332
  " <key>StandardOutPath</key>",
2037
- ` <string>${stdoutPath}</string>`,
2333
+ ` <string>${escapePlistString(stdoutPath)}</string>`,
2038
2334
  " <key>StandardErrorPath</key>",
2039
- ` <string>${stderrPath}</string>`,
2335
+ ` <string>${escapePlistString(stderrPath)}</string>`,
2040
2336
  "</dict>",
2041
2337
  "</plist>",
2042
2338
  ""
@@ -2128,6 +2424,7 @@ var readMacLaunchAgentProgramArguments = (plistPath, deps) => {
2128
2424
  const text = deps.execFileSync("plutil", ["-convert", "json", "-o", "-", plistPath], { encoding: "utf-8" });
2129
2425
  const parsed = JSON.parse(text);
2130
2426
  const programArguments = parsed?.ProgramArguments;
2427
+ const workingDirectory = typeof parsed?.WorkingDirectory === "string" ? parsed.WorkingDirectory : void 0;
2131
2428
  if (!Array.isArray(programArguments) || programArguments.length < 2 || programArguments.some((value) => typeof value !== "string")) {
2132
2429
  return { ok: false, reason: "missing_program_arguments" };
2133
2430
  }
@@ -2135,7 +2432,8 @@ var readMacLaunchAgentProgramArguments = (plistPath, deps) => {
2135
2432
  return {
2136
2433
  ok: true,
2137
2434
  command: formatCommand(commandArgs),
2138
- programArguments: commandArgs
2435
+ programArguments: commandArgs,
2436
+ ...workingDirectory ? { workingDirectory } : {}
2139
2437
  };
2140
2438
  } catch {
2141
2439
  return { ok: false, reason: "malformed_plist" };
@@ -2151,11 +2449,13 @@ var classifyMacAutostartStatus = (entrypoint, location, deps) => {
2151
2449
  });
2152
2450
  }
2153
2451
  const parsed = readMacLaunchAgentProgramArguments(location, deps);
2452
+ const expectedWorkingDirectory = resolveMacWorkingDirectory(deps.homedir());
2154
2453
  if (!parsed.ok) {
2155
2454
  return createMacAutostartStatus(entrypoint, location, {
2156
2455
  installed: true,
2157
2456
  health: "malformed",
2158
2457
  needsRepair: true,
2458
+ expectedWorkingDirectory,
2159
2459
  reason: parsed.reason
2160
2460
  });
2161
2461
  }
@@ -2170,15 +2470,41 @@ var classifyMacAutostartStatus = (entrypoint, location, deps) => {
2170
2470
  health: "needs_repair",
2171
2471
  needsRepair: true,
2172
2472
  command: parsed.command,
2473
+ workingDirectory: parsed.workingDirectory,
2474
+ expectedWorkingDirectory,
2173
2475
  reason: actualStatus.reason
2174
2476
  });
2175
2477
  }
2478
+ if (parsed.workingDirectory !== expectedWorkingDirectory) {
2479
+ return createMacAutostartStatus(entrypoint, location, {
2480
+ installed: true,
2481
+ health: "needs_repair",
2482
+ needsRepair: true,
2483
+ command: parsed.command,
2484
+ workingDirectory: parsed.workingDirectory,
2485
+ expectedWorkingDirectory,
2486
+ reason: "working_directory_mismatch"
2487
+ });
2488
+ }
2489
+ if (!pathIsDirectory(expectedWorkingDirectory, deps)) {
2490
+ return createMacAutostartStatus(entrypoint, location, {
2491
+ installed: true,
2492
+ health: "needs_repair",
2493
+ needsRepair: true,
2494
+ command: parsed.command,
2495
+ workingDirectory: parsed.workingDirectory,
2496
+ expectedWorkingDirectory,
2497
+ reason: "working_directory_mismatch"
2498
+ });
2499
+ }
2176
2500
  if (entrypoint.isTransient) {
2177
2501
  return createMacAutostartStatus(entrypoint, location, {
2178
2502
  installed: true,
2179
2503
  health: "healthy",
2180
2504
  needsRepair: false,
2181
- command: parsed.command
2505
+ command: parsed.command,
2506
+ workingDirectory: parsed.workingDirectory,
2507
+ expectedWorkingDirectory
2182
2508
  });
2183
2509
  }
2184
2510
  const mismatchReason = classifyExpectedProgramArguments(expectedArgs, actualArgs, deps);
@@ -2188,6 +2514,8 @@ var classifyMacAutostartStatus = (entrypoint, location, deps) => {
2188
2514
  health: "needs_repair",
2189
2515
  needsRepair: true,
2190
2516
  command: parsed.command,
2517
+ workingDirectory: parsed.workingDirectory,
2518
+ expectedWorkingDirectory,
2191
2519
  reason: mismatchReason
2192
2520
  });
2193
2521
  }
@@ -2195,7 +2523,9 @@ var classifyMacAutostartStatus = (entrypoint, location, deps) => {
2195
2523
  installed: true,
2196
2524
  health: "healthy",
2197
2525
  needsRepair: false,
2198
- command: parsed.command
2526
+ command: parsed.command,
2527
+ workingDirectory: parsed.workingDirectory,
2528
+ expectedWorkingDirectory
2199
2529
  });
2200
2530
  };
2201
2531
  var runCommand = (exec, command, args, ignoreFailure = false) => {
@@ -2223,12 +2553,14 @@ var installMacAutostart = (deps = {}) => {
2223
2553
  const plistPath = getLaunchAgentPath(home);
2224
2554
  const stdoutPath = join5(home, "Library", "Logs", "opendevbrowser-daemon.log");
2225
2555
  const stderrPath = join5(home, "Library", "Logs", "opendevbrowser-daemon.err.log");
2556
+ const workingDirectory = resolveMacWorkingDirectory(home);
2226
2557
  const logsDir = dirname3(stdoutPath);
2227
2558
  resolved.mkdirSync(dirname3(plistPath), { recursive: true });
2228
2559
  resolved.mkdirSync(logsDir, { recursive: true });
2560
+ resolved.mkdirSync(workingDirectory, { recursive: true });
2229
2561
  resolved.writeFileSync(
2230
2562
  plistPath,
2231
- buildLaunchAgentPlist(entrypoint, { stdoutPath, stderrPath }),
2563
+ buildLaunchAgentPlist(entrypoint, { stdoutPath, stderrPath, workingDirectory }),
2232
2564
  { encoding: "utf-8" }
2233
2565
  );
2234
2566
  const uid = resolved.uid;
@@ -2240,7 +2572,9 @@ var installMacAutostart = (deps = {}) => {
2240
2572
  installed: true,
2241
2573
  health: "healthy",
2242
2574
  needsRepair: false,
2243
- command: entrypoint.command
2575
+ command: entrypoint.command,
2576
+ workingDirectory,
2577
+ expectedWorkingDirectory: workingDirectory
2244
2578
  });
2245
2579
  };
2246
2580
  var uninstallMacAutostart = (deps = {}) => {
@@ -2832,7 +3166,7 @@ function formatAutostartReconciliationMessage(result) {
2832
3166
  }
2833
3167
 
2834
3168
  // src/cli/commands/run.ts
2835
- import { readFileSync as readFileSync2 } from "fs";
3169
+ import { readFileSync as readFileSync3 } from "fs";
2836
3170
 
2837
3171
  // src/cli/output.ts
2838
3172
  var normalizeExitCode = (code) => {
@@ -2996,7 +3330,7 @@ async function runScriptCommand(args) {
2996
3330
  const outputOptions = { format: args.outputFormat, quiet: args.quiet };
2997
3331
  let scriptRaw = "";
2998
3332
  if (runArgs.scriptPath) {
2999
- scriptRaw = readFileSync2(runArgs.scriptPath, "utf-8");
3333
+ scriptRaw = readFileSync3(runArgs.scriptPath, "utf-8");
3000
3334
  } else if (!process.stdin.isTTY) {
3001
3335
  scriptRaw = await readScriptFromStdin();
3002
3336
  } else {
@@ -3356,6 +3690,11 @@ var readRunnableStepCommand = (step) => {
3356
3690
  if (!command) return null;
3357
3691
  return UNRESOLVED_COMMAND_PLACEHOLDER_RE.test(command) ? null : command;
3358
3692
  };
3693
+ var readDisplayableNextStep = (value) => {
3694
+ const text = readNonEmptyString(value);
3695
+ if (!text) return null;
3696
+ return UNRESOLVED_COMMAND_PLACEHOLDER_RE.test(text) ? null : text;
3697
+ };
3359
3698
  var readMeta = (data) => {
3360
3699
  return asRecord(asRecord(data)?.meta);
3361
3700
  };
@@ -3378,6 +3717,9 @@ var readPrimaryNextStep = (data) => {
3378
3717
  const nextStep = commands.find((entry) => typeof entry === "string" && entry.trim().length > 0);
3379
3718
  return nextStep?.trim() ?? null;
3380
3719
  };
3720
+ var inferPrimaryIssueNextStep = (data) => {
3721
+ return summarizePrimaryProviderIssue(readFailures(data))?.guidance?.recommendedNextCommands[0] ?? null;
3722
+ };
3381
3723
  var readFailures = (data) => {
3382
3724
  const meta = readMeta(data);
3383
3725
  if (!meta) return [];
@@ -3396,12 +3738,27 @@ var readSuggestedSteps = (data) => {
3396
3738
  }) : [];
3397
3739
  };
3398
3740
  var buildNextStepMessage = (message, nextStep) => {
3399
- return nextStep ? `${message} Next step: ${nextStep}` : message;
3741
+ const displayableNextStep = readDisplayableNextStep(nextStep);
3742
+ return displayableNextStep ? `${message} Next step: ${displayableNextStep}` : message;
3743
+ };
3744
+ var buildProviderFollowupErrorMessage = (message) => {
3745
+ const normalized = message.toLowerCase();
3746
+ if (normalized.includes("next step:")) return message;
3747
+ if (normalized.includes("requires login or an existing session")) {
3748
+ return buildNextStepMessage(
3749
+ message,
3750
+ "Reuse an authenticated browser session, import logged-in cookies, or use the provider sign-in flow."
3751
+ );
3752
+ }
3753
+ if (normalized.includes("requires manual browser follow-up") || normalized.includes("requires a live browser-rendered page")) {
3754
+ return buildNextStepMessage(message, "Retry with browser assistance or a headed browser session.");
3755
+ }
3756
+ return message;
3400
3757
  };
3401
3758
  var readSuggestedNextAction = (data) => {
3402
3759
  const record = asRecord(data);
3403
3760
  if (!record) return null;
3404
- return readNonEmptyString(record.suggestedNextAction) ?? readNonEmptyString(asRecord(record.sessionInspector)?.suggestedNextAction);
3761
+ return readDisplayableNextStep(record.suggestedNextAction) ?? readDisplayableNextStep(asRecord(record.sessionInspector)?.suggestedNextAction);
3405
3762
  };
3406
3763
  var readSuggestedStepCommand = (data) => {
3407
3764
  let current = asRecord(data);
@@ -3419,18 +3776,19 @@ var readSuggestedStepReason = (data) => {
3419
3776
  while (current) {
3420
3777
  const [firstStep] = readSuggestedSteps(current);
3421
3778
  if (firstStep) {
3422
- return readNonEmptyString(firstStep.reason);
3779
+ return readDisplayableNextStep(firstStep.reason);
3423
3780
  }
3424
3781
  current = asRecord(current.challengePlan);
3425
3782
  }
3426
3783
  return null;
3427
3784
  };
3785
+ var readWorkflowGuidanceNextStep = (data) => readSuggestedNextAction(data) ?? readSuggestedStepCommand(data) ?? readSuggestedStepReason(data);
3428
3786
  var buildWorkflowCompletionMessage = (workflowLabel, data) => {
3429
3787
  const explicitSummary = readPrimarySummary(data);
3430
3788
  if (explicitSummary) {
3431
3789
  return buildNextStepMessage(
3432
3790
  `${workflowLabel} completed with provider follow-up required: ${explicitSummary}`,
3433
- readPrimaryNextStep(data)
3791
+ readPrimaryNextStep(data) ?? inferPrimaryIssueNextStep(data)
3434
3792
  );
3435
3793
  }
3436
3794
  const inferred = summarizePrimaryProviderIssue(readFailures(data));
@@ -3444,7 +3802,7 @@ var buildWorkflowCompletionMessage = (workflowLabel, data) => {
3444
3802
  if (followthroughSummary) {
3445
3803
  return buildNextStepMessage(
3446
3804
  `${workflowLabel} completed. ${followthroughSummary}`,
3447
- readSuggestedNextAction(data) ?? readSuggestedStepCommand(data) ?? readSuggestedStepReason(data)
3805
+ readWorkflowGuidanceNextStep(data)
3448
3806
  );
3449
3807
  }
3450
3808
  return `${workflowLabel} completed.`;
@@ -3929,12 +4287,12 @@ function parseSnapshotArgs(rawArgs) {
3929
4287
  if (arg === "--max-chars") {
3930
4288
  const value = rawArgs[i + 1];
3931
4289
  if (!value) throw createUsageError("Missing value for --max-chars");
3932
- parsed.maxChars = Number(value);
4290
+ parsed.maxChars = parseNumberFlag(value, "--max-chars", { min: 1 });
3933
4291
  i += 1;
3934
4292
  continue;
3935
4293
  }
3936
4294
  if (arg?.startsWith("--max-chars=")) {
3937
- parsed.maxChars = Number(arg.split("=", 2)[1]);
4295
+ parsed.maxChars = parseNumberFlag(arg.split("=", 2)[1] ?? "", "--max-chars", { min: 1 });
3938
4296
  continue;
3939
4297
  }
3940
4298
  if (arg === "--cursor") {
@@ -4193,7 +4551,7 @@ async function runAnnotate(args) {
4193
4551
  }
4194
4552
 
4195
4553
  // src/cli/commands/canvas.ts
4196
- import { readFileSync as readFileSync3 } from "fs";
4554
+ import { readFileSync as readFileSync4 } from "fs";
4197
4555
  var DEFAULT_FEEDBACK_STREAM_TIMEOUT_MS = 3e4;
4198
4556
  var isRecord = (value) => {
4199
4557
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -4394,7 +4752,7 @@ var resolveCanvasParams = (canvasArgs) => {
4394
4752
  if (hasParamsFile) {
4395
4753
  let raw = "";
4396
4754
  try {
4397
- raw = readFileSync3(canvasArgs.paramsFile ?? "", "utf8");
4755
+ raw = readFileSync4(canvasArgs.paramsFile ?? "", "utf8");
4398
4756
  } catch (error) {
4399
4757
  const message = error instanceof Error ? error.message : "Unable to read file";
4400
4758
  throw createUsageError(`Invalid --params-file: ${message}`);
@@ -4463,7 +4821,7 @@ async function runCanvas(args) {
4463
4821
  }
4464
4822
 
4465
4823
  // src/cli/commands/rpc.ts
4466
- import { readFileSync as readFileSync4 } from "fs";
4824
+ import { readFileSync as readFileSync5 } from "fs";
4467
4825
  var requireValue4 = (value, flag) => {
4468
4826
  if (!value) throw createUsageError(`Missing value for ${flag}`);
4469
4827
  return value;
@@ -4543,7 +4901,7 @@ var resolveRpcParams = (rpcArgs) => {
4543
4901
  if (hasParamsFile) {
4544
4902
  let raw = "";
4545
4903
  try {
4546
- raw = readFileSync4(rpcArgs.paramsFile ?? "", "utf8");
4904
+ raw = readFileSync5(rpcArgs.paramsFile ?? "", "utf8");
4547
4905
  } catch (error) {
4548
4906
  const message = error instanceof Error ? error.message : "Unable to read file";
4549
4907
  throw createUsageError(`Invalid --params-file: ${message}`);
@@ -4974,12 +5332,12 @@ function parseScrollArgs(rawArgs) {
4974
5332
  if (arg === "--dy") {
4975
5333
  const value = rawArgs[i + 1];
4976
5334
  if (!value) throw createUsageError("Missing value for --dy");
4977
- parsed.dy = Number(value);
5335
+ parsed.dy = parseNumberFlag(value, "--dy");
4978
5336
  i += 1;
4979
5337
  continue;
4980
5338
  }
4981
5339
  if (arg?.startsWith("--dy=")) {
4982
- parsed.dy = Number(arg.split("=", 2)[1]);
5340
+ parsed.dy = parseNumberFlag(arg.split("=", 2)[1] ?? "", "--dy");
4983
5341
  continue;
4984
5342
  }
4985
5343
  }
@@ -5475,12 +5833,12 @@ function parseDomHtmlArgs(rawArgs) {
5475
5833
  if (arg === "--max-chars") {
5476
5834
  const value = rawArgs[i + 1];
5477
5835
  if (!value) throw createUsageError("Missing value for --max-chars");
5478
- parsed.maxChars = Number(value);
5836
+ parsed.maxChars = parseNumberFlag(value, "--max-chars", { min: 1 });
5479
5837
  i += 1;
5480
5838
  continue;
5481
5839
  }
5482
5840
  if (arg?.startsWith("--max-chars=")) {
5483
- parsed.maxChars = Number(arg.split("=", 2)[1]);
5841
+ parsed.maxChars = parseNumberFlag(arg.split("=", 2)[1] ?? "", "--max-chars", { min: 1 });
5484
5842
  continue;
5485
5843
  }
5486
5844
  }
@@ -5530,12 +5888,12 @@ function parseDomTextArgs(rawArgs) {
5530
5888
  if (arg === "--max-chars") {
5531
5889
  const value = rawArgs[i + 1];
5532
5890
  if (!value) throw createUsageError("Missing value for --max-chars");
5533
- parsed.maxChars = Number(value);
5891
+ parsed.maxChars = parseNumberFlag(value, "--max-chars", { min: 1 });
5534
5892
  i += 1;
5535
5893
  continue;
5536
5894
  }
5537
5895
  if (arg?.startsWith("--max-chars=")) {
5538
- parsed.maxChars = Number(arg.split("=", 2)[1]);
5896
+ parsed.maxChars = parseNumberFlag(arg.split("=", 2)[1] ?? "", "--max-chars", { min: 1 });
5539
5897
  continue;
5540
5898
  }
5541
5899
  }
@@ -6050,23 +6408,23 @@ function parseConsolePollArgs(rawArgs) {
6050
6408
  if (arg === "--since-seq") {
6051
6409
  const value = rawArgs[i + 1];
6052
6410
  if (!value) throw createUsageError("Missing value for --since-seq");
6053
- parsed.sinceSeq = Number(value);
6411
+ parsed.sinceSeq = parseNumberFlag(value, "--since-seq", { min: 0 });
6054
6412
  i += 1;
6055
6413
  continue;
6056
6414
  }
6057
6415
  if (arg?.startsWith("--since-seq=")) {
6058
- parsed.sinceSeq = Number(arg.split("=", 2)[1]);
6416
+ parsed.sinceSeq = parseNumberFlag(arg.split("=", 2)[1] ?? "", "--since-seq", { min: 0 });
6059
6417
  continue;
6060
6418
  }
6061
6419
  if (arg === "--max") {
6062
6420
  const value = rawArgs[i + 1];
6063
6421
  if (!value) throw createUsageError("Missing value for --max");
6064
- parsed.max = Number(value);
6422
+ parsed.max = parseNumberFlag(value, "--max", { min: 1 });
6065
6423
  i += 1;
6066
6424
  continue;
6067
6425
  }
6068
6426
  if (arg?.startsWith("--max=")) {
6069
- parsed.max = Number(arg.split("=", 2)[1]);
6427
+ parsed.max = parseNumberFlag(arg.split("=", 2)[1] ?? "", "--max", { min: 1 });
6070
6428
  continue;
6071
6429
  }
6072
6430
  }
@@ -6098,23 +6456,23 @@ function parseNetworkPollArgs(rawArgs) {
6098
6456
  if (arg === "--since-seq") {
6099
6457
  const value = rawArgs[i + 1];
6100
6458
  if (!value) throw createUsageError("Missing value for --since-seq");
6101
- parsed.sinceSeq = Number(value);
6459
+ parsed.sinceSeq = parseNumberFlag(value, "--since-seq", { min: 0 });
6102
6460
  i += 1;
6103
6461
  continue;
6104
6462
  }
6105
6463
  if (arg?.startsWith("--since-seq=")) {
6106
- parsed.sinceSeq = Number(arg.split("=", 2)[1]);
6464
+ parsed.sinceSeq = parseNumberFlag(arg.split("=", 2)[1] ?? "", "--since-seq", { min: 0 });
6107
6465
  continue;
6108
6466
  }
6109
6467
  if (arg === "--max") {
6110
6468
  const value = rawArgs[i + 1];
6111
6469
  if (!value) throw createUsageError("Missing value for --max");
6112
- parsed.max = Number(value);
6470
+ parsed.max = parseNumberFlag(value, "--max", { min: 1 });
6113
6471
  i += 1;
6114
6472
  continue;
6115
6473
  }
6116
6474
  if (arg?.startsWith("--max=")) {
6117
- parsed.max = Number(arg.split("=", 2)[1]);
6475
+ parsed.max = parseNumberFlag(arg.split("=", 2)[1] ?? "", "--max", { min: 1 });
6118
6476
  continue;
6119
6477
  }
6120
6478
  }
@@ -6326,7 +6684,7 @@ async function runDesktopAccessibilitySnapshot(args) {
6326
6684
  }
6327
6685
 
6328
6686
  // src/cli/commands/session/cookie-import.ts
6329
- import { readFileSync as readFileSync5 } from "fs";
6687
+ import { readFileSync as readFileSync6 } from "fs";
6330
6688
  var requireValue5 = (value, flag) => {
6331
6689
  if (!value) {
6332
6690
  throw createUsageError(`Missing value for ${flag}`);
@@ -6441,7 +6799,7 @@ var resolveCookies = (parsed) => {
6441
6799
  }
6442
6800
  let raw = "";
6443
6801
  try {
6444
- raw = readFileSync5(parsed.cookiesFile ?? "", "utf8");
6802
+ raw = readFileSync6(parsed.cookiesFile ?? "", "utf8");
6445
6803
  } catch (error) {
6446
6804
  const message = error instanceof Error ? error.message : "Unable to read file";
6447
6805
  throw createUsageError(`Invalid --cookies-file: ${message}`);
@@ -6560,6 +6918,7 @@ async function runCookieList(args) {
6560
6918
 
6561
6919
  // src/cli/commands/macro-resolve.ts
6562
6920
  var MACRO_TRANSPORT_TIMEOUT_BUFFER_MS = 6e4;
6921
+ var BROWSER_MODE_VALUES = /* @__PURE__ */ new Set(["auto", "extension", "managed"]);
6563
6922
  var deriveMacroTransportTimeoutMs = (timeoutMs) => {
6564
6923
  return Math.max(
6565
6924
  DEFAULT_WORKFLOW_TRANSPORT_TIMEOUT_MS,
@@ -6578,27 +6937,16 @@ var asRecord2 = (value) => {
6578
6937
  }
6579
6938
  return value;
6580
6939
  };
6581
- var readNonEmptyString2 = (value) => typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
6582
6940
  var hasExecutionBlocker = (result) => {
6583
6941
  const execution = asRecord2(asRecord2(result)?.execution);
6584
6942
  const meta = asRecord2(execution?.meta);
6585
6943
  return asRecord2(meta?.blocker) !== null;
6586
6944
  };
6587
- var readMacroResolveSummary = (result) => readNonEmptyString2(asRecord2(result)?.followthroughSummary);
6588
- var readMacroResolveNextStep = (result) => {
6589
- const record = asRecord2(result);
6590
- const explicit = readNonEmptyString2(record?.suggestedNextAction);
6591
- if (explicit) {
6592
- return explicit;
6593
- }
6594
- const [firstStep] = Array.isArray(record?.suggestedSteps) ? record.suggestedSteps.filter((step) => Boolean(step) && typeof step === "object") : [];
6595
- return readNonEmptyString2(firstStep?.command) ?? readNonEmptyString2(firstStep?.reason);
6596
- };
6597
6945
  var buildMacroResolveMessage = (execute, result) => {
6598
- const summary = readMacroResolveSummary(result);
6599
- const nextStep = readMacroResolveNextStep(result);
6946
+ const summary = readFollowthroughSummary(result);
6947
+ const nextStep = readWorkflowGuidanceNextStep(result);
6600
6948
  if (summary) {
6601
- return nextStep ? `${summary} Next step: ${nextStep}` : summary;
6949
+ return buildNextStepMessage(summary, nextStep);
6602
6950
  }
6603
6951
  if (!execute) {
6604
6952
  return "Macro resolved.";
@@ -6648,6 +6996,23 @@ var parseMacroResolveArgs = (rawArgs) => {
6648
6996
  parsed.timeoutMs = parseNumberFlag(requireValue7(arg.split("=", 2)[1], "--timeout-ms"), "--timeout-ms", { min: 1 });
6649
6997
  continue;
6650
6998
  }
6999
+ if (arg === "--browser-mode") {
7000
+ const value = requireValue7(rawArgs[index + 1], "--browser-mode").toLowerCase();
7001
+ if (!BROWSER_MODE_VALUES.has(value)) {
7002
+ throw createUsageError(`Invalid --browser-mode: ${value}`);
7003
+ }
7004
+ parsed.browserMode = value;
7005
+ index += 1;
7006
+ continue;
7007
+ }
7008
+ if (arg?.startsWith("--browser-mode=")) {
7009
+ const value = requireValue7(arg.split("=", 2)[1], "--browser-mode").toLowerCase();
7010
+ if (!BROWSER_MODE_VALUES.has(value)) {
7011
+ throw createUsageError(`Invalid --browser-mode: ${value}`);
7012
+ }
7013
+ parsed.browserMode = value;
7014
+ continue;
7015
+ }
6651
7016
  if (arg === "--challenge-automation-mode") {
6652
7017
  const value = requireValue7(rawArgs[index + 1], "--challenge-automation-mode");
6653
7018
  if (!isChallengeAutomationMode(value)) {
@@ -6673,12 +7038,19 @@ async function runMacroResolve(args) {
6673
7038
  if (!parsed.expression) {
6674
7039
  throw createUsageError("Missing --expression");
6675
7040
  }
7041
+ if (!parsed.execute && parsed.browserMode) {
7042
+ throw createUsageError("--browser-mode requires --execute for macro-resolve");
7043
+ }
7044
+ if (!parsed.execute && parsed.challengeAutomationMode) {
7045
+ throw createUsageError("--challenge-automation-mode requires --execute for macro-resolve");
7046
+ }
6676
7047
  const params = {
6677
7048
  expression: parsed.expression,
6678
7049
  defaultProvider: parsed.defaultProvider,
6679
7050
  includeCatalog: parsed.includeCatalog ?? false,
6680
7051
  execute: parsed.execute ?? false,
6681
7052
  ...typeof parsed.timeoutMs === "number" ? { timeoutMs: parsed.timeoutMs } : {},
7053
+ ...parsed.browserMode ? { browserMode: parsed.browserMode } : {},
6682
7054
  ...parsed.challengeAutomationMode ? { challengeAutomationMode: parsed.challengeAutomationMode } : {}
6683
7055
  };
6684
7056
  const result = typeof parsed.timeoutMs === "number" ? await callDaemon("macro.resolve", params, {
@@ -6695,6 +7067,7 @@ async function runMacroResolve(args) {
6695
7067
  var SOURCE_VALUES = /* @__PURE__ */ new Set(["web", "community", "social", "shopping"]);
6696
7068
  var SOURCE_SELECTION_VALUES = /* @__PURE__ */ new Set(["auto", "web", "community", "social", "shopping", "all"]);
6697
7069
  var MODE_VALUES = /* @__PURE__ */ new Set(["compact", "json", "md", "context", "path"]);
7070
+ var BROWSER_MODE_VALUES2 = /* @__PURE__ */ new Set(["auto", "extension", "managed"]);
6698
7071
  var COOKIE_POLICY_VALUES = /* @__PURE__ */ new Set(["off", "auto", "required"]);
6699
7072
  var requireValue8 = (rawArgs, index, flag) => {
6700
7073
  const value = rawArgs[index + 1];
@@ -6844,6 +7217,23 @@ var parseResearchRunArgs = (rawArgs) => {
6844
7217
  parsed.ttlHours = parseNumberFlag(arg.split("=", 2)[1] ?? "", "--ttl-hours", { min: 1, max: 168 });
6845
7218
  continue;
6846
7219
  }
7220
+ if (arg === "--browser-mode") {
7221
+ const value = requireValue8(rawArgs, index, "--browser-mode").toLowerCase();
7222
+ if (!BROWSER_MODE_VALUES2.has(value)) {
7223
+ throw createUsageError(`Invalid --browser-mode: ${value}`);
7224
+ }
7225
+ parsed.browserMode = value;
7226
+ index += 1;
7227
+ continue;
7228
+ }
7229
+ if (arg?.startsWith("--browser-mode=")) {
7230
+ const value = (arg.split("=", 2)[1] ?? "").toLowerCase();
7231
+ if (!BROWSER_MODE_VALUES2.has(value)) {
7232
+ throw createUsageError(`Invalid --browser-mode: ${value}`);
7233
+ }
7234
+ parsed.browserMode = value;
7235
+ continue;
7236
+ }
6847
7237
  if (arg === "--use-cookies") {
6848
7238
  parsed.useCookies = true;
6849
7239
  continue;
@@ -6911,6 +7301,7 @@ async function runResearchCommand(args) {
6911
7301
  timeoutMs: parsed.timeoutMs ?? DEFAULT_WORKFLOW_TRANSPORT_TIMEOUT_MS,
6912
7302
  outputDir: parsed.outputDir,
6913
7303
  ttlHours: parsed.ttlHours,
7304
+ browserMode: parsed.browserMode,
6914
7305
  useCookies: parsed.useCookies,
6915
7306
  challengeAutomationMode: parsed.challengeAutomationMode,
6916
7307
  cookiePolicyOverride: parsed.cookiePolicyOverride
@@ -6927,7 +7318,7 @@ async function runResearchCommand(args) {
6927
7318
  var SORT_VALUES = /* @__PURE__ */ new Set(["best_deal", "lowest_price", "highest_rating", "fastest_shipping"]);
6928
7319
  var MODE_VALUES2 = /* @__PURE__ */ new Set(["compact", "json", "md", "context", "path"]);
6929
7320
  var COOKIE_POLICY_VALUES2 = /* @__PURE__ */ new Set(["off", "auto", "required"]);
6930
- var BROWSER_MODE_VALUES = /* @__PURE__ */ new Set(["auto", "extension", "managed"]);
7321
+ var BROWSER_MODE_VALUES3 = /* @__PURE__ */ new Set(["auto", "extension", "managed"]);
6931
7322
  var SHOPPING_TRANSPORT_TIMEOUT_BUFFER_MS = 6e4;
6932
7323
  var deriveShoppingTransportTimeoutMs = (timeoutMs) => {
6933
7324
  return Math.max(
@@ -6996,7 +7387,7 @@ var parseShoppingRunArgs = (rawArgs) => {
6996
7387
  }
6997
7388
  if (arg === "--browser-mode") {
6998
7389
  const value = requireValue9(rawArgs, index, "--browser-mode").toLowerCase();
6999
- if (!BROWSER_MODE_VALUES.has(value)) {
7390
+ if (!BROWSER_MODE_VALUES3.has(value)) {
7000
7391
  throw createUsageError(`Invalid --browser-mode: ${value}`);
7001
7392
  }
7002
7393
  parsed.browserMode = value;
@@ -7005,7 +7396,7 @@ var parseShoppingRunArgs = (rawArgs) => {
7005
7396
  }
7006
7397
  if (arg?.startsWith("--browser-mode=")) {
7007
7398
  const value = (arg.split("=", 2)[1] ?? "").toLowerCase();
7008
- if (!BROWSER_MODE_VALUES.has(value)) {
7399
+ if (!BROWSER_MODE_VALUES3.has(value)) {
7009
7400
  throw createUsageError(`Invalid --browser-mode: ${value}`);
7010
7401
  }
7011
7402
  parsed.browserMode = value;
@@ -7165,6 +7556,7 @@ var parseBoolean3 = (value, flag) => {
7165
7556
  throw createUsageError(`Invalid ${flag}: ${value}`);
7166
7557
  };
7167
7558
  var COOKIE_POLICY_VALUES3 = /* @__PURE__ */ new Set(["off", "auto", "required"]);
7559
+ var BROWSER_MODE_VALUES4 = /* @__PURE__ */ new Set(["auto", "extension", "managed"]);
7168
7560
  var parseProductVideoArgs = (rawArgs) => {
7169
7561
  const parsed = {};
7170
7562
  for (let index = 0; index < rawArgs.length; index += 1) {
@@ -7247,6 +7639,23 @@ var parseProductVideoArgs = (rawArgs) => {
7247
7639
  parsed.timeoutMs = parseNumberFlag(arg.split("=", 2)[1] ?? "", "--timeout-ms", { min: 1 });
7248
7640
  continue;
7249
7641
  }
7642
+ if (arg === "--browser-mode") {
7643
+ const value = requireValue10(rawArgs, index, "--browser-mode").toLowerCase();
7644
+ if (!BROWSER_MODE_VALUES4.has(value)) {
7645
+ throw createUsageError(`Invalid --browser-mode: ${value}`);
7646
+ }
7647
+ parsed.browserMode = value;
7648
+ index += 1;
7649
+ continue;
7650
+ }
7651
+ if (arg?.startsWith("--browser-mode=")) {
7652
+ const value = (arg.split("=", 2)[1] ?? "").toLowerCase();
7653
+ if (!BROWSER_MODE_VALUES4.has(value)) {
7654
+ throw createUsageError(`Invalid --browser-mode: ${value}`);
7655
+ }
7656
+ parsed.browserMode = value;
7657
+ continue;
7658
+ }
7250
7659
  if (arg === "--use-cookies") {
7251
7660
  parsed.useCookies = true;
7252
7661
  continue;
@@ -7312,6 +7721,7 @@ async function runProductVideoCommand(args) {
7312
7721
  output_dir: parsed.outputDir,
7313
7722
  ttl_hours: parsed.ttlHours,
7314
7723
  timeoutMs,
7724
+ browserMode: parsed.browserMode,
7315
7725
  useCookies: parsed.useCookies,
7316
7726
  challengeAutomationMode: parsed.challengeAutomationMode,
7317
7727
  cookiePolicyOverride: parsed.cookiePolicyOverride
@@ -7327,6 +7737,7 @@ async function runProductVideoCommand(args) {
7327
7737
  var MODE_VALUES3 = /* @__PURE__ */ new Set(["compact", "json", "md", "context", "path"]);
7328
7738
  var CAPTURE_MODE_VALUES = /* @__PURE__ */ new Set(["off", "deep"]);
7329
7739
  var COOKIE_POLICY_VALUES4 = /* @__PURE__ */ new Set(["off", "auto", "required"]);
7740
+ var BROWSER_MODE_VALUES5 = /* @__PURE__ */ new Set(["auto", "extension", "managed"]);
7330
7741
  var requireValue11 = (rawArgs, index, flag) => {
7331
7742
  const value = rawArgs[index + 1];
7332
7743
  if (!value) {
@@ -7417,6 +7828,23 @@ var parseInspiredesignRunArgs = (rawArgs) => {
7417
7828
  parsed.ttlHours = parseNumberFlag(arg.split("=", 2)[1] ?? "", "--ttl-hours", { min: 1, max: 168 });
7418
7829
  continue;
7419
7830
  }
7831
+ if (arg === "--browser-mode") {
7832
+ const value = requireValue11(rawArgs, index, "--browser-mode").toLowerCase();
7833
+ if (!BROWSER_MODE_VALUES5.has(value)) {
7834
+ throw createUsageError(`Invalid --browser-mode: ${value}`);
7835
+ }
7836
+ parsed.browserMode = value;
7837
+ index += 1;
7838
+ continue;
7839
+ }
7840
+ if (arg?.startsWith("--browser-mode=")) {
7841
+ const value = (arg.split("=", 2)[1] ?? "").toLowerCase();
7842
+ if (!BROWSER_MODE_VALUES5.has(value)) {
7843
+ throw createUsageError(`Invalid --browser-mode: ${value}`);
7844
+ }
7845
+ parsed.browserMode = value;
7846
+ continue;
7847
+ }
7420
7848
  if (arg === "--use-cookies") {
7421
7849
  parsed.useCookies = true;
7422
7850
  continue;
@@ -7480,6 +7908,7 @@ async function runInspiredesignCommand(args) {
7480
7908
  timeoutMs: parsed.timeoutMs ?? DEFAULT_WORKFLOW_TRANSPORT_TIMEOUT_MS,
7481
7909
  outputDir: parsed.outputDir,
7482
7910
  ttlHours: parsed.ttlHours,
7911
+ browserMode: parsed.browserMode,
7483
7912
  useCookies: parsed.useCookies,
7484
7913
  challengeAutomationMode: parsed.challengeAutomationMode,
7485
7914
  cookiePolicyOverride: parsed.cookiePolicyOverride
@@ -7494,7 +7923,7 @@ async function runInspiredesignCommand(args) {
7494
7923
  // package.json
7495
7924
  var package_default = {
7496
7925
  name: "opendevbrowser",
7497
- version: "0.0.26",
7926
+ version: "0.0.28",
7498
7927
  description: "Browser automation runtime with snapshot-refs-actions, browser replay screencasts, public read-only desktop observation, and browser-scoped computer-use orchestration",
7499
7928
  type: "module",
7500
7929
  main: "dist/index.js",
@@ -7776,14 +8205,15 @@ async function promptUninstallMode() {
7776
8205
  });
7777
8206
  }
7778
8207
  function emitFatalError(error, outputFormat) {
8208
+ const message = buildProviderFollowupErrorMessage(error.message);
7779
8209
  if (outputFormat === "text") {
7780
- console.error(`Error: ${error.message}`);
8210
+ console.error(`Error: ${message}`);
7781
8211
  if (error.exitCode === EXIT_USAGE) {
7782
8212
  console.error("\nFor help: npx opendevbrowser --help");
7783
8213
  }
7784
8214
  return;
7785
8215
  }
7786
- writeOutput(formatErrorPayload(error), { format: outputFormat });
8216
+ writeOutput({ ...formatErrorPayload(error), error: message }, { format: outputFormat });
7787
8217
  }
7788
8218
  async function main() {
7789
8219
  let outputFormat = null;
@@ -7828,7 +8258,7 @@ async function main() {
7828
8258
  });
7829
8259
  registerCommand({
7830
8260
  name: "update",
7831
- description: "Clear cached plugin and refresh managed skill packs",
8261
+ description: "Repair cached plugin pins and refresh managed skill packs",
7832
8262
  run: () => buildUpdateCommandResult(args, runUpdate(), {
7833
8263
  resolveUpdateSkillModes,
7834
8264
  hasInstalledConfig,