@mrciphersmith/keryx 0.2.96 → 0.2.97
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1052 -481
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -40253,9 +40253,9 @@ var init_memory = __esm(() => {
|
|
|
40253
40253
|
|
|
40254
40254
|
// src/lib/serve-config.ts
|
|
40255
40255
|
import { existsSync as existsSync25 } from "fs";
|
|
40256
|
-
import
|
|
40256
|
+
import path174 from "path";
|
|
40257
40257
|
function serveConfigPath(dir) {
|
|
40258
|
-
return
|
|
40258
|
+
return path174.join(keryxConfigDir(dir), "serve.json");
|
|
40259
40259
|
}
|
|
40260
40260
|
function parseIpv4(value) {
|
|
40261
40261
|
const parts = value.split(".");
|
|
@@ -47249,7 +47249,7 @@ import path43 from "path";
|
|
|
47249
47249
|
// package.json
|
|
47250
47250
|
var package_default = {
|
|
47251
47251
|
name: "@mrciphersmith/keryx",
|
|
47252
|
-
version: "0.2.
|
|
47252
|
+
version: "0.2.97",
|
|
47253
47253
|
description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
47254
47254
|
private: false,
|
|
47255
47255
|
publishConfig: {
|
|
@@ -69894,12 +69894,12 @@ function entryProblems(name, entry) {
|
|
|
69894
69894
|
}
|
|
69895
69895
|
function oauthProblems(name, oauth) {
|
|
69896
69896
|
const problems = [];
|
|
69897
|
-
const known = new Set(["clientId", "
|
|
69897
|
+
const known = new Set(["clientId", "scopes", "callbackPort"]);
|
|
69898
69898
|
for (const key of Object.keys(oauth)) {
|
|
69899
69899
|
if (!known.has(key))
|
|
69900
69900
|
problems.push(`server "${name}" oauth has unknown field "${key}"`);
|
|
69901
69901
|
}
|
|
69902
|
-
for (const field of ["clientId"
|
|
69902
|
+
for (const field of ["clientId"]) {
|
|
69903
69903
|
if (oauth[field] !== undefined && typeof oauth[field] !== "string") {
|
|
69904
69904
|
problems.push(`server "${name}" oauth.${field} must be a string`);
|
|
69905
69905
|
}
|
|
@@ -70459,6 +70459,229 @@ function createMcpInteractiveTools(deps) {
|
|
|
70459
70459
|
return [searchTool, useTool];
|
|
70460
70460
|
}
|
|
70461
70461
|
|
|
70462
|
+
// src/mcp-servers/credentials.ts
|
|
70463
|
+
init_config_dir();
|
|
70464
|
+
import path165 from "path";
|
|
70465
|
+
var FORGET_CODE_VERIFIER = { code_verifier: undefined };
|
|
70466
|
+
var CREDENTIALS_SCHEMA_VERSION = 1;
|
|
70467
|
+
function credentialsFile(configDir) {
|
|
70468
|
+
return path165.join(configDir ?? ensureKeryxConfigDir(), "mcp-credentials.json");
|
|
70469
|
+
}
|
|
70470
|
+
function credentialKey(serverName, serverUrl) {
|
|
70471
|
+
return `${serverName}:${serverUrl}`;
|
|
70472
|
+
}
|
|
70473
|
+
function readAll2(configDir) {
|
|
70474
|
+
const file = credentialsFile(configDir);
|
|
70475
|
+
const read = readConfigFile(file);
|
|
70476
|
+
if (!read.ok) {
|
|
70477
|
+
return isDefiniteAbsence(read.reason) ? { file: {} } : { file: {}, problem: `${file} could not be read (${read.reason})` };
|
|
70478
|
+
}
|
|
70479
|
+
try {
|
|
70480
|
+
const parsed = parseJsonTolerant(read.text);
|
|
70481
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
70482
|
+
return { file: {}, problem: `${file} must be a JSON object` };
|
|
70483
|
+
}
|
|
70484
|
+
const parsedFile = parsed;
|
|
70485
|
+
if (typeof parsedFile.schemaVersion === "number" && parsedFile.schemaVersion > CREDENTIALS_SCHEMA_VERSION) {
|
|
70486
|
+
return {
|
|
70487
|
+
file: {},
|
|
70488
|
+
problem: `${file} was written by a newer keryx ` + `(schemaVersion ${parsedFile.schemaVersion} > ${CREDENTIALS_SCHEMA_VERSION}); nothing was changed`
|
|
70489
|
+
};
|
|
70490
|
+
}
|
|
70491
|
+
return { file: parsedFile };
|
|
70492
|
+
} catch (error) {
|
|
70493
|
+
return {
|
|
70494
|
+
file: {},
|
|
70495
|
+
problem: `${file} is not valid JSON (${error instanceof Error ? error.message : String(error)}); nothing was changed`
|
|
70496
|
+
};
|
|
70497
|
+
}
|
|
70498
|
+
}
|
|
70499
|
+
function readCredential(serverName, serverUrl, configDir) {
|
|
70500
|
+
const { file, problem } = readAll2(configDir);
|
|
70501
|
+
const record = file.credentials?.[credentialKey(serverName, serverUrl)];
|
|
70502
|
+
return problem === undefined ? { record } : { record, problem };
|
|
70503
|
+
}
|
|
70504
|
+
function writeCredential(serverName, serverUrl, patch, configDir) {
|
|
70505
|
+
const target = credentialsFile(configDir);
|
|
70506
|
+
const outcome = withFileLock2(`${target}.lock`, () => mergeUnlocked(target, serverName, serverUrl, patch, configDir));
|
|
70507
|
+
if (outcome === null) {
|
|
70508
|
+
return { ok: false, error: `${target} is locked by another keryx; nothing was written` };
|
|
70509
|
+
}
|
|
70510
|
+
return outcome;
|
|
70511
|
+
}
|
|
70512
|
+
function mergeUnlocked(target, serverName, serverUrl, patch, configDir) {
|
|
70513
|
+
const { file, problem } = readAll2(configDir);
|
|
70514
|
+
if (problem !== undefined) {
|
|
70515
|
+
return { ok: false, error: problem };
|
|
70516
|
+
}
|
|
70517
|
+
const key = credentialKey(serverName, serverUrl);
|
|
70518
|
+
const credentials = { ...file.credentials, [key]: { ...file.credentials?.[key], ...patch } };
|
|
70519
|
+
writeOwnerOnlyFileAtomic(target, `${JSON.stringify({ schemaVersion: CREDENTIALS_SCHEMA_VERSION, credentials }, null, 2)}
|
|
70520
|
+
`);
|
|
70521
|
+
return { ok: true, file: target };
|
|
70522
|
+
}
|
|
70523
|
+
function clearCredential(serverName, serverUrl, configDir) {
|
|
70524
|
+
const target = credentialsFile(configDir);
|
|
70525
|
+
const outcome = withFileLock2(`${target}.lock`, () => {
|
|
70526
|
+
const { file, problem } = readAll2(configDir);
|
|
70527
|
+
if (problem !== undefined)
|
|
70528
|
+
return { ok: false, error: problem };
|
|
70529
|
+
const credentials = { ...file.credentials };
|
|
70530
|
+
delete credentials[credentialKey(serverName, serverUrl)];
|
|
70531
|
+
writeOwnerOnlyFileAtomic(target, `${JSON.stringify({ schemaVersion: CREDENTIALS_SCHEMA_VERSION, credentials }, null, 2)}
|
|
70532
|
+
`);
|
|
70533
|
+
return { ok: true, file: target };
|
|
70534
|
+
});
|
|
70535
|
+
if (outcome === null) {
|
|
70536
|
+
return { ok: false, error: `${target} is locked by another keryx; nothing was removed` };
|
|
70537
|
+
}
|
|
70538
|
+
return outcome;
|
|
70539
|
+
}
|
|
70540
|
+
function forgetByName(serverName, configDir) {
|
|
70541
|
+
const target = credentialsFile(configDir);
|
|
70542
|
+
const outcome = withFileLock2(`${target}.lock`, () => {
|
|
70543
|
+
const { file, problem } = readAll2(configDir);
|
|
70544
|
+
if (problem !== undefined)
|
|
70545
|
+
return { count: 0, error: problem };
|
|
70546
|
+
const credentials = { ...file.credentials };
|
|
70547
|
+
let count = 0;
|
|
70548
|
+
for (const key of Object.keys(credentials)) {
|
|
70549
|
+
const separator = key.indexOf(":");
|
|
70550
|
+
if (separator === -1)
|
|
70551
|
+
continue;
|
|
70552
|
+
if (key.slice(0, separator) !== serverName)
|
|
70553
|
+
continue;
|
|
70554
|
+
delete credentials[key];
|
|
70555
|
+
count++;
|
|
70556
|
+
}
|
|
70557
|
+
if (count > 0) {
|
|
70558
|
+
writeOwnerOnlyFileAtomic(target, `${JSON.stringify({ schemaVersion: CREDENTIALS_SCHEMA_VERSION, credentials }, null, 2)}
|
|
70559
|
+
`);
|
|
70560
|
+
}
|
|
70561
|
+
return { count };
|
|
70562
|
+
});
|
|
70563
|
+
return outcome ?? { count: 0, error: `${target} is locked by another keryx; nothing was removed` };
|
|
70564
|
+
}
|
|
70565
|
+
var EXPIRY_MARGIN_MS = 30000;
|
|
70566
|
+
function isExpired(tokens, now) {
|
|
70567
|
+
if (tokens === undefined)
|
|
70568
|
+
return true;
|
|
70569
|
+
if (tokens.expires_at === undefined) {
|
|
70570
|
+
return false;
|
|
70571
|
+
}
|
|
70572
|
+
return tokens.expires_at - EXPIRY_MARGIN_MS <= now;
|
|
70573
|
+
}
|
|
70574
|
+
function describeCredential(record, now) {
|
|
70575
|
+
if (record?.tokens === undefined)
|
|
70576
|
+
return "no stored credential";
|
|
70577
|
+
if (isExpired(record.tokens, now)) {
|
|
70578
|
+
return record.tokens.refresh_token === undefined ? "stored credential has expired; re-authenticate" : "stored credential has expired; it will be refreshed on the next connect";
|
|
70579
|
+
}
|
|
70580
|
+
if (record.tokens.expires_at === undefined)
|
|
70581
|
+
return "stored credential, no stated expiry";
|
|
70582
|
+
const minutes = Math.max(0, Math.round((record.tokens.expires_at - now) / 60000));
|
|
70583
|
+
return `stored credential, valid for about ${minutes} more minute(s)`;
|
|
70584
|
+
}
|
|
70585
|
+
function usesOAuth(server) {
|
|
70586
|
+
if (typeof server.url !== "string" || server.url === "")
|
|
70587
|
+
return false;
|
|
70588
|
+
if (server.oauth === false)
|
|
70589
|
+
return false;
|
|
70590
|
+
if (server.bearer_token_env_var !== undefined && server.bearer_token_env_var !== "")
|
|
70591
|
+
return false;
|
|
70592
|
+
return !Object.keys(server.headers ?? {}).some((name) => name.toLowerCase() === "authorization");
|
|
70593
|
+
}
|
|
70594
|
+
|
|
70595
|
+
// src/mcp-servers/oauth-provider.ts
|
|
70596
|
+
class OAuthInteractionRequiredError extends Error {
|
|
70597
|
+
serverName;
|
|
70598
|
+
constructor(serverName) {
|
|
70599
|
+
super(`server "${serverName}" needs OAuth and this process cannot ask: run \`keryx mcp auth ${serverName}\` in a terminal`);
|
|
70600
|
+
this.serverName = serverName;
|
|
70601
|
+
this.name = "OAuthInteractionRequiredError";
|
|
70602
|
+
}
|
|
70603
|
+
}
|
|
70604
|
+
var CLIENT_NAME = "keryx";
|
|
70605
|
+
function createOAuthProvider(deps) {
|
|
70606
|
+
const now = deps.now ?? Date.now;
|
|
70607
|
+
const read = () => readCredential(deps.serverName, deps.serverUrl, deps.configDir);
|
|
70608
|
+
return {
|
|
70609
|
+
get redirectUrl() {
|
|
70610
|
+
return deps.redirectUrl;
|
|
70611
|
+
},
|
|
70612
|
+
get clientMetadata() {
|
|
70613
|
+
return {
|
|
70614
|
+
client_name: CLIENT_NAME,
|
|
70615
|
+
redirect_uris: deps.redirectUrl === undefined ? [] : [deps.redirectUrl],
|
|
70616
|
+
grant_types: ["authorization_code", "refresh_token"],
|
|
70617
|
+
response_types: ["code"],
|
|
70618
|
+
...deps.scopes === undefined || deps.scopes.length === 0 ? {} : { scope: deps.scopes.join(" ") },
|
|
70619
|
+
token_endpoint_auth_method: "none"
|
|
70620
|
+
};
|
|
70621
|
+
},
|
|
70622
|
+
state() {
|
|
70623
|
+
if (deps.state === undefined) {
|
|
70624
|
+
if (!deps.interactive)
|
|
70625
|
+
throw new OAuthInteractionRequiredError(deps.serverName);
|
|
70626
|
+
throw new Error("no callback listener is running; state is minted by the listener");
|
|
70627
|
+
}
|
|
70628
|
+
return deps.state;
|
|
70629
|
+
},
|
|
70630
|
+
clientInformation() {
|
|
70631
|
+
if (deps.clientId !== undefined && deps.clientId !== "") {
|
|
70632
|
+
return { client_id: deps.clientId };
|
|
70633
|
+
}
|
|
70634
|
+
return read().record?.client;
|
|
70635
|
+
},
|
|
70636
|
+
saveClientInformation(client) {
|
|
70637
|
+
if (!deps.interactive)
|
|
70638
|
+
throw new OAuthInteractionRequiredError(deps.serverName);
|
|
70639
|
+
const write = writeCredential(deps.serverName, deps.serverUrl, { client }, deps.configDir);
|
|
70640
|
+
if (!write.ok)
|
|
70641
|
+
throw new Error(write.error);
|
|
70642
|
+
},
|
|
70643
|
+
tokens() {
|
|
70644
|
+
const stored = read().record?.tokens;
|
|
70645
|
+
if (stored === undefined)
|
|
70646
|
+
return;
|
|
70647
|
+
if (isExpired(stored, now()) && stored.refresh_token === undefined)
|
|
70648
|
+
return;
|
|
70649
|
+
return stored;
|
|
70650
|
+
},
|
|
70651
|
+
saveTokens(sdkTokens) {
|
|
70652
|
+
const tokens = {
|
|
70653
|
+
access_token: sdkTokens.access_token,
|
|
70654
|
+
...sdkTokens.token_type === undefined ? {} : { token_type: sdkTokens.token_type },
|
|
70655
|
+
...sdkTokens.refresh_token === undefined ? {} : { refresh_token: sdkTokens.refresh_token },
|
|
70656
|
+
...sdkTokens.scope === undefined ? {} : { scope: sdkTokens.scope },
|
|
70657
|
+
...sdkTokens.expires_in === undefined ? {} : { expires_at: now() + sdkTokens.expires_in * 1000 }
|
|
70658
|
+
};
|
|
70659
|
+
const write = writeCredential(deps.serverName, deps.serverUrl, { tokens, ...FORGET_CODE_VERIFIER }, deps.configDir);
|
|
70660
|
+
if (!write.ok)
|
|
70661
|
+
throw new Error(write.error);
|
|
70662
|
+
},
|
|
70663
|
+
async redirectToAuthorization(url) {
|
|
70664
|
+
if (!deps.interactive)
|
|
70665
|
+
throw new OAuthInteractionRequiredError(deps.serverName);
|
|
70666
|
+
if (deps.openBrowser === undefined)
|
|
70667
|
+
throw new OAuthInteractionRequiredError(deps.serverName);
|
|
70668
|
+
await deps.openBrowser(url);
|
|
70669
|
+
},
|
|
70670
|
+
saveCodeVerifier(codeVerifier) {
|
|
70671
|
+
const write = writeCredential(deps.serverName, deps.serverUrl, { code_verifier: codeVerifier }, deps.configDir);
|
|
70672
|
+
if (!write.ok)
|
|
70673
|
+
throw new Error(write.error);
|
|
70674
|
+
},
|
|
70675
|
+
codeVerifier() {
|
|
70676
|
+
const verifier = read().record?.code_verifier;
|
|
70677
|
+
if (verifier === undefined) {
|
|
70678
|
+
throw new Error("no PKCE verifier stored; the authorisation was not started by this keryx");
|
|
70679
|
+
}
|
|
70680
|
+
return verifier;
|
|
70681
|
+
}
|
|
70682
|
+
};
|
|
70683
|
+
}
|
|
70684
|
+
|
|
70462
70685
|
// src/mcp-servers/doctor.ts
|
|
70463
70686
|
function redactValues(values) {
|
|
70464
70687
|
const out = {};
|
|
@@ -70555,12 +70778,38 @@ async function diagnose(server, options) {
|
|
|
70555
70778
|
detail: describeHollow(resolved.hollow)
|
|
70556
70779
|
};
|
|
70557
70780
|
}
|
|
70781
|
+
const declaresOAuth = server.raw.oauth !== undefined && server.raw.oauth !== false;
|
|
70782
|
+
if (declaresOAuth && usesOAuth(server.raw)) {
|
|
70783
|
+
const stored = readCredential(server.name, server.url ?? "", options.configDir);
|
|
70784
|
+
if (stored.problem !== undefined) {
|
|
70785
|
+
return { ...base, status: "needs_auth", toolCount: 0, skipped: [], detail: stored.problem };
|
|
70786
|
+
}
|
|
70787
|
+
const tokens = stored.record?.tokens;
|
|
70788
|
+
if (tokens === undefined || isExpired(tokens, (options.now ?? Date.now)()) && tokens.refresh_token === undefined) {
|
|
70789
|
+
return {
|
|
70790
|
+
...base,
|
|
70791
|
+
status: "needs_auth",
|
|
70792
|
+
toolCount: 0,
|
|
70793
|
+
skipped: [],
|
|
70794
|
+
detail: tokens === undefined ? `no stored credential; run \`keryx mcp auth ${server.name}\`` : `stored credential has expired and cannot be refreshed; run \`keryx mcp auth ${server.name}\``
|
|
70795
|
+
};
|
|
70796
|
+
}
|
|
70797
|
+
}
|
|
70558
70798
|
}
|
|
70559
70799
|
const timeoutMs = server.startup_timeout_sec !== undefined ? server.startup_timeout_sec * 1000 : options.connectTimeoutMs ?? DEFAULT_DOCTOR_TIMEOUT_MS;
|
|
70560
70800
|
let connection;
|
|
70561
70801
|
try {
|
|
70562
70802
|
connection = await withTimeout(options.connect(server), timeoutMs, server.name);
|
|
70563
70803
|
} catch (error) {
|
|
70804
|
+
if (base.transport === "http" && usesOAuth(server.raw) && needsAuthorisation(error)) {
|
|
70805
|
+
return {
|
|
70806
|
+
...base,
|
|
70807
|
+
status: "needs_auth",
|
|
70808
|
+
toolCount: 0,
|
|
70809
|
+
skipped: [],
|
|
70810
|
+
detail: `${displayUrl(server.raw.url)} requires authorisation; run \`keryx mcp auth ${server.name}\``
|
|
70811
|
+
};
|
|
70812
|
+
}
|
|
70564
70813
|
return {
|
|
70565
70814
|
...base,
|
|
70566
70815
|
status: "failed",
|
|
@@ -70586,6 +70835,20 @@ async function diagnose(server, options) {
|
|
|
70586
70835
|
} catch {}
|
|
70587
70836
|
}
|
|
70588
70837
|
}
|
|
70838
|
+
function needsAuthorisation(error) {
|
|
70839
|
+
if (httpStatusOf(error) === 401)
|
|
70840
|
+
return true;
|
|
70841
|
+
if (error instanceof OAuthInteractionRequiredError)
|
|
70842
|
+
return true;
|
|
70843
|
+
return DEAD_CREDENTIAL_CODES.has(oauthErrorCodeOf(error));
|
|
70844
|
+
}
|
|
70845
|
+
var DEAD_CREDENTIAL_CODES = new Set(["invalid_grant", "invalid_client"]);
|
|
70846
|
+
function oauthErrorCodeOf(error) {
|
|
70847
|
+
if (typeof error !== "object" || error === null)
|
|
70848
|
+
return "";
|
|
70849
|
+
const code = error.errorCode;
|
|
70850
|
+
return typeof code === "string" ? code : "";
|
|
70851
|
+
}
|
|
70589
70852
|
function explainConnectFailure(transport, server, error) {
|
|
70590
70853
|
const message = sanitiseForDisplay(messageOf(error));
|
|
70591
70854
|
if (transport !== "http")
|
|
@@ -70710,193 +70973,144 @@ function formatDoctorReport(report) {
|
|
|
70710
70973
|
`);
|
|
70711
70974
|
}
|
|
70712
70975
|
|
|
70713
|
-
// src/
|
|
70714
|
-
import
|
|
70715
|
-
|
|
70716
|
-
|
|
70717
|
-
|
|
70718
|
-
var DEFAULT_STARTUP_TIMEOUT_MS = 15000;
|
|
70719
|
-
async function startServers(servers, connect, options = {}) {
|
|
70720
|
-
const concurrency = Math.max(1, options.concurrency ?? DEFAULT_CONNECT_CONCURRENCY);
|
|
70721
|
-
const states = new Map;
|
|
70722
|
-
const catalogs = [];
|
|
70723
|
-
const enabled = [];
|
|
70724
|
-
for (const server of servers) {
|
|
70725
|
-
if (server.enabled) {
|
|
70726
|
-
enabled.push(server);
|
|
70727
|
-
} else {
|
|
70728
|
-
states.set(server.name, { name: server.name, status: "disabled", toolCount: 0 });
|
|
70729
|
-
}
|
|
70976
|
+
// src/lib/oauth/open-url.ts
|
|
70977
|
+
import { spawn as spawn5 } from "child_process";
|
|
70978
|
+
function browserOpenPlan(url, platform = process.platform, env = process.env) {
|
|
70979
|
+
if (url.length === 0) {
|
|
70980
|
+
return;
|
|
70730
70981
|
}
|
|
70731
|
-
|
|
70732
|
-
|
|
70733
|
-
|
|
70734
|
-
|
|
70735
|
-
|
|
70736
|
-
|
|
70737
|
-
|
|
70738
|
-
|
|
70739
|
-
|
|
70740
|
-
|
|
70741
|
-
|
|
70742
|
-
|
|
70982
|
+
if (platform === "darwin") {
|
|
70983
|
+
return { cmd: "open", args: [url] };
|
|
70984
|
+
}
|
|
70985
|
+
if (platform === "win32") {
|
|
70986
|
+
return { cmd: "cmd", args: ["/c", "start", "", url] };
|
|
70987
|
+
}
|
|
70988
|
+
if (typeof env.DISPLAY === "string" && env.DISPLAY.length > 0) {
|
|
70989
|
+
return { cmd: "xdg-open", args: [url] };
|
|
70990
|
+
}
|
|
70991
|
+
if (typeof env.WAYLAND_DISPLAY === "string" && env.WAYLAND_DISPLAY.length > 0) {
|
|
70992
|
+
return { cmd: "xdg-open", args: [url] };
|
|
70993
|
+
}
|
|
70994
|
+
return;
|
|
70995
|
+
}
|
|
70996
|
+
function openVerificationUrl(url, deps = {}) {
|
|
70997
|
+
const plan = browserOpenPlan(url, deps.platform ?? process.platform, deps.env ?? process.env);
|
|
70998
|
+
if (plan === undefined) {
|
|
70999
|
+
return;
|
|
71000
|
+
}
|
|
71001
|
+
const spawnFn = deps.spawn ?? spawn5;
|
|
71002
|
+
try {
|
|
71003
|
+
const child = spawnFn(plan.cmd, plan.args, { stdio: "ignore", detached: true });
|
|
71004
|
+
child.on("error", () => {});
|
|
71005
|
+
child.unref();
|
|
71006
|
+
} catch {}
|
|
71007
|
+
}
|
|
71008
|
+
|
|
71009
|
+
// src/mcp-servers/oauth-callback.ts
|
|
71010
|
+
import { randomBytes as randomBytes4, timingSafeEqual } from "crypto";
|
|
71011
|
+
function matchesState(given, expected) {
|
|
71012
|
+
if (given === null)
|
|
71013
|
+
return false;
|
|
71014
|
+
const a = Buffer.from(given, "utf8");
|
|
71015
|
+
const b = Buffer.from(expected, "utf8");
|
|
71016
|
+
if (a.length !== b.length)
|
|
71017
|
+
return false;
|
|
71018
|
+
return timingSafeEqual(a, b);
|
|
71019
|
+
}
|
|
71020
|
+
var LOOPBACK_HOST = "127.0.0.1";
|
|
71021
|
+
var CALLBACK_TIMEOUT_MS = 5 * 60000;
|
|
71022
|
+
function page(title, detail) {
|
|
71023
|
+
return [
|
|
71024
|
+
"<!doctype html><meta charset=utf-8>",
|
|
71025
|
+
`<title>${title}</title>`,
|
|
71026
|
+
'<body style="font:14px system-ui;padding:3rem;max-width:34rem">',
|
|
71027
|
+
`<h1 style="font-size:1.1rem">${title}</h1>`,
|
|
71028
|
+
`<p>${detail}</p>`,
|
|
71029
|
+
"</body>"
|
|
71030
|
+
].join("");
|
|
71031
|
+
}
|
|
71032
|
+
function startCallbackListener(options = {}) {
|
|
71033
|
+
const state = randomBytes4(32).toString("base64url");
|
|
71034
|
+
const timeoutMs = options.timeoutMs ?? CALLBACK_TIMEOUT_MS;
|
|
71035
|
+
const serve = options.serve ?? Bun.serve;
|
|
71036
|
+
let settle = () => {};
|
|
71037
|
+
const result = new Promise((resolve) => {
|
|
71038
|
+
settle = resolve;
|
|
71039
|
+
});
|
|
71040
|
+
let done = false;
|
|
71041
|
+
let timer;
|
|
71042
|
+
const server = serve({
|
|
71043
|
+
hostname: LOOPBACK_HOST,
|
|
71044
|
+
port: options.port ?? 0,
|
|
71045
|
+
fetch(request) {
|
|
71046
|
+
const url = new URL(request.url);
|
|
71047
|
+
if (done) {
|
|
71048
|
+
return new Response(page("Already handled", "This authorisation was already completed."), {
|
|
71049
|
+
status: 410,
|
|
71050
|
+
headers: { "content-type": "text/html" }
|
|
70743
71051
|
});
|
|
70744
|
-
|
|
70745
|
-
|
|
70746
|
-
|
|
70747
|
-
|
|
70748
|
-
|
|
70749
|
-
|
|
70750
|
-
toolCount: catalog.entries.length,
|
|
70751
|
-
connection
|
|
71052
|
+
}
|
|
71053
|
+
const given = url.searchParams.get("state");
|
|
71054
|
+
if (!matchesState(given, state)) {
|
|
71055
|
+
return new Response(page("Not this flow", "This request did not come from the authorisation keryx started."), {
|
|
71056
|
+
status: 400,
|
|
71057
|
+
headers: { "content-type": "text/html" }
|
|
70752
71058
|
});
|
|
70753
|
-
}
|
|
70754
|
-
|
|
70755
|
-
|
|
70756
|
-
|
|
70757
|
-
|
|
70758
|
-
}
|
|
70759
|
-
|
|
70760
|
-
|
|
70761
|
-
|
|
70762
|
-
|
|
70763
|
-
|
|
71059
|
+
}
|
|
71060
|
+
const error = url.searchParams.get("error");
|
|
71061
|
+
if (error !== null) {
|
|
71062
|
+
const description = url.searchParams.get("error_description");
|
|
71063
|
+
done = true;
|
|
71064
|
+
settle({ ok: false, reason: description === null ? error : `${error}: ${description}` });
|
|
71065
|
+
return new Response(page("Authorisation refused", "You can close this tab and return to keryx."), {
|
|
71066
|
+
status: 200,
|
|
71067
|
+
headers: { "content-type": "text/html" }
|
|
71068
|
+
});
|
|
71069
|
+
}
|
|
71070
|
+
const code = url.searchParams.get("code");
|
|
71071
|
+
if (code === null || code === "") {
|
|
71072
|
+
return new Response(page("No code", "The authorisation server returned no code."), {
|
|
71073
|
+
status: 400,
|
|
71074
|
+
headers: { "content-type": "text/html" }
|
|
70764
71075
|
});
|
|
70765
71076
|
}
|
|
71077
|
+
done = true;
|
|
71078
|
+
settle({ ok: true, code });
|
|
71079
|
+
return new Response(page("Authorised", "keryx has what it needs. You can close this tab."), {
|
|
71080
|
+
status: 200,
|
|
71081
|
+
headers: { "content-type": "text/html" }
|
|
71082
|
+
});
|
|
70766
71083
|
}
|
|
70767
|
-
};
|
|
70768
|
-
|
|
70769
|
-
return {
|
|
70770
|
-
servers: [...states.values()].sort((a, b) => a.name.localeCompare(b.name)),
|
|
70771
|
-
catalog: mergeCatalogs(catalogs)
|
|
70772
|
-
};
|
|
70773
|
-
}
|
|
70774
|
-
async function withTimeout2(work, ms, name, onAbandoned) {
|
|
70775
|
-
let timer;
|
|
70776
|
-
let settled = false;
|
|
70777
|
-
try {
|
|
70778
|
-
return await Promise.race([
|
|
70779
|
-
work,
|
|
70780
|
-
new Promise((_resolve, reject) => {
|
|
70781
|
-
timer = setTimeout(() => reject(new Error(`server "${name}" did not answer within ${ms}ms`)), ms);
|
|
70782
|
-
})
|
|
70783
|
-
]).then((value) => {
|
|
70784
|
-
settled = true;
|
|
70785
|
-
return value;
|
|
70786
|
-
});
|
|
70787
|
-
} finally {
|
|
71084
|
+
});
|
|
71085
|
+
const close = () => {
|
|
70788
71086
|
if (timer !== undefined)
|
|
70789
71087
|
clearTimeout(timer);
|
|
70790
|
-
|
|
70791
|
-
work.then(onAbandoned).catch(() => {});
|
|
70792
|
-
}
|
|
70793
|
-
}
|
|
70794
|
-
}
|
|
70795
|
-
async function closeServers(states) {
|
|
70796
|
-
await Promise.all(states.map(async (state) => {
|
|
71088
|
+
timer = undefined;
|
|
70797
71089
|
try {
|
|
70798
|
-
|
|
71090
|
+
server.stop(true);
|
|
70799
71091
|
} catch {}
|
|
70800
|
-
}
|
|
70801
|
-
|
|
70802
|
-
|
|
70803
|
-
|
|
70804
|
-
|
|
70805
|
-
|
|
70806
|
-
|
|
70807
|
-
|
|
70808
|
-
|
|
70809
|
-
}
|
|
70810
|
-
|
|
70811
|
-
|
|
70812
|
-
|
|
70813
|
-
|
|
70814
|
-
url: server.url ?? null,
|
|
70815
|
-
env: server.env ?? {},
|
|
70816
|
-
cwd: server.cwd ?? null,
|
|
70817
|
-
headers: sortedKeys(server.headers),
|
|
70818
|
-
bearer_token_env_var: server.bearer_token_env_var ?? null
|
|
71092
|
+
};
|
|
71093
|
+
timer = setTimeout(() => {
|
|
71094
|
+
if (done)
|
|
71095
|
+
return;
|
|
71096
|
+
done = true;
|
|
71097
|
+
settle({
|
|
71098
|
+
ok: false,
|
|
71099
|
+
reason: `no response within ${Math.round(timeoutMs / 1000)}s \u2014 the browser tab was never completed`
|
|
71100
|
+
});
|
|
71101
|
+
}, timeoutMs);
|
|
71102
|
+
timer.unref?.();
|
|
71103
|
+
result.then(() => {
|
|
71104
|
+
const drain = setTimeout(close, 50);
|
|
71105
|
+
drain.unref?.();
|
|
70819
71106
|
});
|
|
70820
|
-
return
|
|
70821
|
-
}
|
|
70822
|
-
|
|
70823
|
-
|
|
70824
|
-
|
|
70825
|
-
|
|
70826
|
-
|
|
70827
|
-
}
|
|
70828
|
-
function loadTrustStore(configDir) {
|
|
70829
|
-
const read = readConfigFile(trustFile(configDir));
|
|
70830
|
-
if (!read.ok)
|
|
70831
|
-
return {};
|
|
70832
|
-
try {
|
|
70833
|
-
const parsed = parseJsonTolerant(read.text);
|
|
70834
|
-
const approvals = parsed.approvals;
|
|
70835
|
-
if (approvals === undefined)
|
|
70836
|
-
return {};
|
|
70837
|
-
const clean = {};
|
|
70838
|
-
for (const [key, value] of Object.entries(approvals)) {
|
|
70839
|
-
if (typeof value === "string")
|
|
70840
|
-
clean[key] = value;
|
|
70841
|
-
}
|
|
70842
|
-
return clean;
|
|
70843
|
-
} catch {
|
|
70844
|
-
return {};
|
|
70845
|
-
}
|
|
70846
|
-
}
|
|
70847
|
-
function requiresApproval(server, approvals) {
|
|
70848
|
-
if (!server.projectLocal)
|
|
70849
|
-
return false;
|
|
70850
|
-
return approvals[trustKey(server)] !== serverFingerprint(server);
|
|
70851
|
-
}
|
|
70852
|
-
function approveServer(server, configDir) {
|
|
70853
|
-
const file = trustFile(configDir);
|
|
70854
|
-
const read = readConfigFile(file);
|
|
70855
|
-
if (!read.ok && !isDefiniteAbsence(read.reason)) {
|
|
70856
|
-
return { ok: false, error: `${file} could not be read (${read.reason}); nothing was written` };
|
|
70857
|
-
}
|
|
70858
|
-
const approvals = loadTrustStore(configDir);
|
|
70859
|
-
approvals[trustKey(server)] = serverFingerprint(server);
|
|
70860
|
-
writeOwnerOnlyFileAtomic(file, `${JSON.stringify({ approvals }, null, 2)}
|
|
70861
|
-
`);
|
|
70862
|
-
return { ok: true, file };
|
|
70863
|
-
}
|
|
70864
|
-
function revokeServer(server, configDir) {
|
|
70865
|
-
const file = trustFile(configDir);
|
|
70866
|
-
const approvals = loadTrustStore(configDir);
|
|
70867
|
-
delete approvals[trustKey(server)];
|
|
70868
|
-
writeOwnerOnlyFileAtomic(file, `${JSON.stringify({ approvals }, null, 2)}
|
|
70869
|
-
`);
|
|
70870
|
-
return { ok: true, file };
|
|
70871
|
-
}
|
|
70872
|
-
function describeForApproval(server) {
|
|
70873
|
-
const raw = server.raw;
|
|
70874
|
-
if (raw.url !== undefined && raw.url.length > 0) {
|
|
70875
|
-
const shown = displayUrl(raw.url);
|
|
70876
|
-
const credentials = credentialSummary(raw);
|
|
70877
|
-
return credentials === undefined ? shown : `${shown} [sends ${credentials}]`;
|
|
70878
|
-
}
|
|
70879
|
-
return [raw.command, ...raw.args ?? []].filter(Boolean).join(" ");
|
|
70880
|
-
}
|
|
70881
|
-
function credentialSummary(raw) {
|
|
70882
|
-
const named = new Set;
|
|
70883
|
-
if (raw.bearer_token_env_var !== undefined && raw.bearer_token_env_var !== "") {
|
|
70884
|
-
named.add(raw.bearer_token_env_var);
|
|
70885
|
-
}
|
|
70886
|
-
for (const value of Object.values(raw.headers ?? {})) {
|
|
70887
|
-
for (const match of value.matchAll(/\$\{([A-Za-z_][A-Za-z0-9_]*)/g))
|
|
70888
|
-
named.add(match[1]);
|
|
70889
|
-
}
|
|
70890
|
-
const headerNames = Object.keys(raw.headers ?? {});
|
|
70891
|
-
if (named.size === 0 && headerNames.length === 0)
|
|
70892
|
-
return;
|
|
70893
|
-
const parts = [];
|
|
70894
|
-
if (named.size > 0)
|
|
70895
|
-
parts.push([...named].sort().join(", "));
|
|
70896
|
-
const literal = headerNames.filter((n) => !/\$\{/.test(raw.headers?.[n] ?? ""));
|
|
70897
|
-
if (literal.length > 0)
|
|
70898
|
-
parts.push(`literal header(s) ${literal.sort().join(", ")}`);
|
|
70899
|
-
return parts.join(" + ");
|
|
71107
|
+
return {
|
|
71108
|
+
redirectUrl: `http://${LOOPBACK_HOST}:${server.port}/callback`,
|
|
71109
|
+
boundHost: server.hostname ?? "",
|
|
71110
|
+
state,
|
|
71111
|
+
result,
|
|
71112
|
+
close
|
|
71113
|
+
};
|
|
70900
71114
|
}
|
|
70901
71115
|
|
|
70902
71116
|
// src/mcp-client/wire.ts
|
|
@@ -71015,6 +71229,7 @@ async function connectHttpMcpServer(url, options = {}) {
|
|
|
71015
71229
|
}
|
|
71016
71230
|
const sdk = await loadHttpSdk();
|
|
71017
71231
|
const transport = new sdk.StreamableHTTPClientTransport(parsed, {
|
|
71232
|
+
...options.authProvider === undefined ? {} : { authProvider: options.authProvider },
|
|
71018
71233
|
fetch: (input, init) => fetch(input, { ...init, redirect: "error" }),
|
|
71019
71234
|
requestInit: {
|
|
71020
71235
|
...Object.keys(headers).length > 0 ? { headers } : {},
|
|
@@ -71148,6 +71363,195 @@ async function connectStdioMcpServer(argv, options, handshakeTimeoutMs, signal,
|
|
|
71148
71363
|
};
|
|
71149
71364
|
}
|
|
71150
71365
|
|
|
71366
|
+
// src/mcp-servers/runtime.ts
|
|
71367
|
+
import path167 from "path";
|
|
71368
|
+
|
|
71369
|
+
// src/mcp-servers/manager.ts
|
|
71370
|
+
var DEFAULT_CONNECT_CONCURRENCY = 4;
|
|
71371
|
+
var DEFAULT_STARTUP_TIMEOUT_MS = 15000;
|
|
71372
|
+
async function startServers(servers, connect, options = {}) {
|
|
71373
|
+
const concurrency = Math.max(1, options.concurrency ?? DEFAULT_CONNECT_CONCURRENCY);
|
|
71374
|
+
const states = new Map;
|
|
71375
|
+
const catalogs = [];
|
|
71376
|
+
const enabled = [];
|
|
71377
|
+
for (const server of servers) {
|
|
71378
|
+
if (server.enabled) {
|
|
71379
|
+
enabled.push(server);
|
|
71380
|
+
} else {
|
|
71381
|
+
states.set(server.name, { name: server.name, status: "disabled", toolCount: 0 });
|
|
71382
|
+
}
|
|
71383
|
+
}
|
|
71384
|
+
let next = 0;
|
|
71385
|
+
const worker = async () => {
|
|
71386
|
+
for (;; ) {
|
|
71387
|
+
const index = next++;
|
|
71388
|
+
const server = enabled[index];
|
|
71389
|
+
if (server === undefined)
|
|
71390
|
+
return;
|
|
71391
|
+
const timeoutMs = server.startup_timeout_sec !== undefined ? server.startup_timeout_sec * 1000 : options.defaultStartupTimeoutMs ?? DEFAULT_STARTUP_TIMEOUT_MS;
|
|
71392
|
+
let connection;
|
|
71393
|
+
try {
|
|
71394
|
+
connection = await withTimeout2(connect(server), timeoutMs, server.name, (late) => {
|
|
71395
|
+
late.close().catch(() => {});
|
|
71396
|
+
});
|
|
71397
|
+
const tools = await withTimeout2(connection.listTools(), timeoutMs, server.name);
|
|
71398
|
+
const catalog = catalogForServer(server.name, tools);
|
|
71399
|
+
catalogs.push(catalog);
|
|
71400
|
+
states.set(server.name, {
|
|
71401
|
+
name: server.name,
|
|
71402
|
+
status: "connected",
|
|
71403
|
+
toolCount: catalog.entries.length,
|
|
71404
|
+
connection
|
|
71405
|
+
});
|
|
71406
|
+
} catch (error) {
|
|
71407
|
+
if (connection !== undefined) {
|
|
71408
|
+
try {
|
|
71409
|
+
await connection.close();
|
|
71410
|
+
} catch {}
|
|
71411
|
+
}
|
|
71412
|
+
states.set(server.name, {
|
|
71413
|
+
name: server.name,
|
|
71414
|
+
status: "failed",
|
|
71415
|
+
error: error instanceof Error ? error.message : String(error),
|
|
71416
|
+
toolCount: 0
|
|
71417
|
+
});
|
|
71418
|
+
}
|
|
71419
|
+
}
|
|
71420
|
+
};
|
|
71421
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, Math.max(enabled.length, 1)) }, worker));
|
|
71422
|
+
return {
|
|
71423
|
+
servers: [...states.values()].sort((a, b) => a.name.localeCompare(b.name)),
|
|
71424
|
+
catalog: mergeCatalogs(catalogs)
|
|
71425
|
+
};
|
|
71426
|
+
}
|
|
71427
|
+
async function withTimeout2(work, ms, name, onAbandoned) {
|
|
71428
|
+
let timer;
|
|
71429
|
+
let settled = false;
|
|
71430
|
+
try {
|
|
71431
|
+
return await Promise.race([
|
|
71432
|
+
work,
|
|
71433
|
+
new Promise((_resolve, reject) => {
|
|
71434
|
+
timer = setTimeout(() => reject(new Error(`server "${name}" did not answer within ${ms}ms`)), ms);
|
|
71435
|
+
})
|
|
71436
|
+
]).then((value) => {
|
|
71437
|
+
settled = true;
|
|
71438
|
+
return value;
|
|
71439
|
+
});
|
|
71440
|
+
} finally {
|
|
71441
|
+
if (timer !== undefined)
|
|
71442
|
+
clearTimeout(timer);
|
|
71443
|
+
if (!settled && onAbandoned !== undefined) {
|
|
71444
|
+
work.then(onAbandoned).catch(() => {});
|
|
71445
|
+
}
|
|
71446
|
+
}
|
|
71447
|
+
}
|
|
71448
|
+
async function closeServers(states) {
|
|
71449
|
+
await Promise.all(states.map(async (state) => {
|
|
71450
|
+
try {
|
|
71451
|
+
await state.connection?.close();
|
|
71452
|
+
} catch {}
|
|
71453
|
+
}));
|
|
71454
|
+
}
|
|
71455
|
+
|
|
71456
|
+
// src/mcp-servers/trust.ts
|
|
71457
|
+
init_config_dir();
|
|
71458
|
+
import { createHash as createHash31 } from "crypto";
|
|
71459
|
+
import path166 from "path";
|
|
71460
|
+
function trustFile(configDir) {
|
|
71461
|
+
return path166.join(configDir ?? ensureKeryxConfigDir(), "mcp-servers-trust.json");
|
|
71462
|
+
}
|
|
71463
|
+
function serverFingerprint(server) {
|
|
71464
|
+
const material = JSON.stringify({
|
|
71465
|
+
command: server.command ?? null,
|
|
71466
|
+
args: server.args ?? [],
|
|
71467
|
+
url: server.url ?? null,
|
|
71468
|
+
env: server.env ?? {},
|
|
71469
|
+
cwd: server.cwd ?? null,
|
|
71470
|
+
headers: sortedKeys(server.headers),
|
|
71471
|
+
bearer_token_env_var: server.bearer_token_env_var ?? null
|
|
71472
|
+
});
|
|
71473
|
+
return createHash31("sha256").update(material).digest("hex").slice(0, 32);
|
|
71474
|
+
}
|
|
71475
|
+
function sortedKeys(values) {
|
|
71476
|
+
return Object.entries(values ?? {}).sort(([a], [b]) => a.localeCompare(b));
|
|
71477
|
+
}
|
|
71478
|
+
function trustKey(server) {
|
|
71479
|
+
return `${server.file}::${server.name}`;
|
|
71480
|
+
}
|
|
71481
|
+
function loadTrustStore(configDir) {
|
|
71482
|
+
const read = readConfigFile(trustFile(configDir));
|
|
71483
|
+
if (!read.ok)
|
|
71484
|
+
return {};
|
|
71485
|
+
try {
|
|
71486
|
+
const parsed = parseJsonTolerant(read.text);
|
|
71487
|
+
const approvals = parsed.approvals;
|
|
71488
|
+
if (approvals === undefined)
|
|
71489
|
+
return {};
|
|
71490
|
+
const clean = {};
|
|
71491
|
+
for (const [key, value] of Object.entries(approvals)) {
|
|
71492
|
+
if (typeof value === "string")
|
|
71493
|
+
clean[key] = value;
|
|
71494
|
+
}
|
|
71495
|
+
return clean;
|
|
71496
|
+
} catch {
|
|
71497
|
+
return {};
|
|
71498
|
+
}
|
|
71499
|
+
}
|
|
71500
|
+
function requiresApproval(server, approvals) {
|
|
71501
|
+
if (!server.projectLocal)
|
|
71502
|
+
return false;
|
|
71503
|
+
return approvals[trustKey(server)] !== serverFingerprint(server);
|
|
71504
|
+
}
|
|
71505
|
+
function approveServer(server, configDir) {
|
|
71506
|
+
const file = trustFile(configDir);
|
|
71507
|
+
const read = readConfigFile(file);
|
|
71508
|
+
if (!read.ok && !isDefiniteAbsence(read.reason)) {
|
|
71509
|
+
return { ok: false, error: `${file} could not be read (${read.reason}); nothing was written` };
|
|
71510
|
+
}
|
|
71511
|
+
const approvals = loadTrustStore(configDir);
|
|
71512
|
+
approvals[trustKey(server)] = serverFingerprint(server);
|
|
71513
|
+
writeOwnerOnlyFileAtomic(file, `${JSON.stringify({ approvals }, null, 2)}
|
|
71514
|
+
`);
|
|
71515
|
+
return { ok: true, file };
|
|
71516
|
+
}
|
|
71517
|
+
function revokeServer(server, configDir) {
|
|
71518
|
+
const file = trustFile(configDir);
|
|
71519
|
+
const approvals = loadTrustStore(configDir);
|
|
71520
|
+
delete approvals[trustKey(server)];
|
|
71521
|
+
writeOwnerOnlyFileAtomic(file, `${JSON.stringify({ approvals }, null, 2)}
|
|
71522
|
+
`);
|
|
71523
|
+
return { ok: true, file };
|
|
71524
|
+
}
|
|
71525
|
+
function describeForApproval(server) {
|
|
71526
|
+
const raw = server.raw;
|
|
71527
|
+
if (raw.url !== undefined && raw.url.length > 0) {
|
|
71528
|
+
const shown = displayUrl(raw.url);
|
|
71529
|
+
const credentials = credentialSummary(raw);
|
|
71530
|
+
return credentials === undefined ? shown : `${shown} [sends ${credentials}]`;
|
|
71531
|
+
}
|
|
71532
|
+
return [raw.command, ...raw.args ?? []].filter(Boolean).join(" ");
|
|
71533
|
+
}
|
|
71534
|
+
function credentialSummary(raw) {
|
|
71535
|
+
const named = new Set;
|
|
71536
|
+
if (raw.bearer_token_env_var !== undefined && raw.bearer_token_env_var !== "") {
|
|
71537
|
+
named.add(raw.bearer_token_env_var);
|
|
71538
|
+
}
|
|
71539
|
+
for (const value of Object.values(raw.headers ?? {})) {
|
|
71540
|
+
for (const match of value.matchAll(/\$\{([A-Za-z_][A-Za-z0-9_]*)/g))
|
|
71541
|
+
named.add(match[1]);
|
|
71542
|
+
}
|
|
71543
|
+
const headerNames = Object.keys(raw.headers ?? {});
|
|
71544
|
+
if (named.size === 0 && headerNames.length === 0)
|
|
71545
|
+
return;
|
|
71546
|
+
const parts = [];
|
|
71547
|
+
if (named.size > 0)
|
|
71548
|
+
parts.push([...named].sort().join(", "));
|
|
71549
|
+
const literal = headerNames.filter((n) => !/\$\{/.test(raw.headers?.[n] ?? ""));
|
|
71550
|
+
if (literal.length > 0)
|
|
71551
|
+
parts.push(`literal header(s) ${literal.sort().join(", ")}`);
|
|
71552
|
+
return parts.join(" + ");
|
|
71553
|
+
}
|
|
71554
|
+
|
|
71151
71555
|
// src/harness/external/env.ts
|
|
71152
71556
|
var EXTERNAL_ENV_DENY = [
|
|
71153
71557
|
"ANTHROPIC_API_KEY",
|
|
@@ -71376,7 +71780,7 @@ function createMcpRuntime(options) {
|
|
|
71376
71780
|
];
|
|
71377
71781
|
const kills = [];
|
|
71378
71782
|
const env = options.env ?? process.env;
|
|
71379
|
-
const connect = options.connect ?? ((server) => defaultConnect(server, env, dialling.signal, kills));
|
|
71783
|
+
const connect = options.connect ?? ((server) => defaultConnect(server, env, dialling.signal, kills, options.configDir));
|
|
71380
71784
|
const settled = startServers(launchable, connect).then((result) => {
|
|
71381
71785
|
catalog = result.catalog;
|
|
71382
71786
|
states = [...heldStates, ...result.servers].sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -71421,7 +71825,25 @@ function handshakeBudgetMs(server) {
|
|
|
71421
71825
|
}
|
|
71422
71826
|
var MAX_TIMEOUT_MS = 24 * 60 * 60 * 1000;
|
|
71423
71827
|
var DEFAULT_HANDSHAKE_MS = 15000;
|
|
71424
|
-
|
|
71828
|
+
var SESSION_REDIRECT_URL = "http://127.0.0.1/keryx-session-never-listens";
|
|
71829
|
+
function sessionAuthProviderOptions(server, runtimeConfigDir, stored, now = Date.now()) {
|
|
71830
|
+
if (!usesOAuth(server.raw))
|
|
71831
|
+
return;
|
|
71832
|
+
const tokens = stored?.tokens;
|
|
71833
|
+
if (tokens === undefined)
|
|
71834
|
+
return;
|
|
71835
|
+
if (isExpired(tokens, now) && tokens.refresh_token === undefined)
|
|
71836
|
+
return;
|
|
71837
|
+
return {
|
|
71838
|
+
serverName: server.name,
|
|
71839
|
+
serverUrl: server.url,
|
|
71840
|
+
...runtimeConfigDir === undefined ? {} : { configDir: runtimeConfigDir },
|
|
71841
|
+
redirectUrl: SESSION_REDIRECT_URL,
|
|
71842
|
+
interactive: false,
|
|
71843
|
+
...server.oauth === false || server.oauth?.clientId === undefined ? {} : { clientId: server.oauth.clientId }
|
|
71844
|
+
};
|
|
71845
|
+
}
|
|
71846
|
+
async function connectRemote(server, env, signal, runtimeConfigDir) {
|
|
71425
71847
|
const target = remoteTargetProblem(server.raw, env);
|
|
71426
71848
|
if (target !== undefined) {
|
|
71427
71849
|
throw new Error(`server "${server.name}": ${target}`);
|
|
@@ -71430,8 +71852,11 @@ async function connectRemote(server, env, signal) {
|
|
|
71430
71852
|
if (!resolved.ok) {
|
|
71431
71853
|
throw new Error(`server "${server.name}": ${describeHollow(resolved.hollow)}`);
|
|
71432
71854
|
}
|
|
71855
|
+
const options = sessionAuthProviderOptions(server, runtimeConfigDir, server.url === undefined ? undefined : readCredential(server.name, server.url, runtimeConfigDir).record);
|
|
71856
|
+
const authProvider = options === undefined ? undefined : createOAuthProvider(options);
|
|
71433
71857
|
return connectHttpMcpServer(server.url, {
|
|
71434
71858
|
headers: resolved.headers,
|
|
71859
|
+
...authProvider === undefined ? {} : { authProvider },
|
|
71435
71860
|
handshakeTimeoutMs: handshakeBudgetMs(server),
|
|
71436
71861
|
...signal === undefined ? {} : { signal }
|
|
71437
71862
|
});
|
|
@@ -71441,9 +71866,9 @@ function timeoutFor(server, rawName) {
|
|
|
71441
71866
|
return;
|
|
71442
71867
|
return server.tool_timeouts?.[rawName] ?? server.tool_timeout_sec;
|
|
71443
71868
|
}
|
|
71444
|
-
async function defaultConnect(server, env, signal, kills) {
|
|
71869
|
+
async function defaultConnect(server, env, signal, kills, configDir) {
|
|
71445
71870
|
if (transportOf(server) === "http")
|
|
71446
|
-
return connectRemote(server, env, signal);
|
|
71871
|
+
return connectRemote(server, env, signal, configDir);
|
|
71447
71872
|
const command = server.command;
|
|
71448
71873
|
if (command === undefined || command === "") {
|
|
71449
71874
|
throw new Error(`server "${server.name}" sets neither command nor url`);
|
|
@@ -71456,22 +71881,22 @@ async function defaultConnect(server, env, signal, kills) {
|
|
|
71456
71881
|
function defaultServerCwd(server, fallbackCwd = process.cwd()) {
|
|
71457
71882
|
if (server.source !== "project")
|
|
71458
71883
|
return fallbackCwd;
|
|
71459
|
-
return
|
|
71884
|
+
return path167.dirname(path167.dirname(server.file));
|
|
71460
71885
|
}
|
|
71461
71886
|
|
|
71462
71887
|
// src/mcp-servers/store.ts
|
|
71463
71888
|
init_config_dir();
|
|
71464
71889
|
import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
|
|
71465
|
-
import
|
|
71890
|
+
import path168 from "path";
|
|
71466
71891
|
var SERVER_NAME_PATTERN = /^[A-Za-z0-9_-]+$/;
|
|
71467
71892
|
function userConfigFile(configDir) {
|
|
71468
|
-
return
|
|
71893
|
+
return path168.join(configDir ?? ensureKeryxConfigDir(), "mcp-servers.json");
|
|
71469
71894
|
}
|
|
71470
71895
|
function projectConfigFile(projectRoot) {
|
|
71471
|
-
return
|
|
71896
|
+
return path168.join(projectRoot, ".keryx", "mcp-servers.json");
|
|
71472
71897
|
}
|
|
71473
71898
|
function overlayFile(configDir) {
|
|
71474
|
-
return
|
|
71899
|
+
return path168.join(configDir ?? ensureKeryxConfigDir(), "mcp-servers-disabled.json");
|
|
71475
71900
|
}
|
|
71476
71901
|
function readForWrite(file) {
|
|
71477
71902
|
const read = readConfigFile(file);
|
|
@@ -71510,7 +71935,7 @@ function writeDocument(file, doc, scope) {
|
|
|
71510
71935
|
writeOwnerOnlyFileAtomic(file, body);
|
|
71511
71936
|
return;
|
|
71512
71937
|
}
|
|
71513
|
-
mkdirSync6(
|
|
71938
|
+
mkdirSync6(path168.dirname(file), { recursive: true });
|
|
71514
71939
|
writeFileSync7(file, body, { encoding: "utf8" });
|
|
71515
71940
|
}
|
|
71516
71941
|
function addServer(options) {
|
|
@@ -71614,7 +72039,9 @@ var MCP_CONSUMER_SUBCOMMANDS = [
|
|
|
71614
72039
|
"disable",
|
|
71615
72040
|
"trust",
|
|
71616
72041
|
"untrust",
|
|
71617
|
-
"doctor"
|
|
72042
|
+
"doctor",
|
|
72043
|
+
"auth",
|
|
72044
|
+
"logout"
|
|
71618
72045
|
];
|
|
71619
72046
|
function isMcpConsumerSubcommand(value) {
|
|
71620
72047
|
return MCP_CONSUMER_SUBCOMMANDS.includes(value ?? "");
|
|
@@ -71637,6 +72064,10 @@ async function runMcpConsumerCommand(subcommand, args, deps) {
|
|
|
71637
72064
|
return trustCommand(args, deps, false);
|
|
71638
72065
|
case "doctor":
|
|
71639
72066
|
return doctorCommand(args, deps);
|
|
72067
|
+
case "auth":
|
|
72068
|
+
return authCommand(args, deps);
|
|
72069
|
+
case "logout":
|
|
72070
|
+
return logoutCommand(args, deps);
|
|
71640
72071
|
}
|
|
71641
72072
|
}
|
|
71642
72073
|
function projectRootOf(deps) {
|
|
@@ -71778,6 +72209,178 @@ function addCommand(argv, deps) {
|
|
|
71778
72209
|
deps.log(`Run \`keryx mcp doctor ${name}\` to check it connects.`);
|
|
71779
72210
|
return 0;
|
|
71780
72211
|
}
|
|
72212
|
+
function logoutCommand(args, deps) {
|
|
72213
|
+
const own = splitAtSeparator(args).own;
|
|
72214
|
+
const name = own[0];
|
|
72215
|
+
if (name === undefined || name.startsWith("-")) {
|
|
72216
|
+
deps.err("usage: keryx mcp logout <name>");
|
|
72217
|
+
return 1;
|
|
72218
|
+
}
|
|
72219
|
+
const config = load(deps);
|
|
72220
|
+
const server = config.servers.find((candidate) => candidate.name === name);
|
|
72221
|
+
const url = server?.url;
|
|
72222
|
+
if (url === undefined) {
|
|
72223
|
+
const removed = forgetByName(name, deps.configDir);
|
|
72224
|
+
if (removed.count === 0) {
|
|
72225
|
+
deps.err(`no stored credential for "${name}".`);
|
|
72226
|
+
return 1;
|
|
72227
|
+
}
|
|
72228
|
+
deps.log(`Forgot ${removed.count} stored credential(s) for "${name}".`);
|
|
72229
|
+
return 0;
|
|
72230
|
+
}
|
|
72231
|
+
const existing = readCredential(name, url, deps.configDir);
|
|
72232
|
+
if (existing.problem !== undefined) {
|
|
72233
|
+
deps.err(existing.problem);
|
|
72234
|
+
return 1;
|
|
72235
|
+
}
|
|
72236
|
+
if (existing.record === undefined) {
|
|
72237
|
+
deps.log(`"${name}" has no stored credential; nothing to forget.`);
|
|
72238
|
+
return 0;
|
|
72239
|
+
}
|
|
72240
|
+
const cleared = clearCredential(name, url, deps.configDir);
|
|
72241
|
+
if (!cleared.ok) {
|
|
72242
|
+
deps.err(cleared.error);
|
|
72243
|
+
return 1;
|
|
72244
|
+
}
|
|
72245
|
+
deps.log(`Forgot the stored credential for "${name}".`);
|
|
72246
|
+
deps.log(`Run \`keryx mcp auth ${name}\` to authorise again.`);
|
|
72247
|
+
return 0;
|
|
72248
|
+
}
|
|
72249
|
+
async function authCommand(args, deps) {
|
|
72250
|
+
const own = splitAtSeparator(args).own;
|
|
72251
|
+
const unknown = own.filter((arg, index, all) => isUnknownFlag(arg, index, all));
|
|
72252
|
+
if (unknown.length > 0) {
|
|
72253
|
+
deps.err(`unknown option ${unknown[0]}`);
|
|
72254
|
+
return 1;
|
|
72255
|
+
}
|
|
72256
|
+
const name = positional3(args);
|
|
72257
|
+
if (name === undefined) {
|
|
72258
|
+
deps.err("usage: keryx mcp auth <name>");
|
|
72259
|
+
return 1;
|
|
72260
|
+
}
|
|
72261
|
+
const config = load(deps);
|
|
72262
|
+
const server = config.servers.find((candidate) => candidate.name === name);
|
|
72263
|
+
if (server === undefined) {
|
|
72264
|
+
deps.err(config.servers.length === 0 ? `no server named "${name}" \u2014 no MCP servers are configured` : `no server named "${name}". Configured: ${config.servers.map((s) => s.name).join(", ")}`);
|
|
72265
|
+
return 1;
|
|
72266
|
+
}
|
|
72267
|
+
if (transportOf(server) !== "http") {
|
|
72268
|
+
deps.err(`"${name}" is a local (stdio) server; OAuth applies to remote ones.`);
|
|
72269
|
+
return 1;
|
|
72270
|
+
}
|
|
72271
|
+
if (requiresApproval(server, loadTrustStore(deps.configDir))) {
|
|
72272
|
+
deps.err(`"${name}" is a project server that has not been approved.`);
|
|
72273
|
+
deps.err(`Read what it connects to, then: keryx mcp trust ${name}`);
|
|
72274
|
+
return 1;
|
|
72275
|
+
}
|
|
72276
|
+
const env = deps.env ?? process.env;
|
|
72277
|
+
const targetProblem = remoteTargetProblem(server.raw, env);
|
|
72278
|
+
if (targetProblem !== undefined) {
|
|
72279
|
+
deps.err(`"${name}": ${targetProblem}`);
|
|
72280
|
+
return 1;
|
|
72281
|
+
}
|
|
72282
|
+
if (!usesOAuth(server.raw)) {
|
|
72283
|
+
deps.err(`"${name}" does not use OAuth \u2014 it authenticates with a header or bearer_token_env_var.`);
|
|
72284
|
+
deps.err("Nothing to do. Remove that credential first if you meant to switch to OAuth.");
|
|
72285
|
+
return 1;
|
|
72286
|
+
}
|
|
72287
|
+
const interactive = resolveInteractive(deps.interactive, process.stdin, process.stdout);
|
|
72288
|
+
if (!interactive) {
|
|
72289
|
+
deps.err(`"${name}" needs OAuth, and this process has no terminal to ask in.`);
|
|
72290
|
+
deps.err("Run `keryx mcp auth " + name + "` from an interactive shell.");
|
|
72291
|
+
return 1;
|
|
72292
|
+
}
|
|
72293
|
+
deps.log(`Authorising "${name}" at ${displayUrl(server.raw.url)}\u2026`);
|
|
72294
|
+
deps.log("A browser window will open. keryx never sees your password \u2014 only the token the server issues.");
|
|
72295
|
+
return await runOAuthFlow(server, deps);
|
|
72296
|
+
}
|
|
72297
|
+
async function runOAuthFlow(server, deps) {
|
|
72298
|
+
const configuredPort = server.oauth === false ? undefined : server.oauth?.callbackPort;
|
|
72299
|
+
const listener = startCallbackListener(configuredPort === undefined ? {} : { port: configuredPort });
|
|
72300
|
+
try {
|
|
72301
|
+
const provider = createOAuthProvider({
|
|
72302
|
+
serverName: server.name,
|
|
72303
|
+
serverUrl: server.url,
|
|
72304
|
+
...deps.configDir === undefined ? {} : { configDir: deps.configDir },
|
|
72305
|
+
interactive: true,
|
|
72306
|
+
openBrowser: deps.openBrowser ?? ((url) => {
|
|
72307
|
+
openAuthorisationUrl(url, deps.log);
|
|
72308
|
+
}),
|
|
72309
|
+
redirectUrl: listener.redirectUrl,
|
|
72310
|
+
state: listener.state,
|
|
72311
|
+
...server.oauth === false || server.oauth?.clientId === undefined ? {} : { clientId: server.oauth.clientId },
|
|
72312
|
+
...server.oauth === false || server.oauth?.scopes === undefined ? {} : { scopes: server.oauth.scopes }
|
|
72313
|
+
});
|
|
72314
|
+
let opened = false;
|
|
72315
|
+
const legOne = await connectHttpMcpServer(server.url, {
|
|
72316
|
+
authProvider: {
|
|
72317
|
+
...provider,
|
|
72318
|
+
redirectToAuthorization: async (url) => {
|
|
72319
|
+
opened = true;
|
|
72320
|
+
await provider.redirectToAuthorization(url);
|
|
72321
|
+
}
|
|
72322
|
+
},
|
|
72323
|
+
handshakeTimeoutMs: 30000
|
|
72324
|
+
}).then((connection) => ({ ok: true, connection }), (error) => ({ ok: false, error }));
|
|
72325
|
+
if (legOne.ok) {
|
|
72326
|
+
await legOne.connection.close();
|
|
72327
|
+
const existing = readCredential(server.name, server.url, deps.configDir).record;
|
|
72328
|
+
if (existing?.tokens === undefined) {
|
|
72329
|
+
deps.log(`"${server.name}" connects without authentication; there is nothing to authorise.`);
|
|
72330
|
+
} else {
|
|
72331
|
+
deps.log(`"${server.name}" is already authorised; nothing to do.`);
|
|
72332
|
+
}
|
|
72333
|
+
return 0;
|
|
72334
|
+
}
|
|
72335
|
+
if (!opened) {
|
|
72336
|
+
const message = legOne.error instanceof Error ? legOne.error.message : String(legOne.error);
|
|
72337
|
+
deps.err(`Could not start authorisation for "${server.name}": ${sanitiseForDisplay(message)}`);
|
|
72338
|
+
return 1;
|
|
72339
|
+
}
|
|
72340
|
+
const result = await listener.result;
|
|
72341
|
+
if (!result.ok) {
|
|
72342
|
+
deps.err(`Authorisation did not complete: ${result.reason}`);
|
|
72343
|
+
return 1;
|
|
72344
|
+
}
|
|
72345
|
+
const sdk = await loadAuthSdk();
|
|
72346
|
+
await sdk.auth(provider, { serverUrl: server.url, authorizationCode: result.code });
|
|
72347
|
+
const stored = readCredential(server.name, server.url, deps.configDir).record;
|
|
72348
|
+
if (stored?.tokens === undefined || stored.tokens.access_token === "") {
|
|
72349
|
+
deps.err("The exchange returned no token. Nothing was stored.");
|
|
72350
|
+
return 1;
|
|
72351
|
+
}
|
|
72352
|
+
deps.log(`Authorised "${server.name}" \u2014 ${describeCredential(stored, Date.now())}.`);
|
|
72353
|
+
deps.log(`Stored owner-only in ${credentialsFile(deps.configDir)}.`);
|
|
72354
|
+
return 0;
|
|
72355
|
+
} catch (error) {
|
|
72356
|
+
deps.err(`Authorisation failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
72357
|
+
return 1;
|
|
72358
|
+
} finally {
|
|
72359
|
+
listener.close();
|
|
72360
|
+
}
|
|
72361
|
+
}
|
|
72362
|
+
async function loadAuthSdk() {
|
|
72363
|
+
const mod = await import("@modelcontextprotocol/sdk/client/auth.js");
|
|
72364
|
+
return mod;
|
|
72365
|
+
}
|
|
72366
|
+
function resolveInteractive(explicit, stdin, stdout) {
|
|
72367
|
+
return explicit ?? bothStreamsAreATerminal(stdin, stdout);
|
|
72368
|
+
}
|
|
72369
|
+
function bothStreamsAreATerminal(stdin, stdout) {
|
|
72370
|
+
return stdin.isTTY === true && stdout.isTTY === true;
|
|
72371
|
+
}
|
|
72372
|
+
function openAuthorisationUrl(url, log, platform = process.platform, env = process.env) {
|
|
72373
|
+
const plan = browserOpenPlan(url.toString(), platform, env);
|
|
72374
|
+
if (plan === undefined) {
|
|
72375
|
+
log("No graphical session detected, so nothing was opened.");
|
|
72376
|
+
log(`Open this URL yourself to continue:
|
|
72377
|
+
|
|
72378
|
+
${url.toString()}
|
|
72379
|
+
`);
|
|
72380
|
+
return;
|
|
72381
|
+
}
|
|
72382
|
+
openVerificationUrl(url.toString());
|
|
72383
|
+
}
|
|
71781
72384
|
function removeCommand(args, deps) {
|
|
71782
72385
|
const own = splitAtSeparator(args).own;
|
|
71783
72386
|
const unknown = own.filter((arg, index, all) => isUnknownFlag(arg, index, all));
|
|
@@ -71928,9 +72531,10 @@ async function doctorCommand(args, deps) {
|
|
|
71928
72531
|
const env = deps.env ?? process.env;
|
|
71929
72532
|
report = await runDoctor2(config, {
|
|
71930
72533
|
only,
|
|
71931
|
-
connect: deps.connect ?? ((server) => defaultConnect(server, env, dialling.signal, kills)),
|
|
72534
|
+
connect: deps.connect ?? ((server) => defaultConnect(server, env, dialling.signal, kills, deps.configDir)),
|
|
71932
72535
|
heldForApproval: (server) => requiresApproval(server, approvals),
|
|
71933
|
-
env
|
|
72536
|
+
env,
|
|
72537
|
+
configDir: deps.configDir
|
|
71934
72538
|
});
|
|
71935
72539
|
} finally {
|
|
71936
72540
|
process.removeListener("SIGINT", onInt);
|
|
@@ -72037,12 +72641,12 @@ function positional3(args) {
|
|
|
72037
72641
|
|
|
72038
72642
|
// src/commands/serve-mcp.ts
|
|
72039
72643
|
init_args();
|
|
72040
|
-
import
|
|
72644
|
+
import path175 from "path";
|
|
72041
72645
|
|
|
72042
72646
|
// src/mcp/discovery.ts
|
|
72043
72647
|
init_fs();
|
|
72044
72648
|
init_json();
|
|
72045
|
-
import
|
|
72649
|
+
import path169 from "path";
|
|
72046
72650
|
var MODULE_MANIFEST_KEY = {
|
|
72047
72651
|
gdgraph: "gdgraph",
|
|
72048
72652
|
security: "security",
|
|
@@ -72104,7 +72708,7 @@ function buildDiscovery(manifest) {
|
|
|
72104
72708
|
};
|
|
72105
72709
|
}
|
|
72106
72710
|
async function loadDiscovery(cwd) {
|
|
72107
|
-
const manifestPath =
|
|
72711
|
+
const manifestPath = path169.join(cwd, ".metaproject", "metaproject.json");
|
|
72108
72712
|
if (!await pathExists(manifestPath)) {
|
|
72109
72713
|
return buildDiscovery({});
|
|
72110
72714
|
}
|
|
@@ -72177,7 +72781,7 @@ init_store2();
|
|
|
72177
72781
|
init_fs();
|
|
72178
72782
|
import { createHash as createHash34, randomUUID as randomUUID17 } from "crypto";
|
|
72179
72783
|
import { appendFile as appendFile7, mkdir as mkdir58, open as open2, readFile as readFile93, rename as rename7, rm as rm12, stat as stat16, writeFile as writeFile50 } from "fs/promises";
|
|
72180
|
-
import
|
|
72784
|
+
import path170 from "path";
|
|
72181
72785
|
|
|
72182
72786
|
// src/sac/policy-experiment.ts
|
|
72183
72787
|
import { createHash as createHash33, createHmac as createHmac2 } from "crypto";
|
|
@@ -72533,7 +73137,7 @@ function hasReportIntegrity(report) {
|
|
|
72533
73137
|
return reportHashPattern.test(reportDigest) && sha2565(body) === reportDigest;
|
|
72534
73138
|
}
|
|
72535
73139
|
async function resolvePolicySelection(workspaceRoot, canonicalFallback) {
|
|
72536
|
-
const configPath =
|
|
73140
|
+
const configPath = path170.join(workspaceRoot, ...policyExperimentConfigPath);
|
|
72537
73141
|
let configJson;
|
|
72538
73142
|
try {
|
|
72539
73143
|
const raw = await readFile93(configPath, "utf8").catch((error) => {
|
|
@@ -72635,7 +73239,7 @@ async function diagnosePolicyReadiness(workspaceRoot) {
|
|
|
72635
73239
|
const integrityReady = steps.every((entry) => entry.step === "activation-flags" || entry.status === "pass");
|
|
72636
73240
|
return Object.freeze({ configPresent, enabled, killSwitch, integrityReady, candidateWouldActivate: integrityReady && enabled && !killSwitch, steps: Object.freeze([...steps]) });
|
|
72637
73241
|
};
|
|
72638
|
-
const configPath =
|
|
73242
|
+
const configPath = path170.join(workspaceRoot, ...policyExperimentConfigPath);
|
|
72639
73243
|
let configJson;
|
|
72640
73244
|
try {
|
|
72641
73245
|
const raw = await readFile93(configPath, "utf8").catch((error) => {
|
|
@@ -72890,9 +73494,9 @@ class FwkReadService {
|
|
|
72890
73494
|
const recordedAt = nowIso2(now);
|
|
72891
73495
|
const id = `receipt-${randomUUID17().replace(/-/g, "").slice(0, 16)}`;
|
|
72892
73496
|
const base = { schemaVersion: "1.0", id, workspaceId, actor, action, decision, recordedAt, cost: { tokens: 0, toolCalls: 1, elapsedMs: 0 }, contextAssembly: { traceRef: assembly.traceRef, configurationRevision: assembly.configurationRevision, selected: assembly.selected.map((id) => `./ids/${id}`), omittedOptional: assembly.omittedOptional.map((id) => `./ids/${id}`) }, policy: { ref: assembly.policyRef, revision: assembly.policyRevision }, ...action === "resource" ? { resourceRef: `./ids/${resourceId ?? "unknown"}` } : {} };
|
|
72893
|
-
const ledger =
|
|
72894
|
-
const checkpointPath =
|
|
72895
|
-
await mkdir58(
|
|
73497
|
+
const ledger = path170.join(this.options.canonical.workspaceRoot, ".metaproject", "context-operations", "access-receipts.jsonl");
|
|
73498
|
+
const checkpointPath = path170.join(path170.dirname(ledger), "access-receipts.checkpoint.json");
|
|
73499
|
+
await mkdir58(path170.dirname(ledger), { recursive: true, mode: 448 });
|
|
72896
73500
|
return withFileLock(`${ledger}.lock`, async () => {
|
|
72897
73501
|
const state = await resolveLedgerState(ledger, checkpointPath, this.options.verifyReceiptLedger ?? verifyAccessReceiptLedger);
|
|
72898
73502
|
const receipt = sealAccessReceipt(base, state.headHash);
|
|
@@ -73004,7 +73608,7 @@ init_workspace_service();
|
|
|
73004
73608
|
init_sac();
|
|
73005
73609
|
import { appendFile as appendFile8, mkdir as mkdir59, readFile as readFile94 } from "fs/promises";
|
|
73006
73610
|
import { randomUUID as randomUUID18 } from "crypto";
|
|
73007
|
-
import
|
|
73611
|
+
import path171 from "path";
|
|
73008
73612
|
|
|
73009
73613
|
class CollaborationServiceError extends Error {
|
|
73010
73614
|
code;
|
|
@@ -73020,7 +73624,7 @@ class CollaborationService {
|
|
|
73020
73624
|
this.input = input;
|
|
73021
73625
|
}
|
|
73022
73626
|
file(id) {
|
|
73023
|
-
return
|
|
73627
|
+
return path171.join(this.input.workspaceRoot, ".metaproject", "workspaces", id, "activity.jsonl");
|
|
73024
73628
|
}
|
|
73025
73629
|
async overview(input) {
|
|
73026
73630
|
const manifest = await this.input.workspaces.show(input);
|
|
@@ -73057,7 +73661,7 @@ class CollaborationService {
|
|
|
73057
73661
|
occurredAt: (this.input.now ?? (() => new Date))().toISOString(),
|
|
73058
73662
|
...suppliedHandoff ? { handoff: { ...suppliedHandoff, from: actor.subject } } : {}
|
|
73059
73663
|
});
|
|
73060
|
-
await mkdir59(
|
|
73664
|
+
await mkdir59(path171.dirname(this.file(manifest.id)), { recursive: true, mode: 448 });
|
|
73061
73665
|
await appendFile8(this.file(manifest.id), `${JSON.stringify(activity)}
|
|
73062
73666
|
`, { mode: 384 });
|
|
73063
73667
|
return normalizeCollaborationResult(activity);
|
|
@@ -73112,9 +73716,9 @@ init_workspace_service();
|
|
|
73112
73716
|
init_fs();
|
|
73113
73717
|
init_machine_wrap_up();
|
|
73114
73718
|
import { mkdir as mkdir60, readdir as readdir31, readFile as readFile95 } from "fs/promises";
|
|
73115
|
-
import
|
|
73719
|
+
import path172 from "path";
|
|
73116
73720
|
function externalSlatesDir(cwd) {
|
|
73117
|
-
return
|
|
73721
|
+
return path172.join(cwd, ".keryx", "external-slates");
|
|
73118
73722
|
}
|
|
73119
73723
|
var EXTERNAL_SESSION_ID_PATTERN = /^[A-Za-z0-9_-]{1,128}$/;
|
|
73120
73724
|
function assertValidExternalSessionId(externalSessionId) {
|
|
@@ -73124,14 +73728,14 @@ function assertValidExternalSessionId(externalSessionId) {
|
|
|
73124
73728
|
}
|
|
73125
73729
|
function externalSlatePath(cwd, externalSessionId) {
|
|
73126
73730
|
assertValidExternalSessionId(externalSessionId);
|
|
73127
|
-
return
|
|
73731
|
+
return path172.join(externalSlatesDir(cwd), `${externalSessionId}.json`);
|
|
73128
73732
|
}
|
|
73129
73733
|
function externalSlateLockPath(cwd, externalSessionId) {
|
|
73130
73734
|
return `${externalSlatePath(cwd, externalSessionId)}.lock`;
|
|
73131
73735
|
}
|
|
73132
73736
|
function externalSlateEvidenceDir(cwd, externalSessionId) {
|
|
73133
73737
|
assertValidExternalSessionId(externalSessionId);
|
|
73134
|
-
return
|
|
73738
|
+
return path172.join(externalSlatesDir(cwd), externalSessionId);
|
|
73135
73739
|
}
|
|
73136
73740
|
async function readExternalSlate(cwd, externalSessionId) {
|
|
73137
73741
|
try {
|
|
@@ -73776,7 +74380,7 @@ function buildToolRegistry() {
|
|
|
73776
74380
|
// src/mcp/resources.ts
|
|
73777
74381
|
init_fs();
|
|
73778
74382
|
init_contained_read();
|
|
73779
|
-
import
|
|
74383
|
+
import path173 from "path";
|
|
73780
74384
|
import { lstat as lstat4, readdir as readdir32, realpath as realpath7, stat as stat17 } from "fs/promises";
|
|
73781
74385
|
var URI_PREFIX = "metaproject://";
|
|
73782
74386
|
function mimeForPath(filePath) {
|
|
@@ -73789,13 +74393,13 @@ function mimeForPath(filePath) {
|
|
|
73789
74393
|
return "text/plain";
|
|
73790
74394
|
}
|
|
73791
74395
|
function dataRoot3(cwd) {
|
|
73792
|
-
return
|
|
74396
|
+
return path173.join(cwd, ".metaproject", "data");
|
|
73793
74397
|
}
|
|
73794
74398
|
function wikiRoot(cwd) {
|
|
73795
|
-
return
|
|
74399
|
+
return path173.join(cwd, ".metaproject", "wiki");
|
|
73796
74400
|
}
|
|
73797
74401
|
function memoryRoot2(cwd) {
|
|
73798
|
-
return
|
|
74402
|
+
return path173.join(cwd, ".metaproject", "memory");
|
|
73799
74403
|
}
|
|
73800
74404
|
var MAX_RESOURCE_BYTES = 8 * 1024 * 1024;
|
|
73801
74405
|
async function walkFiles(root, ownerRoot = root, ancestors = new Set) {
|
|
@@ -73821,7 +74425,7 @@ async function walkFiles(root, ownerRoot = root, ancestors = new Set) {
|
|
|
73821
74425
|
return [];
|
|
73822
74426
|
}
|
|
73823
74427
|
for (const entry of entries) {
|
|
73824
|
-
const full =
|
|
74428
|
+
const full = path173.join(root, entry.name);
|
|
73825
74429
|
const info = await stat17(full).catch(() => null);
|
|
73826
74430
|
if (!info || !isPathInside(ownerReal, await realpath7(full).catch(() => "")))
|
|
73827
74431
|
continue;
|
|
@@ -73849,9 +74453,9 @@ async function listArtifacts(cwd) {
|
|
|
73849
74453
|
if (!moduleEntry.isDirectory()) {
|
|
73850
74454
|
continue;
|
|
73851
74455
|
}
|
|
73852
|
-
const artifactsDir =
|
|
74456
|
+
const artifactsDir = path173.join(base, moduleEntry.name, "artifacts");
|
|
73853
74457
|
for (const file of await walkFiles(artifactsDir)) {
|
|
73854
|
-
const rel = toPosix(
|
|
74458
|
+
const rel = toPosix(path173.relative(artifactsDir, file));
|
|
73855
74459
|
const relPath = `${moduleEntry.name}/${rel}`;
|
|
73856
74460
|
listings.push({
|
|
73857
74461
|
uri: `${URI_PREFIX}artifacts/${relPath}`,
|
|
@@ -73865,7 +74469,7 @@ async function listArtifacts(cwd) {
|
|
|
73865
74469
|
async function listUnderRoot(cwd, cls, root) {
|
|
73866
74470
|
const listings = [];
|
|
73867
74471
|
for (const file of await walkFiles(root)) {
|
|
73868
|
-
const rel = toPosix(
|
|
74472
|
+
const rel = toPosix(path173.relative(root, file));
|
|
73869
74473
|
listings.push({
|
|
73870
74474
|
uri: `${URI_PREFIX}${cls}/${rel}`,
|
|
73871
74475
|
name: rel,
|
|
@@ -73915,12 +74519,12 @@ function resolveConfined(cwd, cls, relPath) {
|
|
|
73915
74519
|
if (moduleName.includes("..") || moduleName.length === 0) {
|
|
73916
74520
|
return null;
|
|
73917
74521
|
}
|
|
73918
|
-
const root =
|
|
73919
|
-
const absolute =
|
|
74522
|
+
const root = path173.join(dataRoot3(cwd), moduleName, "artifacts");
|
|
74523
|
+
const absolute = path173.resolve(root, rest);
|
|
73920
74524
|
return isPathInside(root, absolute) ? { root, absolute } : null;
|
|
73921
74525
|
}
|
|
73922
74526
|
const root = cls === "wiki" ? wikiRoot(cwd) : memoryRoot2(cwd);
|
|
73923
|
-
const absolute =
|
|
74527
|
+
const absolute = path173.resolve(root, relPath);
|
|
73924
74528
|
return isPathInside(root, absolute) ? { root, absolute } : null;
|
|
73925
74529
|
}
|
|
73926
74530
|
async function readResource(cwd, roots, uri) {
|
|
@@ -74131,7 +74735,7 @@ async function serveMcpCommand(args = [], cwd = process.cwd()) {
|
|
|
74131
74735
|
return;
|
|
74132
74736
|
}
|
|
74133
74737
|
const http = args.includes("--http");
|
|
74134
|
-
const projectRoot =
|
|
74738
|
+
const projectRoot = path175.resolve(resolveServeRoot(optionValue(args, "--cwd"), cwd, process.env));
|
|
74135
74739
|
try {
|
|
74136
74740
|
await serveMcp({ cwd: projectRoot, http });
|
|
74137
74741
|
} catch (error) {
|
|
@@ -74207,7 +74811,9 @@ function printMcpHelp() {
|
|
|
74207
74811
|
"keryx mcp add <name> --transport http <url> # a remote one",
|
|
74208
74812
|
"keryx mcp remove|enable|disable <name>",
|
|
74209
74813
|
"keryx mcp trust|untrust <name> # approve a project-scoped server",
|
|
74210
|
-
"keryx mcp doctor [name] [--json] # dial it and say what is wrong"
|
|
74814
|
+
"keryx mcp doctor [name] [--json] # dial it and say what is wrong",
|
|
74815
|
+
"keryx mcp auth <name> # authorise a remote server in your browser",
|
|
74816
|
+
"keryx mcp logout <name> # forget its stored credential"
|
|
74211
74817
|
]);
|
|
74212
74818
|
heading("Publishing keryx itself (the retired `keryx mcp` spellings)");
|
|
74213
74819
|
helpUsage([
|
|
@@ -74233,14 +74839,14 @@ function printMcpHelp() {
|
|
|
74233
74839
|
// src/commands/status.ts
|
|
74234
74840
|
init_fs();
|
|
74235
74841
|
init_json();
|
|
74236
|
-
import
|
|
74842
|
+
import path176 from "path";
|
|
74237
74843
|
async function statusCommand(args = []) {
|
|
74238
74844
|
if (args.includes("--help") || args.includes("-h")) {
|
|
74239
74845
|
printHelp16();
|
|
74240
74846
|
return;
|
|
74241
74847
|
}
|
|
74242
|
-
const root =
|
|
74243
|
-
const manifestPath =
|
|
74848
|
+
const root = path176.join(process.cwd(), ".metaproject");
|
|
74849
|
+
const manifestPath = path176.join(root, "metaproject.json");
|
|
74244
74850
|
if (!await pathExists(root)) {
|
|
74245
74851
|
console.log("Metaproject: not initialized");
|
|
74246
74852
|
console.log("Run: keryx init");
|
|
@@ -74280,7 +74886,7 @@ Reports the workspace root and one enabled/disabled line per module. Use
|
|
|
74280
74886
|
|
|
74281
74887
|
// src/commands/harness.ts
|
|
74282
74888
|
import { readFileSync as readFileSync7, writeFileSync as writeFileSync8 } from "fs";
|
|
74283
|
-
import
|
|
74889
|
+
import path179 from "path";
|
|
74284
74890
|
import { randomUUID as randomUUID20 } from "crypto";
|
|
74285
74891
|
|
|
74286
74892
|
// src/security/harness-scan.ts
|
|
@@ -75353,7 +75959,7 @@ ${command.argv.join(" ")}`);
|
|
|
75353
75959
|
}
|
|
75354
75960
|
|
|
75355
75961
|
// src/harness/process/sandbox/profile.ts
|
|
75356
|
-
import
|
|
75962
|
+
import path177 from "path";
|
|
75357
75963
|
var DEFAULT_SECRET_SUBPATHS = [
|
|
75358
75964
|
".ssh",
|
|
75359
75965
|
".gnupg",
|
|
@@ -75378,7 +75984,7 @@ function defaultReadDenyList(home) {
|
|
|
75378
75984
|
if (!home) {
|
|
75379
75985
|
return [];
|
|
75380
75986
|
}
|
|
75381
|
-
return DEFAULT_SECRET_SUBPATHS.map((sub) =>
|
|
75987
|
+
return DEFAULT_SECRET_SUBPATHS.map((sub) => path177.join(home, sub));
|
|
75382
75988
|
}
|
|
75383
75989
|
function defaultSandboxProfile(cwd, tmpDir, home) {
|
|
75384
75990
|
return {
|
|
@@ -75398,10 +76004,10 @@ function dedupe3(values) {
|
|
|
75398
76004
|
init_config_dir();
|
|
75399
76005
|
init_shell_config();
|
|
75400
76006
|
import { existsSync as existsSync26 } from "fs";
|
|
75401
|
-
import
|
|
76007
|
+
import path178 from "path";
|
|
75402
76008
|
var SECRET_KEY_PATTERN2 = /api[_-]?key|secret|token|password|credential/i;
|
|
75403
76009
|
function sandboxConfigPath(dir) {
|
|
75404
|
-
return
|
|
76010
|
+
return path178.join(path178.dirname(shellConfigPath(dir)), "sandbox.json");
|
|
75405
76011
|
}
|
|
75406
76012
|
function isShellDefault(v) {
|
|
75407
76013
|
return v === "off" || v === "workspace" || v === "strict" || v === "1";
|
|
@@ -76520,7 +77126,7 @@ async function harnessExec(args, deps) {
|
|
|
76520
77126
|
env: commandEnv,
|
|
76521
77127
|
cwd
|
|
76522
77128
|
};
|
|
76523
|
-
const worktreeRoot =
|
|
77129
|
+
const worktreeRoot = path179.parse(path179.resolve(cwd, commandPath)).root || cwd;
|
|
76524
77130
|
const budget = {
|
|
76525
77131
|
reservationId: idSeq(),
|
|
76526
77132
|
maxRuntimeMs: maxRuntimeMs ?? EXEC_DEFAULT_RUNTIME_MS
|
|
@@ -78466,7 +79072,7 @@ function workspaceReadTool(cwd) {
|
|
|
78466
79072
|
// src/harness/tool/builtin/workspace-lifecycle-tool.ts
|
|
78467
79073
|
import { randomUUID as randomUUID22 } from "crypto";
|
|
78468
79074
|
import { writeFile as writeFile52 } from "fs/promises";
|
|
78469
|
-
import
|
|
79075
|
+
import path180 from "path";
|
|
78470
79076
|
init_slate();
|
|
78471
79077
|
init_store3();
|
|
78472
79078
|
init_guard2();
|
|
@@ -78612,7 +79218,7 @@ function workspaceProposeTool(cwd, getSessionDir) {
|
|
|
78612
79218
|
return { output: `workspace_propose: unrecognized 'kind' \u2014 expected one of: ${PROPOSAL_KINDS.join(", ")}`, isError: true };
|
|
78613
79219
|
}
|
|
78614
79220
|
const explicitSessionId = typeof input.sessionId === "string" && input.sessionId.length > 0 ? input.sessionId : undefined;
|
|
78615
|
-
const sessionRef = explicitSessionId ??
|
|
79221
|
+
const sessionRef = explicitSessionId ?? path180.basename(getSessionDir() ?? "");
|
|
78616
79222
|
if (sessionRef.length === 0) {
|
|
78617
79223
|
return { output: "workspace_propose: no 'sessionId' given and no active session in this run", isError: true };
|
|
78618
79224
|
}
|
|
@@ -78784,7 +79390,7 @@ init_shell_config();
|
|
|
78784
79390
|
init_command_risk();
|
|
78785
79391
|
init_shell_syntax();
|
|
78786
79392
|
import { existsSync as existsSync30 } from "fs";
|
|
78787
|
-
import
|
|
79393
|
+
import path181 from "path";
|
|
78788
79394
|
import { createHash as createHash41 } from "crypto";
|
|
78789
79395
|
var PREFIX_BANNED = new Set([
|
|
78790
79396
|
"sh",
|
|
@@ -78989,7 +79595,7 @@ function emptyShellPermissions() {
|
|
|
78989
79595
|
return { allow: [] };
|
|
78990
79596
|
}
|
|
78991
79597
|
function shellPermissionsPath(dir) {
|
|
78992
|
-
return
|
|
79598
|
+
return path181.join(path181.dirname(shellConfigPath(dir)), "permissions.json");
|
|
78993
79599
|
}
|
|
78994
79600
|
function loadShellPermissionsWithAudit(dir) {
|
|
78995
79601
|
try {
|
|
@@ -79027,7 +79633,7 @@ function loadShellPermissions(dir) {
|
|
|
79027
79633
|
function saveShellPermissions(perms, dir, options = {}) {
|
|
79028
79634
|
try {
|
|
79029
79635
|
const file = shellPermissionsPath(dir);
|
|
79030
|
-
ensureKeryxConfigDir(
|
|
79636
|
+
ensureKeryxConfigDir(path181.dirname(file));
|
|
79031
79637
|
const cleaned = Array.from(new Set(perms.allow.map((p) => p.trim()).filter((p) => p.length > 0)));
|
|
79032
79638
|
const body = {
|
|
79033
79639
|
allow: options.skipValidation === true ? cleaned : cleaned.filter((p) => validateShellPattern(p).ok)
|
|
@@ -79466,12 +80072,12 @@ function remoteRequest(providerId, endpoint, query, key) {
|
|
|
79466
80072
|
// src/lib/search-config.ts
|
|
79467
80073
|
init_config_dir();
|
|
79468
80074
|
import { existsSync as existsSync31 } from "fs";
|
|
79469
|
-
import
|
|
80075
|
+
import path182 from "path";
|
|
79470
80076
|
function searchConfigPath(dir) {
|
|
79471
|
-
return
|
|
80077
|
+
return path182.join(keryxConfigDir(dir), "search-providers.json");
|
|
79472
80078
|
}
|
|
79473
80079
|
function searchCredentialPath(dir) {
|
|
79474
|
-
return
|
|
80080
|
+
return path182.join(keryxConfigDir(dir), "search-credentials.json");
|
|
79475
80081
|
}
|
|
79476
80082
|
function readJson5(file) {
|
|
79477
80083
|
try {
|
|
@@ -79600,7 +80206,7 @@ function createDefaultSearchProviderController(configDir) {
|
|
|
79600
80206
|
import { createHash as createHash42, randomUUID as randomUUID24 } from "crypto";
|
|
79601
80207
|
import { mkdtemp, rm as rm13 } from "fs/promises";
|
|
79602
80208
|
import { tmpdir as tmpdir4 } from "os";
|
|
79603
|
-
import
|
|
80209
|
+
import path183 from "path";
|
|
79604
80210
|
init_metaproject_adapter();
|
|
79605
80211
|
init_ledger();
|
|
79606
80212
|
init_orchestrate();
|
|
@@ -79863,8 +80469,8 @@ function createSpawnSubagentTool(deps) {
|
|
|
79863
80469
|
let dispatchId = "";
|
|
79864
80470
|
let closing = false;
|
|
79865
80471
|
try {
|
|
79866
|
-
ephemeralDir = await mkdtemp(
|
|
79867
|
-
dispatchId =
|
|
80472
|
+
ephemeralDir = await mkdtemp(path183.join(tmpdir4(), "keryx-subagent-slate-"));
|
|
80473
|
+
dispatchId = path183.basename(ephemeralDir);
|
|
79868
80474
|
openedChildSlate = await openSlate({
|
|
79869
80475
|
dir: ephemeralDir,
|
|
79870
80476
|
cwd,
|
|
@@ -80107,7 +80713,7 @@ import { randomUUID as randomUUID25 } from "crypto";
|
|
|
80107
80713
|
import { execFile as execFile4 } from "child_process";
|
|
80108
80714
|
import { mkdir as mkdir61 } from "fs/promises";
|
|
80109
80715
|
import { tmpdir as tmpdir6 } from "os";
|
|
80110
|
-
import
|
|
80716
|
+
import path185 from "path";
|
|
80111
80717
|
import { promisify as promisify4 } from "util";
|
|
80112
80718
|
|
|
80113
80719
|
// src/harness/child/git-worktree-port.ts
|
|
@@ -80359,7 +80965,7 @@ function readRuntimeBlock(dispatch) {
|
|
|
80359
80965
|
// src/harness/external/runtime.ts
|
|
80360
80966
|
import { mkdtemp as mkdtemp2, rm as rm14, writeFile as writeFile53 } from "fs/promises";
|
|
80361
80967
|
import { tmpdir as tmpdir5 } from "os";
|
|
80362
|
-
import
|
|
80968
|
+
import path184 from "path";
|
|
80363
80969
|
|
|
80364
80970
|
// src/harness/external/prompt.ts
|
|
80365
80971
|
var EXTERNAL_RUNTIME_DIRECTIVE = [
|
|
@@ -81402,8 +82008,8 @@ async function prepareResultSchema() {
|
|
|
81402
82008
|
try {
|
|
81403
82009
|
const schema = await loadSchema("subagent-result");
|
|
81404
82010
|
const schemaText = JSON.stringify(schema, null, 2);
|
|
81405
|
-
schemaDir = await mkdtemp2(
|
|
81406
|
-
const schemaPath =
|
|
82011
|
+
schemaDir = await mkdtemp2(path184.join(tmpdir5(), "keryx-external-result-schema-"));
|
|
82012
|
+
const schemaPath = path184.join(schemaDir, "subagent-result.schema.json");
|
|
81407
82013
|
await writeFile53(schemaPath, schemaText, "utf8");
|
|
81408
82014
|
return { ok: true, schema, schemaText, schemaPath, schemaDir };
|
|
81409
82015
|
} catch (error) {
|
|
@@ -81660,7 +82266,7 @@ async function createRunExternal(options) {
|
|
|
81660
82266
|
}
|
|
81661
82267
|
const config = gate.config;
|
|
81662
82268
|
const idSeq = options.idSeq ?? (() => randomUUID25());
|
|
81663
|
-
const worktreesDir = options.worktreesDir ??
|
|
82269
|
+
const worktreesDir = options.worktreesDir ?? path185.join(tmpdir6(), "keryx-external-worktrees");
|
|
81664
82270
|
const spawn = options.spawn ?? createBunSpawnPort();
|
|
81665
82271
|
const worktree = options.worktree ?? ensuringParentDir(createGitWorktreePort({ repoRoot: options.cwd, worktreesDir }), worktreesDir);
|
|
81666
82272
|
const readWorkingDiff = options.readWorkingDiff ?? (() => defaultWorkingDiff(options.cwd));
|
|
@@ -81970,7 +82576,7 @@ class LiveMarkdownBlock {
|
|
|
81970
82576
|
// src/tui/theme.ts
|
|
81971
82577
|
init_config_dir();
|
|
81972
82578
|
import { existsSync as existsSync32 } from "fs";
|
|
81973
|
-
import
|
|
82579
|
+
import path186 from "path";
|
|
81974
82580
|
var THEME_NAMES = ["groknight", "grokday", "tokyonight", "keryx"];
|
|
81975
82581
|
var THEME_IDS = ["auto", ...THEME_NAMES];
|
|
81976
82582
|
var DEFAULT_THEME_ID = "groknight";
|
|
@@ -82100,7 +82706,7 @@ function onThemeChange(listener) {
|
|
|
82100
82706
|
};
|
|
82101
82707
|
}
|
|
82102
82708
|
function tuiConfigPath(dir) {
|
|
82103
|
-
return
|
|
82709
|
+
return path186.join(keryxConfigDir(dir), "tui.json");
|
|
82104
82710
|
}
|
|
82105
82711
|
function loadPersistedThemeId(dir) {
|
|
82106
82712
|
try {
|
|
@@ -82157,7 +82763,7 @@ init_workspace_service();
|
|
|
82157
82763
|
init_service8();
|
|
82158
82764
|
init_store2();
|
|
82159
82765
|
init_fs();
|
|
82160
|
-
import
|
|
82766
|
+
import path187 from "path";
|
|
82161
82767
|
import { readFile as readFile97 } from "fs/promises";
|
|
82162
82768
|
var POSITIVE_INTEGER = /^[1-9][0-9]*$/;
|
|
82163
82769
|
var DEFAULT_AUTO_GOAL_ROUNDS = 8;
|
|
@@ -82233,7 +82839,7 @@ function getFlowService() {
|
|
|
82233
82839
|
async function autoProvisionFlow(cwd, goalText) {
|
|
82234
82840
|
const service = getFlowService();
|
|
82235
82841
|
const result = await service.init({ cwd, title: goalText });
|
|
82236
|
-
const acFile =
|
|
82842
|
+
const acFile = path187.join(cwd, result.dir, "acceptance-criteria.md");
|
|
82237
82843
|
await writeFileAtomic(acFile, [
|
|
82238
82844
|
"# Acceptance Criteria",
|
|
82239
82845
|
"",
|
|
@@ -83784,7 +84390,7 @@ init_proposal_lifecycle();
|
|
|
83784
84390
|
init_workspace_service();
|
|
83785
84391
|
import { randomUUID as randomUUID27 } from "crypto";
|
|
83786
84392
|
import { readdir as readdir34, stat as stat18 } from "fs/promises";
|
|
83787
|
-
import
|
|
84393
|
+
import path188 from "path";
|
|
83788
84394
|
|
|
83789
84395
|
// src/sac/lifecycle-flag.ts
|
|
83790
84396
|
init_store();
|
|
@@ -83943,7 +84549,7 @@ async function readExternalUnboundCandidates(cwd) {
|
|
|
83943
84549
|
}
|
|
83944
84550
|
return groups;
|
|
83945
84551
|
}, []));
|
|
83946
|
-
const metaPath =
|
|
84552
|
+
const metaPath = path188.join(extDir, `${id}.json`);
|
|
83947
84553
|
if (await pathExists(unboundDismissedReceiptPath(metaPath)))
|
|
83948
84554
|
continue;
|
|
83949
84555
|
candidates.push({
|
|
@@ -83957,7 +84563,7 @@ async function readExternalUnboundCandidates(cwd) {
|
|
|
83957
84563
|
return candidates;
|
|
83958
84564
|
}
|
|
83959
84565
|
async function readNewestUnboundCandidateForExternal(cwd, externalSessionId) {
|
|
83960
|
-
const evidenceDir =
|
|
84566
|
+
const evidenceDir = path188.join(externalSlatesDir(cwd), externalSessionId);
|
|
83961
84567
|
let entries;
|
|
83962
84568
|
try {
|
|
83963
84569
|
entries = (await readdir34(evidenceDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
|
|
@@ -83966,7 +84572,7 @@ async function readNewestUnboundCandidateForExternal(cwd, externalSessionId) {
|
|
|
83966
84572
|
}
|
|
83967
84573
|
entries.sort();
|
|
83968
84574
|
for (let i = entries.length - 1;i >= 0; i--) {
|
|
83969
|
-
const evidencePath =
|
|
84575
|
+
const evidencePath = path188.join(evidenceDir, entries[i]);
|
|
83970
84576
|
if (await pathExists(unboundDismissedReceiptPath(evidencePath)))
|
|
83971
84577
|
continue;
|
|
83972
84578
|
const result = readConfigFile(evidencePath);
|
|
@@ -84009,7 +84615,7 @@ var SESSION_ATTRIBUTION_SLACK_MS = 5 * 60000;
|
|
|
84009
84615
|
var MTIME_CLOCK_SKEW_TOLERANCE_MS = 5000;
|
|
84010
84616
|
async function collectReceiptTargets(cwd, owner) {
|
|
84011
84617
|
const targets = new Set;
|
|
84012
|
-
const workspacesDir =
|
|
84618
|
+
const workspacesDir = path188.join(cwd, ".metaproject", "workspaces");
|
|
84013
84619
|
let workspaceIds;
|
|
84014
84620
|
try {
|
|
84015
84621
|
workspaceIds = (await readdir34(workspacesDir, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
@@ -84017,7 +84623,7 @@ async function collectReceiptTargets(cwd, owner) {
|
|
|
84017
84623
|
return targets;
|
|
84018
84624
|
}
|
|
84019
84625
|
for (const workspaceId of workspaceIds) {
|
|
84020
|
-
const receiptsDir =
|
|
84626
|
+
const receiptsDir = path188.join(workspacesDir, workspaceId, `${owner}-write-receipts`);
|
|
84021
84627
|
let files;
|
|
84022
84628
|
try {
|
|
84023
84629
|
files = (await readdir34(receiptsDir)).filter((name) => name.endsWith(".json"));
|
|
@@ -84025,7 +84631,7 @@ async function collectReceiptTargets(cwd, owner) {
|
|
|
84025
84631
|
continue;
|
|
84026
84632
|
}
|
|
84027
84633
|
for (const file of files) {
|
|
84028
|
-
const result = readConfigFile(
|
|
84634
|
+
const result = readConfigFile(path188.join(receiptsDir, file));
|
|
84029
84635
|
if (!result.ok)
|
|
84030
84636
|
continue;
|
|
84031
84637
|
try {
|
|
@@ -84068,7 +84674,7 @@ async function findSkillFiles(dir) {
|
|
|
84068
84674
|
}
|
|
84069
84675
|
const found = [];
|
|
84070
84676
|
for (const entry of entries) {
|
|
84071
|
-
const full =
|
|
84677
|
+
const full = path188.join(dir, entry.name);
|
|
84072
84678
|
if (entry.isDirectory()) {
|
|
84073
84679
|
found.push(...await findSkillFiles(full));
|
|
84074
84680
|
} else if (entry.isFile() && entry.name === "SKILL.md") {
|
|
@@ -84127,10 +84733,10 @@ async function detectUnreviewedSacPathChanges(cwd, sessions) {
|
|
|
84127
84733
|
changedAt: attribution.changedAt
|
|
84128
84734
|
});
|
|
84129
84735
|
}
|
|
84130
|
-
const sacSkillsDir =
|
|
84736
|
+
const sacSkillsDir = path188.join(cwd, ".metaproject", "project-skills", "sac");
|
|
84131
84737
|
const skillFiles = await findSkillFiles(sacSkillsDir);
|
|
84132
84738
|
for (const absolutePath of skillFiles) {
|
|
84133
|
-
const targetRef =
|
|
84739
|
+
const targetRef = path188.relative(path188.join(cwd, ".metaproject"), absolutePath).split(path188.sep).join("/");
|
|
84134
84740
|
if (skillReceipts.has(targetRef))
|
|
84135
84741
|
continue;
|
|
84136
84742
|
const attribution = await attributeToSession(absolutePath, scoped);
|
|
@@ -84148,12 +84754,12 @@ async function detectUnreviewedSacPathChanges(cwd, sessions) {
|
|
|
84148
84754
|
return items;
|
|
84149
84755
|
}
|
|
84150
84756
|
async function isSlateEngaged(dir) {
|
|
84151
|
-
if (await pathExists(
|
|
84757
|
+
if (await pathExists(path188.join(dir, "slate.json")))
|
|
84152
84758
|
return true;
|
|
84153
|
-
if (await pathExists(
|
|
84759
|
+
if (await pathExists(path188.join(dir, "terminal-state.json")))
|
|
84154
84760
|
return true;
|
|
84155
84761
|
try {
|
|
84156
|
-
const entries = await readdir34(
|
|
84762
|
+
const entries = await readdir34(path188.join(dir, "slate-archive"));
|
|
84157
84763
|
return entries.length > 0;
|
|
84158
84764
|
} catch {
|
|
84159
84765
|
return false;
|
|
@@ -84167,7 +84773,7 @@ async function safeReadSlate(dir) {
|
|
|
84167
84773
|
}
|
|
84168
84774
|
}
|
|
84169
84775
|
async function readTerminalState(dir) {
|
|
84170
|
-
const result = readConfigFile(
|
|
84776
|
+
const result = readConfigFile(path188.join(dir, "terminal-state.json"));
|
|
84171
84777
|
if (!result.ok) {
|
|
84172
84778
|
return;
|
|
84173
84779
|
}
|
|
@@ -84191,7 +84797,7 @@ async function resolveUnboundCandidateTarget(cwd, target) {
|
|
|
84191
84797
|
if (session === undefined) {
|
|
84192
84798
|
throw new Error(`No session or evidence path matches "${target}"`);
|
|
84193
84799
|
}
|
|
84194
|
-
const archiveDir =
|
|
84800
|
+
const archiveDir = path188.join(sessionDir(session.projectPath, session.id), "slate-archive");
|
|
84195
84801
|
let entries;
|
|
84196
84802
|
try {
|
|
84197
84803
|
entries = (await readdir34(archiveDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
|
|
@@ -84201,7 +84807,7 @@ async function resolveUnboundCandidateTarget(cwd, target) {
|
|
|
84201
84807
|
if (entries.length === 0)
|
|
84202
84808
|
throw new Error(`No unbound-candidate artifacts for session ${session.id}`);
|
|
84203
84809
|
entries.sort();
|
|
84204
|
-
return
|
|
84810
|
+
return path188.join(archiveDir, entries[entries.length - 1]);
|
|
84205
84811
|
}
|
|
84206
84812
|
async function dismissUnboundByTarget(cwd, target, reason) {
|
|
84207
84813
|
const evidencePath = await resolveUnboundCandidateTarget(cwd, target);
|
|
@@ -84227,7 +84833,7 @@ async function dismissUnboundCandidate(candidatePath, reason) {
|
|
|
84227
84833
|
return { removed: candidatePath, receipt: receiptPath };
|
|
84228
84834
|
}
|
|
84229
84835
|
async function readNewestUnboundCandidate(dir) {
|
|
84230
|
-
const archiveDir =
|
|
84836
|
+
const archiveDir = path188.join(dir, "slate-archive");
|
|
84231
84837
|
let entries;
|
|
84232
84838
|
try {
|
|
84233
84839
|
entries = (await readdir34(archiveDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
|
|
@@ -84236,7 +84842,7 @@ async function readNewestUnboundCandidate(dir) {
|
|
|
84236
84842
|
}
|
|
84237
84843
|
entries.sort();
|
|
84238
84844
|
for (let i = entries.length - 1;i >= 0; i--) {
|
|
84239
|
-
const evidencePath =
|
|
84845
|
+
const evidencePath = path188.join(archiveDir, entries[i]);
|
|
84240
84846
|
if (await pathExists(unboundDismissedReceiptPath(evidencePath)))
|
|
84241
84847
|
continue;
|
|
84242
84848
|
const result = readConfigFile(evidencePath);
|
|
@@ -84266,7 +84872,7 @@ function isFailureOutcome(g) {
|
|
|
84266
84872
|
return g.outcome === "error" || g.outcome === "no_credential" || g.outcome === "conflict";
|
|
84267
84873
|
}
|
|
84268
84874
|
async function readNewestWrapUpOutcome(dir) {
|
|
84269
|
-
const archiveDir =
|
|
84875
|
+
const archiveDir = path188.join(dir, "slate-archive");
|
|
84270
84876
|
let entries;
|
|
84271
84877
|
try {
|
|
84272
84878
|
entries = (await readdir34(archiveDir)).filter((name) => name.endsWith("-wrap-up-outcome.json"));
|
|
@@ -84275,7 +84881,7 @@ async function readNewestWrapUpOutcome(dir) {
|
|
|
84275
84881
|
}
|
|
84276
84882
|
entries.sort();
|
|
84277
84883
|
for (let i = entries.length - 1;i >= 0; i--) {
|
|
84278
|
-
const evidencePath =
|
|
84884
|
+
const evidencePath = path188.join(archiveDir, entries[i]);
|
|
84279
84885
|
const result = readConfigFile(evidencePath);
|
|
84280
84886
|
if (!result.ok) {
|
|
84281
84887
|
continue;
|
|
@@ -87756,41 +88362,6 @@ init_patch_risk();
|
|
|
87756
88362
|
init_catalog();
|
|
87757
88363
|
init_grants();
|
|
87758
88364
|
init_login();
|
|
87759
|
-
|
|
87760
|
-
// src/lib/oauth/open-url.ts
|
|
87761
|
-
import { spawn as spawn5 } from "child_process";
|
|
87762
|
-
function browserOpenPlan(url, platform = process.platform, env = process.env) {
|
|
87763
|
-
if (url.length === 0) {
|
|
87764
|
-
return;
|
|
87765
|
-
}
|
|
87766
|
-
if (platform === "darwin") {
|
|
87767
|
-
return { cmd: "open", args: [url] };
|
|
87768
|
-
}
|
|
87769
|
-
if (platform === "win32") {
|
|
87770
|
-
return { cmd: "cmd", args: ["/c", "start", "", url] };
|
|
87771
|
-
}
|
|
87772
|
-
if (typeof env.DISPLAY === "string" && env.DISPLAY.length > 0) {
|
|
87773
|
-
return { cmd: "xdg-open", args: [url] };
|
|
87774
|
-
}
|
|
87775
|
-
if (typeof env.WAYLAND_DISPLAY === "string" && env.WAYLAND_DISPLAY.length > 0) {
|
|
87776
|
-
return { cmd: "xdg-open", args: [url] };
|
|
87777
|
-
}
|
|
87778
|
-
return;
|
|
87779
|
-
}
|
|
87780
|
-
function openVerificationUrl(url, deps = {}) {
|
|
87781
|
-
const plan = browserOpenPlan(url, deps.platform ?? process.platform, deps.env ?? process.env);
|
|
87782
|
-
if (plan === undefined) {
|
|
87783
|
-
return;
|
|
87784
|
-
}
|
|
87785
|
-
const spawnFn = deps.spawn ?? spawn5;
|
|
87786
|
-
try {
|
|
87787
|
-
const child = spawnFn(plan.cmd, plan.args, { stdio: "ignore", detached: true });
|
|
87788
|
-
child.on("error", () => {});
|
|
87789
|
-
child.unref();
|
|
87790
|
-
} catch {}
|
|
87791
|
-
}
|
|
87792
|
-
|
|
87793
|
-
// src/tui/tui-shell.ts
|
|
87794
88365
|
init_shell_config();
|
|
87795
88366
|
init_provider_config();
|
|
87796
88367
|
|
|
@@ -87826,10 +88397,10 @@ function describeElicitationPrompt(tool, inputJson) {
|
|
|
87826
88397
|
// src/lib/permission-mode-config.ts
|
|
87827
88398
|
init_permission_mode();
|
|
87828
88399
|
init_config_dir();
|
|
87829
|
-
import
|
|
88400
|
+
import path189 from "path";
|
|
87830
88401
|
var EMPTY3 = { schemaVersion: 1, projects: {} };
|
|
87831
88402
|
function permissionModeConfigPath(dir) {
|
|
87832
|
-
return
|
|
88403
|
+
return path189.join(keryxConfigDir(dir), "permission-mode.json");
|
|
87833
88404
|
}
|
|
87834
88405
|
function withRegistryLock2(dir, fn) {
|
|
87835
88406
|
return withFileLock2(`${permissionModeConfigPath(dir)}.lock`, fn, {
|
|
@@ -88347,7 +88918,7 @@ function showComposerChoice(otui, r, dock, request) {
|
|
|
88347
88918
|
// src/lib/version-check.ts
|
|
88348
88919
|
init_config_dir();
|
|
88349
88920
|
init_fs();
|
|
88350
|
-
import
|
|
88921
|
+
import path190 from "path";
|
|
88351
88922
|
var REGISTRY_URL = "https://registry.npmjs.org/@mrciphersmith%2Fkeryx/latest";
|
|
88352
88923
|
var FIXED_INSTALL_COMMAND = "npm install -g @mrciphersmith/keryx@latest";
|
|
88353
88924
|
var RESPONSE_BODY_LIMIT_BYTES = 64 * 1024;
|
|
@@ -88549,7 +89120,7 @@ async function checkVersion(options) {
|
|
|
88549
89120
|
const now = options.now ?? Date.now;
|
|
88550
89121
|
const timestamp = now();
|
|
88551
89122
|
const configDir = ensureKeryxConfigDir(options.cacheDir);
|
|
88552
|
-
const cacheFile =
|
|
89123
|
+
const cacheFile = path190.join(configDir, "version-check.json");
|
|
88553
89124
|
const cache = parseCache(cacheFile);
|
|
88554
89125
|
if (cache?.latestVersion !== undefined && cache.successAt !== undefined && timestamp - cache.successAt >= 0 && timestamp - cache.successAt < SUCCESS_CACHE_TTL_MS) {
|
|
88555
89126
|
return resultFor(options.currentVersion, current, cache.latestVersion, "cache");
|
|
@@ -97125,7 +97696,7 @@ Shell:
|
|
|
97125
97696
|
init_fs();
|
|
97126
97697
|
import { readFile as readFile98 } from "fs/promises";
|
|
97127
97698
|
import { stdin } from "process";
|
|
97128
|
-
import
|
|
97699
|
+
import path191 from "path";
|
|
97129
97700
|
var MODULES = [
|
|
97130
97701
|
{ name: "gdgraph", flag: "--no-gdgraph", desc: "code graph, symbols, affected context", defaultEnabled: true },
|
|
97131
97702
|
{ name: "gdctx", flag: "--no-gdctx", desc: "token-aware command/read output", defaultEnabled: true },
|
|
@@ -97171,8 +97742,8 @@ async function modulesCommand(args = []) {
|
|
|
97171
97742
|
return;
|
|
97172
97743
|
}
|
|
97173
97744
|
const wantsJson = args.includes("--json") && (sub === undefined || sub === "status" || sub === "list" || sub === "--json");
|
|
97174
|
-
const metaprojectRoot =
|
|
97175
|
-
const manifestPath =
|
|
97745
|
+
const metaprojectRoot = path191.join(process.cwd(), ".metaproject");
|
|
97746
|
+
const manifestPath = path191.join(metaprojectRoot, "metaproject.json");
|
|
97176
97747
|
if (!await pathExists(manifestPath)) {
|
|
97177
97748
|
if (wantsJson) {
|
|
97178
97749
|
console.log(JSON.stringify({ schemaVersion: 1, error: "not-initialized", modules: [] }, null, 2));
|
|
@@ -97291,7 +97862,7 @@ init_serve_config();
|
|
|
97291
97862
|
|
|
97292
97863
|
// src/lib/serve-credential.ts
|
|
97293
97864
|
init_config_dir();
|
|
97294
|
-
import { createHash as createHash43, randomBytes as
|
|
97865
|
+
import { createHash as createHash43, randomBytes as randomBytes5, randomUUID as randomUUID30 } from "crypto";
|
|
97295
97866
|
import {
|
|
97296
97867
|
chmodSync as chmodSync4,
|
|
97297
97868
|
closeSync as closeSync3,
|
|
@@ -97303,9 +97874,9 @@ import {
|
|
|
97303
97874
|
unlinkSync as unlinkSync3,
|
|
97304
97875
|
writeFileSync as writeFileSync9
|
|
97305
97876
|
} from "fs";
|
|
97306
|
-
import
|
|
97877
|
+
import path192 from "path";
|
|
97307
97878
|
function serveCredentialPath(dir) {
|
|
97308
|
-
return
|
|
97879
|
+
return path192.join(keryxConfigDir(dir), "serve-credentials.json");
|
|
97309
97880
|
}
|
|
97310
97881
|
function constantTimeEqual(a, b) {
|
|
97311
97882
|
const width = Math.max(a.length, b.length);
|
|
@@ -97411,8 +97982,8 @@ function withServeCredentialLock(dir, fn, onWaiting) {
|
|
|
97411
97982
|
});
|
|
97412
97983
|
}
|
|
97413
97984
|
function mintRecord(now) {
|
|
97414
|
-
const token =
|
|
97415
|
-
const salt =
|
|
97985
|
+
const token = randomBytes5(32).toString("base64url");
|
|
97986
|
+
const salt = randomBytes5(32).toString("hex");
|
|
97416
97987
|
return {
|
|
97417
97988
|
token,
|
|
97418
97989
|
record: { id: randomUUID30(), algorithm: "sha256", salt, hash: hashToken(salt, token), createdAt: now }
|
|
@@ -97539,22 +98110,22 @@ class AuthFailureThrottle {
|
|
|
97539
98110
|
init_config_dir();
|
|
97540
98111
|
import { createHash as createHash44 } from "crypto";
|
|
97541
98112
|
import { existsSync as existsSync34, readdirSync as readdirSync3, rmSync as rmSync2 } from "fs";
|
|
97542
|
-
import
|
|
98113
|
+
import path193 from "path";
|
|
97543
98114
|
var MAX_TURN_EVENTS = 1e4;
|
|
97544
98115
|
function turnsRoot(dir) {
|
|
97545
|
-
return
|
|
98116
|
+
return path193.join(keryxConfigDir(dir), "turns");
|
|
97546
98117
|
}
|
|
97547
98118
|
function turnDir(turnId, dir) {
|
|
97548
|
-
return
|
|
98119
|
+
return path193.join(turnsRoot(dir), turnId);
|
|
97549
98120
|
}
|
|
97550
98121
|
function keyPath(project, idempotencyKey, dir) {
|
|
97551
98122
|
const projectBytes = Buffer.byteLength(project, "utf8");
|
|
97552
98123
|
const digest = createHash44("sha256").update(`${projectBytes}:${project}\x00${idempotencyKey}`, "utf8").digest("hex");
|
|
97553
|
-
return
|
|
98124
|
+
return path193.join(turnsRoot(dir), "keys", `${digest}.json`);
|
|
97554
98125
|
}
|
|
97555
98126
|
function legacyKeyPath(idempotencyKey, dir) {
|
|
97556
98127
|
const digest = createHash44("sha256").update(idempotencyKey, "utf8").digest("hex");
|
|
97557
|
-
return
|
|
98128
|
+
return path193.join(turnsRoot(dir), "keys", `${digest}.json`);
|
|
97558
98129
|
}
|
|
97559
98130
|
function adoptLegacyClaim(project, idempotencyKey, dir) {
|
|
97560
98131
|
const legacy = legacyKeyPath(idempotencyKey, dir);
|
|
@@ -97618,7 +98189,7 @@ function ensureTurnDir(turnId, dir) {
|
|
|
97618
98189
|
}
|
|
97619
98190
|
function createTurnRecord(record, dir) {
|
|
97620
98191
|
ensureTurnDir(record.turnId, dir);
|
|
97621
|
-
writeOwnerOnlyFile(
|
|
98192
|
+
writeOwnerOnlyFile(path193.join(turnDir(record.turnId, dir), "turn.json"), `${JSON.stringify(record, null, 2)}
|
|
97622
98193
|
`);
|
|
97623
98194
|
}
|
|
97624
98195
|
function appendTurnEvent(event, dir, opts) {
|
|
@@ -97627,12 +98198,12 @@ function appendTurnEvent(event, dir, opts) {
|
|
|
97627
98198
|
}
|
|
97628
98199
|
const line = JSON.stringify(event);
|
|
97629
98200
|
try {
|
|
97630
|
-
appendOwnerOnlyLine(
|
|
98201
|
+
appendOwnerOnlyLine(path193.join(turnDir(event.turnId, dir), "events.jsonl"), line);
|
|
97631
98202
|
} catch (error) {
|
|
97632
98203
|
if (error?.code !== "ENOENT") {
|
|
97633
98204
|
throw error;
|
|
97634
98205
|
}
|
|
97635
|
-
appendOwnerOnlyLine(
|
|
98206
|
+
appendOwnerOnlyLine(path193.join(ensureTurnDir(event.turnId, dir), "events.jsonl"), line);
|
|
97636
98207
|
}
|
|
97637
98208
|
return true;
|
|
97638
98209
|
}
|
|
@@ -97640,7 +98211,7 @@ function readTurnEvents(turnId, after = -1, dir) {
|
|
|
97640
98211
|
if (!isTurnId(turnId)) {
|
|
97641
98212
|
return { ok: false, reason: "not-a-turn-id" };
|
|
97642
98213
|
}
|
|
97643
|
-
const read = readTurnFile(
|
|
98214
|
+
const read = readTurnFile(path193.join(turnDir(turnId, dir), "events.jsonl"));
|
|
97644
98215
|
if (!read.ok) {
|
|
97645
98216
|
if (isDefiniteAbsence2(read.reason)) {
|
|
97646
98217
|
return { ok: true, value: [] };
|
|
@@ -97668,7 +98239,7 @@ function readTurnRecord(turnId, dir) {
|
|
|
97668
98239
|
if (!isTurnId(turnId)) {
|
|
97669
98240
|
return { ok: false, reason: "not-a-turn-id" };
|
|
97670
98241
|
}
|
|
97671
|
-
const read = readTurnFile(
|
|
98242
|
+
const read = readTurnFile(path193.join(turnDir(turnId, dir), "turn.json"));
|
|
97672
98243
|
if (!read.ok) {
|
|
97673
98244
|
return { ok: false, reason: read.reason };
|
|
97674
98245
|
}
|
|
@@ -97687,7 +98258,7 @@ function finishTurn(turnId, result, dir) {
|
|
|
97687
98258
|
if (!record.ok) {
|
|
97688
98259
|
return false;
|
|
97689
98260
|
}
|
|
97690
|
-
writeOwnerOnlyFile(
|
|
98261
|
+
writeOwnerOnlyFile(path193.join(turnDir(turnId, dir), "turn.json"), `${JSON.stringify({ ...record.value, result }, null, 2)}
|
|
97691
98262
|
`);
|
|
97692
98263
|
return true;
|
|
97693
98264
|
}
|
|
@@ -97733,7 +98304,7 @@ function releaseIdempotencyKey(project, idempotencyKey, turnId, dir) {
|
|
|
97733
98304
|
|
|
97734
98305
|
// src/lib/serve-turn.ts
|
|
97735
98306
|
import { randomUUID as randomUUID31 } from "crypto";
|
|
97736
|
-
import
|
|
98307
|
+
import path194 from "path";
|
|
97737
98308
|
init_service();
|
|
97738
98309
|
var REMOTE_ORIGIN = "remote:http";
|
|
97739
98310
|
var MAX_PROMPT_CHARS = 32000;
|
|
@@ -97802,9 +98373,9 @@ function isUuid(value) {
|
|
|
97802
98373
|
return typeof value === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(value);
|
|
97803
98374
|
}
|
|
97804
98375
|
function resolveProject(declared, dir) {
|
|
97805
|
-
const wanted =
|
|
98376
|
+
const wanted = path194.resolve(declared);
|
|
97806
98377
|
for (const entry of listProjects(dir, () => {})) {
|
|
97807
|
-
if (
|
|
98378
|
+
if (path194.resolve(entry.path) === wanted) {
|
|
97808
98379
|
return { ok: true, project: entry.path };
|
|
97809
98380
|
}
|
|
97810
98381
|
}
|
|
@@ -98840,7 +99411,7 @@ function printHelp19() {
|
|
|
98840
99411
|
import { spawn as spawn6 } from "child_process";
|
|
98841
99412
|
import { chmod as chmod4, mkdir as mkdir62, readFile as readFile99, readdir as readdir35, writeFile as writeFile54 } from "fs/promises";
|
|
98842
99413
|
import { access as access4, constants as constants2, existsSync as existsSync35 } from "fs";
|
|
98843
|
-
import
|
|
99414
|
+
import path195 from "path";
|
|
98844
99415
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
98845
99416
|
init_config2();
|
|
98846
99417
|
init_config();
|
|
@@ -98857,8 +99428,8 @@ async function updateCommand(args = []) {
|
|
|
98857
99428
|
return;
|
|
98858
99429
|
}
|
|
98859
99430
|
const projectRoot = process.cwd();
|
|
98860
|
-
const metaprojectRoot =
|
|
98861
|
-
banner("keryx update", `Refreshing the .metaproject workspace in ${
|
|
99431
|
+
const metaprojectRoot = path195.join(projectRoot, ".metaproject");
|
|
99432
|
+
banner("keryx update", `Refreshing the .metaproject workspace in ${path195.basename(projectRoot)}/`);
|
|
98862
99433
|
if (!await pathExists(metaprojectRoot)) {
|
|
98863
99434
|
console.log(` ${style.red(symbols.cross)} Metaproject is not initialized.`);
|
|
98864
99435
|
console.log(` ${style.cyan(symbols.arrow)} Run ${style.cyan("keryx init")} first.`);
|
|
@@ -98919,7 +99490,7 @@ async function updateCommand(args = []) {
|
|
|
98919
99490
|
nextSteps(steps);
|
|
98920
99491
|
}
|
|
98921
99492
|
async function previewServiceFiles(projectRoot, options) {
|
|
98922
|
-
const metaprojectRoot =
|
|
99493
|
+
const metaprojectRoot = path195.join(projectRoot, ".metaproject");
|
|
98923
99494
|
const manifestState = await readManifest5(metaprojectRoot);
|
|
98924
99495
|
const manifest = manifestState.manifest;
|
|
98925
99496
|
const enableTasks = moduleEnabled2(manifest, "tasks") || !options.noTasks;
|
|
@@ -98948,12 +99519,12 @@ async function previewServiceFiles(projectRoot, options) {
|
|
|
98948
99519
|
});
|
|
98949
99520
|
}
|
|
98950
99521
|
async function refreshServiceFiles(projectRoot, options) {
|
|
98951
|
-
const metaprojectRoot =
|
|
99522
|
+
const metaprojectRoot = path195.join(projectRoot, ".metaproject");
|
|
98952
99523
|
const manifestState = await readManifest5(metaprojectRoot);
|
|
98953
99524
|
const manifest = manifestState.manifest;
|
|
98954
99525
|
const recoveredManifest = !manifestState.exists || !manifestState.valid;
|
|
98955
99526
|
if (manifestState.migrated) {
|
|
98956
|
-
await writeFile54(
|
|
99527
|
+
await writeFile54(path195.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
|
|
98957
99528
|
`, "utf8");
|
|
98958
99529
|
}
|
|
98959
99530
|
const enableGdgraph = moduleEnabled2(manifest, "gdgraph");
|
|
@@ -98990,9 +99561,9 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
98990
99561
|
enableSecurity,
|
|
98991
99562
|
enableSac
|
|
98992
99563
|
});
|
|
98993
|
-
await writeTextIfChanged4(
|
|
98994
|
-
await writeTextIfChanged4(
|
|
98995
|
-
await writeTextIfChanged4(
|
|
99564
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "core", "README.md"), renderMetaprojectCoreReadme());
|
|
99565
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "hooks", "README.md"), renderHooksReadme());
|
|
99566
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "skills", "project-rules", "README.md"), renderProjectRulesSkillReadme({ sources: ruleSources }));
|
|
98996
99567
|
await writeRoutingEntrypointPair(metaprojectRoot, {
|
|
98997
99568
|
enableGdgraph,
|
|
98998
99569
|
enableGdctx,
|
|
@@ -99015,7 +99586,7 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
99015
99586
|
}
|
|
99016
99587
|
}
|
|
99017
99588
|
});
|
|
99018
|
-
await writeTextIfChanged4(
|
|
99589
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "keryx-dashboard.html"), renderMetaprojectDashboardHtml({
|
|
99019
99590
|
enableGdgraph,
|
|
99020
99591
|
enableGdctx,
|
|
99021
99592
|
enableGdwiki,
|
|
@@ -99027,7 +99598,7 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
99027
99598
|
enableSecurity,
|
|
99028
99599
|
data: dashboardData
|
|
99029
99600
|
}));
|
|
99030
|
-
await writeTextIfMissing4(
|
|
99601
|
+
await writeTextIfMissing4(path195.join(metaprojectRoot, "README.md"), renderMetaprojectReadme({
|
|
99031
99602
|
enableGdgraph,
|
|
99032
99603
|
enableGdctx,
|
|
99033
99604
|
enableGdwiki,
|
|
@@ -99040,31 +99611,31 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
99040
99611
|
}));
|
|
99041
99612
|
if (enableGdgraph) {
|
|
99042
99613
|
await installGdgraphCoreScripts2(metaprojectRoot);
|
|
99043
|
-
await writeTextIfChanged4(
|
|
99044
|
-
await writeTextIfChanged4(
|
|
99045
|
-
await writeTextIfChanged4(
|
|
99614
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "modules", "gdgraph.md"), renderGdgraphManifest());
|
|
99615
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "core", "gdgraph", "README.md"), renderGdgraphCoreReadme());
|
|
99616
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "skills", "gdgraph", "SKILL.md"), renderGdgraphSkillReadme());
|
|
99046
99617
|
await seedAssetsLock(metaprojectRoot);
|
|
99047
99618
|
if (manifest.modules?.gdgraph?.hooks?.gitPostCommit) {
|
|
99048
99619
|
await installManagedHook2(projectRoot, "post-commit", "gdgraph-post-commit", renderGdgraphPostCommitHook());
|
|
99049
99620
|
}
|
|
99050
99621
|
}
|
|
99051
99622
|
if (enableGdctx) {
|
|
99052
|
-
await writeTextIfMissing4(
|
|
99053
|
-
await writeTextIfChanged4(
|
|
99054
|
-
await writeTextIfChanged4(
|
|
99055
|
-
await writeTextIfChanged4(
|
|
99623
|
+
await writeTextIfMissing4(path195.join(metaprojectRoot, "gdctx.config.json"), renderGdctxConfig());
|
|
99624
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "modules", "gdctx.md"), renderGdctxManifest());
|
|
99625
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "core", "gdctx", "README.md"), renderGdctxCoreReadme());
|
|
99626
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "skills", "gdctx", "SKILL.md"), renderGdctxSkillReadme());
|
|
99056
99627
|
}
|
|
99057
99628
|
if (enableGdwiki) {
|
|
99058
|
-
await writeTextIfMissing4(
|
|
99059
|
-
await writeTextIfChanged4(
|
|
99060
|
-
await writeTextIfChanged4(
|
|
99629
|
+
await writeTextIfMissing4(path195.join(metaprojectRoot, "wiki", "templates", "page.md"), renderWikiPageTemplate());
|
|
99630
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "modules", "gdwiki.md"), renderGdwikiManifest());
|
|
99631
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "skills", "gdwiki", "SKILL.md"), renderGdwikiSkillReadme());
|
|
99061
99632
|
if (manifest.modules?.gdgraph?.hooks?.gitPostCommit) {
|
|
99062
99633
|
await installManagedHook2(projectRoot, "post-commit", "gdwiki-post-commit", renderGdwikiPostCommitHook());
|
|
99063
99634
|
}
|
|
99064
99635
|
}
|
|
99065
99636
|
if (enableSac) {
|
|
99066
|
-
await writeTextIfMissing4(
|
|
99067
|
-
await writeTextIfChanged4(
|
|
99637
|
+
await writeTextIfMissing4(path195.join(metaprojectRoot, "modules", "sac.md"), renderSacManifest());
|
|
99638
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "skills", "sac", "SKILL.md"), renderSacSkillReadme());
|
|
99068
99639
|
}
|
|
99069
99640
|
if (enableGdskills) {
|
|
99070
99641
|
await installGdskills(metaprojectRoot, gdskillsProfile, { createDataDirs: false });
|
|
@@ -99073,25 +99644,25 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
99073
99644
|
}
|
|
99074
99645
|
}
|
|
99075
99646
|
if (enableHealth) {
|
|
99076
|
-
await writeTextIfMissing4(
|
|
99077
|
-
await writeTextIfChanged4(
|
|
99078
|
-
await writeTextIfChanged4(
|
|
99079
|
-
await writeTextIfChanged4(
|
|
99647
|
+
await writeTextIfMissing4(path195.join(metaprojectRoot, "health.config.json"), renderHealthConfig());
|
|
99648
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "modules", "health.md"), renderHealthManifest());
|
|
99649
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "core", "health", "README.md"), renderHealthCoreReadme());
|
|
99650
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "skills", "health", "SKILL.md"), renderHealthSkillReadme());
|
|
99080
99651
|
if (manifest.modules?.health?.hooks?.gitPostCommit) {
|
|
99081
99652
|
await installManagedHook2(projectRoot, "post-commit", "health-post-commit", renderHealthPostCommitHook());
|
|
99082
99653
|
}
|
|
99083
99654
|
}
|
|
99084
99655
|
if (enableTesting) {
|
|
99085
|
-
await writeTextIfMissing4(
|
|
99656
|
+
await writeTextIfMissing4(path195.join(metaprojectRoot, "testing.config.json"), renderTestingConfig({
|
|
99086
99657
|
postCommitRefresh: Boolean(manifest.modules?.testing?.hooks?.gitPostCommit),
|
|
99087
99658
|
prePushGate: Boolean(manifest.modules?.testing?.hooks?.prePush)
|
|
99088
99659
|
}));
|
|
99089
|
-
await writeTextIfChanged4(
|
|
99090
|
-
await writeTextIfChanged4(
|
|
99091
|
-
await writeTextIfChanged4(
|
|
99660
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "modules", "testing.md"), renderTestingManifest());
|
|
99661
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "core", "testing", "README.md"), renderTestingCoreReadme());
|
|
99662
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "skills", "testing", "SKILL.md"), renderTestingSkillReadme());
|
|
99092
99663
|
if (enableGdwiki) {
|
|
99093
|
-
await writeTextIfMissing4(
|
|
99094
|
-
await writeTextIfMissing4(
|
|
99664
|
+
await writeTextIfMissing4(path195.join(metaprojectRoot, "wiki", "testing", "README.md"), renderTestingWikiReadme());
|
|
99665
|
+
await writeTextIfMissing4(path195.join(metaprojectRoot, "wiki", "testing", "conventions.md"), renderTestingWikiConventions());
|
|
99095
99666
|
}
|
|
99096
99667
|
if (manifest.modules?.testing?.hooks?.gitPostCommit) {
|
|
99097
99668
|
await installManagedHook2(projectRoot, "post-commit", "testing-post-commit", renderTestingPostCommitHook());
|
|
@@ -99104,24 +99675,24 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
99104
99675
|
await installManagedHook2(projectRoot, "post-commit", "metaproject-dashboard-post-commit", renderMetaprojectDashboardPostCommitHook());
|
|
99105
99676
|
}
|
|
99106
99677
|
if (enableMemory) {
|
|
99107
|
-
await writeTextIfMissing4(
|
|
99108
|
-
await writeTextIfMissing4(
|
|
99109
|
-
await writeTextIfChanged4(
|
|
99110
|
-
await writeTextIfChanged4(
|
|
99111
|
-
await writeTextIfChanged4(
|
|
99678
|
+
await writeTextIfMissing4(path195.join(metaprojectRoot, "memory.config.json"), renderMemoryConfig());
|
|
99679
|
+
await writeTextIfMissing4(path195.join(metaprojectRoot, "memory", "templates", "entry.md"), renderMemoryEntryTemplate());
|
|
99680
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "modules", "memory.md"), renderMemoryManifest());
|
|
99681
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "core", "memory", "README.md"), renderMemoryCoreReadme());
|
|
99682
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "skills", "memory", "SKILL.md"), renderMemorySkillReadme());
|
|
99112
99683
|
}
|
|
99113
99684
|
if (enableTasks) {
|
|
99114
|
-
await writeTextIfChanged4(
|
|
99115
|
-
await writeTextIfChanged4(
|
|
99116
|
-
await writeTextIfChanged4(
|
|
99117
|
-
await writeTextIfChanged4(
|
|
99118
|
-
await writeTextIfChanged4(
|
|
99119
|
-
await writeTextIfChanged4(
|
|
99685
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "flows", "README.md"), renderFlowsReadme());
|
|
99686
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "modules", "tasks.md"), renderTasksManifest());
|
|
99687
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "skills", "flow", "SKILL.md"), renderFlowSkillRouter());
|
|
99688
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "skills", "flow", "init.md"), renderFlowInitSkill());
|
|
99689
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "skills", "flow", "manage.md"), renderFlowManageSkill());
|
|
99690
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "skills", "flow", "complete.md"), renderFlowCompleteSkill());
|
|
99120
99691
|
}
|
|
99121
99692
|
if (enableSecurity) {
|
|
99122
|
-
await writeTextIfMissing4(
|
|
99123
|
-
await writeTextIfChanged4(
|
|
99124
|
-
await writeTextIfChanged4(
|
|
99693
|
+
await writeTextIfMissing4(path195.join(metaprojectRoot, "security.config.json"), renderSecurityConfig());
|
|
99694
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "modules", "security.md"), renderSecurityManifest());
|
|
99695
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "core", "security", "README.md"), renderSecurityCoreReadme());
|
|
99125
99696
|
if (manifest.modules?.security?.hooks?.prePush) {
|
|
99126
99697
|
await installManagedHook2(projectRoot, "pre-push", "security-pre-push", renderSecurityPrePushHook());
|
|
99127
99698
|
}
|
|
@@ -99172,13 +99743,13 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
99172
99743
|
};
|
|
99173
99744
|
}
|
|
99174
99745
|
async function buildDashboard(projectRoot = process.cwd()) {
|
|
99175
|
-
const metaprojectRoot =
|
|
99746
|
+
const metaprojectRoot = path195.join(projectRoot, ".metaproject");
|
|
99176
99747
|
if (!await pathExists(metaprojectRoot)) {
|
|
99177
99748
|
throw new Error("Metaproject is not initialized. Run: keryx init");
|
|
99178
99749
|
}
|
|
99179
99750
|
const manifest = (await readManifest5(metaprojectRoot)).manifest;
|
|
99180
99751
|
const data = await collectDashboardData(metaprojectRoot);
|
|
99181
|
-
const dashboardPath =
|
|
99752
|
+
const dashboardPath = path195.join(metaprojectRoot, "keryx-dashboard.html");
|
|
99182
99753
|
await writeTextIfChanged4(dashboardPath, renderMetaprojectDashboardHtml({
|
|
99183
99754
|
enableGdgraph: moduleEnabled2(manifest, "gdgraph"),
|
|
99184
99755
|
enableGdctx: moduleEnabled2(manifest, "gdctx"),
|
|
@@ -99198,7 +99769,7 @@ async function shouldInstallDashboardPostCommitHook(projectRoot, manifest) {
|
|
|
99198
99769
|
if (Object.values(modules).some((module) => Boolean(module.hooks?.gitPostCommit))) {
|
|
99199
99770
|
return true;
|
|
99200
99771
|
}
|
|
99201
|
-
const hookPath =
|
|
99772
|
+
const hookPath = path195.join(projectRoot, ".git", "hooks", "post-commit");
|
|
99202
99773
|
if (!await pathExists(hookPath)) {
|
|
99203
99774
|
return false;
|
|
99204
99775
|
}
|
|
@@ -99218,11 +99789,11 @@ async function collectDashboardData(metaprojectRoot) {
|
|
|
99218
99789
|
if (testing) {
|
|
99219
99790
|
data.testing = testing;
|
|
99220
99791
|
}
|
|
99221
|
-
const wiki = await collectMarkdownPages(
|
|
99792
|
+
const wiki = await collectMarkdownPages(path195.join(metaprojectRoot, "wiki"), "wiki");
|
|
99222
99793
|
if (wiki.length > 0) {
|
|
99223
99794
|
data.wiki = { pages: wiki };
|
|
99224
99795
|
}
|
|
99225
|
-
const memory = await collectMarkdownPages(
|
|
99796
|
+
const memory = await collectMarkdownPages(path195.join(metaprojectRoot, "memory"), "memory");
|
|
99226
99797
|
if (memory.length > 0) {
|
|
99227
99798
|
data.memory = { entries: memory };
|
|
99228
99799
|
}
|
|
@@ -99237,7 +99808,7 @@ async function collectDashboardData(metaprojectRoot) {
|
|
|
99237
99808
|
return data;
|
|
99238
99809
|
}
|
|
99239
99810
|
async function collectTasksDashboardData(metaprojectRoot) {
|
|
99240
|
-
const flowsRoot =
|
|
99811
|
+
const flowsRoot = path195.join(metaprojectRoot, "flows");
|
|
99241
99812
|
if (!await pathExists(flowsRoot)) {
|
|
99242
99813
|
return null;
|
|
99243
99814
|
}
|
|
@@ -99249,7 +99820,7 @@ async function collectTasksDashboardData(metaprojectRoot) {
|
|
|
99249
99820
|
}
|
|
99250
99821
|
const flows = [];
|
|
99251
99822
|
for (const dir of dirEntries) {
|
|
99252
|
-
const flowPath =
|
|
99823
|
+
const flowPath = path195.join(flowsRoot, dir, "flow.json");
|
|
99253
99824
|
if (!await pathExists(flowPath)) {
|
|
99254
99825
|
continue;
|
|
99255
99826
|
}
|
|
@@ -99257,7 +99828,7 @@ async function collectTasksDashboardData(metaprojectRoot) {
|
|
|
99257
99828
|
const flow = JSON.parse(await readFile99(flowPath, "utf8"));
|
|
99258
99829
|
const tasks = Array.isArray(flow.tasks) ? flow.tasks : [];
|
|
99259
99830
|
let acTotal = 0;
|
|
99260
|
-
const acPath =
|
|
99831
|
+
const acPath = path195.join(flowsRoot, dir, "acceptance-criteria.md");
|
|
99261
99832
|
if (await pathExists(acPath)) {
|
|
99262
99833
|
const acContent = await readFile99(acPath, "utf8");
|
|
99263
99834
|
acTotal = (acContent.match(/^- AC\d+:/gm) ?? []).length;
|
|
@@ -99311,7 +99882,7 @@ async function collectDashboardDocs(metaprojectRoot, wiki, memory) {
|
|
|
99311
99882
|
"data/testing/context.md"
|
|
99312
99883
|
];
|
|
99313
99884
|
for (const href of staticHrefs) {
|
|
99314
|
-
const filePath =
|
|
99885
|
+
const filePath = path195.join(metaprojectRoot, ...href.split("/"));
|
|
99315
99886
|
if (!await pathExists(filePath)) {
|
|
99316
99887
|
continue;
|
|
99317
99888
|
}
|
|
@@ -99328,7 +99899,7 @@ async function collectDashboardDocs(metaprojectRoot, wiki, memory) {
|
|
|
99328
99899
|
return docs;
|
|
99329
99900
|
}
|
|
99330
99901
|
async function collectHealthDashboardData(metaprojectRoot) {
|
|
99331
|
-
const reportPath =
|
|
99902
|
+
const reportPath = path195.join(metaprojectRoot, "data", "health", "artifacts", "latest.json");
|
|
99332
99903
|
if (!await pathExists(reportPath)) {
|
|
99333
99904
|
return;
|
|
99334
99905
|
}
|
|
@@ -99437,8 +100008,8 @@ function metricToScope(metric) {
|
|
|
99437
100008
|
};
|
|
99438
100009
|
}
|
|
99439
100010
|
async function collectGraphDashboardData(metaprojectRoot) {
|
|
99440
|
-
const nodesPath =
|
|
99441
|
-
const edgesPath =
|
|
100011
|
+
const nodesPath = path195.join(metaprojectRoot, "data", "gdgraph", "storage", "nodes.jsonl");
|
|
100012
|
+
const edgesPath = path195.join(metaprojectRoot, "data", "gdgraph", "storage", "edges.jsonl");
|
|
99442
100013
|
if (!await pathExists(nodesPath) || !await pathExists(edgesPath)) {
|
|
99443
100014
|
return;
|
|
99444
100015
|
}
|
|
@@ -99489,8 +100060,8 @@ async function collectGraphDashboardData(metaprojectRoot) {
|
|
|
99489
100060
|
};
|
|
99490
100061
|
}
|
|
99491
100062
|
async function collectTestingDashboardData(metaprojectRoot) {
|
|
99492
|
-
const reportPath =
|
|
99493
|
-
const contextPath =
|
|
100063
|
+
const reportPath = path195.join(metaprojectRoot, "data", "testing", "artifacts", "latest.json");
|
|
100064
|
+
const contextPath = path195.join(metaprojectRoot, "data", "testing", "context.md");
|
|
99494
100065
|
if (await pathExists(reportPath)) {
|
|
99495
100066
|
const report = JSON.parse(await readFile99(reportPath, "utf8"));
|
|
99496
100067
|
const totalTests = numberOrUndefined(report.total);
|
|
@@ -99519,7 +100090,7 @@ async function collectMarkdownPages(root, hrefPrefix) {
|
|
|
99519
100090
|
const files = await listMarkdownFiles(root);
|
|
99520
100091
|
const pages = [];
|
|
99521
100092
|
for (const filePath of files.slice(0, 40)) {
|
|
99522
|
-
const relativePath =
|
|
100093
|
+
const relativePath = path195.relative(root, filePath).split(path195.sep).join("/");
|
|
99523
100094
|
if (relativePath === "index.md" || relativePath.startsWith("templates/")) {
|
|
99524
100095
|
continue;
|
|
99525
100096
|
}
|
|
@@ -99540,7 +100111,7 @@ async function listMarkdownFiles(root) {
|
|
|
99540
100111
|
const entries = await readdir35(root, { withFileTypes: true });
|
|
99541
100112
|
const files = [];
|
|
99542
100113
|
for (const entry of entries) {
|
|
99543
|
-
const fullPath =
|
|
100114
|
+
const fullPath = path195.join(root, entry.name);
|
|
99544
100115
|
if (entry.isDirectory()) {
|
|
99545
100116
|
files.push(...await listMarkdownFiles(fullPath));
|
|
99546
100117
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
@@ -99588,7 +100159,7 @@ async function writeRecoveredManifest(metaprojectRoot, modules) {
|
|
|
99588
100159
|
const manifest = {
|
|
99589
100160
|
schemaVersion: 1,
|
|
99590
100161
|
standardVersion: STANDARD_VERSION,
|
|
99591
|
-
name: `${
|
|
100162
|
+
name: `${path195.basename(path195.dirname(metaprojectRoot))}-metaproject`,
|
|
99592
100163
|
createdBy: "keryx",
|
|
99593
100164
|
profiles: computeProfiles(enabledModuleKeys),
|
|
99594
100165
|
paths: {
|
|
@@ -99678,11 +100249,11 @@ async function writeRecoveredManifest(metaprojectRoot, modules) {
|
|
|
99678
100249
|
metaproject: ".metaproject/index.md"
|
|
99679
100250
|
}
|
|
99680
100251
|
};
|
|
99681
|
-
await writeFile54(
|
|
100252
|
+
await writeFile54(path195.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
|
|
99682
100253
|
`, "utf8");
|
|
99683
100254
|
}
|
|
99684
100255
|
async function enableTasksInManifest(metaprojectRoot) {
|
|
99685
|
-
const manifestPath =
|
|
100256
|
+
const manifestPath = path195.join(metaprojectRoot, "metaproject.json");
|
|
99686
100257
|
if (!await pathExists(manifestPath)) {
|
|
99687
100258
|
return;
|
|
99688
100259
|
}
|
|
@@ -99705,7 +100276,7 @@ async function enableTasksInManifest(metaprojectRoot) {
|
|
|
99705
100276
|
`, "utf8");
|
|
99706
100277
|
}
|
|
99707
100278
|
async function updateManifestAgentEntrypoints(metaprojectRoot, ruleSources) {
|
|
99708
|
-
const manifestPath =
|
|
100279
|
+
const manifestPath = path195.join(metaprojectRoot, "metaproject.json");
|
|
99709
100280
|
if (!await pathExists(manifestPath)) {
|
|
99710
100281
|
return;
|
|
99711
100282
|
}
|
|
@@ -99741,72 +100312,72 @@ async function updateRuntime(projectRoot) {
|
|
|
99741
100312
|
}
|
|
99742
100313
|
}
|
|
99743
100314
|
async function findRuntimeRoot(projectRoot) {
|
|
99744
|
-
const projectRuntime =
|
|
99745
|
-
if (await pathExists(
|
|
100315
|
+
const projectRuntime = path195.join(projectRoot, ".metaproject", "runtime", "keryx");
|
|
100316
|
+
if (await pathExists(path195.join(projectRuntime, ".git"))) {
|
|
99746
100317
|
return projectRuntime;
|
|
99747
100318
|
}
|
|
99748
100319
|
const home = process.env.HOME;
|
|
99749
100320
|
if (!home) {
|
|
99750
100321
|
return null;
|
|
99751
100322
|
}
|
|
99752
|
-
const globalRuntime =
|
|
99753
|
-
if (await pathExists(
|
|
100323
|
+
const globalRuntime = path195.join(home, ".keryx", "keryx");
|
|
100324
|
+
if (await pathExists(path195.join(globalRuntime, ".git"))) {
|
|
99754
100325
|
return globalRuntime;
|
|
99755
100326
|
}
|
|
99756
100327
|
return null;
|
|
99757
100328
|
}
|
|
99758
100329
|
async function createServiceDirs(metaprojectRoot, modules) {
|
|
99759
100330
|
const dirs = [
|
|
99760
|
-
|
|
99761
|
-
|
|
99762
|
-
|
|
99763
|
-
|
|
99764
|
-
|
|
100331
|
+
path195.join(metaprojectRoot, "core"),
|
|
100332
|
+
path195.join(metaprojectRoot, "hooks", "post-update.d"),
|
|
100333
|
+
path195.join(metaprojectRoot, "modules"),
|
|
100334
|
+
path195.join(metaprojectRoot, "rules"),
|
|
100335
|
+
path195.join(metaprojectRoot, "skills", "project-rules"),
|
|
99765
100336
|
...modules.enableGdgraph ? [
|
|
99766
|
-
|
|
99767
|
-
|
|
100337
|
+
path195.join(metaprojectRoot, "core", "gdgraph"),
|
|
100338
|
+
path195.join(metaprojectRoot, "skills", "gdgraph")
|
|
99768
100339
|
] : [],
|
|
99769
100340
|
...modules.enableGdctx ? [
|
|
99770
|
-
|
|
99771
|
-
|
|
100341
|
+
path195.join(metaprojectRoot, "core", "gdctx"),
|
|
100342
|
+
path195.join(metaprojectRoot, "skills", "gdctx")
|
|
99772
100343
|
] : [],
|
|
99773
100344
|
...modules.enableGdwiki ? [
|
|
99774
|
-
|
|
99775
|
-
|
|
100345
|
+
path195.join(metaprojectRoot, "skills", "gdwiki"),
|
|
100346
|
+
path195.join(metaprojectRoot, "wiki", "templates")
|
|
99776
100347
|
] : [],
|
|
99777
100348
|
...modules.enableHealth ? [
|
|
99778
|
-
|
|
99779
|
-
|
|
100349
|
+
path195.join(metaprojectRoot, "core", "health"),
|
|
100350
|
+
path195.join(metaprojectRoot, "skills", "health")
|
|
99780
100351
|
] : [],
|
|
99781
100352
|
...modules.enableTesting ? [
|
|
99782
|
-
|
|
99783
|
-
|
|
100353
|
+
path195.join(metaprojectRoot, "core", "testing"),
|
|
100354
|
+
path195.join(metaprojectRoot, "skills", "testing")
|
|
99784
100355
|
] : [],
|
|
99785
100356
|
...modules.enableMemory ? [
|
|
99786
|
-
|
|
99787
|
-
|
|
99788
|
-
|
|
100357
|
+
path195.join(metaprojectRoot, "core", "memory"),
|
|
100358
|
+
path195.join(metaprojectRoot, "skills", "memory"),
|
|
100359
|
+
path195.join(metaprojectRoot, "memory", "templates")
|
|
99789
100360
|
] : [],
|
|
99790
100361
|
...modules.enableTasks ? [
|
|
99791
|
-
|
|
99792
|
-
|
|
100362
|
+
path195.join(metaprojectRoot, "flows"),
|
|
100363
|
+
path195.join(metaprojectRoot, "skills", "flow")
|
|
99793
100364
|
] : [],
|
|
99794
100365
|
...modules.enableSecurity ? [
|
|
99795
|
-
|
|
100366
|
+
path195.join(metaprojectRoot, "core", "security")
|
|
99796
100367
|
] : [],
|
|
99797
100368
|
...modules.enableSac ? [
|
|
99798
|
-
|
|
100369
|
+
path195.join(metaprojectRoot, "skills", "sac")
|
|
99799
100370
|
] : []
|
|
99800
100371
|
];
|
|
99801
100372
|
await Promise.all(dirs.map((dir) => mkdir62(dir, { recursive: true })));
|
|
99802
100373
|
}
|
|
99803
100374
|
async function installGdgraphCoreScripts2(metaprojectRoot) {
|
|
99804
|
-
const gdgraphCoreRoot =
|
|
100375
|
+
const gdgraphCoreRoot = path195.join(metaprojectRoot, "core", "gdgraph");
|
|
99805
100376
|
await mkdir62(gdgraphCoreRoot, { recursive: true });
|
|
99806
100377
|
for (const file of GDGRAPH_CORE_SOURCES) {
|
|
99807
|
-
await copyFileIfChanged2(runtimeSourcePath2(`../gdgraph/${file}`),
|
|
100378
|
+
await copyFileIfChanged2(runtimeSourcePath2(`../gdgraph/${file}`), path195.join(gdgraphCoreRoot, file));
|
|
99808
100379
|
}
|
|
99809
|
-
await writeTextIfChanged4(
|
|
100380
|
+
await writeTextIfChanged4(path195.join(gdgraphCoreRoot, "cli.ts"), renderGdgraphCoreCli());
|
|
99810
100381
|
}
|
|
99811
100382
|
async function installManagedHook2(projectRoot, hookName, blockId, content) {
|
|
99812
100383
|
const hooksRoot = await resolveGitHooksRoot(projectRoot);
|
|
@@ -99814,7 +100385,7 @@ async function installManagedHook2(projectRoot, hookName, blockId, content) {
|
|
|
99814
100385
|
return;
|
|
99815
100386
|
}
|
|
99816
100387
|
await mkdir62(hooksRoot, { recursive: true });
|
|
99817
|
-
const hookPath =
|
|
100388
|
+
const hookPath = path195.join(hooksRoot, hookName);
|
|
99818
100389
|
const blockStart = `# keryx:${blockId}:begin`;
|
|
99819
100390
|
const blockEnd = `# keryx:${blockId}:end`;
|
|
99820
100391
|
const managedBlock = `${blockStart}
|
|
@@ -99835,7 +100406,7 @@ async function removeManagedHook2(projectRoot, hookName, blockId) {
|
|
|
99835
100406
|
if (!hooksRoot) {
|
|
99836
100407
|
return;
|
|
99837
100408
|
}
|
|
99838
|
-
const hookPath =
|
|
100409
|
+
const hookPath = path195.join(hooksRoot, hookName);
|
|
99839
100410
|
if (!await pathExists(hookPath)) {
|
|
99840
100411
|
return;
|
|
99841
100412
|
}
|
|
@@ -99857,7 +100428,7 @@ async function prePushHasSecurityBlock2(projectRoot) {
|
|
|
99857
100428
|
if (!hooksRoot) {
|
|
99858
100429
|
return false;
|
|
99859
100430
|
}
|
|
99860
|
-
const hookPath =
|
|
100431
|
+
const hookPath = path195.join(hooksRoot, "pre-push");
|
|
99861
100432
|
if (!await pathExists(hookPath)) {
|
|
99862
100433
|
return false;
|
|
99863
100434
|
}
|
|
@@ -99872,7 +100443,7 @@ async function agentSettingsHasSecuritySentinel2(projectRoot) {
|
|
|
99872
100443
|
return (await readFile99(file, "utf8")).includes(AGENT_HOOKS_SENTINEL);
|
|
99873
100444
|
}
|
|
99874
100445
|
async function readManifest5(metaprojectRoot) {
|
|
99875
|
-
const manifestPath =
|
|
100446
|
+
const manifestPath = path195.join(metaprojectRoot, "metaproject.json");
|
|
99876
100447
|
if (!await pathExists(manifestPath)) {
|
|
99877
100448
|
return {
|
|
99878
100449
|
exists: false,
|
|
@@ -99941,7 +100512,7 @@ async function inferManifestFromExistingMetaproject(metaprojectRoot) {
|
|
|
99941
100512
|
}
|
|
99942
100513
|
async function anyPathExists(root, candidates) {
|
|
99943
100514
|
for (const candidate of candidates) {
|
|
99944
|
-
if (await pathExists(
|
|
100515
|
+
if (await pathExists(path195.join(root, candidate))) {
|
|
99945
100516
|
return true;
|
|
99946
100517
|
}
|
|
99947
100518
|
}
|
|
@@ -99964,13 +100535,13 @@ function parseUpdateArgs(args) {
|
|
|
99964
100535
|
};
|
|
99965
100536
|
}
|
|
99966
100537
|
async function runPostUpdateHooks(projectRoot) {
|
|
99967
|
-
const hooksDir =
|
|
100538
|
+
const hooksDir = path195.join(projectRoot, ".metaproject", "hooks", "post-update.d");
|
|
99968
100539
|
if (!await pathExists(hooksDir)) {
|
|
99969
100540
|
return;
|
|
99970
100541
|
}
|
|
99971
100542
|
const entries = (await readdir35(hooksDir)).sort();
|
|
99972
100543
|
for (const entry of entries) {
|
|
99973
|
-
const hookPath =
|
|
100544
|
+
const hookPath = path195.join(hooksDir, entry);
|
|
99974
100545
|
try {
|
|
99975
100546
|
await accessExecutable(hookPath);
|
|
99976
100547
|
} catch {
|
|
@@ -100011,14 +100582,14 @@ async function writeTextIfChanged4(filePath, content) {
|
|
|
100011
100582
|
if (await pathExists(filePath) && await readFile99(filePath, "utf8") === content) {
|
|
100012
100583
|
return;
|
|
100013
100584
|
}
|
|
100014
|
-
await mkdir62(
|
|
100585
|
+
await mkdir62(path195.dirname(filePath), { recursive: true });
|
|
100015
100586
|
await writeFile54(filePath, content, "utf8");
|
|
100016
100587
|
}
|
|
100017
100588
|
async function writeTextIfMissing4(filePath, content) {
|
|
100018
100589
|
if (await pathExists(filePath)) {
|
|
100019
100590
|
return;
|
|
100020
100591
|
}
|
|
100021
|
-
await mkdir62(
|
|
100592
|
+
await mkdir62(path195.dirname(filePath), { recursive: true });
|
|
100022
100593
|
await writeFile54(filePath, content, "utf8");
|
|
100023
100594
|
}
|
|
100024
100595
|
async function copyFileIfChanged2(from, to) {
|
|
@@ -100026,7 +100597,7 @@ async function copyFileIfChanged2(from, to) {
|
|
|
100026
100597
|
if (await pathExists(to) && await readFile99(to, "utf8") === next) {
|
|
100027
100598
|
return;
|
|
100028
100599
|
}
|
|
100029
|
-
await mkdir62(
|
|
100600
|
+
await mkdir62(path195.dirname(to), { recursive: true });
|
|
100030
100601
|
await writeFile54(to, next, "utf8");
|
|
100031
100602
|
}
|
|
100032
100603
|
function runtimeSourcePath2(relativePath) {
|
|
@@ -100035,7 +100606,7 @@ function runtimeSourcePath2(relativePath) {
|
|
|
100035
100606
|
return directPath;
|
|
100036
100607
|
}
|
|
100037
100608
|
if (relativePath.startsWith("../")) {
|
|
100038
|
-
const packagedSourcePath =
|
|
100609
|
+
const packagedSourcePath = path195.join(path195.dirname(fileURLToPath7(import.meta.url)), "..", "src", relativePath.slice(3));
|
|
100039
100610
|
if (existsSync35(packagedSourcePath)) {
|
|
100040
100611
|
return packagedSourcePath;
|
|
100041
100612
|
}
|
|
@@ -100070,7 +100641,7 @@ function printHelp20() {
|
|
|
100070
100641
|
|
|
100071
100642
|
// src/commands/dashboard.ts
|
|
100072
100643
|
import { spawn as spawn7 } from "child_process";
|
|
100073
|
-
import
|
|
100644
|
+
import path196 from "path";
|
|
100074
100645
|
init_args();
|
|
100075
100646
|
async function dashboardCommand(args = []) {
|
|
100076
100647
|
const options = parseOptions(args);
|
|
@@ -100081,7 +100652,7 @@ async function dashboardCommand(args = []) {
|
|
|
100081
100652
|
}
|
|
100082
100653
|
if (subcommand === "build") {
|
|
100083
100654
|
const result = await buildDashboard();
|
|
100084
|
-
const rel =
|
|
100655
|
+
const rel = path196.relative(process.cwd(), result.path);
|
|
100085
100656
|
console.log(` ${style.green(symbols.ok)} Dashboard built ${style.cyan(symbols.arrow)} ${style.cyan(rel)}`);
|
|
100086
100657
|
note(`Open it: keryx dashboard open`);
|
|
100087
100658
|
return;
|
|
@@ -100089,7 +100660,7 @@ async function dashboardCommand(args = []) {
|
|
|
100089
100660
|
if (subcommand === "open") {
|
|
100090
100661
|
const result = await buildDashboard();
|
|
100091
100662
|
await openFile(result.path);
|
|
100092
|
-
const rel =
|
|
100663
|
+
const rel = path196.relative(process.cwd(), result.path);
|
|
100093
100664
|
console.log(` ${style.green(symbols.ok)} Opened ${style.cyan(rel)}`);
|
|
100094
100665
|
return;
|
|
100095
100666
|
}
|
|
@@ -100138,7 +100709,7 @@ import { readFileSync as readFileSync8 } from "fs";
|
|
|
100138
100709
|
// src/agents/bootstrap.ts
|
|
100139
100710
|
import { mkdir as mkdir63, readFile as readFile100, writeFile as writeFile55 } from "fs/promises";
|
|
100140
100711
|
import { homedir as homedir8 } from "os";
|
|
100141
|
-
import
|
|
100712
|
+
import path197 from "path";
|
|
100142
100713
|
init_fs();
|
|
100143
100714
|
var AGENT_BOOTSTRAP_START = "<!-- keryx:global-bootstrap -->";
|
|
100144
100715
|
var AGENT_BOOTSTRAP_END = "<!-- /keryx:global-bootstrap -->";
|
|
@@ -100148,35 +100719,35 @@ var AGENT_BOOTSTRAP_RUNTIMES = [
|
|
|
100148
100719
|
aliases: ["claude-code"],
|
|
100149
100720
|
label: "Claude Code",
|
|
100150
100721
|
fileName: "CLAUDE.md",
|
|
100151
|
-
filePath: (homeRoot) =>
|
|
100722
|
+
filePath: (homeRoot) => path197.join(homeRoot, ".claude", "CLAUDE.md")
|
|
100152
100723
|
},
|
|
100153
100724
|
{
|
|
100154
100725
|
id: "opencode",
|
|
100155
100726
|
aliases: ["open-code"],
|
|
100156
100727
|
label: "OpenCode",
|
|
100157
100728
|
fileName: "AGENTS.md",
|
|
100158
|
-
filePath: (homeRoot) =>
|
|
100729
|
+
filePath: (homeRoot) => path197.join(homeRoot, ".config", "opencode", "AGENTS.md")
|
|
100159
100730
|
},
|
|
100160
100731
|
{
|
|
100161
100732
|
id: "zcode",
|
|
100162
100733
|
aliases: ["zed", "zed-code"],
|
|
100163
100734
|
label: "ZCode",
|
|
100164
100735
|
fileName: "AGENTS.md",
|
|
100165
|
-
filePath: (homeRoot) =>
|
|
100736
|
+
filePath: (homeRoot) => path197.join(homeRoot, ".zcode", "AGENTS.md")
|
|
100166
100737
|
},
|
|
100167
100738
|
{
|
|
100168
100739
|
id: "codex",
|
|
100169
100740
|
aliases: [],
|
|
100170
100741
|
label: "Codex",
|
|
100171
100742
|
fileName: "AGENTS.md",
|
|
100172
|
-
filePath: (homeRoot) =>
|
|
100743
|
+
filePath: (homeRoot) => path197.join(homeRoot, ".codex", "AGENTS.md")
|
|
100173
100744
|
},
|
|
100174
100745
|
{
|
|
100175
100746
|
id: "antigravity",
|
|
100176
100747
|
aliases: ["antigravuty", "antigravity-code"],
|
|
100177
100748
|
label: "Antigravity",
|
|
100178
100749
|
fileName: "AGENTS.md",
|
|
100179
|
-
filePath: (homeRoot) =>
|
|
100750
|
+
filePath: (homeRoot) => path197.join(homeRoot, ".config", "antigravity", "AGENTS.md")
|
|
100180
100751
|
}
|
|
100181
100752
|
];
|
|
100182
100753
|
function agentBootstrapRuntimeIds() {
|
|
@@ -100224,7 +100795,7 @@ async function installAgentBootstrap(runtime, options = {}) {
|
|
|
100224
100795
|
const dryRun = options.dryRun === true;
|
|
100225
100796
|
const wrote = next !== current;
|
|
100226
100797
|
if (wrote && !dryRun) {
|
|
100227
|
-
await mkdir63(
|
|
100798
|
+
await mkdir63(path197.dirname(filePath), { recursive: true });
|
|
100228
100799
|
await writeFile55(filePath, next, "utf8");
|
|
100229
100800
|
}
|
|
100230
100801
|
const status = dryRun ? statusFromContent(runtime, filePath, exists, next) : await agentBootstrapStatus(runtime, homeRoot);
|
|
@@ -100761,7 +101332,7 @@ function printBootstrapHelp() {
|
|
|
100761
101332
|
// src/commands/metrics.ts
|
|
100762
101333
|
init_args();
|
|
100763
101334
|
import { readFile as readFile101 } from "fs/promises";
|
|
100764
|
-
import
|
|
101335
|
+
import path199 from "path";
|
|
100765
101336
|
|
|
100766
101337
|
// src/metrics/benchmark.ts
|
|
100767
101338
|
var RELIABILITIES2 = new Set(["exact", "estimated", "unknown"]);
|
|
@@ -101578,7 +102149,7 @@ function buildContainmentManifest(inputs, options = {}) {
|
|
|
101578
102149
|
|
|
101579
102150
|
// src/metrics/oracle-runner.ts
|
|
101580
102151
|
import { mkdir as mkdir64, writeFile as writeFile56 } from "fs/promises";
|
|
101581
|
-
import
|
|
102152
|
+
import path198 from "path";
|
|
101582
102153
|
|
|
101583
102154
|
// src/metrics/ir.ts
|
|
101584
102155
|
function toIdSet(ids) {
|
|
@@ -102024,9 +102595,9 @@ function buildEvidenceBundle(input, options = {}) {
|
|
|
102024
102595
|
async function persistEvidenceBundle(outDir, bundle, ladder = "metastore") {
|
|
102025
102596
|
const safeTarget = bundle.target.replace(/[^A-Za-z0-9._/-]/g, "_");
|
|
102026
102597
|
const safeCase = bundle.caseId.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
102027
|
-
const dir =
|
|
102598
|
+
const dir = path198.join(outDir, "bench", ladder, safeTarget, safeCase, bundle.variant, String(bundle.seed));
|
|
102028
102599
|
await mkdir64(dir, { recursive: true });
|
|
102029
|
-
const write = (name, value) => writeFile56(
|
|
102600
|
+
const write = (name, value) => writeFile56(path198.join(dir, name), `${JSON.stringify(value, null, 2)}
|
|
102030
102601
|
`, "utf8");
|
|
102031
102602
|
await Promise.all([
|
|
102032
102603
|
write("inputs.json", bundle.inputs),
|
|
@@ -102066,7 +102637,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
102066
102637
|
console.log("# metrics status");
|
|
102067
102638
|
console.log("");
|
|
102068
102639
|
console.log(`root: ${root}`);
|
|
102069
|
-
console.log(`enabled: ${await Bun.file(
|
|
102640
|
+
console.log(`enabled: ${await Bun.file(path199.join(projectRoot, ".metaproject", "metaproject.json")).exists() ? "yes" : "no"}`);
|
|
102070
102641
|
const latest = await readLatestPointer(root);
|
|
102071
102642
|
console.log(`latest: ${latest.status}`);
|
|
102072
102643
|
return;
|
|
@@ -102078,7 +102649,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
102078
102649
|
process.exitCode = 1;
|
|
102079
102650
|
return;
|
|
102080
102651
|
}
|
|
102081
|
-
const record = JSON.parse(await readFile101(
|
|
102652
|
+
const record = JSON.parse(await readFile101(path199.resolve(projectRoot, file), "utf8"));
|
|
102082
102653
|
const result = validateRunRecord(record);
|
|
102083
102654
|
console.log(result.valid ? "valid: yes" : "valid: no");
|
|
102084
102655
|
for (const error of result.errors)
|
|
@@ -102103,7 +102674,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
102103
102674
|
process.exitCode = 1;
|
|
102104
102675
|
return;
|
|
102105
102676
|
}
|
|
102106
|
-
const file =
|
|
102677
|
+
const file = path199.join(metricsRoot(projectRoot), "runs", `${runId}.json`);
|
|
102107
102678
|
if (!await Bun.file(file).exists()) {
|
|
102108
102679
|
console.error(`Run not found: ${runId}`);
|
|
102109
102680
|
process.exitCode = 1;
|
|
@@ -102120,8 +102691,8 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
102120
102691
|
process.exitCode = 1;
|
|
102121
102692
|
return;
|
|
102122
102693
|
}
|
|
102123
|
-
const a = JSON.parse(await readFile101(
|
|
102124
|
-
const b = JSON.parse(await readFile101(
|
|
102694
|
+
const a = JSON.parse(await readFile101(path199.join(metricsRoot(projectRoot), "runs", `${runA}.json`), "utf8"));
|
|
102695
|
+
const b = JSON.parse(await readFile101(path199.join(metricsRoot(projectRoot), "runs", `${runB}.json`), "utf8"));
|
|
102125
102696
|
const comparison = compareExecutionRuns(a, b);
|
|
102126
102697
|
console.log(stableJson(comparison));
|
|
102127
102698
|
return;
|
|
@@ -102155,8 +102726,8 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
102155
102726
|
return;
|
|
102156
102727
|
}
|
|
102157
102728
|
const template = createPairedBenchmarkTemplate(taskIds);
|
|
102158
|
-
await Bun.write(
|
|
102159
|
-
console.log(`manifest: ${
|
|
102729
|
+
await Bun.write(path199.resolve(projectRoot, out), stableJson(template));
|
|
102730
|
+
console.log(`manifest: ${path199.relative(projectRoot, path199.resolve(projectRoot, out))}`);
|
|
102160
102731
|
return;
|
|
102161
102732
|
}
|
|
102162
102733
|
if (subcommand === "benchmark" && args[1] === "run") {
|
|
@@ -102170,7 +102741,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
102170
102741
|
process.exitCode = 1;
|
|
102171
102742
|
return;
|
|
102172
102743
|
}
|
|
102173
|
-
const raw = JSON.parse(await readFile101(
|
|
102744
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, file), "utf8"));
|
|
102174
102745
|
const input = Array.isArray(raw) ? raw : raw.runs ?? [];
|
|
102175
102746
|
const result = validatePairedBenchmark(input);
|
|
102176
102747
|
console.log(stableJson(result));
|
|
@@ -102182,7 +102753,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
102182
102753
|
process.exitCode = 1;
|
|
102183
102754
|
}
|
|
102184
102755
|
async function loadAffectedSets(projectRoot, file) {
|
|
102185
|
-
const raw = JSON.parse(await readFile101(
|
|
102756
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, file), "utf8"));
|
|
102186
102757
|
const map = new Map;
|
|
102187
102758
|
for (const entry of raw.targets ?? []) {
|
|
102188
102759
|
if (typeof entry.target === "string")
|
|
@@ -102253,7 +102824,7 @@ async function runHarnessLayer(projectRoot, args, ladder) {
|
|
|
102253
102824
|
let tasks;
|
|
102254
102825
|
let model;
|
|
102255
102826
|
try {
|
|
102256
|
-
const raw = JSON.parse(await readFile101(
|
|
102827
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, resultsPath), "utf8"));
|
|
102257
102828
|
tasks = raw.tasks ?? [];
|
|
102258
102829
|
model = raw.model;
|
|
102259
102830
|
} catch (error) {
|
|
@@ -102284,7 +102855,7 @@ async function runSafetyCompletionHonestyLayer(projectRoot, args, ladder) {
|
|
|
102284
102855
|
let cases;
|
|
102285
102856
|
let model;
|
|
102286
102857
|
try {
|
|
102287
|
-
const raw = JSON.parse(await readFile101(
|
|
102858
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, resultsPath), "utf8"));
|
|
102288
102859
|
cases = raw.cases ?? [];
|
|
102289
102860
|
model = raw.model;
|
|
102290
102861
|
} catch (error) {
|
|
@@ -102309,7 +102880,7 @@ async function runSafetyFalsePremiseLayer(projectRoot, args, ladder) {
|
|
|
102309
102880
|
let cases;
|
|
102310
102881
|
let model;
|
|
102311
102882
|
try {
|
|
102312
|
-
const raw = JSON.parse(await readFile101(
|
|
102883
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, resultsPath), "utf8"));
|
|
102313
102884
|
cases = raw.cases ?? [];
|
|
102314
102885
|
model = raw.model;
|
|
102315
102886
|
} catch (error) {
|
|
@@ -102334,7 +102905,7 @@ async function runSafetyContainmentLayer(projectRoot, args, ladder, caseClass) {
|
|
|
102334
102905
|
let cases;
|
|
102335
102906
|
let model;
|
|
102336
102907
|
try {
|
|
102337
|
-
const raw = JSON.parse(await readFile101(
|
|
102908
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, resultsPath), "utf8"));
|
|
102338
102909
|
cases = raw.cases ?? [];
|
|
102339
102910
|
model = raw.model;
|
|
102340
102911
|
} catch (error) {
|
|
@@ -102396,12 +102967,12 @@ async function runGdgraphLayer(projectRoot, args, ladder) {
|
|
|
102396
102967
|
}
|
|
102397
102968
|
const manifests = buildOracleManifestsByGold(inputs, { ladder });
|
|
102398
102969
|
if (outDir) {
|
|
102399
|
-
const resolvedOut =
|
|
102970
|
+
const resolvedOut = path199.resolve(projectRoot, outDir);
|
|
102400
102971
|
for (const input of inputs) {
|
|
102401
102972
|
for (const named of input.golds) {
|
|
102402
102973
|
const bundle = buildEvidenceBundle({ target: input.target, system: input.system, gold: named.gold }, { ladder, goldReference: goldPathFor(named.kind), timestamp: new Date().toISOString() });
|
|
102403
|
-
const dir = await persistEvidenceBundle(
|
|
102404
|
-
console.error(`bundle[${named.kind}]: ${
|
|
102974
|
+
const dir = await persistEvidenceBundle(path199.join(resolvedOut, named.kind), bundle, ladder);
|
|
102975
|
+
console.error(`bundle[${named.kind}]: ${path199.relative(projectRoot, dir)}`);
|
|
102405
102976
|
}
|
|
102406
102977
|
}
|
|
102407
102978
|
}
|
|
@@ -102425,7 +102996,7 @@ async function runGdgraphLayer(projectRoot, args, ladder) {
|
|
|
102425
102996
|
return allValid;
|
|
102426
102997
|
}
|
|
102427
102998
|
async function loadCoverageMap2(projectRoot, file) {
|
|
102428
|
-
const raw = JSON.parse(await readFile101(
|
|
102999
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, file), "utf8"));
|
|
102429
103000
|
return raw.coverageMap ?? {};
|
|
102430
103001
|
}
|
|
102431
103002
|
async function runTestingLayer(projectRoot, args, ladder) {
|
|
@@ -102462,7 +103033,7 @@ async function runTestingLayer(projectRoot, args, ladder) {
|
|
|
102462
103033
|
return result.valid;
|
|
102463
103034
|
}
|
|
102464
103035
|
async function loadMemoryGoldK(projectRoot, file) {
|
|
102465
|
-
const raw = JSON.parse(await readFile101(
|
|
103036
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, file), "utf8"));
|
|
102466
103037
|
return typeof raw.k === "number" && raw.k > 0 ? raw.k : 3;
|
|
102467
103038
|
}
|
|
102468
103039
|
async function runMemoryLayer(projectRoot, args, ladder) {
|
|
@@ -102499,11 +103070,11 @@ async function runMemoryLayer(projectRoot, args, ladder) {
|
|
|
102499
103070
|
return result.valid;
|
|
102500
103071
|
}
|
|
102501
103072
|
async function loadWikiGoldK(projectRoot, file) {
|
|
102502
|
-
const raw = JSON.parse(await readFile101(
|
|
103073
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, file), "utf8"));
|
|
102503
103074
|
return typeof raw.k === "number" && raw.k > 0 ? raw.k : 5;
|
|
102504
103075
|
}
|
|
102505
103076
|
async function loadWikiGroundedness(projectRoot, file) {
|
|
102506
|
-
const raw = JSON.parse(await readFile101(
|
|
103077
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, file), "utf8"));
|
|
102507
103078
|
const map = new Map;
|
|
102508
103079
|
for (const entry of raw.targets ?? []) {
|
|
102509
103080
|
if (typeof entry.target !== "string" || !Array.isArray(entry.scores) || entry.scores.length !== 3)
|
|
@@ -102559,7 +103130,7 @@ async function runWikiLayer(projectRoot, args, ladder) {
|
|
|
102559
103130
|
return result.valid;
|
|
102560
103131
|
}
|
|
102561
103132
|
async function loadGdctxFacts(projectRoot, file) {
|
|
102562
|
-
const raw = JSON.parse(await readFile101(
|
|
103133
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, file), "utf8"));
|
|
102563
103134
|
const inputs = [];
|
|
102564
103135
|
for (const entry of raw.inputs ?? []) {
|
|
102565
103136
|
if (typeof entry.input === "string") {
|
|
@@ -102597,7 +103168,7 @@ async function collect(projectRoot, args) {
|
|
|
102597
103168
|
process.exitCode = 1;
|
|
102598
103169
|
return;
|
|
102599
103170
|
}
|
|
102600
|
-
const raw = JSON.parse(await readFile101(
|
|
103171
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, eventFile), "utf8"));
|
|
102601
103172
|
const events = Array.isArray(raw) ? raw : raw.events;
|
|
102602
103173
|
const startedAt = optionValue(args, "--started-at") ?? events[0]?.timestamp_utc ?? new Date().toISOString();
|
|
102603
103174
|
const finishedAt = optionValue(args, "--finished-at") ?? events.at(-1)?.timestamp_utc ?? startedAt;
|
|
@@ -102613,11 +103184,11 @@ async function collect(projectRoot, args) {
|
|
|
102613
103184
|
parentRunId: optionValue(args, "--parent-run-id") ?? null
|
|
102614
103185
|
});
|
|
102615
103186
|
const result = await writeRunArtifacts(metricsRoot(projectRoot), record, { cwd: projectRoot });
|
|
102616
|
-
console.log(`json: ${
|
|
102617
|
-
console.log(`markdown: ${
|
|
103187
|
+
console.log(`json: ${path199.relative(projectRoot, result.jsonPath)}`);
|
|
103188
|
+
console.log(`markdown: ${path199.relative(projectRoot, result.markdownPath)}`);
|
|
102618
103189
|
}
|
|
102619
103190
|
function metricsRoot(projectRoot) {
|
|
102620
|
-
return
|
|
103191
|
+
return path199.join(projectRoot, ".metaproject", "data", "metrics");
|
|
102621
103192
|
}
|
|
102622
103193
|
function printMetricsHelp() {
|
|
102623
103194
|
console.log(`keryx metrics
|
|
@@ -103090,7 +103661,7 @@ Providers with a sanctioned subscription login: grok (SuperGrok), openai (ChatGP
|
|
|
103090
103661
|
Claude Pro/Max, Gemini Google-account login, and DeepSeek OAuth are not offered.
|
|
103091
103662
|
`);
|
|
103092
103663
|
}
|
|
103093
|
-
async function
|
|
103664
|
+
async function authCommand2(args) {
|
|
103094
103665
|
const command = args[0];
|
|
103095
103666
|
if (!command || command === "--help" || command === "-h") {
|
|
103096
103667
|
printAuthHelp();
|
|
@@ -103484,7 +104055,7 @@ var CLI_ROUTES = {
|
|
|
103484
104055
|
modules: modulesCommand,
|
|
103485
104056
|
projects: projectsCommand,
|
|
103486
104057
|
providers: providersCommand,
|
|
103487
|
-
auth:
|
|
104058
|
+
auth: authCommand2,
|
|
103488
104059
|
serve: serveCommand,
|
|
103489
104060
|
update: updateCommand,
|
|
103490
104061
|
dashboard: dashboardCommand,
|