opendevbrowser 0.0.25 → 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.
- package/README.md +7 -10
- package/dist/browser/canvas-manager.d.ts.map +1 -1
- package/dist/canvas/document-store.d.ts.map +1 -1
- package/dist/canvas/types.d.ts +3 -0
- package/dist/canvas/types.d.ts.map +1 -1
- package/dist/{chunk-7U63PZ4W.js → chunk-MWBDO2L5.js} +461 -189
- package/dist/chunk-MWBDO2L5.js.map +1 -0
- package/dist/{chunk-Z6ENAZUN.js → chunk-V5DJUSPV.js} +899 -115
- package/dist/chunk-V5DJUSPV.js.map +1 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/commands/daemon.d.ts +27 -0
- package/dist/cli/commands/daemon.d.ts.map +1 -1
- package/dist/cli/commands/devtools/console-poll.d.ts.map +1 -1
- package/dist/cli/commands/devtools/network-poll.d.ts.map +1 -1
- package/dist/cli/commands/serve.d.ts +10 -13
- package/dist/cli/commands/serve.d.ts.map +1 -1
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/daemon-autostart.d.ts +6 -2
- package/dist/cli/daemon-autostart.d.ts.map +1 -1
- package/dist/cli/daemon-client.d.ts.map +1 -1
- package/dist/cli/daemon-status-policy.d.ts +6 -0
- package/dist/cli/daemon-status-policy.d.ts.map +1 -0
- package/dist/cli/daemon-status.d.ts +1 -0
- package/dist/cli/daemon-status.d.ts.map +1 -1
- package/dist/cli/daemon.d.ts +5 -0
- package/dist/cli/daemon.d.ts.map +1 -1
- package/dist/cli/index.js +566 -154
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/utils/http.d.ts.map +1 -1
- package/dist/cli/utils/parse.d.ts.map +1 -1
- package/dist/daemon-fingerprint.json +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +109 -28
- package/dist/index.js.map +1 -1
- package/dist/inspiredesign/brief-expansion.d.ts +3 -0
- package/dist/inspiredesign/brief-expansion.d.ts.map +1 -1
- package/dist/{providers/inspiredesign-capture-mode.d.ts → inspiredesign/capture-mode.d.ts} +2 -2
- package/dist/inspiredesign/capture-mode.d.ts.map +1 -0
- package/dist/{providers/inspiredesign-capture.d.ts → inspiredesign/capture.d.ts} +3 -3
- package/dist/inspiredesign/capture.d.ts.map +1 -0
- package/dist/{providers/inspiredesign-contract.d.ts → inspiredesign/contract.d.ts} +18 -5
- package/dist/inspiredesign/contract.d.ts.map +1 -0
- package/dist/inspiredesign/handoff.d.ts +1 -11
- package/dist/inspiredesign/handoff.d.ts.map +1 -1
- package/dist/inspiredesign/reference-pattern-board.d.ts +73 -0
- package/dist/inspiredesign/reference-pattern-board.d.ts.map +1 -0
- package/dist/opendevbrowser.d.ts.map +1 -1
- package/dist/opendevbrowser.js +109 -28
- package/dist/opendevbrowser.js.map +1 -1
- package/dist/providers/renderer.d.ts +1 -1
- package/dist/providers/renderer.d.ts.map +1 -1
- package/dist/providers/workflows.d.ts +7 -5
- package/dist/providers/workflows.d.ts.map +1 -1
- package/dist/{providers-CYEJZVXB.js → providers-TR3DUJZV.js} +2 -2
- package/dist/public-surface/generated-manifest.d.ts +3 -3
- package/dist/public-surface/generated-manifest.d.ts.map +1 -1
- package/dist/public-surface/source.d.ts +5 -4
- package/dist/public-surface/source.d.ts.map +1 -1
- package/dist/relay/protocol.d.ts +14 -2
- package/dist/relay/protocol.d.ts.map +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/status.d.ts.map +1 -1
- package/extension/dist/canvas/canvas-runtime.js +13 -6
- package/extension/dist/services/ConnectionManager.js +8 -4
- package/extension/manifest.json +1 -1
- package/package.json +1 -1
- package/skills/opendevbrowser-best-practices/assets/templates/skill-runtime-pack-matrix.json +1 -1
- package/skills/opendevbrowser-design-agent/assets/templates/inspiredesign-advanced-brief.v1.json +67 -31
- package/dist/chunk-7U63PZ4W.js.map +0 -1
- package/dist/chunk-Z6ENAZUN.js.map +0 -1
- package/dist/providers/inspiredesign-capture-mode.d.ts.map +0 -1
- package/dist/providers/inspiredesign-capture.d.ts.map +0 -1
- package/dist/providers/inspiredesign-contract.d.ts.map +0 -1
- /package/dist/{providers-CYEJZVXB.js.map → providers-TR3DUJZV.js.map} +0 -0
package/dist/cli/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
CLI_COMMANDS,
|
|
4
4
|
CLI_COMMAND_HELP_DETAILS,
|
|
5
5
|
DEFAULT_CLICK_TRANSPORT_TIMEOUT_MS,
|
|
6
|
+
DEFAULT_DAEMON_STATUS_FETCH_OPTIONS,
|
|
6
7
|
DEFAULT_DIALOG_TRANSPORT_TIMEOUT_MS,
|
|
7
8
|
DEFAULT_REVIEW_TRANSPORT_TIMEOUT_MS,
|
|
8
9
|
DEFAULT_SCREENSHOT_TRANSPORT_TIMEOUT_MS,
|
|
@@ -19,6 +20,8 @@ import {
|
|
|
19
20
|
VALID_FLAGS,
|
|
20
21
|
buildAnnotateResult,
|
|
21
22
|
callDaemon,
|
|
23
|
+
createDaemonStopHeaders,
|
|
24
|
+
createDisconnectedError,
|
|
22
25
|
createOpenDevBrowserCore,
|
|
23
26
|
createUsageError,
|
|
24
27
|
extractExtension,
|
|
@@ -28,9 +31,9 @@ import {
|
|
|
28
31
|
formatErrorPayload,
|
|
29
32
|
generateSecureToken,
|
|
30
33
|
getChromeUserDataRoots,
|
|
31
|
-
getCurrentDaemonFingerprint,
|
|
32
34
|
getExtensionPath,
|
|
33
35
|
getProfileDirs,
|
|
36
|
+
isCurrentDaemonFingerprint,
|
|
34
37
|
loadGlobalConfig,
|
|
35
38
|
onboarding_metadata_default,
|
|
36
39
|
readDaemonMetadata,
|
|
@@ -38,7 +41,7 @@ import {
|
|
|
38
41
|
resolveExitCode,
|
|
39
42
|
startDaemon,
|
|
40
43
|
toCliError
|
|
41
|
-
} from "../chunk-
|
|
44
|
+
} from "../chunk-MWBDO2L5.js";
|
|
42
45
|
import "../chunk-STGGGVYT.js";
|
|
43
46
|
import {
|
|
44
47
|
createNoOpSkillRemovalResult,
|
|
@@ -71,7 +74,7 @@ import {
|
|
|
71
74
|
setDefaultLogSink,
|
|
72
75
|
stderrSink,
|
|
73
76
|
summarizePrimaryProviderIssue
|
|
74
|
-
} from "../chunk-
|
|
77
|
+
} from "../chunk-V5DJUSPV.js";
|
|
75
78
|
import "../chunk-FUSXMW3G.js";
|
|
76
79
|
|
|
77
80
|
// src/cli/args.ts
|
|
@@ -140,6 +143,17 @@ function parseTransport(args) {
|
|
|
140
143
|
}
|
|
141
144
|
var VALID_FLAG_SET = new Set(VALID_FLAGS);
|
|
142
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
|
+
}
|
|
143
157
|
function parseArgs(argv) {
|
|
144
158
|
let args = expandShortFlags(argv.slice(2));
|
|
145
159
|
let commandOverride = null;
|
|
@@ -234,7 +248,8 @@ function parseArgs(argv) {
|
|
|
234
248
|
} else if (noPrompt) {
|
|
235
249
|
mode = "global";
|
|
236
250
|
}
|
|
237
|
-
for (
|
|
251
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
252
|
+
const arg = args[index] ?? "";
|
|
238
253
|
if (arg.startsWith("--") && !VALID_FLAG_SET.has(arg)) {
|
|
239
254
|
if (arg.includes("=")) {
|
|
240
255
|
const baseFlag = arg.split("=", 2)[0] ?? "";
|
|
@@ -247,6 +262,9 @@ function parseArgs(argv) {
|
|
|
247
262
|
if (arg.startsWith("-") && !arg.startsWith("--") && !SHORT_FLAGS[arg]) {
|
|
248
263
|
throw createUsageError(`Unknown flag: ${arg}`);
|
|
249
264
|
}
|
|
265
|
+
if (shouldSkipValueToken(arg, args[index + 1])) {
|
|
266
|
+
index += 1;
|
|
267
|
+
}
|
|
250
268
|
}
|
|
251
269
|
return {
|
|
252
270
|
command: commandOverride ?? "install",
|
|
@@ -926,45 +944,292 @@ function installLocal(withConfig = false) {
|
|
|
926
944
|
import * as fs4 from "fs";
|
|
927
945
|
import * as path2 from "path";
|
|
928
946
|
import * as os2 from "os";
|
|
947
|
+
import { randomUUID } from "crypto";
|
|
929
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
|
+
];
|
|
930
959
|
function getCacheDir() {
|
|
931
960
|
return process.env.OPENCODE_CACHE_DIR || path2.join(os2.homedir(), ".cache", "opencode");
|
|
932
961
|
}
|
|
933
|
-
function
|
|
934
|
-
const cacheDir = getCacheDir();
|
|
962
|
+
function assertCacheChild(targetPath, cacheDir) {
|
|
935
963
|
const resolvedCache = path2.resolve(cacheDir);
|
|
936
|
-
const resolvedPath = path2.resolve(
|
|
937
|
-
|
|
938
|
-
|
|
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);
|
|
978
|
+
const resolvedCache = path2.resolve(cacheDir);
|
|
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;
|
|
939
1050
|
}
|
|
940
|
-
|
|
941
|
-
|
|
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
|
+
}
|
|
942
1069
|
}
|
|
943
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);
|
|
1100
|
+
}
|
|
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;
|
|
1115
|
+
}
|
|
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
|
+
}
|
|
944
1198
|
function runUpdate() {
|
|
945
1199
|
const cacheDir = getCacheDir();
|
|
946
1200
|
const nodeModulesDir = path2.join(cacheDir, "node_modules");
|
|
947
1201
|
const pluginCacheDir = path2.join(nodeModulesDir, PLUGIN_NAME);
|
|
1202
|
+
const lockfilePath = path2.join(cacheDir, CACHE_LOCKFILE);
|
|
948
1203
|
try {
|
|
949
|
-
if (!
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
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) {
|
|
958
1224
|
return {
|
|
959
1225
|
success: true,
|
|
960
1226
|
message: "No cached plugin found. OpenCode will install the latest version on next run.",
|
|
961
1227
|
cleared: false
|
|
962
1228
|
};
|
|
963
1229
|
}
|
|
964
|
-
rmdir(pluginCacheDir);
|
|
965
1230
|
return {
|
|
966
1231
|
success: true,
|
|
967
|
-
message: "Cache
|
|
1232
|
+
message: "Cache repaired. OpenCode will install the latest version on next run.",
|
|
968
1233
|
cleared: true
|
|
969
1234
|
};
|
|
970
1235
|
} catch (error) {
|
|
@@ -1053,7 +1318,24 @@ function findInstalledConfigs() {
|
|
|
1053
1318
|
import { spawnSync } from "child_process";
|
|
1054
1319
|
|
|
1055
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
|
+
}
|
|
1056
1335
|
function parseNumberFlag(value, flag, options = {}) {
|
|
1336
|
+
if (value.trim() === "" || value !== value.trim() || !decimalPattern(options).test(value)) {
|
|
1337
|
+
throw createUsageError(`Invalid ${flag}: ${value}`);
|
|
1338
|
+
}
|
|
1057
1339
|
const parsed = Number(value);
|
|
1058
1340
|
if (!Number.isFinite(parsed)) {
|
|
1059
1341
|
throw createUsageError(`Invalid ${flag}: ${value}`);
|
|
@@ -1526,7 +1808,15 @@ async function runNativeCommand(args) {
|
|
|
1526
1808
|
// src/cli/commands/serve.ts
|
|
1527
1809
|
var daemonHandle = null;
|
|
1528
1810
|
var PS_MAX_BUFFER = 8 * 1024 * 1024;
|
|
1811
|
+
var DAEMON_SHUTDOWN_POLL_ATTEMPTS = 10;
|
|
1812
|
+
var DAEMON_SHUTDOWN_POLL_DELAY_MS = 100;
|
|
1813
|
+
var DAEMON_SHUTDOWN_STATUS_TIMEOUT_MS = 250;
|
|
1814
|
+
var DAEMON_STOP_TIMEOUT_MS = 1e3;
|
|
1815
|
+
var MIN_PORT = 1;
|
|
1816
|
+
var MAX_PORT = 65535;
|
|
1529
1817
|
var SERVE_COMMAND_PATTERN = /(?:^|\s)(?:\S*[\\/])?(?:opendevbrowser|dist[\\/]+cli[\\/]+index\.js)(?=\s|$).*?\bserve\b/;
|
|
1818
|
+
var SERVE_PORT_SPLIT_PATTERN = /(?:^|\s)--port\s+(\d+)(?=\s|$)/;
|
|
1819
|
+
var SERVE_PORT_EQUALS_PATTERN = /(?:^|\s)--port=(\d+)(?=\s|$)/;
|
|
1530
1820
|
var SERVE_STOP_PATTERN = /(?:^|\s)--stop(?:\s|$)/;
|
|
1531
1821
|
var CURRENT_UID = typeof process.getuid === "function" ? process.getuid() : null;
|
|
1532
1822
|
var CURRENT_EXECUTABLE = process.execPath;
|
|
@@ -1545,29 +1835,6 @@ async function resolveExistingDaemon(port, tokens) {
|
|
|
1545
1835
|
function isPositivePid(value) {
|
|
1546
1836
|
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
|
1547
1837
|
}
|
|
1548
|
-
function rememberStalePid(staleDaemonPids, pid) {
|
|
1549
|
-
if (isPositivePid(pid)) {
|
|
1550
|
-
staleDaemonPids.add(pid);
|
|
1551
|
-
}
|
|
1552
|
-
}
|
|
1553
|
-
async function stopDaemonOnPort(port, token) {
|
|
1554
|
-
try {
|
|
1555
|
-
const response = await fetchWithTimeout(`http://127.0.0.1:${port}/stop`, {
|
|
1556
|
-
method: "POST",
|
|
1557
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
1558
|
-
});
|
|
1559
|
-
return response.ok;
|
|
1560
|
-
} catch {
|
|
1561
|
-
return false;
|
|
1562
|
-
}
|
|
1563
|
-
}
|
|
1564
|
-
async function stopStaleDaemon(port, daemon, staleDaemonPids) {
|
|
1565
|
-
rememberStalePid(staleDaemonPids, daemon.status.pid);
|
|
1566
|
-
const stopped = await stopDaemonOnPort(port, daemon.token);
|
|
1567
|
-
if (!stopped && isPositivePid(daemon.status.pid)) {
|
|
1568
|
-
terminateProcess(daemon.status.pid);
|
|
1569
|
-
}
|
|
1570
|
-
}
|
|
1571
1838
|
function parseServeArgs(rawArgs) {
|
|
1572
1839
|
const parsed = { stop: false };
|
|
1573
1840
|
for (let i = 0; i < rawArgs.length; i += 1) {
|
|
@@ -1581,7 +1848,7 @@ function parseServeArgs(rawArgs) {
|
|
|
1581
1848
|
if (!value) {
|
|
1582
1849
|
throw createUsageError("Missing value for --port");
|
|
1583
1850
|
}
|
|
1584
|
-
parsed.port = parseNumberFlag(value, "--port", { min:
|
|
1851
|
+
parsed.port = parseNumberFlag(value, "--port", { min: MIN_PORT, max: MAX_PORT });
|
|
1585
1852
|
i += 1;
|
|
1586
1853
|
continue;
|
|
1587
1854
|
}
|
|
@@ -1590,7 +1857,7 @@ function parseServeArgs(rawArgs) {
|
|
|
1590
1857
|
if (!value) {
|
|
1591
1858
|
throw createUsageError("Missing value for --port");
|
|
1592
1859
|
}
|
|
1593
|
-
parsed.port = parseNumberFlag(value, "--port", { min:
|
|
1860
|
+
parsed.port = parseNumberFlag(value, "--port", { min: MIN_PORT, max: MAX_PORT });
|
|
1594
1861
|
continue;
|
|
1595
1862
|
}
|
|
1596
1863
|
if (arg === "--token") {
|
|
@@ -1638,6 +1905,14 @@ function parseServeProcessSnapshot(line) {
|
|
|
1638
1905
|
command
|
|
1639
1906
|
};
|
|
1640
1907
|
}
|
|
1908
|
+
function parseServeCommandPort(command) {
|
|
1909
|
+
const rawPort = command.match(SERVE_PORT_EQUALS_PATTERN)?.[1] ?? command.match(SERVE_PORT_SPLIT_PATTERN)?.[1];
|
|
1910
|
+
if (!rawPort) {
|
|
1911
|
+
return null;
|
|
1912
|
+
}
|
|
1913
|
+
const port = Number.parseInt(rawPort, 10);
|
|
1914
|
+
return Number.isInteger(port) && port >= MIN_PORT && port <= MAX_PORT ? port : null;
|
|
1915
|
+
}
|
|
1641
1916
|
function listServeProcessSnapshots() {
|
|
1642
1917
|
const result = spawnSync("ps", ["-axww", "-o", "pid=,uid=,command="], {
|
|
1643
1918
|
encoding: "utf-8",
|
|
@@ -1660,6 +1935,9 @@ function isCurrentExecutableServeProcess(snapshot) {
|
|
|
1660
1935
|
}
|
|
1661
1936
|
return !SERVE_STOP_PATTERN.test(snapshot.command);
|
|
1662
1937
|
}
|
|
1938
|
+
function isRequestedPortServeProcess(snapshot, requestedPort) {
|
|
1939
|
+
return isCurrentExecutableServeProcess(snapshot) && parseServeCommandPort(snapshot.command) === requestedPort;
|
|
1940
|
+
}
|
|
1663
1941
|
function terminateProcess(pid) {
|
|
1664
1942
|
if (!Number.isInteger(pid) || pid <= 0 || pid === process.pid || pid === process.ppid) {
|
|
1665
1943
|
return false;
|
|
@@ -1675,9 +1953,9 @@ function terminateProcess(pid) {
|
|
|
1675
1953
|
}
|
|
1676
1954
|
return true;
|
|
1677
1955
|
}
|
|
1678
|
-
function cleanupCompetingServeProcesses(keepPid) {
|
|
1956
|
+
function cleanupCompetingServeProcesses(requestedPort, keepPid) {
|
|
1679
1957
|
const candidates = listServeProcessSnapshots().filter((snapshot) => {
|
|
1680
|
-
if (!
|
|
1958
|
+
if (!isRequestedPortServeProcess(snapshot, requestedPort)) {
|
|
1681
1959
|
return false;
|
|
1682
1960
|
}
|
|
1683
1961
|
if (snapshot.pid === process.pid || snapshot.pid === process.ppid) {
|
|
@@ -1699,6 +1977,85 @@ function cleanupCompetingServeProcesses(keepPid) {
|
|
|
1699
1977
|
}
|
|
1700
1978
|
return clearedPids;
|
|
1701
1979
|
}
|
|
1980
|
+
function terminateServeProcessByPid(pid) {
|
|
1981
|
+
if (!isPositivePid(pid)) {
|
|
1982
|
+
return false;
|
|
1983
|
+
}
|
|
1984
|
+
const snapshot = listServeProcessSnapshots().find((item) => item.pid === pid);
|
|
1985
|
+
return snapshot ? isCurrentExecutableServeProcess(snapshot) && terminateProcess(pid) : false;
|
|
1986
|
+
}
|
|
1987
|
+
function buildStaleStopMessage(metadata) {
|
|
1988
|
+
const pid = isPositivePid(metadata.pid) ? ` pid=${metadata.pid}` : "";
|
|
1989
|
+
return `Daemon rejected stale stop request for 127.0.0.1:${metadata.port}${pid}. Run \`opendevbrowser status --daemon\` to inspect the active daemon, then restart from the current install if needed.`;
|
|
1990
|
+
}
|
|
1991
|
+
function buildProtectedMismatchMessage(port, status) {
|
|
1992
|
+
return `Daemon on 127.0.0.1:${port} pid=${status.pid} is protected by a different opendevbrowser build. Run \`opendevbrowser status --daemon\` to inspect it, then restart from the current install.`;
|
|
1993
|
+
}
|
|
1994
|
+
async function waitForDaemonShutdown(port, token) {
|
|
1995
|
+
for (let attempt = 0; attempt < DAEMON_SHUTDOWN_POLL_ATTEMPTS; attempt += 1) {
|
|
1996
|
+
const status = await fetchDaemonStatus(port, token, { timeoutMs: DAEMON_SHUTDOWN_STATUS_TIMEOUT_MS });
|
|
1997
|
+
if (!status?.ok) {
|
|
1998
|
+
return true;
|
|
1999
|
+
}
|
|
2000
|
+
await new Promise((resolve5) => setTimeout(resolve5, DAEMON_SHUTDOWN_POLL_DELAY_MS));
|
|
2001
|
+
}
|
|
2002
|
+
return false;
|
|
2003
|
+
}
|
|
2004
|
+
async function stopMismatchedDaemon(port, daemon) {
|
|
2005
|
+
let response;
|
|
2006
|
+
try {
|
|
2007
|
+
response = await fetchWithTimeout(`http://127.0.0.1:${port}/stop`, {
|
|
2008
|
+
method: "POST",
|
|
2009
|
+
headers: createDaemonStopHeaders(daemon.token, "serve.upgrade")
|
|
2010
|
+
}, DAEMON_STOP_TIMEOUT_MS);
|
|
2011
|
+
} catch (error) {
|
|
2012
|
+
const status = await fetchDaemonStatus(port, daemon.token, {
|
|
2013
|
+
timeoutMs: DAEMON_SHUTDOWN_STATUS_TIMEOUT_MS
|
|
2014
|
+
});
|
|
2015
|
+
if (!status?.ok) {
|
|
2016
|
+
return null;
|
|
2017
|
+
}
|
|
2018
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2019
|
+
return `Failed to stop mismatched daemon on 127.0.0.1:${port}: ${message}.`;
|
|
2020
|
+
}
|
|
2021
|
+
if (response.status === 409) {
|
|
2022
|
+
return buildProtectedMismatchMessage(port, daemon.status);
|
|
2023
|
+
}
|
|
2024
|
+
if (!response.ok) {
|
|
2025
|
+
return `Failed to stop mismatched daemon on 127.0.0.1:${port}: stop returned ${response.status}.`;
|
|
2026
|
+
}
|
|
2027
|
+
if (await waitForDaemonShutdown(port, daemon.token)) {
|
|
2028
|
+
return null;
|
|
2029
|
+
}
|
|
2030
|
+
if (terminateServeProcessByPid(daemon.status.pid)) {
|
|
2031
|
+
return null;
|
|
2032
|
+
}
|
|
2033
|
+
return `Timed out waiting for mismatched daemon on 127.0.0.1:${port} to stop.`;
|
|
2034
|
+
}
|
|
2035
|
+
async function prepareExistingDaemon(port, daemon) {
|
|
2036
|
+
if (isCurrentDaemonFingerprint(daemon.status.fingerprint)) {
|
|
2037
|
+
return null;
|
|
2038
|
+
}
|
|
2039
|
+
return await stopMismatchedDaemon(port, daemon);
|
|
2040
|
+
}
|
|
2041
|
+
function buildAlreadyRunningResult(port, status, fallbackRelayPort, clearedCount) {
|
|
2042
|
+
const relayPort = status.relay.port ?? fallbackRelayPort;
|
|
2043
|
+
const staleNote = clearedCount > 0 ? `
|
|
2044
|
+
Cleared ${clearedCount} stale daemon process${clearedCount === 1 ? "" : "es"}.` : "";
|
|
2045
|
+
return {
|
|
2046
|
+
success: true,
|
|
2047
|
+
message: `Daemon already running on 127.0.0.1:${port} (pid=${status.pid}, relay ${relayPort}).${staleNote}`,
|
|
2048
|
+
data: {
|
|
2049
|
+
port,
|
|
2050
|
+
pid: status.pid,
|
|
2051
|
+
relayPort,
|
|
2052
|
+
alreadyRunning: true,
|
|
2053
|
+
staleDaemonsCleared: clearedCount,
|
|
2054
|
+
relay: status.relay
|
|
2055
|
+
},
|
|
2056
|
+
exitCode: null
|
|
2057
|
+
};
|
|
2058
|
+
}
|
|
1702
2059
|
async function runServe(args) {
|
|
1703
2060
|
const serveArgs = parseServeArgs(args.rawArgs);
|
|
1704
2061
|
if (serveArgs.stop) {
|
|
@@ -1714,8 +2071,11 @@ async function runServe(args) {
|
|
|
1714
2071
|
try {
|
|
1715
2072
|
const response = await fetchWithTimeout(`http://127.0.0.1:${metadata2.port}/stop`, {
|
|
1716
2073
|
method: "POST",
|
|
1717
|
-
headers:
|
|
2074
|
+
headers: createDaemonStopHeaders(metadata2.token, "serve.stop")
|
|
1718
2075
|
});
|
|
2076
|
+
if (response.status === 409) {
|
|
2077
|
+
return { success: false, message: buildStaleStopMessage(metadata2), exitCode: EXIT_EXECUTION };
|
|
2078
|
+
}
|
|
1719
2079
|
if (!response.ok) {
|
|
1720
2080
|
throw new Error(`Stop failed (${response.status})`);
|
|
1721
2081
|
}
|
|
@@ -1730,34 +2090,23 @@ async function runServe(args) {
|
|
|
1730
2090
|
const metadata = readDaemonMetadata();
|
|
1731
2091
|
const metadataToken = metadata?.port === requestedPort ? metadata.token : void 0;
|
|
1732
2092
|
const tokenCandidates = resolveTokenCandidates(serveArgs.token, metadataToken, config.daemonToken);
|
|
1733
|
-
const currentFingerprint = getCurrentDaemonFingerprint();
|
|
1734
2093
|
const existingDaemon = await resolveExistingDaemon(requestedPort, tokenCandidates);
|
|
1735
|
-
const staleDaemonPids = new Set(
|
|
2094
|
+
const staleDaemonPids = /* @__PURE__ */ new Set();
|
|
1736
2095
|
const staleCleared = () => staleDaemonPids.size;
|
|
1737
|
-
let replacedStaleFingerprint = false;
|
|
1738
2096
|
if (existingDaemon) {
|
|
1739
|
-
const
|
|
1740
|
-
if (
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
data: {
|
|
1749
|
-
port: requestedPort,
|
|
1750
|
-
pid: existingDaemon.status.pid,
|
|
1751
|
-
relayPort,
|
|
1752
|
-
alreadyRunning: true,
|
|
1753
|
-
staleDaemonsCleared: clearedCount2,
|
|
1754
|
-
relay: existingDaemon.status.relay
|
|
1755
|
-
},
|
|
1756
|
-
exitCode: null
|
|
1757
|
-
};
|
|
2097
|
+
const mismatchMessage = await prepareExistingDaemon(requestedPort, existingDaemon);
|
|
2098
|
+
if (mismatchMessage) {
|
|
2099
|
+
return { success: false, message: mismatchMessage, exitCode: EXIT_EXECUTION };
|
|
2100
|
+
}
|
|
2101
|
+
if (isCurrentDaemonFingerprint(existingDaemon.status.fingerprint)) {
|
|
2102
|
+
for (const pid of cleanupCompetingServeProcesses(requestedPort, existingDaemon.status.pid)) {
|
|
2103
|
+
staleDaemonPids.add(pid);
|
|
2104
|
+
}
|
|
2105
|
+
return buildAlreadyRunningResult(requestedPort, existingDaemon.status, config.relayPort, staleCleared());
|
|
1758
2106
|
}
|
|
1759
|
-
|
|
1760
|
-
|
|
2107
|
+
}
|
|
2108
|
+
for (const pid of cleanupCompetingServeProcesses(requestedPort)) {
|
|
2109
|
+
staleDaemonPids.add(pid);
|
|
1761
2110
|
}
|
|
1762
2111
|
let nativeStatus = getNativeStatusSnapshot();
|
|
1763
2112
|
let nativeMessage = null;
|
|
@@ -1800,35 +2149,17 @@ Cleared ${clearedCount2} stale daemon process${clearedCount2 === 1 ? "" : "es"}.
|
|
|
1800
2149
|
}
|
|
1801
2150
|
const runningDaemon = await resolveExistingDaemon(requestedPort, tokenCandidates);
|
|
1802
2151
|
if (runningDaemon) {
|
|
1803
|
-
const
|
|
1804
|
-
if (
|
|
1805
|
-
|
|
1806
|
-
const clearedCount2 = staleCleared();
|
|
1807
|
-
const staleNote2 = clearedCount2 > 0 ? `
|
|
1808
|
-
Cleared ${clearedCount2} stale daemon process${clearedCount2 === 1 ? "" : "es"}.` : "";
|
|
1809
|
-
return {
|
|
1810
|
-
success: true,
|
|
1811
|
-
message: `Daemon already running on 127.0.0.1:${requestedPort} (pid=${runningDaemon.status.pid}, relay ${relayPort}).${staleNote2}`,
|
|
1812
|
-
data: {
|
|
1813
|
-
port: requestedPort,
|
|
1814
|
-
pid: runningDaemon.status.pid,
|
|
1815
|
-
relayPort,
|
|
1816
|
-
alreadyRunning: true,
|
|
1817
|
-
staleDaemonsCleared: clearedCount2,
|
|
1818
|
-
relay: runningDaemon.status.relay
|
|
1819
|
-
},
|
|
1820
|
-
exitCode: null
|
|
1821
|
-
};
|
|
2152
|
+
const mismatchMessage = await prepareExistingDaemon(requestedPort, runningDaemon);
|
|
2153
|
+
if (mismatchMessage) {
|
|
2154
|
+
return { success: false, message: mismatchMessage, exitCode: EXIT_EXECUTION };
|
|
1822
2155
|
}
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
if (attempt === 0) {
|
|
1826
|
-
continue;
|
|
2156
|
+
if (isCurrentDaemonFingerprint(runningDaemon.status.fingerprint)) {
|
|
2157
|
+
return buildAlreadyRunningResult(requestedPort, runningDaemon.status, config.relayPort, staleCleared());
|
|
1827
2158
|
}
|
|
1828
2159
|
}
|
|
1829
2160
|
if (attempt === 0) {
|
|
1830
2161
|
let clearedNewPid = false;
|
|
1831
|
-
for (const pid of cleanupCompetingServeProcesses()) {
|
|
2162
|
+
for (const pid of cleanupCompetingServeProcesses(requestedPort)) {
|
|
1832
2163
|
const previousSize = staleDaemonPids.size;
|
|
1833
2164
|
staleDaemonPids.add(pid);
|
|
1834
2165
|
if (staleDaemonPids.size > previousSize) {
|
|
@@ -1863,9 +2194,8 @@ Cleared ${clearedCount2} stale daemon process${clearedCount2 === 1 ? "" : "es"}.
|
|
|
1863
2194
|
const clearedCount = staleCleared();
|
|
1864
2195
|
const staleNote = clearedCount > 0 ? `
|
|
1865
2196
|
Cleared ${clearedCount} stale daemon process${clearedCount === 1 ? "" : "es"}.` : "";
|
|
1866
|
-
const fingerprintNote = replacedStaleFingerprint ? "\nReplaced stale daemon fingerprint." : "";
|
|
1867
2197
|
const message = nativeMessage ? `${baseMessage}
|
|
1868
|
-
${nativeMessage}${
|
|
2198
|
+
${nativeMessage}${staleNote}` : `${baseMessage}${staleNote}`;
|
|
1869
2199
|
return {
|
|
1870
2200
|
success: true,
|
|
1871
2201
|
message,
|
|
@@ -1876,9 +2206,9 @@ ${nativeMessage}${fingerprintNote}${staleNote}` : `${baseMessage}${fingerprintNo
|
|
|
1876
2206
|
|
|
1877
2207
|
// src/cli/daemon-autostart.ts
|
|
1878
2208
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
1879
|
-
import { existsSync as existsSync5, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2, writeFileSync as
|
|
2209
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, statSync as statSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
1880
2210
|
import { homedir as homedir4, tmpdir } from "os";
|
|
1881
|
-
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";
|
|
1882
2212
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1883
2213
|
var MAC_LABEL = "com.opendevbrowser.daemon";
|
|
1884
2214
|
var WIN_TASK_NAME = "OpenDevBrowser Daemon";
|
|
@@ -1891,7 +2221,8 @@ var defaultDeps = () => ({
|
|
|
1891
2221
|
homedir: homedir4,
|
|
1892
2222
|
existsSync: existsSync5,
|
|
1893
2223
|
mkdirSync: mkdirSync2,
|
|
1894
|
-
|
|
2224
|
+
statSync: statSync2,
|
|
2225
|
+
writeFileSync: writeFileSync5,
|
|
1895
2226
|
unlinkSync: unlinkSync2,
|
|
1896
2227
|
execFileSync: execFileSync2,
|
|
1897
2228
|
transientEntrypointRoots: void 0
|
|
@@ -1900,6 +2231,17 @@ var NPX_CACHE_SEGMENT_PATTERN = /[\\/]_npx(?:[\\/]|$)/;
|
|
|
1900
2231
|
var formatCommand = (programArguments) => {
|
|
1901
2232
|
return programArguments.map((value) => `"${value}"`).join(" ");
|
|
1902
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
|
+
};
|
|
1903
2245
|
var resolveCliPathFromModule = (moduleUrl, exists) => {
|
|
1904
2246
|
const modulePath = fileURLToPath2(moduleUrl);
|
|
1905
2247
|
const candidate = resolve3(dirname3(modulePath), "..", "index.js");
|
|
@@ -1915,8 +2257,8 @@ var normalizeComparisonPath = (value, platform) => {
|
|
|
1915
2257
|
var isPathInsideRoot = (candidate, root, platform) => {
|
|
1916
2258
|
const normalizedCandidate = normalizeComparisonPath(candidate, platform);
|
|
1917
2259
|
const normalizedRoot = normalizeComparisonPath(root, platform);
|
|
1918
|
-
const relation =
|
|
1919
|
-
return relation === "" || !relation.startsWith("..") && !
|
|
2260
|
+
const relation = relative2(normalizedRoot, normalizedCandidate);
|
|
2261
|
+
return relation === "" || !relation.startsWith("..") && !isAbsolute2(relation);
|
|
1920
2262
|
};
|
|
1921
2263
|
var getTransientEntrypointRoots = (deps) => {
|
|
1922
2264
|
const configuredRoots = deps.transientEntrypointRoots;
|
|
@@ -1962,18 +2304,20 @@ var resolveCliEntrypoint = (deps = {}) => {
|
|
|
1962
2304
|
var getLaunchAgentPath = (home = homedir4()) => {
|
|
1963
2305
|
return join5(home, "Library", "LaunchAgents", `${MAC_LABEL}.plist`);
|
|
1964
2306
|
};
|
|
2307
|
+
var escapePlistString = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
1965
2308
|
var buildLaunchAgentPlist = (entrypoint, options = {}) => {
|
|
1966
2309
|
const label = options.label ?? MAC_LABEL;
|
|
1967
2310
|
const stdoutPath = options.stdoutPath ?? join5(homedir4(), "Library", "Logs", "opendevbrowser-daemon.log");
|
|
1968
2311
|
const stderrPath = options.stderrPath ?? join5(homedir4(), "Library", "Logs", "opendevbrowser-daemon.err.log");
|
|
1969
|
-
const
|
|
2312
|
+
const workingDirectory = options.workingDirectory ?? resolveMacWorkingDirectory(homedir4());
|
|
2313
|
+
const programArgs = [entrypoint.nodePath, ...entrypoint.args].map((value) => ` <string>${escapePlistString(value)}</string>`).join("\n");
|
|
1970
2314
|
return [
|
|
1971
2315
|
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
1972
2316
|
'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
|
|
1973
2317
|
'<plist version="1.0">',
|
|
1974
2318
|
"<dict>",
|
|
1975
2319
|
` <key>Label</key>`,
|
|
1976
|
-
` <string>${label}</string>`,
|
|
2320
|
+
` <string>${escapePlistString(label)}</string>`,
|
|
1977
2321
|
" <key>ProgramArguments</key>",
|
|
1978
2322
|
" <array>",
|
|
1979
2323
|
programArgs,
|
|
@@ -1982,10 +2326,12 @@ var buildLaunchAgentPlist = (entrypoint, options = {}) => {
|
|
|
1982
2326
|
" <true/>",
|
|
1983
2327
|
" <key>KeepAlive</key>",
|
|
1984
2328
|
" <true/>",
|
|
2329
|
+
" <key>WorkingDirectory</key>",
|
|
2330
|
+
` <string>${escapePlistString(workingDirectory)}</string>`,
|
|
1985
2331
|
" <key>StandardOutPath</key>",
|
|
1986
|
-
` <string>${stdoutPath}</string>`,
|
|
2332
|
+
` <string>${escapePlistString(stdoutPath)}</string>`,
|
|
1987
2333
|
" <key>StandardErrorPath</key>",
|
|
1988
|
-
` <string>${stderrPath}</string>`,
|
|
2334
|
+
` <string>${escapePlistString(stderrPath)}</string>`,
|
|
1989
2335
|
"</dict>",
|
|
1990
2336
|
"</plist>",
|
|
1991
2337
|
""
|
|
@@ -2077,6 +2423,7 @@ var readMacLaunchAgentProgramArguments = (plistPath, deps) => {
|
|
|
2077
2423
|
const text = deps.execFileSync("plutil", ["-convert", "json", "-o", "-", plistPath], { encoding: "utf-8" });
|
|
2078
2424
|
const parsed = JSON.parse(text);
|
|
2079
2425
|
const programArguments = parsed?.ProgramArguments;
|
|
2426
|
+
const workingDirectory = typeof parsed?.WorkingDirectory === "string" ? parsed.WorkingDirectory : void 0;
|
|
2080
2427
|
if (!Array.isArray(programArguments) || programArguments.length < 2 || programArguments.some((value) => typeof value !== "string")) {
|
|
2081
2428
|
return { ok: false, reason: "missing_program_arguments" };
|
|
2082
2429
|
}
|
|
@@ -2084,7 +2431,8 @@ var readMacLaunchAgentProgramArguments = (plistPath, deps) => {
|
|
|
2084
2431
|
return {
|
|
2085
2432
|
ok: true,
|
|
2086
2433
|
command: formatCommand(commandArgs),
|
|
2087
|
-
programArguments: commandArgs
|
|
2434
|
+
programArguments: commandArgs,
|
|
2435
|
+
...workingDirectory ? { workingDirectory } : {}
|
|
2088
2436
|
};
|
|
2089
2437
|
} catch {
|
|
2090
2438
|
return { ok: false, reason: "malformed_plist" };
|
|
@@ -2100,11 +2448,13 @@ var classifyMacAutostartStatus = (entrypoint, location, deps) => {
|
|
|
2100
2448
|
});
|
|
2101
2449
|
}
|
|
2102
2450
|
const parsed = readMacLaunchAgentProgramArguments(location, deps);
|
|
2451
|
+
const expectedWorkingDirectory = resolveMacWorkingDirectory(deps.homedir());
|
|
2103
2452
|
if (!parsed.ok) {
|
|
2104
2453
|
return createMacAutostartStatus(entrypoint, location, {
|
|
2105
2454
|
installed: true,
|
|
2106
2455
|
health: "malformed",
|
|
2107
2456
|
needsRepair: true,
|
|
2457
|
+
expectedWorkingDirectory,
|
|
2108
2458
|
reason: parsed.reason
|
|
2109
2459
|
});
|
|
2110
2460
|
}
|
|
@@ -2119,15 +2469,41 @@ var classifyMacAutostartStatus = (entrypoint, location, deps) => {
|
|
|
2119
2469
|
health: "needs_repair",
|
|
2120
2470
|
needsRepair: true,
|
|
2121
2471
|
command: parsed.command,
|
|
2472
|
+
workingDirectory: parsed.workingDirectory,
|
|
2473
|
+
expectedWorkingDirectory,
|
|
2122
2474
|
reason: actualStatus.reason
|
|
2123
2475
|
});
|
|
2124
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
|
+
}
|
|
2125
2499
|
if (entrypoint.isTransient) {
|
|
2126
2500
|
return createMacAutostartStatus(entrypoint, location, {
|
|
2127
2501
|
installed: true,
|
|
2128
2502
|
health: "healthy",
|
|
2129
2503
|
needsRepair: false,
|
|
2130
|
-
command: parsed.command
|
|
2504
|
+
command: parsed.command,
|
|
2505
|
+
workingDirectory: parsed.workingDirectory,
|
|
2506
|
+
expectedWorkingDirectory
|
|
2131
2507
|
});
|
|
2132
2508
|
}
|
|
2133
2509
|
const mismatchReason = classifyExpectedProgramArguments(expectedArgs, actualArgs, deps);
|
|
@@ -2137,6 +2513,8 @@ var classifyMacAutostartStatus = (entrypoint, location, deps) => {
|
|
|
2137
2513
|
health: "needs_repair",
|
|
2138
2514
|
needsRepair: true,
|
|
2139
2515
|
command: parsed.command,
|
|
2516
|
+
workingDirectory: parsed.workingDirectory,
|
|
2517
|
+
expectedWorkingDirectory,
|
|
2140
2518
|
reason: mismatchReason
|
|
2141
2519
|
});
|
|
2142
2520
|
}
|
|
@@ -2144,7 +2522,9 @@ var classifyMacAutostartStatus = (entrypoint, location, deps) => {
|
|
|
2144
2522
|
installed: true,
|
|
2145
2523
|
health: "healthy",
|
|
2146
2524
|
needsRepair: false,
|
|
2147
|
-
command: parsed.command
|
|
2525
|
+
command: parsed.command,
|
|
2526
|
+
workingDirectory: parsed.workingDirectory,
|
|
2527
|
+
expectedWorkingDirectory
|
|
2148
2528
|
});
|
|
2149
2529
|
};
|
|
2150
2530
|
var runCommand = (exec, command, args, ignoreFailure = false) => {
|
|
@@ -2172,12 +2552,14 @@ var installMacAutostart = (deps = {}) => {
|
|
|
2172
2552
|
const plistPath = getLaunchAgentPath(home);
|
|
2173
2553
|
const stdoutPath = join5(home, "Library", "Logs", "opendevbrowser-daemon.log");
|
|
2174
2554
|
const stderrPath = join5(home, "Library", "Logs", "opendevbrowser-daemon.err.log");
|
|
2555
|
+
const workingDirectory = resolveMacWorkingDirectory(home);
|
|
2175
2556
|
const logsDir = dirname3(stdoutPath);
|
|
2176
2557
|
resolved.mkdirSync(dirname3(plistPath), { recursive: true });
|
|
2177
2558
|
resolved.mkdirSync(logsDir, { recursive: true });
|
|
2559
|
+
resolved.mkdirSync(workingDirectory, { recursive: true });
|
|
2178
2560
|
resolved.writeFileSync(
|
|
2179
2561
|
plistPath,
|
|
2180
|
-
buildLaunchAgentPlist(entrypoint, { stdoutPath, stderrPath }),
|
|
2562
|
+
buildLaunchAgentPlist(entrypoint, { stdoutPath, stderrPath, workingDirectory }),
|
|
2181
2563
|
{ encoding: "utf-8" }
|
|
2182
2564
|
);
|
|
2183
2565
|
const uid = resolved.uid;
|
|
@@ -2189,7 +2571,9 @@ var installMacAutostart = (deps = {}) => {
|
|
|
2189
2571
|
installed: true,
|
|
2190
2572
|
health: "healthy",
|
|
2191
2573
|
needsRepair: false,
|
|
2192
|
-
command: entrypoint.command
|
|
2574
|
+
command: entrypoint.command,
|
|
2575
|
+
workingDirectory,
|
|
2576
|
+
expectedWorkingDirectory: workingDirectory
|
|
2193
2577
|
});
|
|
2194
2578
|
};
|
|
2195
2579
|
var uninstallMacAutostart = (deps = {}) => {
|
|
@@ -2433,17 +2817,40 @@ var parseDaemonArgs = (rawArgs) => {
|
|
|
2433
2817
|
var stopDaemonIfRunning = async () => {
|
|
2434
2818
|
const metadata = readDaemonMetadata();
|
|
2435
2819
|
if (!metadata) {
|
|
2436
|
-
return
|
|
2820
|
+
return { outcome: "not_running" };
|
|
2437
2821
|
}
|
|
2438
2822
|
try {
|
|
2439
2823
|
const response = await fetchWithTimeout(`http://127.0.0.1:${metadata.port}/stop`, {
|
|
2440
2824
|
method: "POST",
|
|
2441
|
-
headers:
|
|
2825
|
+
headers: createDaemonStopHeaders(metadata.token, "daemon.uninstall")
|
|
2442
2826
|
});
|
|
2443
|
-
|
|
2444
|
-
|
|
2827
|
+
if (response.status === 409) {
|
|
2828
|
+
return { outcome: "fingerprint_rejected", pid: metadata.pid, port: metadata.port };
|
|
2829
|
+
}
|
|
2830
|
+
return response.ok ? { outcome: "stopped", pid: metadata.pid, port: metadata.port } : { outcome: "failed", pid: metadata.pid, port: metadata.port, status: response.status };
|
|
2831
|
+
} catch (error) {
|
|
2832
|
+
return {
|
|
2833
|
+
outcome: "failed",
|
|
2834
|
+
pid: metadata.pid,
|
|
2835
|
+
port: metadata.port,
|
|
2836
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2837
|
+
};
|
|
2838
|
+
}
|
|
2839
|
+
};
|
|
2840
|
+
var buildStopFailureMessage = (stop) => {
|
|
2841
|
+
const target = stop.port ? `127.0.0.1:${stop.port}` : "recorded daemon";
|
|
2842
|
+
const pid = stop.pid ? ` pid=${stop.pid}` : "";
|
|
2843
|
+
if (stop.outcome === "fingerprint_rejected") {
|
|
2844
|
+
return `Daemon autostart removed, but the running daemon at ${target}${pid} rejected the stop request as stale. Run \`opendevbrowser status --daemon\` to inspect it and restart from the current install if needed.`;
|
|
2845
|
+
}
|
|
2846
|
+
const reason = stop.error ?? (stop.status ? `HTTP ${stop.status}` : "unknown error");
|
|
2847
|
+
return `Daemon autostart removed, but stopping ${target}${pid} failed (${reason}).`;
|
|
2848
|
+
};
|
|
2849
|
+
var shouldFailUninstallStop = (stop) => {
|
|
2850
|
+
if (stop.outcome === "stopped" || stop.outcome === "not_running") {
|
|
2445
2851
|
return false;
|
|
2446
2852
|
}
|
|
2853
|
+
return true;
|
|
2447
2854
|
};
|
|
2448
2855
|
var formatReason = (reason) => {
|
|
2449
2856
|
return reason ? reason.replace(/_/g, " ") : "unknown reason";
|
|
@@ -2518,7 +2925,15 @@ async function runDaemonCommand(args) {
|
|
|
2518
2925
|
exitCode: EXIT_EXECUTION
|
|
2519
2926
|
};
|
|
2520
2927
|
}
|
|
2521
|
-
await stopDaemonIfRunning();
|
|
2928
|
+
const stop = await stopDaemonIfRunning();
|
|
2929
|
+
if (shouldFailUninstallStop(stop)) {
|
|
2930
|
+
return {
|
|
2931
|
+
success: false,
|
|
2932
|
+
message: buildStopFailureMessage(stop),
|
|
2933
|
+
data: { ...result, stop },
|
|
2934
|
+
exitCode: EXIT_EXECUTION
|
|
2935
|
+
};
|
|
2936
|
+
}
|
|
2522
2937
|
return {
|
|
2523
2938
|
success: true,
|
|
2524
2939
|
message: `Daemon autostart removed (${result.platform}).`,
|
|
@@ -2526,7 +2941,7 @@ async function runDaemonCommand(args) {
|
|
|
2526
2941
|
};
|
|
2527
2942
|
}
|
|
2528
2943
|
const autostart = getAutostartStatus();
|
|
2529
|
-
const daemonStatus = await fetchDaemonStatusFromMetadata();
|
|
2944
|
+
const daemonStatus = await fetchDaemonStatusFromMetadata(void 0, DEFAULT_DAEMON_STATUS_FETCH_OPTIONS);
|
|
2530
2945
|
const running = Boolean(daemonStatus);
|
|
2531
2946
|
const message = buildStatusMessage(autostart, running);
|
|
2532
2947
|
const data = {
|
|
@@ -2750,7 +3165,7 @@ function formatAutostartReconciliationMessage(result) {
|
|
|
2750
3165
|
}
|
|
2751
3166
|
|
|
2752
3167
|
// src/cli/commands/run.ts
|
|
2753
|
-
import { readFileSync as
|
|
3168
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
2754
3169
|
|
|
2755
3170
|
// src/cli/output.ts
|
|
2756
3171
|
var normalizeExitCode = (code) => {
|
|
@@ -2914,7 +3329,7 @@ async function runScriptCommand(args) {
|
|
|
2914
3329
|
const outputOptions = { format: args.outputFormat, quiet: args.quiet };
|
|
2915
3330
|
let scriptRaw = "";
|
|
2916
3331
|
if (runArgs.scriptPath) {
|
|
2917
|
-
scriptRaw =
|
|
3332
|
+
scriptRaw = readFileSync3(runArgs.scriptPath, "utf-8");
|
|
2918
3333
|
} else if (!process.stdin.isTTY) {
|
|
2919
3334
|
scriptRaw = await readScriptFromStdin();
|
|
2920
3335
|
} else {
|
|
@@ -3574,11 +3989,6 @@ async function runSessionStatus(args) {
|
|
|
3574
3989
|
}
|
|
3575
3990
|
|
|
3576
3991
|
// src/cli/commands/status.ts
|
|
3577
|
-
var DAEMON_STATUS_READ_OPTIONS = {
|
|
3578
|
-
timeoutMs: 5e3,
|
|
3579
|
-
retryAttempts: 5,
|
|
3580
|
-
retryDelayMs: 250
|
|
3581
|
-
};
|
|
3582
3992
|
var parseStatusArgs2 = (rawArgs) => {
|
|
3583
3993
|
const parsed = { daemon: false };
|
|
3584
3994
|
for (let i = 0; i < rawArgs.length; i += 1) {
|
|
@@ -3619,21 +4029,23 @@ async function runStatus(args) {
|
|
|
3619
4029
|
exitCode: assessment.exitCode ?? void 0
|
|
3620
4030
|
};
|
|
3621
4031
|
}
|
|
3622
|
-
const daemonStatus = await fetchDaemonStatusFromMetadata(void 0,
|
|
4032
|
+
const daemonStatus = await fetchDaemonStatusFromMetadata(void 0, DEFAULT_DAEMON_STATUS_FETCH_OPTIONS);
|
|
3623
4033
|
if (!daemonStatus) {
|
|
3624
|
-
throw
|
|
4034
|
+
throw createDisconnectedError("Daemon not running. Start with `opendevbrowser serve`.");
|
|
3625
4035
|
}
|
|
3626
4036
|
const nativeStatus = getNativeStatusSnapshot();
|
|
3627
4037
|
const nativeAssessment = assessNativeStatus(nativeStatus);
|
|
4038
|
+
const fingerprintLine = daemonStatus.fingerprintCurrent === false ? "Daemon fingerprint: mismatch with current build" : "Daemon fingerprint: current";
|
|
3628
4039
|
const baseLines = [
|
|
3629
4040
|
`Daemon OK (pid=${daemonStatus.pid})`,
|
|
4041
|
+
fingerprintLine,
|
|
3630
4042
|
`Relay: port=${daemonStatus.relay.port ?? "n/a"} ext=${daemonStatus.relay.extensionConnected ? "on" : "off"} handshake=${daemonStatus.relay.extensionHandshakeComplete ? "on" : "off"} cdp=${daemonStatus.relay.cdpConnected ? "on" : "off"} annotate=${daemonStatus.relay.annotationConnected ? "on" : "off"} ops=${daemonStatus.relay.opsConnected ? "on" : "off"} canvas=${daemonStatus.relay.canvasConnected ? "on" : "off"} pairing=${daemonStatus.relay.pairingRequired ? "on" : "off"} health=${daemonStatus.relay.health?.reason ?? "n/a"}`,
|
|
3631
4043
|
`Native: ${nativeAssessment.summary}`,
|
|
3632
4044
|
daemonStatus.relay.lastHandshakeError ? `Relay last handshake error: ${daemonStatus.relay.lastHandshakeError.code} (${daemonStatus.relay.lastHandshakeError.message})` : "Relay last handshake error: none",
|
|
3633
4045
|
"Legend: ext=extension websocket, handshake=extension handshake, cdp=active /cdp client, annotate=annotation channel, ops=ops clients, canvas=canvas clients, pairing=token required, health=relay status"
|
|
3634
4046
|
];
|
|
3635
4047
|
if (!nativeAssessment.success) {
|
|
3636
|
-
baseLines.splice(
|
|
4048
|
+
baseLines.splice(4, 0, `Native detail: ${nativeAssessment.message}`);
|
|
3637
4049
|
}
|
|
3638
4050
|
const baseMessage = baseLines.join("\n");
|
|
3639
4051
|
const message = daemon || args.outputFormat !== "text" ? baseMessage : [
|
|
@@ -3850,12 +4262,12 @@ function parseSnapshotArgs(rawArgs) {
|
|
|
3850
4262
|
if (arg === "--max-chars") {
|
|
3851
4263
|
const value = rawArgs[i + 1];
|
|
3852
4264
|
if (!value) throw createUsageError("Missing value for --max-chars");
|
|
3853
|
-
parsed.maxChars =
|
|
4265
|
+
parsed.maxChars = parseNumberFlag(value, "--max-chars", { min: 1 });
|
|
3854
4266
|
i += 1;
|
|
3855
4267
|
continue;
|
|
3856
4268
|
}
|
|
3857
4269
|
if (arg?.startsWith("--max-chars=")) {
|
|
3858
|
-
parsed.maxChars =
|
|
4270
|
+
parsed.maxChars = parseNumberFlag(arg.split("=", 2)[1] ?? "", "--max-chars", { min: 1 });
|
|
3859
4271
|
continue;
|
|
3860
4272
|
}
|
|
3861
4273
|
if (arg === "--cursor") {
|
|
@@ -4114,7 +4526,7 @@ async function runAnnotate(args) {
|
|
|
4114
4526
|
}
|
|
4115
4527
|
|
|
4116
4528
|
// src/cli/commands/canvas.ts
|
|
4117
|
-
import { readFileSync as
|
|
4529
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
4118
4530
|
var DEFAULT_FEEDBACK_STREAM_TIMEOUT_MS = 3e4;
|
|
4119
4531
|
var isRecord = (value) => {
|
|
4120
4532
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -4315,7 +4727,7 @@ var resolveCanvasParams = (canvasArgs) => {
|
|
|
4315
4727
|
if (hasParamsFile) {
|
|
4316
4728
|
let raw = "";
|
|
4317
4729
|
try {
|
|
4318
|
-
raw =
|
|
4730
|
+
raw = readFileSync4(canvasArgs.paramsFile ?? "", "utf8");
|
|
4319
4731
|
} catch (error) {
|
|
4320
4732
|
const message = error instanceof Error ? error.message : "Unable to read file";
|
|
4321
4733
|
throw createUsageError(`Invalid --params-file: ${message}`);
|
|
@@ -4384,7 +4796,7 @@ async function runCanvas(args) {
|
|
|
4384
4796
|
}
|
|
4385
4797
|
|
|
4386
4798
|
// src/cli/commands/rpc.ts
|
|
4387
|
-
import { readFileSync as
|
|
4799
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
4388
4800
|
var requireValue4 = (value, flag) => {
|
|
4389
4801
|
if (!value) throw createUsageError(`Missing value for ${flag}`);
|
|
4390
4802
|
return value;
|
|
@@ -4464,7 +4876,7 @@ var resolveRpcParams = (rpcArgs) => {
|
|
|
4464
4876
|
if (hasParamsFile) {
|
|
4465
4877
|
let raw = "";
|
|
4466
4878
|
try {
|
|
4467
|
-
raw =
|
|
4879
|
+
raw = readFileSync5(rpcArgs.paramsFile ?? "", "utf8");
|
|
4468
4880
|
} catch (error) {
|
|
4469
4881
|
const message = error instanceof Error ? error.message : "Unable to read file";
|
|
4470
4882
|
throw createUsageError(`Invalid --params-file: ${message}`);
|
|
@@ -4895,12 +5307,12 @@ function parseScrollArgs(rawArgs) {
|
|
|
4895
5307
|
if (arg === "--dy") {
|
|
4896
5308
|
const value = rawArgs[i + 1];
|
|
4897
5309
|
if (!value) throw createUsageError("Missing value for --dy");
|
|
4898
|
-
parsed.dy =
|
|
5310
|
+
parsed.dy = parseNumberFlag(value, "--dy");
|
|
4899
5311
|
i += 1;
|
|
4900
5312
|
continue;
|
|
4901
5313
|
}
|
|
4902
5314
|
if (arg?.startsWith("--dy=")) {
|
|
4903
|
-
parsed.dy =
|
|
5315
|
+
parsed.dy = parseNumberFlag(arg.split("=", 2)[1] ?? "", "--dy");
|
|
4904
5316
|
continue;
|
|
4905
5317
|
}
|
|
4906
5318
|
}
|
|
@@ -5396,12 +5808,12 @@ function parseDomHtmlArgs(rawArgs) {
|
|
|
5396
5808
|
if (arg === "--max-chars") {
|
|
5397
5809
|
const value = rawArgs[i + 1];
|
|
5398
5810
|
if (!value) throw createUsageError("Missing value for --max-chars");
|
|
5399
|
-
parsed.maxChars =
|
|
5811
|
+
parsed.maxChars = parseNumberFlag(value, "--max-chars", { min: 1 });
|
|
5400
5812
|
i += 1;
|
|
5401
5813
|
continue;
|
|
5402
5814
|
}
|
|
5403
5815
|
if (arg?.startsWith("--max-chars=")) {
|
|
5404
|
-
parsed.maxChars =
|
|
5816
|
+
parsed.maxChars = parseNumberFlag(arg.split("=", 2)[1] ?? "", "--max-chars", { min: 1 });
|
|
5405
5817
|
continue;
|
|
5406
5818
|
}
|
|
5407
5819
|
}
|
|
@@ -5451,12 +5863,12 @@ function parseDomTextArgs(rawArgs) {
|
|
|
5451
5863
|
if (arg === "--max-chars") {
|
|
5452
5864
|
const value = rawArgs[i + 1];
|
|
5453
5865
|
if (!value) throw createUsageError("Missing value for --max-chars");
|
|
5454
|
-
parsed.maxChars =
|
|
5866
|
+
parsed.maxChars = parseNumberFlag(value, "--max-chars", { min: 1 });
|
|
5455
5867
|
i += 1;
|
|
5456
5868
|
continue;
|
|
5457
5869
|
}
|
|
5458
5870
|
if (arg?.startsWith("--max-chars=")) {
|
|
5459
|
-
parsed.maxChars =
|
|
5871
|
+
parsed.maxChars = parseNumberFlag(arg.split("=", 2)[1] ?? "", "--max-chars", { min: 1 });
|
|
5460
5872
|
continue;
|
|
5461
5873
|
}
|
|
5462
5874
|
}
|
|
@@ -5971,23 +6383,23 @@ function parseConsolePollArgs(rawArgs) {
|
|
|
5971
6383
|
if (arg === "--since-seq") {
|
|
5972
6384
|
const value = rawArgs[i + 1];
|
|
5973
6385
|
if (!value) throw createUsageError("Missing value for --since-seq");
|
|
5974
|
-
parsed.sinceSeq =
|
|
6386
|
+
parsed.sinceSeq = parseNumberFlag(value, "--since-seq", { min: 0 });
|
|
5975
6387
|
i += 1;
|
|
5976
6388
|
continue;
|
|
5977
6389
|
}
|
|
5978
6390
|
if (arg?.startsWith("--since-seq=")) {
|
|
5979
|
-
parsed.sinceSeq =
|
|
6391
|
+
parsed.sinceSeq = parseNumberFlag(arg.split("=", 2)[1] ?? "", "--since-seq", { min: 0 });
|
|
5980
6392
|
continue;
|
|
5981
6393
|
}
|
|
5982
6394
|
if (arg === "--max") {
|
|
5983
6395
|
const value = rawArgs[i + 1];
|
|
5984
6396
|
if (!value) throw createUsageError("Missing value for --max");
|
|
5985
|
-
parsed.max =
|
|
6397
|
+
parsed.max = parseNumberFlag(value, "--max", { min: 1 });
|
|
5986
6398
|
i += 1;
|
|
5987
6399
|
continue;
|
|
5988
6400
|
}
|
|
5989
6401
|
if (arg?.startsWith("--max=")) {
|
|
5990
|
-
parsed.max =
|
|
6402
|
+
parsed.max = parseNumberFlag(arg.split("=", 2)[1] ?? "", "--max", { min: 1 });
|
|
5991
6403
|
continue;
|
|
5992
6404
|
}
|
|
5993
6405
|
}
|
|
@@ -6019,23 +6431,23 @@ function parseNetworkPollArgs(rawArgs) {
|
|
|
6019
6431
|
if (arg === "--since-seq") {
|
|
6020
6432
|
const value = rawArgs[i + 1];
|
|
6021
6433
|
if (!value) throw createUsageError("Missing value for --since-seq");
|
|
6022
|
-
parsed.sinceSeq =
|
|
6434
|
+
parsed.sinceSeq = parseNumberFlag(value, "--since-seq", { min: 0 });
|
|
6023
6435
|
i += 1;
|
|
6024
6436
|
continue;
|
|
6025
6437
|
}
|
|
6026
6438
|
if (arg?.startsWith("--since-seq=")) {
|
|
6027
|
-
parsed.sinceSeq =
|
|
6439
|
+
parsed.sinceSeq = parseNumberFlag(arg.split("=", 2)[1] ?? "", "--since-seq", { min: 0 });
|
|
6028
6440
|
continue;
|
|
6029
6441
|
}
|
|
6030
6442
|
if (arg === "--max") {
|
|
6031
6443
|
const value = rawArgs[i + 1];
|
|
6032
6444
|
if (!value) throw createUsageError("Missing value for --max");
|
|
6033
|
-
parsed.max =
|
|
6445
|
+
parsed.max = parseNumberFlag(value, "--max", { min: 1 });
|
|
6034
6446
|
i += 1;
|
|
6035
6447
|
continue;
|
|
6036
6448
|
}
|
|
6037
6449
|
if (arg?.startsWith("--max=")) {
|
|
6038
|
-
parsed.max =
|
|
6450
|
+
parsed.max = parseNumberFlag(arg.split("=", 2)[1] ?? "", "--max", { min: 1 });
|
|
6039
6451
|
continue;
|
|
6040
6452
|
}
|
|
6041
6453
|
}
|
|
@@ -6247,7 +6659,7 @@ async function runDesktopAccessibilitySnapshot(args) {
|
|
|
6247
6659
|
}
|
|
6248
6660
|
|
|
6249
6661
|
// src/cli/commands/session/cookie-import.ts
|
|
6250
|
-
import { readFileSync as
|
|
6662
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
6251
6663
|
var requireValue5 = (value, flag) => {
|
|
6252
6664
|
if (!value) {
|
|
6253
6665
|
throw createUsageError(`Missing value for ${flag}`);
|
|
@@ -6362,7 +6774,7 @@ var resolveCookies = (parsed) => {
|
|
|
6362
6774
|
}
|
|
6363
6775
|
let raw = "";
|
|
6364
6776
|
try {
|
|
6365
|
-
raw =
|
|
6777
|
+
raw = readFileSync6(parsed.cookiesFile ?? "", "utf8");
|
|
6366
6778
|
} catch (error) {
|
|
6367
6779
|
const message = error instanceof Error ? error.message : "Unable to read file";
|
|
6368
6780
|
throw createUsageError(`Invalid --cookies-file: ${message}`);
|
|
@@ -7415,7 +7827,7 @@ async function runInspiredesignCommand(args) {
|
|
|
7415
7827
|
// package.json
|
|
7416
7828
|
var package_default = {
|
|
7417
7829
|
name: "opendevbrowser",
|
|
7418
|
-
version: "0.0.
|
|
7830
|
+
version: "0.0.27",
|
|
7419
7831
|
description: "Browser automation runtime with snapshot-refs-actions, browser replay screencasts, public read-only desktop observation, and browser-scoped computer-use orchestration",
|
|
7420
7832
|
type: "module",
|
|
7421
7833
|
main: "dist/index.js",
|
|
@@ -7749,7 +8161,7 @@ async function main() {
|
|
|
7749
8161
|
});
|
|
7750
8162
|
registerCommand({
|
|
7751
8163
|
name: "update",
|
|
7752
|
-
description: "
|
|
8164
|
+
description: "Repair cached plugin pins and refresh managed skill packs",
|
|
7753
8165
|
run: () => buildUpdateCommandResult(args, runUpdate(), {
|
|
7754
8166
|
resolveUpdateSkillModes,
|
|
7755
8167
|
hasInstalledConfig,
|