overlord-cli 0.2608141252.0 → 0.2608170844.0
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/dist/index.js +791 -689
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -278,16 +278,16 @@ function resolveCommandArgs({
|
|
|
278
278
|
return parsed;
|
|
279
279
|
}
|
|
280
280
|
function normalizeVersion(raw) {
|
|
281
|
-
const
|
|
282
|
-
if (!
|
|
281
|
+
const trimmed8 = raw.trim();
|
|
282
|
+
if (!trimmed8) {
|
|
283
283
|
throw new CliError({ message: "Received an empty version from npm." });
|
|
284
284
|
}
|
|
285
285
|
try {
|
|
286
|
-
const parsed = JSON.parse(
|
|
286
|
+
const parsed = JSON.parse(trimmed8);
|
|
287
287
|
if (typeof parsed === "string" && parsed.trim()) return parsed.trim();
|
|
288
288
|
} catch {
|
|
289
289
|
}
|
|
290
|
-
return
|
|
290
|
+
return trimmed8.replace(/^"+|"+$/g, "");
|
|
291
291
|
}
|
|
292
292
|
function compareVersions(left, right) {
|
|
293
293
|
const leftParts = left.split(".").map((part) => Number.parseInt(part, 10));
|
|
@@ -1346,11 +1346,11 @@ import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3, wr
|
|
|
1346
1346
|
import os from "node:os";
|
|
1347
1347
|
import path3 from "node:path";
|
|
1348
1348
|
function normalizeBackendUrl(value) {
|
|
1349
|
-
const
|
|
1350
|
-
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(
|
|
1351
|
-
return
|
|
1349
|
+
const trimmed8 = value.trim().replace(/\/+$/, "");
|
|
1350
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed8)) {
|
|
1351
|
+
return trimmed8;
|
|
1352
1352
|
}
|
|
1353
|
-
return `http://${
|
|
1353
|
+
return `http://${trimmed8}`;
|
|
1354
1354
|
}
|
|
1355
1355
|
function isLoopbackBackendUrl(backendUrl) {
|
|
1356
1356
|
try {
|
|
@@ -2412,12 +2412,12 @@ function parseConnectorManifestYaml(text) {
|
|
|
2412
2412
|
};
|
|
2413
2413
|
for (const rawLine of text.split("\n")) {
|
|
2414
2414
|
const line = rawLine.replace(/\r$/, "");
|
|
2415
|
-
const
|
|
2416
|
-
if (
|
|
2415
|
+
const trimmed8 = line.trim();
|
|
2416
|
+
if (trimmed8 === "" || trimmed8.startsWith("#")) continue;
|
|
2417
2417
|
const indent = line.length - line.trimStart().length;
|
|
2418
2418
|
if (pending) {
|
|
2419
2419
|
if (indent > pending.indent) {
|
|
2420
|
-
const container =
|
|
2420
|
+
const container = trimmed8.startsWith("- ") ? [] : {};
|
|
2421
2421
|
pending.parent[pending.key] = container;
|
|
2422
2422
|
stack.push({ indent, container });
|
|
2423
2423
|
pending = null;
|
|
@@ -2430,16 +2430,16 @@ function parseConnectorManifestYaml(text) {
|
|
|
2430
2430
|
stack.pop();
|
|
2431
2431
|
}
|
|
2432
2432
|
const frame = stack[stack.length - 1];
|
|
2433
|
-
if (
|
|
2433
|
+
if (trimmed8.startsWith("- ")) {
|
|
2434
2434
|
if (Array.isArray(frame.container)) {
|
|
2435
|
-
frame.container.push(parseScalar(
|
|
2435
|
+
frame.container.push(parseScalar(trimmed8.slice(2)));
|
|
2436
2436
|
}
|
|
2437
2437
|
continue;
|
|
2438
2438
|
}
|
|
2439
|
-
const colon =
|
|
2439
|
+
const colon = trimmed8.indexOf(":");
|
|
2440
2440
|
if (colon < 0 || Array.isArray(frame.container)) continue;
|
|
2441
|
-
const key =
|
|
2442
|
-
const rest =
|
|
2441
|
+
const key = trimmed8.slice(0, colon).trim();
|
|
2442
|
+
const rest = trimmed8.slice(colon + 1).trim();
|
|
2443
2443
|
if (rest === "") {
|
|
2444
2444
|
pending = { parent: frame.container, key, indent };
|
|
2445
2445
|
} else {
|
|
@@ -2633,10 +2633,10 @@ ${managedBlock}
|
|
|
2633
2633
|
|
|
2634
2634
|
${after}`;
|
|
2635
2635
|
}
|
|
2636
|
-
const
|
|
2637
|
-
if (!
|
|
2636
|
+
const trimmed8 = existingContent.trimEnd();
|
|
2637
|
+
if (!trimmed8) return `${managedBlock}
|
|
2638
2638
|
`;
|
|
2639
|
-
return `${
|
|
2639
|
+
return `${trimmed8}
|
|
2640
2640
|
|
|
2641
2641
|
${managedBlock}
|
|
2642
2642
|
`;
|
|
@@ -3073,16 +3073,21 @@ function normalizeBaseUrl4(value) {
|
|
|
3073
3073
|
function resolveAuthHeaders({ baseUrl }) {
|
|
3074
3074
|
const fromEnv = process.env.OVERLORD_USER_TOKEN?.trim() || process.env.OVLD_USER_TOKEN?.trim() || process.env.USER_TOKEN?.trim();
|
|
3075
3075
|
if (fromEnv) {
|
|
3076
|
-
return {
|
|
3076
|
+
return {
|
|
3077
|
+
headers: { Authorization: `Bearer ${fromEnv}` },
|
|
3078
|
+
fromStored: false,
|
|
3079
|
+
storedCredentialBackendUrl: null
|
|
3080
|
+
};
|
|
3077
3081
|
}
|
|
3078
3082
|
const stored = readStoredAuthCredentials();
|
|
3079
|
-
if (!stored) return { headers: {}, fromStored: false };
|
|
3083
|
+
if (!stored) return { headers: {}, fromStored: false, storedCredentialBackendUrl: null };
|
|
3080
3084
|
if (normalizeBaseUrl4(stored.backendUrl) !== normalizeBaseUrl4(baseUrl)) {
|
|
3081
|
-
return { headers: {}, fromStored: false };
|
|
3085
|
+
return { headers: {}, fromStored: false, storedCredentialBackendUrl: stored.backendUrl };
|
|
3082
3086
|
}
|
|
3083
3087
|
return {
|
|
3084
3088
|
headers: { Authorization: `Bearer ${stored.token}` },
|
|
3085
|
-
fromStored: true
|
|
3089
|
+
fromStored: true,
|
|
3090
|
+
storedCredentialBackendUrl: null
|
|
3086
3091
|
};
|
|
3087
3092
|
}
|
|
3088
3093
|
async function readResponseJson2(response) {
|
|
@@ -3146,6 +3151,12 @@ function createBackendClient() {
|
|
|
3146
3151
|
}) {
|
|
3147
3152
|
const url = `${baseUrl}${path34.startsWith("/") ? path34 : `/${path34}`}`;
|
|
3148
3153
|
const auth = resolveAuthHeaders({ baseUrl });
|
|
3154
|
+
if (auth.storedCredentialBackendUrl) {
|
|
3155
|
+
throw new CliError({
|
|
3156
|
+
message: `Saved credentials are for ${normalizeBaseUrl4(auth.storedCredentialBackendUrl)}, but this CLI is configured for ${baseUrl}.
|
|
3157
|
+
The backend URL may have changed. Run \`ovld auth login\` to sign in to the current backend.`
|
|
3158
|
+
});
|
|
3159
|
+
}
|
|
3149
3160
|
let response;
|
|
3150
3161
|
try {
|
|
3151
3162
|
response = await fetch(url, {
|
|
@@ -5555,8 +5566,8 @@ function resolveNativeSessionId({
|
|
|
5555
5566
|
env = process.env
|
|
5556
5567
|
}) {
|
|
5557
5568
|
if (explicit !== void 0) {
|
|
5558
|
-
const
|
|
5559
|
-
return
|
|
5569
|
+
const trimmed8 = explicit.trim();
|
|
5570
|
+
return trimmed8 === "null" ? null : trimmed8 || void 0;
|
|
5560
5571
|
}
|
|
5561
5572
|
const normalizedAgent = agent.toLowerCase();
|
|
5562
5573
|
if (normalizedAgent === "codex") {
|
|
@@ -16324,22 +16335,22 @@ var init_contract = __esm({
|
|
|
16324
16335
|
|
|
16325
16336
|
// ../packages/contract/dist/agent-launch-flags.js
|
|
16326
16337
|
function parseAgentLaunchFlagText(text) {
|
|
16327
|
-
const
|
|
16328
|
-
if (!
|
|
16338
|
+
const trimmed8 = text.trim();
|
|
16339
|
+
if (!trimmed8)
|
|
16329
16340
|
return null;
|
|
16330
|
-
const eqIndex =
|
|
16331
|
-
if (eqIndex > 0 &&
|
|
16332
|
-
const name =
|
|
16333
|
-
const value =
|
|
16341
|
+
const eqIndex = trimmed8.indexOf("=");
|
|
16342
|
+
if (eqIndex > 0 && trimmed8.startsWith("--")) {
|
|
16343
|
+
const name = trimmed8.slice(0, eqIndex).trim();
|
|
16344
|
+
const value = trimmed8.slice(eqIndex + 1).trim();
|
|
16334
16345
|
return name ? { name, value: value.length > 0 ? value : null } : null;
|
|
16335
16346
|
}
|
|
16336
|
-
const spaceIndex =
|
|
16337
|
-
if (spaceIndex > 0 &&
|
|
16338
|
-
const name =
|
|
16339
|
-
const value =
|
|
16347
|
+
const spaceIndex = trimmed8.indexOf(" ");
|
|
16348
|
+
if (spaceIndex > 0 && trimmed8.startsWith("--")) {
|
|
16349
|
+
const name = trimmed8.slice(0, spaceIndex).trim();
|
|
16350
|
+
const value = trimmed8.slice(spaceIndex + 1).trim();
|
|
16340
16351
|
return name ? { name, value: value.length > 0 ? value : null } : null;
|
|
16341
16352
|
}
|
|
16342
|
-
return { name:
|
|
16353
|
+
return { name: trimmed8 };
|
|
16343
16354
|
}
|
|
16344
16355
|
function normalizeAgentLaunchFlags(input) {
|
|
16345
16356
|
if (!Array.isArray(input))
|
|
@@ -16490,11 +16501,11 @@ function accessModeToResourcePathPermission({ accessMode, isPrimary = false }) {
|
|
|
16490
16501
|
return accessMode === "read" ? "ro" : "rw";
|
|
16491
16502
|
}
|
|
16492
16503
|
function formatResourcePathWithPermission({ path: path34, accessMode, isPrimary = false }) {
|
|
16493
|
-
const
|
|
16494
|
-
if (!
|
|
16504
|
+
const trimmed8 = path34.trim();
|
|
16505
|
+
if (!trimmed8)
|
|
16495
16506
|
return "";
|
|
16496
16507
|
const permission = accessModeToResourcePathPermission({ accessMode, isPrimary });
|
|
16497
|
-
return `${
|
|
16508
|
+
return `${trimmed8}:${permission}`;
|
|
16498
16509
|
}
|
|
16499
16510
|
function formatProjectResourcePathsFromManifest(projectResources) {
|
|
16500
16511
|
if (!Array.isArray(projectResources))
|
|
@@ -16632,12 +16643,304 @@ var init_terminal_profile_types2 = __esm({
|
|
|
16632
16643
|
}
|
|
16633
16644
|
});
|
|
16634
16645
|
|
|
16635
|
-
// ../packages/core/dist/service/latch-
|
|
16646
|
+
// ../packages/core/dist/service/latch-discovery.js
|
|
16647
|
+
import { execFileSync } from "node:child_process";
|
|
16648
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync8, readFileSync as readFileSync14, statSync as statSync4, writeFileSync as writeFileSync7 } from "node:fs";
|
|
16649
|
+
import os3 from "node:os";
|
|
16650
|
+
import path15 from "node:path";
|
|
16651
|
+
function defaultLatchDiscoveryCachePath() {
|
|
16652
|
+
const home = process.env.OVLD_HOME?.trim() || path15.join(os3.homedir(), ".ovld");
|
|
16653
|
+
return path15.join(home, LATCH_DISCOVERY_CACHE_FILENAME);
|
|
16654
|
+
}
|
|
16636
16655
|
function trimmed2(value) {
|
|
16637
16656
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
16638
16657
|
}
|
|
16658
|
+
function asBoolean(value) {
|
|
16659
|
+
return value === true;
|
|
16660
|
+
}
|
|
16661
|
+
function asStringArray(value) {
|
|
16662
|
+
if (!Array.isArray(value))
|
|
16663
|
+
return [];
|
|
16664
|
+
return value.filter((entry) => typeof entry === "string");
|
|
16665
|
+
}
|
|
16666
|
+
function parseLatchCapabilitiesReport(raw) {
|
|
16667
|
+
const text = raw.trim();
|
|
16668
|
+
if (!text)
|
|
16669
|
+
return null;
|
|
16670
|
+
let parsed;
|
|
16671
|
+
try {
|
|
16672
|
+
parsed = JSON.parse(text);
|
|
16673
|
+
} catch {
|
|
16674
|
+
return null;
|
|
16675
|
+
}
|
|
16676
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
16677
|
+
return null;
|
|
16678
|
+
const cast = parsed;
|
|
16679
|
+
const protocolVersion = typeof cast.protocolVersion === "number" && Number.isFinite(cast.protocolVersion) ? Math.trunc(cast.protocolVersion) : null;
|
|
16680
|
+
const productVersion = trimmed2(cast.productVersion);
|
|
16681
|
+
if (protocolVersion === null || !productVersion)
|
|
16682
|
+
return null;
|
|
16683
|
+
if (!cast.capabilities || typeof cast.capabilities !== "object" || Array.isArray(cast.capabilities)) {
|
|
16684
|
+
return null;
|
|
16685
|
+
}
|
|
16686
|
+
const caps = cast.capabilities;
|
|
16687
|
+
return {
|
|
16688
|
+
protocolVersion,
|
|
16689
|
+
productVersion,
|
|
16690
|
+
capabilities: {
|
|
16691
|
+
create: asBoolean(caps.create),
|
|
16692
|
+
openViewer: asBoolean(caps.openViewer),
|
|
16693
|
+
localAttach: asBoolean(caps.localAttach),
|
|
16694
|
+
cloudAttach: asBoolean(caps.cloudAttach),
|
|
16695
|
+
selfUpdate: asBoolean(caps.selfUpdate),
|
|
16696
|
+
extensions: asStringArray(caps.extensions)
|
|
16697
|
+
}
|
|
16698
|
+
};
|
|
16699
|
+
}
|
|
16700
|
+
function evaluateLatchCapabilities({ report, executable, resolvedPath, checkedAt = (/* @__PURE__ */ new Date()).toISOString() }) {
|
|
16701
|
+
if (report.protocolVersion !== SUPPORTED_LATCH_PROTOCOL_VERSION) {
|
|
16702
|
+
return {
|
|
16703
|
+
state: "incompatible",
|
|
16704
|
+
executable,
|
|
16705
|
+
resolvedPath,
|
|
16706
|
+
protocolVersion: report.protocolVersion,
|
|
16707
|
+
productVersion: report.productVersion,
|
|
16708
|
+
missingCapability: "protocolVersion",
|
|
16709
|
+
detail: `Latch protocolVersion ${report.protocolVersion} is not supported (need ${SUPPORTED_LATCH_PROTOCOL_VERSION}).`,
|
|
16710
|
+
checkedAt,
|
|
16711
|
+
latchSelectable: false,
|
|
16712
|
+
directSelectable: true
|
|
16713
|
+
};
|
|
16714
|
+
}
|
|
16715
|
+
for (const required of REQUIRED_LATCH_CAPABILITIES) {
|
|
16716
|
+
if (!report.capabilities[required]) {
|
|
16717
|
+
return {
|
|
16718
|
+
state: "incompatible",
|
|
16719
|
+
executable,
|
|
16720
|
+
resolvedPath,
|
|
16721
|
+
protocolVersion: report.protocolVersion,
|
|
16722
|
+
productVersion: report.productVersion,
|
|
16723
|
+
missingCapability: required,
|
|
16724
|
+
detail: `Latch is missing required capability \`${required}\`.`,
|
|
16725
|
+
checkedAt,
|
|
16726
|
+
latchSelectable: false,
|
|
16727
|
+
directSelectable: true
|
|
16728
|
+
};
|
|
16729
|
+
}
|
|
16730
|
+
}
|
|
16731
|
+
return {
|
|
16732
|
+
state: "found",
|
|
16733
|
+
executable,
|
|
16734
|
+
resolvedPath,
|
|
16735
|
+
protocolVersion: report.protocolVersion,
|
|
16736
|
+
productVersion: report.productVersion,
|
|
16737
|
+
capabilities: report.capabilities,
|
|
16738
|
+
checkedAt,
|
|
16739
|
+
latchSelectable: true,
|
|
16740
|
+
directSelectable: true
|
|
16741
|
+
};
|
|
16742
|
+
}
|
|
16743
|
+
function resolveLatchExecutableOnPath(executable) {
|
|
16744
|
+
try {
|
|
16745
|
+
const found = execFileSync("which", [executable], {
|
|
16746
|
+
encoding: "utf8",
|
|
16747
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
16748
|
+
}).trim();
|
|
16749
|
+
return found || null;
|
|
16750
|
+
} catch {
|
|
16751
|
+
return null;
|
|
16752
|
+
}
|
|
16753
|
+
}
|
|
16754
|
+
function resolveLatchExecutablePathFromEnvironment({ executable, homeDirectory = os3.homedir(), platform: platform2 = process.platform, resolveOnPath = resolveLatchExecutableOnPath, fileExists = existsSync15 }) {
|
|
16755
|
+
const name = trimmed2(executable) ?? DEFAULT_LATCH_EXECUTABLE;
|
|
16756
|
+
if (path15.isAbsolute(name) || name.includes("/") || name.includes("\\")) {
|
|
16757
|
+
return fileExists(name) ? path15.resolve(name) : null;
|
|
16758
|
+
}
|
|
16759
|
+
const resolvedOnPath = resolveOnPath(name);
|
|
16760
|
+
if (resolvedOnPath)
|
|
16761
|
+
return resolvedOnPath;
|
|
16762
|
+
const fallbacks = [
|
|
16763
|
+
path15.join(homeDirectory, ".local", "bin", name),
|
|
16764
|
+
...platform2 === "darwin" ? [`/opt/homebrew/bin/${name}`, `/usr/local/bin/${name}`] : []
|
|
16765
|
+
];
|
|
16766
|
+
const fallback = fallbacks.find(fileExists);
|
|
16767
|
+
return fallback ? path15.resolve(fallback) : null;
|
|
16768
|
+
}
|
|
16769
|
+
function resolveLatchExecutablePath(executable) {
|
|
16770
|
+
return resolveLatchExecutablePathFromEnvironment({ executable });
|
|
16771
|
+
}
|
|
16772
|
+
function notInstalledResult({ executable, checkedAt }) {
|
|
16773
|
+
return {
|
|
16774
|
+
state: "not_installed",
|
|
16775
|
+
executable,
|
|
16776
|
+
installCommand: LATCH_STANDALONE_INSTALL_COMMAND,
|
|
16777
|
+
checkedAt,
|
|
16778
|
+
latchSelectable: false,
|
|
16779
|
+
directSelectable: true
|
|
16780
|
+
};
|
|
16781
|
+
}
|
|
16782
|
+
function probeLatchCapabilities({ executable = DEFAULT_LATCH_EXECUTABLE, resolvePath = resolveLatchExecutablePath, runCommand: runCommand2 = defaultCommandRunner, now = () => (/* @__PURE__ */ new Date()).toISOString() } = {}) {
|
|
16783
|
+
const exe = trimmed2(executable) ?? DEFAULT_LATCH_EXECUTABLE;
|
|
16784
|
+
const checkedAt = now();
|
|
16785
|
+
const resolvedPath = resolvePath(exe);
|
|
16786
|
+
if (!resolvedPath) {
|
|
16787
|
+
return notInstalledResult({ executable: exe, checkedAt });
|
|
16788
|
+
}
|
|
16789
|
+
const ran = runCommand2({ executable: resolvedPath, argv: ["capabilities", "--json"] });
|
|
16790
|
+
if (ran.status === null && ran.stderr === "ENOENT") {
|
|
16791
|
+
return notInstalledResult({ executable: exe, checkedAt });
|
|
16792
|
+
}
|
|
16793
|
+
const report = parseLatchCapabilitiesReport(ran.stdout);
|
|
16794
|
+
if (!report) {
|
|
16795
|
+
return {
|
|
16796
|
+
state: "incompatible",
|
|
16797
|
+
executable: exe,
|
|
16798
|
+
resolvedPath,
|
|
16799
|
+
protocolVersion: null,
|
|
16800
|
+
productVersion: null,
|
|
16801
|
+
missingCapability: "capabilities",
|
|
16802
|
+
detail: ran.status === 0 ? "Latch capabilities output was not valid JSON in the expected shape." : `Latch capabilities failed (exit ${ran.status ?? "null"}).`,
|
|
16803
|
+
checkedAt,
|
|
16804
|
+
latchSelectable: false,
|
|
16805
|
+
directSelectable: true
|
|
16806
|
+
};
|
|
16807
|
+
}
|
|
16808
|
+
return evaluateLatchCapabilities({ report, executable: exe, resolvedPath, checkedAt });
|
|
16809
|
+
}
|
|
16810
|
+
function latchDiscoveryCacheKey({ executionTargetId, executable }) {
|
|
16811
|
+
const exe = trimmed2(executable) ?? DEFAULT_LATCH_EXECUTABLE;
|
|
16812
|
+
return `${executionTargetId}::${exe}`;
|
|
16813
|
+
}
|
|
16814
|
+
function readBinaryMtimeMs(resolvedPath) {
|
|
16815
|
+
if (!resolvedPath)
|
|
16816
|
+
return null;
|
|
16817
|
+
try {
|
|
16818
|
+
return statSync4(resolvedPath).mtimeMs;
|
|
16819
|
+
} catch {
|
|
16820
|
+
return null;
|
|
16821
|
+
}
|
|
16822
|
+
}
|
|
16823
|
+
function isLatchDiscoveryCacheFresh({ entry, resolvePath = resolveLatchExecutablePath }) {
|
|
16824
|
+
const { result } = entry;
|
|
16825
|
+
if (result.state === "not_installed") {
|
|
16826
|
+
return resolvePath(result.executable) === null;
|
|
16827
|
+
}
|
|
16828
|
+
const pathStill = result.resolvedPath;
|
|
16829
|
+
if (!pathStill || !existsSync15(pathStill))
|
|
16830
|
+
return false;
|
|
16831
|
+
const mtime = readBinaryMtimeMs(pathStill);
|
|
16832
|
+
if (mtime === null || entry.binaryMtimeMs === null)
|
|
16833
|
+
return false;
|
|
16834
|
+
return mtime === entry.binaryMtimeMs;
|
|
16835
|
+
}
|
|
16836
|
+
function readLatchDiscoveryCacheFile(filePath) {
|
|
16837
|
+
if (!existsSync15(filePath))
|
|
16838
|
+
return { version: 1, entries: {} };
|
|
16839
|
+
try {
|
|
16840
|
+
const parsed = JSON.parse(readFileSync14(filePath, "utf8"));
|
|
16841
|
+
if (parsed.version !== 1 || !parsed.entries || typeof parsed.entries !== "object") {
|
|
16842
|
+
return { version: 1, entries: {} };
|
|
16843
|
+
}
|
|
16844
|
+
return { version: 1, entries: parsed.entries };
|
|
16845
|
+
} catch {
|
|
16846
|
+
return { version: 1, entries: {} };
|
|
16847
|
+
}
|
|
16848
|
+
}
|
|
16849
|
+
function writeLatchDiscoveryCacheFile({ filePath, cache: cache2 }) {
|
|
16850
|
+
mkdirSync8(path15.dirname(filePath), { recursive: true });
|
|
16851
|
+
writeFileSync7(filePath, `${JSON.stringify(cache2, null, 2)}
|
|
16852
|
+
`, { mode: 384 });
|
|
16853
|
+
}
|
|
16854
|
+
function discoverLatchForExecutionTarget({ executionTargetId, executable = DEFAULT_LATCH_EXECUTABLE, cacheFilePath, force = false, resolvePath = resolveLatchExecutablePath, runCommand: runCommand2 = defaultCommandRunner, now = () => (/* @__PURE__ */ new Date()).toISOString() }) {
|
|
16855
|
+
const exe = trimmed2(executable) ?? DEFAULT_LATCH_EXECUTABLE;
|
|
16856
|
+
const key = latchDiscoveryCacheKey({ executionTargetId, executable: exe });
|
|
16857
|
+
const cache2 = readLatchDiscoveryCacheFile(cacheFilePath);
|
|
16858
|
+
const existing = cache2.entries[key];
|
|
16859
|
+
if (!force && existing && isLatchDiscoveryCacheFresh({ entry: existing, resolvePath })) {
|
|
16860
|
+
return { ...existing.result, checkedAt: existing.result.checkedAt, directSelectable: true };
|
|
16861
|
+
}
|
|
16862
|
+
const result = probeLatchCapabilities({ executable: exe, resolvePath, runCommand: runCommand2, now });
|
|
16863
|
+
const binaryMtimeMs = result.state === "not_installed" ? null : readBinaryMtimeMs(result.resolvedPath);
|
|
16864
|
+
cache2.entries[key] = {
|
|
16865
|
+
result,
|
|
16866
|
+
binaryMtimeMs,
|
|
16867
|
+
cachedAt: result.checkedAt
|
|
16868
|
+
};
|
|
16869
|
+
writeLatchDiscoveryCacheFile({ filePath: cacheFilePath, cache: cache2 });
|
|
16870
|
+
return result;
|
|
16871
|
+
}
|
|
16872
|
+
var SUPPORTED_LATCH_PROTOCOL_VERSION, REQUIRED_LATCH_CAPABILITIES, LATCH_STANDALONE_INSTALL_COMMAND, LATCH_DISCOVERY_CACHE_FILENAME, defaultCommandRunner;
|
|
16873
|
+
var init_latch_discovery = __esm({
|
|
16874
|
+
"../packages/core/dist/service/latch-discovery.js"() {
|
|
16875
|
+
"use strict";
|
|
16876
|
+
init_latch_environment();
|
|
16877
|
+
init_terminal_profile_types2();
|
|
16878
|
+
SUPPORTED_LATCH_PROTOCOL_VERSION = 1;
|
|
16879
|
+
REQUIRED_LATCH_CAPABILITIES = ["create"];
|
|
16880
|
+
LATCH_STANDALONE_INSTALL_COMMAND = "curl -fsSL https://raw.githubusercontent.com/jchaselubitz/Latch/main/scripts/install-cli.sh | bash";
|
|
16881
|
+
LATCH_DISCOVERY_CACHE_FILENAME = "latch-discovery.json";
|
|
16882
|
+
defaultCommandRunner = ({ executable, argv }) => {
|
|
16883
|
+
try {
|
|
16884
|
+
const stdout = execFileSync(executable, argv, {
|
|
16885
|
+
encoding: "utf8",
|
|
16886
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
16887
|
+
timeout: 5e3,
|
|
16888
|
+
env: latchChildEnvironment()
|
|
16889
|
+
});
|
|
16890
|
+
return { status: 0, stdout: String(stdout), stderr: "" };
|
|
16891
|
+
} catch (error) {
|
|
16892
|
+
const err = error;
|
|
16893
|
+
if (err.code === "ENOENT") {
|
|
16894
|
+
return { status: null, stdout: "", stderr: "ENOENT" };
|
|
16895
|
+
}
|
|
16896
|
+
return {
|
|
16897
|
+
status: typeof err.status === "number" ? err.status : 1,
|
|
16898
|
+
stdout: err.stdout ? String(err.stdout) : "",
|
|
16899
|
+
stderr: err.stderr ? String(err.stderr) : String(error)
|
|
16900
|
+
};
|
|
16901
|
+
}
|
|
16902
|
+
};
|
|
16903
|
+
}
|
|
16904
|
+
});
|
|
16905
|
+
|
|
16906
|
+
// ../packages/core/dist/service/latch-binary.js
|
|
16907
|
+
import { existsSync as existsSync16 } from "node:fs";
|
|
16908
|
+
function trimmed3(value) {
|
|
16909
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
16910
|
+
}
|
|
16911
|
+
function resolveLatchBinaryPath(executable, resolve = resolveLatchExecutablePath) {
|
|
16912
|
+
const name = trimmed3(executable) ?? DEFAULT_LATCH_EXECUTABLE;
|
|
16913
|
+
const cached = cache.get(name);
|
|
16914
|
+
if (cached) {
|
|
16915
|
+
if (existsSync16(cached))
|
|
16916
|
+
return cached;
|
|
16917
|
+
cache.delete(name);
|
|
16918
|
+
}
|
|
16919
|
+
const resolved = resolve(name);
|
|
16920
|
+
if (resolved)
|
|
16921
|
+
cache.set(name, resolved);
|
|
16922
|
+
return resolved;
|
|
16923
|
+
}
|
|
16924
|
+
function latchBinaryMissingMessage(executable) {
|
|
16925
|
+
const name = trimmed3(executable) ?? DEFAULT_LATCH_EXECUTABLE;
|
|
16926
|
+
return `Latch executable "${name}" was not found on this device (checked PATH, ~/.local/bin, and the Homebrew prefixes).`;
|
|
16927
|
+
}
|
|
16928
|
+
var cache;
|
|
16929
|
+
var init_latch_binary = __esm({
|
|
16930
|
+
"../packages/core/dist/service/latch-binary.js"() {
|
|
16931
|
+
"use strict";
|
|
16932
|
+
init_latch_discovery();
|
|
16933
|
+
init_terminal_profile_types2();
|
|
16934
|
+
cache = /* @__PURE__ */ new Map();
|
|
16935
|
+
}
|
|
16936
|
+
});
|
|
16937
|
+
|
|
16938
|
+
// ../packages/core/dist/service/latch-launch.js
|
|
16939
|
+
function trimmed4(value) {
|
|
16940
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
16941
|
+
}
|
|
16639
16942
|
function buildLatchCreateManifest({ commandString, shell = process.env.SHELL?.trim() || "/bin/bash", cwd, env, size = DEFAULT_LATCH_TERMINAL_SIZE, title, name, commandLabel, externalRunId }) {
|
|
16640
|
-
const shellPath =
|
|
16943
|
+
const shellPath = trimmed4(shell) ?? "/bin/bash";
|
|
16641
16944
|
const cleanedEnv = {};
|
|
16642
16945
|
for (const [key, value] of Object.entries(env)) {
|
|
16643
16946
|
if (key.trim() && typeof value === "string")
|
|
@@ -16646,15 +16949,15 @@ function buildLatchCreateManifest({ commandString, shell = process.env.SHELL?.tr
|
|
|
16646
16949
|
const display = {
|
|
16647
16950
|
source: {
|
|
16648
16951
|
kind: "overlord",
|
|
16649
|
-
...
|
|
16952
|
+
...trimmed4(externalRunId) ? { external_run_id: trimmed4(externalRunId) } : {}
|
|
16650
16953
|
}
|
|
16651
16954
|
};
|
|
16652
|
-
if (
|
|
16653
|
-
display.title =
|
|
16654
|
-
if (
|
|
16655
|
-
display.name =
|
|
16656
|
-
if (
|
|
16657
|
-
display.command_label =
|
|
16955
|
+
if (trimmed4(title))
|
|
16956
|
+
display.title = trimmed4(title);
|
|
16957
|
+
if (trimmed4(name))
|
|
16958
|
+
display.name = trimmed4(name);
|
|
16959
|
+
if (trimmed4(commandLabel))
|
|
16960
|
+
display.command_label = trimmed4(commandLabel);
|
|
16658
16961
|
return {
|
|
16659
16962
|
format_version: LATCH_MANIFEST_FORMAT_VERSION,
|
|
16660
16963
|
launch: {
|
|
@@ -16673,7 +16976,7 @@ function buildLatchCreateManifest({ commandString, shell = process.env.SHELL?.tr
|
|
|
16673
16976
|
function parseLatchCreateReport(raw) {
|
|
16674
16977
|
try {
|
|
16675
16978
|
const parsed = JSON.parse(raw);
|
|
16676
|
-
const id =
|
|
16979
|
+
const id = trimmed4(parsed.session?.id);
|
|
16677
16980
|
if (!id)
|
|
16678
16981
|
return null;
|
|
16679
16982
|
const protocolVersion = typeof parsed.protocolVersion === "number" && Number.isFinite(parsed.protocolVersion) ? parsed.protocolVersion : LATCH_MANIFEST_FORMAT_VERSION;
|
|
@@ -16681,9 +16984,9 @@ function parseLatchCreateReport(raw) {
|
|
|
16681
16984
|
protocolVersion,
|
|
16682
16985
|
session: {
|
|
16683
16986
|
id,
|
|
16684
|
-
name:
|
|
16685
|
-
state:
|
|
16686
|
-
createdAt:
|
|
16987
|
+
name: trimmed4(parsed.session?.name) ?? id,
|
|
16988
|
+
state: trimmed4(parsed.session?.state) ?? "running",
|
|
16989
|
+
createdAt: trimmed4(parsed.session?.createdAt) ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
16687
16990
|
}
|
|
16688
16991
|
};
|
|
16689
16992
|
} catch {
|
|
@@ -16695,28 +16998,28 @@ function toExecutionProviderSession({ createReport, executionTargetId, createdAt
|
|
|
16695
16998
|
provider: "latch",
|
|
16696
16999
|
providerSessionId: createReport.session.id,
|
|
16697
17000
|
sessionName: createReport.session.name,
|
|
16698
|
-
executionTargetId:
|
|
17001
|
+
executionTargetId: trimmed4(executionTargetId),
|
|
16699
17002
|
agentSessionId: null,
|
|
16700
17003
|
createdAt: createdAt ?? createReport.session.createdAt,
|
|
16701
17004
|
lastObservedState: createReport.session.state
|
|
16702
17005
|
};
|
|
16703
17006
|
}
|
|
16704
17007
|
function existingLatchProviderSession({ latchSessionId, executionTargetId, createdAt = (/* @__PURE__ */ new Date()).toISOString() }) {
|
|
16705
|
-
const providerSessionId =
|
|
17008
|
+
const providerSessionId = trimmed4(latchSessionId);
|
|
16706
17009
|
if (!providerSessionId)
|
|
16707
17010
|
return null;
|
|
16708
17011
|
return {
|
|
16709
17012
|
provider: "latch",
|
|
16710
17013
|
providerSessionId,
|
|
16711
17014
|
sessionName: null,
|
|
16712
|
-
executionTargetId:
|
|
17015
|
+
executionTargetId: trimmed4(executionTargetId),
|
|
16713
17016
|
agentSessionId: null,
|
|
16714
17017
|
createdAt,
|
|
16715
17018
|
lastObservedState: "running"
|
|
16716
17019
|
};
|
|
16717
17020
|
}
|
|
16718
17021
|
function latchViewerFlagForKind(kind) {
|
|
16719
|
-
switch (
|
|
17022
|
+
switch (trimmed4(kind)?.toLowerCase()) {
|
|
16720
17023
|
case "iterm":
|
|
16721
17024
|
case "iterm2":
|
|
16722
17025
|
return "iterm";
|
|
@@ -16740,7 +17043,7 @@ function compareLatchProductVersions(a, b) {
|
|
|
16740
17043
|
return 0;
|
|
16741
17044
|
}
|
|
16742
17045
|
function latchSupportsOpenAs(productVersion) {
|
|
16743
|
-
const version =
|
|
17046
|
+
const version = trimmed4(productVersion);
|
|
16744
17047
|
if (!version)
|
|
16745
17048
|
return false;
|
|
16746
17049
|
return compareLatchProductVersions(version, LATCH_OPEN_AS_MIN_PRODUCT_VERSION) >= 0;
|
|
@@ -16754,10 +17057,10 @@ function buildLatchOpenArgs({ providerSessionId, viewer, openAs, productVersion
|
|
|
16754
17057
|
return args;
|
|
16755
17058
|
}
|
|
16756
17059
|
function latchAttachCommand({ executable = "latch", providerSessionId }) {
|
|
16757
|
-
return `${
|
|
17060
|
+
return `${trimmed4(executable) ?? "latch"} attach ${providerSessionId}`;
|
|
16758
17061
|
}
|
|
16759
17062
|
function shouldUseLatchProvider({ providerKind, latchSelectable }) {
|
|
16760
|
-
return
|
|
17063
|
+
return trimmed4(providerKind)?.toLowerCase() === "latch" && latchSelectable === true;
|
|
16761
17064
|
}
|
|
16762
17065
|
var LATCH_MANIFEST_FORMAT_VERSION, DEFAULT_LATCH_TERMINAL_SIZE, LATCH_OPEN_AS_MIN_PRODUCT_VERSION;
|
|
16763
17066
|
var init_latch_launch = __esm({
|
|
@@ -16772,11 +17075,11 @@ var init_latch_launch = __esm({
|
|
|
16772
17075
|
|
|
16773
17076
|
// ../packages/core/dist/service/latch-session.js
|
|
16774
17077
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
16775
|
-
function
|
|
17078
|
+
function trimmed5(value) {
|
|
16776
17079
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
16777
17080
|
}
|
|
16778
17081
|
function parseState(value) {
|
|
16779
|
-
switch (
|
|
17082
|
+
switch (trimmed5(value)) {
|
|
16780
17083
|
case "running":
|
|
16781
17084
|
return "running";
|
|
16782
17085
|
case "exited":
|
|
@@ -16790,7 +17093,10 @@ function parseState(value) {
|
|
|
16790
17093
|
}
|
|
16791
17094
|
}
|
|
16792
17095
|
function runLatchJson({ executable, args }) {
|
|
16793
|
-
const
|
|
17096
|
+
const binary = resolveLatchBinaryPath(executable);
|
|
17097
|
+
if (!binary)
|
|
17098
|
+
throw new LatchSessionCommandError(latchBinaryMissingMessage(executable));
|
|
17099
|
+
const result = spawnSync2(binary, args, {
|
|
16794
17100
|
encoding: "utf8",
|
|
16795
17101
|
shell: false,
|
|
16796
17102
|
timeout: 1e4,
|
|
@@ -16815,11 +17121,11 @@ function runLatchJson({ executable, args }) {
|
|
|
16815
17121
|
}
|
|
16816
17122
|
}
|
|
16817
17123
|
function inspectLatchSession({ executable = "latch", providerSessionId }) {
|
|
16818
|
-
const sessionId =
|
|
17124
|
+
const sessionId = trimmed5(providerSessionId);
|
|
16819
17125
|
if (!sessionId)
|
|
16820
17126
|
throw new LatchSessionCommandError("A Latch session id is required.");
|
|
16821
17127
|
const report = runLatchJson({
|
|
16822
|
-
executable:
|
|
17128
|
+
executable: trimmed5(executable) ?? "latch",
|
|
16823
17129
|
args: ["inspect", sessionId, "--json"]
|
|
16824
17130
|
});
|
|
16825
17131
|
const state = parseState(report.state);
|
|
@@ -16827,8 +17133,8 @@ function inspectLatchSession({ executable = "latch", providerSessionId }) {
|
|
|
16827
17133
|
throw new LatchSessionCommandError("Latch returned an unsupported session state.");
|
|
16828
17134
|
const exit = report.exit && typeof report.exit === "object" && !Array.isArray(report.exit) ? report.exit : null;
|
|
16829
17135
|
return {
|
|
16830
|
-
providerSessionId:
|
|
16831
|
-
name:
|
|
17136
|
+
providerSessionId: trimmed5(report.id) ?? sessionId,
|
|
17137
|
+
name: trimmed5(report.name) ?? sessionId,
|
|
16832
17138
|
state,
|
|
16833
17139
|
exitCode: typeof exit?.code === "number" && Number.isFinite(exit.code) ? exit.code : null,
|
|
16834
17140
|
inspectedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -16836,20 +17142,20 @@ function inspectLatchSession({ executable = "latch", providerSessionId }) {
|
|
|
16836
17142
|
}
|
|
16837
17143
|
function readLatchProductVersion(executable) {
|
|
16838
17144
|
try {
|
|
16839
|
-
return
|
|
17145
|
+
return trimmed5(runLatchJson({ executable, args: ["capabilities", "--json"] }).productVersion);
|
|
16840
17146
|
} catch {
|
|
16841
17147
|
return null;
|
|
16842
17148
|
}
|
|
16843
17149
|
}
|
|
16844
17150
|
function openLatchSession({ executable = "latch", providerSessionId, viewerKind, openAs }) {
|
|
16845
|
-
const sessionId =
|
|
17151
|
+
const sessionId = trimmed5(providerSessionId);
|
|
16846
17152
|
if (!sessionId)
|
|
16847
17153
|
throw new LatchSessionCommandError("A Latch session id is required.");
|
|
16848
17154
|
const viewer = latchViewerFlagForKind(viewerKind);
|
|
16849
17155
|
if (!viewer) {
|
|
16850
17156
|
throw new LatchSessionCommandError(`Latch cannot open the configured viewer "${viewerKind}".`);
|
|
16851
17157
|
}
|
|
16852
|
-
const exe =
|
|
17158
|
+
const exe = trimmed5(executable) ?? "latch";
|
|
16853
17159
|
const report = runLatchJson({
|
|
16854
17160
|
executable: exe,
|
|
16855
17161
|
args: buildLatchOpenArgs({
|
|
@@ -16863,25 +17169,25 @@ function openLatchSession({ executable = "latch", providerSessionId, viewerKind,
|
|
|
16863
17169
|
throw new LatchSessionCommandError(`Latch did not open the ${viewer} viewer.`);
|
|
16864
17170
|
}
|
|
16865
17171
|
return {
|
|
16866
|
-
providerSessionId:
|
|
16867
|
-
viewer:
|
|
17172
|
+
providerSessionId: trimmed5(report.id) ?? sessionId,
|
|
17173
|
+
viewer: trimmed5(report.viewer) ?? viewer,
|
|
16868
17174
|
opened: true,
|
|
16869
|
-
behavior:
|
|
17175
|
+
behavior: trimmed5(report.behavior) ? parseViewerOpenAs(report.behavior) : null
|
|
16870
17176
|
};
|
|
16871
17177
|
}
|
|
16872
17178
|
function stopLatchSession({ executable = "latch", providerSessionId }) {
|
|
16873
|
-
const sessionId =
|
|
17179
|
+
const sessionId = trimmed5(providerSessionId);
|
|
16874
17180
|
if (!sessionId)
|
|
16875
17181
|
throw new LatchSessionCommandError("A Latch session id is required.");
|
|
16876
17182
|
const report = runLatchJson({
|
|
16877
|
-
executable:
|
|
17183
|
+
executable: trimmed5(executable) ?? "latch",
|
|
16878
17184
|
args: ["stop", sessionId, "--json"]
|
|
16879
17185
|
});
|
|
16880
17186
|
const state = parseState(report.state);
|
|
16881
17187
|
if (!state)
|
|
16882
17188
|
throw new LatchSessionCommandError("Latch returned an unsupported session state.");
|
|
16883
17189
|
return {
|
|
16884
|
-
providerSessionId:
|
|
17190
|
+
providerSessionId: trimmed5(report.id) ?? sessionId,
|
|
16885
17191
|
state
|
|
16886
17192
|
};
|
|
16887
17193
|
}
|
|
@@ -16889,6 +17195,7 @@ var LatchSessionCommandError;
|
|
|
16889
17195
|
var init_latch_session = __esm({
|
|
16890
17196
|
"../packages/core/dist/service/latch-session.js"() {
|
|
16891
17197
|
"use strict";
|
|
17198
|
+
init_latch_binary();
|
|
16892
17199
|
init_latch_environment();
|
|
16893
17200
|
init_latch_launch();
|
|
16894
17201
|
init_terminal_profile_types2();
|
|
@@ -16903,17 +17210,17 @@ var init_latch_session = __esm({
|
|
|
16903
17210
|
|
|
16904
17211
|
// ../packages/core/dist/service/latch-events.js
|
|
16905
17212
|
import { spawn as spawn3 } from "node:child_process";
|
|
16906
|
-
function
|
|
17213
|
+
function trimmed6(value) {
|
|
16907
17214
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
16908
17215
|
}
|
|
16909
17216
|
function parseHarnessEvent(value) {
|
|
16910
17217
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
16911
17218
|
return null;
|
|
16912
17219
|
const row = value;
|
|
16913
|
-
const type =
|
|
16914
|
-
const sessionId =
|
|
16915
|
-
const at =
|
|
16916
|
-
const harnessVersion =
|
|
17220
|
+
const type = trimmed6(row.type);
|
|
17221
|
+
const sessionId = trimmed6(row.sessionId);
|
|
17222
|
+
const at = trimmed6(row.at);
|
|
17223
|
+
const harnessVersion = trimmed6(row.harnessVersion);
|
|
16917
17224
|
const connectorEpoch = typeof row.connectorEpoch === "number" && Number.isFinite(row.connectorEpoch) ? row.connectorEpoch : null;
|
|
16918
17225
|
if (!type || !HARNESS_EVENT_TYPES.has(type) || !sessionId || !at || !harnessVersion)
|
|
16919
17226
|
return null;
|
|
@@ -16924,26 +17231,26 @@ function parseHarnessEvent(value) {
|
|
|
16924
17231
|
case "user_message":
|
|
16925
17232
|
case "assistant_delta":
|
|
16926
17233
|
case "assistant_message": {
|
|
16927
|
-
const text =
|
|
17234
|
+
const text = trimmed6(row.text);
|
|
16928
17235
|
if (!text)
|
|
16929
17236
|
return null;
|
|
16930
17237
|
return { ...base, type, text };
|
|
16931
17238
|
}
|
|
16932
17239
|
case "tool_started": {
|
|
16933
|
-
const tool =
|
|
17240
|
+
const tool = trimmed6(row.tool);
|
|
16934
17241
|
if (!tool)
|
|
16935
17242
|
return null;
|
|
16936
17243
|
return { ...base, type, tool, input: row.input };
|
|
16937
17244
|
}
|
|
16938
17245
|
case "tool_finished": {
|
|
16939
|
-
const tool =
|
|
17246
|
+
const tool = trimmed6(row.tool);
|
|
16940
17247
|
if (!tool)
|
|
16941
17248
|
return null;
|
|
16942
17249
|
return { ...base, type, tool, output: row.output };
|
|
16943
17250
|
}
|
|
16944
17251
|
case "awaiting_input": {
|
|
16945
|
-
const requestId =
|
|
16946
|
-
const kind =
|
|
17252
|
+
const requestId = trimmed6(row.requestId);
|
|
17253
|
+
const kind = trimmed6(row.kind);
|
|
16947
17254
|
const prompt = typeof row.prompt === "string" ? row.prompt : null;
|
|
16948
17255
|
if (!requestId || kind !== "permission" && kind !== "question" || prompt === null) {
|
|
16949
17256
|
return null;
|
|
@@ -16959,7 +17266,7 @@ function parseHarnessEvent(value) {
|
|
|
16959
17266
|
};
|
|
16960
17267
|
}
|
|
16961
17268
|
case "status": {
|
|
16962
|
-
const status =
|
|
17269
|
+
const status = trimmed6(row.status);
|
|
16963
17270
|
if (!status)
|
|
16964
17271
|
return null;
|
|
16965
17272
|
return { ...base, type, status };
|
|
@@ -16984,11 +17291,13 @@ function parseHarnessEventNdjson(raw) {
|
|
|
16984
17291
|
return events;
|
|
16985
17292
|
}
|
|
16986
17293
|
async function collectLatchEvents({ executable = "latch", providerSessionId, from = 0, idleMs = 250, timeoutMs = 8e3 }) {
|
|
16987
|
-
const sessionId =
|
|
17294
|
+
const sessionId = trimmed6(providerSessionId);
|
|
16988
17295
|
if (!sessionId)
|
|
16989
17296
|
throw new LatchSessionCommandError("A Latch session id is required.");
|
|
16990
17297
|
const fromCursor = Number.isFinite(from) && from > 0 ? Math.floor(from) : 0;
|
|
16991
|
-
const bin =
|
|
17298
|
+
const bin = resolveLatchBinaryPath(executable);
|
|
17299
|
+
if (!bin)
|
|
17300
|
+
throw new LatchSessionCommandError(latchBinaryMissingMessage(executable));
|
|
16992
17301
|
return new Promise((resolve, reject) => {
|
|
16993
17302
|
const child = spawn3(bin, ["events", sessionId, "--json", "--from", String(fromCursor)], {
|
|
16994
17303
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -17056,6 +17365,7 @@ var HARNESS_EVENT_TYPES;
|
|
|
17056
17365
|
var init_latch_events = __esm({
|
|
17057
17366
|
"../packages/core/dist/service/latch-events.js"() {
|
|
17058
17367
|
"use strict";
|
|
17368
|
+
init_latch_binary();
|
|
17059
17369
|
init_latch_environment();
|
|
17060
17370
|
init_latch_session();
|
|
17061
17371
|
HARNESS_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
@@ -17085,12 +17395,12 @@ var init_util2 = __esm({
|
|
|
17085
17395
|
});
|
|
17086
17396
|
|
|
17087
17397
|
// ../packages/core/dist/service/local-target/project-metadata.js
|
|
17088
|
-
import { execFileSync } from "node:child_process";
|
|
17089
|
-
import { existsSync as
|
|
17090
|
-
import
|
|
17398
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
17399
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync9, readFileSync as readFileSync15, writeFileSync as writeFileSync8 } from "node:fs";
|
|
17400
|
+
import path16 from "node:path";
|
|
17091
17401
|
function writeIfChanged(filePath, content) {
|
|
17092
|
-
if (!
|
|
17093
|
-
|
|
17402
|
+
if (!existsSync17(filePath) || readFileSync15(filePath, "utf8") !== content) {
|
|
17403
|
+
writeFileSync8(filePath, content);
|
|
17094
17404
|
}
|
|
17095
17405
|
}
|
|
17096
17406
|
function replaceManagedBlock(content, block) {
|
|
@@ -17101,25 +17411,25 @@ function replaceManagedBlock(content, block) {
|
|
|
17101
17411
|
}
|
|
17102
17412
|
function ensureAgentInstructions(checkoutPath) {
|
|
17103
17413
|
for (const filename of ["AGENTS.md", "CLAUDE.md"]) {
|
|
17104
|
-
const instructionPath =
|
|
17105
|
-
const existing =
|
|
17414
|
+
const instructionPath = path16.join(checkoutPath, filename);
|
|
17415
|
+
const existing = existsSync17(instructionPath) ? readFileSync15(instructionPath, "utf8") : "";
|
|
17106
17416
|
writeIfChanged(instructionPath, replaceManagedBlock(existing, PROJECT_METADATA_INSTRUCTION));
|
|
17107
17417
|
}
|
|
17108
17418
|
}
|
|
17109
17419
|
function ensureGitignore(checkoutPath) {
|
|
17110
|
-
const gitignorePath =
|
|
17111
|
-
const existing =
|
|
17420
|
+
const gitignorePath = path16.join(checkoutPath, ".gitignore");
|
|
17421
|
+
const existing = existsSync17(gitignorePath) ? readFileSync15(gitignorePath, "utf8") : "";
|
|
17112
17422
|
const existingPatterns = new Set(existing.split(/\r?\n/).map((line) => line.trim().replace(/^\//, "")).filter(Boolean));
|
|
17113
17423
|
const additions = [".overlord/tmp/", ".overlord/logs/"].filter((pattern) => !existingPatterns.has(pattern));
|
|
17114
17424
|
if (additions.length === 0)
|
|
17115
17425
|
return;
|
|
17116
17426
|
const separator = existing.length === 0 || existing.endsWith("\n") ? "" : "\n";
|
|
17117
|
-
|
|
17427
|
+
writeFileSync8(gitignorePath, `${existing}${separator}${additions.join("\n")}
|
|
17118
17428
|
`);
|
|
17119
17429
|
}
|
|
17120
17430
|
function resolveGitRoot(directoryPath) {
|
|
17121
17431
|
try {
|
|
17122
|
-
return
|
|
17432
|
+
return execFileSync2("git", ["-C", directoryPath, "rev-parse", "--show-toplevel"], {
|
|
17123
17433
|
encoding: "utf8",
|
|
17124
17434
|
stdio: ["ignore", "pipe", "ignore"]
|
|
17125
17435
|
}).trim();
|
|
@@ -17176,9 +17486,9 @@ function selectPrimaryProjectEntry(projects) {
|
|
|
17176
17486
|
return latestLinkedAt(projects);
|
|
17177
17487
|
}
|
|
17178
17488
|
function parseProjectJson(projectJsonPath) {
|
|
17179
|
-
if (!
|
|
17489
|
+
if (!existsSync17(projectJsonPath))
|
|
17180
17490
|
return null;
|
|
17181
|
-
const parsed = JSON.parse(
|
|
17491
|
+
const parsed = JSON.parse(readFileSync15(projectJsonPath, "utf8"));
|
|
17182
17492
|
const fromArray = Array.isArray(parsed.projects) ? parsed.projects.map((entry) => parseProjectEntry(entry)).filter((entry) => entry !== null) : [];
|
|
17183
17493
|
const fromTopLevel = parseProjectEntry(parsed);
|
|
17184
17494
|
const projects = fromArray.length > 0 ? fromArray : fromTopLevel ? [fromTopLevel] : [];
|
|
@@ -17245,12 +17555,12 @@ function serializeProjectEntry(entry) {
|
|
|
17245
17555
|
};
|
|
17246
17556
|
}
|
|
17247
17557
|
function writeProjectJson({ directoryPath, projectId, projectName, resourceId, resourceKey, executionTargetId, isPrimary }) {
|
|
17248
|
-
const overlordDir =
|
|
17249
|
-
|
|
17250
|
-
|
|
17251
|
-
|
|
17558
|
+
const overlordDir = path16.join(directoryPath, ".overlord");
|
|
17559
|
+
mkdirSync9(overlordDir, { recursive: true });
|
|
17560
|
+
mkdirSync9(path16.join(overlordDir, "tmp"), { recursive: true });
|
|
17561
|
+
mkdirSync9(path16.join(overlordDir, "logs"), { recursive: true });
|
|
17252
17562
|
protectProjectMetadata(directoryPath);
|
|
17253
|
-
const projectJsonPath =
|
|
17563
|
+
const projectJsonPath = path16.join(overlordDir, "project.json");
|
|
17254
17564
|
const existing = parseProjectJson(projectJsonPath);
|
|
17255
17565
|
const existingProjects = existing?.projects ?? [];
|
|
17256
17566
|
const existingEntry = existingProjects.find((entry) => entry.projectId === projectId);
|
|
@@ -17277,7 +17587,7 @@ function writeProjectJson({ directoryPath, projectId, projectName, resourceId, r
|
|
|
17277
17587
|
];
|
|
17278
17588
|
const primary = selectPrimaryProjectEntry(projects) ?? nextEntry;
|
|
17279
17589
|
const primaryResourceIds = primary.resourceIdsByExecutionTarget ?? {};
|
|
17280
|
-
|
|
17590
|
+
writeFileSync8(projectJsonPath, `${JSON.stringify({
|
|
17281
17591
|
_warning: PROJECT_JSON_WARNING,
|
|
17282
17592
|
version: PROJECT_JSON_VERSION,
|
|
17283
17593
|
projectId: primary.projectId,
|
|
@@ -17312,11 +17622,11 @@ ${INSTRUCTION_BLOCK_END}`;
|
|
|
17312
17622
|
});
|
|
17313
17623
|
|
|
17314
17624
|
// ../packages/core/dist/repository/git-tree.js
|
|
17315
|
-
import { execFileSync as
|
|
17316
|
-
import
|
|
17625
|
+
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
17626
|
+
import path17 from "node:path";
|
|
17317
17627
|
function runGit(cwd, args) {
|
|
17318
17628
|
try {
|
|
17319
|
-
return
|
|
17629
|
+
return execFileSync3("git", args, {
|
|
17320
17630
|
cwd,
|
|
17321
17631
|
encoding: "utf8",
|
|
17322
17632
|
maxBuffer: 10 * 1024 * 1024,
|
|
@@ -17336,17 +17646,17 @@ function gitPath(cwd, args) {
|
|
|
17336
17646
|
}
|
|
17337
17647
|
}
|
|
17338
17648
|
function addDirectoryAncestors(paths, filePath) {
|
|
17339
|
-
let parent =
|
|
17649
|
+
let parent = path17.posix.dirname(filePath);
|
|
17340
17650
|
while (parent !== "." && parent !== "/") {
|
|
17341
17651
|
paths.add(parent);
|
|
17342
|
-
parent =
|
|
17652
|
+
parent = path17.posix.dirname(parent);
|
|
17343
17653
|
}
|
|
17344
17654
|
}
|
|
17345
17655
|
function toEntry(entryPath, type) {
|
|
17346
|
-
const parentPath =
|
|
17656
|
+
const parentPath = path17.posix.dirname(entryPath);
|
|
17347
17657
|
return {
|
|
17348
17658
|
path: entryPath,
|
|
17349
|
-
name:
|
|
17659
|
+
name: path17.posix.basename(entryPath),
|
|
17350
17660
|
type,
|
|
17351
17661
|
parentPath: parentPath === "." ? null : parentPath,
|
|
17352
17662
|
depth: entryPath.split("/").length - 1
|
|
@@ -17358,7 +17668,7 @@ function compareEntries(a, b) {
|
|
|
17358
17668
|
return a.path < b.path ? -1 : 1;
|
|
17359
17669
|
}
|
|
17360
17670
|
function readRepositoryTree(rootPath, options = {}) {
|
|
17361
|
-
const resolvedRoot =
|
|
17671
|
+
const resolvedRoot = path17.resolve(rootPath);
|
|
17362
17672
|
const gitRoot = gitPath(resolvedRoot, ["rev-parse", "--show-toplevel"]);
|
|
17363
17673
|
if (!gitRoot) {
|
|
17364
17674
|
throw new RepositoryReadError("not_git_repository", `${resolvedRoot} is not inside a git repository.`);
|
|
@@ -17381,7 +17691,7 @@ function readRepositoryTree(rootPath, options = {}) {
|
|
|
17381
17691
|
const truncated = entries.length > maxEntries;
|
|
17382
17692
|
return {
|
|
17383
17693
|
rootPath: resolvedRoot,
|
|
17384
|
-
gitRoot:
|
|
17694
|
+
gitRoot: path17.resolve(gitRoot),
|
|
17385
17695
|
branch: gitPath(gitRoot, ["branch", "--show-current"]),
|
|
17386
17696
|
commit: gitPath(gitRoot, ["rev-parse", "--short", "HEAD"]),
|
|
17387
17697
|
entries: truncated ? entries.slice(0, maxEntries) : entries,
|
|
@@ -17403,274 +17713,14 @@ var init_git_tree = __esm({
|
|
|
17403
17713
|
}
|
|
17404
17714
|
});
|
|
17405
17715
|
|
|
17406
|
-
// ../packages/core/dist/service/latch-discovery.js
|
|
17407
|
-
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
17408
|
-
import { existsSync as existsSync16, mkdirSync as mkdirSync9, readFileSync as readFileSync15, statSync as statSync4, writeFileSync as writeFileSync8 } from "node:fs";
|
|
17409
|
-
import os3 from "node:os";
|
|
17410
|
-
import path17 from "node:path";
|
|
17411
|
-
function defaultLatchDiscoveryCachePath() {
|
|
17412
|
-
const home = process.env.OVLD_HOME?.trim() || path17.join(os3.homedir(), ".ovld");
|
|
17413
|
-
return path17.join(home, LATCH_DISCOVERY_CACHE_FILENAME);
|
|
17414
|
-
}
|
|
17415
|
-
function trimmed5(value) {
|
|
17416
|
-
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
17417
|
-
}
|
|
17418
|
-
function asBoolean(value) {
|
|
17419
|
-
return value === true;
|
|
17420
|
-
}
|
|
17421
|
-
function asStringArray(value) {
|
|
17422
|
-
if (!Array.isArray(value))
|
|
17423
|
-
return [];
|
|
17424
|
-
return value.filter((entry) => typeof entry === "string");
|
|
17425
|
-
}
|
|
17426
|
-
function parseLatchCapabilitiesReport(raw) {
|
|
17427
|
-
const text = raw.trim();
|
|
17428
|
-
if (!text)
|
|
17429
|
-
return null;
|
|
17430
|
-
let parsed;
|
|
17431
|
-
try {
|
|
17432
|
-
parsed = JSON.parse(text);
|
|
17433
|
-
} catch {
|
|
17434
|
-
return null;
|
|
17435
|
-
}
|
|
17436
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
17437
|
-
return null;
|
|
17438
|
-
const cast = parsed;
|
|
17439
|
-
const protocolVersion = typeof cast.protocolVersion === "number" && Number.isFinite(cast.protocolVersion) ? Math.trunc(cast.protocolVersion) : null;
|
|
17440
|
-
const productVersion = trimmed5(cast.productVersion);
|
|
17441
|
-
if (protocolVersion === null || !productVersion)
|
|
17442
|
-
return null;
|
|
17443
|
-
if (!cast.capabilities || typeof cast.capabilities !== "object" || Array.isArray(cast.capabilities)) {
|
|
17444
|
-
return null;
|
|
17445
|
-
}
|
|
17446
|
-
const caps = cast.capabilities;
|
|
17447
|
-
return {
|
|
17448
|
-
protocolVersion,
|
|
17449
|
-
productVersion,
|
|
17450
|
-
capabilities: {
|
|
17451
|
-
create: asBoolean(caps.create),
|
|
17452
|
-
openViewer: asBoolean(caps.openViewer),
|
|
17453
|
-
localAttach: asBoolean(caps.localAttach),
|
|
17454
|
-
cloudAttach: asBoolean(caps.cloudAttach),
|
|
17455
|
-
selfUpdate: asBoolean(caps.selfUpdate),
|
|
17456
|
-
extensions: asStringArray(caps.extensions)
|
|
17457
|
-
}
|
|
17458
|
-
};
|
|
17459
|
-
}
|
|
17460
|
-
function evaluateLatchCapabilities({ report, executable, resolvedPath, checkedAt = (/* @__PURE__ */ new Date()).toISOString() }) {
|
|
17461
|
-
if (report.protocolVersion !== SUPPORTED_LATCH_PROTOCOL_VERSION) {
|
|
17462
|
-
return {
|
|
17463
|
-
state: "incompatible",
|
|
17464
|
-
executable,
|
|
17465
|
-
resolvedPath,
|
|
17466
|
-
protocolVersion: report.protocolVersion,
|
|
17467
|
-
productVersion: report.productVersion,
|
|
17468
|
-
missingCapability: "protocolVersion",
|
|
17469
|
-
detail: `Latch protocolVersion ${report.protocolVersion} is not supported (need ${SUPPORTED_LATCH_PROTOCOL_VERSION}).`,
|
|
17470
|
-
checkedAt,
|
|
17471
|
-
latchSelectable: false,
|
|
17472
|
-
directSelectable: true
|
|
17473
|
-
};
|
|
17474
|
-
}
|
|
17475
|
-
for (const required of REQUIRED_LATCH_CAPABILITIES) {
|
|
17476
|
-
if (!report.capabilities[required]) {
|
|
17477
|
-
return {
|
|
17478
|
-
state: "incompatible",
|
|
17479
|
-
executable,
|
|
17480
|
-
resolvedPath,
|
|
17481
|
-
protocolVersion: report.protocolVersion,
|
|
17482
|
-
productVersion: report.productVersion,
|
|
17483
|
-
missingCapability: required,
|
|
17484
|
-
detail: `Latch is missing required capability \`${required}\`.`,
|
|
17485
|
-
checkedAt,
|
|
17486
|
-
latchSelectable: false,
|
|
17487
|
-
directSelectable: true
|
|
17488
|
-
};
|
|
17489
|
-
}
|
|
17490
|
-
}
|
|
17491
|
-
return {
|
|
17492
|
-
state: "found",
|
|
17493
|
-
executable,
|
|
17494
|
-
resolvedPath,
|
|
17495
|
-
protocolVersion: report.protocolVersion,
|
|
17496
|
-
productVersion: report.productVersion,
|
|
17497
|
-
capabilities: report.capabilities,
|
|
17498
|
-
checkedAt,
|
|
17499
|
-
latchSelectable: true,
|
|
17500
|
-
directSelectable: true
|
|
17501
|
-
};
|
|
17502
|
-
}
|
|
17503
|
-
function resolveLatchExecutableOnPath(executable) {
|
|
17504
|
-
try {
|
|
17505
|
-
const found = execFileSync3("which", [executable], {
|
|
17506
|
-
encoding: "utf8",
|
|
17507
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
17508
|
-
}).trim();
|
|
17509
|
-
return found || null;
|
|
17510
|
-
} catch {
|
|
17511
|
-
return null;
|
|
17512
|
-
}
|
|
17513
|
-
}
|
|
17514
|
-
function resolveLatchExecutablePathFromEnvironment({ executable, homeDirectory = os3.homedir(), platform: platform2 = process.platform, resolveOnPath = resolveLatchExecutableOnPath, fileExists = existsSync16 }) {
|
|
17515
|
-
const name = trimmed5(executable) ?? DEFAULT_LATCH_EXECUTABLE;
|
|
17516
|
-
if (path17.isAbsolute(name) || name.includes("/") || name.includes("\\")) {
|
|
17517
|
-
return fileExists(name) ? path17.resolve(name) : null;
|
|
17518
|
-
}
|
|
17519
|
-
const resolvedOnPath = resolveOnPath(name);
|
|
17520
|
-
if (resolvedOnPath)
|
|
17521
|
-
return resolvedOnPath;
|
|
17522
|
-
const fallbacks = [
|
|
17523
|
-
path17.join(homeDirectory, ".local", "bin", name),
|
|
17524
|
-
...platform2 === "darwin" ? [`/opt/homebrew/bin/${name}`, `/usr/local/bin/${name}`] : []
|
|
17525
|
-
];
|
|
17526
|
-
const fallback = fallbacks.find(fileExists);
|
|
17527
|
-
return fallback ? path17.resolve(fallback) : null;
|
|
17528
|
-
}
|
|
17529
|
-
function resolveLatchExecutablePath(executable) {
|
|
17530
|
-
return resolveLatchExecutablePathFromEnvironment({ executable });
|
|
17531
|
-
}
|
|
17532
|
-
function notInstalledResult({ executable, checkedAt }) {
|
|
17533
|
-
return {
|
|
17534
|
-
state: "not_installed",
|
|
17535
|
-
executable,
|
|
17536
|
-
installCommand: LATCH_STANDALONE_INSTALL_COMMAND,
|
|
17537
|
-
checkedAt,
|
|
17538
|
-
latchSelectable: false,
|
|
17539
|
-
directSelectable: true
|
|
17540
|
-
};
|
|
17541
|
-
}
|
|
17542
|
-
function probeLatchCapabilities({ executable = DEFAULT_LATCH_EXECUTABLE, resolvePath = resolveLatchExecutablePath, runCommand: runCommand2 = defaultCommandRunner, now = () => (/* @__PURE__ */ new Date()).toISOString() } = {}) {
|
|
17543
|
-
const exe = trimmed5(executable) ?? DEFAULT_LATCH_EXECUTABLE;
|
|
17544
|
-
const checkedAt = now();
|
|
17545
|
-
const resolvedPath = resolvePath(exe);
|
|
17546
|
-
if (!resolvedPath) {
|
|
17547
|
-
return notInstalledResult({ executable: exe, checkedAt });
|
|
17548
|
-
}
|
|
17549
|
-
const ran = runCommand2({ executable: resolvedPath, argv: ["capabilities", "--json"] });
|
|
17550
|
-
if (ran.status === null && ran.stderr === "ENOENT") {
|
|
17551
|
-
return notInstalledResult({ executable: exe, checkedAt });
|
|
17552
|
-
}
|
|
17553
|
-
const report = parseLatchCapabilitiesReport(ran.stdout);
|
|
17554
|
-
if (!report) {
|
|
17555
|
-
return {
|
|
17556
|
-
state: "incompatible",
|
|
17557
|
-
executable: exe,
|
|
17558
|
-
resolvedPath,
|
|
17559
|
-
protocolVersion: null,
|
|
17560
|
-
productVersion: null,
|
|
17561
|
-
missingCapability: "capabilities",
|
|
17562
|
-
detail: ran.status === 0 ? "Latch capabilities output was not valid JSON in the expected shape." : `Latch capabilities failed (exit ${ran.status ?? "null"}).`,
|
|
17563
|
-
checkedAt,
|
|
17564
|
-
latchSelectable: false,
|
|
17565
|
-
directSelectable: true
|
|
17566
|
-
};
|
|
17567
|
-
}
|
|
17568
|
-
return evaluateLatchCapabilities({ report, executable: exe, resolvedPath, checkedAt });
|
|
17569
|
-
}
|
|
17570
|
-
function latchDiscoveryCacheKey({ executionTargetId, executable }) {
|
|
17571
|
-
const exe = trimmed5(executable) ?? DEFAULT_LATCH_EXECUTABLE;
|
|
17572
|
-
return `${executionTargetId}::${exe}`;
|
|
17573
|
-
}
|
|
17574
|
-
function readBinaryMtimeMs(resolvedPath) {
|
|
17575
|
-
if (!resolvedPath)
|
|
17576
|
-
return null;
|
|
17577
|
-
try {
|
|
17578
|
-
return statSync4(resolvedPath).mtimeMs;
|
|
17579
|
-
} catch {
|
|
17580
|
-
return null;
|
|
17581
|
-
}
|
|
17582
|
-
}
|
|
17583
|
-
function isLatchDiscoveryCacheFresh({ entry, resolvePath = resolveLatchExecutablePath }) {
|
|
17584
|
-
const { result } = entry;
|
|
17585
|
-
if (result.state === "not_installed") {
|
|
17586
|
-
return resolvePath(result.executable) === null;
|
|
17587
|
-
}
|
|
17588
|
-
const pathStill = result.resolvedPath;
|
|
17589
|
-
if (!pathStill || !existsSync16(pathStill))
|
|
17590
|
-
return false;
|
|
17591
|
-
const mtime = readBinaryMtimeMs(pathStill);
|
|
17592
|
-
if (mtime === null || entry.binaryMtimeMs === null)
|
|
17593
|
-
return false;
|
|
17594
|
-
return mtime === entry.binaryMtimeMs;
|
|
17595
|
-
}
|
|
17596
|
-
function readLatchDiscoveryCacheFile(filePath) {
|
|
17597
|
-
if (!existsSync16(filePath))
|
|
17598
|
-
return { version: 1, entries: {} };
|
|
17599
|
-
try {
|
|
17600
|
-
const parsed = JSON.parse(readFileSync15(filePath, "utf8"));
|
|
17601
|
-
if (parsed.version !== 1 || !parsed.entries || typeof parsed.entries !== "object") {
|
|
17602
|
-
return { version: 1, entries: {} };
|
|
17603
|
-
}
|
|
17604
|
-
return { version: 1, entries: parsed.entries };
|
|
17605
|
-
} catch {
|
|
17606
|
-
return { version: 1, entries: {} };
|
|
17607
|
-
}
|
|
17608
|
-
}
|
|
17609
|
-
function writeLatchDiscoveryCacheFile({ filePath, cache }) {
|
|
17610
|
-
mkdirSync9(path17.dirname(filePath), { recursive: true });
|
|
17611
|
-
writeFileSync8(filePath, `${JSON.stringify(cache, null, 2)}
|
|
17612
|
-
`, { mode: 384 });
|
|
17613
|
-
}
|
|
17614
|
-
function discoverLatchForExecutionTarget({ executionTargetId, executable = DEFAULT_LATCH_EXECUTABLE, cacheFilePath, force = false, resolvePath = resolveLatchExecutablePath, runCommand: runCommand2 = defaultCommandRunner, now = () => (/* @__PURE__ */ new Date()).toISOString() }) {
|
|
17615
|
-
const exe = trimmed5(executable) ?? DEFAULT_LATCH_EXECUTABLE;
|
|
17616
|
-
const key = latchDiscoveryCacheKey({ executionTargetId, executable: exe });
|
|
17617
|
-
const cache = readLatchDiscoveryCacheFile(cacheFilePath);
|
|
17618
|
-
const existing = cache.entries[key];
|
|
17619
|
-
if (!force && existing && isLatchDiscoveryCacheFresh({ entry: existing, resolvePath })) {
|
|
17620
|
-
return { ...existing.result, checkedAt: existing.result.checkedAt, directSelectable: true };
|
|
17621
|
-
}
|
|
17622
|
-
const result = probeLatchCapabilities({ executable: exe, resolvePath, runCommand: runCommand2, now });
|
|
17623
|
-
const binaryMtimeMs = result.state === "not_installed" ? null : readBinaryMtimeMs(result.resolvedPath);
|
|
17624
|
-
cache.entries[key] = {
|
|
17625
|
-
result,
|
|
17626
|
-
binaryMtimeMs,
|
|
17627
|
-
cachedAt: result.checkedAt
|
|
17628
|
-
};
|
|
17629
|
-
writeLatchDiscoveryCacheFile({ filePath: cacheFilePath, cache });
|
|
17630
|
-
return result;
|
|
17631
|
-
}
|
|
17632
|
-
var SUPPORTED_LATCH_PROTOCOL_VERSION, REQUIRED_LATCH_CAPABILITIES, LATCH_STANDALONE_INSTALL_COMMAND, LATCH_DISCOVERY_CACHE_FILENAME, defaultCommandRunner;
|
|
17633
|
-
var init_latch_discovery = __esm({
|
|
17634
|
-
"../packages/core/dist/service/latch-discovery.js"() {
|
|
17635
|
-
"use strict";
|
|
17636
|
-
init_latch_environment();
|
|
17637
|
-
init_terminal_profile_types2();
|
|
17638
|
-
SUPPORTED_LATCH_PROTOCOL_VERSION = 1;
|
|
17639
|
-
REQUIRED_LATCH_CAPABILITIES = ["create"];
|
|
17640
|
-
LATCH_STANDALONE_INSTALL_COMMAND = "curl -fsSL https://raw.githubusercontent.com/jchaselubitz/Latch/main/scripts/install-cli.sh | bash";
|
|
17641
|
-
LATCH_DISCOVERY_CACHE_FILENAME = "latch-discovery.json";
|
|
17642
|
-
defaultCommandRunner = ({ executable, argv }) => {
|
|
17643
|
-
try {
|
|
17644
|
-
const stdout = execFileSync3(executable, argv, {
|
|
17645
|
-
encoding: "utf8",
|
|
17646
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
17647
|
-
timeout: 5e3,
|
|
17648
|
-
env: latchChildEnvironment()
|
|
17649
|
-
});
|
|
17650
|
-
return { status: 0, stdout: String(stdout), stderr: "" };
|
|
17651
|
-
} catch (error) {
|
|
17652
|
-
const err = error;
|
|
17653
|
-
if (err.code === "ENOENT") {
|
|
17654
|
-
return { status: null, stdout: "", stderr: "ENOENT" };
|
|
17655
|
-
}
|
|
17656
|
-
return {
|
|
17657
|
-
status: typeof err.status === "number" ? err.status : 1,
|
|
17658
|
-
stdout: err.stdout ? String(err.stdout) : "",
|
|
17659
|
-
stderr: err.stderr ? String(err.stderr) : String(error)
|
|
17660
|
-
};
|
|
17661
|
-
}
|
|
17662
|
-
};
|
|
17663
|
-
}
|
|
17664
|
-
});
|
|
17665
|
-
|
|
17666
17716
|
// ../packages/core/dist/service/latch-send.js
|
|
17667
17717
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
17668
|
-
function
|
|
17718
|
+
function trimmed7(value) {
|
|
17669
17719
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
17670
17720
|
}
|
|
17671
17721
|
function probeLatchInteractionCapabilities({ executable = "latch", providerSessionId }) {
|
|
17672
|
-
const sessionId =
|
|
17673
|
-
const bin =
|
|
17722
|
+
const sessionId = trimmed7(providerSessionId);
|
|
17723
|
+
const bin = resolveLatchBinaryPath(executable);
|
|
17674
17724
|
if (!sessionId) {
|
|
17675
17725
|
return {
|
|
17676
17726
|
sendMessage: false,
|
|
@@ -17679,6 +17729,14 @@ function probeLatchInteractionCapabilities({ executable = "latch", providerSessi
|
|
|
17679
17729
|
canSend: { ok: false, reason: "A Latch session id is required." }
|
|
17680
17730
|
};
|
|
17681
17731
|
}
|
|
17732
|
+
if (!bin) {
|
|
17733
|
+
return {
|
|
17734
|
+
sendMessage: false,
|
|
17735
|
+
sendKeys: false,
|
|
17736
|
+
resolve: false,
|
|
17737
|
+
canSend: { ok: false, reason: latchBinaryMissingMessage(executable) }
|
|
17738
|
+
};
|
|
17739
|
+
}
|
|
17682
17740
|
const help = spawnSync3(bin, ["send", "--help"], {
|
|
17683
17741
|
encoding: "utf8",
|
|
17684
17742
|
shell: false,
|
|
@@ -17705,9 +17763,9 @@ ${help.stderr}`),
|
|
|
17705
17763
|
};
|
|
17706
17764
|
}
|
|
17707
17765
|
function resolveLatchInput({ executable = "latch", providerSessionId, requestId, choice }) {
|
|
17708
|
-
const sessionId =
|
|
17709
|
-
const id =
|
|
17710
|
-
const selected =
|
|
17766
|
+
const sessionId = trimmed7(providerSessionId);
|
|
17767
|
+
const id = trimmed7(requestId);
|
|
17768
|
+
const selected = trimmed7(choice);
|
|
17711
17769
|
if (!sessionId)
|
|
17712
17770
|
throw new LatchSessionCommandError("A Latch session id is required.");
|
|
17713
17771
|
if (!id)
|
|
@@ -17721,7 +17779,9 @@ function resolveLatchInput({ executable = "latch", providerSessionId, requestId,
|
|
|
17721
17779
|
if (!capabilities.resolve || !capabilities.canSend.ok) {
|
|
17722
17780
|
throw new LatchSessionCommandError(capabilities.canSend.reason || "This Latch session cannot resolve a pending prompt.");
|
|
17723
17781
|
}
|
|
17724
|
-
const bin =
|
|
17782
|
+
const bin = resolveLatchBinaryPath(executable);
|
|
17783
|
+
if (!bin)
|
|
17784
|
+
throw new LatchSessionCommandError(latchBinaryMissingMessage(executable));
|
|
17725
17785
|
const result = spawnSync3(bin, ["send", sessionId, "--resolve", `${id}=${selected}`, "--json"], {
|
|
17726
17786
|
encoding: "utf8",
|
|
17727
17787
|
shell: false,
|
|
@@ -17745,6 +17805,7 @@ function resolveLatchInput({ executable = "latch", providerSessionId, requestId,
|
|
|
17745
17805
|
var init_latch_send = __esm({
|
|
17746
17806
|
"../packages/core/dist/service/latch-send.js"() {
|
|
17747
17807
|
"use strict";
|
|
17808
|
+
init_latch_binary();
|
|
17748
17809
|
init_latch_environment();
|
|
17749
17810
|
init_latch_session();
|
|
17750
17811
|
}
|
|
@@ -17789,7 +17850,7 @@ var init_git_run = __esm({
|
|
|
17789
17850
|
});
|
|
17790
17851
|
|
|
17791
17852
|
// ../packages/core/dist/service/local-target/worktree-git.js
|
|
17792
|
-
import { existsSync as
|
|
17853
|
+
import { existsSync as existsSync18, realpathSync as realpathSync2 } from "node:fs";
|
|
17793
17854
|
import path18 from "node:path";
|
|
17794
17855
|
function resolveRealPath(targetPath) {
|
|
17795
17856
|
try {
|
|
@@ -17825,7 +17886,7 @@ function removeGitWorktree({ primaryRepoPath, worktreePath, force }) {
|
|
|
17825
17886
|
args.push(worktreePath);
|
|
17826
17887
|
runGitResult(primaryRepoPath, args);
|
|
17827
17888
|
runGitResult(primaryRepoPath, ["worktree", "prune"]);
|
|
17828
|
-
return !
|
|
17889
|
+
return !existsSync18(worktreePath);
|
|
17829
17890
|
}
|
|
17830
17891
|
function collectManagedWorktrees({ worktreeRoot, projects }) {
|
|
17831
17892
|
const resolvedRoot = resolveRealPath(worktreeRoot);
|
|
@@ -17833,7 +17894,7 @@ function collectManagedWorktrees({ worktreeRoot, projects }) {
|
|
|
17833
17894
|
const seen = /* @__PURE__ */ new Set();
|
|
17834
17895
|
for (const project of projects) {
|
|
17835
17896
|
const repoPath = project.primaryRepoPath;
|
|
17836
|
-
if (!repoPath || !
|
|
17897
|
+
if (!repoPath || !existsSync18(repoPath))
|
|
17837
17898
|
continue;
|
|
17838
17899
|
const out = runGitResult(repoPath, ["worktree", "list", "--porcelain"]);
|
|
17839
17900
|
if (!out.ok)
|
|
@@ -17911,10 +17972,10 @@ var init_worktree_git = __esm({
|
|
|
17911
17972
|
});
|
|
17912
17973
|
|
|
17913
17974
|
// ../packages/core/dist/service/local-target/branch-actions-git.js
|
|
17914
|
-
import { existsSync as
|
|
17975
|
+
import { existsSync as existsSync19 } from "node:fs";
|
|
17915
17976
|
function integrateBranch(input) {
|
|
17916
17977
|
const { branchName, baseBranch, worktreePath, primaryRepoPath } = input;
|
|
17917
|
-
if (!
|
|
17978
|
+
if (!existsSync19(worktreePath)) {
|
|
17918
17979
|
return {
|
|
17919
17980
|
ok: false,
|
|
17920
17981
|
code: "BRANCH_NO_WORKTREE",
|
|
@@ -17988,15 +18049,15 @@ function integrateBranch(input) {
|
|
|
17988
18049
|
}
|
|
17989
18050
|
function commitBranch(input) {
|
|
17990
18051
|
const { branchName, worktreePath } = input;
|
|
17991
|
-
const
|
|
17992
|
-
if (!
|
|
18052
|
+
const trimmed8 = (input.message ?? "").trim();
|
|
18053
|
+
if (!trimmed8) {
|
|
17993
18054
|
return {
|
|
17994
18055
|
ok: false,
|
|
17995
18056
|
code: "BRANCH_COMMIT_MESSAGE_REQUIRED",
|
|
17996
18057
|
message: "A commit message is required."
|
|
17997
18058
|
};
|
|
17998
18059
|
}
|
|
17999
|
-
if (!
|
|
18060
|
+
if (!existsSync19(worktreePath)) {
|
|
18000
18061
|
return {
|
|
18001
18062
|
ok: false,
|
|
18002
18063
|
code: "BRANCH_NO_WORKTREE",
|
|
@@ -18028,7 +18089,7 @@ function commitBranch(input) {
|
|
|
18028
18089
|
detail: staged.stderr || staged.stdout
|
|
18029
18090
|
};
|
|
18030
18091
|
}
|
|
18031
|
-
const commit = runGitResult(worktreePath, ["commit", "-m",
|
|
18092
|
+
const commit = runGitResult(worktreePath, ["commit", "-m", trimmed8]);
|
|
18032
18093
|
if (!commit.ok) {
|
|
18033
18094
|
return {
|
|
18034
18095
|
ok: false,
|
|
@@ -18052,7 +18113,7 @@ function pushParent(input) {
|
|
|
18052
18113
|
};
|
|
18053
18114
|
}
|
|
18054
18115
|
let summary = `Pushed ${baseBranch} to origin.`;
|
|
18055
|
-
if (
|
|
18116
|
+
if (existsSync19(worktreePath) && !worktreeIsDirty(worktreePath)) {
|
|
18056
18117
|
if (removeGitWorktree({ primaryRepoPath, worktreePath, force: false })) {
|
|
18057
18118
|
summary += ` Removed the merged worktree for ${branchName}.`;
|
|
18058
18119
|
}
|
|
@@ -18061,7 +18122,7 @@ function pushParent(input) {
|
|
|
18061
18122
|
}
|
|
18062
18123
|
function publishBranch(input) {
|
|
18063
18124
|
const { branchName, worktreePath, primaryRepoPath } = input;
|
|
18064
|
-
const repo =
|
|
18125
|
+
const repo = existsSync19(worktreePath) ? worktreePath : primaryRepoPath;
|
|
18065
18126
|
const push = runGitResult(repo, ["push", "-u", "origin", branchName]);
|
|
18066
18127
|
if (!push.ok) {
|
|
18067
18128
|
return {
|
|
@@ -18111,7 +18172,7 @@ var init_branch_status_git = __esm({
|
|
|
18111
18172
|
});
|
|
18112
18173
|
|
|
18113
18174
|
// ../packages/core/dist/service/local-target/commit-message-diff-git.js
|
|
18114
|
-
import { existsSync as
|
|
18175
|
+
import { existsSync as existsSync20 } from "node:fs";
|
|
18115
18176
|
function collectWorktreeChanges(worktreePath) {
|
|
18116
18177
|
const status = runGitResult(worktreePath, ["status", "--porcelain"]);
|
|
18117
18178
|
const diff = runGitResult(worktreePath, ["diff", "HEAD"]);
|
|
@@ -18127,7 +18188,7 @@ ${diff.stdout}`);
|
|
|
18127
18188
|
return sections.join("\n\n");
|
|
18128
18189
|
}
|
|
18129
18190
|
function gatherCommitMessageDiff(worktreePath) {
|
|
18130
|
-
if (!
|
|
18191
|
+
if (!existsSync20(worktreePath)) {
|
|
18131
18192
|
return {
|
|
18132
18193
|
ok: false,
|
|
18133
18194
|
code: "BRANCH_NO_WORKTREE",
|
|
@@ -18188,7 +18249,7 @@ var init_result = __esm({
|
|
|
18188
18249
|
});
|
|
18189
18250
|
|
|
18190
18251
|
// ../packages/core/dist/service/local-target/in-process-provider.js
|
|
18191
|
-
import { existsSync as
|
|
18252
|
+
import { existsSync as existsSync21 } from "node:fs";
|
|
18192
18253
|
var InProcessProvider;
|
|
18193
18254
|
var init_in_process_provider = __esm({
|
|
18194
18255
|
"../packages/core/dist/service/local-target/in-process-provider.js"() {
|
|
@@ -18221,7 +18282,7 @@ var init_in_process_provider = __esm({
|
|
|
18221
18282
|
*/
|
|
18222
18283
|
async observeResource(input) {
|
|
18223
18284
|
const observedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
18224
|
-
const state =
|
|
18285
|
+
const state = existsSync21(input.path) ? "available" : "missing";
|
|
18225
18286
|
return ok(this.target, { state, observedAt });
|
|
18226
18287
|
}
|
|
18227
18288
|
/**
|
|
@@ -18668,7 +18729,7 @@ var init_branch_planning = __esm({
|
|
|
18668
18729
|
|
|
18669
18730
|
// src/branch-preparation.ts
|
|
18670
18731
|
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
18671
|
-
import { existsSync as
|
|
18732
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync10, statSync as statSync5 } from "node:fs";
|
|
18672
18733
|
import os4 from "node:os";
|
|
18673
18734
|
import path21 from "node:path";
|
|
18674
18735
|
function runGit3(cwd, args, options = {}) {
|
|
@@ -18840,7 +18901,7 @@ function ensureBranchRef(gitRoot, decision) {
|
|
|
18840
18901
|
function ensureWorktree(gitRoot, decision) {
|
|
18841
18902
|
ensureBranchRef(gitRoot, decision);
|
|
18842
18903
|
mkdirSync10(path21.dirname(decision.worktreePath), { recursive: true });
|
|
18843
|
-
if (
|
|
18904
|
+
if (existsSync22(decision.worktreePath)) {
|
|
18844
18905
|
if (!statSync5(decision.worktreePath).isDirectory()) {
|
|
18845
18906
|
throw new Error(`Worktree path exists and is not a directory: ${decision.worktreePath}`);
|
|
18846
18907
|
}
|
|
@@ -19015,16 +19076,16 @@ async function resolveProjectByIdOrName({
|
|
|
19015
19076
|
backend,
|
|
19016
19077
|
projectRef
|
|
19017
19078
|
}) {
|
|
19018
|
-
const
|
|
19079
|
+
const trimmed8 = projectRef.trim();
|
|
19019
19080
|
try {
|
|
19020
|
-
return await backend.get(`/api/projects/${encodeURIComponent(
|
|
19081
|
+
return await backend.get(`/api/projects/${encodeURIComponent(trimmed8)}`);
|
|
19021
19082
|
} catch (error) {
|
|
19022
19083
|
if (!(error instanceof CliError) || !/project not found/i.test(error.message)) throw error;
|
|
19023
19084
|
}
|
|
19024
19085
|
const projects = await listAccessibleProjects({ backend });
|
|
19025
|
-
const match = projects.find((project) => project.name ===
|
|
19086
|
+
const match = projects.find((project) => project.name === trimmed8) ?? projects.find((project) => project.slug === trimmed8);
|
|
19026
19087
|
if (!match) {
|
|
19027
|
-
throw new CliError({ message: `Project not found: ${
|
|
19088
|
+
throw new CliError({ message: `Project not found: ${trimmed8}` });
|
|
19028
19089
|
}
|
|
19029
19090
|
return match;
|
|
19030
19091
|
}
|
|
@@ -20108,7 +20169,7 @@ var init_launch = __esm({
|
|
|
20108
20169
|
});
|
|
20109
20170
|
|
|
20110
20171
|
// src/launch-bootstrap.ts
|
|
20111
|
-
import { existsSync as
|
|
20172
|
+
import { existsSync as existsSync23, readdirSync as readdirSync4, readFileSync as readFileSync16, statSync as statSync6 } from "node:fs";
|
|
20112
20173
|
import path25 from "node:path";
|
|
20113
20174
|
function firstCapture(match) {
|
|
20114
20175
|
if (!match) return null;
|
|
@@ -20130,7 +20191,7 @@ function recoverLaunchBootstrapFromProjectTmp({
|
|
|
20130
20191
|
const trimmedMission = missionId.trim();
|
|
20131
20192
|
if (!trimmedMission) return empty;
|
|
20132
20193
|
const tmpDir = projectTmpDir(workingDirectory);
|
|
20133
|
-
if (!
|
|
20194
|
+
if (!existsSync23(tmpDir)) return empty;
|
|
20134
20195
|
const prefix = `launch-${safeMissionPart(trimmedMission)}-`;
|
|
20135
20196
|
let newest = null;
|
|
20136
20197
|
try {
|
|
@@ -20188,8 +20249,8 @@ function promptText({
|
|
|
20188
20249
|
return new Promise((resolve) => {
|
|
20189
20250
|
rl.question(`${question}${suffix}: `, (answer) => {
|
|
20190
20251
|
rl.close();
|
|
20191
|
-
const
|
|
20192
|
-
resolve(
|
|
20252
|
+
const trimmed8 = answer.trim();
|
|
20253
|
+
resolve(trimmed8 || defaultValue || "");
|
|
20193
20254
|
});
|
|
20194
20255
|
});
|
|
20195
20256
|
}
|
|
@@ -20778,7 +20839,7 @@ var init_protocol_help = __esm({
|
|
|
20778
20839
|
|
|
20779
20840
|
// src/vcs-sessions.ts
|
|
20780
20841
|
import { createHash as createHash8 } from "node:crypto";
|
|
20781
|
-
import { existsSync as
|
|
20842
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync11, readFileSync as readFileSync18, writeFileSync as writeFileSync10 } from "node:fs";
|
|
20782
20843
|
import path27 from "node:path";
|
|
20783
20844
|
function manifestPath2(workingDirectory) {
|
|
20784
20845
|
const key = createHash8("sha256").update(path27.resolve(workingDirectory)).digest("hex");
|
|
@@ -20787,7 +20848,7 @@ function manifestPath2(workingDirectory) {
|
|
|
20787
20848
|
function readEntries(workingDirectory) {
|
|
20788
20849
|
try {
|
|
20789
20850
|
const target = manifestPath2(workingDirectory);
|
|
20790
|
-
if (!
|
|
20851
|
+
if (!existsSync24(target)) return [];
|
|
20791
20852
|
const raw = JSON.parse(readFileSync18(target, "utf8"));
|
|
20792
20853
|
if (!Array.isArray(raw.entries)) return [];
|
|
20793
20854
|
const now = Date.now();
|
|
@@ -20861,7 +20922,7 @@ var init_vcs_sessions = __esm({
|
|
|
20861
20922
|
import { execFileSync as execFileSync7 } from "node:child_process";
|
|
20862
20923
|
import { createHash as createHash9 } from "node:crypto";
|
|
20863
20924
|
import {
|
|
20864
|
-
existsSync as
|
|
20925
|
+
existsSync as existsSync25,
|
|
20865
20926
|
mkdirSync as mkdirSync12,
|
|
20866
20927
|
readdirSync as readdirSync5,
|
|
20867
20928
|
readFileSync as readFileSync19,
|
|
@@ -20973,7 +21034,7 @@ function readBashSnapshot({
|
|
|
20973
21034
|
}) {
|
|
20974
21035
|
try {
|
|
20975
21036
|
const target = bashSnapshotPath({ workingDirectory, missionId });
|
|
20976
|
-
if (!
|
|
21037
|
+
if (!existsSync25(target)) return /* @__PURE__ */ new Map();
|
|
20977
21038
|
const raw = JSON.parse(readFileSync19(target, "utf8"));
|
|
20978
21039
|
const byPath = /* @__PURE__ */ new Map();
|
|
20979
21040
|
if (Array.isArray(raw.files)) {
|
|
@@ -21065,7 +21126,7 @@ function resetTouchedFiles({
|
|
|
21065
21126
|
touchedFilesPath({ workingDirectory, missionId }),
|
|
21066
21127
|
bashSnapshotPath({ workingDirectory, missionId })
|
|
21067
21128
|
]) {
|
|
21068
|
-
if (
|
|
21129
|
+
if (existsSync25(target)) rmSync5(target);
|
|
21069
21130
|
}
|
|
21070
21131
|
} catch {
|
|
21071
21132
|
}
|
|
@@ -21076,14 +21137,14 @@ function resetRationaleNotes({
|
|
|
21076
21137
|
}) {
|
|
21077
21138
|
try {
|
|
21078
21139
|
const target = rationaleNotesPath({ workingDirectory, missionId });
|
|
21079
|
-
if (
|
|
21140
|
+
if (existsSync25(target)) rmSync5(target);
|
|
21080
21141
|
} catch {
|
|
21081
21142
|
}
|
|
21082
21143
|
}
|
|
21083
21144
|
function truncateNoteText(value, max = 500) {
|
|
21084
|
-
const
|
|
21085
|
-
if (!
|
|
21086
|
-
return
|
|
21145
|
+
const trimmed8 = (value ?? "").replace(/\s+/g, " ").trim();
|
|
21146
|
+
if (!trimmed8) return null;
|
|
21147
|
+
return trimmed8.length > max ? `${trimmed8.slice(0, max - 1)}...` : trimmed8;
|
|
21087
21148
|
}
|
|
21088
21149
|
function readRationaleNotes({
|
|
21089
21150
|
workingDirectory,
|
|
@@ -21091,7 +21152,7 @@ function readRationaleNotes({
|
|
|
21091
21152
|
}) {
|
|
21092
21153
|
try {
|
|
21093
21154
|
const target = rationaleNotesPath({ workingDirectory, missionId });
|
|
21094
|
-
if (!
|
|
21155
|
+
if (!existsSync25(target)) return [];
|
|
21095
21156
|
const raw = JSON.parse(readFileSync19(target, "utf8"));
|
|
21096
21157
|
if (!Array.isArray(raw.notes)) return [];
|
|
21097
21158
|
return raw.notes.flatMap((note) => {
|
|
@@ -21179,7 +21240,7 @@ function hasTouchedFilesLog({
|
|
|
21179
21240
|
workingDirectory,
|
|
21180
21241
|
missionId
|
|
21181
21242
|
}) {
|
|
21182
|
-
return
|
|
21243
|
+
return existsSync25(touchedFilesPath({ workingDirectory, missionId }));
|
|
21183
21244
|
}
|
|
21184
21245
|
function readTouchedLog({
|
|
21185
21246
|
workingDirectory,
|
|
@@ -21187,7 +21248,7 @@ function readTouchedLog({
|
|
|
21187
21248
|
}) {
|
|
21188
21249
|
try {
|
|
21189
21250
|
const target = touchedFilesPath({ workingDirectory, missionId });
|
|
21190
|
-
if (!
|
|
21251
|
+
if (!existsSync25(target)) return null;
|
|
21191
21252
|
const raw = JSON.parse(readFileSync19(target, "utf8"));
|
|
21192
21253
|
const result = /* @__PURE__ */ new Set();
|
|
21193
21254
|
if (Array.isArray(raw.paths)) {
|
|
@@ -21241,7 +21302,7 @@ function readPeerTouchedClaims({
|
|
|
21241
21302
|
);
|
|
21242
21303
|
if (activeMissionIds.size === 0) return [];
|
|
21243
21304
|
const directory = path28.join(resolveGlobalDataDir(), "vcs-touched");
|
|
21244
|
-
if (!
|
|
21305
|
+
if (!existsSync25(directory)) return [];
|
|
21245
21306
|
const claims = [];
|
|
21246
21307
|
for (const name of readdirSync5(directory)) {
|
|
21247
21308
|
if (!name.endsWith(".json") || name.endsWith(".bash-snapshot.json")) continue;
|
|
@@ -21271,7 +21332,7 @@ function readBaselineSnapshot({
|
|
|
21271
21332
|
}) {
|
|
21272
21333
|
try {
|
|
21273
21334
|
const target = baselineFilePath({ workingDirectory, missionId });
|
|
21274
|
-
if (!
|
|
21335
|
+
if (!existsSync25(target)) return /* @__PURE__ */ new Map();
|
|
21275
21336
|
const raw = JSON.parse(readFileSync19(target, "utf8"));
|
|
21276
21337
|
if (Array.isArray(raw.files)) {
|
|
21277
21338
|
const byPath = /* @__PURE__ */ new Map();
|
|
@@ -21367,7 +21428,7 @@ function compileIgnorePattern(rawPattern) {
|
|
|
21367
21428
|
function loadIgnoreRules(repoRoot) {
|
|
21368
21429
|
try {
|
|
21369
21430
|
const target = path28.join(repoRoot, OVERLORD_IGNORE_FILENAME);
|
|
21370
|
-
if (!
|
|
21431
|
+
if (!existsSync25(target)) return [];
|
|
21371
21432
|
const rules = [];
|
|
21372
21433
|
for (const rawLine of readFileSync19(target, "utf8").split("\n")) {
|
|
21373
21434
|
const line = rawLine.replace(/\r$/, "").trim();
|
|
@@ -21501,18 +21562,18 @@ import { readFileSync as readFileSync20 } from "node:fs";
|
|
|
21501
21562
|
import path29 from "node:path";
|
|
21502
21563
|
function firstNonEmptyString(value) {
|
|
21503
21564
|
if (typeof value !== "string") return null;
|
|
21504
|
-
const
|
|
21505
|
-
return
|
|
21565
|
+
const trimmed8 = value.trim();
|
|
21566
|
+
return trimmed8 ? trimmed8 : null;
|
|
21506
21567
|
}
|
|
21507
21568
|
function collectCandidates(toolInput) {
|
|
21508
21569
|
const counts = /* @__PURE__ */ new Map();
|
|
21509
21570
|
const order = [];
|
|
21510
21571
|
const bump = (raw, amount) => {
|
|
21511
21572
|
if (typeof raw !== "string") return;
|
|
21512
|
-
const
|
|
21513
|
-
if (!
|
|
21514
|
-
if (!counts.has(
|
|
21515
|
-
counts.set(
|
|
21573
|
+
const trimmed8 = raw.trim();
|
|
21574
|
+
if (!trimmed8) return;
|
|
21575
|
+
if (!counts.has(trimmed8)) order.push(trimmed8);
|
|
21576
|
+
counts.set(trimmed8, (counts.get(trimmed8) ?? 0) + amount);
|
|
21516
21577
|
};
|
|
21517
21578
|
bump(toolInput.file_path, 1);
|
|
21518
21579
|
bump(toolInput.filePath, 1);
|
|
@@ -21736,7 +21797,7 @@ var init_resource_observations = __esm({
|
|
|
21736
21797
|
|
|
21737
21798
|
// src/runner-identity.ts
|
|
21738
21799
|
import { randomUUID } from "node:crypto";
|
|
21739
|
-
import { existsSync as
|
|
21800
|
+
import { existsSync as existsSync26, mkdirSync as mkdirSync13, readFileSync as readFileSync21, writeFileSync as writeFileSync12 } from "node:fs";
|
|
21740
21801
|
import { hostname as hostname4 } from "node:os";
|
|
21741
21802
|
import path30 from "node:path";
|
|
21742
21803
|
function envValue(name) {
|
|
@@ -21750,7 +21811,7 @@ function resolveRunnerInstanceId(dataDir = resolveGlobalDataDir()) {
|
|
|
21750
21811
|
const pinned = envValue("OVERLORD_RUNNER_INSTANCE_ID");
|
|
21751
21812
|
if (pinned) return pinned;
|
|
21752
21813
|
const statePath = runnerInstanceStatePath(dataDir);
|
|
21753
|
-
if (
|
|
21814
|
+
if (existsSync26(statePath)) {
|
|
21754
21815
|
try {
|
|
21755
21816
|
const parsed = JSON.parse(readFileSync21(statePath, "utf8"));
|
|
21756
21817
|
if (typeof parsed.runnerInstanceId === "string" && parsed.runnerInstanceId.trim()) {
|
|
@@ -21804,7 +21865,15 @@ var init_runner_identity = __esm({
|
|
|
21804
21865
|
|
|
21805
21866
|
// src/runner-service.ts
|
|
21806
21867
|
import { execFile } from "node:child_process";
|
|
21807
|
-
import {
|
|
21868
|
+
import {
|
|
21869
|
+
chmodSync as chmodSync5,
|
|
21870
|
+
existsSync as existsSync27,
|
|
21871
|
+
mkdirSync as mkdirSync14,
|
|
21872
|
+
readFileSync as readFileSync22,
|
|
21873
|
+
rmSync as rmSync6,
|
|
21874
|
+
statSync as statSync7,
|
|
21875
|
+
writeFileSync as writeFileSync13
|
|
21876
|
+
} from "node:fs";
|
|
21808
21877
|
import os5 from "node:os";
|
|
21809
21878
|
import path31 from "node:path";
|
|
21810
21879
|
import { promisify } from "node:util";
|
|
@@ -21812,6 +21881,38 @@ function writeServiceFile(filePath, contents) {
|
|
|
21812
21881
|
writeFileSync13(filePath, contents, { mode: 384 });
|
|
21813
21882
|
chmodSync5(filePath, 384);
|
|
21814
21883
|
}
|
|
21884
|
+
function resolveRunnerEntryScript(argv = process.argv) {
|
|
21885
|
+
return argv[1] ? path31.resolve(argv[1]) : null;
|
|
21886
|
+
}
|
|
21887
|
+
function readRunnerExecutableFileIdentity(filePath, stat = statSync7) {
|
|
21888
|
+
try {
|
|
21889
|
+
const metadata = stat(filePath);
|
|
21890
|
+
return { dev: metadata.dev, ino: metadata.ino, mtimeMs: metadata.mtimeMs };
|
|
21891
|
+
} catch (error) {
|
|
21892
|
+
return error.code === "ENOENT" ? null : void 0;
|
|
21893
|
+
}
|
|
21894
|
+
}
|
|
21895
|
+
function captureRunnerSupervisorIdentity({
|
|
21896
|
+
execPath = process.execPath,
|
|
21897
|
+
entryScript = resolveRunnerEntryScript(),
|
|
21898
|
+
stat = statSync7
|
|
21899
|
+
} = {}) {
|
|
21900
|
+
return {
|
|
21901
|
+
program: readRunnerExecutableFileIdentity(execPath, stat),
|
|
21902
|
+
entryScript: entryScript ? readRunnerExecutableFileIdentity(entryScript, stat) : void 0
|
|
21903
|
+
};
|
|
21904
|
+
}
|
|
21905
|
+
function executableIdentityChanged(initial, current) {
|
|
21906
|
+
if (current === null) return true;
|
|
21907
|
+
if (!initial || !current) return false;
|
|
21908
|
+
return initial.dev !== current.dev || initial.ino !== current.ino || initial.mtimeMs !== current.mtimeMs;
|
|
21909
|
+
}
|
|
21910
|
+
function shouldRestartRunnerSupervisor({
|
|
21911
|
+
initial,
|
|
21912
|
+
current
|
|
21913
|
+
}) {
|
|
21914
|
+
return executableIdentityChanged(initial.program, current.program) || executableIdentityChanged(initial.entryScript, current.entryScript);
|
|
21915
|
+
}
|
|
21815
21916
|
function emptyRunnerServiceState() {
|
|
21816
21917
|
return {
|
|
21817
21918
|
serviceKind: null,
|
|
@@ -21832,7 +21933,7 @@ function runnerServiceStatePath(dataDir = resolveGlobalDataDir()) {
|
|
|
21832
21933
|
}
|
|
21833
21934
|
function readRunnerServiceState(dataDir = resolveGlobalDataDir()) {
|
|
21834
21935
|
const filePath = runnerServiceStatePath(dataDir);
|
|
21835
|
-
if (!
|
|
21936
|
+
if (!existsSync27(filePath)) return emptyRunnerServiceState();
|
|
21836
21937
|
try {
|
|
21837
21938
|
const parsed = JSON.parse(readFileSync22(filePath, "utf8"));
|
|
21838
21939
|
return { ...emptyRunnerServiceState(), ...parsed };
|
|
@@ -21850,45 +21951,14 @@ function patchRunnerServiceState(patch, dataDir = resolveGlobalDataDir()) {
|
|
|
21850
21951
|
writeRunnerServiceState(next, dataDir);
|
|
21851
21952
|
return next;
|
|
21852
21953
|
}
|
|
21853
|
-
function runnerFocusStatePath(dataDir = resolveGlobalDataDir()) {
|
|
21854
|
-
return path31.join(dataDir, RUNNER_FOCUS_STATE_FILENAME);
|
|
21855
|
-
}
|
|
21856
|
-
function readDesktopFocusState(dataDir = resolveGlobalDataDir()) {
|
|
21857
|
-
const filePath = runnerFocusStatePath(dataDir);
|
|
21858
|
-
if (!existsSync26(filePath)) return { lastFocusedAt: null };
|
|
21859
|
-
try {
|
|
21860
|
-
const parsed = JSON.parse(readFileSync22(filePath, "utf8"));
|
|
21861
|
-
return { lastFocusedAt: parsed.lastFocusedAt ?? null };
|
|
21862
|
-
} catch {
|
|
21863
|
-
return { lastFocusedAt: null };
|
|
21864
|
-
}
|
|
21865
|
-
}
|
|
21866
|
-
function withinWindow(timestamp, now, windowMs) {
|
|
21867
|
-
if (!timestamp) return false;
|
|
21868
|
-
const ms = Date.parse(timestamp);
|
|
21869
|
-
if (Number.isNaN(ms)) return false;
|
|
21870
|
-
return now - ms <= windowMs;
|
|
21871
|
-
}
|
|
21872
|
-
function selectBasePollIntervalMs({
|
|
21873
|
-
lastLaunchedAt,
|
|
21874
|
-
lastDesktopFocusAt = null,
|
|
21875
|
-
now
|
|
21876
|
-
}) {
|
|
21877
|
-
const jobActive = withinWindow(lastLaunchedAt, now, IDLE_BACKOFF_MS);
|
|
21878
|
-
const focusActive = withinWindow(lastDesktopFocusAt, now, DESKTOP_FOCUS_WINDOW_MS);
|
|
21879
|
-
return jobActive || focusActive ? FAST_POLL_INTERVAL_MS : SLOW_POLL_INTERVAL_MS;
|
|
21880
|
-
}
|
|
21881
21954
|
function applyPollJitter(intervalMs, random = Math.random) {
|
|
21882
21955
|
const spread = intervalMs * 0.1;
|
|
21883
21956
|
return Math.round(intervalMs + (random() * 2 - 1) * spread);
|
|
21884
21957
|
}
|
|
21885
|
-
function nextRunnerLastError(pollError) {
|
|
21886
|
-
return pollError;
|
|
21887
|
-
}
|
|
21888
21958
|
function resolveOverlordAppInvocation({
|
|
21889
21959
|
platform: platform2 = process.platform,
|
|
21890
21960
|
homedir: homedir2 = os5.homedir(),
|
|
21891
|
-
exists =
|
|
21961
|
+
exists = existsSync27
|
|
21892
21962
|
} = {}) {
|
|
21893
21963
|
if (platform2 !== "darwin") return null;
|
|
21894
21964
|
const appDirs = [
|
|
@@ -21920,27 +21990,15 @@ function resolveOvldInvocation(argv = process.argv, execPath = process.execPath)
|
|
|
21920
21990
|
}
|
|
21921
21991
|
return { program: execPath, args: ["runner", "supervise"] };
|
|
21922
21992
|
}
|
|
21923
|
-
function describeServicePublisher({
|
|
21924
|
-
execProgram,
|
|
21925
|
-
platform: platform2 = process.platform
|
|
21926
|
-
}) {
|
|
21927
|
-
if (platform2 !== "darwin" || !execProgram) {
|
|
21928
|
-
return { publisher: "unknown", needsReinstallForOverlord: false };
|
|
21929
|
-
}
|
|
21930
|
-
if (execProgram.includes(".app/Contents/MacOS/")) {
|
|
21931
|
-
return { publisher: "overlord", needsReinstallForOverlord: false };
|
|
21932
|
-
}
|
|
21933
|
-
return { publisher: "node", needsReinstallForOverlord: true };
|
|
21934
|
-
}
|
|
21935
21993
|
function buildRunnerServiceEnv({
|
|
21936
21994
|
backendUrl,
|
|
21937
21995
|
overlordHome,
|
|
21938
21996
|
runAsElectronNode
|
|
21939
21997
|
}) {
|
|
21940
21998
|
const env = {
|
|
21941
|
-
OVERLORD_BACKEND_URL: backendUrl,
|
|
21942
21999
|
PATH: process.env.PATH?.trim() || "/usr/local/bin:/usr/bin:/bin"
|
|
21943
22000
|
};
|
|
22001
|
+
if (!isLoopbackBackendUrl(backendUrl)) env.OVERLORD_BACKEND_URL = backendUrl;
|
|
21944
22002
|
const home = overlordHome?.trim() || process.env.OVLD_HOME?.trim();
|
|
21945
22003
|
if (home) env.OVLD_HOME = path31.resolve(home);
|
|
21946
22004
|
const token = process.env.OVERLORD_USER_TOKEN?.trim() || process.env.OVLD_USER_TOKEN?.trim();
|
|
@@ -22025,31 +22083,72 @@ async function runCommand(program, args) {
|
|
|
22025
22083
|
const { stdout } = await execFileAsync(program, args);
|
|
22026
22084
|
return { stdout: stdout.toString() };
|
|
22027
22085
|
}
|
|
22086
|
+
function launchdDomainTarget(uid) {
|
|
22087
|
+
return `gui/${uid}`;
|
|
22088
|
+
}
|
|
22089
|
+
function launchdServiceTarget(uid) {
|
|
22090
|
+
return `${launchdDomainTarget(uid)}/${LAUNCHD_LABEL}`;
|
|
22091
|
+
}
|
|
22092
|
+
function planLaunchdUnloadCommands(opts) {
|
|
22093
|
+
return [{ args: ["bootout", launchdServiceTarget(opts.uid)], tolerateFailure: true }];
|
|
22094
|
+
}
|
|
22095
|
+
function planLaunchdLoadCommands(opts) {
|
|
22096
|
+
const service = launchdServiceTarget(opts.uid);
|
|
22097
|
+
return [
|
|
22098
|
+
{ args: ["enable", service], tolerateFailure: true },
|
|
22099
|
+
{
|
|
22100
|
+
args: ["bootstrap", launchdDomainTarget(opts.uid), opts.plistPath],
|
|
22101
|
+
tolerateFailure: true
|
|
22102
|
+
},
|
|
22103
|
+
{ args: ["kickstart", service], tolerateFailure: false }
|
|
22104
|
+
];
|
|
22105
|
+
}
|
|
22028
22106
|
function resolveServiceManager(platform2 = process.platform) {
|
|
22029
22107
|
if (platform2 === "darwin") return new LaunchdManager();
|
|
22030
22108
|
if (platform2 === "linux") return new SystemdUserManager();
|
|
22031
22109
|
return null;
|
|
22032
22110
|
}
|
|
22033
|
-
var execFileAsync, RUNNER_SERVICE_STATE_FILENAME,
|
|
22111
|
+
var execFileAsync, RUNNER_SERVICE_STATE_FILENAME, LAUNCHD_LABEL, SYSTEMD_UNIT_NAME, FALLBACK_POLL_INTERVAL_MS, LaunchdManager, SystemdUserManager;
|
|
22034
22112
|
var init_runner_service = __esm({
|
|
22035
22113
|
"src/runner-service.ts"() {
|
|
22036
22114
|
"use strict";
|
|
22037
22115
|
init_config();
|
|
22038
22116
|
execFileAsync = promisify(execFile);
|
|
22039
22117
|
RUNNER_SERVICE_STATE_FILENAME = "runner-service.json";
|
|
22040
|
-
RUNNER_FOCUS_STATE_FILENAME = "runner-focus.json";
|
|
22041
22118
|
LAUNCHD_LABEL = "io.overlord.runner";
|
|
22042
22119
|
SYSTEMD_UNIT_NAME = "overlord-runner";
|
|
22043
|
-
|
|
22044
|
-
SLOW_POLL_INTERVAL_MS = 5e3;
|
|
22045
|
-
IDLE_BACKOFF_MS = 2 * 60 * 60 * 1e3;
|
|
22046
|
-
DESKTOP_FOCUS_WINDOW_MS = 30 * 60 * 1e3;
|
|
22120
|
+
FALLBACK_POLL_INTERVAL_MS = 5e3;
|
|
22047
22121
|
LaunchdManager = class {
|
|
22048
22122
|
kind = "launchd";
|
|
22049
22123
|
identifier = LAUNCHD_LABEL;
|
|
22124
|
+
exec;
|
|
22125
|
+
constructor(exec) {
|
|
22126
|
+
this.exec = exec ?? ((args) => runCommand("launchctl", args));
|
|
22127
|
+
}
|
|
22050
22128
|
unitPath() {
|
|
22051
22129
|
return path31.join(launchAgentsDir(), `${LAUNCHD_LABEL}.plist`);
|
|
22052
22130
|
}
|
|
22131
|
+
uid() {
|
|
22132
|
+
return process.getuid?.() ?? "";
|
|
22133
|
+
}
|
|
22134
|
+
async runSteps(steps) {
|
|
22135
|
+
for (const step of steps) {
|
|
22136
|
+
try {
|
|
22137
|
+
await this.exec(step.args);
|
|
22138
|
+
} catch (error) {
|
|
22139
|
+
if (!step.tolerateFailure) throw error;
|
|
22140
|
+
}
|
|
22141
|
+
}
|
|
22142
|
+
}
|
|
22143
|
+
/** True when launchd still has the label registered, whatever its run state. */
|
|
22144
|
+
async isLoaded() {
|
|
22145
|
+
try {
|
|
22146
|
+
await this.exec(["list", LAUNCHD_LABEL]);
|
|
22147
|
+
return true;
|
|
22148
|
+
} catch {
|
|
22149
|
+
return false;
|
|
22150
|
+
}
|
|
22151
|
+
}
|
|
22053
22152
|
render(opts) {
|
|
22054
22153
|
return renderLaunchdPlist({
|
|
22055
22154
|
label: LAUNCHD_LABEL,
|
|
@@ -22061,41 +22160,33 @@ var init_runner_service = __esm({
|
|
|
22061
22160
|
async install(opts) {
|
|
22062
22161
|
mkdirSync14(launchAgentsDir(), { recursive: true });
|
|
22063
22162
|
mkdirSync14(path31.join(resolveGlobalDataDir(), "logs"), { recursive: true });
|
|
22163
|
+
await this.runSteps(planLaunchdUnloadCommands({ uid: this.uid() }));
|
|
22064
22164
|
writeServiceFile(this.unitPath(), this.render(opts));
|
|
22065
22165
|
if (opts.autoStart) await this.start();
|
|
22066
22166
|
}
|
|
22067
22167
|
async start() {
|
|
22068
|
-
|
|
22069
|
-
await runCommand("launchctl", ["load", "-w", this.unitPath()]);
|
|
22070
|
-
} catch {
|
|
22071
|
-
await runCommand("launchctl", [
|
|
22072
|
-
"kickstart",
|
|
22073
|
-
"-k",
|
|
22074
|
-
`gui/${process.getuid?.() ?? ""}/${LAUNCHD_LABEL}`
|
|
22075
|
-
]);
|
|
22076
|
-
}
|
|
22168
|
+
await this.runSteps(planLaunchdLoadCommands({ uid: this.uid(), plistPath: this.unitPath() }));
|
|
22077
22169
|
}
|
|
22078
22170
|
async stop() {
|
|
22079
|
-
await
|
|
22171
|
+
await this.runSteps(planLaunchdUnloadCommands({ uid: this.uid() }));
|
|
22080
22172
|
}
|
|
22081
22173
|
async restart() {
|
|
22082
|
-
|
|
22083
|
-
await this.stop();
|
|
22084
|
-
} catch {
|
|
22085
|
-
}
|
|
22174
|
+
await this.stop();
|
|
22086
22175
|
await this.start();
|
|
22087
22176
|
}
|
|
22088
22177
|
async uninstall() {
|
|
22089
|
-
|
|
22090
|
-
|
|
22091
|
-
|
|
22178
|
+
await this.runSteps(planLaunchdUnloadCommands({ uid: this.uid() }));
|
|
22179
|
+
if (await this.isLoaded()) {
|
|
22180
|
+
throw new Error(
|
|
22181
|
+
`launchctl still reports ${LAUNCHD_LABEL} as loaded after bootout; left ${this.unitPath()} in place so the service can be repaired with \`ovld runner service restart\`.`
|
|
22182
|
+
);
|
|
22092
22183
|
}
|
|
22093
|
-
if (
|
|
22184
|
+
if (existsSync27(this.unitPath())) rmSync6(this.unitPath());
|
|
22094
22185
|
}
|
|
22095
22186
|
async status() {
|
|
22096
|
-
const installed =
|
|
22187
|
+
const installed = existsSync27(this.unitPath());
|
|
22097
22188
|
try {
|
|
22098
|
-
const { stdout } = await
|
|
22189
|
+
const { stdout } = await this.exec(["list"]);
|
|
22099
22190
|
const line = stdout.split("\n").find((row) => row.includes(LAUNCHD_LABEL));
|
|
22100
22191
|
if (!line) return { installed, running: installed ? "stopped" : "unknown" };
|
|
22101
22192
|
const pid = line.trim().split(/\s+/)[0];
|
|
@@ -22136,14 +22227,14 @@ var init_runner_service = __esm({
|
|
|
22136
22227
|
await runCommand("systemctl", ["--user", "disable", this.identifier]);
|
|
22137
22228
|
} catch {
|
|
22138
22229
|
}
|
|
22139
|
-
if (
|
|
22230
|
+
if (existsSync27(this.unitPath())) rmSync6(this.unitPath());
|
|
22140
22231
|
try {
|
|
22141
22232
|
await runCommand("systemctl", ["--user", "daemon-reload"]);
|
|
22142
22233
|
} catch {
|
|
22143
22234
|
}
|
|
22144
22235
|
}
|
|
22145
22236
|
async status() {
|
|
22146
|
-
const installed =
|
|
22237
|
+
const installed = existsSync27(this.unitPath());
|
|
22147
22238
|
try {
|
|
22148
22239
|
const { stdout } = await runCommand("systemctl", ["--user", "is-active", this.identifier]);
|
|
22149
22240
|
return { installed, running: stdout.trim() === "active" ? "running" : "stopped" };
|
|
@@ -22158,9 +22249,9 @@ var init_runner_service = __esm({
|
|
|
22158
22249
|
// src/select-prompt.ts
|
|
22159
22250
|
import { createInterface as createInterface4 } from "node:readline";
|
|
22160
22251
|
function parseNumberedSelection(answer, count) {
|
|
22161
|
-
const
|
|
22162
|
-
if (!/^\d+$/.test(
|
|
22163
|
-
const value = Number.parseInt(
|
|
22252
|
+
const trimmed8 = answer.trim();
|
|
22253
|
+
if (!/^\d+$/.test(trimmed8)) return null;
|
|
22254
|
+
const value = Number.parseInt(trimmed8, 10);
|
|
22164
22255
|
if (!Number.isInteger(value) || value < 1 || value > count) return null;
|
|
22165
22256
|
return value - 1;
|
|
22166
22257
|
}
|
|
@@ -22210,7 +22301,7 @@ var init_select_prompt = __esm({
|
|
|
22210
22301
|
|
|
22211
22302
|
// src/session-key.ts
|
|
22212
22303
|
import { createHash as createHash10 } from "node:crypto";
|
|
22213
|
-
import { existsSync as
|
|
22304
|
+
import { existsSync as existsSync28, mkdirSync as mkdirSync15, readFileSync as readFileSync23, rmSync as rmSync7, writeFileSync as writeFileSync14 } from "node:fs";
|
|
22214
22305
|
import path32 from "node:path";
|
|
22215
22306
|
function sessionKeyCachePath({
|
|
22216
22307
|
missionId,
|
|
@@ -22225,7 +22316,7 @@ function readCachedSessionKey({
|
|
|
22225
22316
|
}) {
|
|
22226
22317
|
try {
|
|
22227
22318
|
const filePath = sessionKeyCachePath({ missionId, workingDirectory });
|
|
22228
|
-
if (!
|
|
22319
|
+
if (!existsSync28(filePath)) return void 0;
|
|
22229
22320
|
const raw = JSON.parse(readFileSync23(filePath, "utf8"));
|
|
22230
22321
|
return typeof raw.sessionKey === "string" && raw.sessionKey.trim() ? raw.sessionKey.trim() : void 0;
|
|
22231
22322
|
} catch {
|
|
@@ -22238,13 +22329,13 @@ function writeCachedSessionKey({
|
|
|
22238
22329
|
sessionKey
|
|
22239
22330
|
}) {
|
|
22240
22331
|
try {
|
|
22241
|
-
const
|
|
22242
|
-
if (!
|
|
22332
|
+
const trimmed8 = sessionKey.trim();
|
|
22333
|
+
if (!trimmed8) return;
|
|
22243
22334
|
const filePath = sessionKeyCachePath({ missionId, workingDirectory });
|
|
22244
22335
|
mkdirSync15(path32.dirname(filePath), { recursive: true });
|
|
22245
22336
|
writeFileSync14(
|
|
22246
22337
|
filePath,
|
|
22247
|
-
JSON.stringify({ sessionKey:
|
|
22338
|
+
JSON.stringify({ sessionKey: trimmed8, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }),
|
|
22248
22339
|
{ mode: 384 }
|
|
22249
22340
|
);
|
|
22250
22341
|
} catch {
|
|
@@ -22256,7 +22347,7 @@ function clearCachedSessionKey({
|
|
|
22256
22347
|
}) {
|
|
22257
22348
|
try {
|
|
22258
22349
|
const filePath = sessionKeyCachePath({ missionId, workingDirectory });
|
|
22259
|
-
if (
|
|
22350
|
+
if (existsSync28(filePath)) rmSync7(filePath);
|
|
22260
22351
|
} catch {
|
|
22261
22352
|
}
|
|
22262
22353
|
}
|
|
@@ -22272,9 +22363,10 @@ var commands_exports = {};
|
|
|
22272
22363
|
__export(commands_exports, {
|
|
22273
22364
|
resolveProtocolFileInputs: () => resolveProtocolFileInputs,
|
|
22274
22365
|
runManagementCommand: () => runManagementCommand,
|
|
22275
|
-
runProtocolCommand: () => runProtocolCommand
|
|
22366
|
+
runProtocolCommand: () => runProtocolCommand,
|
|
22367
|
+
runRunnerOnce: () => runRunnerOnce
|
|
22276
22368
|
});
|
|
22277
|
-
import { existsSync as
|
|
22369
|
+
import { existsSync as existsSync29, readFileSync as readFileSync24 } from "node:fs";
|
|
22278
22370
|
import os6 from "node:os";
|
|
22279
22371
|
import path33 from "node:path";
|
|
22280
22372
|
function resourceAccessModeBody(value) {
|
|
@@ -22904,7 +22996,7 @@ async function runProtocolCommand({
|
|
|
22904
22996
|
clearCachedSessionKey({ missionId, workingDirectory });
|
|
22905
22997
|
removeActiveSession({ workingDirectory, missionId });
|
|
22906
22998
|
clearActiveMissionPointer(workingDirectory);
|
|
22907
|
-
const connectorInstallsEditHook =
|
|
22999
|
+
const connectorInstallsEditHook = existsSync29(path33.join(os6.homedir(), ".claude")) || existsSync29(
|
|
22908
23000
|
path33.join(
|
|
22909
23001
|
os6.homedir(),
|
|
22910
23002
|
".cursor",
|
|
@@ -23435,6 +23527,165 @@ function isNoExecutionTargetRegisteredError(error) {
|
|
|
23435
23527
|
const message = error instanceof Error ? error.message : String(error);
|
|
23436
23528
|
return message.includes("(no_execution_target_registered)");
|
|
23437
23529
|
}
|
|
23530
|
+
async function runRunnerOnce({
|
|
23531
|
+
runtime,
|
|
23532
|
+
parsed,
|
|
23533
|
+
json
|
|
23534
|
+
}) {
|
|
23535
|
+
let claim;
|
|
23536
|
+
try {
|
|
23537
|
+
claim = await runtime.backend.post({
|
|
23538
|
+
path: "/api/runner/claim",
|
|
23539
|
+
body: {
|
|
23540
|
+
projectId: flagValue(parsed.flags, "--project-id"),
|
|
23541
|
+
...clientDeviceIdentity(),
|
|
23542
|
+
// Contract v40: the claim poll doubles as this runner instance's
|
|
23543
|
+
// heartbeat, so local target liveness reflects a runner that can
|
|
23544
|
+
// actually take work rather than any CLI traffic from the machine.
|
|
23545
|
+
...runnerRegistrationPayload()
|
|
23546
|
+
}
|
|
23547
|
+
});
|
|
23548
|
+
} catch (error) {
|
|
23549
|
+
if (isNoExecutionTargetRegisteredError(error)) {
|
|
23550
|
+
throw new CliError({
|
|
23551
|
+
message: `${error instanceof Error ? error.message : String(error)}
|
|
23552
|
+
The runner will not register a target for you \u2014 declare this machine first, then start it again.`
|
|
23553
|
+
});
|
|
23554
|
+
}
|
|
23555
|
+
throw error;
|
|
23556
|
+
}
|
|
23557
|
+
const request = asRecord5(claim).request;
|
|
23558
|
+
const longPoll = asRecord5(claim).longPoll === true;
|
|
23559
|
+
if (!request) return { launched: false, longPoll };
|
|
23560
|
+
const requestRecord = asRecord5(request);
|
|
23561
|
+
const requestId = String(requestRecord.id);
|
|
23562
|
+
const projectId = String(requestRecord.projectId ?? "");
|
|
23563
|
+
const executionTargetId = typeof requestRecord.executionTargetId === "string" ? requestRecord.executionTargetId.trim() : "";
|
|
23564
|
+
if (projectId && executionTargetId) {
|
|
23565
|
+
try {
|
|
23566
|
+
await reportRunnerResourceObservations({
|
|
23567
|
+
backend: runtime.backend,
|
|
23568
|
+
projectId,
|
|
23569
|
+
executionTargetId
|
|
23570
|
+
});
|
|
23571
|
+
} catch {
|
|
23572
|
+
}
|
|
23573
|
+
}
|
|
23574
|
+
try {
|
|
23575
|
+
await runtime.backend.post({ path: `/api/runner/requests/${requestId}/launching` });
|
|
23576
|
+
const mutation = parseMutationFromMetadata(requestRecord.metadata);
|
|
23577
|
+
if (mutation) {
|
|
23578
|
+
const mutationResult = await executeLocalTargetMutation({ mutation });
|
|
23579
|
+
if (!mutationResult.ok) {
|
|
23580
|
+
await runtime.backend.post({
|
|
23581
|
+
path: `/api/runner/requests/${requestId}/failed`,
|
|
23582
|
+
body: { error: mutationResult.message }
|
|
23583
|
+
});
|
|
23584
|
+
throw new CliError({ message: mutationResult.message });
|
|
23585
|
+
}
|
|
23586
|
+
await runtime.backend.post({
|
|
23587
|
+
path: `/api/runner/requests/${requestId}/completed`,
|
|
23588
|
+
body: { mutationResult }
|
|
23589
|
+
});
|
|
23590
|
+
if (json) printJson({ request, mutationResult });
|
|
23591
|
+
else console.log(`Completed ${mutation.kind} for ${requestRecord.missionId}`);
|
|
23592
|
+
return { launched: true, longPoll };
|
|
23593
|
+
}
|
|
23594
|
+
const requestedAgent = typeof requestRecord.requestedAgent === "string" ? requestRecord.requestedAgent.trim() : "";
|
|
23595
|
+
if (!requestedAgent) {
|
|
23596
|
+
throw new CliError({
|
|
23597
|
+
message: `Execution request ${requestId} has no agent; the objective must specify one before it can be launched.`
|
|
23598
|
+
});
|
|
23599
|
+
}
|
|
23600
|
+
const launchConfig = asRecord5(requestRecord.launchConfig);
|
|
23601
|
+
const terminal = await resolveTerminalLaunchSettings({ runtime, flags: parsed.flags });
|
|
23602
|
+
const missionId = String(requestRecord.missionId);
|
|
23603
|
+
const dryRun = flagBoolean(parsed.flags, "--dry-run");
|
|
23604
|
+
const prepared = await prepareMissionBranch({
|
|
23605
|
+
runtime,
|
|
23606
|
+
options: {
|
|
23607
|
+
missionId,
|
|
23608
|
+
workingDirectory: String(requestRecord.workingDirectory ?? process.cwd()),
|
|
23609
|
+
objectiveId: String(requestRecord.objectiveId ?? ""),
|
|
23610
|
+
workspaceAutomationEnabled: await readWorktreeBranchAutomationEnabled(runtime),
|
|
23611
|
+
dryRun,
|
|
23612
|
+
overrideBranch: flagValue(parsed.flags, "--branch"),
|
|
23613
|
+
noWorktree: flagBoolean(parsed.flags, "--no-worktree")
|
|
23614
|
+
}
|
|
23615
|
+
});
|
|
23616
|
+
await recordBranchPrepared({
|
|
23617
|
+
runtime,
|
|
23618
|
+
missionId,
|
|
23619
|
+
requestId,
|
|
23620
|
+
branchAutomation: prepared.branchAutomation
|
|
23621
|
+
});
|
|
23622
|
+
const launchSession = launchSessionSnapshotFromMetadata(asRecord5(requestRecord.metadata));
|
|
23623
|
+
const result = await launchAgent({
|
|
23624
|
+
runtime,
|
|
23625
|
+
options: {
|
|
23626
|
+
agent: requestedAgent,
|
|
23627
|
+
missionId,
|
|
23628
|
+
workingDirectory: prepared.workingDirectory,
|
|
23629
|
+
model: typeof requestRecord.requestedModel === "string" ? requestRecord.requestedModel : void 0,
|
|
23630
|
+
thinking: typeof requestRecord.requestedReasoningEffort === "string" ? requestRecord.requestedReasoningEffort : void 0,
|
|
23631
|
+
flags: normalizeAgentLaunchFlags(launchConfig.flags),
|
|
23632
|
+
preCommand: typeof launchConfig.preCommand === "string" ? launchConfig.preCommand : void 0,
|
|
23633
|
+
preLaunchCommands: Array.isArray(requestRecord.preLaunchCommands) ? requestRecord.preLaunchCommands.filter(
|
|
23634
|
+
(value) => typeof value === "string"
|
|
23635
|
+
) : void 0,
|
|
23636
|
+
launchEnvVars: parseLaunchEnvVarsValue(requestRecord.launchEnvVars),
|
|
23637
|
+
executionRequestId: requestId,
|
|
23638
|
+
executionTargetId: executionTargetId || void 0,
|
|
23639
|
+
launchSession,
|
|
23640
|
+
...terminal,
|
|
23641
|
+
dryRun
|
|
23642
|
+
}
|
|
23643
|
+
});
|
|
23644
|
+
if (result.providerFallbackWarning) {
|
|
23645
|
+
console.error(`[overlord] ${result.providerFallbackWarning}`);
|
|
23646
|
+
}
|
|
23647
|
+
if (result.viewerOpen && result.viewerOpen.ok === false) {
|
|
23648
|
+
console.error(`[overlord] ${result.viewerOpen.warning}`);
|
|
23649
|
+
}
|
|
23650
|
+
if (result.status && result.status !== 0) {
|
|
23651
|
+
throw new CliError({ message: `Launch command exited with status ${result.status}` });
|
|
23652
|
+
}
|
|
23653
|
+
await runtime.backend.post({
|
|
23654
|
+
path: `/api/runner/requests/${requestId}/launched`,
|
|
23655
|
+
body: result.providerSession ? { providerSession: result.providerSession } : {}
|
|
23656
|
+
});
|
|
23657
|
+
if (result.providerSession && !dryRun) {
|
|
23658
|
+
void postLatchHarnessEventsAfterLaunch({
|
|
23659
|
+
backend: runtime.backend,
|
|
23660
|
+
requestId,
|
|
23661
|
+
providerSessionId: result.providerSession.providerSessionId,
|
|
23662
|
+
executable: launchSession?.executionProvider.kind === "latch" ? launchSession.executionProvider.executable : void 0
|
|
23663
|
+
});
|
|
23664
|
+
}
|
|
23665
|
+
if (json || dryRun) {
|
|
23666
|
+
printJson({
|
|
23667
|
+
request,
|
|
23668
|
+
plan: result.plan,
|
|
23669
|
+
status: result.status,
|
|
23670
|
+
providerSession: result.providerSession ?? null,
|
|
23671
|
+
viewerOpen: result.viewerOpen ?? null,
|
|
23672
|
+
providerFallbackWarning: result.providerFallbackWarning ?? null
|
|
23673
|
+
});
|
|
23674
|
+
} else {
|
|
23675
|
+
console.log(`Launched ${requestedAgent} for ${requestRecord.missionId}`);
|
|
23676
|
+
}
|
|
23677
|
+
return { launched: true, longPoll };
|
|
23678
|
+
} catch (error) {
|
|
23679
|
+
try {
|
|
23680
|
+
await runtime.backend.post({
|
|
23681
|
+
path: `/api/runner/requests/${requestId}/failed`,
|
|
23682
|
+
body: { error: error instanceof Error ? error.message : String(error) }
|
|
23683
|
+
});
|
|
23684
|
+
} catch {
|
|
23685
|
+
}
|
|
23686
|
+
throw error;
|
|
23687
|
+
}
|
|
23688
|
+
}
|
|
23438
23689
|
async function runRunnerCommand({
|
|
23439
23690
|
runtime,
|
|
23440
23691
|
parsed,
|
|
@@ -23468,161 +23719,7 @@ async function runRunnerCommand({
|
|
|
23468
23719
|
message: "Usage: ovld runner once|start|supervise|status|clear <objective_id>|clear-all|service <install|start|stop|restart|status|uninstall>"
|
|
23469
23720
|
});
|
|
23470
23721
|
}
|
|
23471
|
-
const runOnce =
|
|
23472
|
-
let claim;
|
|
23473
|
-
try {
|
|
23474
|
-
claim = await runtime.backend.post({
|
|
23475
|
-
path: "/api/runner/claim",
|
|
23476
|
-
body: {
|
|
23477
|
-
projectId: flagValue(parsed.flags, "--project-id"),
|
|
23478
|
-
...clientDeviceIdentity(),
|
|
23479
|
-
// Contract v40: the claim poll doubles as this runner instance's
|
|
23480
|
-
// heartbeat, so local target liveness reflects a runner that can
|
|
23481
|
-
// actually take work rather than any CLI traffic from the machine.
|
|
23482
|
-
...runnerRegistrationPayload()
|
|
23483
|
-
}
|
|
23484
|
-
});
|
|
23485
|
-
} catch (error) {
|
|
23486
|
-
if (isNoExecutionTargetRegisteredError(error)) {
|
|
23487
|
-
throw new CliError({
|
|
23488
|
-
message: `${error instanceof Error ? error.message : String(error)}
|
|
23489
|
-
The runner will not register a target for you \u2014 declare this machine first, then start it again.`
|
|
23490
|
-
});
|
|
23491
|
-
}
|
|
23492
|
-
throw error;
|
|
23493
|
-
}
|
|
23494
|
-
const request = asRecord5(claim).request;
|
|
23495
|
-
const longPoll = asRecord5(claim).longPoll === true;
|
|
23496
|
-
if (!request) return { launched: false, longPoll };
|
|
23497
|
-
const requestRecord = asRecord5(request);
|
|
23498
|
-
const requestId = String(requestRecord.id);
|
|
23499
|
-
const projectId = String(requestRecord.projectId ?? "");
|
|
23500
|
-
const executionTargetId = typeof requestRecord.executionTargetId === "string" ? requestRecord.executionTargetId.trim() : "";
|
|
23501
|
-
if (projectId && executionTargetId) {
|
|
23502
|
-
try {
|
|
23503
|
-
await reportRunnerResourceObservations({
|
|
23504
|
-
backend: runtime.backend,
|
|
23505
|
-
projectId,
|
|
23506
|
-
executionTargetId
|
|
23507
|
-
});
|
|
23508
|
-
} catch {
|
|
23509
|
-
}
|
|
23510
|
-
}
|
|
23511
|
-
try {
|
|
23512
|
-
await runtime.backend.post({ path: `/api/runner/requests/${requestId}/launching` });
|
|
23513
|
-
const mutation = parseMutationFromMetadata(requestRecord.metadata);
|
|
23514
|
-
if (mutation) {
|
|
23515
|
-
const mutationResult = await executeLocalTargetMutation({ mutation });
|
|
23516
|
-
if (!mutationResult.ok) {
|
|
23517
|
-
await runtime.backend.post({
|
|
23518
|
-
path: `/api/runner/requests/${requestId}/failed`,
|
|
23519
|
-
body: { error: mutationResult.message }
|
|
23520
|
-
});
|
|
23521
|
-
throw new CliError({ message: mutationResult.message });
|
|
23522
|
-
}
|
|
23523
|
-
await runtime.backend.post({
|
|
23524
|
-
path: `/api/runner/requests/${requestId}/completed`,
|
|
23525
|
-
body: { mutationResult }
|
|
23526
|
-
});
|
|
23527
|
-
if (json) printJson({ request, mutationResult });
|
|
23528
|
-
else console.log(`Completed ${mutation.kind} for ${requestRecord.missionId}`);
|
|
23529
|
-
return { launched: true, longPoll };
|
|
23530
|
-
}
|
|
23531
|
-
const requestedAgent = typeof requestRecord.requestedAgent === "string" ? requestRecord.requestedAgent.trim() : "";
|
|
23532
|
-
if (!requestedAgent) {
|
|
23533
|
-
throw new CliError({
|
|
23534
|
-
message: `Execution request ${requestId} has no agent; the objective must specify one before it can be launched.`
|
|
23535
|
-
});
|
|
23536
|
-
}
|
|
23537
|
-
const launchConfig = asRecord5(requestRecord.launchConfig);
|
|
23538
|
-
const terminal = await resolveTerminalLaunchSettings({ runtime, flags: parsed.flags });
|
|
23539
|
-
const missionId = String(requestRecord.missionId);
|
|
23540
|
-
const dryRun = flagBoolean(parsed.flags, "--dry-run");
|
|
23541
|
-
const prepared = await prepareMissionBranch({
|
|
23542
|
-
runtime,
|
|
23543
|
-
options: {
|
|
23544
|
-
missionId,
|
|
23545
|
-
workingDirectory: String(requestRecord.workingDirectory ?? process.cwd()),
|
|
23546
|
-
objectiveId: String(requestRecord.objectiveId ?? ""),
|
|
23547
|
-
workspaceAutomationEnabled: await readWorktreeBranchAutomationEnabled(runtime),
|
|
23548
|
-
dryRun,
|
|
23549
|
-
overrideBranch: flagValue(parsed.flags, "--branch"),
|
|
23550
|
-
noWorktree: flagBoolean(parsed.flags, "--no-worktree")
|
|
23551
|
-
}
|
|
23552
|
-
});
|
|
23553
|
-
await recordBranchPrepared({
|
|
23554
|
-
runtime,
|
|
23555
|
-
missionId,
|
|
23556
|
-
requestId,
|
|
23557
|
-
branchAutomation: prepared.branchAutomation
|
|
23558
|
-
});
|
|
23559
|
-
const launchSession = launchSessionSnapshotFromMetadata(asRecord5(requestRecord.metadata));
|
|
23560
|
-
const result = await launchAgent({
|
|
23561
|
-
runtime,
|
|
23562
|
-
options: {
|
|
23563
|
-
agent: requestedAgent,
|
|
23564
|
-
missionId,
|
|
23565
|
-
workingDirectory: prepared.workingDirectory,
|
|
23566
|
-
model: typeof requestRecord.requestedModel === "string" ? requestRecord.requestedModel : void 0,
|
|
23567
|
-
thinking: typeof requestRecord.requestedReasoningEffort === "string" ? requestRecord.requestedReasoningEffort : void 0,
|
|
23568
|
-
flags: normalizeAgentLaunchFlags(launchConfig.flags),
|
|
23569
|
-
preCommand: typeof launchConfig.preCommand === "string" ? launchConfig.preCommand : void 0,
|
|
23570
|
-
preLaunchCommands: Array.isArray(requestRecord.preLaunchCommands) ? requestRecord.preLaunchCommands.filter(
|
|
23571
|
-
(value) => typeof value === "string"
|
|
23572
|
-
) : void 0,
|
|
23573
|
-
launchEnvVars: parseLaunchEnvVarsValue(requestRecord.launchEnvVars),
|
|
23574
|
-
executionRequestId: requestId,
|
|
23575
|
-
executionTargetId: executionTargetId || void 0,
|
|
23576
|
-
launchSession,
|
|
23577
|
-
...terminal,
|
|
23578
|
-
dryRun
|
|
23579
|
-
}
|
|
23580
|
-
});
|
|
23581
|
-
if (result.providerFallbackWarning) {
|
|
23582
|
-
console.error(`[overlord] ${result.providerFallbackWarning}`);
|
|
23583
|
-
}
|
|
23584
|
-
if (result.viewerOpen && result.viewerOpen.ok === false) {
|
|
23585
|
-
console.error(`[overlord] ${result.viewerOpen.warning}`);
|
|
23586
|
-
}
|
|
23587
|
-
if (result.status && result.status !== 0) {
|
|
23588
|
-
throw new CliError({ message: `Launch command exited with status ${result.status}` });
|
|
23589
|
-
}
|
|
23590
|
-
await runtime.backend.post({
|
|
23591
|
-
path: `/api/runner/requests/${requestId}/launched`,
|
|
23592
|
-
body: result.providerSession ? { providerSession: result.providerSession } : {}
|
|
23593
|
-
});
|
|
23594
|
-
if (result.providerSession && !dryRun) {
|
|
23595
|
-
void postLatchHarnessEventsAfterLaunch({
|
|
23596
|
-
backend: runtime.backend,
|
|
23597
|
-
requestId,
|
|
23598
|
-
providerSessionId: result.providerSession.providerSessionId,
|
|
23599
|
-
executable: launchSession?.executionProvider.kind === "latch" ? launchSession.executionProvider.executable : void 0
|
|
23600
|
-
});
|
|
23601
|
-
}
|
|
23602
|
-
if (json || dryRun) {
|
|
23603
|
-
printJson({
|
|
23604
|
-
request,
|
|
23605
|
-
plan: result.plan,
|
|
23606
|
-
status: result.status,
|
|
23607
|
-
providerSession: result.providerSession ?? null,
|
|
23608
|
-
viewerOpen: result.viewerOpen ?? null,
|
|
23609
|
-
providerFallbackWarning: result.providerFallbackWarning ?? null
|
|
23610
|
-
});
|
|
23611
|
-
} else {
|
|
23612
|
-
console.log(`Launched ${requestedAgent} for ${requestRecord.missionId}`);
|
|
23613
|
-
}
|
|
23614
|
-
return { launched: true, longPoll };
|
|
23615
|
-
} catch (error) {
|
|
23616
|
-
try {
|
|
23617
|
-
await runtime.backend.post({
|
|
23618
|
-
path: `/api/runner/requests/${requestId}/failed`,
|
|
23619
|
-
body: { error: error instanceof Error ? error.message : String(error) }
|
|
23620
|
-
});
|
|
23621
|
-
} catch {
|
|
23622
|
-
}
|
|
23623
|
-
throw error;
|
|
23624
|
-
}
|
|
23625
|
-
};
|
|
23722
|
+
const runOnce = () => runRunnerOnce({ runtime, parsed, json });
|
|
23626
23723
|
if (sub === "once") {
|
|
23627
23724
|
const result = await runOnce();
|
|
23628
23725
|
if (!result.launched) {
|
|
@@ -23646,8 +23743,18 @@ async function runRunnerSupervisor({
|
|
|
23646
23743
|
runOnce,
|
|
23647
23744
|
json
|
|
23648
23745
|
}) {
|
|
23649
|
-
if (!json) console.log("Runner supervisor started.
|
|
23746
|
+
if (!json) console.log("Runner supervisor started. Polling for execution requests.");
|
|
23747
|
+
const initialExecutableIdentity = captureRunnerSupervisorIdentity();
|
|
23650
23748
|
for (; ; ) {
|
|
23749
|
+
if (shouldRestartRunnerSupervisor({
|
|
23750
|
+
initial: initialExecutableIdentity,
|
|
23751
|
+
current: captureRunnerSupervisorIdentity()
|
|
23752
|
+
})) {
|
|
23753
|
+
console.error(
|
|
23754
|
+
"Runner supervisor executable was replaced or removed; exiting cleanly so the service manager restarts the installed build."
|
|
23755
|
+
);
|
|
23756
|
+
process.exit(0);
|
|
23757
|
+
}
|
|
23651
23758
|
const now = /* @__PURE__ */ new Date();
|
|
23652
23759
|
let result = { launched: false, longPoll: false };
|
|
23653
23760
|
let lastError = null;
|
|
@@ -23656,25 +23763,22 @@ async function runRunnerSupervisor({
|
|
|
23656
23763
|
} catch (error) {
|
|
23657
23764
|
if (isNoExecutionTargetRegisteredError(error)) throw error;
|
|
23658
23765
|
lastError = error instanceof Error ? error.message : String(error);
|
|
23766
|
+
console.error(`[overlord] Runner poll failed: ${lastError}`);
|
|
23659
23767
|
}
|
|
23660
23768
|
const state = readRunnerServiceState();
|
|
23661
|
-
const focus = readDesktopFocusState();
|
|
23662
|
-
const base = selectBasePollIntervalMs({
|
|
23663
|
-
lastLaunchedAt: result.launched ? now.toISOString() : state.lastLaunchedAt,
|
|
23664
|
-
lastDesktopFocusAt: focus.lastFocusedAt,
|
|
23665
|
-
now: now.getTime()
|
|
23666
|
-
});
|
|
23667
23769
|
patchRunnerServiceState({
|
|
23668
23770
|
lastHeartbeatAt: now.toISOString(),
|
|
23669
23771
|
lastClaimedAt: result.launched ? now.toISOString() : state.lastClaimedAt,
|
|
23670
23772
|
lastLaunchedAt: result.launched ? now.toISOString() : state.lastLaunchedAt,
|
|
23671
|
-
// Reflect the latest poll's outcome
|
|
23672
|
-
//
|
|
23673
|
-
|
|
23674
|
-
|
|
23773
|
+
// Reflect the latest poll's outcome rather than falling back to the stored
|
|
23774
|
+
// value. A successful poll must clear a resolved failure (for example, an
|
|
23775
|
+
// auth error from before login) instead of leaving the desktop status box
|
|
23776
|
+
// stuck on "Runner error"; persistent failures recur on every poll.
|
|
23777
|
+
lastError,
|
|
23778
|
+
currentPollIntervalMs: FALLBACK_POLL_INTERVAL_MS
|
|
23675
23779
|
});
|
|
23676
23780
|
if (!result.longPoll) {
|
|
23677
|
-
await new Promise((resolve) => setTimeout(resolve, applyPollJitter(
|
|
23781
|
+
await new Promise((resolve) => setTimeout(resolve, applyPollJitter(FALLBACK_POLL_INTERVAL_MS)));
|
|
23678
23782
|
}
|
|
23679
23783
|
}
|
|
23680
23784
|
}
|
|
@@ -23692,10 +23796,8 @@ async function runRunnerServiceCommand({
|
|
|
23692
23796
|
if (action === "status") {
|
|
23693
23797
|
const runState = await manager.status();
|
|
23694
23798
|
const state = readRunnerServiceState();
|
|
23695
|
-
const
|
|
23696
|
-
|
|
23697
|
-
});
|
|
23698
|
-
const reinstallHint = runState.installed && needsReinstallForOverlord ? 'This service is registered under "Node.js Foundation" because it runs the plain node binary. Reinstall it from the Overlord desktop app (or with the desktop app present) to re-register it under "Overlord".' : null;
|
|
23799
|
+
const publisher = process.platform !== "darwin" || !state.execProgram ? "unknown" : state.execProgram.includes(".app/Contents/MacOS/") ? "overlord" : "node";
|
|
23800
|
+
const reinstallHint = runState.installed && publisher === "node" ? 'This service is registered under "Node.js Foundation" because it runs the plain node binary. Reinstall it from the Overlord desktop app (or with the desktop app present) to re-register it under "Overlord".' : null;
|
|
23699
23801
|
const payload = {
|
|
23700
23802
|
supported: true,
|
|
23701
23803
|
kind: manager.kind,
|