@mrciphersmith/keryx 0.2.95 → 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 +1077 -486
- 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",
|
|
@@ -71332,7 +71736,20 @@ function buildMcpChildEnv(input) {
|
|
|
71332
71736
|
// src/mcp-servers/runtime.ts
|
|
71333
71737
|
var CLOSE_GRACE_MS = 750;
|
|
71334
71738
|
var KILL_GRACE_MS = 4500;
|
|
71335
|
-
|
|
71739
|
+
async function within(work, ms) {
|
|
71740
|
+
let timer;
|
|
71741
|
+
try {
|
|
71742
|
+
await Promise.race([
|
|
71743
|
+
work,
|
|
71744
|
+
new Promise((resolve) => {
|
|
71745
|
+
timer = setTimeout(resolve, ms);
|
|
71746
|
+
})
|
|
71747
|
+
]);
|
|
71748
|
+
} finally {
|
|
71749
|
+
if (timer !== undefined)
|
|
71750
|
+
clearTimeout(timer);
|
|
71751
|
+
}
|
|
71752
|
+
}
|
|
71336
71753
|
function createMcpRuntime(options) {
|
|
71337
71754
|
const dialling = new AbortController;
|
|
71338
71755
|
const config = loadMcpServers({
|
|
@@ -71363,7 +71780,7 @@ function createMcpRuntime(options) {
|
|
|
71363
71780
|
];
|
|
71364
71781
|
const kills = [];
|
|
71365
71782
|
const env = options.env ?? process.env;
|
|
71366
|
-
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));
|
|
71367
71784
|
const settled = startServers(launchable, connect).then((result) => {
|
|
71368
71785
|
catalog = result.catalog;
|
|
71369
71786
|
states = [...heldStates, ...result.servers].sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -71377,6 +71794,7 @@ function createMcpRuntime(options) {
|
|
|
71377
71794
|
}
|
|
71378
71795
|
await closeServers(fresh);
|
|
71379
71796
|
};
|
|
71797
|
+
let closing;
|
|
71380
71798
|
return {
|
|
71381
71799
|
catalog: () => catalog,
|
|
71382
71800
|
servers: () => states,
|
|
@@ -71386,14 +71804,17 @@ function createMcpRuntime(options) {
|
|
|
71386
71804
|
ready: async () => {
|
|
71387
71805
|
await settled;
|
|
71388
71806
|
},
|
|
71389
|
-
close:
|
|
71807
|
+
close: () => closing ??= closeNow()
|
|
71808
|
+
};
|
|
71809
|
+
async function closeNow() {
|
|
71810
|
+
{
|
|
71390
71811
|
dialling.abort();
|
|
71391
|
-
await
|
|
71812
|
+
await within(settled, CLOSE_GRACE_MS);
|
|
71392
71813
|
await closeOnce(states);
|
|
71393
|
-
await
|
|
71814
|
+
await within(Promise.all(kills), KILL_GRACE_MS);
|
|
71394
71815
|
settled.then(() => closeOnce(states)).catch(() => {});
|
|
71395
71816
|
}
|
|
71396
|
-
}
|
|
71817
|
+
}
|
|
71397
71818
|
}
|
|
71398
71819
|
function handshakeBudgetMs(server) {
|
|
71399
71820
|
const configured = server.startup_timeout_sec;
|
|
@@ -71404,7 +71825,25 @@ function handshakeBudgetMs(server) {
|
|
|
71404
71825
|
}
|
|
71405
71826
|
var MAX_TIMEOUT_MS = 24 * 60 * 60 * 1000;
|
|
71406
71827
|
var DEFAULT_HANDSHAKE_MS = 15000;
|
|
71407
|
-
|
|
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) {
|
|
71408
71847
|
const target = remoteTargetProblem(server.raw, env);
|
|
71409
71848
|
if (target !== undefined) {
|
|
71410
71849
|
throw new Error(`server "${server.name}": ${target}`);
|
|
@@ -71413,8 +71852,11 @@ async function connectRemote(server, env, signal) {
|
|
|
71413
71852
|
if (!resolved.ok) {
|
|
71414
71853
|
throw new Error(`server "${server.name}": ${describeHollow(resolved.hollow)}`);
|
|
71415
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);
|
|
71416
71857
|
return connectHttpMcpServer(server.url, {
|
|
71417
71858
|
headers: resolved.headers,
|
|
71859
|
+
...authProvider === undefined ? {} : { authProvider },
|
|
71418
71860
|
handshakeTimeoutMs: handshakeBudgetMs(server),
|
|
71419
71861
|
...signal === undefined ? {} : { signal }
|
|
71420
71862
|
});
|
|
@@ -71424,9 +71866,9 @@ function timeoutFor(server, rawName) {
|
|
|
71424
71866
|
return;
|
|
71425
71867
|
return server.tool_timeouts?.[rawName] ?? server.tool_timeout_sec;
|
|
71426
71868
|
}
|
|
71427
|
-
async function defaultConnect(server, env, signal, kills) {
|
|
71869
|
+
async function defaultConnect(server, env, signal, kills, configDir) {
|
|
71428
71870
|
if (transportOf(server) === "http")
|
|
71429
|
-
return connectRemote(server, env, signal);
|
|
71871
|
+
return connectRemote(server, env, signal, configDir);
|
|
71430
71872
|
const command = server.command;
|
|
71431
71873
|
if (command === undefined || command === "") {
|
|
71432
71874
|
throw new Error(`server "${server.name}" sets neither command nor url`);
|
|
@@ -71439,22 +71881,22 @@ async function defaultConnect(server, env, signal, kills) {
|
|
|
71439
71881
|
function defaultServerCwd(server, fallbackCwd = process.cwd()) {
|
|
71440
71882
|
if (server.source !== "project")
|
|
71441
71883
|
return fallbackCwd;
|
|
71442
|
-
return
|
|
71884
|
+
return path167.dirname(path167.dirname(server.file));
|
|
71443
71885
|
}
|
|
71444
71886
|
|
|
71445
71887
|
// src/mcp-servers/store.ts
|
|
71446
71888
|
init_config_dir();
|
|
71447
71889
|
import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
|
|
71448
|
-
import
|
|
71890
|
+
import path168 from "path";
|
|
71449
71891
|
var SERVER_NAME_PATTERN = /^[A-Za-z0-9_-]+$/;
|
|
71450
71892
|
function userConfigFile(configDir) {
|
|
71451
|
-
return
|
|
71893
|
+
return path168.join(configDir ?? ensureKeryxConfigDir(), "mcp-servers.json");
|
|
71452
71894
|
}
|
|
71453
71895
|
function projectConfigFile(projectRoot) {
|
|
71454
|
-
return
|
|
71896
|
+
return path168.join(projectRoot, ".keryx", "mcp-servers.json");
|
|
71455
71897
|
}
|
|
71456
71898
|
function overlayFile(configDir) {
|
|
71457
|
-
return
|
|
71899
|
+
return path168.join(configDir ?? ensureKeryxConfigDir(), "mcp-servers-disabled.json");
|
|
71458
71900
|
}
|
|
71459
71901
|
function readForWrite(file) {
|
|
71460
71902
|
const read = readConfigFile(file);
|
|
@@ -71493,7 +71935,7 @@ function writeDocument(file, doc, scope) {
|
|
|
71493
71935
|
writeOwnerOnlyFileAtomic(file, body);
|
|
71494
71936
|
return;
|
|
71495
71937
|
}
|
|
71496
|
-
mkdirSync6(
|
|
71938
|
+
mkdirSync6(path168.dirname(file), { recursive: true });
|
|
71497
71939
|
writeFileSync7(file, body, { encoding: "utf8" });
|
|
71498
71940
|
}
|
|
71499
71941
|
function addServer(options) {
|
|
@@ -71597,7 +72039,9 @@ var MCP_CONSUMER_SUBCOMMANDS = [
|
|
|
71597
72039
|
"disable",
|
|
71598
72040
|
"trust",
|
|
71599
72041
|
"untrust",
|
|
71600
|
-
"doctor"
|
|
72042
|
+
"doctor",
|
|
72043
|
+
"auth",
|
|
72044
|
+
"logout"
|
|
71601
72045
|
];
|
|
71602
72046
|
function isMcpConsumerSubcommand(value) {
|
|
71603
72047
|
return MCP_CONSUMER_SUBCOMMANDS.includes(value ?? "");
|
|
@@ -71620,6 +72064,10 @@ async function runMcpConsumerCommand(subcommand, args, deps) {
|
|
|
71620
72064
|
return trustCommand(args, deps, false);
|
|
71621
72065
|
case "doctor":
|
|
71622
72066
|
return doctorCommand(args, deps);
|
|
72067
|
+
case "auth":
|
|
72068
|
+
return authCommand(args, deps);
|
|
72069
|
+
case "logout":
|
|
72070
|
+
return logoutCommand(args, deps);
|
|
71623
72071
|
}
|
|
71624
72072
|
}
|
|
71625
72073
|
function projectRootOf(deps) {
|
|
@@ -71761,6 +72209,178 @@ function addCommand(argv, deps) {
|
|
|
71761
72209
|
deps.log(`Run \`keryx mcp doctor ${name}\` to check it connects.`);
|
|
71762
72210
|
return 0;
|
|
71763
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
|
+
}
|
|
71764
72384
|
function removeCommand(args, deps) {
|
|
71765
72385
|
const own = splitAtSeparator(args).own;
|
|
71766
72386
|
const unknown = own.filter((arg, index, all) => isUnknownFlag(arg, index, all));
|
|
@@ -71911,9 +72531,10 @@ async function doctorCommand(args, deps) {
|
|
|
71911
72531
|
const env = deps.env ?? process.env;
|
|
71912
72532
|
report = await runDoctor2(config, {
|
|
71913
72533
|
only,
|
|
71914
|
-
connect: deps.connect ?? ((server) => defaultConnect(server, env, dialling.signal, kills)),
|
|
72534
|
+
connect: deps.connect ?? ((server) => defaultConnect(server, env, dialling.signal, kills, deps.configDir)),
|
|
71915
72535
|
heldForApproval: (server) => requiresApproval(server, approvals),
|
|
71916
|
-
env
|
|
72536
|
+
env,
|
|
72537
|
+
configDir: deps.configDir
|
|
71917
72538
|
});
|
|
71918
72539
|
} finally {
|
|
71919
72540
|
process.removeListener("SIGINT", onInt);
|
|
@@ -72020,12 +72641,12 @@ function positional3(args) {
|
|
|
72020
72641
|
|
|
72021
72642
|
// src/commands/serve-mcp.ts
|
|
72022
72643
|
init_args();
|
|
72023
|
-
import
|
|
72644
|
+
import path175 from "path";
|
|
72024
72645
|
|
|
72025
72646
|
// src/mcp/discovery.ts
|
|
72026
72647
|
init_fs();
|
|
72027
72648
|
init_json();
|
|
72028
|
-
import
|
|
72649
|
+
import path169 from "path";
|
|
72029
72650
|
var MODULE_MANIFEST_KEY = {
|
|
72030
72651
|
gdgraph: "gdgraph",
|
|
72031
72652
|
security: "security",
|
|
@@ -72087,7 +72708,7 @@ function buildDiscovery(manifest) {
|
|
|
72087
72708
|
};
|
|
72088
72709
|
}
|
|
72089
72710
|
async function loadDiscovery(cwd) {
|
|
72090
|
-
const manifestPath =
|
|
72711
|
+
const manifestPath = path169.join(cwd, ".metaproject", "metaproject.json");
|
|
72091
72712
|
if (!await pathExists(manifestPath)) {
|
|
72092
72713
|
return buildDiscovery({});
|
|
72093
72714
|
}
|
|
@@ -72160,7 +72781,7 @@ init_store2();
|
|
|
72160
72781
|
init_fs();
|
|
72161
72782
|
import { createHash as createHash34, randomUUID as randomUUID17 } from "crypto";
|
|
72162
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";
|
|
72163
|
-
import
|
|
72784
|
+
import path170 from "path";
|
|
72164
72785
|
|
|
72165
72786
|
// src/sac/policy-experiment.ts
|
|
72166
72787
|
import { createHash as createHash33, createHmac as createHmac2 } from "crypto";
|
|
@@ -72516,7 +73137,7 @@ function hasReportIntegrity(report) {
|
|
|
72516
73137
|
return reportHashPattern.test(reportDigest) && sha2565(body) === reportDigest;
|
|
72517
73138
|
}
|
|
72518
73139
|
async function resolvePolicySelection(workspaceRoot, canonicalFallback) {
|
|
72519
|
-
const configPath =
|
|
73140
|
+
const configPath = path170.join(workspaceRoot, ...policyExperimentConfigPath);
|
|
72520
73141
|
let configJson;
|
|
72521
73142
|
try {
|
|
72522
73143
|
const raw = await readFile93(configPath, "utf8").catch((error) => {
|
|
@@ -72618,7 +73239,7 @@ async function diagnosePolicyReadiness(workspaceRoot) {
|
|
|
72618
73239
|
const integrityReady = steps.every((entry) => entry.step === "activation-flags" || entry.status === "pass");
|
|
72619
73240
|
return Object.freeze({ configPresent, enabled, killSwitch, integrityReady, candidateWouldActivate: integrityReady && enabled && !killSwitch, steps: Object.freeze([...steps]) });
|
|
72620
73241
|
};
|
|
72621
|
-
const configPath =
|
|
73242
|
+
const configPath = path170.join(workspaceRoot, ...policyExperimentConfigPath);
|
|
72622
73243
|
let configJson;
|
|
72623
73244
|
try {
|
|
72624
73245
|
const raw = await readFile93(configPath, "utf8").catch((error) => {
|
|
@@ -72873,9 +73494,9 @@ class FwkReadService {
|
|
|
72873
73494
|
const recordedAt = nowIso2(now);
|
|
72874
73495
|
const id = `receipt-${randomUUID17().replace(/-/g, "").slice(0, 16)}`;
|
|
72875
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"}` } : {} };
|
|
72876
|
-
const ledger =
|
|
72877
|
-
const checkpointPath =
|
|
72878
|
-
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 });
|
|
72879
73500
|
return withFileLock(`${ledger}.lock`, async () => {
|
|
72880
73501
|
const state = await resolveLedgerState(ledger, checkpointPath, this.options.verifyReceiptLedger ?? verifyAccessReceiptLedger);
|
|
72881
73502
|
const receipt = sealAccessReceipt(base, state.headHash);
|
|
@@ -72987,7 +73608,7 @@ init_workspace_service();
|
|
|
72987
73608
|
init_sac();
|
|
72988
73609
|
import { appendFile as appendFile8, mkdir as mkdir59, readFile as readFile94 } from "fs/promises";
|
|
72989
73610
|
import { randomUUID as randomUUID18 } from "crypto";
|
|
72990
|
-
import
|
|
73611
|
+
import path171 from "path";
|
|
72991
73612
|
|
|
72992
73613
|
class CollaborationServiceError extends Error {
|
|
72993
73614
|
code;
|
|
@@ -73003,7 +73624,7 @@ class CollaborationService {
|
|
|
73003
73624
|
this.input = input;
|
|
73004
73625
|
}
|
|
73005
73626
|
file(id) {
|
|
73006
|
-
return
|
|
73627
|
+
return path171.join(this.input.workspaceRoot, ".metaproject", "workspaces", id, "activity.jsonl");
|
|
73007
73628
|
}
|
|
73008
73629
|
async overview(input) {
|
|
73009
73630
|
const manifest = await this.input.workspaces.show(input);
|
|
@@ -73040,7 +73661,7 @@ class CollaborationService {
|
|
|
73040
73661
|
occurredAt: (this.input.now ?? (() => new Date))().toISOString(),
|
|
73041
73662
|
...suppliedHandoff ? { handoff: { ...suppliedHandoff, from: actor.subject } } : {}
|
|
73042
73663
|
});
|
|
73043
|
-
await mkdir59(
|
|
73664
|
+
await mkdir59(path171.dirname(this.file(manifest.id)), { recursive: true, mode: 448 });
|
|
73044
73665
|
await appendFile8(this.file(manifest.id), `${JSON.stringify(activity)}
|
|
73045
73666
|
`, { mode: 384 });
|
|
73046
73667
|
return normalizeCollaborationResult(activity);
|
|
@@ -73095,9 +73716,9 @@ init_workspace_service();
|
|
|
73095
73716
|
init_fs();
|
|
73096
73717
|
init_machine_wrap_up();
|
|
73097
73718
|
import { mkdir as mkdir60, readdir as readdir31, readFile as readFile95 } from "fs/promises";
|
|
73098
|
-
import
|
|
73719
|
+
import path172 from "path";
|
|
73099
73720
|
function externalSlatesDir(cwd) {
|
|
73100
|
-
return
|
|
73721
|
+
return path172.join(cwd, ".keryx", "external-slates");
|
|
73101
73722
|
}
|
|
73102
73723
|
var EXTERNAL_SESSION_ID_PATTERN = /^[A-Za-z0-9_-]{1,128}$/;
|
|
73103
73724
|
function assertValidExternalSessionId(externalSessionId) {
|
|
@@ -73107,14 +73728,14 @@ function assertValidExternalSessionId(externalSessionId) {
|
|
|
73107
73728
|
}
|
|
73108
73729
|
function externalSlatePath(cwd, externalSessionId) {
|
|
73109
73730
|
assertValidExternalSessionId(externalSessionId);
|
|
73110
|
-
return
|
|
73731
|
+
return path172.join(externalSlatesDir(cwd), `${externalSessionId}.json`);
|
|
73111
73732
|
}
|
|
73112
73733
|
function externalSlateLockPath(cwd, externalSessionId) {
|
|
73113
73734
|
return `${externalSlatePath(cwd, externalSessionId)}.lock`;
|
|
73114
73735
|
}
|
|
73115
73736
|
function externalSlateEvidenceDir(cwd, externalSessionId) {
|
|
73116
73737
|
assertValidExternalSessionId(externalSessionId);
|
|
73117
|
-
return
|
|
73738
|
+
return path172.join(externalSlatesDir(cwd), externalSessionId);
|
|
73118
73739
|
}
|
|
73119
73740
|
async function readExternalSlate(cwd, externalSessionId) {
|
|
73120
73741
|
try {
|
|
@@ -73759,7 +74380,7 @@ function buildToolRegistry() {
|
|
|
73759
74380
|
// src/mcp/resources.ts
|
|
73760
74381
|
init_fs();
|
|
73761
74382
|
init_contained_read();
|
|
73762
|
-
import
|
|
74383
|
+
import path173 from "path";
|
|
73763
74384
|
import { lstat as lstat4, readdir as readdir32, realpath as realpath7, stat as stat17 } from "fs/promises";
|
|
73764
74385
|
var URI_PREFIX = "metaproject://";
|
|
73765
74386
|
function mimeForPath(filePath) {
|
|
@@ -73772,13 +74393,13 @@ function mimeForPath(filePath) {
|
|
|
73772
74393
|
return "text/plain";
|
|
73773
74394
|
}
|
|
73774
74395
|
function dataRoot3(cwd) {
|
|
73775
|
-
return
|
|
74396
|
+
return path173.join(cwd, ".metaproject", "data");
|
|
73776
74397
|
}
|
|
73777
74398
|
function wikiRoot(cwd) {
|
|
73778
|
-
return
|
|
74399
|
+
return path173.join(cwd, ".metaproject", "wiki");
|
|
73779
74400
|
}
|
|
73780
74401
|
function memoryRoot2(cwd) {
|
|
73781
|
-
return
|
|
74402
|
+
return path173.join(cwd, ".metaproject", "memory");
|
|
73782
74403
|
}
|
|
73783
74404
|
var MAX_RESOURCE_BYTES = 8 * 1024 * 1024;
|
|
73784
74405
|
async function walkFiles(root, ownerRoot = root, ancestors = new Set) {
|
|
@@ -73804,7 +74425,7 @@ async function walkFiles(root, ownerRoot = root, ancestors = new Set) {
|
|
|
73804
74425
|
return [];
|
|
73805
74426
|
}
|
|
73806
74427
|
for (const entry of entries) {
|
|
73807
|
-
const full =
|
|
74428
|
+
const full = path173.join(root, entry.name);
|
|
73808
74429
|
const info = await stat17(full).catch(() => null);
|
|
73809
74430
|
if (!info || !isPathInside(ownerReal, await realpath7(full).catch(() => "")))
|
|
73810
74431
|
continue;
|
|
@@ -73832,9 +74453,9 @@ async function listArtifacts(cwd) {
|
|
|
73832
74453
|
if (!moduleEntry.isDirectory()) {
|
|
73833
74454
|
continue;
|
|
73834
74455
|
}
|
|
73835
|
-
const artifactsDir =
|
|
74456
|
+
const artifactsDir = path173.join(base, moduleEntry.name, "artifacts");
|
|
73836
74457
|
for (const file of await walkFiles(artifactsDir)) {
|
|
73837
|
-
const rel = toPosix(
|
|
74458
|
+
const rel = toPosix(path173.relative(artifactsDir, file));
|
|
73838
74459
|
const relPath = `${moduleEntry.name}/${rel}`;
|
|
73839
74460
|
listings.push({
|
|
73840
74461
|
uri: `${URI_PREFIX}artifacts/${relPath}`,
|
|
@@ -73848,7 +74469,7 @@ async function listArtifacts(cwd) {
|
|
|
73848
74469
|
async function listUnderRoot(cwd, cls, root) {
|
|
73849
74470
|
const listings = [];
|
|
73850
74471
|
for (const file of await walkFiles(root)) {
|
|
73851
|
-
const rel = toPosix(
|
|
74472
|
+
const rel = toPosix(path173.relative(root, file));
|
|
73852
74473
|
listings.push({
|
|
73853
74474
|
uri: `${URI_PREFIX}${cls}/${rel}`,
|
|
73854
74475
|
name: rel,
|
|
@@ -73898,12 +74519,12 @@ function resolveConfined(cwd, cls, relPath) {
|
|
|
73898
74519
|
if (moduleName.includes("..") || moduleName.length === 0) {
|
|
73899
74520
|
return null;
|
|
73900
74521
|
}
|
|
73901
|
-
const root =
|
|
73902
|
-
const absolute =
|
|
74522
|
+
const root = path173.join(dataRoot3(cwd), moduleName, "artifacts");
|
|
74523
|
+
const absolute = path173.resolve(root, rest);
|
|
73903
74524
|
return isPathInside(root, absolute) ? { root, absolute } : null;
|
|
73904
74525
|
}
|
|
73905
74526
|
const root = cls === "wiki" ? wikiRoot(cwd) : memoryRoot2(cwd);
|
|
73906
|
-
const absolute =
|
|
74527
|
+
const absolute = path173.resolve(root, relPath);
|
|
73907
74528
|
return isPathInside(root, absolute) ? { root, absolute } : null;
|
|
73908
74529
|
}
|
|
73909
74530
|
async function readResource(cwd, roots, uri) {
|
|
@@ -74114,7 +74735,7 @@ async function serveMcpCommand(args = [], cwd = process.cwd()) {
|
|
|
74114
74735
|
return;
|
|
74115
74736
|
}
|
|
74116
74737
|
const http = args.includes("--http");
|
|
74117
|
-
const projectRoot =
|
|
74738
|
+
const projectRoot = path175.resolve(resolveServeRoot(optionValue(args, "--cwd"), cwd, process.env));
|
|
74118
74739
|
try {
|
|
74119
74740
|
await serveMcp({ cwd: projectRoot, http });
|
|
74120
74741
|
} catch (error) {
|
|
@@ -74190,7 +74811,9 @@ function printMcpHelp() {
|
|
|
74190
74811
|
"keryx mcp add <name> --transport http <url> # a remote one",
|
|
74191
74812
|
"keryx mcp remove|enable|disable <name>",
|
|
74192
74813
|
"keryx mcp trust|untrust <name> # approve a project-scoped server",
|
|
74193
|
-
"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"
|
|
74194
74817
|
]);
|
|
74195
74818
|
heading("Publishing keryx itself (the retired `keryx mcp` spellings)");
|
|
74196
74819
|
helpUsage([
|
|
@@ -74216,14 +74839,14 @@ function printMcpHelp() {
|
|
|
74216
74839
|
// src/commands/status.ts
|
|
74217
74840
|
init_fs();
|
|
74218
74841
|
init_json();
|
|
74219
|
-
import
|
|
74842
|
+
import path176 from "path";
|
|
74220
74843
|
async function statusCommand(args = []) {
|
|
74221
74844
|
if (args.includes("--help") || args.includes("-h")) {
|
|
74222
74845
|
printHelp16();
|
|
74223
74846
|
return;
|
|
74224
74847
|
}
|
|
74225
|
-
const root =
|
|
74226
|
-
const manifestPath =
|
|
74848
|
+
const root = path176.join(process.cwd(), ".metaproject");
|
|
74849
|
+
const manifestPath = path176.join(root, "metaproject.json");
|
|
74227
74850
|
if (!await pathExists(root)) {
|
|
74228
74851
|
console.log("Metaproject: not initialized");
|
|
74229
74852
|
console.log("Run: keryx init");
|
|
@@ -74263,7 +74886,7 @@ Reports the workspace root and one enabled/disabled line per module. Use
|
|
|
74263
74886
|
|
|
74264
74887
|
// src/commands/harness.ts
|
|
74265
74888
|
import { readFileSync as readFileSync7, writeFileSync as writeFileSync8 } from "fs";
|
|
74266
|
-
import
|
|
74889
|
+
import path179 from "path";
|
|
74267
74890
|
import { randomUUID as randomUUID20 } from "crypto";
|
|
74268
74891
|
|
|
74269
74892
|
// src/security/harness-scan.ts
|
|
@@ -75336,7 +75959,7 @@ ${command.argv.join(" ")}`);
|
|
|
75336
75959
|
}
|
|
75337
75960
|
|
|
75338
75961
|
// src/harness/process/sandbox/profile.ts
|
|
75339
|
-
import
|
|
75962
|
+
import path177 from "path";
|
|
75340
75963
|
var DEFAULT_SECRET_SUBPATHS = [
|
|
75341
75964
|
".ssh",
|
|
75342
75965
|
".gnupg",
|
|
@@ -75361,7 +75984,7 @@ function defaultReadDenyList(home) {
|
|
|
75361
75984
|
if (!home) {
|
|
75362
75985
|
return [];
|
|
75363
75986
|
}
|
|
75364
|
-
return DEFAULT_SECRET_SUBPATHS.map((sub) =>
|
|
75987
|
+
return DEFAULT_SECRET_SUBPATHS.map((sub) => path177.join(home, sub));
|
|
75365
75988
|
}
|
|
75366
75989
|
function defaultSandboxProfile(cwd, tmpDir, home) {
|
|
75367
75990
|
return {
|
|
@@ -75381,10 +76004,10 @@ function dedupe3(values) {
|
|
|
75381
76004
|
init_config_dir();
|
|
75382
76005
|
init_shell_config();
|
|
75383
76006
|
import { existsSync as existsSync26 } from "fs";
|
|
75384
|
-
import
|
|
76007
|
+
import path178 from "path";
|
|
75385
76008
|
var SECRET_KEY_PATTERN2 = /api[_-]?key|secret|token|password|credential/i;
|
|
75386
76009
|
function sandboxConfigPath(dir) {
|
|
75387
|
-
return
|
|
76010
|
+
return path178.join(path178.dirname(shellConfigPath(dir)), "sandbox.json");
|
|
75388
76011
|
}
|
|
75389
76012
|
function isShellDefault(v) {
|
|
75390
76013
|
return v === "off" || v === "workspace" || v === "strict" || v === "1";
|
|
@@ -76503,7 +77126,7 @@ async function harnessExec(args, deps) {
|
|
|
76503
77126
|
env: commandEnv,
|
|
76504
77127
|
cwd
|
|
76505
77128
|
};
|
|
76506
|
-
const worktreeRoot =
|
|
77129
|
+
const worktreeRoot = path179.parse(path179.resolve(cwd, commandPath)).root || cwd;
|
|
76507
77130
|
const budget = {
|
|
76508
77131
|
reservationId: idSeq(),
|
|
76509
77132
|
maxRuntimeMs: maxRuntimeMs ?? EXEC_DEFAULT_RUNTIME_MS
|
|
@@ -78449,7 +79072,7 @@ function workspaceReadTool(cwd) {
|
|
|
78449
79072
|
// src/harness/tool/builtin/workspace-lifecycle-tool.ts
|
|
78450
79073
|
import { randomUUID as randomUUID22 } from "crypto";
|
|
78451
79074
|
import { writeFile as writeFile52 } from "fs/promises";
|
|
78452
|
-
import
|
|
79075
|
+
import path180 from "path";
|
|
78453
79076
|
init_slate();
|
|
78454
79077
|
init_store3();
|
|
78455
79078
|
init_guard2();
|
|
@@ -78595,7 +79218,7 @@ function workspaceProposeTool(cwd, getSessionDir) {
|
|
|
78595
79218
|
return { output: `workspace_propose: unrecognized 'kind' \u2014 expected one of: ${PROPOSAL_KINDS.join(", ")}`, isError: true };
|
|
78596
79219
|
}
|
|
78597
79220
|
const explicitSessionId = typeof input.sessionId === "string" && input.sessionId.length > 0 ? input.sessionId : undefined;
|
|
78598
|
-
const sessionRef = explicitSessionId ??
|
|
79221
|
+
const sessionRef = explicitSessionId ?? path180.basename(getSessionDir() ?? "");
|
|
78599
79222
|
if (sessionRef.length === 0) {
|
|
78600
79223
|
return { output: "workspace_propose: no 'sessionId' given and no active session in this run", isError: true };
|
|
78601
79224
|
}
|
|
@@ -78767,7 +79390,7 @@ init_shell_config();
|
|
|
78767
79390
|
init_command_risk();
|
|
78768
79391
|
init_shell_syntax();
|
|
78769
79392
|
import { existsSync as existsSync30 } from "fs";
|
|
78770
|
-
import
|
|
79393
|
+
import path181 from "path";
|
|
78771
79394
|
import { createHash as createHash41 } from "crypto";
|
|
78772
79395
|
var PREFIX_BANNED = new Set([
|
|
78773
79396
|
"sh",
|
|
@@ -78972,7 +79595,7 @@ function emptyShellPermissions() {
|
|
|
78972
79595
|
return { allow: [] };
|
|
78973
79596
|
}
|
|
78974
79597
|
function shellPermissionsPath(dir) {
|
|
78975
|
-
return
|
|
79598
|
+
return path181.join(path181.dirname(shellConfigPath(dir)), "permissions.json");
|
|
78976
79599
|
}
|
|
78977
79600
|
function loadShellPermissionsWithAudit(dir) {
|
|
78978
79601
|
try {
|
|
@@ -79010,7 +79633,7 @@ function loadShellPermissions(dir) {
|
|
|
79010
79633
|
function saveShellPermissions(perms, dir, options = {}) {
|
|
79011
79634
|
try {
|
|
79012
79635
|
const file = shellPermissionsPath(dir);
|
|
79013
|
-
ensureKeryxConfigDir(
|
|
79636
|
+
ensureKeryxConfigDir(path181.dirname(file));
|
|
79014
79637
|
const cleaned = Array.from(new Set(perms.allow.map((p) => p.trim()).filter((p) => p.length > 0)));
|
|
79015
79638
|
const body = {
|
|
79016
79639
|
allow: options.skipValidation === true ? cleaned : cleaned.filter((p) => validateShellPattern(p).ok)
|
|
@@ -79449,12 +80072,12 @@ function remoteRequest(providerId, endpoint, query, key) {
|
|
|
79449
80072
|
// src/lib/search-config.ts
|
|
79450
80073
|
init_config_dir();
|
|
79451
80074
|
import { existsSync as existsSync31 } from "fs";
|
|
79452
|
-
import
|
|
80075
|
+
import path182 from "path";
|
|
79453
80076
|
function searchConfigPath(dir) {
|
|
79454
|
-
return
|
|
80077
|
+
return path182.join(keryxConfigDir(dir), "search-providers.json");
|
|
79455
80078
|
}
|
|
79456
80079
|
function searchCredentialPath(dir) {
|
|
79457
|
-
return
|
|
80080
|
+
return path182.join(keryxConfigDir(dir), "search-credentials.json");
|
|
79458
80081
|
}
|
|
79459
80082
|
function readJson5(file) {
|
|
79460
80083
|
try {
|
|
@@ -79583,7 +80206,7 @@ function createDefaultSearchProviderController(configDir) {
|
|
|
79583
80206
|
import { createHash as createHash42, randomUUID as randomUUID24 } from "crypto";
|
|
79584
80207
|
import { mkdtemp, rm as rm13 } from "fs/promises";
|
|
79585
80208
|
import { tmpdir as tmpdir4 } from "os";
|
|
79586
|
-
import
|
|
80209
|
+
import path183 from "path";
|
|
79587
80210
|
init_metaproject_adapter();
|
|
79588
80211
|
init_ledger();
|
|
79589
80212
|
init_orchestrate();
|
|
@@ -79846,8 +80469,8 @@ function createSpawnSubagentTool(deps) {
|
|
|
79846
80469
|
let dispatchId = "";
|
|
79847
80470
|
let closing = false;
|
|
79848
80471
|
try {
|
|
79849
|
-
ephemeralDir = await mkdtemp(
|
|
79850
|
-
dispatchId =
|
|
80472
|
+
ephemeralDir = await mkdtemp(path183.join(tmpdir4(), "keryx-subagent-slate-"));
|
|
80473
|
+
dispatchId = path183.basename(ephemeralDir);
|
|
79851
80474
|
openedChildSlate = await openSlate({
|
|
79852
80475
|
dir: ephemeralDir,
|
|
79853
80476
|
cwd,
|
|
@@ -80090,7 +80713,7 @@ import { randomUUID as randomUUID25 } from "crypto";
|
|
|
80090
80713
|
import { execFile as execFile4 } from "child_process";
|
|
80091
80714
|
import { mkdir as mkdir61 } from "fs/promises";
|
|
80092
80715
|
import { tmpdir as tmpdir6 } from "os";
|
|
80093
|
-
import
|
|
80716
|
+
import path185 from "path";
|
|
80094
80717
|
import { promisify as promisify4 } from "util";
|
|
80095
80718
|
|
|
80096
80719
|
// src/harness/child/git-worktree-port.ts
|
|
@@ -80342,7 +80965,7 @@ function readRuntimeBlock(dispatch) {
|
|
|
80342
80965
|
// src/harness/external/runtime.ts
|
|
80343
80966
|
import { mkdtemp as mkdtemp2, rm as rm14, writeFile as writeFile53 } from "fs/promises";
|
|
80344
80967
|
import { tmpdir as tmpdir5 } from "os";
|
|
80345
|
-
import
|
|
80968
|
+
import path184 from "path";
|
|
80346
80969
|
|
|
80347
80970
|
// src/harness/external/prompt.ts
|
|
80348
80971
|
var EXTERNAL_RUNTIME_DIRECTIVE = [
|
|
@@ -81385,8 +82008,8 @@ async function prepareResultSchema() {
|
|
|
81385
82008
|
try {
|
|
81386
82009
|
const schema = await loadSchema("subagent-result");
|
|
81387
82010
|
const schemaText = JSON.stringify(schema, null, 2);
|
|
81388
|
-
schemaDir = await mkdtemp2(
|
|
81389
|
-
const schemaPath =
|
|
82011
|
+
schemaDir = await mkdtemp2(path184.join(tmpdir5(), "keryx-external-result-schema-"));
|
|
82012
|
+
const schemaPath = path184.join(schemaDir, "subagent-result.schema.json");
|
|
81390
82013
|
await writeFile53(schemaPath, schemaText, "utf8");
|
|
81391
82014
|
return { ok: true, schema, schemaText, schemaPath, schemaDir };
|
|
81392
82015
|
} catch (error) {
|
|
@@ -81643,7 +82266,7 @@ async function createRunExternal(options) {
|
|
|
81643
82266
|
}
|
|
81644
82267
|
const config = gate.config;
|
|
81645
82268
|
const idSeq = options.idSeq ?? (() => randomUUID25());
|
|
81646
|
-
const worktreesDir = options.worktreesDir ??
|
|
82269
|
+
const worktreesDir = options.worktreesDir ?? path185.join(tmpdir6(), "keryx-external-worktrees");
|
|
81647
82270
|
const spawn = options.spawn ?? createBunSpawnPort();
|
|
81648
82271
|
const worktree = options.worktree ?? ensuringParentDir(createGitWorktreePort({ repoRoot: options.cwd, worktreesDir }), worktreesDir);
|
|
81649
82272
|
const readWorkingDiff = options.readWorkingDiff ?? (() => defaultWorkingDiff(options.cwd));
|
|
@@ -81953,7 +82576,7 @@ class LiveMarkdownBlock {
|
|
|
81953
82576
|
// src/tui/theme.ts
|
|
81954
82577
|
init_config_dir();
|
|
81955
82578
|
import { existsSync as existsSync32 } from "fs";
|
|
81956
|
-
import
|
|
82579
|
+
import path186 from "path";
|
|
81957
82580
|
var THEME_NAMES = ["groknight", "grokday", "tokyonight", "keryx"];
|
|
81958
82581
|
var THEME_IDS = ["auto", ...THEME_NAMES];
|
|
81959
82582
|
var DEFAULT_THEME_ID = "groknight";
|
|
@@ -82083,7 +82706,7 @@ function onThemeChange(listener) {
|
|
|
82083
82706
|
};
|
|
82084
82707
|
}
|
|
82085
82708
|
function tuiConfigPath(dir) {
|
|
82086
|
-
return
|
|
82709
|
+
return path186.join(keryxConfigDir(dir), "tui.json");
|
|
82087
82710
|
}
|
|
82088
82711
|
function loadPersistedThemeId(dir) {
|
|
82089
82712
|
try {
|
|
@@ -82140,7 +82763,7 @@ init_workspace_service();
|
|
|
82140
82763
|
init_service8();
|
|
82141
82764
|
init_store2();
|
|
82142
82765
|
init_fs();
|
|
82143
|
-
import
|
|
82766
|
+
import path187 from "path";
|
|
82144
82767
|
import { readFile as readFile97 } from "fs/promises";
|
|
82145
82768
|
var POSITIVE_INTEGER = /^[1-9][0-9]*$/;
|
|
82146
82769
|
var DEFAULT_AUTO_GOAL_ROUNDS = 8;
|
|
@@ -82216,7 +82839,7 @@ function getFlowService() {
|
|
|
82216
82839
|
async function autoProvisionFlow(cwd, goalText) {
|
|
82217
82840
|
const service = getFlowService();
|
|
82218
82841
|
const result = await service.init({ cwd, title: goalText });
|
|
82219
|
-
const acFile =
|
|
82842
|
+
const acFile = path187.join(cwd, result.dir, "acceptance-criteria.md");
|
|
82220
82843
|
await writeFileAtomic(acFile, [
|
|
82221
82844
|
"# Acceptance Criteria",
|
|
82222
82845
|
"",
|
|
@@ -83767,7 +84390,7 @@ init_proposal_lifecycle();
|
|
|
83767
84390
|
init_workspace_service();
|
|
83768
84391
|
import { randomUUID as randomUUID27 } from "crypto";
|
|
83769
84392
|
import { readdir as readdir34, stat as stat18 } from "fs/promises";
|
|
83770
|
-
import
|
|
84393
|
+
import path188 from "path";
|
|
83771
84394
|
|
|
83772
84395
|
// src/sac/lifecycle-flag.ts
|
|
83773
84396
|
init_store();
|
|
@@ -83926,7 +84549,7 @@ async function readExternalUnboundCandidates(cwd) {
|
|
|
83926
84549
|
}
|
|
83927
84550
|
return groups;
|
|
83928
84551
|
}, []));
|
|
83929
|
-
const metaPath =
|
|
84552
|
+
const metaPath = path188.join(extDir, `${id}.json`);
|
|
83930
84553
|
if (await pathExists(unboundDismissedReceiptPath(metaPath)))
|
|
83931
84554
|
continue;
|
|
83932
84555
|
candidates.push({
|
|
@@ -83940,7 +84563,7 @@ async function readExternalUnboundCandidates(cwd) {
|
|
|
83940
84563
|
return candidates;
|
|
83941
84564
|
}
|
|
83942
84565
|
async function readNewestUnboundCandidateForExternal(cwd, externalSessionId) {
|
|
83943
|
-
const evidenceDir =
|
|
84566
|
+
const evidenceDir = path188.join(externalSlatesDir(cwd), externalSessionId);
|
|
83944
84567
|
let entries;
|
|
83945
84568
|
try {
|
|
83946
84569
|
entries = (await readdir34(evidenceDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
|
|
@@ -83949,7 +84572,7 @@ async function readNewestUnboundCandidateForExternal(cwd, externalSessionId) {
|
|
|
83949
84572
|
}
|
|
83950
84573
|
entries.sort();
|
|
83951
84574
|
for (let i = entries.length - 1;i >= 0; i--) {
|
|
83952
|
-
const evidencePath =
|
|
84575
|
+
const evidencePath = path188.join(evidenceDir, entries[i]);
|
|
83953
84576
|
if (await pathExists(unboundDismissedReceiptPath(evidencePath)))
|
|
83954
84577
|
continue;
|
|
83955
84578
|
const result = readConfigFile(evidencePath);
|
|
@@ -83992,7 +84615,7 @@ var SESSION_ATTRIBUTION_SLACK_MS = 5 * 60000;
|
|
|
83992
84615
|
var MTIME_CLOCK_SKEW_TOLERANCE_MS = 5000;
|
|
83993
84616
|
async function collectReceiptTargets(cwd, owner) {
|
|
83994
84617
|
const targets = new Set;
|
|
83995
|
-
const workspacesDir =
|
|
84618
|
+
const workspacesDir = path188.join(cwd, ".metaproject", "workspaces");
|
|
83996
84619
|
let workspaceIds;
|
|
83997
84620
|
try {
|
|
83998
84621
|
workspaceIds = (await readdir34(workspacesDir, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
@@ -84000,7 +84623,7 @@ async function collectReceiptTargets(cwd, owner) {
|
|
|
84000
84623
|
return targets;
|
|
84001
84624
|
}
|
|
84002
84625
|
for (const workspaceId of workspaceIds) {
|
|
84003
|
-
const receiptsDir =
|
|
84626
|
+
const receiptsDir = path188.join(workspacesDir, workspaceId, `${owner}-write-receipts`);
|
|
84004
84627
|
let files;
|
|
84005
84628
|
try {
|
|
84006
84629
|
files = (await readdir34(receiptsDir)).filter((name) => name.endsWith(".json"));
|
|
@@ -84008,7 +84631,7 @@ async function collectReceiptTargets(cwd, owner) {
|
|
|
84008
84631
|
continue;
|
|
84009
84632
|
}
|
|
84010
84633
|
for (const file of files) {
|
|
84011
|
-
const result = readConfigFile(
|
|
84634
|
+
const result = readConfigFile(path188.join(receiptsDir, file));
|
|
84012
84635
|
if (!result.ok)
|
|
84013
84636
|
continue;
|
|
84014
84637
|
try {
|
|
@@ -84051,7 +84674,7 @@ async function findSkillFiles(dir) {
|
|
|
84051
84674
|
}
|
|
84052
84675
|
const found = [];
|
|
84053
84676
|
for (const entry of entries) {
|
|
84054
|
-
const full =
|
|
84677
|
+
const full = path188.join(dir, entry.name);
|
|
84055
84678
|
if (entry.isDirectory()) {
|
|
84056
84679
|
found.push(...await findSkillFiles(full));
|
|
84057
84680
|
} else if (entry.isFile() && entry.name === "SKILL.md") {
|
|
@@ -84110,10 +84733,10 @@ async function detectUnreviewedSacPathChanges(cwd, sessions) {
|
|
|
84110
84733
|
changedAt: attribution.changedAt
|
|
84111
84734
|
});
|
|
84112
84735
|
}
|
|
84113
|
-
const sacSkillsDir =
|
|
84736
|
+
const sacSkillsDir = path188.join(cwd, ".metaproject", "project-skills", "sac");
|
|
84114
84737
|
const skillFiles = await findSkillFiles(sacSkillsDir);
|
|
84115
84738
|
for (const absolutePath of skillFiles) {
|
|
84116
|
-
const targetRef =
|
|
84739
|
+
const targetRef = path188.relative(path188.join(cwd, ".metaproject"), absolutePath).split(path188.sep).join("/");
|
|
84117
84740
|
if (skillReceipts.has(targetRef))
|
|
84118
84741
|
continue;
|
|
84119
84742
|
const attribution = await attributeToSession(absolutePath, scoped);
|
|
@@ -84131,12 +84754,12 @@ async function detectUnreviewedSacPathChanges(cwd, sessions) {
|
|
|
84131
84754
|
return items;
|
|
84132
84755
|
}
|
|
84133
84756
|
async function isSlateEngaged(dir) {
|
|
84134
|
-
if (await pathExists(
|
|
84757
|
+
if (await pathExists(path188.join(dir, "slate.json")))
|
|
84135
84758
|
return true;
|
|
84136
|
-
if (await pathExists(
|
|
84759
|
+
if (await pathExists(path188.join(dir, "terminal-state.json")))
|
|
84137
84760
|
return true;
|
|
84138
84761
|
try {
|
|
84139
|
-
const entries = await readdir34(
|
|
84762
|
+
const entries = await readdir34(path188.join(dir, "slate-archive"));
|
|
84140
84763
|
return entries.length > 0;
|
|
84141
84764
|
} catch {
|
|
84142
84765
|
return false;
|
|
@@ -84150,7 +84773,7 @@ async function safeReadSlate(dir) {
|
|
|
84150
84773
|
}
|
|
84151
84774
|
}
|
|
84152
84775
|
async function readTerminalState(dir) {
|
|
84153
|
-
const result = readConfigFile(
|
|
84776
|
+
const result = readConfigFile(path188.join(dir, "terminal-state.json"));
|
|
84154
84777
|
if (!result.ok) {
|
|
84155
84778
|
return;
|
|
84156
84779
|
}
|
|
@@ -84174,7 +84797,7 @@ async function resolveUnboundCandidateTarget(cwd, target) {
|
|
|
84174
84797
|
if (session === undefined) {
|
|
84175
84798
|
throw new Error(`No session or evidence path matches "${target}"`);
|
|
84176
84799
|
}
|
|
84177
|
-
const archiveDir =
|
|
84800
|
+
const archiveDir = path188.join(sessionDir(session.projectPath, session.id), "slate-archive");
|
|
84178
84801
|
let entries;
|
|
84179
84802
|
try {
|
|
84180
84803
|
entries = (await readdir34(archiveDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
|
|
@@ -84184,7 +84807,7 @@ async function resolveUnboundCandidateTarget(cwd, target) {
|
|
|
84184
84807
|
if (entries.length === 0)
|
|
84185
84808
|
throw new Error(`No unbound-candidate artifacts for session ${session.id}`);
|
|
84186
84809
|
entries.sort();
|
|
84187
|
-
return
|
|
84810
|
+
return path188.join(archiveDir, entries[entries.length - 1]);
|
|
84188
84811
|
}
|
|
84189
84812
|
async function dismissUnboundByTarget(cwd, target, reason) {
|
|
84190
84813
|
const evidencePath = await resolveUnboundCandidateTarget(cwd, target);
|
|
@@ -84210,7 +84833,7 @@ async function dismissUnboundCandidate(candidatePath, reason) {
|
|
|
84210
84833
|
return { removed: candidatePath, receipt: receiptPath };
|
|
84211
84834
|
}
|
|
84212
84835
|
async function readNewestUnboundCandidate(dir) {
|
|
84213
|
-
const archiveDir =
|
|
84836
|
+
const archiveDir = path188.join(dir, "slate-archive");
|
|
84214
84837
|
let entries;
|
|
84215
84838
|
try {
|
|
84216
84839
|
entries = (await readdir34(archiveDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
|
|
@@ -84219,7 +84842,7 @@ async function readNewestUnboundCandidate(dir) {
|
|
|
84219
84842
|
}
|
|
84220
84843
|
entries.sort();
|
|
84221
84844
|
for (let i = entries.length - 1;i >= 0; i--) {
|
|
84222
|
-
const evidencePath =
|
|
84845
|
+
const evidencePath = path188.join(archiveDir, entries[i]);
|
|
84223
84846
|
if (await pathExists(unboundDismissedReceiptPath(evidencePath)))
|
|
84224
84847
|
continue;
|
|
84225
84848
|
const result = readConfigFile(evidencePath);
|
|
@@ -84249,7 +84872,7 @@ function isFailureOutcome(g) {
|
|
|
84249
84872
|
return g.outcome === "error" || g.outcome === "no_credential" || g.outcome === "conflict";
|
|
84250
84873
|
}
|
|
84251
84874
|
async function readNewestWrapUpOutcome(dir) {
|
|
84252
|
-
const archiveDir =
|
|
84875
|
+
const archiveDir = path188.join(dir, "slate-archive");
|
|
84253
84876
|
let entries;
|
|
84254
84877
|
try {
|
|
84255
84878
|
entries = (await readdir34(archiveDir)).filter((name) => name.endsWith("-wrap-up-outcome.json"));
|
|
@@ -84258,7 +84881,7 @@ async function readNewestWrapUpOutcome(dir) {
|
|
|
84258
84881
|
}
|
|
84259
84882
|
entries.sort();
|
|
84260
84883
|
for (let i = entries.length - 1;i >= 0; i--) {
|
|
84261
|
-
const evidencePath =
|
|
84884
|
+
const evidencePath = path188.join(archiveDir, entries[i]);
|
|
84262
84885
|
const result = readConfigFile(evidencePath);
|
|
84263
84886
|
if (!result.ok) {
|
|
84264
84887
|
continue;
|
|
@@ -87739,41 +88362,6 @@ init_patch_risk();
|
|
|
87739
88362
|
init_catalog();
|
|
87740
88363
|
init_grants();
|
|
87741
88364
|
init_login();
|
|
87742
|
-
|
|
87743
|
-
// src/lib/oauth/open-url.ts
|
|
87744
|
-
import { spawn as spawn5 } from "child_process";
|
|
87745
|
-
function browserOpenPlan(url, platform = process.platform, env = process.env) {
|
|
87746
|
-
if (url.length === 0) {
|
|
87747
|
-
return;
|
|
87748
|
-
}
|
|
87749
|
-
if (platform === "darwin") {
|
|
87750
|
-
return { cmd: "open", args: [url] };
|
|
87751
|
-
}
|
|
87752
|
-
if (platform === "win32") {
|
|
87753
|
-
return { cmd: "cmd", args: ["/c", "start", "", url] };
|
|
87754
|
-
}
|
|
87755
|
-
if (typeof env.DISPLAY === "string" && env.DISPLAY.length > 0) {
|
|
87756
|
-
return { cmd: "xdg-open", args: [url] };
|
|
87757
|
-
}
|
|
87758
|
-
if (typeof env.WAYLAND_DISPLAY === "string" && env.WAYLAND_DISPLAY.length > 0) {
|
|
87759
|
-
return { cmd: "xdg-open", args: [url] };
|
|
87760
|
-
}
|
|
87761
|
-
return;
|
|
87762
|
-
}
|
|
87763
|
-
function openVerificationUrl(url, deps = {}) {
|
|
87764
|
-
const plan = browserOpenPlan(url, deps.platform ?? process.platform, deps.env ?? process.env);
|
|
87765
|
-
if (plan === undefined) {
|
|
87766
|
-
return;
|
|
87767
|
-
}
|
|
87768
|
-
const spawnFn = deps.spawn ?? spawn5;
|
|
87769
|
-
try {
|
|
87770
|
-
const child = spawnFn(plan.cmd, plan.args, { stdio: "ignore", detached: true });
|
|
87771
|
-
child.on("error", () => {});
|
|
87772
|
-
child.unref();
|
|
87773
|
-
} catch {}
|
|
87774
|
-
}
|
|
87775
|
-
|
|
87776
|
-
// src/tui/tui-shell.ts
|
|
87777
88365
|
init_shell_config();
|
|
87778
88366
|
init_provider_config();
|
|
87779
88367
|
|
|
@@ -87809,10 +88397,10 @@ function describeElicitationPrompt(tool, inputJson) {
|
|
|
87809
88397
|
// src/lib/permission-mode-config.ts
|
|
87810
88398
|
init_permission_mode();
|
|
87811
88399
|
init_config_dir();
|
|
87812
|
-
import
|
|
88400
|
+
import path189 from "path";
|
|
87813
88401
|
var EMPTY3 = { schemaVersion: 1, projects: {} };
|
|
87814
88402
|
function permissionModeConfigPath(dir) {
|
|
87815
|
-
return
|
|
88403
|
+
return path189.join(keryxConfigDir(dir), "permission-mode.json");
|
|
87816
88404
|
}
|
|
87817
88405
|
function withRegistryLock2(dir, fn) {
|
|
87818
88406
|
return withFileLock2(`${permissionModeConfigPath(dir)}.lock`, fn, {
|
|
@@ -88330,7 +88918,7 @@ function showComposerChoice(otui, r, dock, request) {
|
|
|
88330
88918
|
// src/lib/version-check.ts
|
|
88331
88919
|
init_config_dir();
|
|
88332
88920
|
init_fs();
|
|
88333
|
-
import
|
|
88921
|
+
import path190 from "path";
|
|
88334
88922
|
var REGISTRY_URL = "https://registry.npmjs.org/@mrciphersmith%2Fkeryx/latest";
|
|
88335
88923
|
var FIXED_INSTALL_COMMAND = "npm install -g @mrciphersmith/keryx@latest";
|
|
88336
88924
|
var RESPONSE_BODY_LIMIT_BYTES = 64 * 1024;
|
|
@@ -88532,7 +89120,7 @@ async function checkVersion(options) {
|
|
|
88532
89120
|
const now = options.now ?? Date.now;
|
|
88533
89121
|
const timestamp = now();
|
|
88534
89122
|
const configDir = ensureKeryxConfigDir(options.cacheDir);
|
|
88535
|
-
const cacheFile =
|
|
89123
|
+
const cacheFile = path190.join(configDir, "version-check.json");
|
|
88536
89124
|
const cache = parseCache(cacheFile);
|
|
88537
89125
|
if (cache?.latestVersion !== undefined && cache.successAt !== undefined && timestamp - cache.successAt >= 0 && timestamp - cache.successAt < SUCCESS_CACHE_TTL_MS) {
|
|
88538
89126
|
return resultFor(options.currentVersion, current, cache.latestVersion, "cache");
|
|
@@ -96960,6 +97548,9 @@ Example: keryx shell --provider ollama --model llama3.1:latest`);
|
|
|
96960
97548
|
});
|
|
96961
97549
|
}
|
|
96962
97550
|
} finally {
|
|
97551
|
+
try {
|
|
97552
|
+
await readlineMcp?.close();
|
|
97553
|
+
} catch {}
|
|
96963
97554
|
destroy();
|
|
96964
97555
|
rl.close();
|
|
96965
97556
|
}
|
|
@@ -97105,7 +97696,7 @@ Shell:
|
|
|
97105
97696
|
init_fs();
|
|
97106
97697
|
import { readFile as readFile98 } from "fs/promises";
|
|
97107
97698
|
import { stdin } from "process";
|
|
97108
|
-
import
|
|
97699
|
+
import path191 from "path";
|
|
97109
97700
|
var MODULES = [
|
|
97110
97701
|
{ name: "gdgraph", flag: "--no-gdgraph", desc: "code graph, symbols, affected context", defaultEnabled: true },
|
|
97111
97702
|
{ name: "gdctx", flag: "--no-gdctx", desc: "token-aware command/read output", defaultEnabled: true },
|
|
@@ -97151,8 +97742,8 @@ async function modulesCommand(args = []) {
|
|
|
97151
97742
|
return;
|
|
97152
97743
|
}
|
|
97153
97744
|
const wantsJson = args.includes("--json") && (sub === undefined || sub === "status" || sub === "list" || sub === "--json");
|
|
97154
|
-
const metaprojectRoot =
|
|
97155
|
-
const manifestPath =
|
|
97745
|
+
const metaprojectRoot = path191.join(process.cwd(), ".metaproject");
|
|
97746
|
+
const manifestPath = path191.join(metaprojectRoot, "metaproject.json");
|
|
97156
97747
|
if (!await pathExists(manifestPath)) {
|
|
97157
97748
|
if (wantsJson) {
|
|
97158
97749
|
console.log(JSON.stringify({ schemaVersion: 1, error: "not-initialized", modules: [] }, null, 2));
|
|
@@ -97271,7 +97862,7 @@ init_serve_config();
|
|
|
97271
97862
|
|
|
97272
97863
|
// src/lib/serve-credential.ts
|
|
97273
97864
|
init_config_dir();
|
|
97274
|
-
import { createHash as createHash43, randomBytes as
|
|
97865
|
+
import { createHash as createHash43, randomBytes as randomBytes5, randomUUID as randomUUID30 } from "crypto";
|
|
97275
97866
|
import {
|
|
97276
97867
|
chmodSync as chmodSync4,
|
|
97277
97868
|
closeSync as closeSync3,
|
|
@@ -97283,9 +97874,9 @@ import {
|
|
|
97283
97874
|
unlinkSync as unlinkSync3,
|
|
97284
97875
|
writeFileSync as writeFileSync9
|
|
97285
97876
|
} from "fs";
|
|
97286
|
-
import
|
|
97877
|
+
import path192 from "path";
|
|
97287
97878
|
function serveCredentialPath(dir) {
|
|
97288
|
-
return
|
|
97879
|
+
return path192.join(keryxConfigDir(dir), "serve-credentials.json");
|
|
97289
97880
|
}
|
|
97290
97881
|
function constantTimeEqual(a, b) {
|
|
97291
97882
|
const width = Math.max(a.length, b.length);
|
|
@@ -97391,8 +97982,8 @@ function withServeCredentialLock(dir, fn, onWaiting) {
|
|
|
97391
97982
|
});
|
|
97392
97983
|
}
|
|
97393
97984
|
function mintRecord(now) {
|
|
97394
|
-
const token =
|
|
97395
|
-
const salt =
|
|
97985
|
+
const token = randomBytes5(32).toString("base64url");
|
|
97986
|
+
const salt = randomBytes5(32).toString("hex");
|
|
97396
97987
|
return {
|
|
97397
97988
|
token,
|
|
97398
97989
|
record: { id: randomUUID30(), algorithm: "sha256", salt, hash: hashToken(salt, token), createdAt: now }
|
|
@@ -97519,22 +98110,22 @@ class AuthFailureThrottle {
|
|
|
97519
98110
|
init_config_dir();
|
|
97520
98111
|
import { createHash as createHash44 } from "crypto";
|
|
97521
98112
|
import { existsSync as existsSync34, readdirSync as readdirSync3, rmSync as rmSync2 } from "fs";
|
|
97522
|
-
import
|
|
98113
|
+
import path193 from "path";
|
|
97523
98114
|
var MAX_TURN_EVENTS = 1e4;
|
|
97524
98115
|
function turnsRoot(dir) {
|
|
97525
|
-
return
|
|
98116
|
+
return path193.join(keryxConfigDir(dir), "turns");
|
|
97526
98117
|
}
|
|
97527
98118
|
function turnDir(turnId, dir) {
|
|
97528
|
-
return
|
|
98119
|
+
return path193.join(turnsRoot(dir), turnId);
|
|
97529
98120
|
}
|
|
97530
98121
|
function keyPath(project, idempotencyKey, dir) {
|
|
97531
98122
|
const projectBytes = Buffer.byteLength(project, "utf8");
|
|
97532
98123
|
const digest = createHash44("sha256").update(`${projectBytes}:${project}\x00${idempotencyKey}`, "utf8").digest("hex");
|
|
97533
|
-
return
|
|
98124
|
+
return path193.join(turnsRoot(dir), "keys", `${digest}.json`);
|
|
97534
98125
|
}
|
|
97535
98126
|
function legacyKeyPath(idempotencyKey, dir) {
|
|
97536
98127
|
const digest = createHash44("sha256").update(idempotencyKey, "utf8").digest("hex");
|
|
97537
|
-
return
|
|
98128
|
+
return path193.join(turnsRoot(dir), "keys", `${digest}.json`);
|
|
97538
98129
|
}
|
|
97539
98130
|
function adoptLegacyClaim(project, idempotencyKey, dir) {
|
|
97540
98131
|
const legacy = legacyKeyPath(idempotencyKey, dir);
|
|
@@ -97598,7 +98189,7 @@ function ensureTurnDir(turnId, dir) {
|
|
|
97598
98189
|
}
|
|
97599
98190
|
function createTurnRecord(record, dir) {
|
|
97600
98191
|
ensureTurnDir(record.turnId, dir);
|
|
97601
|
-
writeOwnerOnlyFile(
|
|
98192
|
+
writeOwnerOnlyFile(path193.join(turnDir(record.turnId, dir), "turn.json"), `${JSON.stringify(record, null, 2)}
|
|
97602
98193
|
`);
|
|
97603
98194
|
}
|
|
97604
98195
|
function appendTurnEvent(event, dir, opts) {
|
|
@@ -97607,12 +98198,12 @@ function appendTurnEvent(event, dir, opts) {
|
|
|
97607
98198
|
}
|
|
97608
98199
|
const line = JSON.stringify(event);
|
|
97609
98200
|
try {
|
|
97610
|
-
appendOwnerOnlyLine(
|
|
98201
|
+
appendOwnerOnlyLine(path193.join(turnDir(event.turnId, dir), "events.jsonl"), line);
|
|
97611
98202
|
} catch (error) {
|
|
97612
98203
|
if (error?.code !== "ENOENT") {
|
|
97613
98204
|
throw error;
|
|
97614
98205
|
}
|
|
97615
|
-
appendOwnerOnlyLine(
|
|
98206
|
+
appendOwnerOnlyLine(path193.join(ensureTurnDir(event.turnId, dir), "events.jsonl"), line);
|
|
97616
98207
|
}
|
|
97617
98208
|
return true;
|
|
97618
98209
|
}
|
|
@@ -97620,7 +98211,7 @@ function readTurnEvents(turnId, after = -1, dir) {
|
|
|
97620
98211
|
if (!isTurnId(turnId)) {
|
|
97621
98212
|
return { ok: false, reason: "not-a-turn-id" };
|
|
97622
98213
|
}
|
|
97623
|
-
const read = readTurnFile(
|
|
98214
|
+
const read = readTurnFile(path193.join(turnDir(turnId, dir), "events.jsonl"));
|
|
97624
98215
|
if (!read.ok) {
|
|
97625
98216
|
if (isDefiniteAbsence2(read.reason)) {
|
|
97626
98217
|
return { ok: true, value: [] };
|
|
@@ -97648,7 +98239,7 @@ function readTurnRecord(turnId, dir) {
|
|
|
97648
98239
|
if (!isTurnId(turnId)) {
|
|
97649
98240
|
return { ok: false, reason: "not-a-turn-id" };
|
|
97650
98241
|
}
|
|
97651
|
-
const read = readTurnFile(
|
|
98242
|
+
const read = readTurnFile(path193.join(turnDir(turnId, dir), "turn.json"));
|
|
97652
98243
|
if (!read.ok) {
|
|
97653
98244
|
return { ok: false, reason: read.reason };
|
|
97654
98245
|
}
|
|
@@ -97667,7 +98258,7 @@ function finishTurn(turnId, result, dir) {
|
|
|
97667
98258
|
if (!record.ok) {
|
|
97668
98259
|
return false;
|
|
97669
98260
|
}
|
|
97670
|
-
writeOwnerOnlyFile(
|
|
98261
|
+
writeOwnerOnlyFile(path193.join(turnDir(turnId, dir), "turn.json"), `${JSON.stringify({ ...record.value, result }, null, 2)}
|
|
97671
98262
|
`);
|
|
97672
98263
|
return true;
|
|
97673
98264
|
}
|
|
@@ -97713,7 +98304,7 @@ function releaseIdempotencyKey(project, idempotencyKey, turnId, dir) {
|
|
|
97713
98304
|
|
|
97714
98305
|
// src/lib/serve-turn.ts
|
|
97715
98306
|
import { randomUUID as randomUUID31 } from "crypto";
|
|
97716
|
-
import
|
|
98307
|
+
import path194 from "path";
|
|
97717
98308
|
init_service();
|
|
97718
98309
|
var REMOTE_ORIGIN = "remote:http";
|
|
97719
98310
|
var MAX_PROMPT_CHARS = 32000;
|
|
@@ -97782,9 +98373,9 @@ function isUuid(value) {
|
|
|
97782
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);
|
|
97783
98374
|
}
|
|
97784
98375
|
function resolveProject(declared, dir) {
|
|
97785
|
-
const wanted =
|
|
98376
|
+
const wanted = path194.resolve(declared);
|
|
97786
98377
|
for (const entry of listProjects(dir, () => {})) {
|
|
97787
|
-
if (
|
|
98378
|
+
if (path194.resolve(entry.path) === wanted) {
|
|
97788
98379
|
return { ok: true, project: entry.path };
|
|
97789
98380
|
}
|
|
97790
98381
|
}
|
|
@@ -98820,7 +99411,7 @@ function printHelp19() {
|
|
|
98820
99411
|
import { spawn as spawn6 } from "child_process";
|
|
98821
99412
|
import { chmod as chmod4, mkdir as mkdir62, readFile as readFile99, readdir as readdir35, writeFile as writeFile54 } from "fs/promises";
|
|
98822
99413
|
import { access as access4, constants as constants2, existsSync as existsSync35 } from "fs";
|
|
98823
|
-
import
|
|
99414
|
+
import path195 from "path";
|
|
98824
99415
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
98825
99416
|
init_config2();
|
|
98826
99417
|
init_config();
|
|
@@ -98837,8 +99428,8 @@ async function updateCommand(args = []) {
|
|
|
98837
99428
|
return;
|
|
98838
99429
|
}
|
|
98839
99430
|
const projectRoot = process.cwd();
|
|
98840
|
-
const metaprojectRoot =
|
|
98841
|
-
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)}/`);
|
|
98842
99433
|
if (!await pathExists(metaprojectRoot)) {
|
|
98843
99434
|
console.log(` ${style.red(symbols.cross)} Metaproject is not initialized.`);
|
|
98844
99435
|
console.log(` ${style.cyan(symbols.arrow)} Run ${style.cyan("keryx init")} first.`);
|
|
@@ -98899,7 +99490,7 @@ async function updateCommand(args = []) {
|
|
|
98899
99490
|
nextSteps(steps);
|
|
98900
99491
|
}
|
|
98901
99492
|
async function previewServiceFiles(projectRoot, options) {
|
|
98902
|
-
const metaprojectRoot =
|
|
99493
|
+
const metaprojectRoot = path195.join(projectRoot, ".metaproject");
|
|
98903
99494
|
const manifestState = await readManifest5(metaprojectRoot);
|
|
98904
99495
|
const manifest = manifestState.manifest;
|
|
98905
99496
|
const enableTasks = moduleEnabled2(manifest, "tasks") || !options.noTasks;
|
|
@@ -98928,12 +99519,12 @@ async function previewServiceFiles(projectRoot, options) {
|
|
|
98928
99519
|
});
|
|
98929
99520
|
}
|
|
98930
99521
|
async function refreshServiceFiles(projectRoot, options) {
|
|
98931
|
-
const metaprojectRoot =
|
|
99522
|
+
const metaprojectRoot = path195.join(projectRoot, ".metaproject");
|
|
98932
99523
|
const manifestState = await readManifest5(metaprojectRoot);
|
|
98933
99524
|
const manifest = manifestState.manifest;
|
|
98934
99525
|
const recoveredManifest = !manifestState.exists || !manifestState.valid;
|
|
98935
99526
|
if (manifestState.migrated) {
|
|
98936
|
-
await writeFile54(
|
|
99527
|
+
await writeFile54(path195.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
|
|
98937
99528
|
`, "utf8");
|
|
98938
99529
|
}
|
|
98939
99530
|
const enableGdgraph = moduleEnabled2(manifest, "gdgraph");
|
|
@@ -98970,9 +99561,9 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
98970
99561
|
enableSecurity,
|
|
98971
99562
|
enableSac
|
|
98972
99563
|
});
|
|
98973
|
-
await writeTextIfChanged4(
|
|
98974
|
-
await writeTextIfChanged4(
|
|
98975
|
-
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 }));
|
|
98976
99567
|
await writeRoutingEntrypointPair(metaprojectRoot, {
|
|
98977
99568
|
enableGdgraph,
|
|
98978
99569
|
enableGdctx,
|
|
@@ -98995,7 +99586,7 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
98995
99586
|
}
|
|
98996
99587
|
}
|
|
98997
99588
|
});
|
|
98998
|
-
await writeTextIfChanged4(
|
|
99589
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "keryx-dashboard.html"), renderMetaprojectDashboardHtml({
|
|
98999
99590
|
enableGdgraph,
|
|
99000
99591
|
enableGdctx,
|
|
99001
99592
|
enableGdwiki,
|
|
@@ -99007,7 +99598,7 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
99007
99598
|
enableSecurity,
|
|
99008
99599
|
data: dashboardData
|
|
99009
99600
|
}));
|
|
99010
|
-
await writeTextIfMissing4(
|
|
99601
|
+
await writeTextIfMissing4(path195.join(metaprojectRoot, "README.md"), renderMetaprojectReadme({
|
|
99011
99602
|
enableGdgraph,
|
|
99012
99603
|
enableGdctx,
|
|
99013
99604
|
enableGdwiki,
|
|
@@ -99020,31 +99611,31 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
99020
99611
|
}));
|
|
99021
99612
|
if (enableGdgraph) {
|
|
99022
99613
|
await installGdgraphCoreScripts2(metaprojectRoot);
|
|
99023
|
-
await writeTextIfChanged4(
|
|
99024
|
-
await writeTextIfChanged4(
|
|
99025
|
-
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());
|
|
99026
99617
|
await seedAssetsLock(metaprojectRoot);
|
|
99027
99618
|
if (manifest.modules?.gdgraph?.hooks?.gitPostCommit) {
|
|
99028
99619
|
await installManagedHook2(projectRoot, "post-commit", "gdgraph-post-commit", renderGdgraphPostCommitHook());
|
|
99029
99620
|
}
|
|
99030
99621
|
}
|
|
99031
99622
|
if (enableGdctx) {
|
|
99032
|
-
await writeTextIfMissing4(
|
|
99033
|
-
await writeTextIfChanged4(
|
|
99034
|
-
await writeTextIfChanged4(
|
|
99035
|
-
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());
|
|
99036
99627
|
}
|
|
99037
99628
|
if (enableGdwiki) {
|
|
99038
|
-
await writeTextIfMissing4(
|
|
99039
|
-
await writeTextIfChanged4(
|
|
99040
|
-
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());
|
|
99041
99632
|
if (manifest.modules?.gdgraph?.hooks?.gitPostCommit) {
|
|
99042
99633
|
await installManagedHook2(projectRoot, "post-commit", "gdwiki-post-commit", renderGdwikiPostCommitHook());
|
|
99043
99634
|
}
|
|
99044
99635
|
}
|
|
99045
99636
|
if (enableSac) {
|
|
99046
|
-
await writeTextIfMissing4(
|
|
99047
|
-
await writeTextIfChanged4(
|
|
99637
|
+
await writeTextIfMissing4(path195.join(metaprojectRoot, "modules", "sac.md"), renderSacManifest());
|
|
99638
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "skills", "sac", "SKILL.md"), renderSacSkillReadme());
|
|
99048
99639
|
}
|
|
99049
99640
|
if (enableGdskills) {
|
|
99050
99641
|
await installGdskills(metaprojectRoot, gdskillsProfile, { createDataDirs: false });
|
|
@@ -99053,25 +99644,25 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
99053
99644
|
}
|
|
99054
99645
|
}
|
|
99055
99646
|
if (enableHealth) {
|
|
99056
|
-
await writeTextIfMissing4(
|
|
99057
|
-
await writeTextIfChanged4(
|
|
99058
|
-
await writeTextIfChanged4(
|
|
99059
|
-
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());
|
|
99060
99651
|
if (manifest.modules?.health?.hooks?.gitPostCommit) {
|
|
99061
99652
|
await installManagedHook2(projectRoot, "post-commit", "health-post-commit", renderHealthPostCommitHook());
|
|
99062
99653
|
}
|
|
99063
99654
|
}
|
|
99064
99655
|
if (enableTesting) {
|
|
99065
|
-
await writeTextIfMissing4(
|
|
99656
|
+
await writeTextIfMissing4(path195.join(metaprojectRoot, "testing.config.json"), renderTestingConfig({
|
|
99066
99657
|
postCommitRefresh: Boolean(manifest.modules?.testing?.hooks?.gitPostCommit),
|
|
99067
99658
|
prePushGate: Boolean(manifest.modules?.testing?.hooks?.prePush)
|
|
99068
99659
|
}));
|
|
99069
|
-
await writeTextIfChanged4(
|
|
99070
|
-
await writeTextIfChanged4(
|
|
99071
|
-
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());
|
|
99072
99663
|
if (enableGdwiki) {
|
|
99073
|
-
await writeTextIfMissing4(
|
|
99074
|
-
await writeTextIfMissing4(
|
|
99664
|
+
await writeTextIfMissing4(path195.join(metaprojectRoot, "wiki", "testing", "README.md"), renderTestingWikiReadme());
|
|
99665
|
+
await writeTextIfMissing4(path195.join(metaprojectRoot, "wiki", "testing", "conventions.md"), renderTestingWikiConventions());
|
|
99075
99666
|
}
|
|
99076
99667
|
if (manifest.modules?.testing?.hooks?.gitPostCommit) {
|
|
99077
99668
|
await installManagedHook2(projectRoot, "post-commit", "testing-post-commit", renderTestingPostCommitHook());
|
|
@@ -99084,24 +99675,24 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
99084
99675
|
await installManagedHook2(projectRoot, "post-commit", "metaproject-dashboard-post-commit", renderMetaprojectDashboardPostCommitHook());
|
|
99085
99676
|
}
|
|
99086
99677
|
if (enableMemory) {
|
|
99087
|
-
await writeTextIfMissing4(
|
|
99088
|
-
await writeTextIfMissing4(
|
|
99089
|
-
await writeTextIfChanged4(
|
|
99090
|
-
await writeTextIfChanged4(
|
|
99091
|
-
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());
|
|
99092
99683
|
}
|
|
99093
99684
|
if (enableTasks) {
|
|
99094
|
-
await writeTextIfChanged4(
|
|
99095
|
-
await writeTextIfChanged4(
|
|
99096
|
-
await writeTextIfChanged4(
|
|
99097
|
-
await writeTextIfChanged4(
|
|
99098
|
-
await writeTextIfChanged4(
|
|
99099
|
-
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());
|
|
99100
99691
|
}
|
|
99101
99692
|
if (enableSecurity) {
|
|
99102
|
-
await writeTextIfMissing4(
|
|
99103
|
-
await writeTextIfChanged4(
|
|
99104
|
-
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());
|
|
99105
99696
|
if (manifest.modules?.security?.hooks?.prePush) {
|
|
99106
99697
|
await installManagedHook2(projectRoot, "pre-push", "security-pre-push", renderSecurityPrePushHook());
|
|
99107
99698
|
}
|
|
@@ -99152,13 +99743,13 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
99152
99743
|
};
|
|
99153
99744
|
}
|
|
99154
99745
|
async function buildDashboard(projectRoot = process.cwd()) {
|
|
99155
|
-
const metaprojectRoot =
|
|
99746
|
+
const metaprojectRoot = path195.join(projectRoot, ".metaproject");
|
|
99156
99747
|
if (!await pathExists(metaprojectRoot)) {
|
|
99157
99748
|
throw new Error("Metaproject is not initialized. Run: keryx init");
|
|
99158
99749
|
}
|
|
99159
99750
|
const manifest = (await readManifest5(metaprojectRoot)).manifest;
|
|
99160
99751
|
const data = await collectDashboardData(metaprojectRoot);
|
|
99161
|
-
const dashboardPath =
|
|
99752
|
+
const dashboardPath = path195.join(metaprojectRoot, "keryx-dashboard.html");
|
|
99162
99753
|
await writeTextIfChanged4(dashboardPath, renderMetaprojectDashboardHtml({
|
|
99163
99754
|
enableGdgraph: moduleEnabled2(manifest, "gdgraph"),
|
|
99164
99755
|
enableGdctx: moduleEnabled2(manifest, "gdctx"),
|
|
@@ -99178,7 +99769,7 @@ async function shouldInstallDashboardPostCommitHook(projectRoot, manifest) {
|
|
|
99178
99769
|
if (Object.values(modules).some((module) => Boolean(module.hooks?.gitPostCommit))) {
|
|
99179
99770
|
return true;
|
|
99180
99771
|
}
|
|
99181
|
-
const hookPath =
|
|
99772
|
+
const hookPath = path195.join(projectRoot, ".git", "hooks", "post-commit");
|
|
99182
99773
|
if (!await pathExists(hookPath)) {
|
|
99183
99774
|
return false;
|
|
99184
99775
|
}
|
|
@@ -99198,11 +99789,11 @@ async function collectDashboardData(metaprojectRoot) {
|
|
|
99198
99789
|
if (testing) {
|
|
99199
99790
|
data.testing = testing;
|
|
99200
99791
|
}
|
|
99201
|
-
const wiki = await collectMarkdownPages(
|
|
99792
|
+
const wiki = await collectMarkdownPages(path195.join(metaprojectRoot, "wiki"), "wiki");
|
|
99202
99793
|
if (wiki.length > 0) {
|
|
99203
99794
|
data.wiki = { pages: wiki };
|
|
99204
99795
|
}
|
|
99205
|
-
const memory = await collectMarkdownPages(
|
|
99796
|
+
const memory = await collectMarkdownPages(path195.join(metaprojectRoot, "memory"), "memory");
|
|
99206
99797
|
if (memory.length > 0) {
|
|
99207
99798
|
data.memory = { entries: memory };
|
|
99208
99799
|
}
|
|
@@ -99217,7 +99808,7 @@ async function collectDashboardData(metaprojectRoot) {
|
|
|
99217
99808
|
return data;
|
|
99218
99809
|
}
|
|
99219
99810
|
async function collectTasksDashboardData(metaprojectRoot) {
|
|
99220
|
-
const flowsRoot =
|
|
99811
|
+
const flowsRoot = path195.join(metaprojectRoot, "flows");
|
|
99221
99812
|
if (!await pathExists(flowsRoot)) {
|
|
99222
99813
|
return null;
|
|
99223
99814
|
}
|
|
@@ -99229,7 +99820,7 @@ async function collectTasksDashboardData(metaprojectRoot) {
|
|
|
99229
99820
|
}
|
|
99230
99821
|
const flows = [];
|
|
99231
99822
|
for (const dir of dirEntries) {
|
|
99232
|
-
const flowPath =
|
|
99823
|
+
const flowPath = path195.join(flowsRoot, dir, "flow.json");
|
|
99233
99824
|
if (!await pathExists(flowPath)) {
|
|
99234
99825
|
continue;
|
|
99235
99826
|
}
|
|
@@ -99237,7 +99828,7 @@ async function collectTasksDashboardData(metaprojectRoot) {
|
|
|
99237
99828
|
const flow = JSON.parse(await readFile99(flowPath, "utf8"));
|
|
99238
99829
|
const tasks = Array.isArray(flow.tasks) ? flow.tasks : [];
|
|
99239
99830
|
let acTotal = 0;
|
|
99240
|
-
const acPath =
|
|
99831
|
+
const acPath = path195.join(flowsRoot, dir, "acceptance-criteria.md");
|
|
99241
99832
|
if (await pathExists(acPath)) {
|
|
99242
99833
|
const acContent = await readFile99(acPath, "utf8");
|
|
99243
99834
|
acTotal = (acContent.match(/^- AC\d+:/gm) ?? []).length;
|
|
@@ -99291,7 +99882,7 @@ async function collectDashboardDocs(metaprojectRoot, wiki, memory) {
|
|
|
99291
99882
|
"data/testing/context.md"
|
|
99292
99883
|
];
|
|
99293
99884
|
for (const href of staticHrefs) {
|
|
99294
|
-
const filePath =
|
|
99885
|
+
const filePath = path195.join(metaprojectRoot, ...href.split("/"));
|
|
99295
99886
|
if (!await pathExists(filePath)) {
|
|
99296
99887
|
continue;
|
|
99297
99888
|
}
|
|
@@ -99308,7 +99899,7 @@ async function collectDashboardDocs(metaprojectRoot, wiki, memory) {
|
|
|
99308
99899
|
return docs;
|
|
99309
99900
|
}
|
|
99310
99901
|
async function collectHealthDashboardData(metaprojectRoot) {
|
|
99311
|
-
const reportPath =
|
|
99902
|
+
const reportPath = path195.join(metaprojectRoot, "data", "health", "artifacts", "latest.json");
|
|
99312
99903
|
if (!await pathExists(reportPath)) {
|
|
99313
99904
|
return;
|
|
99314
99905
|
}
|
|
@@ -99417,8 +100008,8 @@ function metricToScope(metric) {
|
|
|
99417
100008
|
};
|
|
99418
100009
|
}
|
|
99419
100010
|
async function collectGraphDashboardData(metaprojectRoot) {
|
|
99420
|
-
const nodesPath =
|
|
99421
|
-
const edgesPath =
|
|
100011
|
+
const nodesPath = path195.join(metaprojectRoot, "data", "gdgraph", "storage", "nodes.jsonl");
|
|
100012
|
+
const edgesPath = path195.join(metaprojectRoot, "data", "gdgraph", "storage", "edges.jsonl");
|
|
99422
100013
|
if (!await pathExists(nodesPath) || !await pathExists(edgesPath)) {
|
|
99423
100014
|
return;
|
|
99424
100015
|
}
|
|
@@ -99469,8 +100060,8 @@ async function collectGraphDashboardData(metaprojectRoot) {
|
|
|
99469
100060
|
};
|
|
99470
100061
|
}
|
|
99471
100062
|
async function collectTestingDashboardData(metaprojectRoot) {
|
|
99472
|
-
const reportPath =
|
|
99473
|
-
const contextPath =
|
|
100063
|
+
const reportPath = path195.join(metaprojectRoot, "data", "testing", "artifacts", "latest.json");
|
|
100064
|
+
const contextPath = path195.join(metaprojectRoot, "data", "testing", "context.md");
|
|
99474
100065
|
if (await pathExists(reportPath)) {
|
|
99475
100066
|
const report = JSON.parse(await readFile99(reportPath, "utf8"));
|
|
99476
100067
|
const totalTests = numberOrUndefined(report.total);
|
|
@@ -99499,7 +100090,7 @@ async function collectMarkdownPages(root, hrefPrefix) {
|
|
|
99499
100090
|
const files = await listMarkdownFiles(root);
|
|
99500
100091
|
const pages = [];
|
|
99501
100092
|
for (const filePath of files.slice(0, 40)) {
|
|
99502
|
-
const relativePath =
|
|
100093
|
+
const relativePath = path195.relative(root, filePath).split(path195.sep).join("/");
|
|
99503
100094
|
if (relativePath === "index.md" || relativePath.startsWith("templates/")) {
|
|
99504
100095
|
continue;
|
|
99505
100096
|
}
|
|
@@ -99520,7 +100111,7 @@ async function listMarkdownFiles(root) {
|
|
|
99520
100111
|
const entries = await readdir35(root, { withFileTypes: true });
|
|
99521
100112
|
const files = [];
|
|
99522
100113
|
for (const entry of entries) {
|
|
99523
|
-
const fullPath =
|
|
100114
|
+
const fullPath = path195.join(root, entry.name);
|
|
99524
100115
|
if (entry.isDirectory()) {
|
|
99525
100116
|
files.push(...await listMarkdownFiles(fullPath));
|
|
99526
100117
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
@@ -99568,7 +100159,7 @@ async function writeRecoveredManifest(metaprojectRoot, modules) {
|
|
|
99568
100159
|
const manifest = {
|
|
99569
100160
|
schemaVersion: 1,
|
|
99570
100161
|
standardVersion: STANDARD_VERSION,
|
|
99571
|
-
name: `${
|
|
100162
|
+
name: `${path195.basename(path195.dirname(metaprojectRoot))}-metaproject`,
|
|
99572
100163
|
createdBy: "keryx",
|
|
99573
100164
|
profiles: computeProfiles(enabledModuleKeys),
|
|
99574
100165
|
paths: {
|
|
@@ -99658,11 +100249,11 @@ async function writeRecoveredManifest(metaprojectRoot, modules) {
|
|
|
99658
100249
|
metaproject: ".metaproject/index.md"
|
|
99659
100250
|
}
|
|
99660
100251
|
};
|
|
99661
|
-
await writeFile54(
|
|
100252
|
+
await writeFile54(path195.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
|
|
99662
100253
|
`, "utf8");
|
|
99663
100254
|
}
|
|
99664
100255
|
async function enableTasksInManifest(metaprojectRoot) {
|
|
99665
|
-
const manifestPath =
|
|
100256
|
+
const manifestPath = path195.join(metaprojectRoot, "metaproject.json");
|
|
99666
100257
|
if (!await pathExists(manifestPath)) {
|
|
99667
100258
|
return;
|
|
99668
100259
|
}
|
|
@@ -99685,7 +100276,7 @@ async function enableTasksInManifest(metaprojectRoot) {
|
|
|
99685
100276
|
`, "utf8");
|
|
99686
100277
|
}
|
|
99687
100278
|
async function updateManifestAgentEntrypoints(metaprojectRoot, ruleSources) {
|
|
99688
|
-
const manifestPath =
|
|
100279
|
+
const manifestPath = path195.join(metaprojectRoot, "metaproject.json");
|
|
99689
100280
|
if (!await pathExists(manifestPath)) {
|
|
99690
100281
|
return;
|
|
99691
100282
|
}
|
|
@@ -99721,72 +100312,72 @@ async function updateRuntime(projectRoot) {
|
|
|
99721
100312
|
}
|
|
99722
100313
|
}
|
|
99723
100314
|
async function findRuntimeRoot(projectRoot) {
|
|
99724
|
-
const projectRuntime =
|
|
99725
|
-
if (await pathExists(
|
|
100315
|
+
const projectRuntime = path195.join(projectRoot, ".metaproject", "runtime", "keryx");
|
|
100316
|
+
if (await pathExists(path195.join(projectRuntime, ".git"))) {
|
|
99726
100317
|
return projectRuntime;
|
|
99727
100318
|
}
|
|
99728
100319
|
const home = process.env.HOME;
|
|
99729
100320
|
if (!home) {
|
|
99730
100321
|
return null;
|
|
99731
100322
|
}
|
|
99732
|
-
const globalRuntime =
|
|
99733
|
-
if (await pathExists(
|
|
100323
|
+
const globalRuntime = path195.join(home, ".keryx", "keryx");
|
|
100324
|
+
if (await pathExists(path195.join(globalRuntime, ".git"))) {
|
|
99734
100325
|
return globalRuntime;
|
|
99735
100326
|
}
|
|
99736
100327
|
return null;
|
|
99737
100328
|
}
|
|
99738
100329
|
async function createServiceDirs(metaprojectRoot, modules) {
|
|
99739
100330
|
const dirs = [
|
|
99740
|
-
|
|
99741
|
-
|
|
99742
|
-
|
|
99743
|
-
|
|
99744
|
-
|
|
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"),
|
|
99745
100336
|
...modules.enableGdgraph ? [
|
|
99746
|
-
|
|
99747
|
-
|
|
100337
|
+
path195.join(metaprojectRoot, "core", "gdgraph"),
|
|
100338
|
+
path195.join(metaprojectRoot, "skills", "gdgraph")
|
|
99748
100339
|
] : [],
|
|
99749
100340
|
...modules.enableGdctx ? [
|
|
99750
|
-
|
|
99751
|
-
|
|
100341
|
+
path195.join(metaprojectRoot, "core", "gdctx"),
|
|
100342
|
+
path195.join(metaprojectRoot, "skills", "gdctx")
|
|
99752
100343
|
] : [],
|
|
99753
100344
|
...modules.enableGdwiki ? [
|
|
99754
|
-
|
|
99755
|
-
|
|
100345
|
+
path195.join(metaprojectRoot, "skills", "gdwiki"),
|
|
100346
|
+
path195.join(metaprojectRoot, "wiki", "templates")
|
|
99756
100347
|
] : [],
|
|
99757
100348
|
...modules.enableHealth ? [
|
|
99758
|
-
|
|
99759
|
-
|
|
100349
|
+
path195.join(metaprojectRoot, "core", "health"),
|
|
100350
|
+
path195.join(metaprojectRoot, "skills", "health")
|
|
99760
100351
|
] : [],
|
|
99761
100352
|
...modules.enableTesting ? [
|
|
99762
|
-
|
|
99763
|
-
|
|
100353
|
+
path195.join(metaprojectRoot, "core", "testing"),
|
|
100354
|
+
path195.join(metaprojectRoot, "skills", "testing")
|
|
99764
100355
|
] : [],
|
|
99765
100356
|
...modules.enableMemory ? [
|
|
99766
|
-
|
|
99767
|
-
|
|
99768
|
-
|
|
100357
|
+
path195.join(metaprojectRoot, "core", "memory"),
|
|
100358
|
+
path195.join(metaprojectRoot, "skills", "memory"),
|
|
100359
|
+
path195.join(metaprojectRoot, "memory", "templates")
|
|
99769
100360
|
] : [],
|
|
99770
100361
|
...modules.enableTasks ? [
|
|
99771
|
-
|
|
99772
|
-
|
|
100362
|
+
path195.join(metaprojectRoot, "flows"),
|
|
100363
|
+
path195.join(metaprojectRoot, "skills", "flow")
|
|
99773
100364
|
] : [],
|
|
99774
100365
|
...modules.enableSecurity ? [
|
|
99775
|
-
|
|
100366
|
+
path195.join(metaprojectRoot, "core", "security")
|
|
99776
100367
|
] : [],
|
|
99777
100368
|
...modules.enableSac ? [
|
|
99778
|
-
|
|
100369
|
+
path195.join(metaprojectRoot, "skills", "sac")
|
|
99779
100370
|
] : []
|
|
99780
100371
|
];
|
|
99781
100372
|
await Promise.all(dirs.map((dir) => mkdir62(dir, { recursive: true })));
|
|
99782
100373
|
}
|
|
99783
100374
|
async function installGdgraphCoreScripts2(metaprojectRoot) {
|
|
99784
|
-
const gdgraphCoreRoot =
|
|
100375
|
+
const gdgraphCoreRoot = path195.join(metaprojectRoot, "core", "gdgraph");
|
|
99785
100376
|
await mkdir62(gdgraphCoreRoot, { recursive: true });
|
|
99786
100377
|
for (const file of GDGRAPH_CORE_SOURCES) {
|
|
99787
|
-
await copyFileIfChanged2(runtimeSourcePath2(`../gdgraph/${file}`),
|
|
100378
|
+
await copyFileIfChanged2(runtimeSourcePath2(`../gdgraph/${file}`), path195.join(gdgraphCoreRoot, file));
|
|
99788
100379
|
}
|
|
99789
|
-
await writeTextIfChanged4(
|
|
100380
|
+
await writeTextIfChanged4(path195.join(gdgraphCoreRoot, "cli.ts"), renderGdgraphCoreCli());
|
|
99790
100381
|
}
|
|
99791
100382
|
async function installManagedHook2(projectRoot, hookName, blockId, content) {
|
|
99792
100383
|
const hooksRoot = await resolveGitHooksRoot(projectRoot);
|
|
@@ -99794,7 +100385,7 @@ async function installManagedHook2(projectRoot, hookName, blockId, content) {
|
|
|
99794
100385
|
return;
|
|
99795
100386
|
}
|
|
99796
100387
|
await mkdir62(hooksRoot, { recursive: true });
|
|
99797
|
-
const hookPath =
|
|
100388
|
+
const hookPath = path195.join(hooksRoot, hookName);
|
|
99798
100389
|
const blockStart = `# keryx:${blockId}:begin`;
|
|
99799
100390
|
const blockEnd = `# keryx:${blockId}:end`;
|
|
99800
100391
|
const managedBlock = `${blockStart}
|
|
@@ -99815,7 +100406,7 @@ async function removeManagedHook2(projectRoot, hookName, blockId) {
|
|
|
99815
100406
|
if (!hooksRoot) {
|
|
99816
100407
|
return;
|
|
99817
100408
|
}
|
|
99818
|
-
const hookPath =
|
|
100409
|
+
const hookPath = path195.join(hooksRoot, hookName);
|
|
99819
100410
|
if (!await pathExists(hookPath)) {
|
|
99820
100411
|
return;
|
|
99821
100412
|
}
|
|
@@ -99837,7 +100428,7 @@ async function prePushHasSecurityBlock2(projectRoot) {
|
|
|
99837
100428
|
if (!hooksRoot) {
|
|
99838
100429
|
return false;
|
|
99839
100430
|
}
|
|
99840
|
-
const hookPath =
|
|
100431
|
+
const hookPath = path195.join(hooksRoot, "pre-push");
|
|
99841
100432
|
if (!await pathExists(hookPath)) {
|
|
99842
100433
|
return false;
|
|
99843
100434
|
}
|
|
@@ -99852,7 +100443,7 @@ async function agentSettingsHasSecuritySentinel2(projectRoot) {
|
|
|
99852
100443
|
return (await readFile99(file, "utf8")).includes(AGENT_HOOKS_SENTINEL);
|
|
99853
100444
|
}
|
|
99854
100445
|
async function readManifest5(metaprojectRoot) {
|
|
99855
|
-
const manifestPath =
|
|
100446
|
+
const manifestPath = path195.join(metaprojectRoot, "metaproject.json");
|
|
99856
100447
|
if (!await pathExists(manifestPath)) {
|
|
99857
100448
|
return {
|
|
99858
100449
|
exists: false,
|
|
@@ -99921,7 +100512,7 @@ async function inferManifestFromExistingMetaproject(metaprojectRoot) {
|
|
|
99921
100512
|
}
|
|
99922
100513
|
async function anyPathExists(root, candidates) {
|
|
99923
100514
|
for (const candidate of candidates) {
|
|
99924
|
-
if (await pathExists(
|
|
100515
|
+
if (await pathExists(path195.join(root, candidate))) {
|
|
99925
100516
|
return true;
|
|
99926
100517
|
}
|
|
99927
100518
|
}
|
|
@@ -99944,13 +100535,13 @@ function parseUpdateArgs(args) {
|
|
|
99944
100535
|
};
|
|
99945
100536
|
}
|
|
99946
100537
|
async function runPostUpdateHooks(projectRoot) {
|
|
99947
|
-
const hooksDir =
|
|
100538
|
+
const hooksDir = path195.join(projectRoot, ".metaproject", "hooks", "post-update.d");
|
|
99948
100539
|
if (!await pathExists(hooksDir)) {
|
|
99949
100540
|
return;
|
|
99950
100541
|
}
|
|
99951
100542
|
const entries = (await readdir35(hooksDir)).sort();
|
|
99952
100543
|
for (const entry of entries) {
|
|
99953
|
-
const hookPath =
|
|
100544
|
+
const hookPath = path195.join(hooksDir, entry);
|
|
99954
100545
|
try {
|
|
99955
100546
|
await accessExecutable(hookPath);
|
|
99956
100547
|
} catch {
|
|
@@ -99991,14 +100582,14 @@ async function writeTextIfChanged4(filePath, content) {
|
|
|
99991
100582
|
if (await pathExists(filePath) && await readFile99(filePath, "utf8") === content) {
|
|
99992
100583
|
return;
|
|
99993
100584
|
}
|
|
99994
|
-
await mkdir62(
|
|
100585
|
+
await mkdir62(path195.dirname(filePath), { recursive: true });
|
|
99995
100586
|
await writeFile54(filePath, content, "utf8");
|
|
99996
100587
|
}
|
|
99997
100588
|
async function writeTextIfMissing4(filePath, content) {
|
|
99998
100589
|
if (await pathExists(filePath)) {
|
|
99999
100590
|
return;
|
|
100000
100591
|
}
|
|
100001
|
-
await mkdir62(
|
|
100592
|
+
await mkdir62(path195.dirname(filePath), { recursive: true });
|
|
100002
100593
|
await writeFile54(filePath, content, "utf8");
|
|
100003
100594
|
}
|
|
100004
100595
|
async function copyFileIfChanged2(from, to) {
|
|
@@ -100006,7 +100597,7 @@ async function copyFileIfChanged2(from, to) {
|
|
|
100006
100597
|
if (await pathExists(to) && await readFile99(to, "utf8") === next) {
|
|
100007
100598
|
return;
|
|
100008
100599
|
}
|
|
100009
|
-
await mkdir62(
|
|
100600
|
+
await mkdir62(path195.dirname(to), { recursive: true });
|
|
100010
100601
|
await writeFile54(to, next, "utf8");
|
|
100011
100602
|
}
|
|
100012
100603
|
function runtimeSourcePath2(relativePath) {
|
|
@@ -100015,7 +100606,7 @@ function runtimeSourcePath2(relativePath) {
|
|
|
100015
100606
|
return directPath;
|
|
100016
100607
|
}
|
|
100017
100608
|
if (relativePath.startsWith("../")) {
|
|
100018
|
-
const packagedSourcePath =
|
|
100609
|
+
const packagedSourcePath = path195.join(path195.dirname(fileURLToPath7(import.meta.url)), "..", "src", relativePath.slice(3));
|
|
100019
100610
|
if (existsSync35(packagedSourcePath)) {
|
|
100020
100611
|
return packagedSourcePath;
|
|
100021
100612
|
}
|
|
@@ -100050,7 +100641,7 @@ function printHelp20() {
|
|
|
100050
100641
|
|
|
100051
100642
|
// src/commands/dashboard.ts
|
|
100052
100643
|
import { spawn as spawn7 } from "child_process";
|
|
100053
|
-
import
|
|
100644
|
+
import path196 from "path";
|
|
100054
100645
|
init_args();
|
|
100055
100646
|
async function dashboardCommand(args = []) {
|
|
100056
100647
|
const options = parseOptions(args);
|
|
@@ -100061,7 +100652,7 @@ async function dashboardCommand(args = []) {
|
|
|
100061
100652
|
}
|
|
100062
100653
|
if (subcommand === "build") {
|
|
100063
100654
|
const result = await buildDashboard();
|
|
100064
|
-
const rel =
|
|
100655
|
+
const rel = path196.relative(process.cwd(), result.path);
|
|
100065
100656
|
console.log(` ${style.green(symbols.ok)} Dashboard built ${style.cyan(symbols.arrow)} ${style.cyan(rel)}`);
|
|
100066
100657
|
note(`Open it: keryx dashboard open`);
|
|
100067
100658
|
return;
|
|
@@ -100069,7 +100660,7 @@ async function dashboardCommand(args = []) {
|
|
|
100069
100660
|
if (subcommand === "open") {
|
|
100070
100661
|
const result = await buildDashboard();
|
|
100071
100662
|
await openFile(result.path);
|
|
100072
|
-
const rel =
|
|
100663
|
+
const rel = path196.relative(process.cwd(), result.path);
|
|
100073
100664
|
console.log(` ${style.green(symbols.ok)} Opened ${style.cyan(rel)}`);
|
|
100074
100665
|
return;
|
|
100075
100666
|
}
|
|
@@ -100118,7 +100709,7 @@ import { readFileSync as readFileSync8 } from "fs";
|
|
|
100118
100709
|
// src/agents/bootstrap.ts
|
|
100119
100710
|
import { mkdir as mkdir63, readFile as readFile100, writeFile as writeFile55 } from "fs/promises";
|
|
100120
100711
|
import { homedir as homedir8 } from "os";
|
|
100121
|
-
import
|
|
100712
|
+
import path197 from "path";
|
|
100122
100713
|
init_fs();
|
|
100123
100714
|
var AGENT_BOOTSTRAP_START = "<!-- keryx:global-bootstrap -->";
|
|
100124
100715
|
var AGENT_BOOTSTRAP_END = "<!-- /keryx:global-bootstrap -->";
|
|
@@ -100128,35 +100719,35 @@ var AGENT_BOOTSTRAP_RUNTIMES = [
|
|
|
100128
100719
|
aliases: ["claude-code"],
|
|
100129
100720
|
label: "Claude Code",
|
|
100130
100721
|
fileName: "CLAUDE.md",
|
|
100131
|
-
filePath: (homeRoot) =>
|
|
100722
|
+
filePath: (homeRoot) => path197.join(homeRoot, ".claude", "CLAUDE.md")
|
|
100132
100723
|
},
|
|
100133
100724
|
{
|
|
100134
100725
|
id: "opencode",
|
|
100135
100726
|
aliases: ["open-code"],
|
|
100136
100727
|
label: "OpenCode",
|
|
100137
100728
|
fileName: "AGENTS.md",
|
|
100138
|
-
filePath: (homeRoot) =>
|
|
100729
|
+
filePath: (homeRoot) => path197.join(homeRoot, ".config", "opencode", "AGENTS.md")
|
|
100139
100730
|
},
|
|
100140
100731
|
{
|
|
100141
100732
|
id: "zcode",
|
|
100142
100733
|
aliases: ["zed", "zed-code"],
|
|
100143
100734
|
label: "ZCode",
|
|
100144
100735
|
fileName: "AGENTS.md",
|
|
100145
|
-
filePath: (homeRoot) =>
|
|
100736
|
+
filePath: (homeRoot) => path197.join(homeRoot, ".zcode", "AGENTS.md")
|
|
100146
100737
|
},
|
|
100147
100738
|
{
|
|
100148
100739
|
id: "codex",
|
|
100149
100740
|
aliases: [],
|
|
100150
100741
|
label: "Codex",
|
|
100151
100742
|
fileName: "AGENTS.md",
|
|
100152
|
-
filePath: (homeRoot) =>
|
|
100743
|
+
filePath: (homeRoot) => path197.join(homeRoot, ".codex", "AGENTS.md")
|
|
100153
100744
|
},
|
|
100154
100745
|
{
|
|
100155
100746
|
id: "antigravity",
|
|
100156
100747
|
aliases: ["antigravuty", "antigravity-code"],
|
|
100157
100748
|
label: "Antigravity",
|
|
100158
100749
|
fileName: "AGENTS.md",
|
|
100159
|
-
filePath: (homeRoot) =>
|
|
100750
|
+
filePath: (homeRoot) => path197.join(homeRoot, ".config", "antigravity", "AGENTS.md")
|
|
100160
100751
|
}
|
|
100161
100752
|
];
|
|
100162
100753
|
function agentBootstrapRuntimeIds() {
|
|
@@ -100204,7 +100795,7 @@ async function installAgentBootstrap(runtime, options = {}) {
|
|
|
100204
100795
|
const dryRun = options.dryRun === true;
|
|
100205
100796
|
const wrote = next !== current;
|
|
100206
100797
|
if (wrote && !dryRun) {
|
|
100207
|
-
await mkdir63(
|
|
100798
|
+
await mkdir63(path197.dirname(filePath), { recursive: true });
|
|
100208
100799
|
await writeFile55(filePath, next, "utf8");
|
|
100209
100800
|
}
|
|
100210
100801
|
const status = dryRun ? statusFromContent(runtime, filePath, exists, next) : await agentBootstrapStatus(runtime, homeRoot);
|
|
@@ -100741,7 +101332,7 @@ function printBootstrapHelp() {
|
|
|
100741
101332
|
// src/commands/metrics.ts
|
|
100742
101333
|
init_args();
|
|
100743
101334
|
import { readFile as readFile101 } from "fs/promises";
|
|
100744
|
-
import
|
|
101335
|
+
import path199 from "path";
|
|
100745
101336
|
|
|
100746
101337
|
// src/metrics/benchmark.ts
|
|
100747
101338
|
var RELIABILITIES2 = new Set(["exact", "estimated", "unknown"]);
|
|
@@ -101558,7 +102149,7 @@ function buildContainmentManifest(inputs, options = {}) {
|
|
|
101558
102149
|
|
|
101559
102150
|
// src/metrics/oracle-runner.ts
|
|
101560
102151
|
import { mkdir as mkdir64, writeFile as writeFile56 } from "fs/promises";
|
|
101561
|
-
import
|
|
102152
|
+
import path198 from "path";
|
|
101562
102153
|
|
|
101563
102154
|
// src/metrics/ir.ts
|
|
101564
102155
|
function toIdSet(ids) {
|
|
@@ -102004,9 +102595,9 @@ function buildEvidenceBundle(input, options = {}) {
|
|
|
102004
102595
|
async function persistEvidenceBundle(outDir, bundle, ladder = "metastore") {
|
|
102005
102596
|
const safeTarget = bundle.target.replace(/[^A-Za-z0-9._/-]/g, "_");
|
|
102006
102597
|
const safeCase = bundle.caseId.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
102007
|
-
const dir =
|
|
102598
|
+
const dir = path198.join(outDir, "bench", ladder, safeTarget, safeCase, bundle.variant, String(bundle.seed));
|
|
102008
102599
|
await mkdir64(dir, { recursive: true });
|
|
102009
|
-
const write = (name, value) => writeFile56(
|
|
102600
|
+
const write = (name, value) => writeFile56(path198.join(dir, name), `${JSON.stringify(value, null, 2)}
|
|
102010
102601
|
`, "utf8");
|
|
102011
102602
|
await Promise.all([
|
|
102012
102603
|
write("inputs.json", bundle.inputs),
|
|
@@ -102046,7 +102637,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
102046
102637
|
console.log("# metrics status");
|
|
102047
102638
|
console.log("");
|
|
102048
102639
|
console.log(`root: ${root}`);
|
|
102049
|
-
console.log(`enabled: ${await Bun.file(
|
|
102640
|
+
console.log(`enabled: ${await Bun.file(path199.join(projectRoot, ".metaproject", "metaproject.json")).exists() ? "yes" : "no"}`);
|
|
102050
102641
|
const latest = await readLatestPointer(root);
|
|
102051
102642
|
console.log(`latest: ${latest.status}`);
|
|
102052
102643
|
return;
|
|
@@ -102058,7 +102649,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
102058
102649
|
process.exitCode = 1;
|
|
102059
102650
|
return;
|
|
102060
102651
|
}
|
|
102061
|
-
const record = JSON.parse(await readFile101(
|
|
102652
|
+
const record = JSON.parse(await readFile101(path199.resolve(projectRoot, file), "utf8"));
|
|
102062
102653
|
const result = validateRunRecord(record);
|
|
102063
102654
|
console.log(result.valid ? "valid: yes" : "valid: no");
|
|
102064
102655
|
for (const error of result.errors)
|
|
@@ -102083,7 +102674,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
102083
102674
|
process.exitCode = 1;
|
|
102084
102675
|
return;
|
|
102085
102676
|
}
|
|
102086
|
-
const file =
|
|
102677
|
+
const file = path199.join(metricsRoot(projectRoot), "runs", `${runId}.json`);
|
|
102087
102678
|
if (!await Bun.file(file).exists()) {
|
|
102088
102679
|
console.error(`Run not found: ${runId}`);
|
|
102089
102680
|
process.exitCode = 1;
|
|
@@ -102100,8 +102691,8 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
102100
102691
|
process.exitCode = 1;
|
|
102101
102692
|
return;
|
|
102102
102693
|
}
|
|
102103
|
-
const a = JSON.parse(await readFile101(
|
|
102104
|
-
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"));
|
|
102105
102696
|
const comparison = compareExecutionRuns(a, b);
|
|
102106
102697
|
console.log(stableJson(comparison));
|
|
102107
102698
|
return;
|
|
@@ -102135,8 +102726,8 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
102135
102726
|
return;
|
|
102136
102727
|
}
|
|
102137
102728
|
const template = createPairedBenchmarkTemplate(taskIds);
|
|
102138
|
-
await Bun.write(
|
|
102139
|
-
console.log(`manifest: ${
|
|
102729
|
+
await Bun.write(path199.resolve(projectRoot, out), stableJson(template));
|
|
102730
|
+
console.log(`manifest: ${path199.relative(projectRoot, path199.resolve(projectRoot, out))}`);
|
|
102140
102731
|
return;
|
|
102141
102732
|
}
|
|
102142
102733
|
if (subcommand === "benchmark" && args[1] === "run") {
|
|
@@ -102150,7 +102741,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
102150
102741
|
process.exitCode = 1;
|
|
102151
102742
|
return;
|
|
102152
102743
|
}
|
|
102153
|
-
const raw = JSON.parse(await readFile101(
|
|
102744
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, file), "utf8"));
|
|
102154
102745
|
const input = Array.isArray(raw) ? raw : raw.runs ?? [];
|
|
102155
102746
|
const result = validatePairedBenchmark(input);
|
|
102156
102747
|
console.log(stableJson(result));
|
|
@@ -102162,7 +102753,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
102162
102753
|
process.exitCode = 1;
|
|
102163
102754
|
}
|
|
102164
102755
|
async function loadAffectedSets(projectRoot, file) {
|
|
102165
|
-
const raw = JSON.parse(await readFile101(
|
|
102756
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, file), "utf8"));
|
|
102166
102757
|
const map = new Map;
|
|
102167
102758
|
for (const entry of raw.targets ?? []) {
|
|
102168
102759
|
if (typeof entry.target === "string")
|
|
@@ -102233,7 +102824,7 @@ async function runHarnessLayer(projectRoot, args, ladder) {
|
|
|
102233
102824
|
let tasks;
|
|
102234
102825
|
let model;
|
|
102235
102826
|
try {
|
|
102236
|
-
const raw = JSON.parse(await readFile101(
|
|
102827
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, resultsPath), "utf8"));
|
|
102237
102828
|
tasks = raw.tasks ?? [];
|
|
102238
102829
|
model = raw.model;
|
|
102239
102830
|
} catch (error) {
|
|
@@ -102264,7 +102855,7 @@ async function runSafetyCompletionHonestyLayer(projectRoot, args, ladder) {
|
|
|
102264
102855
|
let cases;
|
|
102265
102856
|
let model;
|
|
102266
102857
|
try {
|
|
102267
|
-
const raw = JSON.parse(await readFile101(
|
|
102858
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, resultsPath), "utf8"));
|
|
102268
102859
|
cases = raw.cases ?? [];
|
|
102269
102860
|
model = raw.model;
|
|
102270
102861
|
} catch (error) {
|
|
@@ -102289,7 +102880,7 @@ async function runSafetyFalsePremiseLayer(projectRoot, args, ladder) {
|
|
|
102289
102880
|
let cases;
|
|
102290
102881
|
let model;
|
|
102291
102882
|
try {
|
|
102292
|
-
const raw = JSON.parse(await readFile101(
|
|
102883
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, resultsPath), "utf8"));
|
|
102293
102884
|
cases = raw.cases ?? [];
|
|
102294
102885
|
model = raw.model;
|
|
102295
102886
|
} catch (error) {
|
|
@@ -102314,7 +102905,7 @@ async function runSafetyContainmentLayer(projectRoot, args, ladder, caseClass) {
|
|
|
102314
102905
|
let cases;
|
|
102315
102906
|
let model;
|
|
102316
102907
|
try {
|
|
102317
|
-
const raw = JSON.parse(await readFile101(
|
|
102908
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, resultsPath), "utf8"));
|
|
102318
102909
|
cases = raw.cases ?? [];
|
|
102319
102910
|
model = raw.model;
|
|
102320
102911
|
} catch (error) {
|
|
@@ -102376,12 +102967,12 @@ async function runGdgraphLayer(projectRoot, args, ladder) {
|
|
|
102376
102967
|
}
|
|
102377
102968
|
const manifests = buildOracleManifestsByGold(inputs, { ladder });
|
|
102378
102969
|
if (outDir) {
|
|
102379
|
-
const resolvedOut =
|
|
102970
|
+
const resolvedOut = path199.resolve(projectRoot, outDir);
|
|
102380
102971
|
for (const input of inputs) {
|
|
102381
102972
|
for (const named of input.golds) {
|
|
102382
102973
|
const bundle = buildEvidenceBundle({ target: input.target, system: input.system, gold: named.gold }, { ladder, goldReference: goldPathFor(named.kind), timestamp: new Date().toISOString() });
|
|
102383
|
-
const dir = await persistEvidenceBundle(
|
|
102384
|
-
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)}`);
|
|
102385
102976
|
}
|
|
102386
102977
|
}
|
|
102387
102978
|
}
|
|
@@ -102405,7 +102996,7 @@ async function runGdgraphLayer(projectRoot, args, ladder) {
|
|
|
102405
102996
|
return allValid;
|
|
102406
102997
|
}
|
|
102407
102998
|
async function loadCoverageMap2(projectRoot, file) {
|
|
102408
|
-
const raw = JSON.parse(await readFile101(
|
|
102999
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, file), "utf8"));
|
|
102409
103000
|
return raw.coverageMap ?? {};
|
|
102410
103001
|
}
|
|
102411
103002
|
async function runTestingLayer(projectRoot, args, ladder) {
|
|
@@ -102442,7 +103033,7 @@ async function runTestingLayer(projectRoot, args, ladder) {
|
|
|
102442
103033
|
return result.valid;
|
|
102443
103034
|
}
|
|
102444
103035
|
async function loadMemoryGoldK(projectRoot, file) {
|
|
102445
|
-
const raw = JSON.parse(await readFile101(
|
|
103036
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, file), "utf8"));
|
|
102446
103037
|
return typeof raw.k === "number" && raw.k > 0 ? raw.k : 3;
|
|
102447
103038
|
}
|
|
102448
103039
|
async function runMemoryLayer(projectRoot, args, ladder) {
|
|
@@ -102479,11 +103070,11 @@ async function runMemoryLayer(projectRoot, args, ladder) {
|
|
|
102479
103070
|
return result.valid;
|
|
102480
103071
|
}
|
|
102481
103072
|
async function loadWikiGoldK(projectRoot, file) {
|
|
102482
|
-
const raw = JSON.parse(await readFile101(
|
|
103073
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, file), "utf8"));
|
|
102483
103074
|
return typeof raw.k === "number" && raw.k > 0 ? raw.k : 5;
|
|
102484
103075
|
}
|
|
102485
103076
|
async function loadWikiGroundedness(projectRoot, file) {
|
|
102486
|
-
const raw = JSON.parse(await readFile101(
|
|
103077
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, file), "utf8"));
|
|
102487
103078
|
const map = new Map;
|
|
102488
103079
|
for (const entry of raw.targets ?? []) {
|
|
102489
103080
|
if (typeof entry.target !== "string" || !Array.isArray(entry.scores) || entry.scores.length !== 3)
|
|
@@ -102539,7 +103130,7 @@ async function runWikiLayer(projectRoot, args, ladder) {
|
|
|
102539
103130
|
return result.valid;
|
|
102540
103131
|
}
|
|
102541
103132
|
async function loadGdctxFacts(projectRoot, file) {
|
|
102542
|
-
const raw = JSON.parse(await readFile101(
|
|
103133
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, file), "utf8"));
|
|
102543
103134
|
const inputs = [];
|
|
102544
103135
|
for (const entry of raw.inputs ?? []) {
|
|
102545
103136
|
if (typeof entry.input === "string") {
|
|
@@ -102577,7 +103168,7 @@ async function collect(projectRoot, args) {
|
|
|
102577
103168
|
process.exitCode = 1;
|
|
102578
103169
|
return;
|
|
102579
103170
|
}
|
|
102580
|
-
const raw = JSON.parse(await readFile101(
|
|
103171
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, eventFile), "utf8"));
|
|
102581
103172
|
const events = Array.isArray(raw) ? raw : raw.events;
|
|
102582
103173
|
const startedAt = optionValue(args, "--started-at") ?? events[0]?.timestamp_utc ?? new Date().toISOString();
|
|
102583
103174
|
const finishedAt = optionValue(args, "--finished-at") ?? events.at(-1)?.timestamp_utc ?? startedAt;
|
|
@@ -102593,11 +103184,11 @@ async function collect(projectRoot, args) {
|
|
|
102593
103184
|
parentRunId: optionValue(args, "--parent-run-id") ?? null
|
|
102594
103185
|
});
|
|
102595
103186
|
const result = await writeRunArtifacts(metricsRoot(projectRoot), record, { cwd: projectRoot });
|
|
102596
|
-
console.log(`json: ${
|
|
102597
|
-
console.log(`markdown: ${
|
|
103187
|
+
console.log(`json: ${path199.relative(projectRoot, result.jsonPath)}`);
|
|
103188
|
+
console.log(`markdown: ${path199.relative(projectRoot, result.markdownPath)}`);
|
|
102598
103189
|
}
|
|
102599
103190
|
function metricsRoot(projectRoot) {
|
|
102600
|
-
return
|
|
103191
|
+
return path199.join(projectRoot, ".metaproject", "data", "metrics");
|
|
102601
103192
|
}
|
|
102602
103193
|
function printMetricsHelp() {
|
|
102603
103194
|
console.log(`keryx metrics
|
|
@@ -103070,7 +103661,7 @@ Providers with a sanctioned subscription login: grok (SuperGrok), openai (ChatGP
|
|
|
103070
103661
|
Claude Pro/Max, Gemini Google-account login, and DeepSeek OAuth are not offered.
|
|
103071
103662
|
`);
|
|
103072
103663
|
}
|
|
103073
|
-
async function
|
|
103664
|
+
async function authCommand2(args) {
|
|
103074
103665
|
const command = args[0];
|
|
103075
103666
|
if (!command || command === "--help" || command === "-h") {
|
|
103076
103667
|
printAuthHelp();
|
|
@@ -103464,7 +104055,7 @@ var CLI_ROUTES = {
|
|
|
103464
104055
|
modules: modulesCommand,
|
|
103465
104056
|
projects: projectsCommand,
|
|
103466
104057
|
providers: providersCommand,
|
|
103467
|
-
auth:
|
|
104058
|
+
auth: authCommand2,
|
|
103468
104059
|
serve: serveCommand,
|
|
103469
104060
|
update: updateCommand,
|
|
103470
104061
|
dashboard: dashboardCommand,
|