overlord-cli 0.2607122001.0 → 0.2607141312.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/README.md +18 -10
- package/dist/contract/conformance-manifest.schema.yaml +328 -0
- package/dist/index.js +640 -235
- package/dist/index.js.map +4 -4
- package/package.json +5 -1
package/dist/index.js
CHANGED
|
@@ -53,46 +53,6 @@ var init_errors = __esm({
|
|
|
53
53
|
}
|
|
54
54
|
});
|
|
55
55
|
|
|
56
|
-
// src/version.ts
|
|
57
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
58
|
-
import path from "node:path";
|
|
59
|
-
import { fileURLToPath } from "node:url";
|
|
60
|
-
function resolvePackageJsonPath() {
|
|
61
|
-
let dir = path.dirname(fileURLToPath(import.meta.url));
|
|
62
|
-
while (true) {
|
|
63
|
-
const candidate = path.join(dir, "package.json");
|
|
64
|
-
if (existsSync(candidate)) {
|
|
65
|
-
const packageJson = JSON.parse(readFileSync(candidate, "utf8"));
|
|
66
|
-
if (packageJson.name === "overlord-cli") {
|
|
67
|
-
return candidate;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
const parent = path.dirname(dir);
|
|
71
|
-
if (parent === dir) {
|
|
72
|
-
break;
|
|
73
|
-
}
|
|
74
|
-
dir = parent;
|
|
75
|
-
}
|
|
76
|
-
throw new Error("Cannot locate overlord-cli package.json");
|
|
77
|
-
}
|
|
78
|
-
function getCliVersion() {
|
|
79
|
-
const packageJson = JSON.parse(readFileSync(resolvePackageJsonPath(), "utf8"));
|
|
80
|
-
return packageJson.version;
|
|
81
|
-
}
|
|
82
|
-
function runVersionCommand({ json = false } = {}) {
|
|
83
|
-
const version = getCliVersion();
|
|
84
|
-
if (json) {
|
|
85
|
-
console.log(JSON.stringify({ version, node: process.version }));
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
console.log(`Overlord CLI ${version}`);
|
|
89
|
-
}
|
|
90
|
-
var init_version = __esm({
|
|
91
|
-
"src/version.ts"() {
|
|
92
|
-
"use strict";
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
|
|
96
56
|
// src/args.ts
|
|
97
57
|
function parseArgs(argv) {
|
|
98
58
|
const positional = [];
|
|
@@ -164,6 +124,46 @@ var init_args = __esm({
|
|
|
164
124
|
}
|
|
165
125
|
});
|
|
166
126
|
|
|
127
|
+
// src/version.ts
|
|
128
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
129
|
+
import path from "node:path";
|
|
130
|
+
import { fileURLToPath } from "node:url";
|
|
131
|
+
function resolvePackageJsonPath() {
|
|
132
|
+
let dir = path.dirname(fileURLToPath(import.meta.url));
|
|
133
|
+
while (true) {
|
|
134
|
+
const candidate = path.join(dir, "package.json");
|
|
135
|
+
if (existsSync(candidate)) {
|
|
136
|
+
const packageJson = JSON.parse(readFileSync(candidate, "utf8"));
|
|
137
|
+
if (packageJson.name === "overlord-cli") {
|
|
138
|
+
return candidate;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const parent = path.dirname(dir);
|
|
142
|
+
if (parent === dir) {
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
dir = parent;
|
|
146
|
+
}
|
|
147
|
+
throw new Error("Cannot locate overlord-cli package.json");
|
|
148
|
+
}
|
|
149
|
+
function getCliVersion() {
|
|
150
|
+
const packageJson = JSON.parse(readFileSync(resolvePackageJsonPath(), "utf8"));
|
|
151
|
+
return packageJson.version;
|
|
152
|
+
}
|
|
153
|
+
function runVersionCommand({ json = false } = {}) {
|
|
154
|
+
const version = getCliVersion();
|
|
155
|
+
if (json) {
|
|
156
|
+
console.log(JSON.stringify({ version, node: process.version }));
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
console.log(`Overlord CLI ${version}`);
|
|
160
|
+
}
|
|
161
|
+
var init_version = __esm({
|
|
162
|
+
"src/version.ts"() {
|
|
163
|
+
"use strict";
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
167
|
// src/output.ts
|
|
168
168
|
function printJson(value) {
|
|
169
169
|
process.stdout.write(`${JSON.stringify(value, null, 2)}
|
|
@@ -1540,6 +1540,9 @@ function readStoredAuthCredentials() {
|
|
|
1540
1540
|
if (typeof raw.token !== "string" || !raw.token.trim()) return null;
|
|
1541
1541
|
if (raw.type !== "session_bearer" && raw.type !== "user_token") return null;
|
|
1542
1542
|
if (typeof raw.backendUrl !== "string" || !raw.backendUrl.trim()) return null;
|
|
1543
|
+
if (raw.expiresAt !== void 0 && raw.expiresAt !== null && typeof raw.expiresAt !== "string") {
|
|
1544
|
+
raw.expiresAt = null;
|
|
1545
|
+
}
|
|
1543
1546
|
return raw;
|
|
1544
1547
|
} catch {
|
|
1545
1548
|
return null;
|
|
@@ -1548,7 +1551,8 @@ function readStoredAuthCredentials() {
|
|
|
1548
1551
|
function writeStoredAuthCredentials({
|
|
1549
1552
|
type,
|
|
1550
1553
|
token,
|
|
1551
|
-
backendUrl
|
|
1554
|
+
backendUrl,
|
|
1555
|
+
expiresAt = null
|
|
1552
1556
|
}) {
|
|
1553
1557
|
const filePath = authCredentialsPath();
|
|
1554
1558
|
mkdirSync2(path4.dirname(filePath), { recursive: true });
|
|
@@ -1556,7 +1560,8 @@ function writeStoredAuthCredentials({
|
|
|
1556
1560
|
type,
|
|
1557
1561
|
token,
|
|
1558
1562
|
backendUrl: backendUrl.replace(/\/+$/, ""),
|
|
1559
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1563
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1564
|
+
expiresAt: expiresAt ?? null
|
|
1560
1565
|
};
|
|
1561
1566
|
writeFileSync2(filePath, `${JSON.stringify(payload, null, 2)}
|
|
1562
1567
|
`, { mode: 384 });
|
|
@@ -1816,7 +1821,12 @@ async function createFullUserTokenFromSession({
|
|
|
1816
1821
|
const payload = await readResponseJson(response);
|
|
1817
1822
|
if (response.ok && payload && typeof payload === "object") {
|
|
1818
1823
|
const secret = payload.secret;
|
|
1819
|
-
if (typeof secret === "string" && secret.startsWith("out_"))
|
|
1824
|
+
if (typeof secret === "string" && secret.startsWith("out_")) {
|
|
1825
|
+
const tokenDto = payload.token;
|
|
1826
|
+
const rawExpiry = tokenDto && typeof tokenDto === "object" ? tokenDto.expiresAt : void 0;
|
|
1827
|
+
const expiresAt = typeof rawExpiry === "string" && rawExpiry.trim() ? rawExpiry : null;
|
|
1828
|
+
return { secret, expiresAt };
|
|
1829
|
+
}
|
|
1820
1830
|
}
|
|
1821
1831
|
throw new CliError({
|
|
1822
1832
|
message: errorMessageFromJson(payload) ?? `Could not create CLI USER_TOKEN (${response.status}). Check your account permissions.`
|
|
@@ -1882,15 +1892,16 @@ async function runInteractiveAuthLogin({
|
|
|
1882
1892
|
});
|
|
1883
1893
|
await validateBearerToken({ backendUrl: normalizedBackendUrl, token: sessionToken });
|
|
1884
1894
|
if (passwordCredentialTarget === "full_user_token") {
|
|
1885
|
-
const
|
|
1895
|
+
const minted = await createFullUserTokenFromSession({
|
|
1886
1896
|
backendUrl: normalizedBackendUrl,
|
|
1887
1897
|
sessionToken
|
|
1888
1898
|
});
|
|
1889
|
-
await validateBearerToken({ backendUrl: normalizedBackendUrl, token:
|
|
1899
|
+
await validateBearerToken({ backendUrl: normalizedBackendUrl, token: minted.secret });
|
|
1890
1900
|
writeStoredAuthCredentials({
|
|
1891
1901
|
type: "user_token",
|
|
1892
|
-
token:
|
|
1893
|
-
backendUrl: normalizedBackendUrl
|
|
1902
|
+
token: minted.secret,
|
|
1903
|
+
backendUrl: normalizedBackendUrl,
|
|
1904
|
+
expiresAt: minted.expiresAt
|
|
1894
1905
|
});
|
|
1895
1906
|
return {
|
|
1896
1907
|
ok: true,
|
|
@@ -1927,6 +1938,21 @@ __export(auth_status_exports, {
|
|
|
1927
1938
|
resolveAuthCredentialSource: () => resolveAuthCredentialSource,
|
|
1928
1939
|
resolveAuthStatus: () => resolveAuthStatus
|
|
1929
1940
|
});
|
|
1941
|
+
function resolveExpiry(expiresAt) {
|
|
1942
|
+
if (typeof expiresAt !== "string" || !expiresAt.trim()) {
|
|
1943
|
+
return { expiresAt: null, expiresInDays: null, expired: false };
|
|
1944
|
+
}
|
|
1945
|
+
const parsed = new Date(expiresAt);
|
|
1946
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
1947
|
+
return { expiresAt: null, expiresInDays: null, expired: false };
|
|
1948
|
+
}
|
|
1949
|
+
const msRemaining = parsed.getTime() - Date.now();
|
|
1950
|
+
return {
|
|
1951
|
+
expiresAt: parsed.toISOString(),
|
|
1952
|
+
expiresInDays: Math.floor(msRemaining / (24 * 60 * 60 * 1e3)),
|
|
1953
|
+
expired: msRemaining <= 0
|
|
1954
|
+
};
|
|
1955
|
+
}
|
|
1930
1956
|
function normalizeBaseUrl3(value) {
|
|
1931
1957
|
return value.replace(/\/+$/, "");
|
|
1932
1958
|
}
|
|
@@ -1972,6 +1998,9 @@ async function resolveAuthStatus({
|
|
|
1972
1998
|
const configPath = findEffectiveConfigPath();
|
|
1973
1999
|
const credential = resolveAuthCredentialSource({ backendUrl, env });
|
|
1974
2000
|
const token = resolveAuthBearerToken({ backendUrl, env });
|
|
2001
|
+
const expiry = resolveExpiry(
|
|
2002
|
+
credential.source === "stored" || credential.source === "stored_mismatch" ? readStoredAuthCredentials()?.expiresAt : null
|
|
2003
|
+
);
|
|
1975
2004
|
if (!token) {
|
|
1976
2005
|
return {
|
|
1977
2006
|
backendUrl,
|
|
@@ -1981,7 +2010,8 @@ async function resolveAuthStatus({
|
|
|
1981
2010
|
credentialSource: credential.source,
|
|
1982
2011
|
credentialType: credential.type,
|
|
1983
2012
|
credentialsPath: credential.credentialsPath,
|
|
1984
|
-
validationError: credential.source === "stored_mismatch" ? `Stored credentials are for ${readStoredAuthCredentials()?.backendUrl ?? "another backend"}.` : null
|
|
2013
|
+
validationError: credential.source === "stored_mismatch" ? `Stored credentials are for ${readStoredAuthCredentials()?.backendUrl ?? "another backend"}.` : null,
|
|
2014
|
+
...expiry
|
|
1985
2015
|
};
|
|
1986
2016
|
}
|
|
1987
2017
|
try {
|
|
@@ -1994,7 +2024,8 @@ async function resolveAuthStatus({
|
|
|
1994
2024
|
credentialSource: credential.source,
|
|
1995
2025
|
credentialType: credential.type,
|
|
1996
2026
|
credentialsPath: credential.credentialsPath,
|
|
1997
|
-
validationError: null
|
|
2027
|
+
validationError: null,
|
|
2028
|
+
...expiry
|
|
1998
2029
|
};
|
|
1999
2030
|
} catch (error) {
|
|
2000
2031
|
return {
|
|
@@ -2005,7 +2036,8 @@ async function resolveAuthStatus({
|
|
|
2005
2036
|
credentialSource: credential.source,
|
|
2006
2037
|
credentialType: credential.type,
|
|
2007
2038
|
credentialsPath: credential.credentialsPath,
|
|
2008
|
-
validationError: error instanceof Error ? error.message : String(error)
|
|
2039
|
+
validationError: error instanceof Error ? error.message : String(error),
|
|
2040
|
+
...expiry
|
|
2009
2041
|
};
|
|
2010
2042
|
}
|
|
2011
2043
|
}
|
|
@@ -2925,12 +2957,12 @@ function createBackendClient() {
|
|
|
2925
2957
|
const clientDevice = clientDeviceIdentity();
|
|
2926
2958
|
async function request({
|
|
2927
2959
|
method,
|
|
2928
|
-
path:
|
|
2960
|
+
path: path27,
|
|
2929
2961
|
body,
|
|
2930
2962
|
extraHeaders,
|
|
2931
2963
|
rawBody
|
|
2932
2964
|
}) {
|
|
2933
|
-
const url = `${baseUrl}${
|
|
2965
|
+
const url = `${baseUrl}${path27.startsWith("/") ? path27 : `/${path27}`}`;
|
|
2934
2966
|
const auth = resolveAuthHeaders({ baseUrl });
|
|
2935
2967
|
let response;
|
|
2936
2968
|
try {
|
|
@@ -2957,15 +2989,14 @@ Start the Desktop/local backend or run \`ovld config set cloud <url>\`.
|
|
|
2957
2989
|
const payload = await readResponseJson2(response);
|
|
2958
2990
|
if (!response.ok) {
|
|
2959
2991
|
if (response.status === 401 && auth.headers.Authorization) {
|
|
2960
|
-
|
|
2961
|
-
const detail = errorMessageFromJson2(payload) ?? `Backend request failed: ${method} ${path26} (${response.status})`;
|
|
2992
|
+
const detail = errorMessageFromJson2(payload) ?? `Backend request failed: ${method} ${path27} (${response.status})`;
|
|
2962
2993
|
throw new CliError({
|
|
2963
2994
|
message: `${detail}
|
|
2964
|
-
` + (auth.fromStored ? "
|
|
2995
|
+
` + (auth.fromStored ? "Your saved credentials were rejected. Run `ovld auth login` to sign in again." : "Run `ovld auth login` or refresh your USER_TOKEN environment variable.")
|
|
2965
2996
|
});
|
|
2966
2997
|
}
|
|
2967
2998
|
throw new CliError({
|
|
2968
|
-
message: errorMessageFromJson2(payload) ?? `Backend request failed: ${method} ${
|
|
2999
|
+
message: errorMessageFromJson2(payload) ?? `Backend request failed: ${method} ${path27} (${response.status})`
|
|
2969
3000
|
});
|
|
2970
3001
|
}
|
|
2971
3002
|
return payload;
|
|
@@ -2973,13 +3004,13 @@ Start the Desktop/local backend or run \`ovld config set cloud <url>\`.
|
|
|
2973
3004
|
return {
|
|
2974
3005
|
baseUrl,
|
|
2975
3006
|
health: () => request({ method: "GET", path: "/api/health" }),
|
|
2976
|
-
get: (
|
|
2977
|
-
post: ({ path:
|
|
2978
|
-
patch: ({ path:
|
|
2979
|
-
delete: (
|
|
2980
|
-
postRaw: ({ path:
|
|
3007
|
+
get: (path27) => request({ method: "GET", path: path27 }),
|
|
3008
|
+
post: ({ path: path27, body }) => request({ method: "POST", path: path27, body }),
|
|
3009
|
+
patch: ({ path: path27, body }) => request({ method: "PATCH", path: path27, body }),
|
|
3010
|
+
delete: (path27) => request({ method: "DELETE", path: path27 }),
|
|
3011
|
+
postRaw: ({ path: path27, body, contentType, filename }) => request({
|
|
2981
3012
|
method: "POST",
|
|
2982
|
-
path:
|
|
3013
|
+
path: path27,
|
|
2983
3014
|
rawBody: body,
|
|
2984
3015
|
extraHeaders: {
|
|
2985
3016
|
"Content-Type": contentType || "application/octet-stream",
|
|
@@ -3509,7 +3540,10 @@ async function runFullSetupCommand({ json }) {
|
|
|
3509
3540
|
const backend = await configureBackendForSetup();
|
|
3510
3541
|
const auth = await configureAuthForSetup({
|
|
3511
3542
|
backendUrl: backend.url,
|
|
3512
|
-
|
|
3543
|
+
// Mint a long-lived (90-day) user token in both modes; see the note in
|
|
3544
|
+
// management.ts. Avoids the ~7-day session_bearer lifetime local logins used
|
|
3545
|
+
// to get.
|
|
3546
|
+
passwordCredentialTarget: "full_user_token"
|
|
3513
3547
|
});
|
|
3514
3548
|
const agents = await configureAgentsForSetup();
|
|
3515
3549
|
const runtime = openCliRuntime();
|
|
@@ -4121,8 +4155,47 @@ async function runAuthStatusCommand({ json }) {
|
|
|
4121
4155
|
console.log(`credential_source=${status.credentialSource}`);
|
|
4122
4156
|
if (status.credentialType) console.log(`credential_type=${status.credentialType}`);
|
|
4123
4157
|
if (status.credentialsPath) console.log(`credentials_path=${status.credentialsPath}`);
|
|
4158
|
+
if (status.expiresAt) {
|
|
4159
|
+
const remaining = status.expired ? "expired" : status.expiresInDays !== null ? `${status.expiresInDays} day(s) remaining` : "unknown";
|
|
4160
|
+
console.log(`expires_at=${status.expiresAt} (${remaining})`);
|
|
4161
|
+
}
|
|
4124
4162
|
if (status.validationError) console.log(`validation_error=${status.validationError}`);
|
|
4125
4163
|
}
|
|
4164
|
+
async function runAuthRepairCommand({
|
|
4165
|
+
rest,
|
|
4166
|
+
json
|
|
4167
|
+
}) {
|
|
4168
|
+
const { resolveAuthStatus: resolveAuthStatus2 } = await Promise.resolve().then(() => (init_auth_status(), auth_status_exports));
|
|
4169
|
+
const status = await resolveAuthStatus2();
|
|
4170
|
+
if (status.loggedIn) {
|
|
4171
|
+
if (json) {
|
|
4172
|
+
printJson({ ok: true, action: "none", reason: "healthy", status });
|
|
4173
|
+
} else {
|
|
4174
|
+
console.log("Authentication is healthy; no repair needed.");
|
|
4175
|
+
if (status.expiresAt && !status.expired && status.expiresInDays !== null) {
|
|
4176
|
+
console.log(
|
|
4177
|
+
`Credential expires ${status.expiresAt} (${status.expiresInDays} day(s) remaining).`
|
|
4178
|
+
);
|
|
4179
|
+
}
|
|
4180
|
+
}
|
|
4181
|
+
return;
|
|
4182
|
+
}
|
|
4183
|
+
if (status.credentialSource === "environment") {
|
|
4184
|
+
throw new CliError({
|
|
4185
|
+
message: `Your credential comes from an environment variable (OVERLORD_USER_TOKEN / OVLD_USER_TOKEN / USER_TOKEN) and cannot be repaired from disk.
|
|
4186
|
+
Refresh that variable with a valid token${status.validationError ? ` (backend said: ${status.validationError})` : ""}.`
|
|
4187
|
+
});
|
|
4188
|
+
}
|
|
4189
|
+
const { clearStoredAuthCredentials: clearStoredAuthCredentials2, readStoredAuthCredentials: readStoredAuthCredentials2 } = await Promise.resolve().then(() => (init_auth_credentials(), auth_credentials_exports));
|
|
4190
|
+
const hadStored = readStoredAuthCredentials2() !== null;
|
|
4191
|
+
if (hadStored) {
|
|
4192
|
+
clearStoredAuthCredentials2();
|
|
4193
|
+
if (!json) console.log("Cleared stale stored credentials; re-authenticating...");
|
|
4194
|
+
} else if (!json) {
|
|
4195
|
+
console.log("No stored credentials found; starting a fresh login...");
|
|
4196
|
+
}
|
|
4197
|
+
await runAuthLoginFlow({ parsed: parseArgs(rest.filter((arg) => arg !== "repair")), json });
|
|
4198
|
+
}
|
|
4126
4199
|
async function runAuthCommand({ rest, json }) {
|
|
4127
4200
|
const parsed = parseArgs(rest);
|
|
4128
4201
|
const sub = parsed.positional[0] ?? "login";
|
|
@@ -4130,11 +4203,21 @@ async function runAuthCommand({ rest, json }) {
|
|
|
4130
4203
|
await runAuthStatusCommand({ json });
|
|
4131
4204
|
return;
|
|
4132
4205
|
}
|
|
4206
|
+
if (sub === "repair") {
|
|
4207
|
+
await runAuthRepairCommand({ rest, json });
|
|
4208
|
+
return;
|
|
4209
|
+
}
|
|
4133
4210
|
if (sub !== "login") {
|
|
4134
4211
|
throw new CliError({
|
|
4135
|
-
message: "Usage: ovld auth login [--token <out_...>] | ovld auth status [--json]"
|
|
4212
|
+
message: "Usage: ovld auth login [--token <out_...>] | ovld auth status [--json] | ovld auth repair [--json]"
|
|
4136
4213
|
});
|
|
4137
4214
|
}
|
|
4215
|
+
await runAuthLoginFlow({ parsed, json });
|
|
4216
|
+
}
|
|
4217
|
+
async function runAuthLoginFlow({
|
|
4218
|
+
parsed,
|
|
4219
|
+
json
|
|
4220
|
+
}) {
|
|
4138
4221
|
const tokenFlag = flagValue(parsed.flags, "--token");
|
|
4139
4222
|
const { findEffectiveConfigPath: findEffectiveConfigPath2, hasExplicitBackendConfig: hasExplicitBackendConfig2, loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
4140
4223
|
let config = loadConfig2();
|
|
@@ -4155,7 +4238,12 @@ async function runAuthCommand({ rest, json }) {
|
|
|
4155
4238
|
const { loginWithUserToken: loginWithUserToken2, runInteractiveAuthLogin: runInteractiveAuthLogin2 } = await Promise.resolve().then(() => (init_auth_login(), auth_login_exports));
|
|
4156
4239
|
const login = tokenFlag ? await loginWithUserToken2({ backendUrl, token: tokenFlag }) : await runInteractiveAuthLogin2({
|
|
4157
4240
|
backendUrl,
|
|
4158
|
-
|
|
4241
|
+
// Always exchange the password sign-in for a long-lived (90-day) user
|
|
4242
|
+
// token, regardless of backend mode. Storing the raw Better Auth session
|
|
4243
|
+
// token (session_bearer) gave local-mode logins a ~7-day lifetime while
|
|
4244
|
+
// cloud logins got 90 days; minting a full_user_token in both modes keeps
|
|
4245
|
+
// the lifetime consistent and avoids surprise "token expired" churn.
|
|
4246
|
+
passwordCredentialTarget: "full_user_token"
|
|
4159
4247
|
});
|
|
4160
4248
|
if (json) {
|
|
4161
4249
|
printJson({
|
|
@@ -4445,6 +4533,164 @@ var init_serve = __esm({
|
|
|
4445
4533
|
}
|
|
4446
4534
|
});
|
|
4447
4535
|
|
|
4536
|
+
// src/contract.ts
|
|
4537
|
+
var contract_exports = {};
|
|
4538
|
+
__export(contract_exports, {
|
|
4539
|
+
runContractCommand: () => runContractCommand,
|
|
4540
|
+
validateAgainstSchema: () => validateAgainstSchema,
|
|
4541
|
+
validateManifest: () => validateManifest
|
|
4542
|
+
});
|
|
4543
|
+
import { existsSync as existsSync10, readFileSync as readFileSync8 } from "node:fs";
|
|
4544
|
+
import path10 from "node:path";
|
|
4545
|
+
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
4546
|
+
import { parse as parseYaml } from "yaml";
|
|
4547
|
+
function schemaPath() {
|
|
4548
|
+
const packaged = path10.join(
|
|
4549
|
+
path10.dirname(fileURLToPath5(import.meta.url)),
|
|
4550
|
+
"contract",
|
|
4551
|
+
"conformance-manifest.schema.yaml"
|
|
4552
|
+
);
|
|
4553
|
+
if (existsSync10(packaged)) return packaged;
|
|
4554
|
+
let dir = process.cwd();
|
|
4555
|
+
while (true) {
|
|
4556
|
+
const candidate = path10.join(dir, "contract", "conformance-manifest.schema.yaml");
|
|
4557
|
+
if (existsSync10(candidate)) return candidate;
|
|
4558
|
+
const parent = path10.dirname(dir);
|
|
4559
|
+
if (parent === dir) break;
|
|
4560
|
+
dir = parent;
|
|
4561
|
+
}
|
|
4562
|
+
return resolveRepoPath("contract/conformance-manifest.schema.yaml");
|
|
4563
|
+
}
|
|
4564
|
+
function loadSchema() {
|
|
4565
|
+
if (cachedSchema) return cachedSchema;
|
|
4566
|
+
const file = schemaPath();
|
|
4567
|
+
if (!existsSync10(file)) {
|
|
4568
|
+
throw new CliError({
|
|
4569
|
+
message: `Could not find conformance-manifest.schema.yaml (looked at ${file}).`
|
|
4570
|
+
});
|
|
4571
|
+
}
|
|
4572
|
+
cachedSchema = parseYaml(readFileSync8(file, "utf8"));
|
|
4573
|
+
return cachedSchema;
|
|
4574
|
+
}
|
|
4575
|
+
function validateAgainstSchema(value, schema, location = "$") {
|
|
4576
|
+
const errors = [];
|
|
4577
|
+
if (schema.type === "object") {
|
|
4578
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
4579
|
+
return [`${location}: must be an object`];
|
|
4580
|
+
}
|
|
4581
|
+
const obj = value;
|
|
4582
|
+
for (const key of schema.required ?? []) {
|
|
4583
|
+
if (!(key in obj) || obj[key] === void 0 || obj[key] === "") {
|
|
4584
|
+
errors.push(`${location}: missing required field "${key}"`);
|
|
4585
|
+
}
|
|
4586
|
+
}
|
|
4587
|
+
if (schema.additionalProperties === false) {
|
|
4588
|
+
const allowed = new Set(Object.keys(schema.properties ?? {}));
|
|
4589
|
+
for (const key of Object.keys(obj)) {
|
|
4590
|
+
if (!allowed.has(key)) {
|
|
4591
|
+
errors.push(`${location}: unexpected field "${key}"`);
|
|
4592
|
+
}
|
|
4593
|
+
}
|
|
4594
|
+
}
|
|
4595
|
+
for (const [key, propSchema] of Object.entries(schema.properties ?? {})) {
|
|
4596
|
+
if (obj[key] === void 0) continue;
|
|
4597
|
+
errors.push(...validateAgainstSchema(obj[key], propSchema, `${location}.${key}`));
|
|
4598
|
+
}
|
|
4599
|
+
return errors;
|
|
4600
|
+
}
|
|
4601
|
+
if (schema.type === "array") {
|
|
4602
|
+
if (!Array.isArray(value)) {
|
|
4603
|
+
return [`${location}: must be an array`];
|
|
4604
|
+
}
|
|
4605
|
+
if (schema.uniqueItems) {
|
|
4606
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4607
|
+
value.forEach((item) => {
|
|
4608
|
+
const key = JSON.stringify(item);
|
|
4609
|
+
if (seen.has(key)) errors.push(`${location}: duplicate item ${key}`);
|
|
4610
|
+
seen.add(key);
|
|
4611
|
+
});
|
|
4612
|
+
}
|
|
4613
|
+
if (schema.items) {
|
|
4614
|
+
value.forEach((item, index) => {
|
|
4615
|
+
errors.push(...validateAgainstSchema(item, schema.items, `${location}[${index}]`));
|
|
4616
|
+
});
|
|
4617
|
+
}
|
|
4618
|
+
return errors;
|
|
4619
|
+
}
|
|
4620
|
+
if (schema.type === "string") {
|
|
4621
|
+
if (typeof value !== "string") {
|
|
4622
|
+
return [`${location}: must be a string`];
|
|
4623
|
+
}
|
|
4624
|
+
if (schema.enum && !schema.enum.includes(value)) {
|
|
4625
|
+
errors.push(`${location}: must be one of ${schema.enum.join(", ")} (got "${value}")`);
|
|
4626
|
+
}
|
|
4627
|
+
if (schema.pattern && !new RegExp(schema.pattern).test(value)) {
|
|
4628
|
+
errors.push(`${location}: must match pattern ${schema.pattern} (got "${value}")`);
|
|
4629
|
+
}
|
|
4630
|
+
return errors;
|
|
4631
|
+
}
|
|
4632
|
+
return errors;
|
|
4633
|
+
}
|
|
4634
|
+
function validateManifest(manifest) {
|
|
4635
|
+
const schema = loadSchema();
|
|
4636
|
+
return validateAgainstSchema(manifest, schema, "$");
|
|
4637
|
+
}
|
|
4638
|
+
function readManifest(manifestPathArg) {
|
|
4639
|
+
const resolved = path10.resolve(process.cwd(), manifestPathArg);
|
|
4640
|
+
if (!existsSync10(resolved)) {
|
|
4641
|
+
throw new CliError({ message: `Manifest not found: ${manifestPathArg}` });
|
|
4642
|
+
}
|
|
4643
|
+
return parseYaml(readFileSync8(resolved, "utf8"));
|
|
4644
|
+
}
|
|
4645
|
+
function runContractCheck({ args, json }) {
|
|
4646
|
+
const manifestPathArg = args.find((arg) => !arg.startsWith("--"));
|
|
4647
|
+
if (!manifestPathArg) {
|
|
4648
|
+
throw new CliError({ message: "Usage: ovld contract check <manifest-path> [--json]" });
|
|
4649
|
+
}
|
|
4650
|
+
const manifest = readManifest(manifestPathArg);
|
|
4651
|
+
const errors = validateManifest(manifest);
|
|
4652
|
+
const valid = errors.length === 0;
|
|
4653
|
+
if (json) {
|
|
4654
|
+
console.log(JSON.stringify({ valid, manifest: manifestPathArg, errors }, null, 2));
|
|
4655
|
+
} else if (valid) {
|
|
4656
|
+
console.log(`\u2713 ${manifestPathArg} conforms to conformance-manifest.schema.yaml`);
|
|
4657
|
+
} else {
|
|
4658
|
+
console.log(`\u2717 ${manifestPathArg} does not conform to conformance-manifest.schema.yaml:`);
|
|
4659
|
+
for (const error of errors) {
|
|
4660
|
+
console.log(` - ${error}`);
|
|
4661
|
+
}
|
|
4662
|
+
}
|
|
4663
|
+
if (!valid) {
|
|
4664
|
+
throw new CliError({ message: `${errors.length} conformance error(s) found.` });
|
|
4665
|
+
}
|
|
4666
|
+
}
|
|
4667
|
+
async function runContractCommand({
|
|
4668
|
+
subcommand,
|
|
4669
|
+
args,
|
|
4670
|
+
primaryCommand
|
|
4671
|
+
}) {
|
|
4672
|
+
const json = args.includes("--json");
|
|
4673
|
+
const rest = args.filter((arg) => arg !== "--json");
|
|
4674
|
+
switch (subcommand) {
|
|
4675
|
+
case "check":
|
|
4676
|
+
runContractCheck({ args: rest, json });
|
|
4677
|
+
return;
|
|
4678
|
+
default:
|
|
4679
|
+
throw new CliError({
|
|
4680
|
+
message: `Unknown contract subcommand: ${subcommand}
|
|
4681
|
+
Usage: ${primaryCommand} contract check <manifest-path> [--json]`
|
|
4682
|
+
});
|
|
4683
|
+
}
|
|
4684
|
+
}
|
|
4685
|
+
var cachedSchema;
|
|
4686
|
+
var init_contract = __esm({
|
|
4687
|
+
"src/contract.ts"() {
|
|
4688
|
+
"use strict";
|
|
4689
|
+
init_config();
|
|
4690
|
+
init_errors();
|
|
4691
|
+
}
|
|
4692
|
+
});
|
|
4693
|
+
|
|
4448
4694
|
// ../packages/core/dist/service/util.js
|
|
4449
4695
|
function nowIso() {
|
|
4450
4696
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -4460,12 +4706,12 @@ var init_util2 = __esm({
|
|
|
4460
4706
|
});
|
|
4461
4707
|
|
|
4462
4708
|
// ../packages/core/dist/service/local-target/project-metadata.js
|
|
4463
|
-
import { existsSync as
|
|
4464
|
-
import
|
|
4709
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync5, readFileSync as readFileSync9, writeFileSync as writeFileSync4 } from "node:fs";
|
|
4710
|
+
import path11 from "node:path";
|
|
4465
4711
|
function parseProjectJson(projectJsonPath) {
|
|
4466
|
-
if (!
|
|
4712
|
+
if (!existsSync11(projectJsonPath))
|
|
4467
4713
|
return null;
|
|
4468
|
-
const parsed = JSON.parse(
|
|
4714
|
+
const parsed = JSON.parse(readFileSync9(projectJsonPath, "utf8"));
|
|
4469
4715
|
if (typeof parsed.projectId !== "string" || typeof parsed.resourceId !== "string")
|
|
4470
4716
|
return null;
|
|
4471
4717
|
const resourceIdsByExecutionTarget = parsed.resourceIdsByExecutionTarget && typeof parsed.resourceIdsByExecutionTarget === "object" && !Array.isArray(parsed.resourceIdsByExecutionTarget) ? Object.fromEntries(Object.entries(parsed.resourceIdsByExecutionTarget).filter((entry) => typeof entry[0] === "string" && entry[0].trim().length > 0 && typeof entry[1] === "string" && entry[1].trim().length > 0)) : {};
|
|
@@ -4495,11 +4741,11 @@ function readProjectJsonLink(projectJsonPath, options = {}) {
|
|
|
4495
4741
|
};
|
|
4496
4742
|
}
|
|
4497
4743
|
function writeProjectJson({ directoryPath, projectId, resourceId, resourceKey, executionTargetId, isPrimary }) {
|
|
4498
|
-
const overlordDir =
|
|
4744
|
+
const overlordDir = path11.join(directoryPath, ".overlord");
|
|
4499
4745
|
mkdirSync5(overlordDir, { recursive: true });
|
|
4500
|
-
mkdirSync5(
|
|
4501
|
-
mkdirSync5(
|
|
4502
|
-
const projectJsonPath =
|
|
4746
|
+
mkdirSync5(path11.join(overlordDir, "tmp"), { recursive: true });
|
|
4747
|
+
mkdirSync5(path11.join(overlordDir, "logs"), { recursive: true });
|
|
4748
|
+
const projectJsonPath = path11.join(overlordDir, "project.json");
|
|
4503
4749
|
const existing = readProjectJsonLink(projectJsonPath);
|
|
4504
4750
|
const resourceIdsByExecutionTarget = {
|
|
4505
4751
|
...existing?.projectId === projectId ? existing.resourceIdsByExecutionTarget : {}
|
|
@@ -4531,7 +4777,7 @@ var init_project_metadata = __esm({
|
|
|
4531
4777
|
|
|
4532
4778
|
// ../packages/core/dist/repository/git-tree.js
|
|
4533
4779
|
import { execFileSync } from "node:child_process";
|
|
4534
|
-
import
|
|
4780
|
+
import path12 from "node:path";
|
|
4535
4781
|
function runGit(cwd, args) {
|
|
4536
4782
|
try {
|
|
4537
4783
|
return execFileSync("git", args, {
|
|
@@ -4554,17 +4800,17 @@ function gitPath(cwd, args) {
|
|
|
4554
4800
|
}
|
|
4555
4801
|
}
|
|
4556
4802
|
function addDirectoryAncestors(paths, filePath) {
|
|
4557
|
-
let parent =
|
|
4803
|
+
let parent = path12.posix.dirname(filePath);
|
|
4558
4804
|
while (parent !== "." && parent !== "/") {
|
|
4559
4805
|
paths.add(parent);
|
|
4560
|
-
parent =
|
|
4806
|
+
parent = path12.posix.dirname(parent);
|
|
4561
4807
|
}
|
|
4562
4808
|
}
|
|
4563
4809
|
function toEntry(entryPath, type) {
|
|
4564
|
-
const parentPath =
|
|
4810
|
+
const parentPath = path12.posix.dirname(entryPath);
|
|
4565
4811
|
return {
|
|
4566
4812
|
path: entryPath,
|
|
4567
|
-
name:
|
|
4813
|
+
name: path12.posix.basename(entryPath),
|
|
4568
4814
|
type,
|
|
4569
4815
|
parentPath: parentPath === "." ? null : parentPath,
|
|
4570
4816
|
depth: entryPath.split("/").length - 1
|
|
@@ -4576,7 +4822,7 @@ function compareEntries(a, b) {
|
|
|
4576
4822
|
return a.path < b.path ? -1 : 1;
|
|
4577
4823
|
}
|
|
4578
4824
|
function readRepositoryTree(rootPath, options = {}) {
|
|
4579
|
-
const resolvedRoot =
|
|
4825
|
+
const resolvedRoot = path12.resolve(rootPath);
|
|
4580
4826
|
const gitRoot = gitPath(resolvedRoot, ["rev-parse", "--show-toplevel"]);
|
|
4581
4827
|
if (!gitRoot) {
|
|
4582
4828
|
throw new RepositoryReadError("not_git_repository", `${resolvedRoot} is not inside a git repository.`);
|
|
@@ -4599,7 +4845,7 @@ function readRepositoryTree(rootPath, options = {}) {
|
|
|
4599
4845
|
const truncated = entries.length > maxEntries;
|
|
4600
4846
|
return {
|
|
4601
4847
|
rootPath: resolvedRoot,
|
|
4602
|
-
gitRoot:
|
|
4848
|
+
gitRoot: path12.resolve(gitRoot),
|
|
4603
4849
|
branch: gitPath(gitRoot, ["branch", "--show-current"]),
|
|
4604
4850
|
commit: gitPath(gitRoot, ["rev-parse", "--short", "HEAD"]),
|
|
4605
4851
|
entries: truncated ? entries.slice(0, maxEntries) : entries,
|
|
@@ -4660,13 +4906,13 @@ var init_git_run = __esm({
|
|
|
4660
4906
|
});
|
|
4661
4907
|
|
|
4662
4908
|
// ../packages/core/dist/service/local-target/worktree-git.js
|
|
4663
|
-
import { existsSync as
|
|
4664
|
-
import
|
|
4909
|
+
import { existsSync as existsSync12, realpathSync } from "node:fs";
|
|
4910
|
+
import path13 from "node:path";
|
|
4665
4911
|
function resolveRealPath(targetPath) {
|
|
4666
4912
|
try {
|
|
4667
4913
|
return realpathSync.native(targetPath);
|
|
4668
4914
|
} catch {
|
|
4669
|
-
return
|
|
4915
|
+
return path13.resolve(targetPath);
|
|
4670
4916
|
}
|
|
4671
4917
|
}
|
|
4672
4918
|
function worktreeIsDirty(worktreePath) {
|
|
@@ -4696,7 +4942,7 @@ function removeGitWorktree({ primaryRepoPath, worktreePath, force }) {
|
|
|
4696
4942
|
args.push(worktreePath);
|
|
4697
4943
|
runGitResult(primaryRepoPath, args);
|
|
4698
4944
|
runGitResult(primaryRepoPath, ["worktree", "prune"]);
|
|
4699
|
-
return !
|
|
4945
|
+
return !existsSync12(worktreePath);
|
|
4700
4946
|
}
|
|
4701
4947
|
function collectManagedWorktrees({ worktreeRoot, projects }) {
|
|
4702
4948
|
const resolvedRoot = resolveRealPath(worktreeRoot);
|
|
@@ -4704,7 +4950,7 @@ function collectManagedWorktrees({ worktreeRoot, projects }) {
|
|
|
4704
4950
|
const seen = /* @__PURE__ */ new Set();
|
|
4705
4951
|
for (const project of projects) {
|
|
4706
4952
|
const repoPath = project.primaryRepoPath;
|
|
4707
|
-
if (!repoPath || !
|
|
4953
|
+
if (!repoPath || !existsSync12(repoPath))
|
|
4708
4954
|
continue;
|
|
4709
4955
|
const out = runGitResult(repoPath, ["worktree", "list", "--porcelain"]);
|
|
4710
4956
|
if (!out.ok)
|
|
@@ -4715,7 +4961,7 @@ function collectManagedWorktrees({ worktreeRoot, projects }) {
|
|
|
4715
4961
|
if (!currentPath)
|
|
4716
4962
|
return;
|
|
4717
4963
|
const resolved = resolveRealPath(currentPath);
|
|
4718
|
-
const underRoot = resolved === resolvedRoot || resolved.startsWith(resolvedRoot +
|
|
4964
|
+
const underRoot = resolved === resolvedRoot || resolved.startsWith(resolvedRoot + path13.sep);
|
|
4719
4965
|
if (underRoot && !seen.has(resolved)) {
|
|
4720
4966
|
seen.add(resolved);
|
|
4721
4967
|
entries.push({
|
|
@@ -4743,7 +4989,7 @@ function collectManagedWorktrees({ worktreeRoot, projects }) {
|
|
|
4743
4989
|
return entries;
|
|
4744
4990
|
}
|
|
4745
4991
|
function removeManagedWorktree({ path: worktreePath, primaryRepoPath, force }) {
|
|
4746
|
-
const target =
|
|
4992
|
+
const target = path13.resolve(worktreePath);
|
|
4747
4993
|
if (!force && worktreeIsDirty(target)) {
|
|
4748
4994
|
return {
|
|
4749
4995
|
removed: [],
|
|
@@ -4757,7 +5003,7 @@ function purgeManagedWorktrees({ entries }) {
|
|
|
4757
5003
|
const removed = [];
|
|
4758
5004
|
const skipped = [];
|
|
4759
5005
|
for (const entry of entries) {
|
|
4760
|
-
const target =
|
|
5006
|
+
const target = path13.resolve(entry.path);
|
|
4761
5007
|
if (worktreeIsDirty(target)) {
|
|
4762
5008
|
skipped.push({ path: target, reason: "uncommitted changes" });
|
|
4763
5009
|
continue;
|
|
@@ -4782,10 +5028,10 @@ var init_worktree_git = __esm({
|
|
|
4782
5028
|
});
|
|
4783
5029
|
|
|
4784
5030
|
// ../packages/core/dist/service/local-target/branch-actions-git.js
|
|
4785
|
-
import { existsSync as
|
|
5031
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
4786
5032
|
function integrateBranch(input) {
|
|
4787
5033
|
const { branchName, baseBranch, worktreePath, primaryRepoPath } = input;
|
|
4788
|
-
if (!
|
|
5034
|
+
if (!existsSync13(worktreePath)) {
|
|
4789
5035
|
return {
|
|
4790
5036
|
ok: false,
|
|
4791
5037
|
code: "BRANCH_NO_WORKTREE",
|
|
@@ -4867,7 +5113,7 @@ function commitBranch(input) {
|
|
|
4867
5113
|
message: "A commit message is required."
|
|
4868
5114
|
};
|
|
4869
5115
|
}
|
|
4870
|
-
if (!
|
|
5116
|
+
if (!existsSync13(worktreePath)) {
|
|
4871
5117
|
return {
|
|
4872
5118
|
ok: false,
|
|
4873
5119
|
code: "BRANCH_NO_WORKTREE",
|
|
@@ -4923,7 +5169,7 @@ function pushParent(input) {
|
|
|
4923
5169
|
};
|
|
4924
5170
|
}
|
|
4925
5171
|
let summary = `Pushed ${baseBranch} to origin.`;
|
|
4926
|
-
if (
|
|
5172
|
+
if (existsSync13(worktreePath) && !worktreeIsDirty(worktreePath)) {
|
|
4927
5173
|
if (removeGitWorktree({ primaryRepoPath, worktreePath, force: false })) {
|
|
4928
5174
|
summary += ` Removed the merged worktree for ${branchName}.`;
|
|
4929
5175
|
}
|
|
@@ -4932,7 +5178,7 @@ function pushParent(input) {
|
|
|
4932
5178
|
}
|
|
4933
5179
|
function publishBranch(input) {
|
|
4934
5180
|
const { branchName, worktreePath, primaryRepoPath } = input;
|
|
4935
|
-
const repo =
|
|
5181
|
+
const repo = existsSync13(worktreePath) ? worktreePath : primaryRepoPath;
|
|
4936
5182
|
const push = runGitResult(repo, ["push", "-u", "origin", branchName]);
|
|
4937
5183
|
if (!push.ok) {
|
|
4938
5184
|
return {
|
|
@@ -4982,7 +5228,7 @@ var init_branch_status_git = __esm({
|
|
|
4982
5228
|
});
|
|
4983
5229
|
|
|
4984
5230
|
// ../packages/core/dist/service/local-target/commit-message-diff-git.js
|
|
4985
|
-
import { existsSync as
|
|
5231
|
+
import { existsSync as existsSync14 } from "node:fs";
|
|
4986
5232
|
function collectWorktreeChanges(worktreePath) {
|
|
4987
5233
|
const status = runGitResult(worktreePath, ["status", "--porcelain"]);
|
|
4988
5234
|
const diff = runGitResult(worktreePath, ["diff", "HEAD"]);
|
|
@@ -4998,7 +5244,7 @@ ${diff.stdout}`);
|
|
|
4998
5244
|
return sections.join("\n\n");
|
|
4999
5245
|
}
|
|
5000
5246
|
function gatherCommitMessageDiff(worktreePath) {
|
|
5001
|
-
if (!
|
|
5247
|
+
if (!existsSync14(worktreePath)) {
|
|
5002
5248
|
return {
|
|
5003
5249
|
ok: false,
|
|
5004
5250
|
code: "BRANCH_NO_WORKTREE",
|
|
@@ -5059,7 +5305,7 @@ var init_result = __esm({
|
|
|
5059
5305
|
});
|
|
5060
5306
|
|
|
5061
5307
|
// ../packages/core/dist/service/local-target/in-process-provider.js
|
|
5062
|
-
import { existsSync as
|
|
5308
|
+
import { existsSync as existsSync15 } from "node:fs";
|
|
5063
5309
|
var InProcessProvider;
|
|
5064
5310
|
var init_in_process_provider = __esm({
|
|
5065
5311
|
"../packages/core/dist/service/local-target/in-process-provider.js"() {
|
|
@@ -5087,7 +5333,7 @@ var init_in_process_provider = __esm({
|
|
|
5087
5333
|
*/
|
|
5088
5334
|
async observeResource(input) {
|
|
5089
5335
|
const observedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5090
|
-
const state =
|
|
5336
|
+
const state = existsSync15(input.path) ? "available" : "missing";
|
|
5091
5337
|
return ok(this.target, { state, observedAt });
|
|
5092
5338
|
}
|
|
5093
5339
|
/**
|
|
@@ -5334,7 +5580,7 @@ var init_local_target_mutation_runner = __esm({
|
|
|
5334
5580
|
});
|
|
5335
5581
|
|
|
5336
5582
|
// ../packages/core/dist/service/project-resource-key.js
|
|
5337
|
-
import
|
|
5583
|
+
import path14 from "node:path";
|
|
5338
5584
|
function deriveProjectResourceKey({ resourceKey, label, directoryPath }) {
|
|
5339
5585
|
const explicit = resourceKey?.trim();
|
|
5340
5586
|
if (explicit)
|
|
@@ -5342,7 +5588,7 @@ function deriveProjectResourceKey({ resourceKey, label, directoryPath }) {
|
|
|
5342
5588
|
const labelKey = label?.trim();
|
|
5343
5589
|
if (labelKey)
|
|
5344
5590
|
return slugify(labelKey);
|
|
5345
|
-
return slugify(
|
|
5591
|
+
return slugify(path14.basename(path14.resolve(directoryPath)));
|
|
5346
5592
|
}
|
|
5347
5593
|
var init_project_resource_key = __esm({
|
|
5348
5594
|
"../packages/core/dist/service/project-resource-key.js"() {
|
|
@@ -5352,7 +5598,7 @@ var init_project_resource_key = __esm({
|
|
|
5352
5598
|
});
|
|
5353
5599
|
|
|
5354
5600
|
// src/branch-planning.ts
|
|
5355
|
-
import
|
|
5601
|
+
import path15 from "node:path";
|
|
5356
5602
|
function stripCombiningMarks(value) {
|
|
5357
5603
|
return value.normalize("NFKD").replace(/[\u0300-\u036f]/g, "");
|
|
5358
5604
|
}
|
|
@@ -5385,7 +5631,7 @@ function missionWorktreePath({
|
|
|
5385
5631
|
branch
|
|
5386
5632
|
}) {
|
|
5387
5633
|
const key = resourceKey.trim() || "project";
|
|
5388
|
-
return
|
|
5634
|
+
return path15.join(worktreeRoot, projectSlug || "project", key, branchLeaf(branch));
|
|
5389
5635
|
}
|
|
5390
5636
|
function normalizeRemoteRef(ref) {
|
|
5391
5637
|
return ref.replace(/^origin\//, "").replace(/^refs\/heads\//, "").replace(/^refs\/remotes\/origin\//, "");
|
|
@@ -5460,9 +5706,9 @@ var init_branch_planning = __esm({
|
|
|
5460
5706
|
|
|
5461
5707
|
// src/branch-preparation.ts
|
|
5462
5708
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
5463
|
-
import { existsSync as
|
|
5709
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync6, statSync as statSync3 } from "node:fs";
|
|
5464
5710
|
import os3 from "node:os";
|
|
5465
|
-
import
|
|
5711
|
+
import path16 from "node:path";
|
|
5466
5712
|
function runGit3(cwd, args, options = {}) {
|
|
5467
5713
|
try {
|
|
5468
5714
|
return execFileSync4("git", args, {
|
|
@@ -5483,7 +5729,7 @@ function lines(value) {
|
|
|
5483
5729
|
function resolveGitRoot(workingDirectory) {
|
|
5484
5730
|
const root = runGit3(workingDirectory, ["rev-parse", "--show-toplevel"]);
|
|
5485
5731
|
if (!root) throw new Error(`${workingDirectory} is not inside a git repository.`);
|
|
5486
|
-
return
|
|
5732
|
+
return path16.resolve(root);
|
|
5487
5733
|
}
|
|
5488
5734
|
function repoDefaultBranch(gitRoot) {
|
|
5489
5735
|
const symbolic = runGit3(gitRoot, ["symbolic-ref", "--short", "refs/remotes/origin/HEAD"], {
|
|
@@ -5617,9 +5863,9 @@ function missionSequence(mission, missionId) {
|
|
|
5617
5863
|
}
|
|
5618
5864
|
function resolveWorktreeRoot() {
|
|
5619
5865
|
const override = process.env.OVERLORD_WORKTREE_ROOT?.trim();
|
|
5620
|
-
if (override) return
|
|
5866
|
+
if (override) return path16.resolve(override);
|
|
5621
5867
|
const home = process.env.OVLD_HOME?.trim() || process.env.OVERLORD_HOME?.trim();
|
|
5622
|
-
return
|
|
5868
|
+
return path16.join(home ? path16.resolve(home) : path16.join(os3.homedir(), ".ovld"), "worktrees");
|
|
5623
5869
|
}
|
|
5624
5870
|
function ensureBranchRef(gitRoot, decision) {
|
|
5625
5871
|
if (decision.action === "reuse") return;
|
|
@@ -5630,8 +5876,8 @@ function ensureBranchRef(gitRoot, decision) {
|
|
|
5630
5876
|
}
|
|
5631
5877
|
function ensureWorktree(gitRoot, decision) {
|
|
5632
5878
|
ensureBranchRef(gitRoot, decision);
|
|
5633
|
-
mkdirSync6(
|
|
5634
|
-
if (
|
|
5879
|
+
mkdirSync6(path16.dirname(decision.worktreePath), { recursive: true });
|
|
5880
|
+
if (existsSync16(decision.worktreePath)) {
|
|
5635
5881
|
if (!statSync3(decision.worktreePath).isDirectory()) {
|
|
5636
5882
|
throw new Error(`Worktree path exists and is not a directory: ${decision.worktreePath}`);
|
|
5637
5883
|
}
|
|
@@ -5772,7 +6018,7 @@ var init_branch_preparation = __esm({
|
|
|
5772
6018
|
});
|
|
5773
6019
|
|
|
5774
6020
|
// src/discover-project-local.ts
|
|
5775
|
-
import
|
|
6021
|
+
import path17 from "node:path";
|
|
5776
6022
|
async function resolvePreferredExecutionTargetId({
|
|
5777
6023
|
backend
|
|
5778
6024
|
}) {
|
|
@@ -5789,9 +6035,9 @@ function discoverProjectJsonFromFilesystem({
|
|
|
5789
6035
|
workingDirectory,
|
|
5790
6036
|
preferredExecutionTargetId
|
|
5791
6037
|
}) {
|
|
5792
|
-
let current =
|
|
6038
|
+
let current = path17.resolve(workingDirectory);
|
|
5793
6039
|
while (true) {
|
|
5794
|
-
const projectJsonPath =
|
|
6040
|
+
const projectJsonPath = path17.join(current, ".overlord", "project.json");
|
|
5795
6041
|
const raw = readProjectJsonLink(projectJsonPath, { preferredExecutionTargetId });
|
|
5796
6042
|
if (raw) {
|
|
5797
6043
|
return {
|
|
@@ -5801,7 +6047,7 @@ function discoverProjectJsonFromFilesystem({
|
|
|
5801
6047
|
isPrimary: raw.isPrimary
|
|
5802
6048
|
};
|
|
5803
6049
|
}
|
|
5804
|
-
const parent =
|
|
6050
|
+
const parent = path17.dirname(current);
|
|
5805
6051
|
if (parent === current) break;
|
|
5806
6052
|
current = parent;
|
|
5807
6053
|
}
|
|
@@ -5843,7 +6089,7 @@ async function discoverProjectOnClient({
|
|
|
5843
6089
|
const resolvedLocal = localWithTarget ?? local;
|
|
5844
6090
|
if (!resolvedLocal) {
|
|
5845
6091
|
throw new CliError({
|
|
5846
|
-
message: `No linked Overlord project found for ${
|
|
6092
|
+
message: `No linked Overlord project found for ${path17.resolve(workingDirectory)}. Run \`ovld add-cwd\` or \`ovld create-project\`.`
|
|
5847
6093
|
});
|
|
5848
6094
|
}
|
|
5849
6095
|
const project = await resolveProjectByIdOrName({ backend, projectRef: resolvedLocal.projectId });
|
|
@@ -6143,7 +6389,7 @@ var init_terminal_launcher = __esm({
|
|
|
6143
6389
|
// src/launch.ts
|
|
6144
6390
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
6145
6391
|
import { chmodSync as chmodSync3, writeFileSync as writeFileSync5 } from "node:fs";
|
|
6146
|
-
import
|
|
6392
|
+
import path18 from "node:path";
|
|
6147
6393
|
function overlordLaunchEnv({
|
|
6148
6394
|
backendUrl,
|
|
6149
6395
|
missionId,
|
|
@@ -6275,7 +6521,7 @@ function launchScriptPath({
|
|
|
6275
6521
|
executionRequestId
|
|
6276
6522
|
}) {
|
|
6277
6523
|
const requestPart = executionRequestId ? safeLaunchScriptPart(executionRequestId) : `${process.pid}`;
|
|
6278
|
-
return
|
|
6524
|
+
return path18.join(tmpDir, `launch-${safeLaunchScriptPart(displayId)}-${requestPart}.sh`);
|
|
6279
6525
|
}
|
|
6280
6526
|
async function buildLaunchPlan({
|
|
6281
6527
|
runtime,
|
|
@@ -6284,7 +6530,7 @@ async function buildLaunchPlan({
|
|
|
6284
6530
|
const context = await loadMissionContext({ runtime, missionId: options.missionId });
|
|
6285
6531
|
pruneStaleProjectTmp({ workingDirectory: options.workingDirectory, create: true });
|
|
6286
6532
|
const tmpDir = ensureProjectTmpDir(options.workingDirectory);
|
|
6287
|
-
const contextFile =
|
|
6533
|
+
const contextFile = path18.join(
|
|
6288
6534
|
tmpDir,
|
|
6289
6535
|
`mission-${context.displayId.replace(/[^a-zA-Z0-9_-]/g, "-")}.md`
|
|
6290
6536
|
);
|
|
@@ -6394,16 +6640,16 @@ var init_launch = __esm({
|
|
|
6394
6640
|
|
|
6395
6641
|
// src/native-session.ts
|
|
6396
6642
|
import { createHash as createHash3 } from "node:crypto";
|
|
6397
|
-
import { existsSync as
|
|
6643
|
+
import { existsSync as existsSync17, readdirSync as readdirSync3, readFileSync as readFileSync10, statSync as statSync4 } from "node:fs";
|
|
6398
6644
|
import { homedir } from "node:os";
|
|
6399
|
-
import
|
|
6645
|
+
import path19 from "node:path";
|
|
6400
6646
|
function sessionCachePath({
|
|
6401
6647
|
agent,
|
|
6402
6648
|
missionId,
|
|
6403
6649
|
workingDirectory
|
|
6404
6650
|
}) {
|
|
6405
|
-
const key = createHash3("sha256").update(`${
|
|
6406
|
-
return
|
|
6651
|
+
const key = createHash3("sha256").update(`${path19.resolve(workingDirectory)}\0${missionId}\0${agent}`).digest("hex");
|
|
6652
|
+
return path19.join(resolveGlobalDataDir(), "native-sessions", key);
|
|
6407
6653
|
}
|
|
6408
6654
|
function readCachedNativeSessionId({
|
|
6409
6655
|
agent,
|
|
@@ -6412,20 +6658,20 @@ function readCachedNativeSessionId({
|
|
|
6412
6658
|
}) {
|
|
6413
6659
|
try {
|
|
6414
6660
|
const filePath = sessionCachePath({ agent, missionId, workingDirectory });
|
|
6415
|
-
if (!
|
|
6416
|
-
const raw = JSON.parse(
|
|
6661
|
+
if (!existsSync17(filePath)) return void 0;
|
|
6662
|
+
const raw = JSON.parse(readFileSync10(filePath, "utf8"));
|
|
6417
6663
|
return typeof raw.externalSessionId === "string" && raw.externalSessionId.trim() ? raw.externalSessionId.trim() : void 0;
|
|
6418
6664
|
} catch {
|
|
6419
6665
|
return void 0;
|
|
6420
6666
|
}
|
|
6421
6667
|
}
|
|
6422
6668
|
function detectCodexSessionIdFromDisk(workingDirectory) {
|
|
6423
|
-
const sessionsDir =
|
|
6424
|
-
if (!
|
|
6669
|
+
const sessionsDir = path19.join(homedir(), ".codex", "sessions");
|
|
6670
|
+
if (!existsSync17(sessionsDir)) return void 0;
|
|
6425
6671
|
const candidates = [];
|
|
6426
6672
|
const visit = (dir) => {
|
|
6427
6673
|
for (const entry of readdirSync3(dir, { withFileTypes: true })) {
|
|
6428
|
-
const filePath =
|
|
6674
|
+
const filePath = path19.join(dir, entry.name);
|
|
6429
6675
|
if (entry.isDirectory()) {
|
|
6430
6676
|
visit(filePath);
|
|
6431
6677
|
continue;
|
|
@@ -6450,7 +6696,7 @@ function detectCodexSessionIdFromDisk(workingDirectory) {
|
|
|
6450
6696
|
let nativeId;
|
|
6451
6697
|
let cwd;
|
|
6452
6698
|
try {
|
|
6453
|
-
const [firstLine = ""] =
|
|
6699
|
+
const [firstLine = ""] = readFileSync10(candidate.filePath, "utf8").split(/\r?\n/, 1);
|
|
6454
6700
|
const obj = JSON.parse(firstLine);
|
|
6455
6701
|
const payload = obj.payload && typeof obj.payload === "object" ? obj.payload : obj;
|
|
6456
6702
|
if (typeof payload.id === "string") nativeId = payload.id.match(UUID_RE)?.[0];
|
|
@@ -6458,10 +6704,10 @@ function detectCodexSessionIdFromDisk(workingDirectory) {
|
|
|
6458
6704
|
} catch {
|
|
6459
6705
|
nativeId = void 0;
|
|
6460
6706
|
}
|
|
6461
|
-
nativeId ??=
|
|
6707
|
+
nativeId ??= path19.basename(candidate.filePath).match(UUID_RE)?.[0];
|
|
6462
6708
|
if (!nativeId) continue;
|
|
6463
6709
|
fallback ??= nativeId;
|
|
6464
|
-
if (cwd &&
|
|
6710
|
+
if (cwd && path19.resolve(cwd) === path19.resolve(workingDirectory)) return nativeId;
|
|
6465
6711
|
}
|
|
6466
6712
|
return fallback;
|
|
6467
6713
|
}
|
|
@@ -6498,8 +6744,8 @@ var init_native_session = __esm({
|
|
|
6498
6744
|
});
|
|
6499
6745
|
|
|
6500
6746
|
// src/org-setup.ts
|
|
6501
|
-
import { readFileSync as
|
|
6502
|
-
import
|
|
6747
|
+
import { readFileSync as readFileSync11 } from "node:fs";
|
|
6748
|
+
import path20 from "node:path";
|
|
6503
6749
|
import { createInterface as createInterface3 } from "node:readline";
|
|
6504
6750
|
function suggestSlug(name) {
|
|
6505
6751
|
const cleaned = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -6578,12 +6824,12 @@ async function runOrgSetupCommand({
|
|
|
6578
6824
|
let logoWarning = null;
|
|
6579
6825
|
if (logoPath) {
|
|
6580
6826
|
try {
|
|
6581
|
-
const resolvedPath =
|
|
6582
|
-
const bytes =
|
|
6827
|
+
const resolvedPath = path20.resolve(logoPath);
|
|
6828
|
+
const bytes = readFileSync11(resolvedPath);
|
|
6583
6829
|
const stored = await runtime.backend.postRaw({
|
|
6584
6830
|
path: "/api/uploads/organization-images",
|
|
6585
6831
|
body: bytes,
|
|
6586
|
-
filename:
|
|
6832
|
+
filename: path20.basename(resolvedPath)
|
|
6587
6833
|
});
|
|
6588
6834
|
await runtime.backend.patch({
|
|
6589
6835
|
path: `/api/organizations/${encodeURIComponent(organization.id)}`,
|
|
@@ -7017,17 +7263,17 @@ var init_protocol_help = __esm({
|
|
|
7017
7263
|
|
|
7018
7264
|
// src/vcs-sessions.ts
|
|
7019
7265
|
import { createHash as createHash4 } from "node:crypto";
|
|
7020
|
-
import { existsSync as
|
|
7021
|
-
import
|
|
7266
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync6 } from "node:fs";
|
|
7267
|
+
import path21 from "node:path";
|
|
7022
7268
|
function manifestPath2(workingDirectory) {
|
|
7023
|
-
const key = createHash4("sha256").update(
|
|
7024
|
-
return
|
|
7269
|
+
const key = createHash4("sha256").update(path21.resolve(workingDirectory)).digest("hex");
|
|
7270
|
+
return path21.join(resolveGlobalDataDir(), "vcs-sessions", `${key}.json`);
|
|
7025
7271
|
}
|
|
7026
7272
|
function readEntries(workingDirectory) {
|
|
7027
7273
|
try {
|
|
7028
7274
|
const target = manifestPath2(workingDirectory);
|
|
7029
|
-
if (!
|
|
7030
|
-
const raw = JSON.parse(
|
|
7275
|
+
if (!existsSync18(target)) return [];
|
|
7276
|
+
const raw = JSON.parse(readFileSync12(target, "utf8"));
|
|
7031
7277
|
if (!Array.isArray(raw.entries)) return [];
|
|
7032
7278
|
const now = Date.now();
|
|
7033
7279
|
return raw.entries.flatMap((entry) => {
|
|
@@ -7046,7 +7292,7 @@ function readEntries(workingDirectory) {
|
|
|
7046
7292
|
function writeEntries(workingDirectory, entries) {
|
|
7047
7293
|
try {
|
|
7048
7294
|
const target = manifestPath2(workingDirectory);
|
|
7049
|
-
mkdirSync7(
|
|
7295
|
+
mkdirSync7(path21.dirname(target), { recursive: true });
|
|
7050
7296
|
writeFileSync6(target, JSON.stringify({ entries }));
|
|
7051
7297
|
} catch {
|
|
7052
7298
|
}
|
|
@@ -7100,20 +7346,20 @@ var init_vcs_sessions = __esm({
|
|
|
7100
7346
|
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
7101
7347
|
import { createHash as createHash5 } from "node:crypto";
|
|
7102
7348
|
import {
|
|
7103
|
-
existsSync as
|
|
7349
|
+
existsSync as existsSync19,
|
|
7104
7350
|
mkdirSync as mkdirSync8,
|
|
7105
7351
|
readdirSync as readdirSync4,
|
|
7106
|
-
readFileSync as
|
|
7352
|
+
readFileSync as readFileSync13,
|
|
7107
7353
|
realpathSync as realpathSync2,
|
|
7108
7354
|
rmSync as rmSync3,
|
|
7109
7355
|
writeFileSync as writeFileSync7
|
|
7110
7356
|
} from "node:fs";
|
|
7111
|
-
import
|
|
7357
|
+
import path22 from "node:path";
|
|
7112
7358
|
function normalizePath(filePath) {
|
|
7113
7359
|
return filePath.replace(/\\/g, "/").trim();
|
|
7114
7360
|
}
|
|
7115
7361
|
function sameWorkingDirectory(left, right) {
|
|
7116
|
-
return typeof left === "string" &&
|
|
7362
|
+
return typeof left === "string" && path22.resolve(left) === path22.resolve(right);
|
|
7117
7363
|
}
|
|
7118
7364
|
function parsePorcelainLine(line) {
|
|
7119
7365
|
const trimmedEnd = line.replace(/\s+$/, "");
|
|
@@ -7164,13 +7410,13 @@ function sessionKeyHash({
|
|
|
7164
7410
|
workingDirectory,
|
|
7165
7411
|
missionId
|
|
7166
7412
|
}) {
|
|
7167
|
-
return createHash5("sha256").update(`${
|
|
7413
|
+
return createHash5("sha256").update(`${path22.resolve(workingDirectory)}\0${missionId}`).digest("hex");
|
|
7168
7414
|
}
|
|
7169
7415
|
function baselineFilePath({
|
|
7170
7416
|
workingDirectory,
|
|
7171
7417
|
missionId
|
|
7172
7418
|
}) {
|
|
7173
|
-
return
|
|
7419
|
+
return path22.join(
|
|
7174
7420
|
resolveGlobalDataDir(),
|
|
7175
7421
|
"vcs-baselines",
|
|
7176
7422
|
`${sessionKeyHash({ workingDirectory, missionId })}.json`
|
|
@@ -7180,7 +7426,7 @@ function touchedFilesPath({
|
|
|
7180
7426
|
workingDirectory,
|
|
7181
7427
|
missionId
|
|
7182
7428
|
}) {
|
|
7183
|
-
return
|
|
7429
|
+
return path22.join(
|
|
7184
7430
|
resolveGlobalDataDir(),
|
|
7185
7431
|
"vcs-touched",
|
|
7186
7432
|
`${sessionKeyHash({ workingDirectory, missionId })}.json`
|
|
@@ -7190,7 +7436,7 @@ function rationaleNotesPath({
|
|
|
7190
7436
|
workingDirectory,
|
|
7191
7437
|
missionId
|
|
7192
7438
|
}) {
|
|
7193
|
-
return
|
|
7439
|
+
return path22.join(
|
|
7194
7440
|
resolveGlobalDataDir(),
|
|
7195
7441
|
"vcs-rationale-notes",
|
|
7196
7442
|
`${sessionKeyHash({ workingDirectory, missionId })}.json`
|
|
@@ -7200,7 +7446,7 @@ function bashSnapshotPath({
|
|
|
7200
7446
|
workingDirectory,
|
|
7201
7447
|
missionId
|
|
7202
7448
|
}) {
|
|
7203
|
-
return
|
|
7449
|
+
return path22.join(
|
|
7204
7450
|
resolveGlobalDataDir(),
|
|
7205
7451
|
"vcs-touched",
|
|
7206
7452
|
`${sessionKeyHash({ workingDirectory, missionId })}.bash-snapshot.json`
|
|
@@ -7212,8 +7458,8 @@ function readBashSnapshot({
|
|
|
7212
7458
|
}) {
|
|
7213
7459
|
try {
|
|
7214
7460
|
const target = bashSnapshotPath({ workingDirectory, missionId });
|
|
7215
|
-
if (!
|
|
7216
|
-
const raw = JSON.parse(
|
|
7461
|
+
if (!existsSync19(target)) return /* @__PURE__ */ new Map();
|
|
7462
|
+
const raw = JSON.parse(readFileSync13(target, "utf8"));
|
|
7217
7463
|
const byPath = /* @__PURE__ */ new Map();
|
|
7218
7464
|
if (Array.isArray(raw.files)) {
|
|
7219
7465
|
for (const entry of raw.files) {
|
|
@@ -7238,7 +7484,7 @@ function writeBashSnapshot({
|
|
|
7238
7484
|
}) {
|
|
7239
7485
|
try {
|
|
7240
7486
|
const target = bashSnapshotPath({ workingDirectory, missionId });
|
|
7241
|
-
mkdirSync8(
|
|
7487
|
+
mkdirSync8(path22.dirname(target), { recursive: true });
|
|
7242
7488
|
writeFileSync7(target, JSON.stringify({ updatedAt: (/* @__PURE__ */ new Date()).toISOString(), files }));
|
|
7243
7489
|
} catch {
|
|
7244
7490
|
}
|
|
@@ -7272,12 +7518,12 @@ function recordBashObservedChanges({
|
|
|
7272
7518
|
}
|
|
7273
7519
|
}
|
|
7274
7520
|
function normalizeAbsolute(filePath) {
|
|
7275
|
-
const resolved =
|
|
7521
|
+
const resolved = path22.resolve(filePath);
|
|
7276
7522
|
try {
|
|
7277
7523
|
return realpathSync2(resolved).replace(/\\/g, "/");
|
|
7278
7524
|
} catch {
|
|
7279
7525
|
try {
|
|
7280
|
-
return
|
|
7526
|
+
return path22.join(realpathSync2(path22.dirname(resolved)), path22.basename(resolved)).replace(/\\/g, "/");
|
|
7281
7527
|
} catch {
|
|
7282
7528
|
return resolved.replace(/\\/g, "/");
|
|
7283
7529
|
}
|
|
@@ -7304,7 +7550,7 @@ function resetTouchedFiles({
|
|
|
7304
7550
|
touchedFilesPath({ workingDirectory, missionId }),
|
|
7305
7551
|
bashSnapshotPath({ workingDirectory, missionId })
|
|
7306
7552
|
]) {
|
|
7307
|
-
if (
|
|
7553
|
+
if (existsSync19(target)) rmSync3(target);
|
|
7308
7554
|
}
|
|
7309
7555
|
} catch {
|
|
7310
7556
|
}
|
|
@@ -7315,7 +7561,7 @@ function resetRationaleNotes({
|
|
|
7315
7561
|
}) {
|
|
7316
7562
|
try {
|
|
7317
7563
|
const target = rationaleNotesPath({ workingDirectory, missionId });
|
|
7318
|
-
if (
|
|
7564
|
+
if (existsSync19(target)) rmSync3(target);
|
|
7319
7565
|
} catch {
|
|
7320
7566
|
}
|
|
7321
7567
|
}
|
|
@@ -7330,8 +7576,8 @@ function readRationaleNotes({
|
|
|
7330
7576
|
}) {
|
|
7331
7577
|
try {
|
|
7332
7578
|
const target = rationaleNotesPath({ workingDirectory, missionId });
|
|
7333
|
-
if (!
|
|
7334
|
-
const raw = JSON.parse(
|
|
7579
|
+
if (!existsSync19(target)) return [];
|
|
7580
|
+
const raw = JSON.parse(readFileSync13(target, "utf8"));
|
|
7335
7581
|
if (!Array.isArray(raw.notes)) return [];
|
|
7336
7582
|
return raw.notes.flatMap((note) => {
|
|
7337
7583
|
if (typeof note !== "object" || note === null || typeof note.filePath !== "string") {
|
|
@@ -7365,7 +7611,7 @@ function recordRationaleNotes({
|
|
|
7365
7611
|
intent: truncateNoteText(note.intent),
|
|
7366
7612
|
transcriptContext: truncateNoteText(note.transcriptContext)
|
|
7367
7613
|
})).filter((note) => note.filePath).map((note) => {
|
|
7368
|
-
const absolute = normalizeAbsolute(
|
|
7614
|
+
const absolute = normalizeAbsolute(path22.resolve(workingDirectory, note.filePath));
|
|
7369
7615
|
return {
|
|
7370
7616
|
filePath: absolute,
|
|
7371
7617
|
toolName: note.toolName,
|
|
@@ -7373,14 +7619,14 @@ function recordRationaleNotes({
|
|
|
7373
7619
|
transcriptContext: note.transcriptContext,
|
|
7374
7620
|
contentHash: hashWorktreeFile({
|
|
7375
7621
|
workingDirectory,
|
|
7376
|
-
filePath:
|
|
7622
|
+
filePath: path22.relative(workingDirectory, absolute)
|
|
7377
7623
|
}),
|
|
7378
7624
|
recordedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7379
7625
|
};
|
|
7380
7626
|
});
|
|
7381
7627
|
if (additions.length === 0) return;
|
|
7382
7628
|
const target = rationaleNotesPath({ workingDirectory, missionId });
|
|
7383
|
-
mkdirSync8(
|
|
7629
|
+
mkdirSync8(path22.dirname(target), { recursive: true });
|
|
7384
7630
|
const prior = readRationaleNotes({ workingDirectory, missionId });
|
|
7385
7631
|
const notesToKeep = [...prior, ...additions].slice(-200);
|
|
7386
7632
|
writeFileSync7(
|
|
@@ -7396,16 +7642,16 @@ function recordTouchedFiles({
|
|
|
7396
7642
|
files
|
|
7397
7643
|
}) {
|
|
7398
7644
|
try {
|
|
7399
|
-
const additions = files.map((filePath) => (filePath ?? "").trim()).filter(Boolean).map((filePath) => normalizeAbsolute(
|
|
7645
|
+
const additions = files.map((filePath) => (filePath ?? "").trim()).filter(Boolean).map((filePath) => normalizeAbsolute(path22.resolve(workingDirectory, filePath)));
|
|
7400
7646
|
if (additions.length === 0) return;
|
|
7401
7647
|
const target = touchedFilesPath({ workingDirectory, missionId });
|
|
7402
|
-
mkdirSync8(
|
|
7648
|
+
mkdirSync8(path22.dirname(target), { recursive: true });
|
|
7403
7649
|
const existing = readTouchedLog({ workingDirectory, missionId })?.paths ?? /* @__PURE__ */ new Set();
|
|
7404
7650
|
for (const filePath of additions) existing.add(filePath);
|
|
7405
7651
|
writeFileSync7(
|
|
7406
7652
|
target,
|
|
7407
7653
|
JSON.stringify({
|
|
7408
|
-
workingDirectory:
|
|
7654
|
+
workingDirectory: path22.resolve(workingDirectory),
|
|
7409
7655
|
missionId,
|
|
7410
7656
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7411
7657
|
paths: Array.from(existing)
|
|
@@ -7418,7 +7664,7 @@ function hasTouchedFilesLog({
|
|
|
7418
7664
|
workingDirectory,
|
|
7419
7665
|
missionId
|
|
7420
7666
|
}) {
|
|
7421
|
-
return
|
|
7667
|
+
return existsSync19(touchedFilesPath({ workingDirectory, missionId }));
|
|
7422
7668
|
}
|
|
7423
7669
|
function readTouchedLog({
|
|
7424
7670
|
workingDirectory,
|
|
@@ -7426,8 +7672,8 @@ function readTouchedLog({
|
|
|
7426
7672
|
}) {
|
|
7427
7673
|
try {
|
|
7428
7674
|
const target = touchedFilesPath({ workingDirectory, missionId });
|
|
7429
|
-
if (!
|
|
7430
|
-
const raw = JSON.parse(
|
|
7675
|
+
if (!existsSync19(target)) return null;
|
|
7676
|
+
const raw = JSON.parse(readFileSync13(target, "utf8"));
|
|
7431
7677
|
const result = /* @__PURE__ */ new Set();
|
|
7432
7678
|
if (Array.isArray(raw.paths)) {
|
|
7433
7679
|
for (const entry of raw.paths) {
|
|
@@ -7460,9 +7706,9 @@ function writeBaseline({
|
|
|
7460
7706
|
}) {
|
|
7461
7707
|
try {
|
|
7462
7708
|
const target = baselineFilePath({ workingDirectory, missionId });
|
|
7463
|
-
mkdirSync8(
|
|
7709
|
+
mkdirSync8(path22.dirname(target), { recursive: true });
|
|
7464
7710
|
const snapshot = {
|
|
7465
|
-
workingDirectory:
|
|
7711
|
+
workingDirectory: path22.resolve(workingDirectory),
|
|
7466
7712
|
missionId,
|
|
7467
7713
|
capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7468
7714
|
files: snapshotBaselineFiles({ workingDirectory, files })
|
|
@@ -7479,13 +7725,13 @@ function readPeerTouchedClaims({
|
|
|
7479
7725
|
readActiveSessions(workingDirectory).map((entry) => entry.missionId.trim()).filter(Boolean)
|
|
7480
7726
|
);
|
|
7481
7727
|
if (activeMissionIds.size === 0) return [];
|
|
7482
|
-
const directory =
|
|
7483
|
-
if (!
|
|
7728
|
+
const directory = path22.join(resolveGlobalDataDir(), "vcs-touched");
|
|
7729
|
+
if (!existsSync19(directory)) return [];
|
|
7484
7730
|
const claims = [];
|
|
7485
7731
|
for (const name of readdirSync4(directory)) {
|
|
7486
7732
|
if (!name.endsWith(".json") || name.endsWith(".bash-snapshot.json")) continue;
|
|
7487
7733
|
try {
|
|
7488
|
-
const raw = JSON.parse(
|
|
7734
|
+
const raw = JSON.parse(readFileSync13(path22.join(directory, name), "utf8"));
|
|
7489
7735
|
const candidateMissionId = typeof raw.missionId === "string" && raw.missionId.trim() ? raw.missionId.trim() : null;
|
|
7490
7736
|
if (!candidateMissionId || candidateMissionId === missionId) continue;
|
|
7491
7737
|
if (!activeMissionIds.has(candidateMissionId)) continue;
|
|
@@ -7510,8 +7756,8 @@ function readBaselineSnapshot({
|
|
|
7510
7756
|
}) {
|
|
7511
7757
|
try {
|
|
7512
7758
|
const target = baselineFilePath({ workingDirectory, missionId });
|
|
7513
|
-
if (!
|
|
7514
|
-
const raw = JSON.parse(
|
|
7759
|
+
if (!existsSync19(target)) return /* @__PURE__ */ new Map();
|
|
7760
|
+
const raw = JSON.parse(readFileSync13(target, "utf8"));
|
|
7515
7761
|
if (Array.isArray(raw.files)) {
|
|
7516
7762
|
const byPath = /* @__PURE__ */ new Map();
|
|
7517
7763
|
for (const entry of raw.files) {
|
|
@@ -7605,10 +7851,10 @@ function compileIgnorePattern(rawPattern) {
|
|
|
7605
7851
|
}
|
|
7606
7852
|
function loadIgnoreRules(repoRoot) {
|
|
7607
7853
|
try {
|
|
7608
|
-
const target =
|
|
7609
|
-
if (!
|
|
7854
|
+
const target = path22.join(repoRoot, OVERLORD_IGNORE_FILENAME);
|
|
7855
|
+
if (!existsSync19(target)) return [];
|
|
7610
7856
|
const rules = [];
|
|
7611
|
-
for (const rawLine of
|
|
7857
|
+
for (const rawLine of readFileSync13(target, "utf8").split("\n")) {
|
|
7612
7858
|
const line = rawLine.replace(/\r$/, "").trim();
|
|
7613
7859
|
if (!line || line.startsWith("#")) continue;
|
|
7614
7860
|
const rule = compileIgnorePattern(line);
|
|
@@ -7642,7 +7888,7 @@ function filterRunAttributableChanges({
|
|
|
7642
7888
|
return [];
|
|
7643
7889
|
}
|
|
7644
7890
|
if (!touched) return [{ filePath: entry.filePath, vcsStatus: entry.vcsStatus }];
|
|
7645
|
-
const absolute = normalizeAbsolute(
|
|
7891
|
+
const absolute = normalizeAbsolute(path22.resolve(repoRoot, entry.filePath));
|
|
7646
7892
|
if (touched.paths.has(absolute)) {
|
|
7647
7893
|
return [
|
|
7648
7894
|
{
|
|
@@ -7673,7 +7919,7 @@ function filterRunAttributableChanges({
|
|
|
7673
7919
|
});
|
|
7674
7920
|
}
|
|
7675
7921
|
function titleFromPath(filePath) {
|
|
7676
|
-
const base =
|
|
7922
|
+
const base = path22.basename(filePath).replace(/\.[^.]+$/, "");
|
|
7677
7923
|
const words = base.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[-_]+/g, " ").trim();
|
|
7678
7924
|
if (!words) return `Update ${filePath}`;
|
|
7679
7925
|
return `Update ${words.replace(/\b\w/g, (char) => char.toUpperCase())}`;
|
|
@@ -7696,7 +7942,7 @@ function draftChangeRationalesFromNotes({
|
|
|
7696
7942
|
if (notes.length === 0 || files.length === 0) return [];
|
|
7697
7943
|
const repoRoot = gitRepoRoot(workingDirectory) ?? workingDirectory;
|
|
7698
7944
|
return files.flatMap((file) => {
|
|
7699
|
-
const absolute = normalizeAbsolute(
|
|
7945
|
+
const absolute = normalizeAbsolute(path22.resolve(repoRoot, file.filePath));
|
|
7700
7946
|
const currentHash = hashWorktreeFile({ workingDirectory, filePath: file.filePath });
|
|
7701
7947
|
const note = latestRelevantNote({ notes, filePath: absolute, currentHash });
|
|
7702
7948
|
if (!note) return [];
|
|
@@ -7736,8 +7982,8 @@ var init_vcs = __esm({
|
|
|
7736
7982
|
});
|
|
7737
7983
|
|
|
7738
7984
|
// src/record-touched.ts
|
|
7739
|
-
import { readFileSync as
|
|
7740
|
-
import
|
|
7985
|
+
import { readFileSync as readFileSync14 } from "node:fs";
|
|
7986
|
+
import path23 from "node:path";
|
|
7741
7987
|
function collectCandidates(toolInput) {
|
|
7742
7988
|
const counts = /* @__PURE__ */ new Map();
|
|
7743
7989
|
const order = [];
|
|
@@ -7786,7 +8032,7 @@ function editIntent({
|
|
|
7786
8032
|
function recentAssistantContext(transcriptPath) {
|
|
7787
8033
|
if (typeof transcriptPath !== "string" || !transcriptPath.trim()) return null;
|
|
7788
8034
|
try {
|
|
7789
|
-
const lines2 =
|
|
8035
|
+
const lines2 = readFileSync14(transcriptPath, "utf8").split("\n").slice(-200);
|
|
7790
8036
|
for (let index = lines2.length - 1; index >= 0; index -= 1) {
|
|
7791
8037
|
const line = lines2[index]?.trim();
|
|
7792
8038
|
if (!line) continue;
|
|
@@ -7836,7 +8082,7 @@ function recordTouchedFromPayload({
|
|
|
7836
8082
|
const toolInput = toolInputRaw && typeof toolInputRaw === "object" ? toolInputRaw : {};
|
|
7837
8083
|
const toolName = typeof payload.tool_name === "string" && payload.tool_name.trim() ? payload.tool_name.trim() : "file edit";
|
|
7838
8084
|
const cwd = typeof payload.cwd === "string" && payload.cwd.trim() ? payload.cwd.trim() : fallbackCwd;
|
|
7839
|
-
const workingDirectory =
|
|
8085
|
+
const workingDirectory = path23.resolve(cwd);
|
|
7840
8086
|
const trimmedOverride = missionOverride?.trim();
|
|
7841
8087
|
let missionId;
|
|
7842
8088
|
let ambiguous = false;
|
|
@@ -7940,9 +8186,9 @@ var init_resource_observations = __esm({
|
|
|
7940
8186
|
|
|
7941
8187
|
// src/runner-service.ts
|
|
7942
8188
|
import { execFile } from "node:child_process";
|
|
7943
|
-
import { chmodSync as chmodSync4, existsSync as
|
|
8189
|
+
import { chmodSync as chmodSync4, existsSync as existsSync20, mkdirSync as mkdirSync9, readFileSync as readFileSync15, rmSync as rmSync4, writeFileSync as writeFileSync8 } from "node:fs";
|
|
7944
8190
|
import os4 from "node:os";
|
|
7945
|
-
import
|
|
8191
|
+
import path24 from "node:path";
|
|
7946
8192
|
import { promisify } from "node:util";
|
|
7947
8193
|
function writeServiceFile(filePath, contents) {
|
|
7948
8194
|
writeFileSync8(filePath, contents, { mode: 384 });
|
|
@@ -7964,13 +8210,13 @@ function emptyRunnerServiceState() {
|
|
|
7964
8210
|
};
|
|
7965
8211
|
}
|
|
7966
8212
|
function runnerServiceStatePath(dataDir = resolveGlobalDataDir()) {
|
|
7967
|
-
return
|
|
8213
|
+
return path24.join(dataDir, RUNNER_SERVICE_STATE_FILENAME);
|
|
7968
8214
|
}
|
|
7969
8215
|
function readRunnerServiceState(dataDir = resolveGlobalDataDir()) {
|
|
7970
8216
|
const filePath = runnerServiceStatePath(dataDir);
|
|
7971
|
-
if (!
|
|
8217
|
+
if (!existsSync20(filePath)) return emptyRunnerServiceState();
|
|
7972
8218
|
try {
|
|
7973
|
-
const parsed = JSON.parse(
|
|
8219
|
+
const parsed = JSON.parse(readFileSync15(filePath, "utf8"));
|
|
7974
8220
|
return { ...emptyRunnerServiceState(), ...parsed };
|
|
7975
8221
|
} catch {
|
|
7976
8222
|
return emptyRunnerServiceState();
|
|
@@ -7986,14 +8232,33 @@ function patchRunnerServiceState(patch, dataDir = resolveGlobalDataDir()) {
|
|
|
7986
8232
|
writeRunnerServiceState(next, dataDir);
|
|
7987
8233
|
return next;
|
|
7988
8234
|
}
|
|
8235
|
+
function runnerFocusStatePath(dataDir = resolveGlobalDataDir()) {
|
|
8236
|
+
return path24.join(dataDir, RUNNER_FOCUS_STATE_FILENAME);
|
|
8237
|
+
}
|
|
8238
|
+
function readDesktopFocusState(dataDir = resolveGlobalDataDir()) {
|
|
8239
|
+
const filePath = runnerFocusStatePath(dataDir);
|
|
8240
|
+
if (!existsSync20(filePath)) return { lastFocusedAt: null };
|
|
8241
|
+
try {
|
|
8242
|
+
const parsed = JSON.parse(readFileSync15(filePath, "utf8"));
|
|
8243
|
+
return { lastFocusedAt: parsed.lastFocusedAt ?? null };
|
|
8244
|
+
} catch {
|
|
8245
|
+
return { lastFocusedAt: null };
|
|
8246
|
+
}
|
|
8247
|
+
}
|
|
8248
|
+
function withinWindow(timestamp, now, windowMs) {
|
|
8249
|
+
if (!timestamp) return false;
|
|
8250
|
+
const ms = Date.parse(timestamp);
|
|
8251
|
+
if (Number.isNaN(ms)) return false;
|
|
8252
|
+
return now - ms <= windowMs;
|
|
8253
|
+
}
|
|
7989
8254
|
function selectBasePollIntervalMs({
|
|
7990
8255
|
lastLaunchedAt,
|
|
8256
|
+
lastDesktopFocusAt = null,
|
|
7991
8257
|
now
|
|
7992
8258
|
}) {
|
|
7993
|
-
|
|
7994
|
-
const
|
|
7995
|
-
|
|
7996
|
-
return now - launchedMs <= IDLE_BACKOFF_MS ? FAST_POLL_INTERVAL_MS : SLOW_POLL_INTERVAL_MS;
|
|
8259
|
+
const jobActive = withinWindow(lastLaunchedAt, now, IDLE_BACKOFF_MS);
|
|
8260
|
+
const focusActive = withinWindow(lastDesktopFocusAt, now, DESKTOP_FOCUS_WINDOW_MS);
|
|
8261
|
+
return jobActive || focusActive ? FAST_POLL_INTERVAL_MS : SLOW_POLL_INTERVAL_MS;
|
|
7997
8262
|
}
|
|
7998
8263
|
function applyPollJitter(intervalMs, random = Math.random) {
|
|
7999
8264
|
const spread = intervalMs * 0.1;
|
|
@@ -8005,16 +8270,16 @@ function nextRunnerLastError(pollError) {
|
|
|
8005
8270
|
function resolveOverlordAppInvocation({
|
|
8006
8271
|
platform: platform2 = process.platform,
|
|
8007
8272
|
homedir: homedir2 = os4.homedir(),
|
|
8008
|
-
exists =
|
|
8273
|
+
exists = existsSync20
|
|
8009
8274
|
} = {}) {
|
|
8010
8275
|
if (platform2 !== "darwin") return null;
|
|
8011
8276
|
const appDirs = [
|
|
8012
8277
|
"/Applications/Overlord.app",
|
|
8013
|
-
|
|
8278
|
+
path24.join(homedir2, "Applications", "Overlord.app")
|
|
8014
8279
|
];
|
|
8015
8280
|
for (const appDir of appDirs) {
|
|
8016
|
-
const program =
|
|
8017
|
-
const script =
|
|
8281
|
+
const program = path24.join(appDir, "Contents", "MacOS", "Overlord");
|
|
8282
|
+
const script = path24.join(appDir, "Contents", "Resources", "cli", "bin", "ovld.mjs");
|
|
8018
8283
|
if (exists(program) && exists(script)) {
|
|
8019
8284
|
return { program, args: [script, "runner", "supervise"], runAsElectronNode: true };
|
|
8020
8285
|
}
|
|
@@ -8026,7 +8291,7 @@ function resolveOvldInvocation(argv = process.argv, execPath = process.execPath)
|
|
|
8026
8291
|
if (override) {
|
|
8027
8292
|
return { program: override, args: ["runner", "supervise"] };
|
|
8028
8293
|
}
|
|
8029
|
-
const scriptPath = argv[1] ?
|
|
8294
|
+
const scriptPath = argv[1] ? path24.resolve(argv[1]) : "";
|
|
8030
8295
|
if (process.env.ELECTRON_RUN_AS_NODE) {
|
|
8031
8296
|
return scriptPath ? { program: execPath, args: [scriptPath, "runner", "supervise"], runAsElectronNode: true } : { program: execPath, args: ["runner", "supervise"], runAsElectronNode: true };
|
|
8032
8297
|
}
|
|
@@ -8059,7 +8324,7 @@ function buildRunnerServiceEnv({
|
|
|
8059
8324
|
PATH: process.env.PATH?.trim() || "/usr/local/bin:/usr/bin:/bin"
|
|
8060
8325
|
};
|
|
8061
8326
|
const home = overlordHome?.trim() || process.env.OVLD_HOME?.trim();
|
|
8062
|
-
if (home) env.OVLD_HOME =
|
|
8327
|
+
if (home) env.OVLD_HOME = path24.resolve(home);
|
|
8063
8328
|
const token = process.env.OVERLORD_USER_TOKEN?.trim() || process.env.OVLD_USER_TOKEN?.trim();
|
|
8064
8329
|
if (token) env.OVERLORD_USER_TOKEN = token;
|
|
8065
8330
|
if (runAsElectronNode || process.env.ELECTRON_RUN_AS_NODE) env.ELECTRON_RUN_AS_NODE = "1";
|
|
@@ -8098,9 +8363,9 @@ ${envEntries}
|
|
|
8098
8363
|
<key>KeepAlive</key>
|
|
8099
8364
|
<true/>
|
|
8100
8365
|
<key>StandardOutPath</key>
|
|
8101
|
-
<string>${xmlEscape(
|
|
8366
|
+
<string>${xmlEscape(path24.join(logDir, "runner-service.out.log"))}</string>
|
|
8102
8367
|
<key>StandardErrorPath</key>
|
|
8103
|
-
<string>${xmlEscape(
|
|
8368
|
+
<string>${xmlEscape(path24.join(logDir, "runner-service.err.log"))}</string>
|
|
8104
8369
|
<key>ProcessType</key>
|
|
8105
8370
|
<string>Background</string>
|
|
8106
8371
|
</dict>
|
|
@@ -8131,12 +8396,12 @@ WantedBy=default.target
|
|
|
8131
8396
|
`;
|
|
8132
8397
|
}
|
|
8133
8398
|
function launchAgentsDir() {
|
|
8134
|
-
return
|
|
8399
|
+
return path24.join(os4.homedir(), "Library", "LaunchAgents");
|
|
8135
8400
|
}
|
|
8136
8401
|
function systemdUserDir() {
|
|
8137
8402
|
const xdg = process.env.XDG_CONFIG_HOME?.trim();
|
|
8138
|
-
const base = xdg ?
|
|
8139
|
-
return
|
|
8403
|
+
const base = xdg ? path24.resolve(xdg) : path24.join(os4.homedir(), ".config");
|
|
8404
|
+
return path24.join(base, "systemd", "user");
|
|
8140
8405
|
}
|
|
8141
8406
|
async function runCommand(program, args) {
|
|
8142
8407
|
const { stdout } = await execFileAsync(program, args);
|
|
@@ -8147,35 +8412,37 @@ function resolveServiceManager(platform2 = process.platform) {
|
|
|
8147
8412
|
if (platform2 === "linux") return new SystemdUserManager();
|
|
8148
8413
|
return null;
|
|
8149
8414
|
}
|
|
8150
|
-
var execFileAsync, RUNNER_SERVICE_STATE_FILENAME, LAUNCHD_LABEL, SYSTEMD_UNIT_NAME, FAST_POLL_INTERVAL_MS, SLOW_POLL_INTERVAL_MS, IDLE_BACKOFF_MS, LaunchdManager, SystemdUserManager;
|
|
8415
|
+
var execFileAsync, RUNNER_SERVICE_STATE_FILENAME, RUNNER_FOCUS_STATE_FILENAME, LAUNCHD_LABEL, SYSTEMD_UNIT_NAME, FAST_POLL_INTERVAL_MS, SLOW_POLL_INTERVAL_MS, IDLE_BACKOFF_MS, DESKTOP_FOCUS_WINDOW_MS, LaunchdManager, SystemdUserManager;
|
|
8151
8416
|
var init_runner_service = __esm({
|
|
8152
8417
|
"src/runner-service.ts"() {
|
|
8153
8418
|
"use strict";
|
|
8154
8419
|
init_config();
|
|
8155
8420
|
execFileAsync = promisify(execFile);
|
|
8156
8421
|
RUNNER_SERVICE_STATE_FILENAME = "runner-service.json";
|
|
8422
|
+
RUNNER_FOCUS_STATE_FILENAME = "runner-focus.json";
|
|
8157
8423
|
LAUNCHD_LABEL = "io.overlord.runner";
|
|
8158
8424
|
SYSTEMD_UNIT_NAME = "overlord-runner";
|
|
8159
8425
|
FAST_POLL_INTERVAL_MS = 3e3;
|
|
8160
|
-
SLOW_POLL_INTERVAL_MS =
|
|
8426
|
+
SLOW_POLL_INTERVAL_MS = 5e3;
|
|
8161
8427
|
IDLE_BACKOFF_MS = 2 * 60 * 60 * 1e3;
|
|
8428
|
+
DESKTOP_FOCUS_WINDOW_MS = 30 * 60 * 1e3;
|
|
8162
8429
|
LaunchdManager = class {
|
|
8163
8430
|
kind = "launchd";
|
|
8164
8431
|
identifier = LAUNCHD_LABEL;
|
|
8165
8432
|
unitPath() {
|
|
8166
|
-
return
|
|
8433
|
+
return path24.join(launchAgentsDir(), `${LAUNCHD_LABEL}.plist`);
|
|
8167
8434
|
}
|
|
8168
8435
|
render(opts) {
|
|
8169
8436
|
return renderLaunchdPlist({
|
|
8170
8437
|
label: LAUNCHD_LABEL,
|
|
8171
8438
|
invocation: opts.invocation,
|
|
8172
8439
|
env: opts.env,
|
|
8173
|
-
logDir:
|
|
8440
|
+
logDir: path24.join(resolveGlobalDataDir(), "logs")
|
|
8174
8441
|
});
|
|
8175
8442
|
}
|
|
8176
8443
|
async install(opts) {
|
|
8177
8444
|
mkdirSync9(launchAgentsDir(), { recursive: true });
|
|
8178
|
-
mkdirSync9(
|
|
8445
|
+
mkdirSync9(path24.join(resolveGlobalDataDir(), "logs"), { recursive: true });
|
|
8179
8446
|
writeServiceFile(this.unitPath(), this.render(opts));
|
|
8180
8447
|
if (opts.autoStart) await this.start();
|
|
8181
8448
|
}
|
|
@@ -8205,10 +8472,10 @@ var init_runner_service = __esm({
|
|
|
8205
8472
|
await this.stop();
|
|
8206
8473
|
} catch {
|
|
8207
8474
|
}
|
|
8208
|
-
if (
|
|
8475
|
+
if (existsSync20(this.unitPath())) rmSync4(this.unitPath());
|
|
8209
8476
|
}
|
|
8210
8477
|
async status() {
|
|
8211
|
-
const installed =
|
|
8478
|
+
const installed = existsSync20(this.unitPath());
|
|
8212
8479
|
try {
|
|
8213
8480
|
const { stdout } = await runCommand("launchctl", ["list"]);
|
|
8214
8481
|
const line = stdout.split("\n").find((row) => row.includes(LAUNCHD_LABEL));
|
|
@@ -8224,7 +8491,7 @@ var init_runner_service = __esm({
|
|
|
8224
8491
|
kind = "systemd";
|
|
8225
8492
|
identifier = `${SYSTEMD_UNIT_NAME}.service`;
|
|
8226
8493
|
unitPath() {
|
|
8227
|
-
return
|
|
8494
|
+
return path24.join(systemdUserDir(), `${SYSTEMD_UNIT_NAME}.service`);
|
|
8228
8495
|
}
|
|
8229
8496
|
render(opts) {
|
|
8230
8497
|
return renderSystemdUnit(opts);
|
|
@@ -8251,14 +8518,14 @@ var init_runner_service = __esm({
|
|
|
8251
8518
|
await runCommand("systemctl", ["--user", "disable", this.identifier]);
|
|
8252
8519
|
} catch {
|
|
8253
8520
|
}
|
|
8254
|
-
if (
|
|
8521
|
+
if (existsSync20(this.unitPath())) rmSync4(this.unitPath());
|
|
8255
8522
|
try {
|
|
8256
8523
|
await runCommand("systemctl", ["--user", "daemon-reload"]);
|
|
8257
8524
|
} catch {
|
|
8258
8525
|
}
|
|
8259
8526
|
}
|
|
8260
8527
|
async status() {
|
|
8261
|
-
const installed =
|
|
8528
|
+
const installed = existsSync20(this.unitPath());
|
|
8262
8529
|
try {
|
|
8263
8530
|
const { stdout } = await runCommand("systemctl", ["--user", "is-active", this.identifier]);
|
|
8264
8531
|
return { installed, running: stdout.trim() === "active" ? "running" : "stopped" };
|
|
@@ -8325,14 +8592,14 @@ var init_select_prompt = __esm({
|
|
|
8325
8592
|
|
|
8326
8593
|
// src/session-key.ts
|
|
8327
8594
|
import { createHash as createHash6 } from "node:crypto";
|
|
8328
|
-
import { existsSync as
|
|
8329
|
-
import
|
|
8595
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync10, readFileSync as readFileSync16, rmSync as rmSync5, writeFileSync as writeFileSync9 } from "node:fs";
|
|
8596
|
+
import path25 from "node:path";
|
|
8330
8597
|
function sessionKeyCachePath({
|
|
8331
8598
|
missionId,
|
|
8332
8599
|
workingDirectory
|
|
8333
8600
|
}) {
|
|
8334
|
-
const key = createHash6("sha256").update(`${
|
|
8335
|
-
return
|
|
8601
|
+
const key = createHash6("sha256").update(`${path25.resolve(workingDirectory)}\0${missionId}`).digest("hex");
|
|
8602
|
+
return path25.join(resolveGlobalDataDir(), "protocol-session-keys", key);
|
|
8336
8603
|
}
|
|
8337
8604
|
function readCachedSessionKey({
|
|
8338
8605
|
missionId,
|
|
@@ -8340,8 +8607,8 @@ function readCachedSessionKey({
|
|
|
8340
8607
|
}) {
|
|
8341
8608
|
try {
|
|
8342
8609
|
const filePath = sessionKeyCachePath({ missionId, workingDirectory });
|
|
8343
|
-
if (!
|
|
8344
|
-
const raw = JSON.parse(
|
|
8610
|
+
if (!existsSync21(filePath)) return void 0;
|
|
8611
|
+
const raw = JSON.parse(readFileSync16(filePath, "utf8"));
|
|
8345
8612
|
return typeof raw.sessionKey === "string" && raw.sessionKey.trim() ? raw.sessionKey.trim() : void 0;
|
|
8346
8613
|
} catch {
|
|
8347
8614
|
return void 0;
|
|
@@ -8356,7 +8623,7 @@ function writeCachedSessionKey({
|
|
|
8356
8623
|
const trimmed = sessionKey.trim();
|
|
8357
8624
|
if (!trimmed) return;
|
|
8358
8625
|
const filePath = sessionKeyCachePath({ missionId, workingDirectory });
|
|
8359
|
-
mkdirSync10(
|
|
8626
|
+
mkdirSync10(path25.dirname(filePath), { recursive: true });
|
|
8360
8627
|
writeFileSync9(
|
|
8361
8628
|
filePath,
|
|
8362
8629
|
JSON.stringify({ sessionKey: trimmed, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }),
|
|
@@ -8371,7 +8638,7 @@ function clearCachedSessionKey({
|
|
|
8371
8638
|
}) {
|
|
8372
8639
|
try {
|
|
8373
8640
|
const filePath = sessionKeyCachePath({ missionId, workingDirectory });
|
|
8374
|
-
if (
|
|
8641
|
+
if (existsSync21(filePath)) rmSync5(filePath);
|
|
8375
8642
|
} catch {
|
|
8376
8643
|
}
|
|
8377
8644
|
}
|
|
@@ -8389,9 +8656,9 @@ __export(commands_exports, {
|
|
|
8389
8656
|
runManagementCommand: () => runManagementCommand,
|
|
8390
8657
|
runProtocolCommand: () => runProtocolCommand
|
|
8391
8658
|
});
|
|
8392
|
-
import { existsSync as
|
|
8659
|
+
import { existsSync as existsSync22, readFileSync as readFileSync17 } from "node:fs";
|
|
8393
8660
|
import os5 from "node:os";
|
|
8394
|
-
import
|
|
8661
|
+
import path26 from "node:path";
|
|
8395
8662
|
function writeProjectJsonFromResource({
|
|
8396
8663
|
directory,
|
|
8397
8664
|
projectId,
|
|
@@ -8425,7 +8692,7 @@ function readChangedFilesFromFlags(flags, stdin) {
|
|
|
8425
8692
|
if (typeof fileFlag === "string") {
|
|
8426
8693
|
const raw = fileFlag === "-" ? stdin ?? "" : (() => {
|
|
8427
8694
|
try {
|
|
8428
|
-
return
|
|
8695
|
+
return readFileSync17(fileFlag, "utf8");
|
|
8429
8696
|
} catch {
|
|
8430
8697
|
return "";
|
|
8431
8698
|
}
|
|
@@ -8477,7 +8744,7 @@ function readJsonFlagContent({
|
|
|
8477
8744
|
if (typeof filePath === "string") {
|
|
8478
8745
|
if (fileInputs[fileFlag] !== void 0) return fileInputs[fileFlag];
|
|
8479
8746
|
try {
|
|
8480
|
-
return
|
|
8747
|
+
return readFileSync17(filePath, "utf8");
|
|
8481
8748
|
} catch {
|
|
8482
8749
|
return void 0;
|
|
8483
8750
|
}
|
|
@@ -8768,7 +9035,7 @@ async function resolveProtocolFileInputs({
|
|
|
8768
9035
|
} else if (process.stdin.isTTY) {
|
|
8769
9036
|
stdinContent = "";
|
|
8770
9037
|
} else {
|
|
8771
|
-
stdinContent =
|
|
9038
|
+
stdinContent = readFileSync17(0, "utf8");
|
|
8772
9039
|
}
|
|
8773
9040
|
return stdinContent;
|
|
8774
9041
|
};
|
|
@@ -8782,7 +9049,7 @@ async function resolveProtocolFileInputs({
|
|
|
8782
9049
|
fileInputs[flagName] = content;
|
|
8783
9050
|
stdinPayload = content;
|
|
8784
9051
|
} else {
|
|
8785
|
-
fileInputs[flagName] =
|
|
9052
|
+
fileInputs[flagName] = readFileSync17(filePath, "utf8");
|
|
8786
9053
|
}
|
|
8787
9054
|
}
|
|
8788
9055
|
if (stdinPayload === void 0 && stdin !== void 0 && Object.keys(fileInputs).length === 0) {
|
|
@@ -8825,7 +9092,7 @@ async function runProtocolCommand({
|
|
|
8825
9092
|
});
|
|
8826
9093
|
if (subcommand === "record-touched") {
|
|
8827
9094
|
const missionOverride = flagValue(parsed.flags, "--mission-id") ?? process.env.MISSION_ID ?? process.env.OVERLORD_MISSION_ID;
|
|
8828
|
-
const rawPayload = stdin ?? (process.stdin.isTTY ? "" :
|
|
9095
|
+
const rawPayload = stdin ?? (process.stdin.isTTY ? "" : readFileSync17(0, "utf8"));
|
|
8829
9096
|
const result2 = recordTouchedFromPayload({
|
|
8830
9097
|
rawPayload,
|
|
8831
9098
|
missionOverride,
|
|
@@ -8940,7 +9207,7 @@ async function runProtocolCommand({
|
|
|
8940
9207
|
if (subcommand === "deliver" && missionId) {
|
|
8941
9208
|
clearCachedSessionKey({ missionId, workingDirectory });
|
|
8942
9209
|
removeActiveSession({ workingDirectory, missionId });
|
|
8943
|
-
const connectorInstallsEditHook =
|
|
9210
|
+
const connectorInstallsEditHook = existsSync22(path26.join(os5.homedir(), ".claude"));
|
|
8944
9211
|
if (connectorInstallsEditHook && !hasTouchedFilesLog({ workingDirectory, missionId }) && readChangedFiles(workingDirectory).length > 0) {
|
|
8945
9212
|
const warning = `[overlord] warning: no touched-files log found at deliver for mission ${missionId} in ${workingDirectory}, even though this connector installs a PostToolUse edit hook. Deliver is falling back to baseline-only attribution, which can misattribute concurrent sessions' edits. Check ~/.ovld/logs/post-tool-use-hook.log for why the hook did not record any touched files.`;
|
|
8946
9213
|
console.error(warning);
|
|
@@ -9031,7 +9298,7 @@ async function runManagementCommand({
|
|
|
9031
9298
|
case "add-cwd": {
|
|
9032
9299
|
const directory = flagValue(parsed.flags, "--directory") ?? process.cwd();
|
|
9033
9300
|
const existingProjectJson = readProjectJsonLink(
|
|
9034
|
-
|
|
9301
|
+
path26.join(directory, ".overlord", "project.json")
|
|
9035
9302
|
);
|
|
9036
9303
|
const resourceKey = flagValue(parsed.flags, "--key") ?? existingProjectJson?.resourceKey;
|
|
9037
9304
|
let projectId = flagValue(parsed.flags, "--project-id");
|
|
@@ -9266,13 +9533,13 @@ async function runManagementCommand({
|
|
|
9266
9533
|
rationales: `/api/missions/${encodeURIComponent(missionId)}/file-changes`,
|
|
9267
9534
|
deliveries: `/api/missions/${encodeURIComponent(missionId)}/events`
|
|
9268
9535
|
};
|
|
9269
|
-
const
|
|
9270
|
-
if (!
|
|
9536
|
+
const path27 = sub ? pathBySub[sub] : void 0;
|
|
9537
|
+
if (!path27) {
|
|
9271
9538
|
throw new CliError({
|
|
9272
9539
|
message: "Usage: ovld mission context|events|deliveries|artifacts|rationales <missionId> [--json]"
|
|
9273
9540
|
});
|
|
9274
9541
|
}
|
|
9275
|
-
const result = await runtime.backend.get(
|
|
9542
|
+
const result = await runtime.backend.get(path27);
|
|
9276
9543
|
if (json || sub === "context") printJson(result);
|
|
9277
9544
|
else if (Array.isArray(result)) for (const row of result) console.log(JSON.stringify(row));
|
|
9278
9545
|
else printJson(result);
|
|
@@ -9468,8 +9735,10 @@ async function runRunnerSupervisor({
|
|
|
9468
9735
|
lastError = error instanceof Error ? error.message : String(error);
|
|
9469
9736
|
}
|
|
9470
9737
|
const state = readRunnerServiceState();
|
|
9738
|
+
const focus = readDesktopFocusState();
|
|
9471
9739
|
const base = selectBasePollIntervalMs({
|
|
9472
9740
|
lastLaunchedAt: launched ? now.toISOString() : state.lastLaunchedAt,
|
|
9741
|
+
lastDesktopFocusAt: focus.lastFocusedAt,
|
|
9473
9742
|
now: now.getTime()
|
|
9474
9743
|
});
|
|
9475
9744
|
patchRunnerServiceState({
|
|
@@ -9637,8 +9906,129 @@ var init_commands = __esm({
|
|
|
9637
9906
|
});
|
|
9638
9907
|
|
|
9639
9908
|
// src/index.ts
|
|
9909
|
+
init_args();
|
|
9640
9910
|
init_errors();
|
|
9641
9911
|
|
|
9912
|
+
// src/flag-registry.ts
|
|
9913
|
+
init_errors();
|
|
9914
|
+
var GLOBAL_FLAGS = ["--json", "--help", "-h"];
|
|
9915
|
+
var LAUNCH_FLAGS = [
|
|
9916
|
+
"--agent",
|
|
9917
|
+
"--mission-id",
|
|
9918
|
+
"--objective-id",
|
|
9919
|
+
"--working-directory",
|
|
9920
|
+
"--terminal",
|
|
9921
|
+
"--no-terminal",
|
|
9922
|
+
"--dry-run",
|
|
9923
|
+
"--branch",
|
|
9924
|
+
"--no-worktree",
|
|
9925
|
+
"--model",
|
|
9926
|
+
"--thinking",
|
|
9927
|
+
"--flag",
|
|
9928
|
+
"--pre-command"
|
|
9929
|
+
];
|
|
9930
|
+
var COMMAND_FLAGS = {
|
|
9931
|
+
version: [],
|
|
9932
|
+
update: ["--check", "--force", "--no-fund"],
|
|
9933
|
+
serve: ["--host", "--port", "--db"],
|
|
9934
|
+
init: [],
|
|
9935
|
+
doctor: [],
|
|
9936
|
+
setup: [],
|
|
9937
|
+
"agent-setup": ["--dry-run", "--home"],
|
|
9938
|
+
config: ["--path", "--url"],
|
|
9939
|
+
auth: ["--token", "--organization-id"],
|
|
9940
|
+
"user-token": ["--label", "--expires-in", "--no-expiry", "--scope", "--id"],
|
|
9941
|
+
prune: [],
|
|
9942
|
+
"create-project": ["--name", "--directory", "--no-directory"],
|
|
9943
|
+
"org-setup": [
|
|
9944
|
+
"--org-name",
|
|
9945
|
+
"--workspace-name",
|
|
9946
|
+
"--workspace-slug",
|
|
9947
|
+
"--logo",
|
|
9948
|
+
"--no-input",
|
|
9949
|
+
"--if-needed"
|
|
9950
|
+
],
|
|
9951
|
+
"add-cwd": ["--directory", "--key", "--project-id", "--primary"],
|
|
9952
|
+
"add-url": ["--url", "--project-id", "--key", "--primary"],
|
|
9953
|
+
create: ["--objectives-json", "--objective", "--prompt", "--project-id", "--title", "--resource"],
|
|
9954
|
+
prompt: [
|
|
9955
|
+
"--objectives-json",
|
|
9956
|
+
"--objective",
|
|
9957
|
+
"--prompt",
|
|
9958
|
+
"--project-id",
|
|
9959
|
+
"--title",
|
|
9960
|
+
"--resource",
|
|
9961
|
+
"--agent"
|
|
9962
|
+
],
|
|
9963
|
+
attach: ["--mission-id", "--agent", "--objective-id", "--model", "--thinking"],
|
|
9964
|
+
execution: ["--mission-id", "--agent", "--objective-id", "--model", "--thinking"],
|
|
9965
|
+
launch: LAUNCH_FLAGS,
|
|
9966
|
+
restart: LAUNCH_FLAGS,
|
|
9967
|
+
run: LAUNCH_FLAGS,
|
|
9968
|
+
connect: LAUNCH_FLAGS,
|
|
9969
|
+
resume: LAUNCH_FLAGS,
|
|
9970
|
+
runner: [
|
|
9971
|
+
"--project-id",
|
|
9972
|
+
"--poll-interval-ms",
|
|
9973
|
+
"--dry-run",
|
|
9974
|
+
"--branch",
|
|
9975
|
+
"--no-worktree",
|
|
9976
|
+
"--terminal",
|
|
9977
|
+
"--no-terminal",
|
|
9978
|
+
"--no-start"
|
|
9979
|
+
],
|
|
9980
|
+
missions: ["--status", "--query", "--project-id", "--limit"],
|
|
9981
|
+
mission: [],
|
|
9982
|
+
changes: ["--mission-id", "--objective-id"]
|
|
9983
|
+
};
|
|
9984
|
+
function editDistance(a, b) {
|
|
9985
|
+
let prev = Array.from({ length: b.length + 1 }, (_unused, j) => j);
|
|
9986
|
+
for (let i = 1; i <= a.length; i += 1) {
|
|
9987
|
+
const curr = new Array(b.length + 1);
|
|
9988
|
+
curr[0] = i;
|
|
9989
|
+
for (let j = 1; j <= b.length; j += 1) {
|
|
9990
|
+
const cost = a.charAt(i - 1) === b.charAt(j - 1) ? 0 : 1;
|
|
9991
|
+
curr[j] = Math.min((curr[j - 1] ?? 0) + 1, (prev[j] ?? 0) + 1, (prev[j - 1] ?? 0) + cost);
|
|
9992
|
+
}
|
|
9993
|
+
prev = curr;
|
|
9994
|
+
}
|
|
9995
|
+
return prev[b.length] ?? 0;
|
|
9996
|
+
}
|
|
9997
|
+
function suggestFlag(unknown, allowed) {
|
|
9998
|
+
let best;
|
|
9999
|
+
let bestDistance = Infinity;
|
|
10000
|
+
for (const candidate of allowed) {
|
|
10001
|
+
const distance = editDistance(unknown, candidate);
|
|
10002
|
+
if (distance < bestDistance) {
|
|
10003
|
+
bestDistance = distance;
|
|
10004
|
+
best = candidate;
|
|
10005
|
+
}
|
|
10006
|
+
}
|
|
10007
|
+
const threshold = Math.max(2, Math.floor(unknown.length / 3));
|
|
10008
|
+
return best !== void 0 && bestDistance <= threshold ? best : void 0;
|
|
10009
|
+
}
|
|
10010
|
+
function assertKnownFlags({
|
|
10011
|
+
command,
|
|
10012
|
+
flags,
|
|
10013
|
+
primaryCommand
|
|
10014
|
+
}) {
|
|
10015
|
+
const allowed = COMMAND_FLAGS[command];
|
|
10016
|
+
if (!allowed) return;
|
|
10017
|
+
const allowedSet = /* @__PURE__ */ new Set([...GLOBAL_FLAGS, ...allowed]);
|
|
10018
|
+
const unknown = [...flags.keys()].filter((flag) => flag.startsWith("--") && !allowedSet.has(flag));
|
|
10019
|
+
if (unknown.length === 0) return;
|
|
10020
|
+
const suggestionPool = [...GLOBAL_FLAGS, ...allowed];
|
|
10021
|
+
const details = unknown.map((flag) => {
|
|
10022
|
+
const suggestion = suggestFlag(flag, suggestionPool);
|
|
10023
|
+
return suggestion ? `${flag} (did you mean ${suggestion}?)` : flag;
|
|
10024
|
+
}).join(", ");
|
|
10025
|
+
const label = unknown.length === 1 ? "Unknown flag" : "Unknown flags";
|
|
10026
|
+
throw new CliError({
|
|
10027
|
+
message: `${label} for \`${primaryCommand} ${command}\`: ${details}
|
|
10028
|
+
Run \`${primaryCommand} help\` for usage.`
|
|
10029
|
+
});
|
|
10030
|
+
}
|
|
10031
|
+
|
|
9642
10032
|
// src/help.ts
|
|
9643
10033
|
function printHelp({ primaryCommand }) {
|
|
9644
10034
|
console.log(`Overlord CLI
|
|
@@ -9663,6 +10053,8 @@ General:
|
|
|
9663
10053
|
${primaryCommand} serve [--host <h>] [--port <p>] [--db <path>] [--json]
|
|
9664
10054
|
Boot the web/REST server (creates + migrates the DB on first run)
|
|
9665
10055
|
${primaryCommand} doctor [--json] Validate backend and connector installs
|
|
10056
|
+
${primaryCommand} contract check <manifest-path> [--json]
|
|
10057
|
+
Validate a conformance-manifest.yaml against the contract schema
|
|
9666
10058
|
${primaryCommand} prune [--json] Delete the contents of .overlord/tmp in the current directory
|
|
9667
10059
|
${primaryCommand} config list [--json] Show local configuration
|
|
9668
10060
|
${primaryCommand} config set Choose local/cloud backend interactively
|
|
@@ -9750,7 +10142,8 @@ var DB_FREE_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
9750
10142
|
"config",
|
|
9751
10143
|
"auth",
|
|
9752
10144
|
"user-token",
|
|
9753
|
-
"prune"
|
|
10145
|
+
"prune",
|
|
10146
|
+
"contract"
|
|
9754
10147
|
]);
|
|
9755
10148
|
var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
|
|
9756
10149
|
...DB_FREE_COMMANDS,
|
|
@@ -9791,6 +10184,9 @@ async function dispatchCommand({
|
|
|
9791
10184
|
args,
|
|
9792
10185
|
stdin
|
|
9793
10186
|
}) {
|
|
10187
|
+
if (command !== void 0) {
|
|
10188
|
+
assertKnownFlags({ command, flags: parseArgs(args).flags, primaryCommand });
|
|
10189
|
+
}
|
|
9794
10190
|
switch (command) {
|
|
9795
10191
|
case void 0:
|
|
9796
10192
|
case "help":
|
|
@@ -9825,6 +10221,15 @@ async function dispatchCommand({
|
|
|
9825
10221
|
await runServeCommand2({ rest: args });
|
|
9826
10222
|
return;
|
|
9827
10223
|
}
|
|
10224
|
+
case "contract": {
|
|
10225
|
+
const [subcommand, ...rest] = args;
|
|
10226
|
+
if (!subcommand) {
|
|
10227
|
+
throw new CliError({ message: "Usage: ovld contract <subcommand> [flags]" });
|
|
10228
|
+
}
|
|
10229
|
+
const { runContractCommand: runContractCommand2 } = await Promise.resolve().then(() => (init_contract(), contract_exports));
|
|
10230
|
+
await runContractCommand2({ subcommand, args: rest, primaryCommand });
|
|
10231
|
+
return;
|
|
10232
|
+
}
|
|
9828
10233
|
case "protocol": {
|
|
9829
10234
|
const [subcommand, ...rest] = args;
|
|
9830
10235
|
if (!subcommand) {
|