opendevbrowser 0.0.26 → 0.0.27

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 (60) 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/types.d.ts +3 -0
  5. package/dist/canvas/types.d.ts.map +1 -1
  6. package/dist/{chunk-GTTYIAI7.js → chunk-MWBDO2L5.js} +95 -17
  7. package/dist/chunk-MWBDO2L5.js.map +1 -0
  8. package/dist/{chunk-AVQL6WAS.js → chunk-V5DJUSPV.js} +891 -114
  9. package/dist/chunk-V5DJUSPV.js.map +1 -0
  10. package/dist/cli/args.d.ts.map +1 -1
  11. package/dist/cli/commands/daemon.d.ts +2 -0
  12. package/dist/cli/commands/daemon.d.ts.map +1 -1
  13. package/dist/cli/commands/devtools/console-poll.d.ts.map +1 -1
  14. package/dist/cli/commands/devtools/network-poll.d.ts.map +1 -1
  15. package/dist/cli/commands/update.d.ts.map +1 -1
  16. package/dist/cli/daemon-autostart.d.ts +6 -2
  17. package/dist/cli/daemon-autostart.d.ts.map +1 -1
  18. package/dist/cli/index.js +394 -61
  19. package/dist/cli/index.js.map +1 -1
  20. package/dist/cli/utils/parse.d.ts.map +1 -1
  21. package/dist/daemon-fingerprint.json +1 -1
  22. package/dist/index.js +6 -6
  23. package/dist/index.js.map +1 -1
  24. package/dist/inspiredesign/brief-expansion.d.ts +3 -0
  25. package/dist/inspiredesign/brief-expansion.d.ts.map +1 -1
  26. package/dist/{providers/inspiredesign-capture-mode.d.ts → inspiredesign/capture-mode.d.ts} +2 -2
  27. package/dist/inspiredesign/capture-mode.d.ts.map +1 -0
  28. package/dist/{providers/inspiredesign-capture.d.ts → inspiredesign/capture.d.ts} +3 -3
  29. package/dist/inspiredesign/capture.d.ts.map +1 -0
  30. package/dist/{providers/inspiredesign-contract.d.ts → inspiredesign/contract.d.ts} +18 -5
  31. package/dist/inspiredesign/contract.d.ts.map +1 -0
  32. package/dist/inspiredesign/handoff.d.ts +1 -11
  33. package/dist/inspiredesign/handoff.d.ts.map +1 -1
  34. package/dist/inspiredesign/reference-pattern-board.d.ts +73 -0
  35. package/dist/inspiredesign/reference-pattern-board.d.ts.map +1 -0
  36. package/dist/opendevbrowser.js +6 -6
  37. package/dist/opendevbrowser.js.map +1 -1
  38. package/dist/providers/renderer.d.ts +1 -1
  39. package/dist/providers/renderer.d.ts.map +1 -1
  40. package/dist/providers/workflows.d.ts +7 -5
  41. package/dist/providers/workflows.d.ts.map +1 -1
  42. package/dist/{providers-T2FQJCF6.js → providers-TR3DUJZV.js} +2 -2
  43. package/dist/public-surface/generated-manifest.d.ts +3 -3
  44. package/dist/public-surface/generated-manifest.d.ts.map +1 -1
  45. package/dist/public-surface/source.d.ts +5 -4
  46. package/dist/public-surface/source.d.ts.map +1 -1
  47. package/dist/relay/protocol.d.ts +14 -2
  48. package/dist/relay/protocol.d.ts.map +1 -1
  49. package/extension/dist/canvas/canvas-runtime.js +13 -6
  50. package/extension/dist/services/ConnectionManager.js +8 -4
  51. package/extension/manifest.json +1 -1
  52. package/package.json +1 -1
  53. package/skills/opendevbrowser-best-practices/assets/templates/skill-runtime-pack-matrix.json +1 -1
  54. package/skills/opendevbrowser-design-agent/assets/templates/inspiredesign-advanced-brief.v1.json +67 -31
  55. package/dist/chunk-AVQL6WAS.js.map +0 -1
  56. package/dist/chunk-GTTYIAI7.js.map +0 -1
  57. package/dist/providers/inspiredesign-capture-mode.d.ts.map +0 -1
  58. package/dist/providers/inspiredesign-capture.d.ts.map +0 -1
  59. package/dist/providers/inspiredesign-contract.d.ts.map +0 -1
  60. /package/dist/{providers-T2FQJCF6.js.map → providers-TR3DUJZV.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-MWBDO2L5.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-V5DJUSPV.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",
@@ -929,45 +944,292 @@ function installLocal(withConfig = false) {
929
944
  import * as fs4 from "fs";
930
945
  import * as path2 from "path";
931
946
  import * as os2 from "os";
947
+ import { randomUUID } from "crypto";
932
948
  var PLUGIN_NAME = "opendevbrowser";
949
+ var CACHE_MANIFEST = "package.json";
950
+ var CACHE_LOCKFILE = "package-lock.json";
951
+ var CACHE_UPDATE_LOCK = ".opendevbrowser-update.lock";
952
+ var CACHE_LOCK_STALE_MS = 30 * 60 * 1e3;
953
+ var DEPENDENCY_SECTIONS = [
954
+ "dependencies",
955
+ "devDependencies",
956
+ "optionalDependencies",
957
+ "peerDependencies"
958
+ ];
933
959
  function getCacheDir() {
934
960
  return process.env.OPENCODE_CACHE_DIR || path2.join(os2.homedir(), ".cache", "opencode");
935
961
  }
936
- function rmdir(dirPath) {
937
- const cacheDir = getCacheDir();
962
+ function assertCacheChild(targetPath, cacheDir) {
963
+ const resolvedCache = path2.resolve(cacheDir);
964
+ const resolvedPath = path2.resolve(targetPath);
965
+ const relativePath = path2.relative(resolvedCache, resolvedPath);
966
+ if (!relativePath || relativePath.startsWith("..") || path2.isAbsolute(relativePath)) {
967
+ throw new Error(`Security: refusing to modify path outside cache directory: ${targetPath}`);
968
+ }
969
+ }
970
+ function isMissingPathError(error) {
971
+ return error instanceof Error && "code" in error && error.code === "ENOENT";
972
+ }
973
+ function isExistingPathError(error) {
974
+ return error instanceof Error && "code" in error && error.code === "EEXIST";
975
+ }
976
+ function assertNoSymlinkCachePath(targetPath, cacheDir) {
977
+ assertCacheChild(targetPath, cacheDir);
938
978
  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}`);
979
+ const relativeSegments = path2.relative(resolvedCache, path2.resolve(targetPath)).split(path2.sep).filter(Boolean);
980
+ for (const candidate of [resolvedCache, ...relativeSegments.map((_, index) => path2.join(resolvedCache, ...relativeSegments.slice(0, index + 1)))]) {
981
+ try {
982
+ if (fs4.lstatSync(candidate).isSymbolicLink()) {
983
+ throw new Error(`Security: refusing to modify symlinked cache path: ${candidate}`);
984
+ }
985
+ } catch (error) {
986
+ if (isMissingPathError(error)) {
987
+ continue;
988
+ }
989
+ throw error;
990
+ }
991
+ }
992
+ }
993
+ function cacheDirectoryExists(cacheDir) {
994
+ try {
995
+ const stat = fs4.lstatSync(cacheDir);
996
+ if (stat.isSymbolicLink()) {
997
+ throw new Error(`Security: refusing to modify symlinked cache path: ${cacheDir}`);
998
+ }
999
+ if (!stat.isDirectory()) {
1000
+ throw new Error(`${cacheDir} must be a directory`);
1001
+ }
1002
+ return true;
1003
+ } catch (error) {
1004
+ if (isMissingPathError(error)) {
1005
+ return false;
1006
+ }
1007
+ throw error;
1008
+ }
1009
+ }
1010
+ function removePathIfExists(targetPath, cacheDir) {
1011
+ assertNoSymlinkCachePath(targetPath, cacheDir);
1012
+ if (!fs4.existsSync(targetPath)) {
1013
+ return false;
1014
+ }
1015
+ fs4.rmSync(targetPath, { recursive: true, force: true });
1016
+ return true;
1017
+ }
1018
+ function cacheFileExists(targetPath, cacheDir) {
1019
+ assertNoSymlinkCachePath(targetPath, cacheDir);
1020
+ try {
1021
+ const stat = fs4.lstatSync(targetPath);
1022
+ if (stat.isSymbolicLink()) {
1023
+ throw new Error(`Security: refusing to modify symlinked cache manifest: ${targetPath}`);
1024
+ }
1025
+ return stat.isFile();
1026
+ } catch (error) {
1027
+ if (isMissingPathError(error)) {
1028
+ return false;
1029
+ }
1030
+ throw error;
1031
+ }
1032
+ }
1033
+ function writeManifestAtomic(manifestPath, cacheDir, manifest) {
1034
+ const tempPath = `${manifestPath}.${process.pid}.tmp`;
1035
+ assertNoSymlinkCachePath(tempPath, cacheDir);
1036
+ let fd = null;
1037
+ try {
1038
+ fd = fs4.openSync(tempPath, "wx");
1039
+ fs4.writeFileSync(fd, `${JSON.stringify(manifest, null, 2)}
1040
+ `, "utf8");
1041
+ fs4.closeSync(fd);
1042
+ fd = null;
1043
+ fs4.renameSync(tempPath, manifestPath);
1044
+ } catch (error) {
1045
+ if (fd !== null) {
1046
+ fs4.closeSync(fd);
1047
+ }
1048
+ fs4.rmSync(tempPath, { force: true });
1049
+ throw error;
1050
+ }
1051
+ }
1052
+ function withCacheMutationLock(cacheDir, action) {
1053
+ const lockPath = path2.join(cacheDir, CACHE_UPDATE_LOCK);
1054
+ assertNoSymlinkCachePath(lockPath, cacheDir);
1055
+ let lock = null;
1056
+ try {
1057
+ lock = openCacheMutationLock(lockPath);
1058
+ return action();
1059
+ } catch (error) {
1060
+ if (isExistingPathError(error)) {
1061
+ throw new Error("another update is already running for this OpenCode cache");
1062
+ }
1063
+ throw error;
1064
+ } finally {
1065
+ if (lock !== null) {
1066
+ fs4.closeSync(lock.fd);
1067
+ removeOwnedCacheMutationLock(lockPath, lock.token);
1068
+ }
1069
+ }
1070
+ }
1071
+ function openCacheMutationLock(lockPath) {
1072
+ try {
1073
+ return writeCacheMutationLock(lockPath);
1074
+ } catch (error) {
1075
+ if (!isExistingPathError(error) || !isStaleCacheMutationLock(lockPath)) {
1076
+ throw error;
1077
+ }
1078
+ removeStaleCacheMutationLock(lockPath);
1079
+ return writeCacheMutationLock(lockPath);
1080
+ }
1081
+ }
1082
+ function writeCacheMutationLock(lockPath) {
1083
+ const fd = fs4.openSync(lockPath, "wx");
1084
+ const token = randomUUID();
1085
+ try {
1086
+ const lock = { pid: process.pid, createdAt: Date.now(), token };
1087
+ fs4.writeFileSync(fd, `${JSON.stringify(lock)}
1088
+ `, "utf8");
1089
+ return { fd, token };
1090
+ } catch (error) {
1091
+ fs4.closeSync(fd);
1092
+ fs4.rmSync(lockPath, { force: true });
1093
+ throw error;
1094
+ }
1095
+ }
1096
+ function isStaleCacheMutationLock(lockPath) {
1097
+ const lock = readCacheMutationLock(lockPath);
1098
+ if (!lock) {
1099
+ return isLegacyCacheMutationLockStale(lockPath);
942
1100
  }
943
- if (fs4.existsSync(dirPath)) {
944
- fs4.rmSync(dirPath, { recursive: true, force: true });
1101
+ if (Date.now() - lock.createdAt < CACHE_LOCK_STALE_MS) {
1102
+ return false;
1103
+ }
1104
+ return !isProcessRunning(lock.pid);
1105
+ }
1106
+ function isLegacyCacheMutationLockStale(lockPath) {
1107
+ try {
1108
+ const pid = readLegacyCacheMutationLockPid(lockPath);
1109
+ if (pid !== null && isProcessRunning(pid)) {
1110
+ return false;
1111
+ }
1112
+ return Date.now() - fs4.statSync(lockPath).mtimeMs >= CACHE_LOCK_STALE_MS;
1113
+ } catch {
1114
+ return false;
945
1115
  }
946
1116
  }
1117
+ function readLegacyCacheMutationLockPid(lockPath) {
1118
+ const raw = fs4.readFileSync(lockPath, "utf8").trim();
1119
+ if (!/^\d+$/.test(raw)) {
1120
+ return null;
1121
+ }
1122
+ const pid = Number(raw);
1123
+ return Number.isSafeInteger(pid) && pid > 0 ? pid : null;
1124
+ }
1125
+ function readCacheMutationLock(lockPath) {
1126
+ try {
1127
+ const parsed = JSON.parse(fs4.readFileSync(lockPath, "utf8"));
1128
+ const { pid, createdAt, token } = parsed;
1129
+ const hasToken = token === void 0 || typeof token === "string" && token.length > 0;
1130
+ if (typeof pid === "number" && typeof createdAt === "number" && hasToken && Number.isInteger(pid) && Number.isFinite(createdAt)) {
1131
+ return token === void 0 ? { pid, createdAt } : { pid, createdAt, token };
1132
+ }
1133
+ } catch {
1134
+ return null;
1135
+ }
1136
+ return null;
1137
+ }
1138
+ function isProcessRunning(pid) {
1139
+ try {
1140
+ process.kill(pid, 0);
1141
+ return true;
1142
+ } catch (error) {
1143
+ return error instanceof Error && "code" in error && error.code === "EPERM";
1144
+ }
1145
+ }
1146
+ function createExistingLockError() {
1147
+ const error = new Error("cache update lock changed before stale cleanup");
1148
+ error.code = "EEXIST";
1149
+ return error;
1150
+ }
1151
+ function removeStaleCacheMutationLock(lockPath) {
1152
+ const content = fs4.readFileSync(lockPath, "utf8");
1153
+ if (!isStaleCacheMutationLock(lockPath) || fs4.readFileSync(lockPath, "utf8") !== content) {
1154
+ throw createExistingLockError();
1155
+ }
1156
+ fs4.rmSync(lockPath, { force: true });
1157
+ }
1158
+ function removeOwnedCacheMutationLock(lockPath, token) {
1159
+ const lock = readCacheMutationLock(lockPath);
1160
+ if (lock?.token === token) {
1161
+ fs4.rmSync(lockPath, { force: true });
1162
+ }
1163
+ }
1164
+ function preflightCacheMutationPaths(cacheDir, paths) {
1165
+ for (const targetPath of paths) {
1166
+ assertNoSymlinkCachePath(targetPath, cacheDir);
1167
+ }
1168
+ }
1169
+ function isJsonObject(value) {
1170
+ return value !== null && typeof value === "object" && !Array.isArray(value);
1171
+ }
1172
+ function removeDependencyPin(manifest, section) {
1173
+ const dependencies = manifest[section];
1174
+ if (!isJsonObject(dependencies) || !(PLUGIN_NAME in dependencies)) {
1175
+ return false;
1176
+ }
1177
+ delete dependencies[PLUGIN_NAME];
1178
+ if (Object.keys(dependencies).length === 0) {
1179
+ delete manifest[section];
1180
+ }
1181
+ return true;
1182
+ }
1183
+ function removeManifestPin(cacheDir) {
1184
+ const manifestPath = path2.join(cacheDir, CACHE_MANIFEST);
1185
+ if (!cacheFileExists(manifestPath, cacheDir)) {
1186
+ return false;
1187
+ }
1188
+ const manifest = JSON.parse(fs4.readFileSync(manifestPath, "utf8"));
1189
+ if (!isJsonObject(manifest)) {
1190
+ throw new Error(`${CACHE_MANIFEST} must contain a JSON object`);
1191
+ }
1192
+ const removed = DEPENDENCY_SECTIONS.map((section) => removeDependencyPin(manifest, section)).some(Boolean);
1193
+ if (removed) {
1194
+ writeManifestAtomic(manifestPath, cacheDir, manifest);
1195
+ }
1196
+ return removed;
1197
+ }
947
1198
  function runUpdate() {
948
1199
  const cacheDir = getCacheDir();
949
1200
  const nodeModulesDir = path2.join(cacheDir, "node_modules");
950
1201
  const pluginCacheDir = path2.join(nodeModulesDir, PLUGIN_NAME);
1202
+ const lockfilePath = path2.join(cacheDir, CACHE_LOCKFILE);
951
1203
  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
- }
1204
+ if (!cacheDirectoryExists(cacheDir)) {
1205
+ return {
1206
+ success: true,
1207
+ message: "No cached plugin found. OpenCode will install the latest version on next run.",
1208
+ cleared: false
1209
+ };
1210
+ }
1211
+ preflightCacheMutationPaths(cacheDir, [
1212
+ path2.join(cacheDir, CACHE_MANIFEST),
1213
+ pluginCacheDir,
1214
+ lockfilePath,
1215
+ path2.join(cacheDir, CACHE_UPDATE_LOCK)
1216
+ ]);
1217
+ const cleared = withCacheMutationLock(cacheDir, () => {
1218
+ const manifestPinRemoved = removeManifestPin(cacheDir);
1219
+ const packageRemoved = removePathIfExists(pluginCacheDir, cacheDir);
1220
+ const lockfileRemoved = removePathIfExists(lockfilePath, cacheDir);
1221
+ return packageRemoved || manifestPinRemoved || lockfileRemoved;
1222
+ });
1223
+ if (!cleared) {
961
1224
  return {
962
1225
  success: true,
963
1226
  message: "No cached plugin found. OpenCode will install the latest version on next run.",
964
1227
  cleared: false
965
1228
  };
966
1229
  }
967
- rmdir(pluginCacheDir);
968
1230
  return {
969
1231
  success: true,
970
- message: "Cache cleared. OpenCode will install the latest version on next run.",
1232
+ message: "Cache repaired. OpenCode will install the latest version on next run.",
971
1233
  cleared: true
972
1234
  };
973
1235
  } catch (error) {
@@ -1056,7 +1318,24 @@ function findInstalledConfigs() {
1056
1318
  import { spawnSync } from "child_process";
1057
1319
 
1058
1320
  // src/cli/utils/parse.ts
1321
+ var SIGNED_INTEGER_PATTERN = /^-?\d+$/;
1322
+ var UNSIGNED_INTEGER_PATTERN = /^\d+$/;
1323
+ var SIGNED_DECIMAL_PATTERN = /^-?(?:\d+|\d+\.\d+|\.\d+)$/;
1324
+ var UNSIGNED_DECIMAL_PATTERN = /^(?:\d+|\d+\.\d+|\.\d+)$/;
1325
+ function allowsNegative(options) {
1326
+ return typeof options.min !== "number" || options.min < 0;
1327
+ }
1328
+ function decimalPattern(options) {
1329
+ const signed = allowsNegative(options);
1330
+ if (options.integer ?? true) {
1331
+ return signed ? SIGNED_INTEGER_PATTERN : UNSIGNED_INTEGER_PATTERN;
1332
+ }
1333
+ return signed ? SIGNED_DECIMAL_PATTERN : UNSIGNED_DECIMAL_PATTERN;
1334
+ }
1059
1335
  function parseNumberFlag(value, flag, options = {}) {
1336
+ if (value.trim() === "" || value !== value.trim() || !decimalPattern(options).test(value)) {
1337
+ throw createUsageError(`Invalid ${flag}: ${value}`);
1338
+ }
1060
1339
  const parsed = Number(value);
1061
1340
  if (!Number.isFinite(parsed)) {
1062
1341
  throw createUsageError(`Invalid ${flag}: ${value}`);
@@ -1927,9 +2206,9 @@ ${nativeMessage}${staleNote}` : `${baseMessage}${staleNote}`;
1927
2206
 
1928
2207
  // src/cli/daemon-autostart.ts
1929
2208
  import { execFileSync as execFileSync2 } from "child_process";
1930
- import { existsSync as existsSync5, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "fs";
2209
+ import { existsSync as existsSync5, mkdirSync as mkdirSync2, statSync as statSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync5 } from "fs";
1931
2210
  import { homedir as homedir4, tmpdir } from "os";
1932
- import { dirname as dirname3, isAbsolute, join as join5, relative, resolve as resolve3 } from "path";
2211
+ import { dirname as dirname3, isAbsolute as isAbsolute2, join as join5, relative as relative2, resolve as resolve3 } from "path";
1933
2212
  import { fileURLToPath as fileURLToPath2 } from "url";
1934
2213
  var MAC_LABEL = "com.opendevbrowser.daemon";
1935
2214
  var WIN_TASK_NAME = "OpenDevBrowser Daemon";
@@ -1942,7 +2221,8 @@ var defaultDeps = () => ({
1942
2221
  homedir: homedir4,
1943
2222
  existsSync: existsSync5,
1944
2223
  mkdirSync: mkdirSync2,
1945
- writeFileSync: writeFileSync4,
2224
+ statSync: statSync2,
2225
+ writeFileSync: writeFileSync5,
1946
2226
  unlinkSync: unlinkSync2,
1947
2227
  execFileSync: execFileSync2,
1948
2228
  transientEntrypointRoots: void 0
@@ -1951,6 +2231,17 @@ var NPX_CACHE_SEGMENT_PATTERN = /[\\/]_npx(?:[\\/]|$)/;
1951
2231
  var formatCommand = (programArguments) => {
1952
2232
  return programArguments.map((value) => `"${value}"`).join(" ");
1953
2233
  };
2234
+ var resolveMacWorkingDirectory = (home) => {
2235
+ return join5(home, ".cache", "opendevbrowser");
2236
+ };
2237
+ var pathIsDirectory = (path5, deps) => {
2238
+ if (!deps.existsSync(path5)) return false;
2239
+ try {
2240
+ return deps.statSync(path5).isDirectory();
2241
+ } catch {
2242
+ return false;
2243
+ }
2244
+ };
1954
2245
  var resolveCliPathFromModule = (moduleUrl, exists) => {
1955
2246
  const modulePath = fileURLToPath2(moduleUrl);
1956
2247
  const candidate = resolve3(dirname3(modulePath), "..", "index.js");
@@ -1966,8 +2257,8 @@ var normalizeComparisonPath = (value, platform) => {
1966
2257
  var isPathInsideRoot = (candidate, root, platform) => {
1967
2258
  const normalizedCandidate = normalizeComparisonPath(candidate, platform);
1968
2259
  const normalizedRoot = normalizeComparisonPath(root, platform);
1969
- const relation = relative(normalizedRoot, normalizedCandidate);
1970
- return relation === "" || !relation.startsWith("..") && !isAbsolute(relation);
2260
+ const relation = relative2(normalizedRoot, normalizedCandidate);
2261
+ return relation === "" || !relation.startsWith("..") && !isAbsolute2(relation);
1971
2262
  };
1972
2263
  var getTransientEntrypointRoots = (deps) => {
1973
2264
  const configuredRoots = deps.transientEntrypointRoots;
@@ -2013,18 +2304,20 @@ var resolveCliEntrypoint = (deps = {}) => {
2013
2304
  var getLaunchAgentPath = (home = homedir4()) => {
2014
2305
  return join5(home, "Library", "LaunchAgents", `${MAC_LABEL}.plist`);
2015
2306
  };
2307
+ var escapePlistString = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
2016
2308
  var buildLaunchAgentPlist = (entrypoint, options = {}) => {
2017
2309
  const label = options.label ?? MAC_LABEL;
2018
2310
  const stdoutPath = options.stdoutPath ?? join5(homedir4(), "Library", "Logs", "opendevbrowser-daemon.log");
2019
2311
  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");
2312
+ const workingDirectory = options.workingDirectory ?? resolveMacWorkingDirectory(homedir4());
2313
+ const programArgs = [entrypoint.nodePath, ...entrypoint.args].map((value) => ` <string>${escapePlistString(value)}</string>`).join("\n");
2021
2314
  return [
2022
2315
  '<?xml version="1.0" encoding="UTF-8"?>',
2023
2316
  '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
2024
2317
  '<plist version="1.0">',
2025
2318
  "<dict>",
2026
2319
  ` <key>Label</key>`,
2027
- ` <string>${label}</string>`,
2320
+ ` <string>${escapePlistString(label)}</string>`,
2028
2321
  " <key>ProgramArguments</key>",
2029
2322
  " <array>",
2030
2323
  programArgs,
@@ -2033,10 +2326,12 @@ var buildLaunchAgentPlist = (entrypoint, options = {}) => {
2033
2326
  " <true/>",
2034
2327
  " <key>KeepAlive</key>",
2035
2328
  " <true/>",
2329
+ " <key>WorkingDirectory</key>",
2330
+ ` <string>${escapePlistString(workingDirectory)}</string>`,
2036
2331
  " <key>StandardOutPath</key>",
2037
- ` <string>${stdoutPath}</string>`,
2332
+ ` <string>${escapePlistString(stdoutPath)}</string>`,
2038
2333
  " <key>StandardErrorPath</key>",
2039
- ` <string>${stderrPath}</string>`,
2334
+ ` <string>${escapePlistString(stderrPath)}</string>`,
2040
2335
  "</dict>",
2041
2336
  "</plist>",
2042
2337
  ""
@@ -2128,6 +2423,7 @@ var readMacLaunchAgentProgramArguments = (plistPath, deps) => {
2128
2423
  const text = deps.execFileSync("plutil", ["-convert", "json", "-o", "-", plistPath], { encoding: "utf-8" });
2129
2424
  const parsed = JSON.parse(text);
2130
2425
  const programArguments = parsed?.ProgramArguments;
2426
+ const workingDirectory = typeof parsed?.WorkingDirectory === "string" ? parsed.WorkingDirectory : void 0;
2131
2427
  if (!Array.isArray(programArguments) || programArguments.length < 2 || programArguments.some((value) => typeof value !== "string")) {
2132
2428
  return { ok: false, reason: "missing_program_arguments" };
2133
2429
  }
@@ -2135,7 +2431,8 @@ var readMacLaunchAgentProgramArguments = (plistPath, deps) => {
2135
2431
  return {
2136
2432
  ok: true,
2137
2433
  command: formatCommand(commandArgs),
2138
- programArguments: commandArgs
2434
+ programArguments: commandArgs,
2435
+ ...workingDirectory ? { workingDirectory } : {}
2139
2436
  };
2140
2437
  } catch {
2141
2438
  return { ok: false, reason: "malformed_plist" };
@@ -2151,11 +2448,13 @@ var classifyMacAutostartStatus = (entrypoint, location, deps) => {
2151
2448
  });
2152
2449
  }
2153
2450
  const parsed = readMacLaunchAgentProgramArguments(location, deps);
2451
+ const expectedWorkingDirectory = resolveMacWorkingDirectory(deps.homedir());
2154
2452
  if (!parsed.ok) {
2155
2453
  return createMacAutostartStatus(entrypoint, location, {
2156
2454
  installed: true,
2157
2455
  health: "malformed",
2158
2456
  needsRepair: true,
2457
+ expectedWorkingDirectory,
2159
2458
  reason: parsed.reason
2160
2459
  });
2161
2460
  }
@@ -2170,15 +2469,41 @@ var classifyMacAutostartStatus = (entrypoint, location, deps) => {
2170
2469
  health: "needs_repair",
2171
2470
  needsRepair: true,
2172
2471
  command: parsed.command,
2472
+ workingDirectory: parsed.workingDirectory,
2473
+ expectedWorkingDirectory,
2173
2474
  reason: actualStatus.reason
2174
2475
  });
2175
2476
  }
2477
+ if (parsed.workingDirectory !== expectedWorkingDirectory) {
2478
+ return createMacAutostartStatus(entrypoint, location, {
2479
+ installed: true,
2480
+ health: "needs_repair",
2481
+ needsRepair: true,
2482
+ command: parsed.command,
2483
+ workingDirectory: parsed.workingDirectory,
2484
+ expectedWorkingDirectory,
2485
+ reason: "working_directory_mismatch"
2486
+ });
2487
+ }
2488
+ if (!pathIsDirectory(expectedWorkingDirectory, deps)) {
2489
+ return createMacAutostartStatus(entrypoint, location, {
2490
+ installed: true,
2491
+ health: "needs_repair",
2492
+ needsRepair: true,
2493
+ command: parsed.command,
2494
+ workingDirectory: parsed.workingDirectory,
2495
+ expectedWorkingDirectory,
2496
+ reason: "working_directory_mismatch"
2497
+ });
2498
+ }
2176
2499
  if (entrypoint.isTransient) {
2177
2500
  return createMacAutostartStatus(entrypoint, location, {
2178
2501
  installed: true,
2179
2502
  health: "healthy",
2180
2503
  needsRepair: false,
2181
- command: parsed.command
2504
+ command: parsed.command,
2505
+ workingDirectory: parsed.workingDirectory,
2506
+ expectedWorkingDirectory
2182
2507
  });
2183
2508
  }
2184
2509
  const mismatchReason = classifyExpectedProgramArguments(expectedArgs, actualArgs, deps);
@@ -2188,6 +2513,8 @@ var classifyMacAutostartStatus = (entrypoint, location, deps) => {
2188
2513
  health: "needs_repair",
2189
2514
  needsRepair: true,
2190
2515
  command: parsed.command,
2516
+ workingDirectory: parsed.workingDirectory,
2517
+ expectedWorkingDirectory,
2191
2518
  reason: mismatchReason
2192
2519
  });
2193
2520
  }
@@ -2195,7 +2522,9 @@ var classifyMacAutostartStatus = (entrypoint, location, deps) => {
2195
2522
  installed: true,
2196
2523
  health: "healthy",
2197
2524
  needsRepair: false,
2198
- command: parsed.command
2525
+ command: parsed.command,
2526
+ workingDirectory: parsed.workingDirectory,
2527
+ expectedWorkingDirectory
2199
2528
  });
2200
2529
  };
2201
2530
  var runCommand = (exec, command, args, ignoreFailure = false) => {
@@ -2223,12 +2552,14 @@ var installMacAutostart = (deps = {}) => {
2223
2552
  const plistPath = getLaunchAgentPath(home);
2224
2553
  const stdoutPath = join5(home, "Library", "Logs", "opendevbrowser-daemon.log");
2225
2554
  const stderrPath = join5(home, "Library", "Logs", "opendevbrowser-daemon.err.log");
2555
+ const workingDirectory = resolveMacWorkingDirectory(home);
2226
2556
  const logsDir = dirname3(stdoutPath);
2227
2557
  resolved.mkdirSync(dirname3(plistPath), { recursive: true });
2228
2558
  resolved.mkdirSync(logsDir, { recursive: true });
2559
+ resolved.mkdirSync(workingDirectory, { recursive: true });
2229
2560
  resolved.writeFileSync(
2230
2561
  plistPath,
2231
- buildLaunchAgentPlist(entrypoint, { stdoutPath, stderrPath }),
2562
+ buildLaunchAgentPlist(entrypoint, { stdoutPath, stderrPath, workingDirectory }),
2232
2563
  { encoding: "utf-8" }
2233
2564
  );
2234
2565
  const uid = resolved.uid;
@@ -2240,7 +2571,9 @@ var installMacAutostart = (deps = {}) => {
2240
2571
  installed: true,
2241
2572
  health: "healthy",
2242
2573
  needsRepair: false,
2243
- command: entrypoint.command
2574
+ command: entrypoint.command,
2575
+ workingDirectory,
2576
+ expectedWorkingDirectory: workingDirectory
2244
2577
  });
2245
2578
  };
2246
2579
  var uninstallMacAutostart = (deps = {}) => {
@@ -2832,7 +3165,7 @@ function formatAutostartReconciliationMessage(result) {
2832
3165
  }
2833
3166
 
2834
3167
  // src/cli/commands/run.ts
2835
- import { readFileSync as readFileSync2 } from "fs";
3168
+ import { readFileSync as readFileSync3 } from "fs";
2836
3169
 
2837
3170
  // src/cli/output.ts
2838
3171
  var normalizeExitCode = (code) => {
@@ -2996,7 +3329,7 @@ async function runScriptCommand(args) {
2996
3329
  const outputOptions = { format: args.outputFormat, quiet: args.quiet };
2997
3330
  let scriptRaw = "";
2998
3331
  if (runArgs.scriptPath) {
2999
- scriptRaw = readFileSync2(runArgs.scriptPath, "utf-8");
3332
+ scriptRaw = readFileSync3(runArgs.scriptPath, "utf-8");
3000
3333
  } else if (!process.stdin.isTTY) {
3001
3334
  scriptRaw = await readScriptFromStdin();
3002
3335
  } else {
@@ -3929,12 +4262,12 @@ function parseSnapshotArgs(rawArgs) {
3929
4262
  if (arg === "--max-chars") {
3930
4263
  const value = rawArgs[i + 1];
3931
4264
  if (!value) throw createUsageError("Missing value for --max-chars");
3932
- parsed.maxChars = Number(value);
4265
+ parsed.maxChars = parseNumberFlag(value, "--max-chars", { min: 1 });
3933
4266
  i += 1;
3934
4267
  continue;
3935
4268
  }
3936
4269
  if (arg?.startsWith("--max-chars=")) {
3937
- parsed.maxChars = Number(arg.split("=", 2)[1]);
4270
+ parsed.maxChars = parseNumberFlag(arg.split("=", 2)[1] ?? "", "--max-chars", { min: 1 });
3938
4271
  continue;
3939
4272
  }
3940
4273
  if (arg === "--cursor") {
@@ -4193,7 +4526,7 @@ async function runAnnotate(args) {
4193
4526
  }
4194
4527
 
4195
4528
  // src/cli/commands/canvas.ts
4196
- import { readFileSync as readFileSync3 } from "fs";
4529
+ import { readFileSync as readFileSync4 } from "fs";
4197
4530
  var DEFAULT_FEEDBACK_STREAM_TIMEOUT_MS = 3e4;
4198
4531
  var isRecord = (value) => {
4199
4532
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -4394,7 +4727,7 @@ var resolveCanvasParams = (canvasArgs) => {
4394
4727
  if (hasParamsFile) {
4395
4728
  let raw = "";
4396
4729
  try {
4397
- raw = readFileSync3(canvasArgs.paramsFile ?? "", "utf8");
4730
+ raw = readFileSync4(canvasArgs.paramsFile ?? "", "utf8");
4398
4731
  } catch (error) {
4399
4732
  const message = error instanceof Error ? error.message : "Unable to read file";
4400
4733
  throw createUsageError(`Invalid --params-file: ${message}`);
@@ -4463,7 +4796,7 @@ async function runCanvas(args) {
4463
4796
  }
4464
4797
 
4465
4798
  // src/cli/commands/rpc.ts
4466
- import { readFileSync as readFileSync4 } from "fs";
4799
+ import { readFileSync as readFileSync5 } from "fs";
4467
4800
  var requireValue4 = (value, flag) => {
4468
4801
  if (!value) throw createUsageError(`Missing value for ${flag}`);
4469
4802
  return value;
@@ -4543,7 +4876,7 @@ var resolveRpcParams = (rpcArgs) => {
4543
4876
  if (hasParamsFile) {
4544
4877
  let raw = "";
4545
4878
  try {
4546
- raw = readFileSync4(rpcArgs.paramsFile ?? "", "utf8");
4879
+ raw = readFileSync5(rpcArgs.paramsFile ?? "", "utf8");
4547
4880
  } catch (error) {
4548
4881
  const message = error instanceof Error ? error.message : "Unable to read file";
4549
4882
  throw createUsageError(`Invalid --params-file: ${message}`);
@@ -4974,12 +5307,12 @@ function parseScrollArgs(rawArgs) {
4974
5307
  if (arg === "--dy") {
4975
5308
  const value = rawArgs[i + 1];
4976
5309
  if (!value) throw createUsageError("Missing value for --dy");
4977
- parsed.dy = Number(value);
5310
+ parsed.dy = parseNumberFlag(value, "--dy");
4978
5311
  i += 1;
4979
5312
  continue;
4980
5313
  }
4981
5314
  if (arg?.startsWith("--dy=")) {
4982
- parsed.dy = Number(arg.split("=", 2)[1]);
5315
+ parsed.dy = parseNumberFlag(arg.split("=", 2)[1] ?? "", "--dy");
4983
5316
  continue;
4984
5317
  }
4985
5318
  }
@@ -5475,12 +5808,12 @@ function parseDomHtmlArgs(rawArgs) {
5475
5808
  if (arg === "--max-chars") {
5476
5809
  const value = rawArgs[i + 1];
5477
5810
  if (!value) throw createUsageError("Missing value for --max-chars");
5478
- parsed.maxChars = Number(value);
5811
+ parsed.maxChars = parseNumberFlag(value, "--max-chars", { min: 1 });
5479
5812
  i += 1;
5480
5813
  continue;
5481
5814
  }
5482
5815
  if (arg?.startsWith("--max-chars=")) {
5483
- parsed.maxChars = Number(arg.split("=", 2)[1]);
5816
+ parsed.maxChars = parseNumberFlag(arg.split("=", 2)[1] ?? "", "--max-chars", { min: 1 });
5484
5817
  continue;
5485
5818
  }
5486
5819
  }
@@ -5530,12 +5863,12 @@ function parseDomTextArgs(rawArgs) {
5530
5863
  if (arg === "--max-chars") {
5531
5864
  const value = rawArgs[i + 1];
5532
5865
  if (!value) throw createUsageError("Missing value for --max-chars");
5533
- parsed.maxChars = Number(value);
5866
+ parsed.maxChars = parseNumberFlag(value, "--max-chars", { min: 1 });
5534
5867
  i += 1;
5535
5868
  continue;
5536
5869
  }
5537
5870
  if (arg?.startsWith("--max-chars=")) {
5538
- parsed.maxChars = Number(arg.split("=", 2)[1]);
5871
+ parsed.maxChars = parseNumberFlag(arg.split("=", 2)[1] ?? "", "--max-chars", { min: 1 });
5539
5872
  continue;
5540
5873
  }
5541
5874
  }
@@ -6050,23 +6383,23 @@ function parseConsolePollArgs(rawArgs) {
6050
6383
  if (arg === "--since-seq") {
6051
6384
  const value = rawArgs[i + 1];
6052
6385
  if (!value) throw createUsageError("Missing value for --since-seq");
6053
- parsed.sinceSeq = Number(value);
6386
+ parsed.sinceSeq = parseNumberFlag(value, "--since-seq", { min: 0 });
6054
6387
  i += 1;
6055
6388
  continue;
6056
6389
  }
6057
6390
  if (arg?.startsWith("--since-seq=")) {
6058
- parsed.sinceSeq = Number(arg.split("=", 2)[1]);
6391
+ parsed.sinceSeq = parseNumberFlag(arg.split("=", 2)[1] ?? "", "--since-seq", { min: 0 });
6059
6392
  continue;
6060
6393
  }
6061
6394
  if (arg === "--max") {
6062
6395
  const value = rawArgs[i + 1];
6063
6396
  if (!value) throw createUsageError("Missing value for --max");
6064
- parsed.max = Number(value);
6397
+ parsed.max = parseNumberFlag(value, "--max", { min: 1 });
6065
6398
  i += 1;
6066
6399
  continue;
6067
6400
  }
6068
6401
  if (arg?.startsWith("--max=")) {
6069
- parsed.max = Number(arg.split("=", 2)[1]);
6402
+ parsed.max = parseNumberFlag(arg.split("=", 2)[1] ?? "", "--max", { min: 1 });
6070
6403
  continue;
6071
6404
  }
6072
6405
  }
@@ -6098,23 +6431,23 @@ function parseNetworkPollArgs(rawArgs) {
6098
6431
  if (arg === "--since-seq") {
6099
6432
  const value = rawArgs[i + 1];
6100
6433
  if (!value) throw createUsageError("Missing value for --since-seq");
6101
- parsed.sinceSeq = Number(value);
6434
+ parsed.sinceSeq = parseNumberFlag(value, "--since-seq", { min: 0 });
6102
6435
  i += 1;
6103
6436
  continue;
6104
6437
  }
6105
6438
  if (arg?.startsWith("--since-seq=")) {
6106
- parsed.sinceSeq = Number(arg.split("=", 2)[1]);
6439
+ parsed.sinceSeq = parseNumberFlag(arg.split("=", 2)[1] ?? "", "--since-seq", { min: 0 });
6107
6440
  continue;
6108
6441
  }
6109
6442
  if (arg === "--max") {
6110
6443
  const value = rawArgs[i + 1];
6111
6444
  if (!value) throw createUsageError("Missing value for --max");
6112
- parsed.max = Number(value);
6445
+ parsed.max = parseNumberFlag(value, "--max", { min: 1 });
6113
6446
  i += 1;
6114
6447
  continue;
6115
6448
  }
6116
6449
  if (arg?.startsWith("--max=")) {
6117
- parsed.max = Number(arg.split("=", 2)[1]);
6450
+ parsed.max = parseNumberFlag(arg.split("=", 2)[1] ?? "", "--max", { min: 1 });
6118
6451
  continue;
6119
6452
  }
6120
6453
  }
@@ -6326,7 +6659,7 @@ async function runDesktopAccessibilitySnapshot(args) {
6326
6659
  }
6327
6660
 
6328
6661
  // src/cli/commands/session/cookie-import.ts
6329
- import { readFileSync as readFileSync5 } from "fs";
6662
+ import { readFileSync as readFileSync6 } from "fs";
6330
6663
  var requireValue5 = (value, flag) => {
6331
6664
  if (!value) {
6332
6665
  throw createUsageError(`Missing value for ${flag}`);
@@ -6441,7 +6774,7 @@ var resolveCookies = (parsed) => {
6441
6774
  }
6442
6775
  let raw = "";
6443
6776
  try {
6444
- raw = readFileSync5(parsed.cookiesFile ?? "", "utf8");
6777
+ raw = readFileSync6(parsed.cookiesFile ?? "", "utf8");
6445
6778
  } catch (error) {
6446
6779
  const message = error instanceof Error ? error.message : "Unable to read file";
6447
6780
  throw createUsageError(`Invalid --cookies-file: ${message}`);
@@ -7494,7 +7827,7 @@ async function runInspiredesignCommand(args) {
7494
7827
  // package.json
7495
7828
  var package_default = {
7496
7829
  name: "opendevbrowser",
7497
- version: "0.0.26",
7830
+ version: "0.0.27",
7498
7831
  description: "Browser automation runtime with snapshot-refs-actions, browser replay screencasts, public read-only desktop observation, and browser-scoped computer-use orchestration",
7499
7832
  type: "module",
7500
7833
  main: "dist/index.js",
@@ -7828,7 +8161,7 @@ async function main() {
7828
8161
  });
7829
8162
  registerCommand({
7830
8163
  name: "update",
7831
- description: "Clear cached plugin and refresh managed skill packs",
8164
+ description: "Repair cached plugin pins and refresh managed skill packs",
7832
8165
  run: () => buildUpdateCommandResult(args, runUpdate(), {
7833
8166
  resolveUpdateSkillModes,
7834
8167
  hasInstalledConfig,