@mrciphersmith/keryx 0.2.96 → 0.2.98
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 +1615 -710
- package/dist/core.js +1 -1
- package/package.json +1 -1
- package/src/gdskills/bundled/rules/core/api-contracts.mdc +1 -0
- package/src/gdskills/bundled/rules/core/code-style-patterns.mdc +1 -0
- package/src/gdskills/bundled/rules/core/database-patterns.mdc +1 -0
- package/src/gdskills/bundled/rules/core/documentation-management.mdc +33 -38
- package/src/gdskills/bundled/rules/core/error-handling.mdc +1 -11
- package/src/gdskills/bundled/rules/core/execution-metrics.md +1 -2
- package/src/gdskills/bundled/rules/core/frontend-assistant.mdc +1 -0
- package/src/gdskills/bundled/rules/core/git-concurrency.mdc +101 -0
- package/src/gdskills/bundled/rules/core/implementation-plans.mdc +23 -11
- package/src/gdskills/bundled/rules/core/mobx-store-template.mdc +1 -0
- package/src/gdskills/bundled/rules/core/nestjs-dto.mdc +1 -0
- package/src/gdskills/bundled/rules/core/playwright-testing.mdc +1 -0
- package/src/gdskills/bundled/rules/core/requirements-management.mdc +15 -11
- package/src/gdskills/bundled/rules/core/rule-management-workflow.mdc +29 -14
- package/src/gdskills/bundled/rules/core/shared-definitions.mdc +1 -1
- package/src/gdskills/bundled/rules/core/skill-lifecycle.mdc +9 -5
- package/src/gdskills/bundled/rules/core/skills-storage-workflow.mdc +59 -16
- package/src/gdskills/bundled/rules/core/storybook-guidelines.mdc +1 -0
- package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.codex.md +48 -71
- package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.cursor.md +48 -71
- package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.md +48 -71
- package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.opencode.md +48 -71
- package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.zed.md +48 -71
- package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.codex.md +4 -4
- package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.cursor.md +4 -4
- package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.md +4 -4
- package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.opencode.md +4 -4
- package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.zed.md +4 -4
- package/src/gdskills/bundled/skills/orchestration/context-collector/orchestrator-prompt.md +2 -2
- package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.codex.md +12 -22
- package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.cursor.md +12 -22
- package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.detail.md +12 -22
- package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.md +12 -22
- package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.opencode.md +12 -22
- package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.zed.md +12 -22
- package/src/gdskills/bundled/skills/orchestration/feature-analyzer/analysis-request.md +2 -2
- package/src/gdskills/bundled/skills/orchestration/feature-analyzer/analysis-request.template.md +1 -1
- package/src/gdskills/bundled/skills/orchestration/feature-analyzer/input-contract.schema.json +4 -4
- package/src/gdskills/bundled/skills/orchestration/feature-analyzer/orchestrator-prompt.md +2 -2
- package/src/gdskills/bundled/skills/orchestration/feature-dev/SKILL.codex.md +2 -2
- package/src/gdskills/bundled/skills/orchestration/feature-dev/SKILL.cursor.md +2 -2
- package/src/gdskills/bundled/skills/orchestration/feature-dev/SKILL.md +2 -2
- package/src/gdskills/bundled/skills/orchestration/flow-orchestrator/SKILL.md +46 -4
- package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.codex.md +2 -2
- package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.cursor.md +2 -2
- package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.md +2 -2
- package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.opencode.md +2 -2
- package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.zed.md +2 -2
- package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.codex.md +3 -3
- package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.cursor.md +3 -3
- package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.md +3 -3
- package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.opencode.md +3 -3
- package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.zed.md +3 -3
- package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.codex.md +65 -23
- package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.cursor.md +65 -23
- package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.md +65 -23
- package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.opencode.md +65 -23
- package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.zed.md +65 -23
- package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.codex.md +19 -10
- package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.cursor.md +19 -10
- package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.md +19 -10
- package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.opencode.md +19 -10
- package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.zed.md +19 -10
- package/src/gdskills/bundled/skills/planning/brainstorm/SKILL.codex.md +1 -1
- package/src/gdskills/bundled/skills/planning/brainstorm/SKILL.cursor.md +1 -1
- package/src/gdskills/bundled/skills/planning/brainstorm/SKILL.md +1 -1
- package/src/gdskills/bundled/skills/planning/interview/SKILL.codex.md +1 -1
- package/src/gdskills/bundled/skills/planning/interview/SKILL.cursor.md +1 -1
- package/src/gdskills/bundled/skills/planning/interview/SKILL.md +1 -1
- package/src/gdskills/bundled/skills/planning/interviewer/SKILL.codex.md +1 -1
- package/src/gdskills/bundled/skills/planning/interviewer/SKILL.cursor.md +1 -1
- package/src/gdskills/bundled/skills/planning/interviewer/SKILL.md +1 -1
- package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.codex.md +7 -7
- package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.cursor.md +7 -7
- package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.md +7 -7
- package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.opencode.md +7 -7
- package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.zed.md +7 -7
- package/src/gdskills/bundled/skills/platform/claude-md-management/SKILL.codex.md +1 -1
- package/src/gdskills/bundled/skills/platform/claude-md-management/SKILL.cursor.md +1 -1
- package/src/gdskills/bundled/skills/platform/claude-md-management/SKILL.md +1 -1
- package/src/gdskills/bundled/skills/platform/hookify/SKILL.codex.md +1 -1
- package/src/gdskills/bundled/skills/platform/hookify/SKILL.cursor.md +1 -1
- package/src/gdskills/bundled/skills/platform/hookify/SKILL.md +1 -1
- package/src/gdskills/bundled/skills/quality/changelog/SKILL.codex.md +1 -1
- package/src/gdskills/bundled/skills/quality/changelog/SKILL.cursor.md +1 -1
- package/src/gdskills/bundled/skills/quality/changelog/SKILL.md +1 -1
- package/src/gdskills/bundled/skills/quality/commit/SKILL.codex.md +2 -2
- package/src/gdskills/bundled/skills/quality/commit/SKILL.cursor.md +2 -2
- package/src/gdskills/bundled/skills/quality/commit/SKILL.md +2 -2
- package/src/gdskills/bundled/skills/quality/db-migrate/SKILL.codex.md +1 -1
- package/src/gdskills/bundled/skills/quality/db-migrate/SKILL.cursor.md +1 -1
- package/src/gdskills/bundled/skills/quality/db-migrate/SKILL.md +1 -1
- package/src/gdskills/bundled/skills/quality/dependency-update/SKILL.codex.md +1 -1
- package/src/gdskills/bundled/skills/quality/dependency-update/SKILL.cursor.md +1 -1
- package/src/gdskills/bundled/skills/quality/dependency-update/SKILL.md +1 -1
- package/src/gdskills/bundled/skills/quality/deploy/SKILL.codex.md +1 -1
- package/src/gdskills/bundled/skills/quality/deploy/SKILL.cursor.md +1 -1
- package/src/gdskills/bundled/skills/quality/deploy/SKILL.md +1 -1
- package/src/gdskills/bundled/skills/quality/perf-check/SKILL.codex.md +1 -1
- package/src/gdskills/bundled/skills/quality/perf-check/SKILL.cursor.md +1 -1
- package/src/gdskills/bundled/skills/quality/perf-check/SKILL.md +1 -1
- package/src/gdskills/bundled/skills/quality/pr/SKILL.codex.md +1 -1
- package/src/gdskills/bundled/skills/quality/pr/SKILL.cursor.md +1 -1
- package/src/gdskills/bundled/skills/quality/pr/SKILL.md +1 -1
- package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.codex.md +2 -2
- package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.cursor.md +2 -2
- package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.md +2 -2
- package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.opencode.md +2 -2
- package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.zed.md +2 -2
- package/src/gdskills/bundled/skills/quality/push/SKILL.codex.md +1 -1
- package/src/gdskills/bundled/skills/quality/push/SKILL.cursor.md +1 -1
- package/src/gdskills/bundled/skills/quality/push/SKILL.md +1 -1
- package/src/gdskills/bundled/skills/quality/test-gen/SKILL.codex.md +7 -2
- package/src/gdskills/bundled/skills/quality/test-gen/SKILL.cursor.md +7 -2
- package/src/gdskills/bundled/skills/quality/test-gen/SKILL.md +7 -2
- package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.codex.md +15 -9
- package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.cursor.md +15 -9
- package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.md +15 -9
- package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.opencode.md +15 -9
- package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.zed.md +15 -9
- package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.codex.md +2 -2
- package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.cursor.md +2 -2
- package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.md +2 -2
- package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.opencode.md +2 -2
- package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.zed.md +2 -2
- package/src/gdskills/bundled/skills/review/code-learned-review/SKILL.codex.md +2 -2
- package/src/gdskills/bundled/skills/review/code-learned-review/SKILL.cursor.md +2 -2
- package/src/gdskills/bundled/skills/review/code-learned-review/SKILL.md +2 -2
- package/src/gdskills/bundled/skills/review/code-learned-review/SKILL.opencode.md +2 -2
- package/src/gdskills/bundled/skills/review/code-learned-review/SKILL.zed.md +2 -2
- package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.codex.md +2 -2
- package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.cursor.md +2 -2
- package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.md +2 -2
- package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.opencode.md +2 -2
- package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.zed.md +2 -2
- package/src/gdskills/bundled/skills/review/code-style-review/SKILL.codex.md +2 -2
- package/src/gdskills/bundled/skills/review/code-style-review/SKILL.cursor.md +2 -2
- package/src/gdskills/bundled/skills/review/code-style-review/SKILL.md +2 -2
- package/src/gdskills/bundled/skills/review/code-style-review/SKILL.opencode.md +2 -2
- package/src/gdskills/bundled/skills/review/code-style-review/SKILL.zed.md +2 -2
- package/src/gdskills/bundled/skills/review/review-architecture/SKILL.md +2 -2
- package/src/gdskills/bundled/skills/review/review-backend/SKILL.md +3 -3
- package/src/gdskills/bundled/skills/review/review-clean-code/SKILL.md +2 -2
- package/src/gdskills/bundled/skills/review/review-core-boundaries/SKILL.md +1 -1
- package/src/gdskills/bundled/skills/review/review-flow-graph/SKILL.md +1 -1
- package/src/gdskills/bundled/skills/review/review-frontend/SKILL.md +2 -2
- package/src/gdskills/bundled/skills/review/review-frontend-conventions/SKILL.md +1 -1
- package/src/gdskills/bundled/skills/review/review-highload/SKILL.md +2 -2
- package/src/gdskills/bundled/skills/review/review-layout/SKILL.md +1 -1
- package/src/gdskills/bundled/skills/review/review-logic/SKILL.md +2 -2
- package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.md +40 -35
- package/src/gdskills/bundled/skills/review/review-orchestrator/input-contract.schema.json +1 -2
- package/src/gdskills/bundled/skills/review/review-orchestrator/review-context.schema.json +1 -5
- package/src/gdskills/bundled/skills/review/review-orchestrator/reviewer-input.schema.json +53 -9
- package/src/gdskills/bundled/skills/review/review-performance/SKILL.md +2 -2
- package/src/gdskills/bundled/skills/review/review-pr-feedback/SKILL.md +6 -6
- package/src/gdskills/bundled/skills/review/review-security-code/SKILL.md +2 -2
- package/src/gdskills/bundled/skills/review/review-style/SKILL.md +3 -3
- package/src/gdskills/bundled/skills/review/review-testing-practices/SKILL.md +1 -1
- package/src/gdskills/bundled/skills/review/review-verifier/SKILL.md +1 -1
- package/src/gdskills/bundled/rules/core/review-agent-profile.mdc +0 -49
- package/src/gdskills/bundled/rules/core/review-strict-profile.mdc +0 -48
package/dist/cli.js
CHANGED
|
@@ -301,6 +301,13 @@ var init_provider_config = __esm(() => {
|
|
|
301
301
|
// src/lib/shell-config.ts
|
|
302
302
|
import { existsSync as existsSync2 } from "fs";
|
|
303
303
|
import path3 from "path";
|
|
304
|
+
function noteSavedCredentialEnv(keys) {
|
|
305
|
+
for (const key of keys)
|
|
306
|
+
savedCredentialKeys.add(key);
|
|
307
|
+
}
|
|
308
|
+
function savedCredentialEnvKeys() {
|
|
309
|
+
return savedCredentialKeys;
|
|
310
|
+
}
|
|
304
311
|
function shellConfigPath(dir) {
|
|
305
312
|
return path3.join(keryxConfigDir(dir), "auth.json");
|
|
306
313
|
}
|
|
@@ -383,10 +390,13 @@ function applySavedApiKeys(dir) {
|
|
|
383
390
|
}
|
|
384
391
|
}
|
|
385
392
|
} catch {}
|
|
393
|
+
noteSavedCredentialEnv(applied);
|
|
386
394
|
return applied;
|
|
387
395
|
}
|
|
396
|
+
var savedCredentialKeys;
|
|
388
397
|
var init_shell_config = __esm(() => {
|
|
389
398
|
init_config_dir();
|
|
399
|
+
savedCredentialKeys = new Set;
|
|
390
400
|
});
|
|
391
401
|
|
|
392
402
|
// src/lib/oauth/grants.ts
|
|
@@ -472,6 +482,7 @@ function applyOAuthAccessToEnv(dir) {
|
|
|
472
482
|
applied.push(envKey);
|
|
473
483
|
}
|
|
474
484
|
}
|
|
485
|
+
noteSavedCredentialEnv(applied);
|
|
475
486
|
return applied;
|
|
476
487
|
}
|
|
477
488
|
function grantFromTokens(method, tokens, now = Date.now) {
|
|
@@ -571,10 +582,61 @@ function customCompatProviders(dir) {
|
|
|
571
582
|
function allOpenAiCompatProviders(dir) {
|
|
572
583
|
return [...OPENAI_COMPAT_PROVIDERS, ...customCompatProviders(dir)];
|
|
573
584
|
}
|
|
585
|
+
function modelsErrorDetail(body) {
|
|
586
|
+
const text = body.slice(0, MODELS_ERROR_BODY_LIMIT).trim();
|
|
587
|
+
if (text.length === 0) {
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
let parsed;
|
|
591
|
+
try {
|
|
592
|
+
parsed = JSON.parse(text);
|
|
593
|
+
} catch {
|
|
594
|
+
parsed = undefined;
|
|
595
|
+
}
|
|
596
|
+
if (typeof parsed === "object" && parsed !== null) {
|
|
597
|
+
const record = parsed;
|
|
598
|
+
const error = record.error;
|
|
599
|
+
if (typeof error === "string" && error.length > 0) {
|
|
600
|
+
return error;
|
|
601
|
+
}
|
|
602
|
+
if (typeof error === "object" && error !== null) {
|
|
603
|
+
const message = error.message;
|
|
604
|
+
if (typeof message === "string" && message.length > 0) {
|
|
605
|
+
return message;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
for (const field of ["detail", "message"]) {
|
|
609
|
+
const value = record[field];
|
|
610
|
+
if (typeof value === "string" && value.length > 0) {
|
|
611
|
+
return value;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
if (text.startsWith("<") || text.includes("<html")) {
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
const firstLine = text.split(`
|
|
620
|
+
`, 1)[0]?.trim();
|
|
621
|
+
return firstLine !== undefined && firstLine.length > 0 ? firstLine : undefined;
|
|
622
|
+
}
|
|
623
|
+
function modelsFailureLine(providerLabel, failure) {
|
|
624
|
+
const detail = "detail" in failure && failure.detail !== undefined ? ` \u2014 ${failure.detail}` : "";
|
|
625
|
+
switch (failure.kind) {
|
|
626
|
+
case "rejected":
|
|
627
|
+
return `${providerLabel} rejected the credential (HTTP ${failure.status})${detail}`;
|
|
628
|
+
case "http":
|
|
629
|
+
return `${providerLabel} could not list models (HTTP ${failure.status})${detail}`;
|
|
630
|
+
case "unreachable":
|
|
631
|
+
return `${providerLabel} could not be reached${detail}`;
|
|
632
|
+
case "empty":
|
|
633
|
+
return `${providerLabel} reports no models`;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
574
636
|
async function fetchOpenAiCompatModelsDetailed(fetchFn, provider, apiKey, opts) {
|
|
575
637
|
const url = `${provider.baseUrl.replace(/\/+$/, "")}${provider.modelsPath ?? DEFAULT_MODELS_PATH}`;
|
|
576
638
|
const timeoutMs = opts?.timeoutMs ?? MODELS_FETCH_TIMEOUT_MS;
|
|
577
|
-
const fallback = { models: [], source: "fallback" };
|
|
639
|
+
const fallback = (failure) => ({ models: [], source: "fallback", failure });
|
|
578
640
|
const controller = new AbortController;
|
|
579
641
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
580
642
|
try {
|
|
@@ -584,7 +646,9 @@ async function fetchOpenAiCompatModelsDetailed(fetchFn, provider, apiKey, opts)
|
|
|
584
646
|
}
|
|
585
647
|
const res = await fetchFn(url, init);
|
|
586
648
|
if (!res.ok) {
|
|
587
|
-
|
|
649
|
+
const detail = modelsErrorDetail(await res.text().catch(() => ""));
|
|
650
|
+
const kind = res.status === 401 || res.status === 403 ? "rejected" : "http";
|
|
651
|
+
return fallback({ kind, status: res.status, ...detail === undefined ? {} : { detail } });
|
|
588
652
|
}
|
|
589
653
|
const body = await res.json();
|
|
590
654
|
const ids = Array.isArray(body?.data) ? body.data.map((m) => {
|
|
@@ -597,11 +661,13 @@ async function fetchOpenAiCompatModelsDetailed(fetchFn, provider, apiKey, opts)
|
|
|
597
661
|
return "";
|
|
598
662
|
}).filter((id) => id.length > 0) : [];
|
|
599
663
|
if (ids.length === 0) {
|
|
600
|
-
return fallback;
|
|
664
|
+
return fallback({ kind: "empty" });
|
|
601
665
|
}
|
|
602
666
|
return { models: Array.from(new Set(ids)).sort(), source: "live" };
|
|
603
|
-
} catch {
|
|
604
|
-
|
|
667
|
+
} catch (err) {
|
|
668
|
+
const aborted = controller.signal.aborted;
|
|
669
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
670
|
+
return fallback({ kind: "unreachable", detail: aborted ? `timed out after ${timeoutMs}ms` : message });
|
|
605
671
|
} finally {
|
|
606
672
|
clearTimeout(timer);
|
|
607
673
|
}
|
|
@@ -821,7 +887,7 @@ optimisation. With no second family configured it reports single-family review
|
|
|
821
887
|
with a stated reason and exits 0 \u2014 that is a normal configuration, not an error.
|
|
822
888
|
`);
|
|
823
889
|
}
|
|
824
|
-
var DEFAULT_MODELS_PATH = "/v1/models", OPENAI_COMPAT_PROVIDERS, MODELS_FETCH_TIMEOUT_MS = 1e4, BALANCE_FETCH_TIMEOUT_MS = 8000;
|
|
890
|
+
var DEFAULT_MODELS_PATH = "/v1/models", OPENAI_COMPAT_PROVIDERS, MODELS_FETCH_TIMEOUT_MS = 1e4, MODELS_ERROR_BODY_LIMIT = 4096, BALANCE_FETCH_TIMEOUT_MS = 8000;
|
|
825
891
|
var init_providers = __esm(() => {
|
|
826
892
|
init_provider_config();
|
|
827
893
|
init_grants();
|
|
@@ -13250,7 +13316,7 @@ var init_staleness = __esm(() => {
|
|
|
13250
13316
|
});
|
|
13251
13317
|
|
|
13252
13318
|
// src/wiki/staleness.ts
|
|
13253
|
-
import { createHash as
|
|
13319
|
+
import { createHash as createHash7 } from "crypto";
|
|
13254
13320
|
import { readFile as readFile29 } from "fs/promises";
|
|
13255
13321
|
import path68 from "path";
|
|
13256
13322
|
async function computePageNodeHash(cwd, keyFiles, graph) {
|
|
@@ -13261,7 +13327,7 @@ async function computePageNodeHash(cwd, keyFiles, graph) {
|
|
|
13261
13327
|
if (knownPaths.has(file)) {
|
|
13262
13328
|
try {
|
|
13263
13329
|
const content = await readFile29(path68.join(cwd, file), "utf8");
|
|
13264
|
-
digest =
|
|
13330
|
+
digest = createHash7("sha256").update(content).digest("hex");
|
|
13265
13331
|
} catch {
|
|
13266
13332
|
digest = "<missing>";
|
|
13267
13333
|
}
|
|
@@ -13271,7 +13337,7 @@ async function computePageNodeHash(cwd, keyFiles, graph) {
|
|
|
13271
13337
|
entries.sort((a, b) => a.path.localeCompare(b.path));
|
|
13272
13338
|
const combined = entries.map((entry) => `${entry.path}:${entry.digest}`).join(`
|
|
13273
13339
|
`);
|
|
13274
|
-
return
|
|
13340
|
+
return createHash7("sha256").update(combined).digest("hex");
|
|
13275
13341
|
}
|
|
13276
13342
|
function isPageUnchangedSinceLastEnrich(pagePath, currentHash, completedNodeHashes) {
|
|
13277
13343
|
const previous = completedNodeHashes?.[pagePath];
|
|
@@ -13325,7 +13391,7 @@ var init_staleness2 = __esm(() => {
|
|
|
13325
13391
|
});
|
|
13326
13392
|
|
|
13327
13393
|
// src/wiki/provenance.ts
|
|
13328
|
-
import { createHash as
|
|
13394
|
+
import { createHash as createHash8 } from "crypto";
|
|
13329
13395
|
function parseProvenance(content) {
|
|
13330
13396
|
const verifiedAt = singleLineField(content, "VerifiedAt");
|
|
13331
13397
|
const verifiedScope = singleLineField(content, "VerifiedScope");
|
|
@@ -13337,7 +13403,7 @@ function parseProvenance(content) {
|
|
|
13337
13403
|
}
|
|
13338
13404
|
async function computeVerifiedScope(cwd, describePaths, graph) {
|
|
13339
13405
|
if (describePaths.length === 0) {
|
|
13340
|
-
return `sha256:${
|
|
13406
|
+
return `sha256:${createHash8("sha256").update("<empty-describe-set>").digest("hex")}`;
|
|
13341
13407
|
}
|
|
13342
13408
|
return `sha256:${await computePageNodeHash(cwd, describePaths, graph)}`;
|
|
13343
13409
|
}
|
|
@@ -13542,7 +13608,7 @@ var init_collect = __esm(() => {
|
|
|
13542
13608
|
});
|
|
13543
13609
|
|
|
13544
13610
|
// src/wiki/section-marker.ts
|
|
13545
|
-
import { createHash as
|
|
13611
|
+
import { createHash as createHash9 } from "crypto";
|
|
13546
13612
|
import { readFile as readFile31 } from "fs/promises";
|
|
13547
13613
|
function parseMarkers(content) {
|
|
13548
13614
|
const lines = content.split(`
|
|
@@ -13673,7 +13739,7 @@ function proposeSectionId(headingText, fallbackOrdinal) {
|
|
|
13673
13739
|
return slug.length > 0 && ID_RE.test(slug) ? slug : `section-${fallbackOrdinal}`;
|
|
13674
13740
|
}
|
|
13675
13741
|
function digest(value) {
|
|
13676
|
-
return
|
|
13742
|
+
return createHash9("sha256").update(value).digest("hex");
|
|
13677
13743
|
}
|
|
13678
13744
|
function planPageMigration(relativePath, content) {
|
|
13679
13745
|
const lines = content.split(`
|
|
@@ -13813,7 +13879,7 @@ var init_section_marker = __esm(() => {
|
|
|
13813
13879
|
});
|
|
13814
13880
|
|
|
13815
13881
|
// src/wiki/section-index.ts
|
|
13816
|
-
import { createHash as
|
|
13882
|
+
import { createHash as createHash10 } from "crypto";
|
|
13817
13883
|
function resolveWikiPageIdentity(relativePath, content) {
|
|
13818
13884
|
const markers = parseMarkers(content);
|
|
13819
13885
|
if (markers.page) {
|
|
@@ -13905,7 +13971,7 @@ function detectLanguage(text) {
|
|
|
13905
13971
|
return ru ? "ru" : "en";
|
|
13906
13972
|
}
|
|
13907
13973
|
function digestOf2(value) {
|
|
13908
|
-
return
|
|
13974
|
+
return createHash10("sha256").update(value).digest("hex");
|
|
13909
13975
|
}
|
|
13910
13976
|
function fieldFromLines(lines, name) {
|
|
13911
13977
|
const pattern = new RegExp(`^${name}:\\s*(.+)$`, "i");
|
|
@@ -14436,7 +14502,7 @@ async function runRuntimeEmbedder(dep, asset, texts, dims) {
|
|
|
14436
14502
|
var MEMORY_EMBEDDING_ID = "memory.embedding", DEFAULT_EMBED_DIMS = 96;
|
|
14437
14503
|
|
|
14438
14504
|
// src/memory/embedding/index.ts
|
|
14439
|
-
import { createHash as
|
|
14505
|
+
import { createHash as createHash11 } from "crypto";
|
|
14440
14506
|
import { mkdir as mkdir21, readFile as readFile32, writeFile as writeFile24 } from "fs/promises";
|
|
14441
14507
|
import path70 from "path";
|
|
14442
14508
|
function embeddingsDir(cwd) {
|
|
@@ -14449,7 +14515,7 @@ ${entry.tags.join(" ")}
|
|
|
14449
14515
|
${entry.details}`.trim();
|
|
14450
14516
|
}
|
|
14451
14517
|
function contentHash(text) {
|
|
14452
|
-
return
|
|
14518
|
+
return createHash11("sha256").update(text).digest("hex").slice(0, 16);
|
|
14453
14519
|
}
|
|
14454
14520
|
async function buildEmbeddingIndex(cwd, entries, embedder, model, now) {
|
|
14455
14521
|
const sorted = [...entries].sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
@@ -15181,7 +15247,7 @@ var init_backlinks = __esm(() => {
|
|
|
15181
15247
|
});
|
|
15182
15248
|
|
|
15183
15249
|
// src/ctx/assembly.ts
|
|
15184
|
-
import { createHash as
|
|
15250
|
+
import { createHash as createHash12 } from "crypto";
|
|
15185
15251
|
import { mkdir as mkdir22, rename as rename2, writeFile as writeFile25 } from "fs/promises";
|
|
15186
15252
|
import path73 from "path";
|
|
15187
15253
|
function assembleContext(input) {
|
|
@@ -15236,7 +15302,7 @@ async function recordContextAssembly(input) {
|
|
|
15236
15302
|
await rename2(temporary, target);
|
|
15237
15303
|
}
|
|
15238
15304
|
function traceIdFor(correlationId, outcome) {
|
|
15239
|
-
return
|
|
15305
|
+
return createHash12("sha256").update(`${outcome}\x00${correlationId}`).digest("hex").slice(0, 24);
|
|
15240
15306
|
}
|
|
15241
15307
|
function traceRefFor(traceId) {
|
|
15242
15308
|
return `./.metaproject/context-operations/traces/${traceId}.json`;
|
|
@@ -16095,9 +16161,9 @@ var init_evidence = __esm(() => {
|
|
|
16095
16161
|
});
|
|
16096
16162
|
|
|
16097
16163
|
// src/wiki/managed-block.ts
|
|
16098
|
-
import { createHash as
|
|
16164
|
+
import { createHash as createHash13 } from "crypto";
|
|
16099
16165
|
function hashBlockContent(content) {
|
|
16100
|
-
return
|
|
16166
|
+
return createHash13("sha256").update(content.trim()).digest("hex");
|
|
16101
16167
|
}
|
|
16102
16168
|
function findManagedBlock(content) {
|
|
16103
16169
|
const lines = content.split(`
|
|
@@ -16348,7 +16414,7 @@ var init_template_structure = __esm(() => {
|
|
|
16348
16414
|
|
|
16349
16415
|
// src/wiki/service.ts
|
|
16350
16416
|
import { spawn as spawn2 } from "child_process";
|
|
16351
|
-
import { createHash as
|
|
16417
|
+
import { createHash as createHash14 } from "crypto";
|
|
16352
16418
|
import { mkdir as mkdir23, readFile as readFile35, readdir as readdir7, rm as rm6, writeFile as writeFile26 } from "fs/promises";
|
|
16353
16419
|
import path75 from "path";
|
|
16354
16420
|
function wikiRootPath2(cwd) {
|
|
@@ -16650,7 +16716,7 @@ function resolveEvidenceScope(cwd, override) {
|
|
|
16650
16716
|
const absolute = path75.resolve(cwd);
|
|
16651
16717
|
return {
|
|
16652
16718
|
projectId: override?.projectId ?? (path75.basename(absolute) || "project"),
|
|
16653
|
-
checkoutId: override?.checkoutId ?? `checkout:${
|
|
16719
|
+
checkoutId: override?.checkoutId ?? `checkout:${createHash14("sha256").update(absolute).digest("hex").slice(0, 16)}`,
|
|
16654
16720
|
...override?.workspaceId ? { workspaceId: override.workspaceId } : {},
|
|
16655
16721
|
...override?.taskId ? { taskId: override.taskId } : {}
|
|
16656
16722
|
};
|
|
@@ -17456,7 +17522,7 @@ var init_service3 = __esm(() => {
|
|
|
17456
17522
|
});
|
|
17457
17523
|
|
|
17458
17524
|
// src/gdgraph/wiki-layer.ts
|
|
17459
|
-
import { createHash as
|
|
17525
|
+
import { createHash as createHash15 } from "crypto";
|
|
17460
17526
|
import { mkdir as mkdir24, stat as stat8, writeFile as writeFile27 } from "fs/promises";
|
|
17461
17527
|
import path76 from "path";
|
|
17462
17528
|
function storageDir(projectRoot) {
|
|
@@ -17513,7 +17579,7 @@ function buildWikiLayer(input) {
|
|
|
17513
17579
|
function computeFingerprints(fileRecords, mtimes) {
|
|
17514
17580
|
return fileRecords.map((record) => ({
|
|
17515
17581
|
path: record.path,
|
|
17516
|
-
contentHash:
|
|
17582
|
+
contentHash: createHash15("sha256").update(record.content).digest("hex"),
|
|
17517
17583
|
mtimeMs: mtimes.get(record.path) ?? 0
|
|
17518
17584
|
})).sort((a, b) => a.path.localeCompare(b.path));
|
|
17519
17585
|
}
|
|
@@ -22153,7 +22219,7 @@ var init_slate = __esm(() => {
|
|
|
22153
22219
|
});
|
|
22154
22220
|
|
|
22155
22221
|
// src/flow/store.ts
|
|
22156
|
-
import { createHash as
|
|
22222
|
+
import { createHash as createHash16 } from "crypto";
|
|
22157
22223
|
import { appendFile as appendFile3, mkdir as mkdir30, readFile as readFile48, readdir as readdir9 } from "fs/promises";
|
|
22158
22224
|
import path90 from "path";
|
|
22159
22225
|
function flowsRoot(cwd) {
|
|
@@ -22325,7 +22391,7 @@ async function acChecksum(cwd, dir) {
|
|
|
22325
22391
|
`).split(`
|
|
22326
22392
|
`).map((line) => line.trimEnd()).join(`
|
|
22327
22393
|
`).trim();
|
|
22328
|
-
return `sha256:${
|
|
22394
|
+
return `sha256:${createHash16("sha256").update(normalized).digest("hex")}`;
|
|
22329
22395
|
}
|
|
22330
22396
|
async function assertAcIntact(cwd, dir, flow) {
|
|
22331
22397
|
if (!flow.acChecksum) {
|
|
@@ -22365,7 +22431,7 @@ var init_slate_course = __esm(() => {
|
|
|
22365
22431
|
});
|
|
22366
22432
|
|
|
22367
22433
|
// src/sac/trusted-wrap-up.ts
|
|
22368
|
-
import { createHash as
|
|
22434
|
+
import { createHash as createHash17, randomUUID as randomUUID7 } from "crypto";
|
|
22369
22435
|
function createTrustedWrapUpAuthority(input) {
|
|
22370
22436
|
const now = input.now ?? (() => new Date);
|
|
22371
22437
|
return Object.freeze({
|
|
@@ -22397,7 +22463,7 @@ function createTrustedWrapUpAuthority(input) {
|
|
|
22397
22463
|
});
|
|
22398
22464
|
}
|
|
22399
22465
|
function digest2(value) {
|
|
22400
|
-
return
|
|
22466
|
+
return createHash17("sha256").update(value).digest("hex");
|
|
22401
22467
|
}
|
|
22402
22468
|
var issued, consumed;
|
|
22403
22469
|
var init_trusted_wrap_up = __esm(() => {
|
|
@@ -22407,7 +22473,7 @@ var init_trusted_wrap_up = __esm(() => {
|
|
|
22407
22473
|
|
|
22408
22474
|
// src/sac/index.ts
|
|
22409
22475
|
import { access as access2, readFile as readFile49, realpath as realpath5 } from "fs/promises";
|
|
22410
|
-
import { createHash as
|
|
22476
|
+
import { createHash as createHash18 } from "crypto";
|
|
22411
22477
|
import path91 from "path";
|
|
22412
22478
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
22413
22479
|
async function resolveSacNormativeSchemaPath(fileName, searchFrom = [fileURLToPath5(new URL(".", import.meta.url)), process.cwd()]) {
|
|
@@ -23085,7 +23151,7 @@ async function validateSacContract(input) {
|
|
|
23085
23151
|
return { valid: errors.length === 0, errors };
|
|
23086
23152
|
}
|
|
23087
23153
|
function hashSacRecord(value) {
|
|
23088
|
-
return
|
|
23154
|
+
return createHash18("sha256").update(typeof value === "string" ? value : JSON.stringify(value)).digest("hex");
|
|
23089
23155
|
}
|
|
23090
23156
|
async function resolveWorkspaceReference(input) {
|
|
23091
23157
|
if (!workspacePathPattern.test(input.uri) || path91.isAbsolute(input.uri) || /^[a-z][a-z0-9+.-]*:/i.test(input.uri) || input.uri.includes("\\"))
|
|
@@ -23196,7 +23262,7 @@ var init_secure_resource_read = __esm(() => {
|
|
|
23196
23262
|
|
|
23197
23263
|
// src/sac/workspace-service.ts
|
|
23198
23264
|
import { mkdir as mkdir31, readdir as readdir10, readFile as readFile50 } from "fs/promises";
|
|
23199
|
-
import { createHash as
|
|
23265
|
+
import { createHash as createHash19, randomUUID as randomUUID8 } from "crypto";
|
|
23200
23266
|
import path93 from "path";
|
|
23201
23267
|
|
|
23202
23268
|
class WorkspaceService {
|
|
@@ -23486,7 +23552,7 @@ class WorkspaceService {
|
|
|
23486
23552
|
}
|
|
23487
23553
|
contentDigest(absolutePath) {
|
|
23488
23554
|
try {
|
|
23489
|
-
return
|
|
23555
|
+
return createHash19("sha256").update(readWorkspaceFileNoFollow(this.root, absolutePath)).digest("hex");
|
|
23490
23556
|
} catch {
|
|
23491
23557
|
return;
|
|
23492
23558
|
}
|
|
@@ -23618,7 +23684,7 @@ var init_workspace_service = __esm(() => {
|
|
|
23618
23684
|
});
|
|
23619
23685
|
|
|
23620
23686
|
// src/sac/guarded-owner-writer.ts
|
|
23621
|
-
import { createHash as
|
|
23687
|
+
import { createHash as createHash20 } from "crypto";
|
|
23622
23688
|
function createGuardedOwnerWriter(input) {
|
|
23623
23689
|
const recoverReceipt = input.recoverReceipt;
|
|
23624
23690
|
return Object.freeze({
|
|
@@ -23643,7 +23709,7 @@ function receiptMatchesIntent(input) {
|
|
|
23643
23709
|
return binding.owner === input.owner && binding.bindingHash === bindingHash(input.owner, input.intent) && binding.intentRef === input.intent.intentRef && binding.proposalId === input.intent.proposalId && binding.proposalRevision === input.intent.proposalRevision && binding.workspaceId === input.intent.workspaceId && binding.correlationId === input.intent.correlationId && binding.idempotencyKey === input.intent.idempotencyKey && binding.reviewerSubject === input.intent.reviewerSubject && binding.reviewerAuthority === input.intent.reviewerAuthority && binding.policyRevision === input.intent.policyRevision;
|
|
23644
23710
|
}
|
|
23645
23711
|
function bindingHash(owner, intent) {
|
|
23646
|
-
return
|
|
23712
|
+
return createHash20("sha256").update(JSON.stringify({ owner, ...intent })).digest("hex");
|
|
23647
23713
|
}
|
|
23648
23714
|
var init_guarded_owner_writer = () => {};
|
|
23649
23715
|
|
|
@@ -24296,7 +24362,7 @@ var init_wrap_up_evidence = __esm(() => {
|
|
|
24296
24362
|
});
|
|
24297
24363
|
|
|
24298
24364
|
// src/sac/session-wrap-up.ts
|
|
24299
|
-
import { createHash as
|
|
24365
|
+
import { createHash as createHash21 } from "crypto";
|
|
24300
24366
|
import { mkdir as mkdir32, writeFile as writeFile33 } from "fs/promises";
|
|
24301
24367
|
import path95 from "path";
|
|
24302
24368
|
function sessionEvidenceRef(workspaceId, sessionId) {
|
|
@@ -24362,12 +24428,12 @@ async function resolveSessionWrapUp(input) {
|
|
|
24362
24428
|
await writeFile33(path95.join(input.cwd, noticeRelPath), floor.notice, "utf8");
|
|
24363
24429
|
const observedAt = now().toISOString();
|
|
24364
24430
|
const evidence = [
|
|
24365
|
-
{ kind: "wrap-up", uri: `./${wrapUpRelPath}`, revision:
|
|
24366
|
-
{ kind: diffKind, uri: `./${diffRelPath}`, revision:
|
|
24367
|
-
{ kind: "session", uri: `./${relPath}`, revision:
|
|
24431
|
+
{ kind: "wrap-up", uri: `./${wrapUpRelPath}`, revision: createHash21("sha256").update(safeWrapUp.content).digest("hex"), observedAt },
|
|
24432
|
+
{ kind: diffKind, uri: `./${diffRelPath}`, revision: createHash21("sha256").update(safeDiff.content).digest("hex"), observedAt },
|
|
24433
|
+
{ kind: "session", uri: `./${relPath}`, revision: createHash21("sha256").update(safeSession.content).digest("hex"), observedAt }
|
|
24368
24434
|
];
|
|
24369
24435
|
if (floor.notice !== undefined) {
|
|
24370
|
-
evidence.push({ kind: REDACTION_NOTICE_KIND, uri: `./${noticeRelPath}`, revision:
|
|
24436
|
+
evidence.push({ kind: REDACTION_NOTICE_KIND, uri: `./${noticeRelPath}`, revision: createHash21("sha256").update(floor.notice).digest("hex"), observedAt });
|
|
24371
24437
|
}
|
|
24372
24438
|
return {
|
|
24373
24439
|
workspaceId: input.workspaceId,
|
|
@@ -24393,7 +24459,7 @@ var init_session_wrap_up = __esm(() => {
|
|
|
24393
24459
|
});
|
|
24394
24460
|
|
|
24395
24461
|
// src/sac/review-confirm-token.ts
|
|
24396
|
-
import { createHash as
|
|
24462
|
+
import { createHash as createHash22, randomBytes as randomBytes3 } from "crypto";
|
|
24397
24463
|
import { readFile as readFile51 } from "fs/promises";
|
|
24398
24464
|
import path96 from "path";
|
|
24399
24465
|
function confirmTokenPath(cwd, workspaceId, proposalId) {
|
|
@@ -24403,7 +24469,7 @@ function confirmReceiptPath(cwd, workspaceId, proposalId, idempotencyKey) {
|
|
|
24403
24469
|
return path96.join(cwd, ".metaproject", "workspaces", workspaceId, "proposals", `${proposalId}.${sha256(idempotencyKey)}.confirm-receipt.json`);
|
|
24404
24470
|
}
|
|
24405
24471
|
function sha256(text) {
|
|
24406
|
-
return
|
|
24472
|
+
return createHash22("sha256").update(text).digest("hex");
|
|
24407
24473
|
}
|
|
24408
24474
|
async function mintConfirmToken(cwd, workspaceId, proposalId, deps = {}) {
|
|
24409
24475
|
const now = deps.now ?? (() => new Date);
|
|
@@ -24564,7 +24630,7 @@ function round(value) {
|
|
|
24564
24630
|
var init_dedup = () => {};
|
|
24565
24631
|
|
|
24566
24632
|
// src/sac/proposal-evidence.ts
|
|
24567
|
-
import { createHash as
|
|
24633
|
+
import { createHash as createHash23 } from "crypto";
|
|
24568
24634
|
import { readFile as readFile52, rm as rm8 } from "fs/promises";
|
|
24569
24635
|
import path97 from "path";
|
|
24570
24636
|
function proposalPath(cwd, workspaceId, proposalId) {
|
|
@@ -24592,7 +24658,7 @@ async function readVerifiedProposalEvidence(cwd, workspaceId, proposalId) {
|
|
|
24592
24658
|
} catch {
|
|
24593
24659
|
return { ok: false, code: "evidence_file_unreadable" };
|
|
24594
24660
|
}
|
|
24595
|
-
if (
|
|
24661
|
+
if (createHash23("sha256").update(content).digest("hex") !== evidence.revision) {
|
|
24596
24662
|
return { ok: false, code: "evidence_revision_mismatch" };
|
|
24597
24663
|
}
|
|
24598
24664
|
return { proposal, evidence, content };
|
|
@@ -24603,7 +24669,7 @@ async function readSidecarNote(cwd, workspaceId, proposalId) {
|
|
|
24603
24669
|
});
|
|
24604
24670
|
}
|
|
24605
24671
|
function digestOf3(content) {
|
|
24606
|
-
return
|
|
24672
|
+
return createHash23("sha256").update(content).digest("hex");
|
|
24607
24673
|
}
|
|
24608
24674
|
async function readTargetVersion(absolutePath) {
|
|
24609
24675
|
try {
|
|
@@ -25295,7 +25361,7 @@ var init_wiki_owner_writer = __esm(() => {
|
|
|
25295
25361
|
});
|
|
25296
25362
|
|
|
25297
25363
|
// src/gdskills/project-skills.ts
|
|
25298
|
-
import { createHash as
|
|
25364
|
+
import { createHash as createHash24 } from "crypto";
|
|
25299
25365
|
import { homedir as homedir3 } from "os";
|
|
25300
25366
|
import { mkdir as mkdir34, readFile as readFile57, stat as stat11 } from "fs/promises";
|
|
25301
25367
|
import path101 from "path";
|
|
@@ -25363,7 +25429,7 @@ function resolveOriginPath(ref, projectRoot) {
|
|
|
25363
25429
|
return path101.resolve(projectRoot, trimmed);
|
|
25364
25430
|
}
|
|
25365
25431
|
function hashOriginContent(content) {
|
|
25366
|
-
return `sha256:${
|
|
25432
|
+
return `sha256:${createHash24("sha256").update(content).digest("hex")}`;
|
|
25367
25433
|
}
|
|
25368
25434
|
async function readSkillOrigin(projectRoot, ref, now = () => new Date) {
|
|
25369
25435
|
const resolved = resolveOriginPath(ref, projectRoot);
|
|
@@ -25623,7 +25689,7 @@ ${filesToRead}
|
|
|
25623
25689
|
|
|
25624
25690
|
## Verification
|
|
25625
25691
|
|
|
25626
|
-
-
|
|
25692
|
+
- Verification status: see \`verification.md\` (written by \`keryx skills verify\`).
|
|
25627
25693
|
- Run: \`keryx skills verify ${moduleName}/${skillName}\`
|
|
25628
25694
|
`;
|
|
25629
25695
|
}
|
|
@@ -25874,7 +25940,7 @@ var init_skill_owner_writer = __esm(() => {
|
|
|
25874
25940
|
});
|
|
25875
25941
|
|
|
25876
25942
|
// src/sac/proposal-lifecycle.ts
|
|
25877
|
-
import { createHash as
|
|
25943
|
+
import { createHash as createHash25, randomUUID as randomUUID11 } from "crypto";
|
|
25878
25944
|
import { appendFile as appendFile4, mkdir as mkdir35, readdir as readdir12, readFile as readFile59 } from "fs/promises";
|
|
25879
25945
|
import path102 from "path";
|
|
25880
25946
|
function supportsOwnerWriteRecovery(writer) {
|
|
@@ -26311,7 +26377,7 @@ class ProposalLifecycleService {
|
|
|
26311
26377
|
}
|
|
26312
26378
|
}
|
|
26313
26379
|
function hash(value) {
|
|
26314
|
-
return
|
|
26380
|
+
return createHash25("sha256").update(value).digest("hex");
|
|
26315
26381
|
}
|
|
26316
26382
|
function recordHash(value) {
|
|
26317
26383
|
return hash(JSON.stringify(value));
|
|
@@ -26535,7 +26601,7 @@ var init_workspace_resolve = __esm(() => {
|
|
|
26535
26601
|
});
|
|
26536
26602
|
|
|
26537
26603
|
// src/sac/machine-wrap-up.ts
|
|
26538
|
-
import { createHash as
|
|
26604
|
+
import { createHash as createHash26, randomUUID as randomUUID13 } from "crypto";
|
|
26539
26605
|
import { mkdir as mkdir36 } from "fs/promises";
|
|
26540
26606
|
import path103 from "path";
|
|
26541
26607
|
function groupSeedsByKind(slate) {
|
|
@@ -26550,7 +26616,7 @@ function groupSeedsByKind(slate) {
|
|
|
26550
26616
|
return map;
|
|
26551
26617
|
}
|
|
26552
26618
|
function sha2562(value) {
|
|
26553
|
-
return
|
|
26619
|
+
return createHash26("sha256").update(value).digest("hex");
|
|
26554
26620
|
}
|
|
26555
26621
|
function mechanicalSummary(diff, course) {
|
|
26556
26622
|
return `Mechanical wrap-up summary (model turn unavailable or timed out):
|
|
@@ -28496,9 +28562,9 @@ var init_orchestrate = __esm(() => {
|
|
|
28496
28562
|
});
|
|
28497
28563
|
|
|
28498
28564
|
// src/harness/policy/profiles.ts
|
|
28499
|
-
import { createHash as
|
|
28565
|
+
import { createHash as createHash27 } from "crypto";
|
|
28500
28566
|
function sha256Hex2(input) {
|
|
28501
|
-
return
|
|
28567
|
+
return createHash27("sha256").update(input, "utf8").digest("hex");
|
|
28502
28568
|
}
|
|
28503
28569
|
function readOnlyReview() {
|
|
28504
28570
|
return {
|
|
@@ -29718,7 +29784,7 @@ var init_supersede = __esm(() => {
|
|
|
29718
29784
|
});
|
|
29719
29785
|
|
|
29720
29786
|
// src/memory/service.ts
|
|
29721
|
-
import { createHash as
|
|
29787
|
+
import { createHash as createHash28 } from "crypto";
|
|
29722
29788
|
import { mkdir as mkdir38, readFile as readFile63, writeFile as writeFile36 } from "fs/promises";
|
|
29723
29789
|
import path110 from "path";
|
|
29724
29790
|
function dataRoot(cwd) {
|
|
@@ -29775,7 +29841,7 @@ function createMemoryService() {
|
|
|
29775
29841
|
const indexPath = path110.join(indexDir, "index.json");
|
|
29776
29842
|
await writeFile36(indexPath, `${JSON.stringify({
|
|
29777
29843
|
catalogVersion: 1,
|
|
29778
|
-
sourceFingerprint:
|
|
29844
|
+
sourceFingerprint: createHash28("sha256").update(JSON.stringify(entries.map((e) => ({
|
|
29779
29845
|
path: e.relativePath,
|
|
29780
29846
|
content: [e.title, e.type, e.status, e.confidence, e.summary, e.details, e.tags, e.scopes, e.validFrom, e.validTo].join("\x00")
|
|
29781
29847
|
})))).digest("hex"),
|
|
@@ -35918,7 +35984,7 @@ var init_github = __esm(() => {
|
|
|
35918
35984
|
});
|
|
35919
35985
|
|
|
35920
35986
|
// src/harness/tool/metaproject-adapter.ts
|
|
35921
|
-
import { lstat as
|
|
35987
|
+
import { lstat as lstat4, readdir as readdir17, readFile as readFile74, realpath as realpath6, stat as stat14 } from "fs/promises";
|
|
35922
35988
|
import { isAbsolute, join, relative, resolve } from "path";
|
|
35923
35989
|
function isMissingExecutable(cause) {
|
|
35924
35990
|
const message = cause instanceof Error ? cause.message : String(cause);
|
|
@@ -35974,12 +36040,16 @@ function flattenAttribution(attribution) {
|
|
|
35974
36040
|
}
|
|
35975
36041
|
async function projectRemovalTrail(cwd, query) {
|
|
35976
36042
|
const lookup = searchRemovals(await loadDeletionTrail(cwd), query);
|
|
36043
|
+
if (lookup.verdict === "trail-absent") {
|
|
36044
|
+
return { verdict: "trail-absent", summary: TRAIL_ABSENT_SUMMARY };
|
|
36045
|
+
}
|
|
36046
|
+
const summary = clipAutomaticRecallText(lookup.reason.split(`${cwd}/`).join(""), MAX_REMOVAL_SUMMARY_BYTES);
|
|
35977
36047
|
if (lookup.verdict !== "recorded-removed") {
|
|
35978
|
-
return { verdict: lookup.verdict, summary
|
|
36048
|
+
return { verdict: lookup.verdict, summary };
|
|
35979
36049
|
}
|
|
35980
36050
|
return {
|
|
35981
36051
|
verdict: "recorded-removed",
|
|
35982
|
-
summary
|
|
36052
|
+
summary,
|
|
35983
36053
|
totalRemovals: lookup.occurrences.length,
|
|
35984
36054
|
removals: lookup.occurrences.slice(0, MAX_REMOVAL_RECORDS).map((item) => ({
|
|
35985
36055
|
layer: item.layer,
|
|
@@ -36049,7 +36119,7 @@ async function parseCatalogSummaries(cwd) {
|
|
|
36049
36119
|
async function walkSkillCatalog(cwd) {
|
|
36050
36120
|
const root = join(cwd, ".metaproject", "skills", "gdskills");
|
|
36051
36121
|
const entries = [];
|
|
36052
|
-
const rootInfo = await
|
|
36122
|
+
const rootInfo = await lstat4(root).catch(() => null);
|
|
36053
36123
|
if (!rootInfo || !rootInfo.isDirectory() || rootInfo.isSymbolicLink())
|
|
36054
36124
|
return entries;
|
|
36055
36125
|
const rootReal = await realpath6(root).catch(() => null);
|
|
@@ -36735,7 +36805,7 @@ function createMetaprojectAdapter(cwd, overrides = {}) {
|
|
|
36735
36805
|
}
|
|
36736
36806
|
};
|
|
36737
36807
|
}
|
|
36738
|
-
var DEFAULT_DEPS, MAX_EXCERPT_BYTES = 400, MAX_QUERY_BYTES2 = 4096, UNKNOWN_PROVENANCE = "unknown", MAX_SEARCH_OUTPUT_BYTES = 20000, SEARCH_CODE_RG_MISSING, MAX_REMOVAL_RECORDS = 5, MAX_REMOVAL_SUMMARY_BYTES = 1600;
|
|
36808
|
+
var DEFAULT_DEPS, MAX_EXCERPT_BYTES = 400, MAX_QUERY_BYTES2 = 4096, UNKNOWN_PROVENANCE = "unknown", MAX_SEARCH_OUTPUT_BYTES = 20000, SEARCH_CODE_RG_MISSING, MAX_REMOVAL_RECORDS = 5, MAX_REMOVAL_SUMMARY_BYTES = 1600, TRAIL_ABSENT_SUMMARY = 'no removal has ever been recorded in this project (it has no deletion trail) \u2014 not the claim "this never existed".';
|
|
36739
36809
|
var init_metaproject_adapter = __esm(() => {
|
|
36740
36810
|
init_wiki_freshness();
|
|
36741
36811
|
init_fs();
|
|
@@ -38150,7 +38220,7 @@ var init_metaproject_operations = __esm(() => {
|
|
|
38150
38220
|
});
|
|
38151
38221
|
|
|
38152
38222
|
// src/wiki/deep-enrich.ts
|
|
38153
|
-
import { randomUUID as randomUUID15, createHash as
|
|
38223
|
+
import { randomUUID as randomUUID15, createHash as createHash29 } from "crypto";
|
|
38154
38224
|
function composeAbortSignals(external, timeout) {
|
|
38155
38225
|
if (external === undefined) {
|
|
38156
38226
|
return { signal: timeout, dispose: () => {} };
|
|
@@ -38181,7 +38251,7 @@ function errorMessage2(cause) {
|
|
|
38181
38251
|
return cause instanceof Error ? cause.message : String(cause);
|
|
38182
38252
|
}
|
|
38183
38253
|
function sha256Hex3(text) {
|
|
38184
|
-
return
|
|
38254
|
+
return createHash29("sha256").update(text).digest("hex");
|
|
38185
38255
|
}
|
|
38186
38256
|
function buildDeepEnrichTools(port) {
|
|
38187
38257
|
const deepOps = METAPROJECT_OPERATIONS.filter((op) => DEEP_ENRICH_OPS.includes(op.name));
|
|
@@ -40253,9 +40323,9 @@ var init_memory = __esm(() => {
|
|
|
40253
40323
|
|
|
40254
40324
|
// src/lib/serve-config.ts
|
|
40255
40325
|
import { existsSync as existsSync25 } from "fs";
|
|
40256
|
-
import
|
|
40326
|
+
import path174 from "path";
|
|
40257
40327
|
function serveConfigPath(dir) {
|
|
40258
|
-
return
|
|
40328
|
+
return path174.join(keryxConfigDir(dir), "serve.json");
|
|
40259
40329
|
}
|
|
40260
40330
|
function parseIpv4(value) {
|
|
40261
40331
|
const parts = value.split(".");
|
|
@@ -41172,15 +41242,43 @@ async function refreshProviderGrant(provider, http, dir) {
|
|
|
41172
41242
|
saveOAuthGrant(provider, next, dir);
|
|
41173
41243
|
return next;
|
|
41174
41244
|
}
|
|
41245
|
+
async function refreshSavedGrants(http, dir, providers = SESSION_REFRESHED_GRANTS) {
|
|
41246
|
+
const warnings = [];
|
|
41247
|
+
const now = http.now ?? Date.now;
|
|
41248
|
+
for (const provider of providers) {
|
|
41249
|
+
if (!SESSION_REFRESHED_GRANTS.includes(provider))
|
|
41250
|
+
continue;
|
|
41251
|
+
const grant = loadOAuthGrant(provider, dir);
|
|
41252
|
+
if (grant === undefined)
|
|
41253
|
+
continue;
|
|
41254
|
+
if (grant.refresh === undefined || grant.refresh.length === 0) {
|
|
41255
|
+
if (grant.expires !== undefined && grant.expires <= now()) {
|
|
41256
|
+
warnings.push(`${provider}: the saved login has expired and holds no refresh token. Run \`keryx auth login ${provider}\`.`);
|
|
41257
|
+
}
|
|
41258
|
+
continue;
|
|
41259
|
+
}
|
|
41260
|
+
if (!grantNeedsRefresh(grant, http.now))
|
|
41261
|
+
continue;
|
|
41262
|
+
try {
|
|
41263
|
+
await refreshProviderGrant(provider, http, dir);
|
|
41264
|
+
} catch (cause) {
|
|
41265
|
+
const reason = cause instanceof Error ? cause.message : String(cause);
|
|
41266
|
+
warnings.push(`${provider}: the saved login is expiring or expired and could not be refreshed (${reason}). ` + `Run \`keryx auth login ${provider}\`.`);
|
|
41267
|
+
}
|
|
41268
|
+
}
|
|
41269
|
+
return warnings;
|
|
41270
|
+
}
|
|
41175
41271
|
function logoutProvider(provider, dir) {
|
|
41176
41272
|
deleteOAuthGrant(provider, dir);
|
|
41177
41273
|
}
|
|
41274
|
+
var SESSION_REFRESHED_GRANTS;
|
|
41178
41275
|
var init_login = __esm(() => {
|
|
41179
41276
|
init_catalog();
|
|
41180
41277
|
init_copilot();
|
|
41181
41278
|
init_device_code();
|
|
41182
41279
|
init_grants();
|
|
41183
41280
|
init_openai_codex();
|
|
41281
|
+
SESSION_REFRESHED_GRANTS = ["grok", "github-copilot"];
|
|
41184
41282
|
});
|
|
41185
41283
|
|
|
41186
41284
|
// src/cli.ts
|
|
@@ -41193,7 +41291,7 @@ import {
|
|
|
41193
41291
|
chmod as chmod2,
|
|
41194
41292
|
mkdir as mkdir14,
|
|
41195
41293
|
readFile as readFile23,
|
|
41196
|
-
unlink,
|
|
41294
|
+
unlink as unlink2,
|
|
41197
41295
|
writeFile as writeFile16
|
|
41198
41296
|
} from "fs/promises";
|
|
41199
41297
|
import { existsSync as existsSync11 } from "fs";
|
|
@@ -42784,7 +42882,7 @@ var BUNDLED_GDSKILLS = [
|
|
|
42784
42882
|
"If the request asks to create, run, resume, track, or finish a managed flow and Task Manager is enabled, route implementation work to `gdskills/orchestration/flow-orchestrator/SKILL.md` before `job-orchestrator`.",
|
|
42785
42883
|
"Prefer project-local skills and module manifests before broad raw file search.",
|
|
42786
42884
|
"Route to the narrowest applicable skill and record unavailable modules explicitly."
|
|
42787
|
-
], ["any repository task", "route context", "which skill should be used", "ordinary product-development request", "agent should decide tools"]),
|
|
42885
|
+
], ["any repository task", "route context", "which skill should be used", "ordinary product-development request", "agent should decide tools"], "Use when a request's routing to a Metaproject module, skill, or project-skill is unclear and needs deciding before any work starts. NOT for: picking between gdgraph, gdctx, gdwiki, memory, and health once the module is already known (see context-router)."),
|
|
42788
42886
|
skill("context-router", "core", ["minimal", "recommended", "full"], "Choose between gdgraph, gdctx, gdwiki, memory, health, and project-skills before raw file reads.", [
|
|
42789
42887
|
"Start from the user's goal, not from command names.",
|
|
42790
42888
|
"Use gdgraph for file relationships and affected context.",
|
|
@@ -42794,13 +42892,13 @@ var BUNDLED_GDSKILLS = [
|
|
|
42794
42892
|
"Use health for normalized quality and gate status; use testing for test selection and test context.",
|
|
42795
42893
|
"Prefer MCP tools/resources for these capabilities when connected; otherwise use the matching skill and CLI command.",
|
|
42796
42894
|
"Use project-skills for known modules, components, stores, services, and domain entities."
|
|
42797
|
-
], ["find files", "understand code", "collect context", "what should I inspect", "agent routing"]),
|
|
42895
|
+
], ["find files", "understand code", "collect context", "what should I inspect", "agent routing"], "Use when the Metaproject module or skill is already known and the next step is picking gdgraph, gdctx, gdwiki, memory, health, or project-skills before reading raw files. NOT for: deciding which top-level skill or module should handle a request (see metaproject-router)."),
|
|
42798
42896
|
skill("entity-skill-router", "core", ["minimal", "recommended", "full"], "Select relevant project-skills for known modules, components, stores, services, and domain entities.", [
|
|
42799
42897
|
"Check `.metaproject/project-skills` for matching module/entity skills.",
|
|
42800
42898
|
"Use gdgraph affected context to find nearby entities when the target is a file.",
|
|
42801
42899
|
"Load only the matching project-skill and directly referenced files.",
|
|
42802
42900
|
"If no project-skill exists, suggest creating one with `keryx skills generate`."
|
|
42803
|
-
], ["project skill", "component pattern", "module-specific work"]),
|
|
42901
|
+
], ["project skill", "component pattern", "module-specific work"], "Use when a known module, component, store, service, or domain entity already has a project-skill that should be loaded instead of searched for from scratch. NOT for: scaffolding a project-skill that does not exist yet (see entity-skill-creator), checking one against current code (see entity-skill-verifier), or applying a finding to one (see entity-skill-learner)."),
|
|
42804
42902
|
skill("entity-skill-creator", "core", ["minimal", "recommended", "full"], "Create canonical project-skills from a path, symbol, wiki page, module, component, store, service, or domain entity.", [
|
|
42805
42903
|
"Normalize the target into module, entity, files, symbols, and wiki references.",
|
|
42806
42904
|
"Collect evidence from gdgraph, gdctx, gdwiki, health, and memory when available.",
|
|
@@ -42809,7 +42907,7 @@ var BUNDLED_GDSKILLS = [
|
|
|
42809
42907
|
"To refresh a project-skill from its Origin (or a new file), run `keryx skills update <module>/<name> [--from <origin>]`.",
|
|
42810
42908
|
"Run `keryx skills route <target>` and `keryx skills inspect <module>/<skill-name>` to confirm registration and routing.",
|
|
42811
42909
|
"Run `keryx skills verify <module>/<skill-name>` and finish with `keryx skills status`."
|
|
42812
|
-
], ["create skill", "generate project skill", "new entity skill", "\u0441\u043E\u0437\u0434\u0430\u0439 \u0441\u043A\u0438\u043B", "\u0441\u043E\u0437\u0434\u0430\u0439 \u0441\u043A\u0438\u043B\u043B \u0434\u043B\u044F <path>", "import skill", "import skills from", "update skill from", "\u043F\u043E\u0434\u0442\u044F\u043D\u0438 \u0441\u043A\u0438\u043B\u044B", "\u043E\u0431\u043D\u043E\u0432\u0438 \u0441\u043A\u0438\u043B"]),
|
|
42910
|
+
], ["create skill", "generate project skill", "new entity skill", "\u0441\u043E\u0437\u0434\u0430\u0439 \u0441\u043A\u0438\u043B", "\u0441\u043E\u0437\u0434\u0430\u0439 \u0441\u043A\u0438\u043B\u043B \u0434\u043B\u044F <path>", "import skill", "import skills from", "update skill from", "\u043F\u043E\u0434\u0442\u044F\u043D\u0438 \u0441\u043A\u0438\u043B\u044B", "\u043E\u0431\u043D\u043E\u0432\u0438 \u0441\u043A\u0438\u043B"], "Use when no project-skill exists yet for a target module, component, store, service, or domain entity, or one needs scaffolding, importing, or refreshing from its origin. NOT for: loading a project-skill that already exists (see entity-skill-router)."),
|
|
42813
42911
|
skill("reviewer-skill-creator", "core", ["recommended", "full"], "Create a project-local reviewer for review-orchestrator from a rules file, review profile, or written team standard.", [
|
|
42814
42912
|
"Read the source in full and sort it into method, convention, and persona before writing anything.",
|
|
42815
42913
|
"Scaffold with `keryx skills create <target> --module review --name <reviewer> --note <gist> --origin <source file>`; the target is a routing key, the note is the prose.",
|
|
@@ -42819,18 +42917,18 @@ var BUNDLED_GDSKILLS = [
|
|
|
42819
42917
|
"Confirm with `keryx review reviewers` \u2014 creating files is not registration, and registration is not discovery.",
|
|
42820
42918
|
"For existing SKILL.md packages, run `keryx skills import --from <dir|file|https-url> --module <module>`; `keryx review import` is the review-shaped alias with the review-vantage-* prefix."
|
|
42821
42919
|
], ["create a reviewer", "new reviewer for review-orchestrator", "make a reviewer from this profile", "\u0441\u043E\u0437\u0434\u0430\u0439 \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430", "\u0441\u043E\u0437\u0434\u0430\u0439 \u043D\u043E\u0432\u043E\u0433\u043E \u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 \u043D\u0430 \u043E\u0441\u043D\u043E\u0432\u0430\u043D\u0438\u0438", "import vantage reviewers", "import overlay reviewers"]),
|
|
42822
|
-
skill("entity-skill-verifier", "core", ["minimal", "recommended", "full"], "
|
|
42823
|
-
"Resolve
|
|
42824
|
-
"
|
|
42825
|
-
"
|
|
42826
|
-
"
|
|
42827
|
-
], ["verify skill", "skill-verify-skill", "stale skill"]),
|
|
42920
|
+
skill("entity-skill-verifier", "core", ["minimal", "recommended", "full"], "Run `keryx skills verify` to check a project-skill's required files, SKILL.md metadata, manifest registration, target-path existence, and evidence artifacts (gdgraph, gdctx, validated gdwiki, Code Health, canonical accepted memory), then classify it as fresh, needs-review, stale, or blocked. The command does not read the skill's prose or compare it against current code \u2014 that comparison is a manual agent step.", [
|
|
42921
|
+
"Resolve the target project-skill through gdgraph affected context or `keryx skills route <target>`.",
|
|
42922
|
+
"Run `keryx skills verify <module>/<skill-name>` (`--dry-run` previews without writing).",
|
|
42923
|
+
"The command checks required files, SKILL.md metadata (version, target, last-verified), manifest registration, target path existence, and evidence artifacts for gdgraph, gdctx, gdwiki, Code Health, and memory consultation, then classifies the skill fresh, needs-review, stale, or blocked and writes the JSON report plus `verification.md`.",
|
|
42924
|
+
"Manually read the skill and the code, wiki, and health evidence it points at to check its claims \u2014 the command does not do this comparison \u2014 then route stale or blocked findings to entity-skill-learner."
|
|
42925
|
+
], ["verify skill", "skill-verify-skill", "stale skill"], "Use when an existing project-skill needs classifying \u2014 fresh, needs-review, stale, or blocked \u2014 from its required files, metadata, manifest registration, target-path existence, and evidence artifacts (gdgraph, gdctx, gdwiki, Code Health, memory), followed by a manual read of its claims against current code as this skill's next step. NOT for: applying a review, test, or health finding to update a skill (see entity-skill-learner)."),
|
|
42828
42926
|
skill("entity-skill-learner", "core", ["minimal", "recommended", "full"], "Update project-skills from review findings, test failures, health reports, memory entries, and verifier reports.", [
|
|
42829
42927
|
"Parse the source report and map findings to project-skills.",
|
|
42830
42928
|
"Classify lessons as anti-patterns, checklist changes, template changes, workflow changes, or architecture rules.",
|
|
42831
42929
|
"Respect manual sections and autonomy policy.",
|
|
42832
42930
|
"Increment version and append `skill-changelog.md` entries with provenance."
|
|
42833
|
-
], ["learn from review", "update skill", "skill lesson"]),
|
|
42931
|
+
], ["learn from review", "update skill", "skill lesson"], "Use when a review finding, test failure, health report, memory entry, or verifier report points at a project-skill that needs updating. NOT for: checking a skill's current accuracy with no source report driving it (see entity-skill-verifier)."),
|
|
42834
42932
|
skill("job-orchestrator", "orchestration", ["recommended", "full"], "Run full task pipelines: clarify, collect context, plan, implement, verify, review, and summarize.", [
|
|
42835
42933
|
"Clarify ambiguity with interviewer-style questions only when required.",
|
|
42836
42934
|
"Collect compact context through Metaproject modules before implementation.",
|
|
@@ -43177,7 +43275,7 @@ var BUNDLED_GDSKILLS = [
|
|
|
43177
43275
|
"Find existing root agent entrypoints.",
|
|
43178
43276
|
"Keep managed Metaproject blocks idempotent.",
|
|
43179
43277
|
"Ensure local `.metaproject/index.md` and skill catalog are first-class references."
|
|
43180
|
-
], ["agents.md", "claude.md", "entrypoint"]),
|
|
43278
|
+
], ["agents.md", "claude.md", "entrypoint"], "Use when AGENTS.md or CLAUDE.md needs its managed Metaproject block added, refreshed, or kept idempotent. NOT for: splitting an oversized entrypoint into rules and project-skills (see agent-entrypoint-distiller)."),
|
|
43181
43279
|
skill("agent-entrypoint-distiller", "platform", ["minimal", "recommended", "full"], "Split large AGENTS.md/CLAUDE.md files into high-priority Metaproject rules and project-specific skills.", [
|
|
43182
43280
|
"Run `keryx rules distill` when the user asks to decompose a large CLAUDE.md/AGENTS.md.",
|
|
43183
43281
|
"Keep root entrypoints compact: non-project/highest-priority instructions plus `.metaproject/index.md` routing.",
|
|
@@ -43187,7 +43285,7 @@ var BUNDLED_GDSKILLS = [
|
|
|
43187
43285
|
"Install hooks only when explicitly enabled.",
|
|
43188
43286
|
"Keep hooks lightweight and idempotent.",
|
|
43189
43287
|
"Avoid network and destructive behavior inside hooks."
|
|
43190
|
-
], ["install hook", "git hook", "post-commit"]),
|
|
43288
|
+
], ["install hook", "git hook", "post-commit"], "Use when a lightweight, explicitly-enabled git hook for graph, health, or skill verification needs installing or checking. NOT for: general hook design guidance not tied to these three (see hookify)."),
|
|
43191
43289
|
skill("hookify", "platform", ["full"], "Use hook guidance for safe hook design and installation.", [
|
|
43192
43290
|
"Detect existing hooks and preserve user content.",
|
|
43193
43291
|
"Install idempotent managed blocks.",
|
|
@@ -43202,23 +43300,23 @@ var BUNDLED_GDSKILLS = [
|
|
|
43202
43300
|
"Read bundled and project-local skill metadata.",
|
|
43203
43301
|
"Generate concise catalog entries grouped by category.",
|
|
43204
43302
|
"Keep catalog deterministic and local-first."
|
|
43205
|
-
], ["skill catalog", "list skills", "skills registry"]),
|
|
43303
|
+
], ["skill catalog", "list skills", "skills registry"], "Use when `.metaproject/skills/catalog.md` or the machine-readable skill registry is missing or out of date with bundled and project-local skill metadata. NOT for: turning skills into runtime artifacts (see skill-runtime-exporter) or pushing them to local runtimes (see skill-sync)."),
|
|
43206
43304
|
skill("skill-runtime-exporter", "platform", ["full"], "Export canonical skills to runtime-compatible Codex or Claude artifacts.", [
|
|
43207
43305
|
"Read canonical skill packages.",
|
|
43208
43306
|
"Remove management-only files from runtime exports.",
|
|
43209
43307
|
"Keep runtime `SKILL.md` concise with references, scripts, and assets as needed."
|
|
43210
|
-
], ["export skill", "runtime skill", "codex skill"]),
|
|
43308
|
+
], ["export skill", "runtime skill", "codex skill"], "Use when canonical skill packages need turning into runtime-compatible Codex or Claude artifacts, stripped of management-only files. NOT for: regenerating the skill catalog (see skill-catalog-manager) or pushing the export to local runtimes (see skill-sync)."),
|
|
43211
43309
|
skill("skill-sync", "platform", ["full"], "Sync exported runtime skills to configured local runtimes only when explicitly enabled.", [
|
|
43212
43310
|
"Read configured runtime targets.",
|
|
43213
43311
|
"Validate runtime skill packages before sync.",
|
|
43214
43312
|
"Sync only selected skills and report changed files."
|
|
43215
|
-
], ["sync skills", "install runtime skills", "global skill sync"])
|
|
43313
|
+
], ["sync skills", "install runtime skills", "global skill sync"], "Use when already-exported runtime skills need pushing to configured local runtimes, and only when sync is explicitly enabled. NOT for: producing the runtime export itself (see skill-runtime-exporter).")
|
|
43216
43314
|
];
|
|
43217
|
-
function skill(name, category, profiles, purpose, workflow, triggers) {
|
|
43315
|
+
function skill(name, category, profiles, purpose, workflow, triggers, descriptionOverride) {
|
|
43218
43316
|
return {
|
|
43219
43317
|
name,
|
|
43220
43318
|
category,
|
|
43221
|
-
description: `Use when ${purpose.charAt(0).toLowerCase()}${purpose.slice(1)}`,
|
|
43319
|
+
description: descriptionOverride ?? `Use when ${purpose.charAt(0).toLowerCase()}${purpose.slice(1)}`,
|
|
43222
43320
|
purpose,
|
|
43223
43321
|
workflow,
|
|
43224
43322
|
triggers,
|
|
@@ -43243,9 +43341,10 @@ function renderBundledSkill(skillEntry) {
|
|
|
43243
43341
|
const workflow = skillEntry.workflow.map((step, index) => `${index + 1}. ${step}`).join(`
|
|
43244
43342
|
`);
|
|
43245
43343
|
const commandContract = renderAgentCommandContract(skillEntry);
|
|
43344
|
+
const descriptionField = skillEntry.description.includes(":") ? `description: ${JSON.stringify(skillEntry.description)}` : `description: ${skillEntry.description}`;
|
|
43246
43345
|
return `---
|
|
43247
43346
|
name: ${skillEntry.name}
|
|
43248
|
-
|
|
43347
|
+
${descriptionField}
|
|
43249
43348
|
---
|
|
43250
43349
|
|
|
43251
43350
|
# ${skillEntry.name}
|
|
@@ -43380,8 +43479,9 @@ ${sections}
|
|
|
43380
43479
|
|
|
43381
43480
|
// src/gdskills/install.ts
|
|
43382
43481
|
init_fs();
|
|
43383
|
-
import { cp, copyFile, mkdir as mkdir7, readFile as readFile10, writeFile as writeFile7 } from "fs/promises";
|
|
43482
|
+
import { cp, copyFile, lstat as lstat3, mkdir as mkdir7, readFile as readFile10, unlink, writeFile as writeFile7 } from "fs/promises";
|
|
43384
43483
|
import { existsSync as existsSync9 } from "fs";
|
|
43484
|
+
import { createHash as createHash5 } from "crypto";
|
|
43385
43485
|
import path31 from "path";
|
|
43386
43486
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
43387
43487
|
|
|
@@ -43742,6 +43842,27 @@ function relativeContractPath(contract) {
|
|
|
43742
43842
|
return contract.sourcePath ?? path30.join("src", "gdskills", "contracts", contract.fileName);
|
|
43743
43843
|
}
|
|
43744
43844
|
|
|
43845
|
+
// src/gdskills/retired-rules.ts
|
|
43846
|
+
var RETIRED_RULES = [
|
|
43847
|
+
{
|
|
43848
|
+
fileName: "review-agent-profile.mdc",
|
|
43849
|
+
shippedSha256: [
|
|
43850
|
+
"e29a832821e501fa8a0acb5130007db1e21f1cee29f8ab6b2a1a7b35f9945281"
|
|
43851
|
+
],
|
|
43852
|
+
reason: "Byte-identical duplicate of code-review-ai-assistant.mdc \u2014 nothing distinguished the two, and only the latter was ever routed to."
|
|
43853
|
+
},
|
|
43854
|
+
{
|
|
43855
|
+
fileName: "review-strict-profile.mdc",
|
|
43856
|
+
shippedSha256: [
|
|
43857
|
+
"2e8847426bfba76ece8a208952402edadc6a062920aed07a721902b635a3f0e6",
|
|
43858
|
+
"bc7e6df820682f75cbc1349e70d98b5692aa1c4bae903e28f41a67edb7a5a484"
|
|
43859
|
+
],
|
|
43860
|
+
reason: "Outlived the review-strict skill it backed; review-orchestrator's Step 6 now resolves the model tier directly via `keryx review tier` instead."
|
|
43861
|
+
}
|
|
43862
|
+
];
|
|
43863
|
+
var LARGEST_SHIPPED_RETIRED_RULE_BYTES = 2248;
|
|
43864
|
+
var RETIRED_RULE_SIZE_CAP_BYTES = LARGEST_SHIPPED_RETIRED_RULE_BYTES * 4;
|
|
43865
|
+
|
|
43745
43866
|
// src/gdskills/install.ts
|
|
43746
43867
|
async function installGdskills(metaprojectRoot, profile, options = {}) {
|
|
43747
43868
|
const skills = getBundledSkillsForProfile(profile);
|
|
@@ -43773,18 +43894,20 @@ async function installGdskills(metaprojectRoot, profile, options = {}) {
|
|
|
43773
43894
|
}
|
|
43774
43895
|
}
|
|
43775
43896
|
await installBundledSharedSkills(skillsRoot);
|
|
43776
|
-
await installBundledRules(metaprojectRoot);
|
|
43897
|
+
const retiredRuleOutcomes = await installBundledRules(metaprojectRoot);
|
|
43777
43898
|
const catalogPath = path31.join(metaprojectRoot, "skills", "catalog.md");
|
|
43778
43899
|
await writeFile7(catalogPath, await preserveProjectSkillsSection(catalogPath, renderGdskillsCatalog(profile)), "utf8");
|
|
43779
43900
|
const manifestPath = path31.join(metaprojectRoot, "modules", "gdskills.md");
|
|
43780
43901
|
await writeFile7(manifestPath, renderGdskillsManifest(profile), "utf8");
|
|
43781
43902
|
await installContracts(contractsRoot);
|
|
43903
|
+
const warnings = retiredRuleOutcomes.map(retiredRuleWarning).filter((warning) => warning !== null);
|
|
43782
43904
|
return {
|
|
43783
43905
|
profile,
|
|
43784
43906
|
installedSkills: skills.length,
|
|
43785
43907
|
skillsRoot,
|
|
43786
43908
|
catalogPath,
|
|
43787
|
-
manifestPath
|
|
43909
|
+
manifestPath,
|
|
43910
|
+
warnings
|
|
43788
43911
|
};
|
|
43789
43912
|
}
|
|
43790
43913
|
async function installBundledSharedSkills(skillsRoot) {
|
|
@@ -43796,10 +43919,83 @@ async function installBundledSharedSkills(skillsRoot) {
|
|
|
43796
43919
|
}
|
|
43797
43920
|
async function installBundledRules(metaprojectRoot) {
|
|
43798
43921
|
const rulesSource = bundledRulesSourcePath();
|
|
43922
|
+
const rulesTarget = path31.join(metaprojectRoot, "rules", "core");
|
|
43799
43923
|
if (!existsSync9(rulesSource)) {
|
|
43800
|
-
return;
|
|
43924
|
+
return [];
|
|
43925
|
+
}
|
|
43926
|
+
await cp(rulesSource, rulesTarget, { recursive: true, force: true });
|
|
43927
|
+
return removeUnmodifiedRetiredRules(rulesTarget);
|
|
43928
|
+
}
|
|
43929
|
+
async function removeUnmodifiedRetiredRules(rulesTarget, fsOps = { unlink }) {
|
|
43930
|
+
const outcomes = [];
|
|
43931
|
+
for (const retired of RETIRED_RULES) {
|
|
43932
|
+
const installedPath = path31.join(rulesTarget, retired.fileName);
|
|
43933
|
+
let stage = "lstat";
|
|
43934
|
+
try {
|
|
43935
|
+
const stats = await lstat3(installedPath);
|
|
43936
|
+
if (!stats.isFile()) {
|
|
43937
|
+
outcomes.push({ fileName: retired.fileName, action: "kept-not-regular-file" });
|
|
43938
|
+
continue;
|
|
43939
|
+
}
|
|
43940
|
+
if (stats.size > RETIRED_RULE_SIZE_CAP_BYTES) {
|
|
43941
|
+
outcomes.push({ fileName: retired.fileName, action: "kept-oversized", sizeBytes: stats.size });
|
|
43942
|
+
continue;
|
|
43943
|
+
}
|
|
43944
|
+
stage = "read";
|
|
43945
|
+
const content = await readFile10(installedPath);
|
|
43946
|
+
const hash = createHash5("sha256").update(normalizeRetiredRuleContent(content)).digest("hex");
|
|
43947
|
+
if (retired.shippedSha256.includes(hash)) {
|
|
43948
|
+
stage = "unlink";
|
|
43949
|
+
await fsOps.unlink(installedPath);
|
|
43950
|
+
outcomes.push({ fileName: retired.fileName, action: "removed" });
|
|
43951
|
+
} else {
|
|
43952
|
+
outcomes.push({ fileName: retired.fileName, action: "kept-modified" });
|
|
43953
|
+
}
|
|
43954
|
+
} catch (error) {
|
|
43955
|
+
if (isErrnoException(error) && error.code === "ENOENT") {
|
|
43956
|
+
continue;
|
|
43957
|
+
}
|
|
43958
|
+
outcomes.push({
|
|
43959
|
+
fileName: retired.fileName,
|
|
43960
|
+
action: "kept-error",
|
|
43961
|
+
stage,
|
|
43962
|
+
errorCode: isErrnoException(error) && error.code ? error.code : "UNKNOWN"
|
|
43963
|
+
});
|
|
43964
|
+
}
|
|
43965
|
+
}
|
|
43966
|
+
return outcomes;
|
|
43967
|
+
}
|
|
43968
|
+
function normalizeRetiredRuleContent(content) {
|
|
43969
|
+
let text = content.toString("utf8");
|
|
43970
|
+
if (text.charCodeAt(0) === 65279) {
|
|
43971
|
+
text = text.slice(1);
|
|
43972
|
+
}
|
|
43973
|
+
return text.replace(/\r\n/g, `
|
|
43974
|
+
`);
|
|
43975
|
+
}
|
|
43976
|
+
function isErrnoException(error) {
|
|
43977
|
+
return error instanceof Error && "code" in error;
|
|
43978
|
+
}
|
|
43979
|
+
function retiredRuleWarning(outcome) {
|
|
43980
|
+
if (outcome.action === "removed") {
|
|
43981
|
+
return null;
|
|
43982
|
+
}
|
|
43983
|
+
const reason = RETIRED_RULES.find((retired) => retired.fileName === outcome.fileName)?.reason ?? "no longer shipped by keryx";
|
|
43984
|
+
const prefix = `${outcome.fileName} is no longer shipped by keryx (${reason})`;
|
|
43985
|
+
switch (outcome.action) {
|
|
43986
|
+
case "kept-modified":
|
|
43987
|
+
return `${prefix}; kept because it differs from every shipped version \u2014 delete it, or rename it if you still rely on it`;
|
|
43988
|
+
case "kept-not-regular-file":
|
|
43989
|
+
return `${prefix}; kept because it is not a regular file (a symlink, directory, or similar) \u2014 keryx will not read or remove it; delete or rename it yourself if appropriate`;
|
|
43990
|
+
case "kept-oversized":
|
|
43991
|
+
return `${prefix}; kept because it is ${outcome.sizeBytes} bytes, far larger than any version keryx ever shipped under this name \u2014 inspect it, then delete or rename it if appropriate`;
|
|
43992
|
+
case "kept-error":
|
|
43993
|
+
return outcome.stage === "unlink" ? `${prefix}; matches a shipped version but could not be removed (${outcome.errorCode}) \u2014 delete it by hand` : `${prefix}; kept because it could not be read (${outcome.errorCode}) \u2014 inspect it, then delete or rename it if appropriate`;
|
|
43994
|
+
default: {
|
|
43995
|
+
const exhaustive = outcome;
|
|
43996
|
+
throw new Error(`unhandled retired rule outcome: ${JSON.stringify(exhaustive)}`);
|
|
43997
|
+
}
|
|
43801
43998
|
}
|
|
43802
|
-
await cp(rulesSource, path31.join(metaprojectRoot, "rules", "core"), { recursive: true, force: true });
|
|
43803
43999
|
}
|
|
43804
44000
|
async function preserveProjectSkillsSection(catalogPath, nextCatalog) {
|
|
43805
44001
|
if (!await pathExists(catalogPath)) {
|
|
@@ -46105,13 +46301,33 @@ function escapeHtml(value) {
|
|
|
46105
46301
|
function renderProjectRulesReadme() {
|
|
46106
46302
|
return `# Project Rules
|
|
46107
46303
|
|
|
46108
|
-
This directory
|
|
46304
|
+
This directory holds two different kinds of rule files. Do not treat them the same way.
|
|
46109
46305
|
|
|
46110
|
-
|
|
46306
|
+
## Imported entrypoint rules (this directory)
|
|
46307
|
+
|
|
46308
|
+
The \`.md\` files here (e.g. \`agents-md.md\`, \`claude-md.md\`) are repository-level instructions
|
|
46309
|
+
imported from root agent entrypoints such as \`AGENTS.md\` or \`CLAUDE.md\`.
|
|
46111
46310
|
|
|
46112
46311
|
- treat files here as high-priority agent-readable mirrors of root instructions;
|
|
46113
46312
|
- update the root entrypoint first when changing project-wide instructions;
|
|
46114
46313
|
- rerun \`keryx rules sync\`, \`keryx init\`, or \`keryx update\` to resync imported rule files.
|
|
46314
|
+
|
|
46315
|
+
## Core rule library (\`core/*.mdc\`)
|
|
46316
|
+
|
|
46317
|
+
\`core/*.mdc\` is an on-demand rule library, not something loaded automatically. A rule file is
|
|
46318
|
+
read only when a skill or \`routing.md\` cites it by path.
|
|
46319
|
+
|
|
46320
|
+
- \`alwaysApply\` and \`globs\` in each rule's frontmatter are Cursor-format metadata for Cursor's
|
|
46321
|
+
own auto-attach behavior; keryx itself does not read or act on either field;
|
|
46322
|
+
- a stack-specific rule (e.g. \`nestjs-dto.mdc\`, \`mobx-store-template.mdc\`) declares the stack(s)
|
|
46323
|
+
it applies to via a \`stack_requires\` frontmatter key, using the same tag vocabulary as a
|
|
46324
|
+
skill's \`metadata.stack_requires\`; it is only relevant to projects on that stack;
|
|
46325
|
+
- the bundled tree (\`src/gdskills/bundled/rules/core/\`) is the source of truth \u2014 \`core/\` here is
|
|
46326
|
+
an installed mirror, and \`keryx init\`/\`keryx update\` overwrite it wholesale on install;
|
|
46327
|
+
a rule retired from the bundled tree is removed from here too when it is an unmodified shipped
|
|
46328
|
+
copy; otherwise it is kept (with a warning that says why \u2014 edited, not a regular file, oversized,
|
|
46329
|
+
or unreadable/unremovable), since discarding it silently could lose your changes or hide a
|
|
46330
|
+
filesystem problem.
|
|
46115
46331
|
`;
|
|
46116
46332
|
}
|
|
46117
46333
|
function renderImportedAgentRules({
|
|
@@ -46119,7 +46335,7 @@ function renderImportedAgentRules({
|
|
|
46119
46335
|
content
|
|
46120
46336
|
}) {
|
|
46121
46337
|
const body = extractAgentRuleBody(content);
|
|
46122
|
-
const normalizedBody = body.length > 0 ? body : `This root entrypoint delegates agent routing to \`.metaproject/index.md\`.
|
|
46338
|
+
const normalizedBody = body.length > 0 && !isHeadingOnlyBody(body) ? body : `This root entrypoint delegates agent routing to \`.metaproject/index.md\`.
|
|
46123
46339
|
|
|
46124
46340
|
Read \`.metaproject/index.md\` first, then follow the high-priority rules, skills, and module references listed there.`;
|
|
46125
46341
|
return `---
|
|
@@ -46143,6 +46359,9 @@ This file is generated from the repository root agent entrypoint. Edit \`${sourc
|
|
|
46143
46359
|
${normalizedBody}
|
|
46144
46360
|
`;
|
|
46145
46361
|
}
|
|
46362
|
+
function isHeadingOnlyBody(body) {
|
|
46363
|
+
return body.replace(/^#[^\n]*\n?/, "").trim().length === 0;
|
|
46364
|
+
}
|
|
46146
46365
|
function extractAgentRuleBody(content) {
|
|
46147
46366
|
const marker = "<!-- keryx:index -->";
|
|
46148
46367
|
const endMarker = "<!-- /keryx:index -->";
|
|
@@ -47243,13 +47462,13 @@ import { readFile as readFile17, rm as rm3 } from "fs/promises";
|
|
|
47243
47462
|
import path44 from "path";
|
|
47244
47463
|
|
|
47245
47464
|
// src/lib/install-plan.ts
|
|
47246
|
-
import { createHash as
|
|
47465
|
+
import { createHash as createHash6 } from "crypto";
|
|
47247
47466
|
import { readFile as readFile16 } from "fs/promises";
|
|
47248
47467
|
import path43 from "path";
|
|
47249
47468
|
// package.json
|
|
47250
47469
|
var package_default = {
|
|
47251
47470
|
name: "@mrciphersmith/keryx",
|
|
47252
|
-
version: "0.2.
|
|
47471
|
+
version: "0.2.98",
|
|
47253
47472
|
description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
47254
47473
|
private: false,
|
|
47255
47474
|
publishConfig: {
|
|
@@ -47390,7 +47609,7 @@ function parseDivergenceResolution(args) {
|
|
|
47390
47609
|
return;
|
|
47391
47610
|
}
|
|
47392
47611
|
function digestOf(content) {
|
|
47393
|
-
return `sha256:${
|
|
47612
|
+
return `sha256:${createHash6("sha256").update(content, "utf8").digest("hex")}`;
|
|
47394
47613
|
}
|
|
47395
47614
|
function installJournalPath(metaprojectRoot) {
|
|
47396
47615
|
return path43.join(metaprojectRoot, INSTALL_JOURNAL_RELATIVE_PATH);
|
|
@@ -49612,8 +49831,10 @@ async function initCommand(args) {
|
|
|
49612
49831
|
await installGdwikiPostCommitHook(projectRoot);
|
|
49613
49832
|
}
|
|
49614
49833
|
}
|
|
49834
|
+
let gdskillsWarnings = [];
|
|
49615
49835
|
if (enableGdskills) {
|
|
49616
|
-
await installGdskills(metaprojectRoot, gdskillsProfile);
|
|
49836
|
+
const gdskillsInstallResult = await installGdskills(metaprojectRoot, gdskillsProfile);
|
|
49837
|
+
gdskillsWarnings = gdskillsInstallResult.warnings;
|
|
49617
49838
|
if (enableGdskillsHook) {
|
|
49618
49839
|
await installGdskillsPostCommitHook(projectRoot);
|
|
49619
49840
|
}
|
|
@@ -49867,6 +50088,12 @@ async function initCommand(args) {
|
|
|
49867
50088
|
if (enableSac) {
|
|
49868
50089
|
statusLine("sac", true, "shared agent context: cross-session workspace propose/review (opt-in)");
|
|
49869
50090
|
}
|
|
50091
|
+
if (gdskillsWarnings.length > 0) {
|
|
50092
|
+
heading("Warnings");
|
|
50093
|
+
for (const warning of gdskillsWarnings) {
|
|
50094
|
+
note(warning);
|
|
50095
|
+
}
|
|
50096
|
+
}
|
|
49870
50097
|
const gitHooksRoot = await resolveGitHooksRoot(projectRoot);
|
|
49871
50098
|
const hookLines = [];
|
|
49872
50099
|
if (enableGdgraph) {
|
|
@@ -50240,7 +50467,7 @@ Use this skill when a task requires code graph context, dependency impact analys
|
|
|
50240
50467
|
4. Use \`keryx gdgraph affected <target>\` before implementation or review.
|
|
50241
50468
|
`;
|
|
50242
50469
|
if (await readFile23(legacyReadmePath, "utf8") === legacyContent) {
|
|
50243
|
-
await
|
|
50470
|
+
await unlink2(legacyReadmePath);
|
|
50244
50471
|
}
|
|
50245
50472
|
}
|
|
50246
50473
|
function runtimeSourcePath(relativePath) {
|
|
@@ -53184,7 +53411,7 @@ function readLiteral(source, start, quote) {
|
|
|
53184
53411
|
|
|
53185
53412
|
// src/wiki/freshness/queue.ts
|
|
53186
53413
|
init_fs();
|
|
53187
|
-
import { readFile as readFile43, rename as rename3, stat as stat10, unlink as
|
|
53414
|
+
import { readFile as readFile43, rename as rename3, stat as stat10, unlink as unlink3, writeFile as writeFile30 } from "fs/promises";
|
|
53188
53415
|
import path86 from "path";
|
|
53189
53416
|
var QUEUE_FILE = "freshness-queue.jsonl";
|
|
53190
53417
|
var ROTATED_FILE = "freshness-queue.1.jsonl";
|
|
@@ -53225,7 +53452,7 @@ async function drainQueue(cwd) {
|
|
|
53225
53452
|
async function clearQueue(cwd) {
|
|
53226
53453
|
for (const file of [rotatedQueuePath(cwd), queuePath(cwd)]) {
|
|
53227
53454
|
if (await pathExists(file)) {
|
|
53228
|
-
await
|
|
53455
|
+
await unlink3(file).catch(() => {
|
|
53229
53456
|
return;
|
|
53230
53457
|
});
|
|
53231
53458
|
}
|
|
@@ -57464,6 +57691,8 @@ var BUNDLED_SKILL_CHECKS = [
|
|
|
57464
57691
|
"frontmatter:name-unique",
|
|
57465
57692
|
"frontmatter:description",
|
|
57466
57693
|
"frontmatter:metadata",
|
|
57694
|
+
"frontmatter:harness-claude",
|
|
57695
|
+
"frontmatter:category",
|
|
57467
57696
|
"catalog:registered",
|
|
57468
57697
|
"model:concrete-declaration",
|
|
57469
57698
|
"persona:name",
|
|
@@ -57608,17 +57837,60 @@ var INSTALLED_PREFIXES = [
|
|
|
57608
57837
|
var GENERATED_PATH_ROOTS = [
|
|
57609
57838
|
{ prefix: "rules/entrypoints/", producedBy: "keryx rules distill" }
|
|
57610
57839
|
];
|
|
57611
|
-
var PATH_REFERENCE = /(?:^|[\s"'`([])((?:\.metaproject\/|skills\/|rules\/|scripts\/)[\w
|
|
57612
|
-
function
|
|
57840
|
+
var PATH_REFERENCE = /(?:^|[\s"'`([])((?:\.metaproject\/|skills\/|rules\/|scripts\/)[\w./@*-]*[\w.@*-])/g;
|
|
57841
|
+
function resolveInstalledReference(reference, known) {
|
|
57613
57842
|
for (const [from, to] of INSTALLED_PREFIXES) {
|
|
57614
57843
|
if (reference.startsWith(from))
|
|
57615
57844
|
return to + reference.slice(from.length);
|
|
57616
57845
|
}
|
|
57617
57846
|
if (reference.startsWith(".metaproject/"))
|
|
57618
57847
|
return;
|
|
57619
|
-
const
|
|
57848
|
+
const segments = reference.split("/");
|
|
57849
|
+
const root = segments[0] ?? "";
|
|
57850
|
+
if (root === "skills") {
|
|
57851
|
+
if (segments.length === 3 && known.has(segments[2] ?? ""))
|
|
57852
|
+
return reference;
|
|
57853
|
+
return null;
|
|
57854
|
+
}
|
|
57620
57855
|
return CHECKED_PATH_ROOTS.includes(root) ? reference : undefined;
|
|
57621
57856
|
}
|
|
57857
|
+
function scanCrossReferences(text, root, known, checkSkillNames, add) {
|
|
57858
|
+
text.split(`
|
|
57859
|
+
`).forEach((line, index) => {
|
|
57860
|
+
if (checkSkillNames) {
|
|
57861
|
+
for (const pattern of SKILL_REFERENCE_PATTERNS) {
|
|
57862
|
+
for (const match of line.matchAll(pattern)) {
|
|
57863
|
+
const referenced = match[1];
|
|
57864
|
+
if (!referenced.includes("-"))
|
|
57865
|
+
continue;
|
|
57866
|
+
if (known.has(referenced))
|
|
57867
|
+
continue;
|
|
57868
|
+
if (KNOWN_EXTERNAL_SKILL_REFERENCES.has(referenced))
|
|
57869
|
+
continue;
|
|
57870
|
+
add("xref:skill", index + 1, `names a skill \`${referenced}\` that this tree does not ship; either bundle it or stop naming it.`);
|
|
57871
|
+
}
|
|
57872
|
+
}
|
|
57873
|
+
}
|
|
57874
|
+
for (const match of line.matchAll(PATH_REFERENCE)) {
|
|
57875
|
+
const raw = match[1];
|
|
57876
|
+
if (raw.includes("<") || raw.includes("*") || raw.includes("$"))
|
|
57877
|
+
continue;
|
|
57878
|
+
const resolved = resolveInstalledReference(raw, known);
|
|
57879
|
+
if (resolved === undefined)
|
|
57880
|
+
continue;
|
|
57881
|
+
if (resolved === null) {
|
|
57882
|
+
add("xref:path", index + 1, `path \`${raw}\` names the SOURCE tree's own layout, not an installed one \u2014 an installed project has no top-level \`skills/\`, only \`.metaproject/skills/gdskills/\u2026\`. Write \`.metaproject/skills/gdskills/${raw.slice("skills/".length)}\` (or the \`skills/gdskills/\u2026\` form), or, to name the whole skill rather than a file in it, drop everything past its directory.`);
|
|
57883
|
+
continue;
|
|
57884
|
+
}
|
|
57885
|
+
if (GENERATED_PATH_ROOTS.some((entry) => resolved === entry.prefix.replace(/\/$/, "") || resolved.startsWith(entry.prefix))) {
|
|
57886
|
+
continue;
|
|
57887
|
+
}
|
|
57888
|
+
if (existsSync24(path143.join(root, resolved)))
|
|
57889
|
+
continue;
|
|
57890
|
+
add("xref:path", index + 1, `path \`${raw}\` resolves to nothing under the shipped tree (looked for \`${resolved}\`).`);
|
|
57891
|
+
}
|
|
57892
|
+
});
|
|
57893
|
+
}
|
|
57622
57894
|
var SKILL_DOCUMENT_NAMES = new Set(HARNESS_SKILL_RUNTIMES.map((runtime) => skillBuildFileName(runtime)));
|
|
57623
57895
|
var KNOWN_SKILL_COMPANION_DOCUMENTS = new Map([
|
|
57624
57896
|
[
|
|
@@ -57650,6 +57922,12 @@ function bundledSkillFiles(root) {
|
|
|
57650
57922
|
function bundledSkillDocuments(root) {
|
|
57651
57923
|
return walkSkillDocuments(root, (name) => SKILL_DOCUMENT_NAMES.has(name));
|
|
57652
57924
|
}
|
|
57925
|
+
function bundledRuleFiles(root) {
|
|
57926
|
+
const rulesRoot = path143.join(root, "rules", "core");
|
|
57927
|
+
if (!existsSync24(rulesRoot))
|
|
57928
|
+
return [];
|
|
57929
|
+
return readdirSync2(rulesRoot, { withFileTypes: true }).filter((entry) => entry.isFile()).map((entry) => path143.join(rulesRoot, entry.name)).sort();
|
|
57930
|
+
}
|
|
57653
57931
|
function defaultBundledRoot() {
|
|
57654
57932
|
const directPath = path143.join(import.meta.dir, "bundled");
|
|
57655
57933
|
if (existsSync24(directPath))
|
|
@@ -57672,6 +57950,7 @@ function evaluateBundledTree(root = defaultBundledRoot()) {
|
|
|
57672
57950
|
const skillDir = path143.dirname(file);
|
|
57673
57951
|
const skill = path143.basename(skillDir);
|
|
57674
57952
|
const rel = path143.relative(skillsRoot, file).split(path143.sep).join("/");
|
|
57953
|
+
const category = path143.dirname(rel).split("/")[0] ?? "";
|
|
57675
57954
|
const text = readFileSync6(file, "utf8");
|
|
57676
57955
|
const add = (check, line, message) => {
|
|
57677
57956
|
findings.push({ check, skill, file: rel, line, message });
|
|
@@ -57710,9 +57989,25 @@ function evaluateBundledTree(root = defaultBundledRoot()) {
|
|
|
57710
57989
|
if (metadataVersion === null) {
|
|
57711
57990
|
add("frontmatter:metadata", 1, "frontmatter `metadata` declares no `version`; without one a skill cannot be said to have changed.");
|
|
57712
57991
|
}
|
|
57992
|
+
const metadataCategory = /^\s{2,}category\s*:\s*(.+)$/m.exec(block);
|
|
57993
|
+
if (metadataCategory !== null) {
|
|
57994
|
+
const declaredCategory = (metadataCategory[1] ?? "").trim().replace(/^["']|["']$/g, "");
|
|
57995
|
+
if (declaredCategory.length > 0 && declaredCategory !== category) {
|
|
57996
|
+
add("frontmatter:category", 1, `frontmatter \`metadata.category\` is "${declaredCategory}" but this skill ships under \`${category}/${skill}\`; set it to "${category}" or drop the field.`);
|
|
57997
|
+
}
|
|
57998
|
+
}
|
|
57999
|
+
}
|
|
58000
|
+
if (path143.basename(file) === "SKILL.md") {
|
|
58001
|
+
const compatibleHarnesses = /^\s{2,}compatible_harnesses\s*:\s*(.+)$/m.exec(block);
|
|
58002
|
+
if (compatibleHarnesses !== null) {
|
|
58003
|
+
const declared = (compatibleHarnesses[1] ?? "").trim().replace(/^["']|["']$/g, "");
|
|
58004
|
+
const harnesses = declared.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
58005
|
+
if (!harnesses.includes("claude")) {
|
|
58006
|
+
add("frontmatter:harness-claude", 1, `frontmatter \`compatible_harnesses\` ("${declared}") omits \`claude\`, but this file IS the Claude build \u2014 add \`claude\` to the list.`);
|
|
58007
|
+
}
|
|
58008
|
+
}
|
|
57713
58009
|
}
|
|
57714
58010
|
}
|
|
57715
|
-
const category = path143.dirname(rel).split("/")[0] ?? "";
|
|
57716
58011
|
if (!catalogued.has(`${category}/${skill}`)) {
|
|
57717
58012
|
add("catalog:registered", null, `BUNDLED_GDSKILLS (src/gdskills/catalog.ts) does not name \`${category}/${skill}\`, so \`keryx skills install\` never copies this directory and no user ever sees it.`);
|
|
57718
58013
|
}
|
|
@@ -57726,35 +58021,15 @@ function evaluateBundledTree(root = defaultBundledRoot()) {
|
|
|
57726
58021
|
for (const offender of homePathOffenders(text)) {
|
|
57727
58022
|
add("path:personal-home", offender.line, offender.why);
|
|
57728
58023
|
}
|
|
57729
|
-
text
|
|
57730
|
-
|
|
57731
|
-
|
|
57732
|
-
|
|
57733
|
-
|
|
57734
|
-
|
|
57735
|
-
|
|
57736
|
-
|
|
57737
|
-
|
|
57738
|
-
if (KNOWN_EXTERNAL_SKILL_REFERENCES.has(referenced))
|
|
57739
|
-
continue;
|
|
57740
|
-
add("xref:skill", index + 1, `names a skill \`${referenced}\` that this tree does not ship; either bundle it or stop naming it.`);
|
|
57741
|
-
}
|
|
57742
|
-
}
|
|
57743
|
-
for (const match of line.matchAll(PATH_REFERENCE)) {
|
|
57744
|
-
const raw = match[1];
|
|
57745
|
-
if (raw.includes("<") || raw.includes("*") || raw.includes("$"))
|
|
57746
|
-
continue;
|
|
57747
|
-
const normalised = normaliseReferencePath(raw);
|
|
57748
|
-
if (normalised === undefined)
|
|
57749
|
-
continue;
|
|
57750
|
-
if (GENERATED_PATH_ROOTS.some((entry) => normalised === entry.prefix.replace(/\/$/, "") || normalised.startsWith(entry.prefix))) {
|
|
57751
|
-
continue;
|
|
57752
|
-
}
|
|
57753
|
-
if (existsSync24(path143.join(root, normalised)))
|
|
57754
|
-
continue;
|
|
57755
|
-
add("xref:path", index + 1, `path \`${raw}\` resolves to nothing under the shipped tree (looked for \`${normalised}\`).`);
|
|
57756
|
-
}
|
|
57757
|
-
});
|
|
58024
|
+
scanCrossReferences(text, root, known, true, add);
|
|
58025
|
+
}
|
|
58026
|
+
for (const file of bundledRuleFiles(root)) {
|
|
58027
|
+
const rel = path143.relative(root, file).split(path143.sep).join("/");
|
|
58028
|
+
const text = readFileSync6(file, "utf8");
|
|
58029
|
+
const add = (check, line, message) => {
|
|
58030
|
+
findings.push({ check, skill: path143.basename(file, path143.extname(file)), file: rel, line, message });
|
|
58031
|
+
};
|
|
58032
|
+
scanCrossReferences(text, root, known, false, add);
|
|
57758
58033
|
}
|
|
57759
58034
|
for (const dir of [...new Set(files.map((file) => path143.dirname(file)))].sort()) {
|
|
57760
58035
|
for (const entry of readdirSync2(dir, { withFileTypes: true })) {
|
|
@@ -57878,6 +58153,12 @@ async function skillsCommand(args) {
|
|
|
57878
58153
|
note(`skills ${relativeToCwd(result.skillsRoot)}`);
|
|
57879
58154
|
note(`catalog ${relativeToCwd(result.catalogPath)}`);
|
|
57880
58155
|
note(`manifest ${relativeToCwd(result.manifestPath)}`);
|
|
58156
|
+
if (result.warnings.length > 0) {
|
|
58157
|
+
console.log("Warnings:");
|
|
58158
|
+
for (const warning of result.warnings) {
|
|
58159
|
+
console.log(`- ${warning}`);
|
|
58160
|
+
}
|
|
58161
|
+
}
|
|
57881
58162
|
nextSteps([
|
|
57882
58163
|
`Browse the catalog: ${style.cyan("keryx skills catalog")}.`,
|
|
57883
58164
|
`Route a request: ${style.cyan("keryx skills route <target>")}.`
|
|
@@ -64758,14 +65039,17 @@ init_pr_comments();
|
|
|
64758
65039
|
init_loop();
|
|
64759
65040
|
|
|
64760
65041
|
// src/review/stack.ts
|
|
64761
|
-
var STACK_TAGS = ["nestjs", "react", "mobx", "prisma"];
|
|
65042
|
+
var STACK_TAGS = ["nestjs", "react", "mobx", "prisma", "playwright", "sql", "http-server"];
|
|
64762
65043
|
function isStackTag(value) {
|
|
64763
65044
|
return STACK_TAGS.includes(value);
|
|
64764
65045
|
}
|
|
64765
65046
|
var TAG_MARKERS = {
|
|
64766
65047
|
react: ["react", "react-dom"],
|
|
64767
65048
|
mobx: ["mobx", "mobx-react", "mobx-react-lite"],
|
|
64768
|
-
prisma: ["prisma", "@prisma/client"]
|
|
65049
|
+
prisma: ["prisma", "@prisma/client"],
|
|
65050
|
+
playwright: ["@playwright/test", "playwright"],
|
|
65051
|
+
sql: ["prisma", "@prisma/client", "typeorm", "sequelize", "knex", "drizzle-orm", "pg", "mysql2", "better-sqlite3", "kysely"],
|
|
65052
|
+
"http-server": ["express", "fastify", "koa", "hono", "@nestjs/core", "@hapi/hapi"]
|
|
64769
65053
|
};
|
|
64770
65054
|
var DEPENDENCY_FIELDS = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"];
|
|
64771
65055
|
async function detectProjectStack(cwd, options = {}) {
|
|
@@ -68127,14 +68411,14 @@ init_fs();
|
|
|
68127
68411
|
init_json();
|
|
68128
68412
|
|
|
68129
68413
|
// src/security/detect/mcp.ts
|
|
68130
|
-
import { createHash as
|
|
68414
|
+
import { createHash as createHash30 } from "crypto";
|
|
68131
68415
|
function hashToolDefinition(tool) {
|
|
68132
68416
|
const canonical = JSON.stringify({
|
|
68133
68417
|
name: tool.name,
|
|
68134
68418
|
description: tool.description ?? "",
|
|
68135
68419
|
inputSchema: tool.inputSchema ?? null
|
|
68136
68420
|
});
|
|
68137
|
-
return
|
|
68421
|
+
return createHash30("sha256").update(canonical).digest("hex");
|
|
68138
68422
|
}
|
|
68139
68423
|
var POISONING_PATTERNS = [
|
|
68140
68424
|
{
|
|
@@ -69236,7 +69520,7 @@ function wrapBwrap(command, profile, launcherPath = BWRAP_PROGRAM) {
|
|
|
69236
69520
|
}
|
|
69237
69521
|
|
|
69238
69522
|
// src/harness/process/sandbox/adapter.ts
|
|
69239
|
-
import { createHash as
|
|
69523
|
+
import { createHash as createHash31 } from "crypto";
|
|
69240
69524
|
|
|
69241
69525
|
// src/harness/process/sandbox/wrap.ts
|
|
69242
69526
|
function wrapWithSandbox(command, profile, opts) {
|
|
@@ -69266,7 +69550,7 @@ function wrapWithSandbox(command, profile, opts) {
|
|
|
69266
69550
|
function spawnError(command, message) {
|
|
69267
69551
|
return {
|
|
69268
69552
|
kind: "spawn-error",
|
|
69269
|
-
observedHash:
|
|
69553
|
+
observedHash: createHash31("sha256").update(`${command.path}
|
|
69270
69554
|
${command.argv.join(" ")}`, "utf8").digest("hex"),
|
|
69271
69555
|
errorMessage: message
|
|
69272
69556
|
};
|
|
@@ -69894,12 +70178,12 @@ function entryProblems(name, entry) {
|
|
|
69894
70178
|
}
|
|
69895
70179
|
function oauthProblems(name, oauth) {
|
|
69896
70180
|
const problems = [];
|
|
69897
|
-
const known = new Set(["clientId", "
|
|
70181
|
+
const known = new Set(["clientId", "scopes", "callbackPort"]);
|
|
69898
70182
|
for (const key of Object.keys(oauth)) {
|
|
69899
70183
|
if (!known.has(key))
|
|
69900
70184
|
problems.push(`server "${name}" oauth has unknown field "${key}"`);
|
|
69901
70185
|
}
|
|
69902
|
-
for (const field of ["clientId"
|
|
70186
|
+
for (const field of ["clientId"]) {
|
|
69903
70187
|
if (oauth[field] !== undefined && typeof oauth[field] !== "string") {
|
|
69904
70188
|
problems.push(`server "${name}" oauth.${field} must be a string`);
|
|
69905
70189
|
}
|
|
@@ -70459,6 +70743,229 @@ function createMcpInteractiveTools(deps) {
|
|
|
70459
70743
|
return [searchTool, useTool];
|
|
70460
70744
|
}
|
|
70461
70745
|
|
|
70746
|
+
// src/mcp-servers/credentials.ts
|
|
70747
|
+
init_config_dir();
|
|
70748
|
+
import path165 from "path";
|
|
70749
|
+
var FORGET_CODE_VERIFIER = { code_verifier: undefined };
|
|
70750
|
+
var CREDENTIALS_SCHEMA_VERSION = 1;
|
|
70751
|
+
function credentialsFile(configDir) {
|
|
70752
|
+
return path165.join(configDir ?? ensureKeryxConfigDir(), "mcp-credentials.json");
|
|
70753
|
+
}
|
|
70754
|
+
function credentialKey(serverName, serverUrl) {
|
|
70755
|
+
return `${serverName}:${serverUrl}`;
|
|
70756
|
+
}
|
|
70757
|
+
function readAll2(configDir) {
|
|
70758
|
+
const file = credentialsFile(configDir);
|
|
70759
|
+
const read = readConfigFile(file);
|
|
70760
|
+
if (!read.ok) {
|
|
70761
|
+
return isDefiniteAbsence(read.reason) ? { file: {} } : { file: {}, problem: `${file} could not be read (${read.reason})` };
|
|
70762
|
+
}
|
|
70763
|
+
try {
|
|
70764
|
+
const parsed = parseJsonTolerant(read.text);
|
|
70765
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
70766
|
+
return { file: {}, problem: `${file} must be a JSON object` };
|
|
70767
|
+
}
|
|
70768
|
+
const parsedFile = parsed;
|
|
70769
|
+
if (typeof parsedFile.schemaVersion === "number" && parsedFile.schemaVersion > CREDENTIALS_SCHEMA_VERSION) {
|
|
70770
|
+
return {
|
|
70771
|
+
file: {},
|
|
70772
|
+
problem: `${file} was written by a newer keryx ` + `(schemaVersion ${parsedFile.schemaVersion} > ${CREDENTIALS_SCHEMA_VERSION}); nothing was changed`
|
|
70773
|
+
};
|
|
70774
|
+
}
|
|
70775
|
+
return { file: parsedFile };
|
|
70776
|
+
} catch (error) {
|
|
70777
|
+
return {
|
|
70778
|
+
file: {},
|
|
70779
|
+
problem: `${file} is not valid JSON (${error instanceof Error ? error.message : String(error)}); nothing was changed`
|
|
70780
|
+
};
|
|
70781
|
+
}
|
|
70782
|
+
}
|
|
70783
|
+
function readCredential(serverName, serverUrl, configDir) {
|
|
70784
|
+
const { file, problem } = readAll2(configDir);
|
|
70785
|
+
const record = file.credentials?.[credentialKey(serverName, serverUrl)];
|
|
70786
|
+
return problem === undefined ? { record } : { record, problem };
|
|
70787
|
+
}
|
|
70788
|
+
function writeCredential(serverName, serverUrl, patch, configDir) {
|
|
70789
|
+
const target = credentialsFile(configDir);
|
|
70790
|
+
const outcome = withFileLock2(`${target}.lock`, () => mergeUnlocked(target, serverName, serverUrl, patch, configDir));
|
|
70791
|
+
if (outcome === null) {
|
|
70792
|
+
return { ok: false, error: `${target} is locked by another keryx; nothing was written` };
|
|
70793
|
+
}
|
|
70794
|
+
return outcome;
|
|
70795
|
+
}
|
|
70796
|
+
function mergeUnlocked(target, serverName, serverUrl, patch, configDir) {
|
|
70797
|
+
const { file, problem } = readAll2(configDir);
|
|
70798
|
+
if (problem !== undefined) {
|
|
70799
|
+
return { ok: false, error: problem };
|
|
70800
|
+
}
|
|
70801
|
+
const key = credentialKey(serverName, serverUrl);
|
|
70802
|
+
const credentials = { ...file.credentials, [key]: { ...file.credentials?.[key], ...patch } };
|
|
70803
|
+
writeOwnerOnlyFileAtomic(target, `${JSON.stringify({ schemaVersion: CREDENTIALS_SCHEMA_VERSION, credentials }, null, 2)}
|
|
70804
|
+
`);
|
|
70805
|
+
return { ok: true, file: target };
|
|
70806
|
+
}
|
|
70807
|
+
function clearCredential(serverName, serverUrl, configDir) {
|
|
70808
|
+
const target = credentialsFile(configDir);
|
|
70809
|
+
const outcome = withFileLock2(`${target}.lock`, () => {
|
|
70810
|
+
const { file, problem } = readAll2(configDir);
|
|
70811
|
+
if (problem !== undefined)
|
|
70812
|
+
return { ok: false, error: problem };
|
|
70813
|
+
const credentials = { ...file.credentials };
|
|
70814
|
+
delete credentials[credentialKey(serverName, serverUrl)];
|
|
70815
|
+
writeOwnerOnlyFileAtomic(target, `${JSON.stringify({ schemaVersion: CREDENTIALS_SCHEMA_VERSION, credentials }, null, 2)}
|
|
70816
|
+
`);
|
|
70817
|
+
return { ok: true, file: target };
|
|
70818
|
+
});
|
|
70819
|
+
if (outcome === null) {
|
|
70820
|
+
return { ok: false, error: `${target} is locked by another keryx; nothing was removed` };
|
|
70821
|
+
}
|
|
70822
|
+
return outcome;
|
|
70823
|
+
}
|
|
70824
|
+
function forgetByName(serverName, configDir) {
|
|
70825
|
+
const target = credentialsFile(configDir);
|
|
70826
|
+
const outcome = withFileLock2(`${target}.lock`, () => {
|
|
70827
|
+
const { file, problem } = readAll2(configDir);
|
|
70828
|
+
if (problem !== undefined)
|
|
70829
|
+
return { count: 0, error: problem };
|
|
70830
|
+
const credentials = { ...file.credentials };
|
|
70831
|
+
let count = 0;
|
|
70832
|
+
for (const key of Object.keys(credentials)) {
|
|
70833
|
+
const separator = key.indexOf(":");
|
|
70834
|
+
if (separator === -1)
|
|
70835
|
+
continue;
|
|
70836
|
+
if (key.slice(0, separator) !== serverName)
|
|
70837
|
+
continue;
|
|
70838
|
+
delete credentials[key];
|
|
70839
|
+
count++;
|
|
70840
|
+
}
|
|
70841
|
+
if (count > 0) {
|
|
70842
|
+
writeOwnerOnlyFileAtomic(target, `${JSON.stringify({ schemaVersion: CREDENTIALS_SCHEMA_VERSION, credentials }, null, 2)}
|
|
70843
|
+
`);
|
|
70844
|
+
}
|
|
70845
|
+
return { count };
|
|
70846
|
+
});
|
|
70847
|
+
return outcome ?? { count: 0, error: `${target} is locked by another keryx; nothing was removed` };
|
|
70848
|
+
}
|
|
70849
|
+
var EXPIRY_MARGIN_MS = 30000;
|
|
70850
|
+
function isExpired(tokens, now) {
|
|
70851
|
+
if (tokens === undefined)
|
|
70852
|
+
return true;
|
|
70853
|
+
if (tokens.expires_at === undefined) {
|
|
70854
|
+
return false;
|
|
70855
|
+
}
|
|
70856
|
+
return tokens.expires_at - EXPIRY_MARGIN_MS <= now;
|
|
70857
|
+
}
|
|
70858
|
+
function describeCredential(record, now) {
|
|
70859
|
+
if (record?.tokens === undefined)
|
|
70860
|
+
return "no stored credential";
|
|
70861
|
+
if (isExpired(record.tokens, now)) {
|
|
70862
|
+
return record.tokens.refresh_token === undefined ? "stored credential has expired; re-authenticate" : "stored credential has expired; it will be refreshed on the next connect";
|
|
70863
|
+
}
|
|
70864
|
+
if (record.tokens.expires_at === undefined)
|
|
70865
|
+
return "stored credential, no stated expiry";
|
|
70866
|
+
const minutes = Math.max(0, Math.round((record.tokens.expires_at - now) / 60000));
|
|
70867
|
+
return `stored credential, valid for about ${minutes} more minute(s)`;
|
|
70868
|
+
}
|
|
70869
|
+
function usesOAuth(server) {
|
|
70870
|
+
if (typeof server.url !== "string" || server.url === "")
|
|
70871
|
+
return false;
|
|
70872
|
+
if (server.oauth === false)
|
|
70873
|
+
return false;
|
|
70874
|
+
if (server.bearer_token_env_var !== undefined && server.bearer_token_env_var !== "")
|
|
70875
|
+
return false;
|
|
70876
|
+
return !Object.keys(server.headers ?? {}).some((name) => name.toLowerCase() === "authorization");
|
|
70877
|
+
}
|
|
70878
|
+
|
|
70879
|
+
// src/mcp-servers/oauth-provider.ts
|
|
70880
|
+
class OAuthInteractionRequiredError extends Error {
|
|
70881
|
+
serverName;
|
|
70882
|
+
constructor(serverName) {
|
|
70883
|
+
super(`server "${serverName}" needs OAuth and this process cannot ask: run \`keryx mcp auth ${serverName}\` in a terminal`);
|
|
70884
|
+
this.serverName = serverName;
|
|
70885
|
+
this.name = "OAuthInteractionRequiredError";
|
|
70886
|
+
}
|
|
70887
|
+
}
|
|
70888
|
+
var CLIENT_NAME = "keryx";
|
|
70889
|
+
function createOAuthProvider(deps) {
|
|
70890
|
+
const now = deps.now ?? Date.now;
|
|
70891
|
+
const read = () => readCredential(deps.serverName, deps.serverUrl, deps.configDir);
|
|
70892
|
+
return {
|
|
70893
|
+
get redirectUrl() {
|
|
70894
|
+
return deps.redirectUrl;
|
|
70895
|
+
},
|
|
70896
|
+
get clientMetadata() {
|
|
70897
|
+
return {
|
|
70898
|
+
client_name: CLIENT_NAME,
|
|
70899
|
+
redirect_uris: deps.redirectUrl === undefined ? [] : [deps.redirectUrl],
|
|
70900
|
+
grant_types: ["authorization_code", "refresh_token"],
|
|
70901
|
+
response_types: ["code"],
|
|
70902
|
+
...deps.scopes === undefined || deps.scopes.length === 0 ? {} : { scope: deps.scopes.join(" ") },
|
|
70903
|
+
token_endpoint_auth_method: "none"
|
|
70904
|
+
};
|
|
70905
|
+
},
|
|
70906
|
+
state() {
|
|
70907
|
+
if (deps.state === undefined) {
|
|
70908
|
+
if (!deps.interactive)
|
|
70909
|
+
throw new OAuthInteractionRequiredError(deps.serverName);
|
|
70910
|
+
throw new Error("no callback listener is running; state is minted by the listener");
|
|
70911
|
+
}
|
|
70912
|
+
return deps.state;
|
|
70913
|
+
},
|
|
70914
|
+
clientInformation() {
|
|
70915
|
+
if (deps.clientId !== undefined && deps.clientId !== "") {
|
|
70916
|
+
return { client_id: deps.clientId };
|
|
70917
|
+
}
|
|
70918
|
+
return read().record?.client;
|
|
70919
|
+
},
|
|
70920
|
+
saveClientInformation(client) {
|
|
70921
|
+
if (!deps.interactive)
|
|
70922
|
+
throw new OAuthInteractionRequiredError(deps.serverName);
|
|
70923
|
+
const write = writeCredential(deps.serverName, deps.serverUrl, { client }, deps.configDir);
|
|
70924
|
+
if (!write.ok)
|
|
70925
|
+
throw new Error(write.error);
|
|
70926
|
+
},
|
|
70927
|
+
tokens() {
|
|
70928
|
+
const stored = read().record?.tokens;
|
|
70929
|
+
if (stored === undefined)
|
|
70930
|
+
return;
|
|
70931
|
+
if (isExpired(stored, now()) && stored.refresh_token === undefined)
|
|
70932
|
+
return;
|
|
70933
|
+
return stored;
|
|
70934
|
+
},
|
|
70935
|
+
saveTokens(sdkTokens) {
|
|
70936
|
+
const tokens = {
|
|
70937
|
+
access_token: sdkTokens.access_token,
|
|
70938
|
+
...sdkTokens.token_type === undefined ? {} : { token_type: sdkTokens.token_type },
|
|
70939
|
+
...sdkTokens.refresh_token === undefined ? {} : { refresh_token: sdkTokens.refresh_token },
|
|
70940
|
+
...sdkTokens.scope === undefined ? {} : { scope: sdkTokens.scope },
|
|
70941
|
+
...sdkTokens.expires_in === undefined ? {} : { expires_at: now() + sdkTokens.expires_in * 1000 }
|
|
70942
|
+
};
|
|
70943
|
+
const write = writeCredential(deps.serverName, deps.serverUrl, { tokens, ...FORGET_CODE_VERIFIER }, deps.configDir);
|
|
70944
|
+
if (!write.ok)
|
|
70945
|
+
throw new Error(write.error);
|
|
70946
|
+
},
|
|
70947
|
+
async redirectToAuthorization(url) {
|
|
70948
|
+
if (!deps.interactive)
|
|
70949
|
+
throw new OAuthInteractionRequiredError(deps.serverName);
|
|
70950
|
+
if (deps.openBrowser === undefined)
|
|
70951
|
+
throw new OAuthInteractionRequiredError(deps.serverName);
|
|
70952
|
+
await deps.openBrowser(url);
|
|
70953
|
+
},
|
|
70954
|
+
saveCodeVerifier(codeVerifier) {
|
|
70955
|
+
const write = writeCredential(deps.serverName, deps.serverUrl, { code_verifier: codeVerifier }, deps.configDir);
|
|
70956
|
+
if (!write.ok)
|
|
70957
|
+
throw new Error(write.error);
|
|
70958
|
+
},
|
|
70959
|
+
codeVerifier() {
|
|
70960
|
+
const verifier = read().record?.code_verifier;
|
|
70961
|
+
if (verifier === undefined) {
|
|
70962
|
+
throw new Error("no PKCE verifier stored; the authorisation was not started by this keryx");
|
|
70963
|
+
}
|
|
70964
|
+
return verifier;
|
|
70965
|
+
}
|
|
70966
|
+
};
|
|
70967
|
+
}
|
|
70968
|
+
|
|
70462
70969
|
// src/mcp-servers/doctor.ts
|
|
70463
70970
|
function redactValues(values) {
|
|
70464
70971
|
const out = {};
|
|
@@ -70555,12 +71062,38 @@ async function diagnose(server, options) {
|
|
|
70555
71062
|
detail: describeHollow(resolved.hollow)
|
|
70556
71063
|
};
|
|
70557
71064
|
}
|
|
71065
|
+
const declaresOAuth = server.raw.oauth !== undefined && server.raw.oauth !== false;
|
|
71066
|
+
if (declaresOAuth && usesOAuth(server.raw)) {
|
|
71067
|
+
const stored = readCredential(server.name, server.url ?? "", options.configDir);
|
|
71068
|
+
if (stored.problem !== undefined) {
|
|
71069
|
+
return { ...base, status: "needs_auth", toolCount: 0, skipped: [], detail: stored.problem };
|
|
71070
|
+
}
|
|
71071
|
+
const tokens = stored.record?.tokens;
|
|
71072
|
+
if (tokens === undefined || isExpired(tokens, (options.now ?? Date.now)()) && tokens.refresh_token === undefined) {
|
|
71073
|
+
return {
|
|
71074
|
+
...base,
|
|
71075
|
+
status: "needs_auth",
|
|
71076
|
+
toolCount: 0,
|
|
71077
|
+
skipped: [],
|
|
71078
|
+
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}\``
|
|
71079
|
+
};
|
|
71080
|
+
}
|
|
71081
|
+
}
|
|
70558
71082
|
}
|
|
70559
71083
|
const timeoutMs = server.startup_timeout_sec !== undefined ? server.startup_timeout_sec * 1000 : options.connectTimeoutMs ?? DEFAULT_DOCTOR_TIMEOUT_MS;
|
|
70560
71084
|
let connection;
|
|
70561
71085
|
try {
|
|
70562
71086
|
connection = await withTimeout(options.connect(server), timeoutMs, server.name);
|
|
70563
71087
|
} catch (error) {
|
|
71088
|
+
if (base.transport === "http" && usesOAuth(server.raw) && needsAuthorisation(error)) {
|
|
71089
|
+
return {
|
|
71090
|
+
...base,
|
|
71091
|
+
status: "needs_auth",
|
|
71092
|
+
toolCount: 0,
|
|
71093
|
+
skipped: [],
|
|
71094
|
+
detail: `${displayUrl(server.raw.url)} requires authorisation; run \`keryx mcp auth ${server.name}\``
|
|
71095
|
+
};
|
|
71096
|
+
}
|
|
70564
71097
|
return {
|
|
70565
71098
|
...base,
|
|
70566
71099
|
status: "failed",
|
|
@@ -70586,6 +71119,20 @@ async function diagnose(server, options) {
|
|
|
70586
71119
|
} catch {}
|
|
70587
71120
|
}
|
|
70588
71121
|
}
|
|
71122
|
+
function needsAuthorisation(error) {
|
|
71123
|
+
if (httpStatusOf(error) === 401)
|
|
71124
|
+
return true;
|
|
71125
|
+
if (error instanceof OAuthInteractionRequiredError)
|
|
71126
|
+
return true;
|
|
71127
|
+
return DEAD_CREDENTIAL_CODES.has(oauthErrorCodeOf(error));
|
|
71128
|
+
}
|
|
71129
|
+
var DEAD_CREDENTIAL_CODES = new Set(["invalid_grant", "invalid_client"]);
|
|
71130
|
+
function oauthErrorCodeOf(error) {
|
|
71131
|
+
if (typeof error !== "object" || error === null)
|
|
71132
|
+
return "";
|
|
71133
|
+
const code = error.errorCode;
|
|
71134
|
+
return typeof code === "string" ? code : "";
|
|
71135
|
+
}
|
|
70589
71136
|
function explainConnectFailure(transport, server, error) {
|
|
70590
71137
|
const message = sanitiseForDisplay(messageOf(error));
|
|
70591
71138
|
if (transport !== "http")
|
|
@@ -70710,193 +71257,144 @@ function formatDoctorReport(report) {
|
|
|
70710
71257
|
`);
|
|
70711
71258
|
}
|
|
70712
71259
|
|
|
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
|
-
}
|
|
71260
|
+
// src/lib/oauth/open-url.ts
|
|
71261
|
+
import { spawn as spawn5 } from "child_process";
|
|
71262
|
+
function browserOpenPlan(url, platform = process.platform, env = process.env) {
|
|
71263
|
+
if (url.length === 0) {
|
|
71264
|
+
return;
|
|
70730
71265
|
}
|
|
70731
|
-
|
|
70732
|
-
|
|
70733
|
-
|
|
70734
|
-
|
|
70735
|
-
|
|
70736
|
-
|
|
70737
|
-
|
|
70738
|
-
|
|
70739
|
-
|
|
70740
|
-
|
|
70741
|
-
|
|
70742
|
-
|
|
71266
|
+
if (platform === "darwin") {
|
|
71267
|
+
return { cmd: "open", args: [url] };
|
|
71268
|
+
}
|
|
71269
|
+
if (platform === "win32") {
|
|
71270
|
+
return { cmd: "cmd", args: ["/c", "start", "", url] };
|
|
71271
|
+
}
|
|
71272
|
+
if (typeof env.DISPLAY === "string" && env.DISPLAY.length > 0) {
|
|
71273
|
+
return { cmd: "xdg-open", args: [url] };
|
|
71274
|
+
}
|
|
71275
|
+
if (typeof env.WAYLAND_DISPLAY === "string" && env.WAYLAND_DISPLAY.length > 0) {
|
|
71276
|
+
return { cmd: "xdg-open", args: [url] };
|
|
71277
|
+
}
|
|
71278
|
+
return;
|
|
71279
|
+
}
|
|
71280
|
+
function openVerificationUrl(url, deps = {}) {
|
|
71281
|
+
const plan = browserOpenPlan(url, deps.platform ?? process.platform, deps.env ?? process.env);
|
|
71282
|
+
if (plan === undefined) {
|
|
71283
|
+
return;
|
|
71284
|
+
}
|
|
71285
|
+
const spawnFn = deps.spawn ?? spawn5;
|
|
71286
|
+
try {
|
|
71287
|
+
const child = spawnFn(plan.cmd, plan.args, { stdio: "ignore", detached: true });
|
|
71288
|
+
child.on("error", () => {});
|
|
71289
|
+
child.unref();
|
|
71290
|
+
} catch {}
|
|
71291
|
+
}
|
|
71292
|
+
|
|
71293
|
+
// src/mcp-servers/oauth-callback.ts
|
|
71294
|
+
import { randomBytes as randomBytes4, timingSafeEqual } from "crypto";
|
|
71295
|
+
function matchesState(given, expected) {
|
|
71296
|
+
if (given === null)
|
|
71297
|
+
return false;
|
|
71298
|
+
const a = Buffer.from(given, "utf8");
|
|
71299
|
+
const b = Buffer.from(expected, "utf8");
|
|
71300
|
+
if (a.length !== b.length)
|
|
71301
|
+
return false;
|
|
71302
|
+
return timingSafeEqual(a, b);
|
|
71303
|
+
}
|
|
71304
|
+
var LOOPBACK_HOST = "127.0.0.1";
|
|
71305
|
+
var CALLBACK_TIMEOUT_MS = 5 * 60000;
|
|
71306
|
+
function page(title, detail) {
|
|
71307
|
+
return [
|
|
71308
|
+
"<!doctype html><meta charset=utf-8>",
|
|
71309
|
+
`<title>${title}</title>`,
|
|
71310
|
+
'<body style="font:14px system-ui;padding:3rem;max-width:34rem">',
|
|
71311
|
+
`<h1 style="font-size:1.1rem">${title}</h1>`,
|
|
71312
|
+
`<p>${detail}</p>`,
|
|
71313
|
+
"</body>"
|
|
71314
|
+
].join("");
|
|
71315
|
+
}
|
|
71316
|
+
function startCallbackListener(options = {}) {
|
|
71317
|
+
const state = randomBytes4(32).toString("base64url");
|
|
71318
|
+
const timeoutMs = options.timeoutMs ?? CALLBACK_TIMEOUT_MS;
|
|
71319
|
+
const serve = options.serve ?? Bun.serve;
|
|
71320
|
+
let settle = () => {};
|
|
71321
|
+
const result = new Promise((resolve) => {
|
|
71322
|
+
settle = resolve;
|
|
71323
|
+
});
|
|
71324
|
+
let done = false;
|
|
71325
|
+
let timer;
|
|
71326
|
+
const server = serve({
|
|
71327
|
+
hostname: LOOPBACK_HOST,
|
|
71328
|
+
port: options.port ?? 0,
|
|
71329
|
+
fetch(request) {
|
|
71330
|
+
const url = new URL(request.url);
|
|
71331
|
+
if (done) {
|
|
71332
|
+
return new Response(page("Already handled", "This authorisation was already completed."), {
|
|
71333
|
+
status: 410,
|
|
71334
|
+
headers: { "content-type": "text/html" }
|
|
70743
71335
|
});
|
|
70744
|
-
|
|
70745
|
-
|
|
70746
|
-
|
|
70747
|
-
|
|
70748
|
-
|
|
70749
|
-
|
|
70750
|
-
toolCount: catalog.entries.length,
|
|
70751
|
-
connection
|
|
71336
|
+
}
|
|
71337
|
+
const given = url.searchParams.get("state");
|
|
71338
|
+
if (!matchesState(given, state)) {
|
|
71339
|
+
return new Response(page("Not this flow", "This request did not come from the authorisation keryx started."), {
|
|
71340
|
+
status: 400,
|
|
71341
|
+
headers: { "content-type": "text/html" }
|
|
70752
71342
|
});
|
|
70753
|
-
}
|
|
70754
|
-
|
|
70755
|
-
|
|
70756
|
-
|
|
70757
|
-
|
|
70758
|
-
}
|
|
70759
|
-
|
|
70760
|
-
|
|
70761
|
-
|
|
70762
|
-
|
|
70763
|
-
|
|
71343
|
+
}
|
|
71344
|
+
const error = url.searchParams.get("error");
|
|
71345
|
+
if (error !== null) {
|
|
71346
|
+
const description = url.searchParams.get("error_description");
|
|
71347
|
+
done = true;
|
|
71348
|
+
settle({ ok: false, reason: description === null ? error : `${error}: ${description}` });
|
|
71349
|
+
return new Response(page("Authorisation refused", "You can close this tab and return to keryx."), {
|
|
71350
|
+
status: 200,
|
|
71351
|
+
headers: { "content-type": "text/html" }
|
|
71352
|
+
});
|
|
71353
|
+
}
|
|
71354
|
+
const code = url.searchParams.get("code");
|
|
71355
|
+
if (code === null || code === "") {
|
|
71356
|
+
return new Response(page("No code", "The authorisation server returned no code."), {
|
|
71357
|
+
status: 400,
|
|
71358
|
+
headers: { "content-type": "text/html" }
|
|
70764
71359
|
});
|
|
70765
71360
|
}
|
|
71361
|
+
done = true;
|
|
71362
|
+
settle({ ok: true, code });
|
|
71363
|
+
return new Response(page("Authorised", "keryx has what it needs. You can close this tab."), {
|
|
71364
|
+
status: 200,
|
|
71365
|
+
headers: { "content-type": "text/html" }
|
|
71366
|
+
});
|
|
70766
71367
|
}
|
|
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 {
|
|
71368
|
+
});
|
|
71369
|
+
const close = () => {
|
|
70788
71370
|
if (timer !== undefined)
|
|
70789
71371
|
clearTimeout(timer);
|
|
70790
|
-
|
|
70791
|
-
work.then(onAbandoned).catch(() => {});
|
|
70792
|
-
}
|
|
70793
|
-
}
|
|
70794
|
-
}
|
|
70795
|
-
async function closeServers(states) {
|
|
70796
|
-
await Promise.all(states.map(async (state) => {
|
|
71372
|
+
timer = undefined;
|
|
70797
71373
|
try {
|
|
70798
|
-
|
|
71374
|
+
server.stop(true);
|
|
70799
71375
|
} 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
|
|
71376
|
+
};
|
|
71377
|
+
timer = setTimeout(() => {
|
|
71378
|
+
if (done)
|
|
71379
|
+
return;
|
|
71380
|
+
done = true;
|
|
71381
|
+
settle({
|
|
71382
|
+
ok: false,
|
|
71383
|
+
reason: `no response within ${Math.round(timeoutMs / 1000)}s \u2014 the browser tab was never completed`
|
|
71384
|
+
});
|
|
71385
|
+
}, timeoutMs);
|
|
71386
|
+
timer.unref?.();
|
|
71387
|
+
result.then(() => {
|
|
71388
|
+
const drain = setTimeout(close, 50);
|
|
71389
|
+
drain.unref?.();
|
|
70819
71390
|
});
|
|
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(" + ");
|
|
71391
|
+
return {
|
|
71392
|
+
redirectUrl: `http://${LOOPBACK_HOST}:${server.port}/callback`,
|
|
71393
|
+
boundHost: server.hostname ?? "",
|
|
71394
|
+
state,
|
|
71395
|
+
result,
|
|
71396
|
+
close
|
|
71397
|
+
};
|
|
70900
71398
|
}
|
|
70901
71399
|
|
|
70902
71400
|
// src/mcp-client/wire.ts
|
|
@@ -71015,6 +71513,7 @@ async function connectHttpMcpServer(url, options = {}) {
|
|
|
71015
71513
|
}
|
|
71016
71514
|
const sdk = await loadHttpSdk();
|
|
71017
71515
|
const transport = new sdk.StreamableHTTPClientTransport(parsed, {
|
|
71516
|
+
...options.authProvider === undefined ? {} : { authProvider: options.authProvider },
|
|
71018
71517
|
fetch: (input, init) => fetch(input, { ...init, redirect: "error" }),
|
|
71019
71518
|
requestInit: {
|
|
71020
71519
|
...Object.keys(headers).length > 0 ? { headers } : {},
|
|
@@ -71148,6 +71647,195 @@ async function connectStdioMcpServer(argv, options, handshakeTimeoutMs, signal,
|
|
|
71148
71647
|
};
|
|
71149
71648
|
}
|
|
71150
71649
|
|
|
71650
|
+
// src/mcp-servers/runtime.ts
|
|
71651
|
+
import path167 from "path";
|
|
71652
|
+
|
|
71653
|
+
// src/mcp-servers/manager.ts
|
|
71654
|
+
var DEFAULT_CONNECT_CONCURRENCY = 4;
|
|
71655
|
+
var DEFAULT_STARTUP_TIMEOUT_MS = 15000;
|
|
71656
|
+
async function startServers(servers, connect, options = {}) {
|
|
71657
|
+
const concurrency = Math.max(1, options.concurrency ?? DEFAULT_CONNECT_CONCURRENCY);
|
|
71658
|
+
const states = new Map;
|
|
71659
|
+
const catalogs = [];
|
|
71660
|
+
const enabled = [];
|
|
71661
|
+
for (const server of servers) {
|
|
71662
|
+
if (server.enabled) {
|
|
71663
|
+
enabled.push(server);
|
|
71664
|
+
} else {
|
|
71665
|
+
states.set(server.name, { name: server.name, status: "disabled", toolCount: 0 });
|
|
71666
|
+
}
|
|
71667
|
+
}
|
|
71668
|
+
let next = 0;
|
|
71669
|
+
const worker = async () => {
|
|
71670
|
+
for (;; ) {
|
|
71671
|
+
const index = next++;
|
|
71672
|
+
const server = enabled[index];
|
|
71673
|
+
if (server === undefined)
|
|
71674
|
+
return;
|
|
71675
|
+
const timeoutMs = server.startup_timeout_sec !== undefined ? server.startup_timeout_sec * 1000 : options.defaultStartupTimeoutMs ?? DEFAULT_STARTUP_TIMEOUT_MS;
|
|
71676
|
+
let connection;
|
|
71677
|
+
try {
|
|
71678
|
+
connection = await withTimeout2(connect(server), timeoutMs, server.name, (late) => {
|
|
71679
|
+
late.close().catch(() => {});
|
|
71680
|
+
});
|
|
71681
|
+
const tools = await withTimeout2(connection.listTools(), timeoutMs, server.name);
|
|
71682
|
+
const catalog = catalogForServer(server.name, tools);
|
|
71683
|
+
catalogs.push(catalog);
|
|
71684
|
+
states.set(server.name, {
|
|
71685
|
+
name: server.name,
|
|
71686
|
+
status: "connected",
|
|
71687
|
+
toolCount: catalog.entries.length,
|
|
71688
|
+
connection
|
|
71689
|
+
});
|
|
71690
|
+
} catch (error) {
|
|
71691
|
+
if (connection !== undefined) {
|
|
71692
|
+
try {
|
|
71693
|
+
await connection.close();
|
|
71694
|
+
} catch {}
|
|
71695
|
+
}
|
|
71696
|
+
states.set(server.name, {
|
|
71697
|
+
name: server.name,
|
|
71698
|
+
status: "failed",
|
|
71699
|
+
error: error instanceof Error ? error.message : String(error),
|
|
71700
|
+
toolCount: 0
|
|
71701
|
+
});
|
|
71702
|
+
}
|
|
71703
|
+
}
|
|
71704
|
+
};
|
|
71705
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, Math.max(enabled.length, 1)) }, worker));
|
|
71706
|
+
return {
|
|
71707
|
+
servers: [...states.values()].sort((a, b) => a.name.localeCompare(b.name)),
|
|
71708
|
+
catalog: mergeCatalogs(catalogs)
|
|
71709
|
+
};
|
|
71710
|
+
}
|
|
71711
|
+
async function withTimeout2(work, ms, name, onAbandoned) {
|
|
71712
|
+
let timer;
|
|
71713
|
+
let settled = false;
|
|
71714
|
+
try {
|
|
71715
|
+
return await Promise.race([
|
|
71716
|
+
work,
|
|
71717
|
+
new Promise((_resolve, reject) => {
|
|
71718
|
+
timer = setTimeout(() => reject(new Error(`server "${name}" did not answer within ${ms}ms`)), ms);
|
|
71719
|
+
})
|
|
71720
|
+
]).then((value) => {
|
|
71721
|
+
settled = true;
|
|
71722
|
+
return value;
|
|
71723
|
+
});
|
|
71724
|
+
} finally {
|
|
71725
|
+
if (timer !== undefined)
|
|
71726
|
+
clearTimeout(timer);
|
|
71727
|
+
if (!settled && onAbandoned !== undefined) {
|
|
71728
|
+
work.then(onAbandoned).catch(() => {});
|
|
71729
|
+
}
|
|
71730
|
+
}
|
|
71731
|
+
}
|
|
71732
|
+
async function closeServers(states) {
|
|
71733
|
+
await Promise.all(states.map(async (state) => {
|
|
71734
|
+
try {
|
|
71735
|
+
await state.connection?.close();
|
|
71736
|
+
} catch {}
|
|
71737
|
+
}));
|
|
71738
|
+
}
|
|
71739
|
+
|
|
71740
|
+
// src/mcp-servers/trust.ts
|
|
71741
|
+
init_config_dir();
|
|
71742
|
+
import { createHash as createHash32 } from "crypto";
|
|
71743
|
+
import path166 from "path";
|
|
71744
|
+
function trustFile(configDir) {
|
|
71745
|
+
return path166.join(configDir ?? ensureKeryxConfigDir(), "mcp-servers-trust.json");
|
|
71746
|
+
}
|
|
71747
|
+
function serverFingerprint(server) {
|
|
71748
|
+
const material = JSON.stringify({
|
|
71749
|
+
command: server.command ?? null,
|
|
71750
|
+
args: server.args ?? [],
|
|
71751
|
+
url: server.url ?? null,
|
|
71752
|
+
env: server.env ?? {},
|
|
71753
|
+
cwd: server.cwd ?? null,
|
|
71754
|
+
headers: sortedKeys(server.headers),
|
|
71755
|
+
bearer_token_env_var: server.bearer_token_env_var ?? null
|
|
71756
|
+
});
|
|
71757
|
+
return createHash32("sha256").update(material).digest("hex").slice(0, 32);
|
|
71758
|
+
}
|
|
71759
|
+
function sortedKeys(values) {
|
|
71760
|
+
return Object.entries(values ?? {}).sort(([a], [b]) => a.localeCompare(b));
|
|
71761
|
+
}
|
|
71762
|
+
function trustKey(server) {
|
|
71763
|
+
return `${server.file}::${server.name}`;
|
|
71764
|
+
}
|
|
71765
|
+
function loadTrustStore(configDir) {
|
|
71766
|
+
const read = readConfigFile(trustFile(configDir));
|
|
71767
|
+
if (!read.ok)
|
|
71768
|
+
return {};
|
|
71769
|
+
try {
|
|
71770
|
+
const parsed = parseJsonTolerant(read.text);
|
|
71771
|
+
const approvals = parsed.approvals;
|
|
71772
|
+
if (approvals === undefined)
|
|
71773
|
+
return {};
|
|
71774
|
+
const clean = {};
|
|
71775
|
+
for (const [key, value] of Object.entries(approvals)) {
|
|
71776
|
+
if (typeof value === "string")
|
|
71777
|
+
clean[key] = value;
|
|
71778
|
+
}
|
|
71779
|
+
return clean;
|
|
71780
|
+
} catch {
|
|
71781
|
+
return {};
|
|
71782
|
+
}
|
|
71783
|
+
}
|
|
71784
|
+
function requiresApproval(server, approvals) {
|
|
71785
|
+
if (!server.projectLocal)
|
|
71786
|
+
return false;
|
|
71787
|
+
return approvals[trustKey(server)] !== serverFingerprint(server);
|
|
71788
|
+
}
|
|
71789
|
+
function approveServer(server, configDir) {
|
|
71790
|
+
const file = trustFile(configDir);
|
|
71791
|
+
const read = readConfigFile(file);
|
|
71792
|
+
if (!read.ok && !isDefiniteAbsence(read.reason)) {
|
|
71793
|
+
return { ok: false, error: `${file} could not be read (${read.reason}); nothing was written` };
|
|
71794
|
+
}
|
|
71795
|
+
const approvals = loadTrustStore(configDir);
|
|
71796
|
+
approvals[trustKey(server)] = serverFingerprint(server);
|
|
71797
|
+
writeOwnerOnlyFileAtomic(file, `${JSON.stringify({ approvals }, null, 2)}
|
|
71798
|
+
`);
|
|
71799
|
+
return { ok: true, file };
|
|
71800
|
+
}
|
|
71801
|
+
function revokeServer(server, configDir) {
|
|
71802
|
+
const file = trustFile(configDir);
|
|
71803
|
+
const approvals = loadTrustStore(configDir);
|
|
71804
|
+
delete approvals[trustKey(server)];
|
|
71805
|
+
writeOwnerOnlyFileAtomic(file, `${JSON.stringify({ approvals }, null, 2)}
|
|
71806
|
+
`);
|
|
71807
|
+
return { ok: true, file };
|
|
71808
|
+
}
|
|
71809
|
+
function describeForApproval(server) {
|
|
71810
|
+
const raw = server.raw;
|
|
71811
|
+
if (raw.url !== undefined && raw.url.length > 0) {
|
|
71812
|
+
const shown = displayUrl(raw.url);
|
|
71813
|
+
const credentials = credentialSummary(raw);
|
|
71814
|
+
return credentials === undefined ? shown : `${shown} [sends ${credentials}]`;
|
|
71815
|
+
}
|
|
71816
|
+
return [raw.command, ...raw.args ?? []].filter(Boolean).join(" ");
|
|
71817
|
+
}
|
|
71818
|
+
function credentialSummary(raw) {
|
|
71819
|
+
const named = new Set;
|
|
71820
|
+
if (raw.bearer_token_env_var !== undefined && raw.bearer_token_env_var !== "") {
|
|
71821
|
+
named.add(raw.bearer_token_env_var);
|
|
71822
|
+
}
|
|
71823
|
+
for (const value of Object.values(raw.headers ?? {})) {
|
|
71824
|
+
for (const match of value.matchAll(/\$\{([A-Za-z_][A-Za-z0-9_]*)/g))
|
|
71825
|
+
named.add(match[1]);
|
|
71826
|
+
}
|
|
71827
|
+
const headerNames = Object.keys(raw.headers ?? {});
|
|
71828
|
+
if (named.size === 0 && headerNames.length === 0)
|
|
71829
|
+
return;
|
|
71830
|
+
const parts = [];
|
|
71831
|
+
if (named.size > 0)
|
|
71832
|
+
parts.push([...named].sort().join(", "));
|
|
71833
|
+
const literal = headerNames.filter((n) => !/\$\{/.test(raw.headers?.[n] ?? ""));
|
|
71834
|
+
if (literal.length > 0)
|
|
71835
|
+
parts.push(`literal header(s) ${literal.sort().join(", ")}`);
|
|
71836
|
+
return parts.join(" + ");
|
|
71837
|
+
}
|
|
71838
|
+
|
|
71151
71839
|
// src/harness/external/env.ts
|
|
71152
71840
|
var EXTERNAL_ENV_DENY = [
|
|
71153
71841
|
"ANTHROPIC_API_KEY",
|
|
@@ -71376,7 +72064,7 @@ function createMcpRuntime(options) {
|
|
|
71376
72064
|
];
|
|
71377
72065
|
const kills = [];
|
|
71378
72066
|
const env = options.env ?? process.env;
|
|
71379
|
-
const connect = options.connect ?? ((server) => defaultConnect(server, env, dialling.signal, kills));
|
|
72067
|
+
const connect = options.connect ?? ((server) => defaultConnect(server, env, dialling.signal, kills, options.configDir));
|
|
71380
72068
|
const settled = startServers(launchable, connect).then((result) => {
|
|
71381
72069
|
catalog = result.catalog;
|
|
71382
72070
|
states = [...heldStates, ...result.servers].sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -71421,7 +72109,25 @@ function handshakeBudgetMs(server) {
|
|
|
71421
72109
|
}
|
|
71422
72110
|
var MAX_TIMEOUT_MS = 24 * 60 * 60 * 1000;
|
|
71423
72111
|
var DEFAULT_HANDSHAKE_MS = 15000;
|
|
71424
|
-
|
|
72112
|
+
var SESSION_REDIRECT_URL = "http://127.0.0.1/keryx-session-never-listens";
|
|
72113
|
+
function sessionAuthProviderOptions(server, runtimeConfigDir, stored, now = Date.now()) {
|
|
72114
|
+
if (!usesOAuth(server.raw))
|
|
72115
|
+
return;
|
|
72116
|
+
const tokens = stored?.tokens;
|
|
72117
|
+
if (tokens === undefined)
|
|
72118
|
+
return;
|
|
72119
|
+
if (isExpired(tokens, now) && tokens.refresh_token === undefined)
|
|
72120
|
+
return;
|
|
72121
|
+
return {
|
|
72122
|
+
serverName: server.name,
|
|
72123
|
+
serverUrl: server.url,
|
|
72124
|
+
...runtimeConfigDir === undefined ? {} : { configDir: runtimeConfigDir },
|
|
72125
|
+
redirectUrl: SESSION_REDIRECT_URL,
|
|
72126
|
+
interactive: false,
|
|
72127
|
+
...server.oauth === false || server.oauth?.clientId === undefined ? {} : { clientId: server.oauth.clientId }
|
|
72128
|
+
};
|
|
72129
|
+
}
|
|
72130
|
+
async function connectRemote(server, env, signal, runtimeConfigDir) {
|
|
71425
72131
|
const target = remoteTargetProblem(server.raw, env);
|
|
71426
72132
|
if (target !== undefined) {
|
|
71427
72133
|
throw new Error(`server "${server.name}": ${target}`);
|
|
@@ -71430,8 +72136,11 @@ async function connectRemote(server, env, signal) {
|
|
|
71430
72136
|
if (!resolved.ok) {
|
|
71431
72137
|
throw new Error(`server "${server.name}": ${describeHollow(resolved.hollow)}`);
|
|
71432
72138
|
}
|
|
72139
|
+
const options = sessionAuthProviderOptions(server, runtimeConfigDir, server.url === undefined ? undefined : readCredential(server.name, server.url, runtimeConfigDir).record);
|
|
72140
|
+
const authProvider = options === undefined ? undefined : createOAuthProvider(options);
|
|
71433
72141
|
return connectHttpMcpServer(server.url, {
|
|
71434
72142
|
headers: resolved.headers,
|
|
72143
|
+
...authProvider === undefined ? {} : { authProvider },
|
|
71435
72144
|
handshakeTimeoutMs: handshakeBudgetMs(server),
|
|
71436
72145
|
...signal === undefined ? {} : { signal }
|
|
71437
72146
|
});
|
|
@@ -71441,9 +72150,9 @@ function timeoutFor(server, rawName) {
|
|
|
71441
72150
|
return;
|
|
71442
72151
|
return server.tool_timeouts?.[rawName] ?? server.tool_timeout_sec;
|
|
71443
72152
|
}
|
|
71444
|
-
async function defaultConnect(server, env, signal, kills) {
|
|
72153
|
+
async function defaultConnect(server, env, signal, kills, configDir) {
|
|
71445
72154
|
if (transportOf(server) === "http")
|
|
71446
|
-
return connectRemote(server, env, signal);
|
|
72155
|
+
return connectRemote(server, env, signal, configDir);
|
|
71447
72156
|
const command = server.command;
|
|
71448
72157
|
if (command === undefined || command === "") {
|
|
71449
72158
|
throw new Error(`server "${server.name}" sets neither command nor url`);
|
|
@@ -71456,22 +72165,22 @@ async function defaultConnect(server, env, signal, kills) {
|
|
|
71456
72165
|
function defaultServerCwd(server, fallbackCwd = process.cwd()) {
|
|
71457
72166
|
if (server.source !== "project")
|
|
71458
72167
|
return fallbackCwd;
|
|
71459
|
-
return
|
|
72168
|
+
return path167.dirname(path167.dirname(server.file));
|
|
71460
72169
|
}
|
|
71461
72170
|
|
|
71462
72171
|
// src/mcp-servers/store.ts
|
|
71463
72172
|
init_config_dir();
|
|
71464
72173
|
import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
|
|
71465
|
-
import
|
|
72174
|
+
import path168 from "path";
|
|
71466
72175
|
var SERVER_NAME_PATTERN = /^[A-Za-z0-9_-]+$/;
|
|
71467
72176
|
function userConfigFile(configDir) {
|
|
71468
|
-
return
|
|
72177
|
+
return path168.join(configDir ?? ensureKeryxConfigDir(), "mcp-servers.json");
|
|
71469
72178
|
}
|
|
71470
72179
|
function projectConfigFile(projectRoot) {
|
|
71471
|
-
return
|
|
72180
|
+
return path168.join(projectRoot, ".keryx", "mcp-servers.json");
|
|
71472
72181
|
}
|
|
71473
72182
|
function overlayFile(configDir) {
|
|
71474
|
-
return
|
|
72183
|
+
return path168.join(configDir ?? ensureKeryxConfigDir(), "mcp-servers-disabled.json");
|
|
71475
72184
|
}
|
|
71476
72185
|
function readForWrite(file) {
|
|
71477
72186
|
const read = readConfigFile(file);
|
|
@@ -71510,7 +72219,7 @@ function writeDocument(file, doc, scope) {
|
|
|
71510
72219
|
writeOwnerOnlyFileAtomic(file, body);
|
|
71511
72220
|
return;
|
|
71512
72221
|
}
|
|
71513
|
-
mkdirSync6(
|
|
72222
|
+
mkdirSync6(path168.dirname(file), { recursive: true });
|
|
71514
72223
|
writeFileSync7(file, body, { encoding: "utf8" });
|
|
71515
72224
|
}
|
|
71516
72225
|
function addServer(options) {
|
|
@@ -71614,7 +72323,9 @@ var MCP_CONSUMER_SUBCOMMANDS = [
|
|
|
71614
72323
|
"disable",
|
|
71615
72324
|
"trust",
|
|
71616
72325
|
"untrust",
|
|
71617
|
-
"doctor"
|
|
72326
|
+
"doctor",
|
|
72327
|
+
"auth",
|
|
72328
|
+
"logout"
|
|
71618
72329
|
];
|
|
71619
72330
|
function isMcpConsumerSubcommand(value) {
|
|
71620
72331
|
return MCP_CONSUMER_SUBCOMMANDS.includes(value ?? "");
|
|
@@ -71637,6 +72348,10 @@ async function runMcpConsumerCommand(subcommand, args, deps) {
|
|
|
71637
72348
|
return trustCommand(args, deps, false);
|
|
71638
72349
|
case "doctor":
|
|
71639
72350
|
return doctorCommand(args, deps);
|
|
72351
|
+
case "auth":
|
|
72352
|
+
return authCommand(args, deps);
|
|
72353
|
+
case "logout":
|
|
72354
|
+
return logoutCommand(args, deps);
|
|
71640
72355
|
}
|
|
71641
72356
|
}
|
|
71642
72357
|
function projectRootOf(deps) {
|
|
@@ -71778,6 +72493,178 @@ function addCommand(argv, deps) {
|
|
|
71778
72493
|
deps.log(`Run \`keryx mcp doctor ${name}\` to check it connects.`);
|
|
71779
72494
|
return 0;
|
|
71780
72495
|
}
|
|
72496
|
+
function logoutCommand(args, deps) {
|
|
72497
|
+
const own = splitAtSeparator(args).own;
|
|
72498
|
+
const name = own[0];
|
|
72499
|
+
if (name === undefined || name.startsWith("-")) {
|
|
72500
|
+
deps.err("usage: keryx mcp logout <name>");
|
|
72501
|
+
return 1;
|
|
72502
|
+
}
|
|
72503
|
+
const config = load(deps);
|
|
72504
|
+
const server = config.servers.find((candidate) => candidate.name === name);
|
|
72505
|
+
const url = server?.url;
|
|
72506
|
+
if (url === undefined) {
|
|
72507
|
+
const removed = forgetByName(name, deps.configDir);
|
|
72508
|
+
if (removed.count === 0) {
|
|
72509
|
+
deps.err(`no stored credential for "${name}".`);
|
|
72510
|
+
return 1;
|
|
72511
|
+
}
|
|
72512
|
+
deps.log(`Forgot ${removed.count} stored credential(s) for "${name}".`);
|
|
72513
|
+
return 0;
|
|
72514
|
+
}
|
|
72515
|
+
const existing = readCredential(name, url, deps.configDir);
|
|
72516
|
+
if (existing.problem !== undefined) {
|
|
72517
|
+
deps.err(existing.problem);
|
|
72518
|
+
return 1;
|
|
72519
|
+
}
|
|
72520
|
+
if (existing.record === undefined) {
|
|
72521
|
+
deps.log(`"${name}" has no stored credential; nothing to forget.`);
|
|
72522
|
+
return 0;
|
|
72523
|
+
}
|
|
72524
|
+
const cleared = clearCredential(name, url, deps.configDir);
|
|
72525
|
+
if (!cleared.ok) {
|
|
72526
|
+
deps.err(cleared.error);
|
|
72527
|
+
return 1;
|
|
72528
|
+
}
|
|
72529
|
+
deps.log(`Forgot the stored credential for "${name}".`);
|
|
72530
|
+
deps.log(`Run \`keryx mcp auth ${name}\` to authorise again.`);
|
|
72531
|
+
return 0;
|
|
72532
|
+
}
|
|
72533
|
+
async function authCommand(args, deps) {
|
|
72534
|
+
const own = splitAtSeparator(args).own;
|
|
72535
|
+
const unknown = own.filter((arg, index, all) => isUnknownFlag(arg, index, all));
|
|
72536
|
+
if (unknown.length > 0) {
|
|
72537
|
+
deps.err(`unknown option ${unknown[0]}`);
|
|
72538
|
+
return 1;
|
|
72539
|
+
}
|
|
72540
|
+
const name = positional3(args);
|
|
72541
|
+
if (name === undefined) {
|
|
72542
|
+
deps.err("usage: keryx mcp auth <name>");
|
|
72543
|
+
return 1;
|
|
72544
|
+
}
|
|
72545
|
+
const config = load(deps);
|
|
72546
|
+
const server = config.servers.find((candidate) => candidate.name === name);
|
|
72547
|
+
if (server === undefined) {
|
|
72548
|
+
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(", ")}`);
|
|
72549
|
+
return 1;
|
|
72550
|
+
}
|
|
72551
|
+
if (transportOf(server) !== "http") {
|
|
72552
|
+
deps.err(`"${name}" is a local (stdio) server; OAuth applies to remote ones.`);
|
|
72553
|
+
return 1;
|
|
72554
|
+
}
|
|
72555
|
+
if (requiresApproval(server, loadTrustStore(deps.configDir))) {
|
|
72556
|
+
deps.err(`"${name}" is a project server that has not been approved.`);
|
|
72557
|
+
deps.err(`Read what it connects to, then: keryx mcp trust ${name}`);
|
|
72558
|
+
return 1;
|
|
72559
|
+
}
|
|
72560
|
+
const env = deps.env ?? process.env;
|
|
72561
|
+
const targetProblem = remoteTargetProblem(server.raw, env);
|
|
72562
|
+
if (targetProblem !== undefined) {
|
|
72563
|
+
deps.err(`"${name}": ${targetProblem}`);
|
|
72564
|
+
return 1;
|
|
72565
|
+
}
|
|
72566
|
+
if (!usesOAuth(server.raw)) {
|
|
72567
|
+
deps.err(`"${name}" does not use OAuth \u2014 it authenticates with a header or bearer_token_env_var.`);
|
|
72568
|
+
deps.err("Nothing to do. Remove that credential first if you meant to switch to OAuth.");
|
|
72569
|
+
return 1;
|
|
72570
|
+
}
|
|
72571
|
+
const interactive = resolveInteractive(deps.interactive, process.stdin, process.stdout);
|
|
72572
|
+
if (!interactive) {
|
|
72573
|
+
deps.err(`"${name}" needs OAuth, and this process has no terminal to ask in.`);
|
|
72574
|
+
deps.err("Run `keryx mcp auth " + name + "` from an interactive shell.");
|
|
72575
|
+
return 1;
|
|
72576
|
+
}
|
|
72577
|
+
deps.log(`Authorising "${name}" at ${displayUrl(server.raw.url)}\u2026`);
|
|
72578
|
+
deps.log("A browser window will open. keryx never sees your password \u2014 only the token the server issues.");
|
|
72579
|
+
return await runOAuthFlow(server, deps);
|
|
72580
|
+
}
|
|
72581
|
+
async function runOAuthFlow(server, deps) {
|
|
72582
|
+
const configuredPort = server.oauth === false ? undefined : server.oauth?.callbackPort;
|
|
72583
|
+
const listener = startCallbackListener(configuredPort === undefined ? {} : { port: configuredPort });
|
|
72584
|
+
try {
|
|
72585
|
+
const provider = createOAuthProvider({
|
|
72586
|
+
serverName: server.name,
|
|
72587
|
+
serverUrl: server.url,
|
|
72588
|
+
...deps.configDir === undefined ? {} : { configDir: deps.configDir },
|
|
72589
|
+
interactive: true,
|
|
72590
|
+
openBrowser: deps.openBrowser ?? ((url) => {
|
|
72591
|
+
openAuthorisationUrl(url, deps.log);
|
|
72592
|
+
}),
|
|
72593
|
+
redirectUrl: listener.redirectUrl,
|
|
72594
|
+
state: listener.state,
|
|
72595
|
+
...server.oauth === false || server.oauth?.clientId === undefined ? {} : { clientId: server.oauth.clientId },
|
|
72596
|
+
...server.oauth === false || server.oauth?.scopes === undefined ? {} : { scopes: server.oauth.scopes }
|
|
72597
|
+
});
|
|
72598
|
+
let opened = false;
|
|
72599
|
+
const legOne = await connectHttpMcpServer(server.url, {
|
|
72600
|
+
authProvider: {
|
|
72601
|
+
...provider,
|
|
72602
|
+
redirectToAuthorization: async (url) => {
|
|
72603
|
+
opened = true;
|
|
72604
|
+
await provider.redirectToAuthorization(url);
|
|
72605
|
+
}
|
|
72606
|
+
},
|
|
72607
|
+
handshakeTimeoutMs: 30000
|
|
72608
|
+
}).then((connection) => ({ ok: true, connection }), (error) => ({ ok: false, error }));
|
|
72609
|
+
if (legOne.ok) {
|
|
72610
|
+
await legOne.connection.close();
|
|
72611
|
+
const existing = readCredential(server.name, server.url, deps.configDir).record;
|
|
72612
|
+
if (existing?.tokens === undefined) {
|
|
72613
|
+
deps.log(`"${server.name}" connects without authentication; there is nothing to authorise.`);
|
|
72614
|
+
} else {
|
|
72615
|
+
deps.log(`"${server.name}" is already authorised; nothing to do.`);
|
|
72616
|
+
}
|
|
72617
|
+
return 0;
|
|
72618
|
+
}
|
|
72619
|
+
if (!opened) {
|
|
72620
|
+
const message = legOne.error instanceof Error ? legOne.error.message : String(legOne.error);
|
|
72621
|
+
deps.err(`Could not start authorisation for "${server.name}": ${sanitiseForDisplay(message)}`);
|
|
72622
|
+
return 1;
|
|
72623
|
+
}
|
|
72624
|
+
const result = await listener.result;
|
|
72625
|
+
if (!result.ok) {
|
|
72626
|
+
deps.err(`Authorisation did not complete: ${result.reason}`);
|
|
72627
|
+
return 1;
|
|
72628
|
+
}
|
|
72629
|
+
const sdk = await loadAuthSdk();
|
|
72630
|
+
await sdk.auth(provider, { serverUrl: server.url, authorizationCode: result.code });
|
|
72631
|
+
const stored = readCredential(server.name, server.url, deps.configDir).record;
|
|
72632
|
+
if (stored?.tokens === undefined || stored.tokens.access_token === "") {
|
|
72633
|
+
deps.err("The exchange returned no token. Nothing was stored.");
|
|
72634
|
+
return 1;
|
|
72635
|
+
}
|
|
72636
|
+
deps.log(`Authorised "${server.name}" \u2014 ${describeCredential(stored, Date.now())}.`);
|
|
72637
|
+
deps.log(`Stored owner-only in ${credentialsFile(deps.configDir)}.`);
|
|
72638
|
+
return 0;
|
|
72639
|
+
} catch (error) {
|
|
72640
|
+
deps.err(`Authorisation failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
72641
|
+
return 1;
|
|
72642
|
+
} finally {
|
|
72643
|
+
listener.close();
|
|
72644
|
+
}
|
|
72645
|
+
}
|
|
72646
|
+
async function loadAuthSdk() {
|
|
72647
|
+
const mod = await import("@modelcontextprotocol/sdk/client/auth.js");
|
|
72648
|
+
return mod;
|
|
72649
|
+
}
|
|
72650
|
+
function resolveInteractive(explicit, stdin, stdout) {
|
|
72651
|
+
return explicit ?? bothStreamsAreATerminal(stdin, stdout);
|
|
72652
|
+
}
|
|
72653
|
+
function bothStreamsAreATerminal(stdin, stdout) {
|
|
72654
|
+
return stdin.isTTY === true && stdout.isTTY === true;
|
|
72655
|
+
}
|
|
72656
|
+
function openAuthorisationUrl(url, log, platform = process.platform, env = process.env, open = openVerificationUrl) {
|
|
72657
|
+
const plan = browserOpenPlan(url.toString(), platform, env);
|
|
72658
|
+
if (plan === undefined) {
|
|
72659
|
+
log("No graphical session detected, so nothing was opened.");
|
|
72660
|
+
log(`Open this URL yourself to continue:
|
|
72661
|
+
|
|
72662
|
+
${url.toString()}
|
|
72663
|
+
`);
|
|
72664
|
+
return;
|
|
72665
|
+
}
|
|
72666
|
+
open(url.toString(), { platform, env });
|
|
72667
|
+
}
|
|
71781
72668
|
function removeCommand(args, deps) {
|
|
71782
72669
|
const own = splitAtSeparator(args).own;
|
|
71783
72670
|
const unknown = own.filter((arg, index, all) => isUnknownFlag(arg, index, all));
|
|
@@ -71928,9 +72815,10 @@ async function doctorCommand(args, deps) {
|
|
|
71928
72815
|
const env = deps.env ?? process.env;
|
|
71929
72816
|
report = await runDoctor2(config, {
|
|
71930
72817
|
only,
|
|
71931
|
-
connect: deps.connect ?? ((server) => defaultConnect(server, env, dialling.signal, kills)),
|
|
72818
|
+
connect: deps.connect ?? ((server) => defaultConnect(server, env, dialling.signal, kills, deps.configDir)),
|
|
71932
72819
|
heldForApproval: (server) => requiresApproval(server, approvals),
|
|
71933
|
-
env
|
|
72820
|
+
env,
|
|
72821
|
+
configDir: deps.configDir
|
|
71934
72822
|
});
|
|
71935
72823
|
} finally {
|
|
71936
72824
|
process.removeListener("SIGINT", onInt);
|
|
@@ -72037,12 +72925,12 @@ function positional3(args) {
|
|
|
72037
72925
|
|
|
72038
72926
|
// src/commands/serve-mcp.ts
|
|
72039
72927
|
init_args();
|
|
72040
|
-
import
|
|
72928
|
+
import path175 from "path";
|
|
72041
72929
|
|
|
72042
72930
|
// src/mcp/discovery.ts
|
|
72043
72931
|
init_fs();
|
|
72044
72932
|
init_json();
|
|
72045
|
-
import
|
|
72933
|
+
import path169 from "path";
|
|
72046
72934
|
var MODULE_MANIFEST_KEY = {
|
|
72047
72935
|
gdgraph: "gdgraph",
|
|
72048
72936
|
security: "security",
|
|
@@ -72104,7 +72992,7 @@ function buildDiscovery(manifest) {
|
|
|
72104
72992
|
};
|
|
72105
72993
|
}
|
|
72106
72994
|
async function loadDiscovery(cwd) {
|
|
72107
|
-
const manifestPath =
|
|
72995
|
+
const manifestPath = path169.join(cwd, ".metaproject", "metaproject.json");
|
|
72108
72996
|
if (!await pathExists(manifestPath)) {
|
|
72109
72997
|
return buildDiscovery({});
|
|
72110
72998
|
}
|
|
@@ -72175,17 +73063,17 @@ init_sac();
|
|
|
72175
73063
|
init_workspace_service();
|
|
72176
73064
|
init_store2();
|
|
72177
73065
|
init_fs();
|
|
72178
|
-
import { createHash as
|
|
73066
|
+
import { createHash as createHash35, randomUUID as randomUUID17 } from "crypto";
|
|
72179
73067
|
import { appendFile as appendFile7, mkdir as mkdir58, open as open2, readFile as readFile93, rename as rename7, rm as rm12, stat as stat16, writeFile as writeFile50 } from "fs/promises";
|
|
72180
|
-
import
|
|
73068
|
+
import path170 from "path";
|
|
72181
73069
|
|
|
72182
73070
|
// src/sac/policy-experiment.ts
|
|
72183
|
-
import { createHash as
|
|
73071
|
+
import { createHash as createHash34, createHmac as createHmac2 } from "crypto";
|
|
72184
73072
|
|
|
72185
73073
|
// src/sac/receipt-integrity.ts
|
|
72186
73074
|
init_sac();
|
|
72187
|
-
import { createHash as
|
|
72188
|
-
var sha2563 = (value) =>
|
|
73075
|
+
import { createHash as createHash33 } from "crypto";
|
|
73076
|
+
var sha2563 = (value) => createHash33("sha256").update(value, "utf8").digest("hex");
|
|
72189
73077
|
function bodyOf(receipt) {
|
|
72190
73078
|
const { integrity: _integrity, ...body } = receipt;
|
|
72191
73079
|
return body;
|
|
@@ -72242,7 +73130,7 @@ function stableValue(value) {
|
|
|
72242
73130
|
return value;
|
|
72243
73131
|
}
|
|
72244
73132
|
var stableJson2 = (value) => JSON.stringify(stableValue(value));
|
|
72245
|
-
var sha2564 = (value) =>
|
|
73133
|
+
var sha2564 = (value) => createHash34("sha256").update(stableJson2(value), "utf8").digest("hex");
|
|
72246
73134
|
var workspacePathPattern2 = /^\.\/(?!.*(?:^|\/)\.\.(?:\/|$))(?:[A-Za-z0-9._-]+\/)*[A-Za-z0-9._-]+$/;
|
|
72247
73135
|
var trustedOutcomes = new WeakMap;
|
|
72248
73136
|
var trustedSandboxEvidence = new WeakMap;
|
|
@@ -72262,7 +73150,7 @@ function policyCorpusDigest(manifest, rows, quarantine) {
|
|
|
72262
73150
|
function splitFor(receiptHash, outcome, split) {
|
|
72263
73151
|
if (outcome.caseClass === "adversarial")
|
|
72264
73152
|
return "adversarial";
|
|
72265
|
-
const byte = Number.parseInt(
|
|
73153
|
+
const byte = Number.parseInt(createHash34("sha256").update(`${split.seed}\x00${receiptHash}`).digest("hex").slice(0, 2), 16);
|
|
72266
73154
|
return byte / 256 * 100 < split.holdoutPercent ? "holdout" : "train";
|
|
72267
73155
|
}
|
|
72268
73156
|
function quarantineCounts(entries) {
|
|
@@ -72346,12 +73234,12 @@ var stableValue2 = (value) => {
|
|
|
72346
73234
|
return value;
|
|
72347
73235
|
};
|
|
72348
73236
|
var stableJson3 = (value) => JSON.stringify(stableValue2(value));
|
|
72349
|
-
var sha2565 = (value) =>
|
|
73237
|
+
var sha2565 = (value) => createHash35("sha256").update(stableJson3(value), "utf8").digest("hex");
|
|
72350
73238
|
var isImmutableVersion2 = (value) => typeof value === "string" && value.length >= 3 && value.length <= 256 && /^[A-Za-z0-9][A-Za-z0-9._:+-]*$/.test(value) && /\d/.test(value) && !immutableVersionPattern.test(value);
|
|
72351
73239
|
var receiptHashPattern2 = /^[a-f0-9]{64}$/;
|
|
72352
73240
|
var ledgerDigestChunkBytes = 64 * 1024;
|
|
72353
73241
|
var isMissing = (error) => error instanceof Error && error.code === "ENOENT";
|
|
72354
|
-
var checkpointHash = (body) =>
|
|
73242
|
+
var checkpointHash = (body) => createHash35("sha256").update(JSON.stringify(body), "utf8").digest("hex");
|
|
72355
73243
|
async function ledgerByteLength(ledger) {
|
|
72356
73244
|
const value = await stat16(ledger, { bigint: true });
|
|
72357
73245
|
if (value.size > BigInt(Number.MAX_SAFE_INTEGER))
|
|
@@ -72359,7 +73247,7 @@ async function ledgerByteLength(ledger) {
|
|
|
72359
73247
|
return Number(value.size);
|
|
72360
73248
|
}
|
|
72361
73249
|
async function digestLedgerPrefix(handle, byteLength) {
|
|
72362
|
-
const digest =
|
|
73250
|
+
const digest = createHash35("sha256");
|
|
72363
73251
|
if (byteLength === 0)
|
|
72364
73252
|
return digest;
|
|
72365
73253
|
const buffer = Buffer.alloc(Math.min(ledgerDigestChunkBytes, byteLength));
|
|
@@ -72483,7 +73371,7 @@ async function auditLedger(ledger, checkpointPath, verifier) {
|
|
|
72483
73371
|
const verification = await verifier(receipts);
|
|
72484
73372
|
if (!verification.ok)
|
|
72485
73373
|
throw new Error(`invalid access receipt ledger: ${verification.reason} at record ${verification.firstInvalidIndex}`);
|
|
72486
|
-
const digest =
|
|
73374
|
+
const digest = createHash35("sha256").update(bytes);
|
|
72487
73375
|
const tailOffset = receipts.length === 0 ? 0 : bytes.lastIndexOf(10, bytes.length - 2) + 1;
|
|
72488
73376
|
const state = { ledgerBytes: bytes.length, recordCount: receipts.length, headHash: verification.headHash, tailOffset, digest };
|
|
72489
73377
|
await writeCheckpoint(checkpointPath, { schemaVersion: "1.1", ledgerBytes: state.ledgerBytes, recordCount: state.recordCount, headHash: state.headHash, tailOffset, contentDigest: digest.copy().digest("hex") });
|
|
@@ -72499,7 +73387,7 @@ async function resolveLedgerState(ledger, checkpointPath, verifier) {
|
|
|
72499
73387
|
throw error;
|
|
72500
73388
|
if (document.present)
|
|
72501
73389
|
throw new Error("invalid access receipt ledger: orphaned-checkpoint");
|
|
72502
|
-
return { ledgerBytes: 0, recordCount: 0, headHash: "GENESIS", tailOffset: 0, digest:
|
|
73390
|
+
return { ledgerBytes: 0, recordCount: 0, headHash: "GENESIS", tailOffset: 0, digest: createHash35("sha256") };
|
|
72503
73391
|
}
|
|
72504
73392
|
if (document.checkpoint) {
|
|
72505
73393
|
const fast = await fastCheckpointState(ledger, document.checkpoint, ledgerBytes);
|
|
@@ -72518,7 +73406,7 @@ var isConfigRecord = (value) => typeof value.enabled === "boolean" && typeof val
|
|
|
72518
73406
|
async function readPinnedJson(input) {
|
|
72519
73407
|
const ref = await resolveWorkspaceReference({ workspaceRoot: input.workspaceRoot, kind: "artifact", uri: input.uri });
|
|
72520
73408
|
const raw = await readFile93(ref, "utf8");
|
|
72521
|
-
const digest =
|
|
73409
|
+
const digest = createHash35("sha256").update(raw).digest("hex");
|
|
72522
73410
|
if (digest !== input.digest)
|
|
72523
73411
|
throw new Error("policy experiment pinned artifact digest mismatch");
|
|
72524
73412
|
return { artifact: JSON.parse(raw), digest };
|
|
@@ -72533,7 +73421,7 @@ function hasReportIntegrity(report) {
|
|
|
72533
73421
|
return reportHashPattern.test(reportDigest) && sha2565(body) === reportDigest;
|
|
72534
73422
|
}
|
|
72535
73423
|
async function resolvePolicySelection(workspaceRoot, canonicalFallback) {
|
|
72536
|
-
const configPath =
|
|
73424
|
+
const configPath = path170.join(workspaceRoot, ...policyExperimentConfigPath);
|
|
72537
73425
|
let configJson;
|
|
72538
73426
|
try {
|
|
72539
73427
|
const raw = await readFile93(configPath, "utf8").catch((error) => {
|
|
@@ -72635,7 +73523,7 @@ async function diagnosePolicyReadiness(workspaceRoot) {
|
|
|
72635
73523
|
const integrityReady = steps.every((entry) => entry.step === "activation-flags" || entry.status === "pass");
|
|
72636
73524
|
return Object.freeze({ configPresent, enabled, killSwitch, integrityReady, candidateWouldActivate: integrityReady && enabled && !killSwitch, steps: Object.freeze([...steps]) });
|
|
72637
73525
|
};
|
|
72638
|
-
const configPath =
|
|
73526
|
+
const configPath = path170.join(workspaceRoot, ...policyExperimentConfigPath);
|
|
72639
73527
|
let configJson;
|
|
72640
73528
|
try {
|
|
72641
73529
|
const raw = await readFile93(configPath, "utf8").catch((error) => {
|
|
@@ -72890,9 +73778,9 @@ class FwkReadService {
|
|
|
72890
73778
|
const recordedAt = nowIso2(now);
|
|
72891
73779
|
const id = `receipt-${randomUUID17().replace(/-/g, "").slice(0, 16)}`;
|
|
72892
73780
|
const base = { schemaVersion: "1.0", id, workspaceId, actor, action, decision, recordedAt, cost: { tokens: 0, toolCalls: 1, elapsedMs: 0 }, contextAssembly: { traceRef: assembly.traceRef, configurationRevision: assembly.configurationRevision, selected: assembly.selected.map((id) => `./ids/${id}`), omittedOptional: assembly.omittedOptional.map((id) => `./ids/${id}`) }, policy: { ref: assembly.policyRef, revision: assembly.policyRevision }, ...action === "resource" ? { resourceRef: `./ids/${resourceId ?? "unknown"}` } : {} };
|
|
72893
|
-
const ledger =
|
|
72894
|
-
const checkpointPath =
|
|
72895
|
-
await mkdir58(
|
|
73781
|
+
const ledger = path170.join(this.options.canonical.workspaceRoot, ".metaproject", "context-operations", "access-receipts.jsonl");
|
|
73782
|
+
const checkpointPath = path170.join(path170.dirname(ledger), "access-receipts.checkpoint.json");
|
|
73783
|
+
await mkdir58(path170.dirname(ledger), { recursive: true, mode: 448 });
|
|
72896
73784
|
return withFileLock(`${ledger}.lock`, async () => {
|
|
72897
73785
|
const state = await resolveLedgerState(ledger, checkpointPath, this.options.verifyReceiptLedger ?? verifyAccessReceiptLedger);
|
|
72898
73786
|
const receipt = sealAccessReceipt(base, state.headHash);
|
|
@@ -72948,7 +73836,7 @@ function createLocalFwkReadService(cwd, opts) {
|
|
|
72948
73836
|
const facts = await Promise.all(manifest.resources.filter((resource) => resource.kind === "evidence").map(async (resource, index) => {
|
|
72949
73837
|
try {
|
|
72950
73838
|
const raw = await workspaces.readResourceForActor({ actorContext, workspaceId, resource });
|
|
72951
|
-
const revision =
|
|
73839
|
+
const revision = createHash35("sha256").update(raw).digest("hex");
|
|
72952
73840
|
return { id: `fact-${index}`, uri: resource.uri, revision: resource.revision ?? revision, observedAt: manifest.updatedAt, expiresAt: "9999-12-31T23:59:59Z", trust: "primary", visible: true, statement: `Evidence reference ${resource.uri}`, status: resource.revision === revision || resource.revision === undefined ? "fresh" : "stale" };
|
|
72953
73841
|
} catch (error) {
|
|
72954
73842
|
withhold(error);
|
|
@@ -72958,7 +73846,7 @@ function createLocalFwkReadService(cwd, opts) {
|
|
|
72958
73846
|
const knowHow = await Promise.all(manifest.resources.filter((resource) => resource.kind === "wiki" || resource.kind === "memory" || resource.kind === "skill").map(async (resource, index) => {
|
|
72959
73847
|
try {
|
|
72960
73848
|
const raw = await workspaces.readResourceForActor({ actorContext, workspaceId, resource, encoding: "utf8" });
|
|
72961
|
-
const revision =
|
|
73849
|
+
const revision = createHash35("sha256").update(raw).digest("hex");
|
|
72962
73850
|
const accepted = /^Status:\s*(accepted|reviewed)\s*$/mi.test(raw);
|
|
72963
73851
|
return { id: `knowhow-${index}`, kind: resource.kind, uri: resource.uri, revision: resource.revision ?? revision, trust: "accepted", status: resource.revision === revision || resource.revision === undefined ? "fresh" : "stale", accepted, visible: true };
|
|
72964
73852
|
} catch (error) {
|
|
@@ -73004,7 +73892,7 @@ init_workspace_service();
|
|
|
73004
73892
|
init_sac();
|
|
73005
73893
|
import { appendFile as appendFile8, mkdir as mkdir59, readFile as readFile94 } from "fs/promises";
|
|
73006
73894
|
import { randomUUID as randomUUID18 } from "crypto";
|
|
73007
|
-
import
|
|
73895
|
+
import path171 from "path";
|
|
73008
73896
|
|
|
73009
73897
|
class CollaborationServiceError extends Error {
|
|
73010
73898
|
code;
|
|
@@ -73020,7 +73908,7 @@ class CollaborationService {
|
|
|
73020
73908
|
this.input = input;
|
|
73021
73909
|
}
|
|
73022
73910
|
file(id) {
|
|
73023
|
-
return
|
|
73911
|
+
return path171.join(this.input.workspaceRoot, ".metaproject", "workspaces", id, "activity.jsonl");
|
|
73024
73912
|
}
|
|
73025
73913
|
async overview(input) {
|
|
73026
73914
|
const manifest = await this.input.workspaces.show(input);
|
|
@@ -73057,7 +73945,7 @@ class CollaborationService {
|
|
|
73057
73945
|
occurredAt: (this.input.now ?? (() => new Date))().toISOString(),
|
|
73058
73946
|
...suppliedHandoff ? { handoff: { ...suppliedHandoff, from: actor.subject } } : {}
|
|
73059
73947
|
});
|
|
73060
|
-
await mkdir59(
|
|
73948
|
+
await mkdir59(path171.dirname(this.file(manifest.id)), { recursive: true, mode: 448 });
|
|
73061
73949
|
await appendFile8(this.file(manifest.id), `${JSON.stringify(activity)}
|
|
73062
73950
|
`, { mode: 384 });
|
|
73063
73951
|
return normalizeCollaborationResult(activity);
|
|
@@ -73112,9 +74000,9 @@ init_workspace_service();
|
|
|
73112
74000
|
init_fs();
|
|
73113
74001
|
init_machine_wrap_up();
|
|
73114
74002
|
import { mkdir as mkdir60, readdir as readdir31, readFile as readFile95 } from "fs/promises";
|
|
73115
|
-
import
|
|
74003
|
+
import path172 from "path";
|
|
73116
74004
|
function externalSlatesDir(cwd) {
|
|
73117
|
-
return
|
|
74005
|
+
return path172.join(cwd, ".keryx", "external-slates");
|
|
73118
74006
|
}
|
|
73119
74007
|
var EXTERNAL_SESSION_ID_PATTERN = /^[A-Za-z0-9_-]{1,128}$/;
|
|
73120
74008
|
function assertValidExternalSessionId(externalSessionId) {
|
|
@@ -73124,14 +74012,14 @@ function assertValidExternalSessionId(externalSessionId) {
|
|
|
73124
74012
|
}
|
|
73125
74013
|
function externalSlatePath(cwd, externalSessionId) {
|
|
73126
74014
|
assertValidExternalSessionId(externalSessionId);
|
|
73127
|
-
return
|
|
74015
|
+
return path172.join(externalSlatesDir(cwd), `${externalSessionId}.json`);
|
|
73128
74016
|
}
|
|
73129
74017
|
function externalSlateLockPath(cwd, externalSessionId) {
|
|
73130
74018
|
return `${externalSlatePath(cwd, externalSessionId)}.lock`;
|
|
73131
74019
|
}
|
|
73132
74020
|
function externalSlateEvidenceDir(cwd, externalSessionId) {
|
|
73133
74021
|
assertValidExternalSessionId(externalSessionId);
|
|
73134
|
-
return
|
|
74022
|
+
return path172.join(externalSlatesDir(cwd), externalSessionId);
|
|
73135
74023
|
}
|
|
73136
74024
|
async function readExternalSlate(cwd, externalSessionId) {
|
|
73137
74025
|
try {
|
|
@@ -73776,8 +74664,8 @@ function buildToolRegistry() {
|
|
|
73776
74664
|
// src/mcp/resources.ts
|
|
73777
74665
|
init_fs();
|
|
73778
74666
|
init_contained_read();
|
|
73779
|
-
import
|
|
73780
|
-
import { lstat as
|
|
74667
|
+
import path173 from "path";
|
|
74668
|
+
import { lstat as lstat5, readdir as readdir32, realpath as realpath7, stat as stat17 } from "fs/promises";
|
|
73781
74669
|
var URI_PREFIX = "metaproject://";
|
|
73782
74670
|
function mimeForPath(filePath) {
|
|
73783
74671
|
if (filePath.endsWith(".json") || filePath.endsWith(".jsonl")) {
|
|
@@ -73789,20 +74677,20 @@ function mimeForPath(filePath) {
|
|
|
73789
74677
|
return "text/plain";
|
|
73790
74678
|
}
|
|
73791
74679
|
function dataRoot3(cwd) {
|
|
73792
|
-
return
|
|
74680
|
+
return path173.join(cwd, ".metaproject", "data");
|
|
73793
74681
|
}
|
|
73794
74682
|
function wikiRoot(cwd) {
|
|
73795
|
-
return
|
|
74683
|
+
return path173.join(cwd, ".metaproject", "wiki");
|
|
73796
74684
|
}
|
|
73797
74685
|
function memoryRoot2(cwd) {
|
|
73798
|
-
return
|
|
74686
|
+
return path173.join(cwd, ".metaproject", "memory");
|
|
73799
74687
|
}
|
|
73800
74688
|
var MAX_RESOURCE_BYTES = 8 * 1024 * 1024;
|
|
73801
74689
|
async function walkFiles(root, ownerRoot = root, ancestors = new Set) {
|
|
73802
74690
|
if (!await pathExists(root)) {
|
|
73803
74691
|
return [];
|
|
73804
74692
|
}
|
|
73805
|
-
const ownerInfo = await
|
|
74693
|
+
const ownerInfo = await lstat5(ownerRoot).catch(() => null);
|
|
73806
74694
|
if (!ownerInfo || !ownerInfo.isDirectory() || ownerInfo.isSymbolicLink())
|
|
73807
74695
|
return [];
|
|
73808
74696
|
const ownerReal = await realpath7(ownerRoot).catch(() => null);
|
|
@@ -73821,7 +74709,7 @@ async function walkFiles(root, ownerRoot = root, ancestors = new Set) {
|
|
|
73821
74709
|
return [];
|
|
73822
74710
|
}
|
|
73823
74711
|
for (const entry of entries) {
|
|
73824
|
-
const full =
|
|
74712
|
+
const full = path173.join(root, entry.name);
|
|
73825
74713
|
const info = await stat17(full).catch(() => null);
|
|
73826
74714
|
if (!info || !isPathInside(ownerReal, await realpath7(full).catch(() => "")))
|
|
73827
74715
|
continue;
|
|
@@ -73849,9 +74737,9 @@ async function listArtifacts(cwd) {
|
|
|
73849
74737
|
if (!moduleEntry.isDirectory()) {
|
|
73850
74738
|
continue;
|
|
73851
74739
|
}
|
|
73852
|
-
const artifactsDir =
|
|
74740
|
+
const artifactsDir = path173.join(base, moduleEntry.name, "artifacts");
|
|
73853
74741
|
for (const file of await walkFiles(artifactsDir)) {
|
|
73854
|
-
const rel = toPosix(
|
|
74742
|
+
const rel = toPosix(path173.relative(artifactsDir, file));
|
|
73855
74743
|
const relPath = `${moduleEntry.name}/${rel}`;
|
|
73856
74744
|
listings.push({
|
|
73857
74745
|
uri: `${URI_PREFIX}artifacts/${relPath}`,
|
|
@@ -73865,7 +74753,7 @@ async function listArtifacts(cwd) {
|
|
|
73865
74753
|
async function listUnderRoot(cwd, cls, root) {
|
|
73866
74754
|
const listings = [];
|
|
73867
74755
|
for (const file of await walkFiles(root)) {
|
|
73868
|
-
const rel = toPosix(
|
|
74756
|
+
const rel = toPosix(path173.relative(root, file));
|
|
73869
74757
|
listings.push({
|
|
73870
74758
|
uri: `${URI_PREFIX}${cls}/${rel}`,
|
|
73871
74759
|
name: rel,
|
|
@@ -73915,12 +74803,12 @@ function resolveConfined(cwd, cls, relPath) {
|
|
|
73915
74803
|
if (moduleName.includes("..") || moduleName.length === 0) {
|
|
73916
74804
|
return null;
|
|
73917
74805
|
}
|
|
73918
|
-
const root =
|
|
73919
|
-
const absolute =
|
|
74806
|
+
const root = path173.join(dataRoot3(cwd), moduleName, "artifacts");
|
|
74807
|
+
const absolute = path173.resolve(root, rest);
|
|
73920
74808
|
return isPathInside(root, absolute) ? { root, absolute } : null;
|
|
73921
74809
|
}
|
|
73922
74810
|
const root = cls === "wiki" ? wikiRoot(cwd) : memoryRoot2(cwd);
|
|
73923
|
-
const absolute =
|
|
74811
|
+
const absolute = path173.resolve(root, relPath);
|
|
73924
74812
|
return isPathInside(root, absolute) ? { root, absolute } : null;
|
|
73925
74813
|
}
|
|
73926
74814
|
async function readResource(cwd, roots, uri) {
|
|
@@ -74131,7 +75019,7 @@ async function serveMcpCommand(args = [], cwd = process.cwd()) {
|
|
|
74131
75019
|
return;
|
|
74132
75020
|
}
|
|
74133
75021
|
const http = args.includes("--http");
|
|
74134
|
-
const projectRoot =
|
|
75022
|
+
const projectRoot = path175.resolve(resolveServeRoot(optionValue(args, "--cwd"), cwd, process.env));
|
|
74135
75023
|
try {
|
|
74136
75024
|
await serveMcp({ cwd: projectRoot, http });
|
|
74137
75025
|
} catch (error) {
|
|
@@ -74207,7 +75095,9 @@ function printMcpHelp() {
|
|
|
74207
75095
|
"keryx mcp add <name> --transport http <url> # a remote one",
|
|
74208
75096
|
"keryx mcp remove|enable|disable <name>",
|
|
74209
75097
|
"keryx mcp trust|untrust <name> # approve a project-scoped server",
|
|
74210
|
-
"keryx mcp doctor [name] [--json] # dial it and say what is wrong"
|
|
75098
|
+
"keryx mcp doctor [name] [--json] # dial it and say what is wrong",
|
|
75099
|
+
"keryx mcp auth <name> # authorise a remote server in your browser",
|
|
75100
|
+
"keryx mcp logout <name> # forget its stored credential"
|
|
74211
75101
|
]);
|
|
74212
75102
|
heading("Publishing keryx itself (the retired `keryx mcp` spellings)");
|
|
74213
75103
|
helpUsage([
|
|
@@ -74233,14 +75123,14 @@ function printMcpHelp() {
|
|
|
74233
75123
|
// src/commands/status.ts
|
|
74234
75124
|
init_fs();
|
|
74235
75125
|
init_json();
|
|
74236
|
-
import
|
|
75126
|
+
import path176 from "path";
|
|
74237
75127
|
async function statusCommand(args = []) {
|
|
74238
75128
|
if (args.includes("--help") || args.includes("-h")) {
|
|
74239
75129
|
printHelp16();
|
|
74240
75130
|
return;
|
|
74241
75131
|
}
|
|
74242
|
-
const root =
|
|
74243
|
-
const manifestPath =
|
|
75132
|
+
const root = path176.join(process.cwd(), ".metaproject");
|
|
75133
|
+
const manifestPath = path176.join(root, "metaproject.json");
|
|
74244
75134
|
if (!await pathExists(root)) {
|
|
74245
75135
|
console.log("Metaproject: not initialized");
|
|
74246
75136
|
console.log("Run: keryx init");
|
|
@@ -74280,7 +75170,7 @@ Reports the workspace root and one enabled/disabled line per module. Use
|
|
|
74280
75170
|
|
|
74281
75171
|
// src/commands/harness.ts
|
|
74282
75172
|
import { readFileSync as readFileSync7, writeFileSync as writeFileSync8 } from "fs";
|
|
74283
|
-
import
|
|
75173
|
+
import path179 from "path";
|
|
74284
75174
|
import { randomUUID as randomUUID20 } from "crypto";
|
|
74285
75175
|
|
|
74286
75176
|
// src/security/harness-scan.ts
|
|
@@ -74336,7 +75226,7 @@ init_make_provider();
|
|
|
74336
75226
|
init_profiles();
|
|
74337
75227
|
|
|
74338
75228
|
// src/harness/run/run.ts
|
|
74339
|
-
import { createHash as
|
|
75229
|
+
import { createHash as createHash39 } from "crypto";
|
|
74340
75230
|
|
|
74341
75231
|
// src/harness/completion/gate.ts
|
|
74342
75232
|
var SCHEMA_VERSION3 = 1;
|
|
@@ -74387,10 +75277,10 @@ function evaluateCompletion(input, deps) {
|
|
|
74387
75277
|
}
|
|
74388
75278
|
|
|
74389
75279
|
// src/harness/evidence/redaction.ts
|
|
74390
|
-
import { createHash as
|
|
75280
|
+
import { createHash as createHash36 } from "crypto";
|
|
74391
75281
|
var CLEAN_CATEGORY = "none";
|
|
74392
75282
|
function hashContent(content) {
|
|
74393
|
-
return
|
|
75283
|
+
return createHash36("sha256").update(content, "utf8").digest("hex");
|
|
74394
75284
|
}
|
|
74395
75285
|
function redactForPersistence(content, deps) {
|
|
74396
75286
|
const scan = deps.scan(content);
|
|
@@ -74441,7 +75331,7 @@ async function metaprojectBlastRadius(port, target) {
|
|
|
74441
75331
|
}
|
|
74442
75332
|
|
|
74443
75333
|
// src/harness/session/session.ts
|
|
74444
|
-
import { createHash as
|
|
75334
|
+
import { createHash as createHash37 } from "crypto";
|
|
74445
75335
|
var SCHEMA_VERSION4 = 1;
|
|
74446
75336
|
function canonicalStringify(value) {
|
|
74447
75337
|
if (Array.isArray(value)) {
|
|
@@ -74455,7 +75345,7 @@ function canonicalStringify(value) {
|
|
|
74455
75345
|
return JSON.stringify(value) ?? "null";
|
|
74456
75346
|
}
|
|
74457
75347
|
function sha2566(input) {
|
|
74458
|
-
return
|
|
75348
|
+
return createHash37("sha256").update(input, "utf8").digest("hex");
|
|
74459
75349
|
}
|
|
74460
75350
|
function deepFreeze(value) {
|
|
74461
75351
|
if (value !== null && typeof value === "object") {
|
|
@@ -74547,11 +75437,11 @@ class AppendOnlySession {
|
|
|
74547
75437
|
}
|
|
74548
75438
|
|
|
74549
75439
|
// src/harness/context/manifest.ts
|
|
74550
|
-
import { createHash as
|
|
75440
|
+
import { createHash as createHash38 } from "crypto";
|
|
74551
75441
|
var MAX_CONTEXT_BYTES = 2 * 1024 * 1024;
|
|
74552
75442
|
var MAX_CONTEXT_TOKENS = 200000;
|
|
74553
75443
|
function sha2567(input) {
|
|
74554
|
-
return
|
|
75444
|
+
return createHash38("sha256").update(input, "utf8").digest("hex");
|
|
74555
75445
|
}
|
|
74556
75446
|
function normalizeSource2(value) {
|
|
74557
75447
|
const record = typeof value === "object" && value !== null ? value : {};
|
|
@@ -74677,7 +75567,7 @@ function canonicalize2(value) {
|
|
|
74677
75567
|
return `{${keys.map((key) => `${JSON.stringify(key)}:${canonicalize2(record[key])}`).join(",")}}`;
|
|
74678
75568
|
}
|
|
74679
75569
|
function sha2568(input) {
|
|
74680
|
-
return
|
|
75570
|
+
return createHash39("sha256").update(input, "utf8").digest("hex");
|
|
74681
75571
|
}
|
|
74682
75572
|
function uniqueInOrder2(values) {
|
|
74683
75573
|
const seen = new Set;
|
|
@@ -75115,7 +76005,7 @@ function replayOffline(fixture, run, deps) {
|
|
|
75115
76005
|
}
|
|
75116
76006
|
|
|
75117
76007
|
// src/harness/tool/registry.ts
|
|
75118
|
-
import { createHash as
|
|
76008
|
+
import { createHash as createHash40 } from "crypto";
|
|
75119
76009
|
function canonicalize3(value) {
|
|
75120
76010
|
if (value === null || typeof value !== "object") {
|
|
75121
76011
|
return JSON.stringify(value) ?? "null";
|
|
@@ -75129,7 +76019,7 @@ function canonicalize3(value) {
|
|
|
75129
76019
|
return `{${entries.join(",")}}`;
|
|
75130
76020
|
}
|
|
75131
76021
|
function sha2569(input) {
|
|
75132
|
-
return
|
|
76022
|
+
return createHash40("sha256").update(input, "utf8").digest("hex");
|
|
75133
76023
|
}
|
|
75134
76024
|
function definitionHash(definition) {
|
|
75135
76025
|
return sha2569(canonicalize3(definition));
|
|
@@ -75280,11 +76170,11 @@ function runContainedProcess(input, deps) {
|
|
|
75280
76170
|
|
|
75281
76171
|
// src/harness/process/real-process-adapter.ts
|
|
75282
76172
|
import { spawnSync } from "child_process";
|
|
75283
|
-
import { createHash as
|
|
76173
|
+
import { createHash as createHash41 } from "crypto";
|
|
75284
76174
|
var DEFAULT_TIMEOUT_MS = 5000;
|
|
75285
76175
|
var DEFAULT_MAX_OUTPUT_BYTES = 10 * 1024 * 1024;
|
|
75286
76176
|
function sha256Hex4(input) {
|
|
75287
|
-
return
|
|
76177
|
+
return createHash41("sha256").update(input, "utf8").digest("hex");
|
|
75288
76178
|
}
|
|
75289
76179
|
function byteLength(chunk) {
|
|
75290
76180
|
if (chunk === null || chunk === undefined)
|
|
@@ -75353,7 +76243,7 @@ ${command.argv.join(" ")}`);
|
|
|
75353
76243
|
}
|
|
75354
76244
|
|
|
75355
76245
|
// src/harness/process/sandbox/profile.ts
|
|
75356
|
-
import
|
|
76246
|
+
import path177 from "path";
|
|
75357
76247
|
var DEFAULT_SECRET_SUBPATHS = [
|
|
75358
76248
|
".ssh",
|
|
75359
76249
|
".gnupg",
|
|
@@ -75378,7 +76268,7 @@ function defaultReadDenyList(home) {
|
|
|
75378
76268
|
if (!home) {
|
|
75379
76269
|
return [];
|
|
75380
76270
|
}
|
|
75381
|
-
return DEFAULT_SECRET_SUBPATHS.map((sub) =>
|
|
76271
|
+
return DEFAULT_SECRET_SUBPATHS.map((sub) => path177.join(home, sub));
|
|
75382
76272
|
}
|
|
75383
76273
|
function defaultSandboxProfile(cwd, tmpDir, home) {
|
|
75384
76274
|
return {
|
|
@@ -75398,10 +76288,10 @@ function dedupe3(values) {
|
|
|
75398
76288
|
init_config_dir();
|
|
75399
76289
|
init_shell_config();
|
|
75400
76290
|
import { existsSync as existsSync26 } from "fs";
|
|
75401
|
-
import
|
|
76291
|
+
import path178 from "path";
|
|
75402
76292
|
var SECRET_KEY_PATTERN2 = /api[_-]?key|secret|token|password|credential/i;
|
|
75403
76293
|
function sandboxConfigPath(dir) {
|
|
75404
|
-
return
|
|
76294
|
+
return path178.join(path178.dirname(shellConfigPath(dir)), "sandbox.json");
|
|
75405
76295
|
}
|
|
75406
76296
|
function isShellDefault(v) {
|
|
75407
76297
|
return v === "off" || v === "workspace" || v === "strict" || v === "1";
|
|
@@ -76520,7 +77410,7 @@ async function harnessExec(args, deps) {
|
|
|
76520
77410
|
env: commandEnv,
|
|
76521
77411
|
cwd
|
|
76522
77412
|
};
|
|
76523
|
-
const worktreeRoot =
|
|
77413
|
+
const worktreeRoot = path179.parse(path179.resolve(cwd, commandPath)).root || cwd;
|
|
76524
77414
|
const budget = {
|
|
76525
77415
|
reservationId: idSeq(),
|
|
76526
77416
|
maxRuntimeMs: maxRuntimeMs ?? EXEC_DEFAULT_RUNTIME_MS
|
|
@@ -77201,12 +78091,14 @@ function resolveShellRestrictedMasks(env, sandboxConfigDir, projectRoot) {
|
|
|
77201
78091
|
tlsTerminate: result.resolution.tlsTerminate
|
|
77202
78092
|
};
|
|
77203
78093
|
}
|
|
77204
|
-
async function resolveShellEnv() {
|
|
78094
|
+
async function resolveShellEnv(configDir) {
|
|
77205
78095
|
await Promise.resolve().then(() => init_shell_config());
|
|
77206
|
-
|
|
78096
|
+
const passSaved = process.env.KERYX_SHELL_PASS_SAVED_KEYS === "1";
|
|
78097
|
+
const source = passSaved ? envWithSavedApiKeys(process.env, configDir) : process.env;
|
|
78098
|
+
const withheld = passSaved ? new Set : savedCredentialEnvKeys();
|
|
77207
78099
|
const env = {};
|
|
77208
|
-
for (const [key, value] of Object.entries(
|
|
77209
|
-
if (typeof value === "string")
|
|
78100
|
+
for (const [key, value] of Object.entries(source)) {
|
|
78101
|
+
if (typeof value === "string" && !withheld.has(key))
|
|
77210
78102
|
env[key] = value;
|
|
77211
78103
|
}
|
|
77212
78104
|
return env;
|
|
@@ -77227,7 +78119,8 @@ async function resolveShellSpawn(root, command, baseEnv, detectOpts) {
|
|
|
77227
78119
|
}
|
|
77228
78120
|
let netClose = async () => {};
|
|
77229
78121
|
if (profile.network === "restricted") {
|
|
77230
|
-
|
|
78122
|
+
await Promise.resolve().then(() => init_shell_config());
|
|
78123
|
+
const masks = resolveShellRestrictedMasks(envWithSavedApiKeys(env), undefined, root);
|
|
77231
78124
|
if (!masks.ok)
|
|
77232
78125
|
return { ok: false, error: `shell_exec: ${masks.reason}` };
|
|
77233
78126
|
const net = await setupNetworkRun(profile, {
|
|
@@ -78466,7 +79359,7 @@ function workspaceReadTool(cwd) {
|
|
|
78466
79359
|
// src/harness/tool/builtin/workspace-lifecycle-tool.ts
|
|
78467
79360
|
import { randomUUID as randomUUID22 } from "crypto";
|
|
78468
79361
|
import { writeFile as writeFile52 } from "fs/promises";
|
|
78469
|
-
import
|
|
79362
|
+
import path180 from "path";
|
|
78470
79363
|
init_slate();
|
|
78471
79364
|
init_store3();
|
|
78472
79365
|
init_guard2();
|
|
@@ -78612,7 +79505,7 @@ function workspaceProposeTool(cwd, getSessionDir) {
|
|
|
78612
79505
|
return { output: `workspace_propose: unrecognized 'kind' \u2014 expected one of: ${PROPOSAL_KINDS.join(", ")}`, isError: true };
|
|
78613
79506
|
}
|
|
78614
79507
|
const explicitSessionId = typeof input.sessionId === "string" && input.sessionId.length > 0 ? input.sessionId : undefined;
|
|
78615
|
-
const sessionRef = explicitSessionId ??
|
|
79508
|
+
const sessionRef = explicitSessionId ?? path180.basename(getSessionDir() ?? "");
|
|
78616
79509
|
if (sessionRef.length === 0) {
|
|
78617
79510
|
return { output: "workspace_propose: no 'sessionId' given and no active session in this run", isError: true };
|
|
78618
79511
|
}
|
|
@@ -78784,8 +79677,8 @@ init_shell_config();
|
|
|
78784
79677
|
init_command_risk();
|
|
78785
79678
|
init_shell_syntax();
|
|
78786
79679
|
import { existsSync as existsSync30 } from "fs";
|
|
78787
|
-
import
|
|
78788
|
-
import { createHash as
|
|
79680
|
+
import path181 from "path";
|
|
79681
|
+
import { createHash as createHash42 } from "crypto";
|
|
78789
79682
|
var PREFIX_BANNED = new Set([
|
|
78790
79683
|
"sh",
|
|
78791
79684
|
"bash",
|
|
@@ -78989,7 +79882,7 @@ function emptyShellPermissions() {
|
|
|
78989
79882
|
return { allow: [] };
|
|
78990
79883
|
}
|
|
78991
79884
|
function shellPermissionsPath(dir) {
|
|
78992
|
-
return
|
|
79885
|
+
return path181.join(path181.dirname(shellConfigPath(dir)), "permissions.json");
|
|
78993
79886
|
}
|
|
78994
79887
|
function loadShellPermissionsWithAudit(dir) {
|
|
78995
79888
|
try {
|
|
@@ -79027,7 +79920,7 @@ function loadShellPermissions(dir) {
|
|
|
79027
79920
|
function saveShellPermissions(perms, dir, options = {}) {
|
|
79028
79921
|
try {
|
|
79029
79922
|
const file = shellPermissionsPath(dir);
|
|
79030
|
-
ensureKeryxConfigDir(
|
|
79923
|
+
ensureKeryxConfigDir(path181.dirname(file));
|
|
79031
79924
|
const cleaned = Array.from(new Set(perms.allow.map((p) => p.trim()).filter((p) => p.length > 0)));
|
|
79032
79925
|
const body = {
|
|
79033
79926
|
allow: options.skipValidation === true ? cleaned : cleaned.filter((p) => validateShellPattern(p).ok)
|
|
@@ -79105,7 +79998,7 @@ function shellPermissionsFingerprint(dir) {
|
|
|
79105
79998
|
if (!read.ok) {
|
|
79106
79999
|
return "";
|
|
79107
80000
|
}
|
|
79108
|
-
return
|
|
80001
|
+
return createHash42("sha256").update(read.text, "utf8").digest("hex");
|
|
79109
80002
|
} catch {
|
|
79110
80003
|
return "";
|
|
79111
80004
|
}
|
|
@@ -79466,12 +80359,12 @@ function remoteRequest(providerId, endpoint, query, key) {
|
|
|
79466
80359
|
// src/lib/search-config.ts
|
|
79467
80360
|
init_config_dir();
|
|
79468
80361
|
import { existsSync as existsSync31 } from "fs";
|
|
79469
|
-
import
|
|
80362
|
+
import path182 from "path";
|
|
79470
80363
|
function searchConfigPath(dir) {
|
|
79471
|
-
return
|
|
80364
|
+
return path182.join(keryxConfigDir(dir), "search-providers.json");
|
|
79472
80365
|
}
|
|
79473
80366
|
function searchCredentialPath(dir) {
|
|
79474
|
-
return
|
|
80367
|
+
return path182.join(keryxConfigDir(dir), "search-credentials.json");
|
|
79475
80368
|
}
|
|
79476
80369
|
function readJson5(file) {
|
|
79477
80370
|
try {
|
|
@@ -79597,10 +80490,10 @@ function createDefaultSearchProviderController(configDir) {
|
|
|
79597
80490
|
return new SearchProviderController(registry, configDir);
|
|
79598
80491
|
}
|
|
79599
80492
|
// src/harness/tool/builtin/spawn-subagent-tool.ts
|
|
79600
|
-
import { createHash as
|
|
80493
|
+
import { createHash as createHash43, randomUUID as randomUUID24 } from "crypto";
|
|
79601
80494
|
import { mkdtemp, rm as rm13 } from "fs/promises";
|
|
79602
80495
|
import { tmpdir as tmpdir4 } from "os";
|
|
79603
|
-
import
|
|
80496
|
+
import path183 from "path";
|
|
79604
80497
|
init_metaproject_adapter();
|
|
79605
80498
|
init_ledger();
|
|
79606
80499
|
init_orchestrate();
|
|
@@ -79647,7 +80540,7 @@ function readExternalRuntimeRequest(input) {
|
|
|
79647
80540
|
return raw.kind === "external" ? raw : undefined;
|
|
79648
80541
|
}
|
|
79649
80542
|
function sha25610(text) {
|
|
79650
|
-
return
|
|
80543
|
+
return createHash43("sha256").update(text).digest("hex");
|
|
79651
80544
|
}
|
|
79652
80545
|
var parentShellPolicy = shellParentProfile;
|
|
79653
80546
|
var childReadOnlyPolicy = shellChildReadOnlyProfile;
|
|
@@ -79863,8 +80756,8 @@ function createSpawnSubagentTool(deps) {
|
|
|
79863
80756
|
let dispatchId = "";
|
|
79864
80757
|
let closing = false;
|
|
79865
80758
|
try {
|
|
79866
|
-
ephemeralDir = await mkdtemp(
|
|
79867
|
-
dispatchId =
|
|
80759
|
+
ephemeralDir = await mkdtemp(path183.join(tmpdir4(), "keryx-subagent-slate-"));
|
|
80760
|
+
dispatchId = path183.basename(ephemeralDir);
|
|
79868
80761
|
openedChildSlate = await openSlate({
|
|
79869
80762
|
dir: ephemeralDir,
|
|
79870
80763
|
cwd,
|
|
@@ -80107,7 +81000,7 @@ import { randomUUID as randomUUID25 } from "crypto";
|
|
|
80107
81000
|
import { execFile as execFile4 } from "child_process";
|
|
80108
81001
|
import { mkdir as mkdir61 } from "fs/promises";
|
|
80109
81002
|
import { tmpdir as tmpdir6 } from "os";
|
|
80110
|
-
import
|
|
81003
|
+
import path185 from "path";
|
|
80111
81004
|
import { promisify as promisify4 } from "util";
|
|
80112
81005
|
|
|
80113
81006
|
// src/harness/child/git-worktree-port.ts
|
|
@@ -80359,7 +81252,7 @@ function readRuntimeBlock(dispatch) {
|
|
|
80359
81252
|
// src/harness/external/runtime.ts
|
|
80360
81253
|
import { mkdtemp as mkdtemp2, rm as rm14, writeFile as writeFile53 } from "fs/promises";
|
|
80361
81254
|
import { tmpdir as tmpdir5 } from "os";
|
|
80362
|
-
import
|
|
81255
|
+
import path184 from "path";
|
|
80363
81256
|
|
|
80364
81257
|
// src/harness/external/prompt.ts
|
|
80365
81258
|
var EXTERNAL_RUNTIME_DIRECTIVE = [
|
|
@@ -81402,8 +82295,8 @@ async function prepareResultSchema() {
|
|
|
81402
82295
|
try {
|
|
81403
82296
|
const schema = await loadSchema("subagent-result");
|
|
81404
82297
|
const schemaText = JSON.stringify(schema, null, 2);
|
|
81405
|
-
schemaDir = await mkdtemp2(
|
|
81406
|
-
const schemaPath =
|
|
82298
|
+
schemaDir = await mkdtemp2(path184.join(tmpdir5(), "keryx-external-result-schema-"));
|
|
82299
|
+
const schemaPath = path184.join(schemaDir, "subagent-result.schema.json");
|
|
81407
82300
|
await writeFile53(schemaPath, schemaText, "utf8");
|
|
81408
82301
|
return { ok: true, schema, schemaText, schemaPath, schemaDir };
|
|
81409
82302
|
} catch (error) {
|
|
@@ -81660,7 +82553,7 @@ async function createRunExternal(options) {
|
|
|
81660
82553
|
}
|
|
81661
82554
|
const config = gate.config;
|
|
81662
82555
|
const idSeq = options.idSeq ?? (() => randomUUID25());
|
|
81663
|
-
const worktreesDir = options.worktreesDir ??
|
|
82556
|
+
const worktreesDir = options.worktreesDir ?? path185.join(tmpdir6(), "keryx-external-worktrees");
|
|
81664
82557
|
const spawn = options.spawn ?? createBunSpawnPort();
|
|
81665
82558
|
const worktree = options.worktree ?? ensuringParentDir(createGitWorktreePort({ repoRoot: options.cwd, worktreesDir }), worktreesDir);
|
|
81666
82559
|
const readWorkingDiff = options.readWorkingDiff ?? (() => defaultWorkingDiff(options.cwd));
|
|
@@ -81970,7 +82863,7 @@ class LiveMarkdownBlock {
|
|
|
81970
82863
|
// src/tui/theme.ts
|
|
81971
82864
|
init_config_dir();
|
|
81972
82865
|
import { existsSync as existsSync32 } from "fs";
|
|
81973
|
-
import
|
|
82866
|
+
import path186 from "path";
|
|
81974
82867
|
var THEME_NAMES = ["groknight", "grokday", "tokyonight", "keryx"];
|
|
81975
82868
|
var THEME_IDS = ["auto", ...THEME_NAMES];
|
|
81976
82869
|
var DEFAULT_THEME_ID = "groknight";
|
|
@@ -82100,7 +82993,7 @@ function onThemeChange(listener) {
|
|
|
82100
82993
|
};
|
|
82101
82994
|
}
|
|
82102
82995
|
function tuiConfigPath(dir) {
|
|
82103
|
-
return
|
|
82996
|
+
return path186.join(keryxConfigDir(dir), "tui.json");
|
|
82104
82997
|
}
|
|
82105
82998
|
function loadPersistedThemeId(dir) {
|
|
82106
82999
|
try {
|
|
@@ -82157,7 +83050,7 @@ init_workspace_service();
|
|
|
82157
83050
|
init_service8();
|
|
82158
83051
|
init_store2();
|
|
82159
83052
|
init_fs();
|
|
82160
|
-
import
|
|
83053
|
+
import path187 from "path";
|
|
82161
83054
|
import { readFile as readFile97 } from "fs/promises";
|
|
82162
83055
|
var POSITIVE_INTEGER = /^[1-9][0-9]*$/;
|
|
82163
83056
|
var DEFAULT_AUTO_GOAL_ROUNDS = 8;
|
|
@@ -82233,7 +83126,7 @@ function getFlowService() {
|
|
|
82233
83126
|
async function autoProvisionFlow(cwd, goalText) {
|
|
82234
83127
|
const service = getFlowService();
|
|
82235
83128
|
const result = await service.init({ cwd, title: goalText });
|
|
82236
|
-
const acFile =
|
|
83129
|
+
const acFile = path187.join(cwd, result.dir, "acceptance-criteria.md");
|
|
82237
83130
|
await writeFileAtomic(acFile, [
|
|
82238
83131
|
"# Acceptance Criteria",
|
|
82239
83132
|
"",
|
|
@@ -83784,7 +84677,7 @@ init_proposal_lifecycle();
|
|
|
83784
84677
|
init_workspace_service();
|
|
83785
84678
|
import { randomUUID as randomUUID27 } from "crypto";
|
|
83786
84679
|
import { readdir as readdir34, stat as stat18 } from "fs/promises";
|
|
83787
|
-
import
|
|
84680
|
+
import path188 from "path";
|
|
83788
84681
|
|
|
83789
84682
|
// src/sac/lifecycle-flag.ts
|
|
83790
84683
|
init_store();
|
|
@@ -83943,7 +84836,7 @@ async function readExternalUnboundCandidates(cwd) {
|
|
|
83943
84836
|
}
|
|
83944
84837
|
return groups;
|
|
83945
84838
|
}, []));
|
|
83946
|
-
const metaPath =
|
|
84839
|
+
const metaPath = path188.join(extDir, `${id}.json`);
|
|
83947
84840
|
if (await pathExists(unboundDismissedReceiptPath(metaPath)))
|
|
83948
84841
|
continue;
|
|
83949
84842
|
candidates.push({
|
|
@@ -83957,7 +84850,7 @@ async function readExternalUnboundCandidates(cwd) {
|
|
|
83957
84850
|
return candidates;
|
|
83958
84851
|
}
|
|
83959
84852
|
async function readNewestUnboundCandidateForExternal(cwd, externalSessionId) {
|
|
83960
|
-
const evidenceDir =
|
|
84853
|
+
const evidenceDir = path188.join(externalSlatesDir(cwd), externalSessionId);
|
|
83961
84854
|
let entries;
|
|
83962
84855
|
try {
|
|
83963
84856
|
entries = (await readdir34(evidenceDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
|
|
@@ -83966,7 +84859,7 @@ async function readNewestUnboundCandidateForExternal(cwd, externalSessionId) {
|
|
|
83966
84859
|
}
|
|
83967
84860
|
entries.sort();
|
|
83968
84861
|
for (let i = entries.length - 1;i >= 0; i--) {
|
|
83969
|
-
const evidencePath =
|
|
84862
|
+
const evidencePath = path188.join(evidenceDir, entries[i]);
|
|
83970
84863
|
if (await pathExists(unboundDismissedReceiptPath(evidencePath)))
|
|
83971
84864
|
continue;
|
|
83972
84865
|
const result = readConfigFile(evidencePath);
|
|
@@ -84009,7 +84902,7 @@ var SESSION_ATTRIBUTION_SLACK_MS = 5 * 60000;
|
|
|
84009
84902
|
var MTIME_CLOCK_SKEW_TOLERANCE_MS = 5000;
|
|
84010
84903
|
async function collectReceiptTargets(cwd, owner) {
|
|
84011
84904
|
const targets = new Set;
|
|
84012
|
-
const workspacesDir =
|
|
84905
|
+
const workspacesDir = path188.join(cwd, ".metaproject", "workspaces");
|
|
84013
84906
|
let workspaceIds;
|
|
84014
84907
|
try {
|
|
84015
84908
|
workspaceIds = (await readdir34(workspacesDir, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
@@ -84017,7 +84910,7 @@ async function collectReceiptTargets(cwd, owner) {
|
|
|
84017
84910
|
return targets;
|
|
84018
84911
|
}
|
|
84019
84912
|
for (const workspaceId of workspaceIds) {
|
|
84020
|
-
const receiptsDir =
|
|
84913
|
+
const receiptsDir = path188.join(workspacesDir, workspaceId, `${owner}-write-receipts`);
|
|
84021
84914
|
let files;
|
|
84022
84915
|
try {
|
|
84023
84916
|
files = (await readdir34(receiptsDir)).filter((name) => name.endsWith(".json"));
|
|
@@ -84025,7 +84918,7 @@ async function collectReceiptTargets(cwd, owner) {
|
|
|
84025
84918
|
continue;
|
|
84026
84919
|
}
|
|
84027
84920
|
for (const file of files) {
|
|
84028
|
-
const result = readConfigFile(
|
|
84921
|
+
const result = readConfigFile(path188.join(receiptsDir, file));
|
|
84029
84922
|
if (!result.ok)
|
|
84030
84923
|
continue;
|
|
84031
84924
|
try {
|
|
@@ -84068,7 +84961,7 @@ async function findSkillFiles(dir) {
|
|
|
84068
84961
|
}
|
|
84069
84962
|
const found = [];
|
|
84070
84963
|
for (const entry of entries) {
|
|
84071
|
-
const full =
|
|
84964
|
+
const full = path188.join(dir, entry.name);
|
|
84072
84965
|
if (entry.isDirectory()) {
|
|
84073
84966
|
found.push(...await findSkillFiles(full));
|
|
84074
84967
|
} else if (entry.isFile() && entry.name === "SKILL.md") {
|
|
@@ -84127,10 +85020,10 @@ async function detectUnreviewedSacPathChanges(cwd, sessions) {
|
|
|
84127
85020
|
changedAt: attribution.changedAt
|
|
84128
85021
|
});
|
|
84129
85022
|
}
|
|
84130
|
-
const sacSkillsDir =
|
|
85023
|
+
const sacSkillsDir = path188.join(cwd, ".metaproject", "project-skills", "sac");
|
|
84131
85024
|
const skillFiles = await findSkillFiles(sacSkillsDir);
|
|
84132
85025
|
for (const absolutePath of skillFiles) {
|
|
84133
|
-
const targetRef =
|
|
85026
|
+
const targetRef = path188.relative(path188.join(cwd, ".metaproject"), absolutePath).split(path188.sep).join("/");
|
|
84134
85027
|
if (skillReceipts.has(targetRef))
|
|
84135
85028
|
continue;
|
|
84136
85029
|
const attribution = await attributeToSession(absolutePath, scoped);
|
|
@@ -84148,12 +85041,12 @@ async function detectUnreviewedSacPathChanges(cwd, sessions) {
|
|
|
84148
85041
|
return items;
|
|
84149
85042
|
}
|
|
84150
85043
|
async function isSlateEngaged(dir) {
|
|
84151
|
-
if (await pathExists(
|
|
85044
|
+
if (await pathExists(path188.join(dir, "slate.json")))
|
|
84152
85045
|
return true;
|
|
84153
|
-
if (await pathExists(
|
|
85046
|
+
if (await pathExists(path188.join(dir, "terminal-state.json")))
|
|
84154
85047
|
return true;
|
|
84155
85048
|
try {
|
|
84156
|
-
const entries = await readdir34(
|
|
85049
|
+
const entries = await readdir34(path188.join(dir, "slate-archive"));
|
|
84157
85050
|
return entries.length > 0;
|
|
84158
85051
|
} catch {
|
|
84159
85052
|
return false;
|
|
@@ -84167,7 +85060,7 @@ async function safeReadSlate(dir) {
|
|
|
84167
85060
|
}
|
|
84168
85061
|
}
|
|
84169
85062
|
async function readTerminalState(dir) {
|
|
84170
|
-
const result = readConfigFile(
|
|
85063
|
+
const result = readConfigFile(path188.join(dir, "terminal-state.json"));
|
|
84171
85064
|
if (!result.ok) {
|
|
84172
85065
|
return;
|
|
84173
85066
|
}
|
|
@@ -84191,7 +85084,7 @@ async function resolveUnboundCandidateTarget(cwd, target) {
|
|
|
84191
85084
|
if (session === undefined) {
|
|
84192
85085
|
throw new Error(`No session or evidence path matches "${target}"`);
|
|
84193
85086
|
}
|
|
84194
|
-
const archiveDir =
|
|
85087
|
+
const archiveDir = path188.join(sessionDir(session.projectPath, session.id), "slate-archive");
|
|
84195
85088
|
let entries;
|
|
84196
85089
|
try {
|
|
84197
85090
|
entries = (await readdir34(archiveDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
|
|
@@ -84201,7 +85094,7 @@ async function resolveUnboundCandidateTarget(cwd, target) {
|
|
|
84201
85094
|
if (entries.length === 0)
|
|
84202
85095
|
throw new Error(`No unbound-candidate artifacts for session ${session.id}`);
|
|
84203
85096
|
entries.sort();
|
|
84204
|
-
return
|
|
85097
|
+
return path188.join(archiveDir, entries[entries.length - 1]);
|
|
84205
85098
|
}
|
|
84206
85099
|
async function dismissUnboundByTarget(cwd, target, reason) {
|
|
84207
85100
|
const evidencePath = await resolveUnboundCandidateTarget(cwd, target);
|
|
@@ -84227,7 +85120,7 @@ async function dismissUnboundCandidate(candidatePath, reason) {
|
|
|
84227
85120
|
return { removed: candidatePath, receipt: receiptPath };
|
|
84228
85121
|
}
|
|
84229
85122
|
async function readNewestUnboundCandidate(dir) {
|
|
84230
|
-
const archiveDir =
|
|
85123
|
+
const archiveDir = path188.join(dir, "slate-archive");
|
|
84231
85124
|
let entries;
|
|
84232
85125
|
try {
|
|
84233
85126
|
entries = (await readdir34(archiveDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
|
|
@@ -84236,7 +85129,7 @@ async function readNewestUnboundCandidate(dir) {
|
|
|
84236
85129
|
}
|
|
84237
85130
|
entries.sort();
|
|
84238
85131
|
for (let i = entries.length - 1;i >= 0; i--) {
|
|
84239
|
-
const evidencePath =
|
|
85132
|
+
const evidencePath = path188.join(archiveDir, entries[i]);
|
|
84240
85133
|
if (await pathExists(unboundDismissedReceiptPath(evidencePath)))
|
|
84241
85134
|
continue;
|
|
84242
85135
|
const result = readConfigFile(evidencePath);
|
|
@@ -84266,7 +85159,7 @@ function isFailureOutcome(g) {
|
|
|
84266
85159
|
return g.outcome === "error" || g.outcome === "no_credential" || g.outcome === "conflict";
|
|
84267
85160
|
}
|
|
84268
85161
|
async function readNewestWrapUpOutcome(dir) {
|
|
84269
|
-
const archiveDir =
|
|
85162
|
+
const archiveDir = path188.join(dir, "slate-archive");
|
|
84270
85163
|
let entries;
|
|
84271
85164
|
try {
|
|
84272
85165
|
entries = (await readdir34(archiveDir)).filter((name) => name.endsWith("-wrap-up-outcome.json"));
|
|
@@ -84275,7 +85168,7 @@ async function readNewestWrapUpOutcome(dir) {
|
|
|
84275
85168
|
}
|
|
84276
85169
|
entries.sort();
|
|
84277
85170
|
for (let i = entries.length - 1;i >= 0; i--) {
|
|
84278
|
-
const evidencePath =
|
|
85171
|
+
const evidencePath = path188.join(archiveDir, entries[i]);
|
|
84279
85172
|
const result = readConfigFile(evidencePath);
|
|
84280
85173
|
if (!result.ok) {
|
|
84281
85174
|
continue;
|
|
@@ -87756,41 +88649,6 @@ init_patch_risk();
|
|
|
87756
88649
|
init_catalog();
|
|
87757
88650
|
init_grants();
|
|
87758
88651
|
init_login();
|
|
87759
|
-
|
|
87760
|
-
// src/lib/oauth/open-url.ts
|
|
87761
|
-
import { spawn as spawn5 } from "child_process";
|
|
87762
|
-
function browserOpenPlan(url, platform = process.platform, env = process.env) {
|
|
87763
|
-
if (url.length === 0) {
|
|
87764
|
-
return;
|
|
87765
|
-
}
|
|
87766
|
-
if (platform === "darwin") {
|
|
87767
|
-
return { cmd: "open", args: [url] };
|
|
87768
|
-
}
|
|
87769
|
-
if (platform === "win32") {
|
|
87770
|
-
return { cmd: "cmd", args: ["/c", "start", "", url] };
|
|
87771
|
-
}
|
|
87772
|
-
if (typeof env.DISPLAY === "string" && env.DISPLAY.length > 0) {
|
|
87773
|
-
return { cmd: "xdg-open", args: [url] };
|
|
87774
|
-
}
|
|
87775
|
-
if (typeof env.WAYLAND_DISPLAY === "string" && env.WAYLAND_DISPLAY.length > 0) {
|
|
87776
|
-
return { cmd: "xdg-open", args: [url] };
|
|
87777
|
-
}
|
|
87778
|
-
return;
|
|
87779
|
-
}
|
|
87780
|
-
function openVerificationUrl(url, deps = {}) {
|
|
87781
|
-
const plan = browserOpenPlan(url, deps.platform ?? process.platform, deps.env ?? process.env);
|
|
87782
|
-
if (plan === undefined) {
|
|
87783
|
-
return;
|
|
87784
|
-
}
|
|
87785
|
-
const spawnFn = deps.spawn ?? spawn5;
|
|
87786
|
-
try {
|
|
87787
|
-
const child = spawnFn(plan.cmd, plan.args, { stdio: "ignore", detached: true });
|
|
87788
|
-
child.on("error", () => {});
|
|
87789
|
-
child.unref();
|
|
87790
|
-
} catch {}
|
|
87791
|
-
}
|
|
87792
|
-
|
|
87793
|
-
// src/tui/tui-shell.ts
|
|
87794
88652
|
init_shell_config();
|
|
87795
88653
|
init_provider_config();
|
|
87796
88654
|
|
|
@@ -87826,10 +88684,10 @@ function describeElicitationPrompt(tool, inputJson) {
|
|
|
87826
88684
|
// src/lib/permission-mode-config.ts
|
|
87827
88685
|
init_permission_mode();
|
|
87828
88686
|
init_config_dir();
|
|
87829
|
-
import
|
|
88687
|
+
import path189 from "path";
|
|
87830
88688
|
var EMPTY3 = { schemaVersion: 1, projects: {} };
|
|
87831
88689
|
function permissionModeConfigPath(dir) {
|
|
87832
|
-
return
|
|
88690
|
+
return path189.join(keryxConfigDir(dir), "permission-mode.json");
|
|
87833
88691
|
}
|
|
87834
88692
|
function withRegistryLock2(dir, fn) {
|
|
87835
88693
|
return withFileLock2(`${permissionModeConfigPath(dir)}.lock`, fn, {
|
|
@@ -88347,7 +89205,7 @@ function showComposerChoice(otui, r, dock, request) {
|
|
|
88347
89205
|
// src/lib/version-check.ts
|
|
88348
89206
|
init_config_dir();
|
|
88349
89207
|
init_fs();
|
|
88350
|
-
import
|
|
89208
|
+
import path190 from "path";
|
|
88351
89209
|
var REGISTRY_URL = "https://registry.npmjs.org/@mrciphersmith%2Fkeryx/latest";
|
|
88352
89210
|
var FIXED_INSTALL_COMMAND = "npm install -g @mrciphersmith/keryx@latest";
|
|
88353
89211
|
var RESPONSE_BODY_LIMIT_BYTES = 64 * 1024;
|
|
@@ -88549,7 +89407,7 @@ async function checkVersion(options) {
|
|
|
88549
89407
|
const now = options.now ?? Date.now;
|
|
88550
89408
|
const timestamp = now();
|
|
88551
89409
|
const configDir = ensureKeryxConfigDir(options.cacheDir);
|
|
88552
|
-
const cacheFile =
|
|
89410
|
+
const cacheFile = path190.join(configDir, "version-check.json");
|
|
88553
89411
|
const cache = parseCache(cacheFile);
|
|
88554
89412
|
if (cache?.latestVersion !== undefined && cache.successAt !== undefined && timestamp - cache.successAt >= 0 && timestamp - cache.successAt < SUCCESS_CACHE_TTL_MS) {
|
|
88555
89413
|
return resultFor(options.currentVersion, current, cache.latestVersion, "cache");
|
|
@@ -92169,6 +93027,9 @@ function promptApiKeyStep(otui, r, opts) {
|
|
|
92169
93027
|
const box = overlayBox(otui, r, "key-picker");
|
|
92170
93028
|
r.root.add(box);
|
|
92171
93029
|
box.add(new otui.TextRenderable(r, { id: "kp-title", content: otui.t`${otui.bold(`Paste your ${opts.label} API key`)} ${otui.dim("(Enter \xB7 Esc to go back)")}` }));
|
|
93030
|
+
if (opts.notice !== undefined) {
|
|
93031
|
+
box.add(new otui.TextRenderable(r, { id: "kp-notice", content: otui.t`${otui.red("\u2717")} ${opts.notice}`, marginTop: 1 }));
|
|
93032
|
+
}
|
|
92172
93033
|
box.add(new otui.TextRenderable(r, {
|
|
92173
93034
|
id: "kp-note",
|
|
92174
93035
|
content: otui.t`${otui.dim(`Set as ${opts.envKey} \xB7 saved to your keryx config dir (owner-only, 0600)`)}`,
|
|
@@ -92288,9 +93149,14 @@ function runDeviceLoginInTui(otui, r, provider) {
|
|
|
92288
93149
|
});
|
|
92289
93150
|
});
|
|
92290
93151
|
}
|
|
92291
|
-
async function modelsForPicker(prov) {
|
|
92292
|
-
|
|
92293
|
-
|
|
93152
|
+
async function modelsForPicker(prov, deps = {}) {
|
|
93153
|
+
return await resolveModelsForPicker(deps.fetch ?? globalThis.fetch, prov, deps.env ?? process.env);
|
|
93154
|
+
}
|
|
93155
|
+
function modelPickerNotice(label, result) {
|
|
93156
|
+
if (result.models.length > 0 || result.failure === undefined) {
|
|
93157
|
+
return;
|
|
93158
|
+
}
|
|
93159
|
+
return modelsFailureLine(label, result.failure);
|
|
92294
93160
|
}
|
|
92295
93161
|
function pickProviderStep(otui, r, detected) {
|
|
92296
93162
|
return new Promise((resolve) => {
|
|
@@ -92377,44 +93243,66 @@ function selectProviderModelInTui(otui, r, detected, options = {}) {
|
|
|
92377
93243
|
continue;
|
|
92378
93244
|
}
|
|
92379
93245
|
if (!options.onlyConnected && selectedBaseUrl !== undefined)
|
|
92380
|
-
saveProviderBaseUrl(prov.name, selectedBaseUrl);
|
|
93246
|
+
saveProviderBaseUrl(prov.name, selectedBaseUrl, options.configDir);
|
|
92381
93247
|
const selectedProvider = selectedBaseUrl === undefined ? prov : { ...prov, baseUrl: selectedBaseUrl };
|
|
92382
93248
|
const envKey = prov.envKey;
|
|
92383
|
-
|
|
92384
|
-
|
|
92385
|
-
|
|
92386
|
-
|
|
92387
|
-
|
|
92388
|
-
|
|
92389
|
-
|
|
92390
|
-
|
|
92391
|
-
|
|
92392
|
-
|
|
92393
|
-
if (offered.length > 1) {
|
|
92394
|
-
method = await pickAuthMethodStep(otui, r, prov.label ?? prov.name, offered);
|
|
92395
|
-
if (method === undefined) {
|
|
92396
|
-
continue;
|
|
92397
|
-
}
|
|
93249
|
+
const label = prov.label ?? prov.name;
|
|
93250
|
+
const collectCredential = async (notice) => {
|
|
93251
|
+
if (options.onlyConnected || envKey === undefined) {
|
|
93252
|
+
return "ok";
|
|
93253
|
+
}
|
|
93254
|
+
if (notice === undefined) {
|
|
93255
|
+
const existingKey = (options.env ?? process.env)[envKey];
|
|
93256
|
+
const hasOauth = oauthAccessToken(prov.name, options.configDir) !== undefined;
|
|
93257
|
+
if (existingKey !== undefined && existingKey.length > 0 || hasOauth) {
|
|
93258
|
+
return "ok";
|
|
92398
93259
|
}
|
|
92399
|
-
|
|
92400
|
-
|
|
92401
|
-
|
|
92402
|
-
|
|
92403
|
-
|
|
92404
|
-
|
|
92405
|
-
|
|
92406
|
-
|
|
92407
|
-
|
|
92408
|
-
|
|
92409
|
-
|
|
92410
|
-
process.env[envKey] = kr.value;
|
|
92411
|
-
saveApiKey(envKey, kr.value);
|
|
92412
|
-
}
|
|
93260
|
+
}
|
|
93261
|
+
const offered = [];
|
|
93262
|
+
if (catalogAllows(prov.name, "device-code"))
|
|
93263
|
+
offered.push("device-code");
|
|
93264
|
+
if (catalogMethods(prov.name).includes("api-key"))
|
|
93265
|
+
offered.push("api-key");
|
|
93266
|
+
let method = offered.length === 1 ? offered[0] : undefined;
|
|
93267
|
+
if (offered.length > 1) {
|
|
93268
|
+
method = await pickAuthMethodStep(otui, r, label, offered);
|
|
93269
|
+
if (method === undefined) {
|
|
93270
|
+
return "back";
|
|
92413
93271
|
}
|
|
92414
93272
|
}
|
|
93273
|
+
if (method === "device-code") {
|
|
93274
|
+
return await runDeviceLoginInTui(otui, r, prov.name) ? "ok" : "back";
|
|
93275
|
+
}
|
|
93276
|
+
const kr = await promptApiKeyStep(otui, r, {
|
|
93277
|
+
label,
|
|
93278
|
+
envKey,
|
|
93279
|
+
...notice === undefined ? {} : { notice }
|
|
93280
|
+
});
|
|
93281
|
+
if (kr.kind === "back") {
|
|
93282
|
+
return "back";
|
|
93283
|
+
}
|
|
93284
|
+
if (kr.kind === "key") {
|
|
93285
|
+
process.env[envKey] = kr.value;
|
|
93286
|
+
noteSavedCredentialEnv([envKey]);
|
|
93287
|
+
saveApiKey(envKey, kr.value, options.configDir);
|
|
93288
|
+
}
|
|
93289
|
+
return "ok";
|
|
93290
|
+
};
|
|
93291
|
+
if (await collectCredential() === "back") {
|
|
93292
|
+
continue;
|
|
92415
93293
|
}
|
|
92416
|
-
const
|
|
92417
|
-
|
|
93294
|
+
const modelDeps = {
|
|
93295
|
+
...options.fetch === undefined ? {} : { fetch: options.fetch },
|
|
93296
|
+
...options.env === undefined ? {} : { env: options.env }
|
|
93297
|
+
};
|
|
93298
|
+
let models = await modelsForPicker(selectedProvider, modelDeps);
|
|
93299
|
+
if (models.failure?.kind === "rejected") {
|
|
93300
|
+
if (await collectCredential(modelsFailureLine(label, models.failure)) === "back") {
|
|
93301
|
+
continue;
|
|
93302
|
+
}
|
|
93303
|
+
models = await modelsForPicker(selectedProvider, modelDeps);
|
|
93304
|
+
}
|
|
93305
|
+
const model = await pickModelInTui(otui, r, models.models, modelPickerNotice(label, models));
|
|
92418
93306
|
if (model === undefined) {
|
|
92419
93307
|
continue;
|
|
92420
93308
|
}
|
|
@@ -92428,13 +93316,16 @@ function adaptiveSelectHeight(count, available, per = 1) {
|
|
|
92428
93316
|
const min = Math.max(1, Math.floor(available / 4));
|
|
92429
93317
|
return Math.min(available, Math.max(min, count * per));
|
|
92430
93318
|
}
|
|
92431
|
-
function pickModelInTui(otui, r, models) {
|
|
93319
|
+
function pickModelInTui(otui, r, models, notice) {
|
|
92432
93320
|
return new Promise((resolve) => {
|
|
92433
93321
|
const all = models;
|
|
92434
|
-
const NO_MODELS = "(no models found)";
|
|
93322
|
+
const NO_MODELS = notice ?? "(no models found)";
|
|
92435
93323
|
const box = overlayBox(otui, r, "model-picker");
|
|
92436
93324
|
r.root.add(box);
|
|
92437
93325
|
box.add(new otui.TextRenderable(r, { id: "mp-title", content: otui.t`${otui.bold("Select a model")}` }));
|
|
93326
|
+
if (all.length === 0 && notice !== undefined) {
|
|
93327
|
+
box.add(new otui.TextRenderable(r, { id: "mp-notice", content: otui.t`${otui.red("\u2717")} ${notice}` }));
|
|
93328
|
+
}
|
|
92438
93329
|
const filterLine = new otui.TextRenderable(r, { id: "mp-filter", content: otui.t`${otui.dim("type to filter \xB7 \u2191/\u2193 Enter \xB7 Esc to go back")}` });
|
|
92439
93330
|
box.add(filterLine);
|
|
92440
93331
|
const NO_MATCH = "(no match)";
|
|
@@ -94530,8 +95421,9 @@ ${formatThemeList(getThemeId())}`);
|
|
|
94530
95421
|
(async () => {
|
|
94531
95422
|
const detected = opts.redetect !== undefined ? await opts.redetect() : opts.detected;
|
|
94532
95423
|
const prov = detected.find((d) => d.name === currentSel.provider);
|
|
94533
|
-
const models = prov !== undefined ? await modelsForPicker(prov) :
|
|
94534
|
-
const
|
|
95424
|
+
const models = prov !== undefined ? await modelsForPicker(prov) : undefined;
|
|
95425
|
+
const notice = prov === undefined || models === undefined ? undefined : modelPickerNotice(prov.label ?? prov.name, models);
|
|
95426
|
+
const chosen = await chrome.withOverlay(() => pickModelInTui(otui, r, models?.models ?? [], notice));
|
|
94535
95427
|
if (chosen !== undefined) {
|
|
94536
95428
|
await switchTo(currentSel.baseUrl === undefined ? { provider: currentSel.provider, model: chosen } : { provider: currentSel.provider, model: chosen, baseUrl: currentSel.baseUrl });
|
|
94537
95429
|
try {
|
|
@@ -95206,7 +96098,8 @@ async function launchTuiChatShell(opts) {
|
|
|
95206
96098
|
return await selectProviderModelInTui(otui, r, detected);
|
|
95207
96099
|
}
|
|
95208
96100
|
const prov = detected.find((d) => d.name === only);
|
|
95209
|
-
const
|
|
96101
|
+
const models = prov === undefined ? undefined : await modelsForPicker(prov);
|
|
96102
|
+
const chosen = await pickModelInTui(otui, r, models?.models ?? [], prov === undefined || models === undefined ? undefined : modelPickerNotice(prov.label ?? prov.name, models));
|
|
95210
96103
|
if (chosen === undefined) {
|
|
95211
96104
|
return;
|
|
95212
96105
|
}
|
|
@@ -95596,17 +96489,19 @@ function oauthCredentialsFor(name) {
|
|
|
95596
96489
|
}
|
|
95597
96490
|
function realSelectProviderModel(baseUrl) {
|
|
95598
96491
|
return async (io, opts) => {
|
|
96492
|
+
const env = envWithSavedApiKeys(process.env);
|
|
95599
96493
|
const detected = await detectProviders({
|
|
95600
96494
|
fetch: globalThis.fetch,
|
|
95601
|
-
env
|
|
96495
|
+
env,
|
|
95602
96496
|
platform: process.platform,
|
|
95603
96497
|
...baseUrl !== undefined ? { baseUrl } : {}
|
|
95604
96498
|
});
|
|
95605
96499
|
const filtered = opts?.onlyProvider !== undefined ? detected.filter((d) => d.name === opts.onlyProvider) : detected;
|
|
95606
96500
|
const list = filtered.length > 0 ? filtered : detected;
|
|
95607
|
-
return pickProviderModel(io, list, { fetch: globalThis.fetch, env
|
|
96501
|
+
return pickProviderModel(io, list, { fetch: globalThis.fetch, env });
|
|
95608
96502
|
};
|
|
95609
96503
|
}
|
|
96504
|
+
var GRANT_REFRESH_TIMEOUT_MS = 5000;
|
|
95610
96505
|
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
95611
96506
|
var PROMPT_MARK = "\u276F ";
|
|
95612
96507
|
var GUTTER = " ";
|
|
@@ -96480,14 +97375,6 @@ ${GUTTER}${turnSeparator()}
|
|
|
96480
97375
|
}
|
|
96481
97376
|
async function resolveTuiStartup(opts) {
|
|
96482
97377
|
const savedCfg = loadShellConfig(opts.configDir);
|
|
96483
|
-
await Promise.resolve().then(() => init_login());
|
|
96484
|
-
const oauthFetch = (input, init) => globalThis.fetch(input, init);
|
|
96485
|
-
await refreshProviderGrant("grok", { fetch: oauthFetch }, opts.configDir).catch(() => {
|
|
96486
|
-
return;
|
|
96487
|
-
});
|
|
96488
|
-
await refreshProviderGrant("github-copilot", { fetch: oauthFetch }, opts.configDir).catch(() => {
|
|
96489
|
-
return;
|
|
96490
|
-
});
|
|
96491
97378
|
const appliedKeys = applySavedApiKeys(opts.configDir);
|
|
96492
97379
|
const { providerArg, modelArg, baseUrl } = opts;
|
|
96493
97380
|
if (providerArg !== undefined && modelArg !== undefined) {
|
|
@@ -96666,6 +97553,15 @@ Example: keryx shell --provider ollama --model llama3.1:latest`);
|
|
|
96666
97553
|
const modelArg = flags.modelArg;
|
|
96667
97554
|
let baseUrl = flags.baseUrl;
|
|
96668
97555
|
let modeFlag = flags.modeFlag;
|
|
97556
|
+
{
|
|
97557
|
+
await Promise.resolve().then(() => init_login());
|
|
97558
|
+
const oauthFetch = (input, init) => globalThis.fetch(input, init);
|
|
97559
|
+
const http = { fetch: oauthFetch, signal: AbortSignal.timeout(GRANT_REFRESH_TIMEOUT_MS) };
|
|
97560
|
+
const warnings = await refreshSavedGrants(http, runtime.cacheDir, providerArg === undefined ? undefined : [providerArg]);
|
|
97561
|
+
for (const warning of warnings)
|
|
97562
|
+
process.stderr.write(`keryx: ${warning}
|
|
97563
|
+
`);
|
|
97564
|
+
}
|
|
96669
97565
|
const surface = chooseShellSurface(flags, runtime.isTty ?? process.stdout.isTTY === true);
|
|
96670
97566
|
if (surface !== "readline") {
|
|
96671
97567
|
const cwd = process.cwd();
|
|
@@ -97125,7 +98021,7 @@ Shell:
|
|
|
97125
98021
|
init_fs();
|
|
97126
98022
|
import { readFile as readFile98 } from "fs/promises";
|
|
97127
98023
|
import { stdin } from "process";
|
|
97128
|
-
import
|
|
98024
|
+
import path191 from "path";
|
|
97129
98025
|
var MODULES = [
|
|
97130
98026
|
{ name: "gdgraph", flag: "--no-gdgraph", desc: "code graph, symbols, affected context", defaultEnabled: true },
|
|
97131
98027
|
{ name: "gdctx", flag: "--no-gdctx", desc: "token-aware command/read output", defaultEnabled: true },
|
|
@@ -97171,8 +98067,8 @@ async function modulesCommand(args = []) {
|
|
|
97171
98067
|
return;
|
|
97172
98068
|
}
|
|
97173
98069
|
const wantsJson = args.includes("--json") && (sub === undefined || sub === "status" || sub === "list" || sub === "--json");
|
|
97174
|
-
const metaprojectRoot =
|
|
97175
|
-
const manifestPath =
|
|
98070
|
+
const metaprojectRoot = path191.join(process.cwd(), ".metaproject");
|
|
98071
|
+
const manifestPath = path191.join(metaprojectRoot, "metaproject.json");
|
|
97176
98072
|
if (!await pathExists(manifestPath)) {
|
|
97177
98073
|
if (wantsJson) {
|
|
97178
98074
|
console.log(JSON.stringify({ schemaVersion: 1, error: "not-initialized", modules: [] }, null, 2));
|
|
@@ -97291,7 +98187,7 @@ init_serve_config();
|
|
|
97291
98187
|
|
|
97292
98188
|
// src/lib/serve-credential.ts
|
|
97293
98189
|
init_config_dir();
|
|
97294
|
-
import { createHash as
|
|
98190
|
+
import { createHash as createHash44, randomBytes as randomBytes5, randomUUID as randomUUID30 } from "crypto";
|
|
97295
98191
|
import {
|
|
97296
98192
|
chmodSync as chmodSync4,
|
|
97297
98193
|
closeSync as closeSync3,
|
|
@@ -97303,9 +98199,9 @@ import {
|
|
|
97303
98199
|
unlinkSync as unlinkSync3,
|
|
97304
98200
|
writeFileSync as writeFileSync9
|
|
97305
98201
|
} from "fs";
|
|
97306
|
-
import
|
|
98202
|
+
import path192 from "path";
|
|
97307
98203
|
function serveCredentialPath(dir) {
|
|
97308
|
-
return
|
|
98204
|
+
return path192.join(keryxConfigDir(dir), "serve-credentials.json");
|
|
97309
98205
|
}
|
|
97310
98206
|
function constantTimeEqual(a, b) {
|
|
97311
98207
|
const width = Math.max(a.length, b.length);
|
|
@@ -97318,10 +98214,10 @@ function constantTimeEqual(a, b) {
|
|
|
97318
98214
|
return difference === 0;
|
|
97319
98215
|
}
|
|
97320
98216
|
function hashToken(salt, token) {
|
|
97321
|
-
return
|
|
98217
|
+
return createHash44("sha256").update(Buffer.from(salt, "hex")).update(Buffer.from(token, "utf8")).digest("hex");
|
|
97322
98218
|
}
|
|
97323
98219
|
function credentialFingerprint(record) {
|
|
97324
|
-
return
|
|
98220
|
+
return createHash44("sha256").update(`keryx-serve-fingerprint:${record.hash}`).digest("hex").slice(0, 8);
|
|
97325
98221
|
}
|
|
97326
98222
|
function isValidRecord(value) {
|
|
97327
98223
|
if (typeof value !== "object" || value === null) {
|
|
@@ -97411,8 +98307,8 @@ function withServeCredentialLock(dir, fn, onWaiting) {
|
|
|
97411
98307
|
});
|
|
97412
98308
|
}
|
|
97413
98309
|
function mintRecord(now) {
|
|
97414
|
-
const token =
|
|
97415
|
-
const salt =
|
|
98310
|
+
const token = randomBytes5(32).toString("base64url");
|
|
98311
|
+
const salt = randomBytes5(32).toString("hex");
|
|
97416
98312
|
return {
|
|
97417
98313
|
token,
|
|
97418
98314
|
record: { id: randomUUID30(), algorithm: "sha256", salt, hash: hashToken(salt, token), createdAt: now }
|
|
@@ -97537,24 +98433,24 @@ class AuthFailureThrottle {
|
|
|
97537
98433
|
|
|
97538
98434
|
// src/lib/serve-turn-store.ts
|
|
97539
98435
|
init_config_dir();
|
|
97540
|
-
import { createHash as
|
|
98436
|
+
import { createHash as createHash45 } from "crypto";
|
|
97541
98437
|
import { existsSync as existsSync34, readdirSync as readdirSync3, rmSync as rmSync2 } from "fs";
|
|
97542
|
-
import
|
|
98438
|
+
import path193 from "path";
|
|
97543
98439
|
var MAX_TURN_EVENTS = 1e4;
|
|
97544
98440
|
function turnsRoot(dir) {
|
|
97545
|
-
return
|
|
98441
|
+
return path193.join(keryxConfigDir(dir), "turns");
|
|
97546
98442
|
}
|
|
97547
98443
|
function turnDir(turnId, dir) {
|
|
97548
|
-
return
|
|
98444
|
+
return path193.join(turnsRoot(dir), turnId);
|
|
97549
98445
|
}
|
|
97550
98446
|
function keyPath(project, idempotencyKey, dir) {
|
|
97551
98447
|
const projectBytes = Buffer.byteLength(project, "utf8");
|
|
97552
|
-
const digest =
|
|
97553
|
-
return
|
|
98448
|
+
const digest = createHash45("sha256").update(`${projectBytes}:${project}\x00${idempotencyKey}`, "utf8").digest("hex");
|
|
98449
|
+
return path193.join(turnsRoot(dir), "keys", `${digest}.json`);
|
|
97554
98450
|
}
|
|
97555
98451
|
function legacyKeyPath(idempotencyKey, dir) {
|
|
97556
|
-
const digest =
|
|
97557
|
-
return
|
|
98452
|
+
const digest = createHash45("sha256").update(idempotencyKey, "utf8").digest("hex");
|
|
98453
|
+
return path193.join(turnsRoot(dir), "keys", `${digest}.json`);
|
|
97558
98454
|
}
|
|
97559
98455
|
function adoptLegacyClaim(project, idempotencyKey, dir) {
|
|
97560
98456
|
const legacy = legacyKeyPath(idempotencyKey, dir);
|
|
@@ -97618,7 +98514,7 @@ function ensureTurnDir(turnId, dir) {
|
|
|
97618
98514
|
}
|
|
97619
98515
|
function createTurnRecord(record, dir) {
|
|
97620
98516
|
ensureTurnDir(record.turnId, dir);
|
|
97621
|
-
writeOwnerOnlyFile(
|
|
98517
|
+
writeOwnerOnlyFile(path193.join(turnDir(record.turnId, dir), "turn.json"), `${JSON.stringify(record, null, 2)}
|
|
97622
98518
|
`);
|
|
97623
98519
|
}
|
|
97624
98520
|
function appendTurnEvent(event, dir, opts) {
|
|
@@ -97627,12 +98523,12 @@ function appendTurnEvent(event, dir, opts) {
|
|
|
97627
98523
|
}
|
|
97628
98524
|
const line = JSON.stringify(event);
|
|
97629
98525
|
try {
|
|
97630
|
-
appendOwnerOnlyLine(
|
|
98526
|
+
appendOwnerOnlyLine(path193.join(turnDir(event.turnId, dir), "events.jsonl"), line);
|
|
97631
98527
|
} catch (error) {
|
|
97632
98528
|
if (error?.code !== "ENOENT") {
|
|
97633
98529
|
throw error;
|
|
97634
98530
|
}
|
|
97635
|
-
appendOwnerOnlyLine(
|
|
98531
|
+
appendOwnerOnlyLine(path193.join(ensureTurnDir(event.turnId, dir), "events.jsonl"), line);
|
|
97636
98532
|
}
|
|
97637
98533
|
return true;
|
|
97638
98534
|
}
|
|
@@ -97640,7 +98536,7 @@ function readTurnEvents(turnId, after = -1, dir) {
|
|
|
97640
98536
|
if (!isTurnId(turnId)) {
|
|
97641
98537
|
return { ok: false, reason: "not-a-turn-id" };
|
|
97642
98538
|
}
|
|
97643
|
-
const read = readTurnFile(
|
|
98539
|
+
const read = readTurnFile(path193.join(turnDir(turnId, dir), "events.jsonl"));
|
|
97644
98540
|
if (!read.ok) {
|
|
97645
98541
|
if (isDefiniteAbsence2(read.reason)) {
|
|
97646
98542
|
return { ok: true, value: [] };
|
|
@@ -97668,7 +98564,7 @@ function readTurnRecord(turnId, dir) {
|
|
|
97668
98564
|
if (!isTurnId(turnId)) {
|
|
97669
98565
|
return { ok: false, reason: "not-a-turn-id" };
|
|
97670
98566
|
}
|
|
97671
|
-
const read = readTurnFile(
|
|
98567
|
+
const read = readTurnFile(path193.join(turnDir(turnId, dir), "turn.json"));
|
|
97672
98568
|
if (!read.ok) {
|
|
97673
98569
|
return { ok: false, reason: read.reason };
|
|
97674
98570
|
}
|
|
@@ -97687,7 +98583,7 @@ function finishTurn(turnId, result, dir) {
|
|
|
97687
98583
|
if (!record.ok) {
|
|
97688
98584
|
return false;
|
|
97689
98585
|
}
|
|
97690
|
-
writeOwnerOnlyFile(
|
|
98586
|
+
writeOwnerOnlyFile(path193.join(turnDir(turnId, dir), "turn.json"), `${JSON.stringify({ ...record.value, result }, null, 2)}
|
|
97691
98587
|
`);
|
|
97692
98588
|
return true;
|
|
97693
98589
|
}
|
|
@@ -97733,7 +98629,7 @@ function releaseIdempotencyKey(project, idempotencyKey, turnId, dir) {
|
|
|
97733
98629
|
|
|
97734
98630
|
// src/lib/serve-turn.ts
|
|
97735
98631
|
import { randomUUID as randomUUID31 } from "crypto";
|
|
97736
|
-
import
|
|
98632
|
+
import path194 from "path";
|
|
97737
98633
|
init_service();
|
|
97738
98634
|
var REMOTE_ORIGIN = "remote:http";
|
|
97739
98635
|
var MAX_PROMPT_CHARS = 32000;
|
|
@@ -97802,9 +98698,9 @@ function isUuid(value) {
|
|
|
97802
98698
|
return typeof value === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(value);
|
|
97803
98699
|
}
|
|
97804
98700
|
function resolveProject(declared, dir) {
|
|
97805
|
-
const wanted =
|
|
98701
|
+
const wanted = path194.resolve(declared);
|
|
97806
98702
|
for (const entry of listProjects(dir, () => {})) {
|
|
97807
|
-
if (
|
|
98703
|
+
if (path194.resolve(entry.path) === wanted) {
|
|
97808
98704
|
return { ok: true, project: entry.path };
|
|
97809
98705
|
}
|
|
97810
98706
|
}
|
|
@@ -98840,7 +99736,7 @@ function printHelp19() {
|
|
|
98840
99736
|
import { spawn as spawn6 } from "child_process";
|
|
98841
99737
|
import { chmod as chmod4, mkdir as mkdir62, readFile as readFile99, readdir as readdir35, writeFile as writeFile54 } from "fs/promises";
|
|
98842
99738
|
import { access as access4, constants as constants2, existsSync as existsSync35 } from "fs";
|
|
98843
|
-
import
|
|
99739
|
+
import path195 from "path";
|
|
98844
99740
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
98845
99741
|
init_config2();
|
|
98846
99742
|
init_config();
|
|
@@ -98857,8 +99753,8 @@ async function updateCommand(args = []) {
|
|
|
98857
99753
|
return;
|
|
98858
99754
|
}
|
|
98859
99755
|
const projectRoot = process.cwd();
|
|
98860
|
-
const metaprojectRoot =
|
|
98861
|
-
banner("keryx update", `Refreshing the .metaproject workspace in ${
|
|
99756
|
+
const metaprojectRoot = path195.join(projectRoot, ".metaproject");
|
|
99757
|
+
banner("keryx update", `Refreshing the .metaproject workspace in ${path195.basename(projectRoot)}/`);
|
|
98862
99758
|
if (!await pathExists(metaprojectRoot)) {
|
|
98863
99759
|
console.log(` ${style.red(symbols.cross)} Metaproject is not initialized.`);
|
|
98864
99760
|
console.log(` ${style.cyan(symbols.arrow)} Run ${style.cyan("keryx init")} first.`);
|
|
@@ -98909,6 +99805,12 @@ async function updateCommand(args = []) {
|
|
|
98909
99805
|
if (summary.modules.sac) {
|
|
98910
99806
|
statusLine("sac", true, "shared agent context: cross-session workspace propose/review (opt-in)");
|
|
98911
99807
|
}
|
|
99808
|
+
if (summary.gdskillsWarnings.length > 0) {
|
|
99809
|
+
heading("Warnings");
|
|
99810
|
+
for (const warning of summary.gdskillsWarnings) {
|
|
99811
|
+
note(warning);
|
|
99812
|
+
}
|
|
99813
|
+
}
|
|
98912
99814
|
const steps = [];
|
|
98913
99815
|
if (options.hooks) {
|
|
98914
99816
|
await runPostUpdateHooks(projectRoot);
|
|
@@ -98919,7 +99821,7 @@ async function updateCommand(args = []) {
|
|
|
98919
99821
|
nextSteps(steps);
|
|
98920
99822
|
}
|
|
98921
99823
|
async function previewServiceFiles(projectRoot, options) {
|
|
98922
|
-
const metaprojectRoot =
|
|
99824
|
+
const metaprojectRoot = path195.join(projectRoot, ".metaproject");
|
|
98923
99825
|
const manifestState = await readManifest5(metaprojectRoot);
|
|
98924
99826
|
const manifest = manifestState.manifest;
|
|
98925
99827
|
const enableTasks = moduleEnabled2(manifest, "tasks") || !options.noTasks;
|
|
@@ -98948,12 +99850,12 @@ async function previewServiceFiles(projectRoot, options) {
|
|
|
98948
99850
|
});
|
|
98949
99851
|
}
|
|
98950
99852
|
async function refreshServiceFiles(projectRoot, options) {
|
|
98951
|
-
const metaprojectRoot =
|
|
99853
|
+
const metaprojectRoot = path195.join(projectRoot, ".metaproject");
|
|
98952
99854
|
const manifestState = await readManifest5(metaprojectRoot);
|
|
98953
99855
|
const manifest = manifestState.manifest;
|
|
98954
99856
|
const recoveredManifest = !manifestState.exists || !manifestState.valid;
|
|
98955
99857
|
if (manifestState.migrated) {
|
|
98956
|
-
await writeFile54(
|
|
99858
|
+
await writeFile54(path195.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
|
|
98957
99859
|
`, "utf8");
|
|
98958
99860
|
}
|
|
98959
99861
|
const enableGdgraph = moduleEnabled2(manifest, "gdgraph");
|
|
@@ -98990,9 +99892,9 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
98990
99892
|
enableSecurity,
|
|
98991
99893
|
enableSac
|
|
98992
99894
|
});
|
|
98993
|
-
await writeTextIfChanged4(
|
|
98994
|
-
await writeTextIfChanged4(
|
|
98995
|
-
await writeTextIfChanged4(
|
|
99895
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "core", "README.md"), renderMetaprojectCoreReadme());
|
|
99896
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "hooks", "README.md"), renderHooksReadme());
|
|
99897
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "skills", "project-rules", "README.md"), renderProjectRulesSkillReadme({ sources: ruleSources }));
|
|
98996
99898
|
await writeRoutingEntrypointPair(metaprojectRoot, {
|
|
98997
99899
|
enableGdgraph,
|
|
98998
99900
|
enableGdctx,
|
|
@@ -99015,7 +99917,7 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
99015
99917
|
}
|
|
99016
99918
|
}
|
|
99017
99919
|
});
|
|
99018
|
-
await writeTextIfChanged4(
|
|
99920
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "keryx-dashboard.html"), renderMetaprojectDashboardHtml({
|
|
99019
99921
|
enableGdgraph,
|
|
99020
99922
|
enableGdctx,
|
|
99021
99923
|
enableGdwiki,
|
|
@@ -99027,7 +99929,7 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
99027
99929
|
enableSecurity,
|
|
99028
99930
|
data: dashboardData
|
|
99029
99931
|
}));
|
|
99030
|
-
await writeTextIfMissing4(
|
|
99932
|
+
await writeTextIfMissing4(path195.join(metaprojectRoot, "README.md"), renderMetaprojectReadme({
|
|
99031
99933
|
enableGdgraph,
|
|
99032
99934
|
enableGdctx,
|
|
99033
99935
|
enableGdwiki,
|
|
@@ -99040,58 +99942,60 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
99040
99942
|
}));
|
|
99041
99943
|
if (enableGdgraph) {
|
|
99042
99944
|
await installGdgraphCoreScripts2(metaprojectRoot);
|
|
99043
|
-
await writeTextIfChanged4(
|
|
99044
|
-
await writeTextIfChanged4(
|
|
99045
|
-
await writeTextIfChanged4(
|
|
99945
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "modules", "gdgraph.md"), renderGdgraphManifest());
|
|
99946
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "core", "gdgraph", "README.md"), renderGdgraphCoreReadme());
|
|
99947
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "skills", "gdgraph", "SKILL.md"), renderGdgraphSkillReadme());
|
|
99046
99948
|
await seedAssetsLock(metaprojectRoot);
|
|
99047
99949
|
if (manifest.modules?.gdgraph?.hooks?.gitPostCommit) {
|
|
99048
99950
|
await installManagedHook2(projectRoot, "post-commit", "gdgraph-post-commit", renderGdgraphPostCommitHook());
|
|
99049
99951
|
}
|
|
99050
99952
|
}
|
|
99051
99953
|
if (enableGdctx) {
|
|
99052
|
-
await writeTextIfMissing4(
|
|
99053
|
-
await writeTextIfChanged4(
|
|
99054
|
-
await writeTextIfChanged4(
|
|
99055
|
-
await writeTextIfChanged4(
|
|
99954
|
+
await writeTextIfMissing4(path195.join(metaprojectRoot, "gdctx.config.json"), renderGdctxConfig());
|
|
99955
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "modules", "gdctx.md"), renderGdctxManifest());
|
|
99956
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "core", "gdctx", "README.md"), renderGdctxCoreReadme());
|
|
99957
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "skills", "gdctx", "SKILL.md"), renderGdctxSkillReadme());
|
|
99056
99958
|
}
|
|
99057
99959
|
if (enableGdwiki) {
|
|
99058
|
-
await writeTextIfMissing4(
|
|
99059
|
-
await writeTextIfChanged4(
|
|
99060
|
-
await writeTextIfChanged4(
|
|
99960
|
+
await writeTextIfMissing4(path195.join(metaprojectRoot, "wiki", "templates", "page.md"), renderWikiPageTemplate());
|
|
99961
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "modules", "gdwiki.md"), renderGdwikiManifest());
|
|
99962
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "skills", "gdwiki", "SKILL.md"), renderGdwikiSkillReadme());
|
|
99061
99963
|
if (manifest.modules?.gdgraph?.hooks?.gitPostCommit) {
|
|
99062
99964
|
await installManagedHook2(projectRoot, "post-commit", "gdwiki-post-commit", renderGdwikiPostCommitHook());
|
|
99063
99965
|
}
|
|
99064
99966
|
}
|
|
99065
99967
|
if (enableSac) {
|
|
99066
|
-
await writeTextIfMissing4(
|
|
99067
|
-
await writeTextIfChanged4(
|
|
99968
|
+
await writeTextIfMissing4(path195.join(metaprojectRoot, "modules", "sac.md"), renderSacManifest());
|
|
99969
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "skills", "sac", "SKILL.md"), renderSacSkillReadme());
|
|
99068
99970
|
}
|
|
99971
|
+
let gdskillsWarnings = [];
|
|
99069
99972
|
if (enableGdskills) {
|
|
99070
|
-
await installGdskills(metaprojectRoot, gdskillsProfile, { createDataDirs: false });
|
|
99973
|
+
const gdskillsInstallResult = await installGdskills(metaprojectRoot, gdskillsProfile, { createDataDirs: false });
|
|
99974
|
+
gdskillsWarnings = gdskillsInstallResult.warnings;
|
|
99071
99975
|
if (manifest.modules?.gdskills?.hooks?.gitPostCommit) {
|
|
99072
99976
|
await installManagedHook2(projectRoot, "post-commit", "gdskills-post-commit", renderGdskillsPostCommitHook());
|
|
99073
99977
|
}
|
|
99074
99978
|
}
|
|
99075
99979
|
if (enableHealth) {
|
|
99076
|
-
await writeTextIfMissing4(
|
|
99077
|
-
await writeTextIfChanged4(
|
|
99078
|
-
await writeTextIfChanged4(
|
|
99079
|
-
await writeTextIfChanged4(
|
|
99980
|
+
await writeTextIfMissing4(path195.join(metaprojectRoot, "health.config.json"), renderHealthConfig());
|
|
99981
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "modules", "health.md"), renderHealthManifest());
|
|
99982
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "core", "health", "README.md"), renderHealthCoreReadme());
|
|
99983
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "skills", "health", "SKILL.md"), renderHealthSkillReadme());
|
|
99080
99984
|
if (manifest.modules?.health?.hooks?.gitPostCommit) {
|
|
99081
99985
|
await installManagedHook2(projectRoot, "post-commit", "health-post-commit", renderHealthPostCommitHook());
|
|
99082
99986
|
}
|
|
99083
99987
|
}
|
|
99084
99988
|
if (enableTesting) {
|
|
99085
|
-
await writeTextIfMissing4(
|
|
99989
|
+
await writeTextIfMissing4(path195.join(metaprojectRoot, "testing.config.json"), renderTestingConfig({
|
|
99086
99990
|
postCommitRefresh: Boolean(manifest.modules?.testing?.hooks?.gitPostCommit),
|
|
99087
99991
|
prePushGate: Boolean(manifest.modules?.testing?.hooks?.prePush)
|
|
99088
99992
|
}));
|
|
99089
|
-
await writeTextIfChanged4(
|
|
99090
|
-
await writeTextIfChanged4(
|
|
99091
|
-
await writeTextIfChanged4(
|
|
99993
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "modules", "testing.md"), renderTestingManifest());
|
|
99994
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "core", "testing", "README.md"), renderTestingCoreReadme());
|
|
99995
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "skills", "testing", "SKILL.md"), renderTestingSkillReadme());
|
|
99092
99996
|
if (enableGdwiki) {
|
|
99093
|
-
await writeTextIfMissing4(
|
|
99094
|
-
await writeTextIfMissing4(
|
|
99997
|
+
await writeTextIfMissing4(path195.join(metaprojectRoot, "wiki", "testing", "README.md"), renderTestingWikiReadme());
|
|
99998
|
+
await writeTextIfMissing4(path195.join(metaprojectRoot, "wiki", "testing", "conventions.md"), renderTestingWikiConventions());
|
|
99095
99999
|
}
|
|
99096
100000
|
if (manifest.modules?.testing?.hooks?.gitPostCommit) {
|
|
99097
100001
|
await installManagedHook2(projectRoot, "post-commit", "testing-post-commit", renderTestingPostCommitHook());
|
|
@@ -99104,24 +100008,24 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
99104
100008
|
await installManagedHook2(projectRoot, "post-commit", "metaproject-dashboard-post-commit", renderMetaprojectDashboardPostCommitHook());
|
|
99105
100009
|
}
|
|
99106
100010
|
if (enableMemory) {
|
|
99107
|
-
await writeTextIfMissing4(
|
|
99108
|
-
await writeTextIfMissing4(
|
|
99109
|
-
await writeTextIfChanged4(
|
|
99110
|
-
await writeTextIfChanged4(
|
|
99111
|
-
await writeTextIfChanged4(
|
|
100011
|
+
await writeTextIfMissing4(path195.join(metaprojectRoot, "memory.config.json"), renderMemoryConfig());
|
|
100012
|
+
await writeTextIfMissing4(path195.join(metaprojectRoot, "memory", "templates", "entry.md"), renderMemoryEntryTemplate());
|
|
100013
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "modules", "memory.md"), renderMemoryManifest());
|
|
100014
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "core", "memory", "README.md"), renderMemoryCoreReadme());
|
|
100015
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "skills", "memory", "SKILL.md"), renderMemorySkillReadme());
|
|
99112
100016
|
}
|
|
99113
100017
|
if (enableTasks) {
|
|
99114
|
-
await writeTextIfChanged4(
|
|
99115
|
-
await writeTextIfChanged4(
|
|
99116
|
-
await writeTextIfChanged4(
|
|
99117
|
-
await writeTextIfChanged4(
|
|
99118
|
-
await writeTextIfChanged4(
|
|
99119
|
-
await writeTextIfChanged4(
|
|
100018
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "flows", "README.md"), renderFlowsReadme());
|
|
100019
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "modules", "tasks.md"), renderTasksManifest());
|
|
100020
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "skills", "flow", "SKILL.md"), renderFlowSkillRouter());
|
|
100021
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "skills", "flow", "init.md"), renderFlowInitSkill());
|
|
100022
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "skills", "flow", "manage.md"), renderFlowManageSkill());
|
|
100023
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "skills", "flow", "complete.md"), renderFlowCompleteSkill());
|
|
99120
100024
|
}
|
|
99121
100025
|
if (enableSecurity) {
|
|
99122
|
-
await writeTextIfMissing4(
|
|
99123
|
-
await writeTextIfChanged4(
|
|
99124
|
-
await writeTextIfChanged4(
|
|
100026
|
+
await writeTextIfMissing4(path195.join(metaprojectRoot, "security.config.json"), renderSecurityConfig());
|
|
100027
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "modules", "security.md"), renderSecurityManifest());
|
|
100028
|
+
await writeTextIfChanged4(path195.join(metaprojectRoot, "core", "security", "README.md"), renderSecurityCoreReadme());
|
|
99125
100029
|
if (manifest.modules?.security?.hooks?.prePush) {
|
|
99126
100030
|
await installManagedHook2(projectRoot, "pre-push", "security-pre-push", renderSecurityPrePushHook());
|
|
99127
100031
|
}
|
|
@@ -99167,18 +100071,19 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
99167
100071
|
sac: enableSac
|
|
99168
100072
|
},
|
|
99169
100073
|
gdskillsProfile,
|
|
100074
|
+
gdskillsWarnings,
|
|
99170
100075
|
backfilledTasks: backfillTasks,
|
|
99171
100076
|
recoveredManifest
|
|
99172
100077
|
};
|
|
99173
100078
|
}
|
|
99174
100079
|
async function buildDashboard(projectRoot = process.cwd()) {
|
|
99175
|
-
const metaprojectRoot =
|
|
100080
|
+
const metaprojectRoot = path195.join(projectRoot, ".metaproject");
|
|
99176
100081
|
if (!await pathExists(metaprojectRoot)) {
|
|
99177
100082
|
throw new Error("Metaproject is not initialized. Run: keryx init");
|
|
99178
100083
|
}
|
|
99179
100084
|
const manifest = (await readManifest5(metaprojectRoot)).manifest;
|
|
99180
100085
|
const data = await collectDashboardData(metaprojectRoot);
|
|
99181
|
-
const dashboardPath =
|
|
100086
|
+
const dashboardPath = path195.join(metaprojectRoot, "keryx-dashboard.html");
|
|
99182
100087
|
await writeTextIfChanged4(dashboardPath, renderMetaprojectDashboardHtml({
|
|
99183
100088
|
enableGdgraph: moduleEnabled2(manifest, "gdgraph"),
|
|
99184
100089
|
enableGdctx: moduleEnabled2(manifest, "gdctx"),
|
|
@@ -99198,7 +100103,7 @@ async function shouldInstallDashboardPostCommitHook(projectRoot, manifest) {
|
|
|
99198
100103
|
if (Object.values(modules).some((module) => Boolean(module.hooks?.gitPostCommit))) {
|
|
99199
100104
|
return true;
|
|
99200
100105
|
}
|
|
99201
|
-
const hookPath =
|
|
100106
|
+
const hookPath = path195.join(projectRoot, ".git", "hooks", "post-commit");
|
|
99202
100107
|
if (!await pathExists(hookPath)) {
|
|
99203
100108
|
return false;
|
|
99204
100109
|
}
|
|
@@ -99218,11 +100123,11 @@ async function collectDashboardData(metaprojectRoot) {
|
|
|
99218
100123
|
if (testing) {
|
|
99219
100124
|
data.testing = testing;
|
|
99220
100125
|
}
|
|
99221
|
-
const wiki = await collectMarkdownPages(
|
|
100126
|
+
const wiki = await collectMarkdownPages(path195.join(metaprojectRoot, "wiki"), "wiki");
|
|
99222
100127
|
if (wiki.length > 0) {
|
|
99223
100128
|
data.wiki = { pages: wiki };
|
|
99224
100129
|
}
|
|
99225
|
-
const memory = await collectMarkdownPages(
|
|
100130
|
+
const memory = await collectMarkdownPages(path195.join(metaprojectRoot, "memory"), "memory");
|
|
99226
100131
|
if (memory.length > 0) {
|
|
99227
100132
|
data.memory = { entries: memory };
|
|
99228
100133
|
}
|
|
@@ -99237,7 +100142,7 @@ async function collectDashboardData(metaprojectRoot) {
|
|
|
99237
100142
|
return data;
|
|
99238
100143
|
}
|
|
99239
100144
|
async function collectTasksDashboardData(metaprojectRoot) {
|
|
99240
|
-
const flowsRoot =
|
|
100145
|
+
const flowsRoot = path195.join(metaprojectRoot, "flows");
|
|
99241
100146
|
if (!await pathExists(flowsRoot)) {
|
|
99242
100147
|
return null;
|
|
99243
100148
|
}
|
|
@@ -99249,7 +100154,7 @@ async function collectTasksDashboardData(metaprojectRoot) {
|
|
|
99249
100154
|
}
|
|
99250
100155
|
const flows = [];
|
|
99251
100156
|
for (const dir of dirEntries) {
|
|
99252
|
-
const flowPath =
|
|
100157
|
+
const flowPath = path195.join(flowsRoot, dir, "flow.json");
|
|
99253
100158
|
if (!await pathExists(flowPath)) {
|
|
99254
100159
|
continue;
|
|
99255
100160
|
}
|
|
@@ -99257,7 +100162,7 @@ async function collectTasksDashboardData(metaprojectRoot) {
|
|
|
99257
100162
|
const flow = JSON.parse(await readFile99(flowPath, "utf8"));
|
|
99258
100163
|
const tasks = Array.isArray(flow.tasks) ? flow.tasks : [];
|
|
99259
100164
|
let acTotal = 0;
|
|
99260
|
-
const acPath =
|
|
100165
|
+
const acPath = path195.join(flowsRoot, dir, "acceptance-criteria.md");
|
|
99261
100166
|
if (await pathExists(acPath)) {
|
|
99262
100167
|
const acContent = await readFile99(acPath, "utf8");
|
|
99263
100168
|
acTotal = (acContent.match(/^- AC\d+:/gm) ?? []).length;
|
|
@@ -99311,7 +100216,7 @@ async function collectDashboardDocs(metaprojectRoot, wiki, memory) {
|
|
|
99311
100216
|
"data/testing/context.md"
|
|
99312
100217
|
];
|
|
99313
100218
|
for (const href of staticHrefs) {
|
|
99314
|
-
const filePath =
|
|
100219
|
+
const filePath = path195.join(metaprojectRoot, ...href.split("/"));
|
|
99315
100220
|
if (!await pathExists(filePath)) {
|
|
99316
100221
|
continue;
|
|
99317
100222
|
}
|
|
@@ -99328,7 +100233,7 @@ async function collectDashboardDocs(metaprojectRoot, wiki, memory) {
|
|
|
99328
100233
|
return docs;
|
|
99329
100234
|
}
|
|
99330
100235
|
async function collectHealthDashboardData(metaprojectRoot) {
|
|
99331
|
-
const reportPath =
|
|
100236
|
+
const reportPath = path195.join(metaprojectRoot, "data", "health", "artifacts", "latest.json");
|
|
99332
100237
|
if (!await pathExists(reportPath)) {
|
|
99333
100238
|
return;
|
|
99334
100239
|
}
|
|
@@ -99437,8 +100342,8 @@ function metricToScope(metric) {
|
|
|
99437
100342
|
};
|
|
99438
100343
|
}
|
|
99439
100344
|
async function collectGraphDashboardData(metaprojectRoot) {
|
|
99440
|
-
const nodesPath =
|
|
99441
|
-
const edgesPath =
|
|
100345
|
+
const nodesPath = path195.join(metaprojectRoot, "data", "gdgraph", "storage", "nodes.jsonl");
|
|
100346
|
+
const edgesPath = path195.join(metaprojectRoot, "data", "gdgraph", "storage", "edges.jsonl");
|
|
99442
100347
|
if (!await pathExists(nodesPath) || !await pathExists(edgesPath)) {
|
|
99443
100348
|
return;
|
|
99444
100349
|
}
|
|
@@ -99489,8 +100394,8 @@ async function collectGraphDashboardData(metaprojectRoot) {
|
|
|
99489
100394
|
};
|
|
99490
100395
|
}
|
|
99491
100396
|
async function collectTestingDashboardData(metaprojectRoot) {
|
|
99492
|
-
const reportPath =
|
|
99493
|
-
const contextPath =
|
|
100397
|
+
const reportPath = path195.join(metaprojectRoot, "data", "testing", "artifacts", "latest.json");
|
|
100398
|
+
const contextPath = path195.join(metaprojectRoot, "data", "testing", "context.md");
|
|
99494
100399
|
if (await pathExists(reportPath)) {
|
|
99495
100400
|
const report = JSON.parse(await readFile99(reportPath, "utf8"));
|
|
99496
100401
|
const totalTests = numberOrUndefined(report.total);
|
|
@@ -99519,7 +100424,7 @@ async function collectMarkdownPages(root, hrefPrefix) {
|
|
|
99519
100424
|
const files = await listMarkdownFiles(root);
|
|
99520
100425
|
const pages = [];
|
|
99521
100426
|
for (const filePath of files.slice(0, 40)) {
|
|
99522
|
-
const relativePath =
|
|
100427
|
+
const relativePath = path195.relative(root, filePath).split(path195.sep).join("/");
|
|
99523
100428
|
if (relativePath === "index.md" || relativePath.startsWith("templates/")) {
|
|
99524
100429
|
continue;
|
|
99525
100430
|
}
|
|
@@ -99540,7 +100445,7 @@ async function listMarkdownFiles(root) {
|
|
|
99540
100445
|
const entries = await readdir35(root, { withFileTypes: true });
|
|
99541
100446
|
const files = [];
|
|
99542
100447
|
for (const entry of entries) {
|
|
99543
|
-
const fullPath =
|
|
100448
|
+
const fullPath = path195.join(root, entry.name);
|
|
99544
100449
|
if (entry.isDirectory()) {
|
|
99545
100450
|
files.push(...await listMarkdownFiles(fullPath));
|
|
99546
100451
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
@@ -99588,7 +100493,7 @@ async function writeRecoveredManifest(metaprojectRoot, modules) {
|
|
|
99588
100493
|
const manifest = {
|
|
99589
100494
|
schemaVersion: 1,
|
|
99590
100495
|
standardVersion: STANDARD_VERSION,
|
|
99591
|
-
name: `${
|
|
100496
|
+
name: `${path195.basename(path195.dirname(metaprojectRoot))}-metaproject`,
|
|
99592
100497
|
createdBy: "keryx",
|
|
99593
100498
|
profiles: computeProfiles(enabledModuleKeys),
|
|
99594
100499
|
paths: {
|
|
@@ -99678,11 +100583,11 @@ async function writeRecoveredManifest(metaprojectRoot, modules) {
|
|
|
99678
100583
|
metaproject: ".metaproject/index.md"
|
|
99679
100584
|
}
|
|
99680
100585
|
};
|
|
99681
|
-
await writeFile54(
|
|
100586
|
+
await writeFile54(path195.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
|
|
99682
100587
|
`, "utf8");
|
|
99683
100588
|
}
|
|
99684
100589
|
async function enableTasksInManifest(metaprojectRoot) {
|
|
99685
|
-
const manifestPath =
|
|
100590
|
+
const manifestPath = path195.join(metaprojectRoot, "metaproject.json");
|
|
99686
100591
|
if (!await pathExists(manifestPath)) {
|
|
99687
100592
|
return;
|
|
99688
100593
|
}
|
|
@@ -99705,7 +100610,7 @@ async function enableTasksInManifest(metaprojectRoot) {
|
|
|
99705
100610
|
`, "utf8");
|
|
99706
100611
|
}
|
|
99707
100612
|
async function updateManifestAgentEntrypoints(metaprojectRoot, ruleSources) {
|
|
99708
|
-
const manifestPath =
|
|
100613
|
+
const manifestPath = path195.join(metaprojectRoot, "metaproject.json");
|
|
99709
100614
|
if (!await pathExists(manifestPath)) {
|
|
99710
100615
|
return;
|
|
99711
100616
|
}
|
|
@@ -99741,72 +100646,72 @@ async function updateRuntime(projectRoot) {
|
|
|
99741
100646
|
}
|
|
99742
100647
|
}
|
|
99743
100648
|
async function findRuntimeRoot(projectRoot) {
|
|
99744
|
-
const projectRuntime =
|
|
99745
|
-
if (await pathExists(
|
|
100649
|
+
const projectRuntime = path195.join(projectRoot, ".metaproject", "runtime", "keryx");
|
|
100650
|
+
if (await pathExists(path195.join(projectRuntime, ".git"))) {
|
|
99746
100651
|
return projectRuntime;
|
|
99747
100652
|
}
|
|
99748
100653
|
const home = process.env.HOME;
|
|
99749
100654
|
if (!home) {
|
|
99750
100655
|
return null;
|
|
99751
100656
|
}
|
|
99752
|
-
const globalRuntime =
|
|
99753
|
-
if (await pathExists(
|
|
100657
|
+
const globalRuntime = path195.join(home, ".keryx", "keryx");
|
|
100658
|
+
if (await pathExists(path195.join(globalRuntime, ".git"))) {
|
|
99754
100659
|
return globalRuntime;
|
|
99755
100660
|
}
|
|
99756
100661
|
return null;
|
|
99757
100662
|
}
|
|
99758
100663
|
async function createServiceDirs(metaprojectRoot, modules) {
|
|
99759
100664
|
const dirs = [
|
|
99760
|
-
|
|
99761
|
-
|
|
99762
|
-
|
|
99763
|
-
|
|
99764
|
-
|
|
100665
|
+
path195.join(metaprojectRoot, "core"),
|
|
100666
|
+
path195.join(metaprojectRoot, "hooks", "post-update.d"),
|
|
100667
|
+
path195.join(metaprojectRoot, "modules"),
|
|
100668
|
+
path195.join(metaprojectRoot, "rules"),
|
|
100669
|
+
path195.join(metaprojectRoot, "skills", "project-rules"),
|
|
99765
100670
|
...modules.enableGdgraph ? [
|
|
99766
|
-
|
|
99767
|
-
|
|
100671
|
+
path195.join(metaprojectRoot, "core", "gdgraph"),
|
|
100672
|
+
path195.join(metaprojectRoot, "skills", "gdgraph")
|
|
99768
100673
|
] : [],
|
|
99769
100674
|
...modules.enableGdctx ? [
|
|
99770
|
-
|
|
99771
|
-
|
|
100675
|
+
path195.join(metaprojectRoot, "core", "gdctx"),
|
|
100676
|
+
path195.join(metaprojectRoot, "skills", "gdctx")
|
|
99772
100677
|
] : [],
|
|
99773
100678
|
...modules.enableGdwiki ? [
|
|
99774
|
-
|
|
99775
|
-
|
|
100679
|
+
path195.join(metaprojectRoot, "skills", "gdwiki"),
|
|
100680
|
+
path195.join(metaprojectRoot, "wiki", "templates")
|
|
99776
100681
|
] : [],
|
|
99777
100682
|
...modules.enableHealth ? [
|
|
99778
|
-
|
|
99779
|
-
|
|
100683
|
+
path195.join(metaprojectRoot, "core", "health"),
|
|
100684
|
+
path195.join(metaprojectRoot, "skills", "health")
|
|
99780
100685
|
] : [],
|
|
99781
100686
|
...modules.enableTesting ? [
|
|
99782
|
-
|
|
99783
|
-
|
|
100687
|
+
path195.join(metaprojectRoot, "core", "testing"),
|
|
100688
|
+
path195.join(metaprojectRoot, "skills", "testing")
|
|
99784
100689
|
] : [],
|
|
99785
100690
|
...modules.enableMemory ? [
|
|
99786
|
-
|
|
99787
|
-
|
|
99788
|
-
|
|
100691
|
+
path195.join(metaprojectRoot, "core", "memory"),
|
|
100692
|
+
path195.join(metaprojectRoot, "skills", "memory"),
|
|
100693
|
+
path195.join(metaprojectRoot, "memory", "templates")
|
|
99789
100694
|
] : [],
|
|
99790
100695
|
...modules.enableTasks ? [
|
|
99791
|
-
|
|
99792
|
-
|
|
100696
|
+
path195.join(metaprojectRoot, "flows"),
|
|
100697
|
+
path195.join(metaprojectRoot, "skills", "flow")
|
|
99793
100698
|
] : [],
|
|
99794
100699
|
...modules.enableSecurity ? [
|
|
99795
|
-
|
|
100700
|
+
path195.join(metaprojectRoot, "core", "security")
|
|
99796
100701
|
] : [],
|
|
99797
100702
|
...modules.enableSac ? [
|
|
99798
|
-
|
|
100703
|
+
path195.join(metaprojectRoot, "skills", "sac")
|
|
99799
100704
|
] : []
|
|
99800
100705
|
];
|
|
99801
100706
|
await Promise.all(dirs.map((dir) => mkdir62(dir, { recursive: true })));
|
|
99802
100707
|
}
|
|
99803
100708
|
async function installGdgraphCoreScripts2(metaprojectRoot) {
|
|
99804
|
-
const gdgraphCoreRoot =
|
|
100709
|
+
const gdgraphCoreRoot = path195.join(metaprojectRoot, "core", "gdgraph");
|
|
99805
100710
|
await mkdir62(gdgraphCoreRoot, { recursive: true });
|
|
99806
100711
|
for (const file of GDGRAPH_CORE_SOURCES) {
|
|
99807
|
-
await copyFileIfChanged2(runtimeSourcePath2(`../gdgraph/${file}`),
|
|
100712
|
+
await copyFileIfChanged2(runtimeSourcePath2(`../gdgraph/${file}`), path195.join(gdgraphCoreRoot, file));
|
|
99808
100713
|
}
|
|
99809
|
-
await writeTextIfChanged4(
|
|
100714
|
+
await writeTextIfChanged4(path195.join(gdgraphCoreRoot, "cli.ts"), renderGdgraphCoreCli());
|
|
99810
100715
|
}
|
|
99811
100716
|
async function installManagedHook2(projectRoot, hookName, blockId, content) {
|
|
99812
100717
|
const hooksRoot = await resolveGitHooksRoot(projectRoot);
|
|
@@ -99814,7 +100719,7 @@ async function installManagedHook2(projectRoot, hookName, blockId, content) {
|
|
|
99814
100719
|
return;
|
|
99815
100720
|
}
|
|
99816
100721
|
await mkdir62(hooksRoot, { recursive: true });
|
|
99817
|
-
const hookPath =
|
|
100722
|
+
const hookPath = path195.join(hooksRoot, hookName);
|
|
99818
100723
|
const blockStart = `# keryx:${blockId}:begin`;
|
|
99819
100724
|
const blockEnd = `# keryx:${blockId}:end`;
|
|
99820
100725
|
const managedBlock = `${blockStart}
|
|
@@ -99835,7 +100740,7 @@ async function removeManagedHook2(projectRoot, hookName, blockId) {
|
|
|
99835
100740
|
if (!hooksRoot) {
|
|
99836
100741
|
return;
|
|
99837
100742
|
}
|
|
99838
|
-
const hookPath =
|
|
100743
|
+
const hookPath = path195.join(hooksRoot, hookName);
|
|
99839
100744
|
if (!await pathExists(hookPath)) {
|
|
99840
100745
|
return;
|
|
99841
100746
|
}
|
|
@@ -99857,7 +100762,7 @@ async function prePushHasSecurityBlock2(projectRoot) {
|
|
|
99857
100762
|
if (!hooksRoot) {
|
|
99858
100763
|
return false;
|
|
99859
100764
|
}
|
|
99860
|
-
const hookPath =
|
|
100765
|
+
const hookPath = path195.join(hooksRoot, "pre-push");
|
|
99861
100766
|
if (!await pathExists(hookPath)) {
|
|
99862
100767
|
return false;
|
|
99863
100768
|
}
|
|
@@ -99872,7 +100777,7 @@ async function agentSettingsHasSecuritySentinel2(projectRoot) {
|
|
|
99872
100777
|
return (await readFile99(file, "utf8")).includes(AGENT_HOOKS_SENTINEL);
|
|
99873
100778
|
}
|
|
99874
100779
|
async function readManifest5(metaprojectRoot) {
|
|
99875
|
-
const manifestPath =
|
|
100780
|
+
const manifestPath = path195.join(metaprojectRoot, "metaproject.json");
|
|
99876
100781
|
if (!await pathExists(manifestPath)) {
|
|
99877
100782
|
return {
|
|
99878
100783
|
exists: false,
|
|
@@ -99941,7 +100846,7 @@ async function inferManifestFromExistingMetaproject(metaprojectRoot) {
|
|
|
99941
100846
|
}
|
|
99942
100847
|
async function anyPathExists(root, candidates) {
|
|
99943
100848
|
for (const candidate of candidates) {
|
|
99944
|
-
if (await pathExists(
|
|
100849
|
+
if (await pathExists(path195.join(root, candidate))) {
|
|
99945
100850
|
return true;
|
|
99946
100851
|
}
|
|
99947
100852
|
}
|
|
@@ -99964,13 +100869,13 @@ function parseUpdateArgs(args) {
|
|
|
99964
100869
|
};
|
|
99965
100870
|
}
|
|
99966
100871
|
async function runPostUpdateHooks(projectRoot) {
|
|
99967
|
-
const hooksDir =
|
|
100872
|
+
const hooksDir = path195.join(projectRoot, ".metaproject", "hooks", "post-update.d");
|
|
99968
100873
|
if (!await pathExists(hooksDir)) {
|
|
99969
100874
|
return;
|
|
99970
100875
|
}
|
|
99971
100876
|
const entries = (await readdir35(hooksDir)).sort();
|
|
99972
100877
|
for (const entry of entries) {
|
|
99973
|
-
const hookPath =
|
|
100878
|
+
const hookPath = path195.join(hooksDir, entry);
|
|
99974
100879
|
try {
|
|
99975
100880
|
await accessExecutable(hookPath);
|
|
99976
100881
|
} catch {
|
|
@@ -100011,14 +100916,14 @@ async function writeTextIfChanged4(filePath, content) {
|
|
|
100011
100916
|
if (await pathExists(filePath) && await readFile99(filePath, "utf8") === content) {
|
|
100012
100917
|
return;
|
|
100013
100918
|
}
|
|
100014
|
-
await mkdir62(
|
|
100919
|
+
await mkdir62(path195.dirname(filePath), { recursive: true });
|
|
100015
100920
|
await writeFile54(filePath, content, "utf8");
|
|
100016
100921
|
}
|
|
100017
100922
|
async function writeTextIfMissing4(filePath, content) {
|
|
100018
100923
|
if (await pathExists(filePath)) {
|
|
100019
100924
|
return;
|
|
100020
100925
|
}
|
|
100021
|
-
await mkdir62(
|
|
100926
|
+
await mkdir62(path195.dirname(filePath), { recursive: true });
|
|
100022
100927
|
await writeFile54(filePath, content, "utf8");
|
|
100023
100928
|
}
|
|
100024
100929
|
async function copyFileIfChanged2(from, to) {
|
|
@@ -100026,7 +100931,7 @@ async function copyFileIfChanged2(from, to) {
|
|
|
100026
100931
|
if (await pathExists(to) && await readFile99(to, "utf8") === next) {
|
|
100027
100932
|
return;
|
|
100028
100933
|
}
|
|
100029
|
-
await mkdir62(
|
|
100934
|
+
await mkdir62(path195.dirname(to), { recursive: true });
|
|
100030
100935
|
await writeFile54(to, next, "utf8");
|
|
100031
100936
|
}
|
|
100032
100937
|
function runtimeSourcePath2(relativePath) {
|
|
@@ -100035,7 +100940,7 @@ function runtimeSourcePath2(relativePath) {
|
|
|
100035
100940
|
return directPath;
|
|
100036
100941
|
}
|
|
100037
100942
|
if (relativePath.startsWith("../")) {
|
|
100038
|
-
const packagedSourcePath =
|
|
100943
|
+
const packagedSourcePath = path195.join(path195.dirname(fileURLToPath7(import.meta.url)), "..", "src", relativePath.slice(3));
|
|
100039
100944
|
if (existsSync35(packagedSourcePath)) {
|
|
100040
100945
|
return packagedSourcePath;
|
|
100041
100946
|
}
|
|
@@ -100070,7 +100975,7 @@ function printHelp20() {
|
|
|
100070
100975
|
|
|
100071
100976
|
// src/commands/dashboard.ts
|
|
100072
100977
|
import { spawn as spawn7 } from "child_process";
|
|
100073
|
-
import
|
|
100978
|
+
import path196 from "path";
|
|
100074
100979
|
init_args();
|
|
100075
100980
|
async function dashboardCommand(args = []) {
|
|
100076
100981
|
const options = parseOptions(args);
|
|
@@ -100081,7 +100986,7 @@ async function dashboardCommand(args = []) {
|
|
|
100081
100986
|
}
|
|
100082
100987
|
if (subcommand === "build") {
|
|
100083
100988
|
const result = await buildDashboard();
|
|
100084
|
-
const rel =
|
|
100989
|
+
const rel = path196.relative(process.cwd(), result.path);
|
|
100085
100990
|
console.log(` ${style.green(symbols.ok)} Dashboard built ${style.cyan(symbols.arrow)} ${style.cyan(rel)}`);
|
|
100086
100991
|
note(`Open it: keryx dashboard open`);
|
|
100087
100992
|
return;
|
|
@@ -100089,7 +100994,7 @@ async function dashboardCommand(args = []) {
|
|
|
100089
100994
|
if (subcommand === "open") {
|
|
100090
100995
|
const result = await buildDashboard();
|
|
100091
100996
|
await openFile(result.path);
|
|
100092
|
-
const rel =
|
|
100997
|
+
const rel = path196.relative(process.cwd(), result.path);
|
|
100093
100998
|
console.log(` ${style.green(symbols.ok)} Opened ${style.cyan(rel)}`);
|
|
100094
100999
|
return;
|
|
100095
101000
|
}
|
|
@@ -100138,7 +101043,7 @@ import { readFileSync as readFileSync8 } from "fs";
|
|
|
100138
101043
|
// src/agents/bootstrap.ts
|
|
100139
101044
|
import { mkdir as mkdir63, readFile as readFile100, writeFile as writeFile55 } from "fs/promises";
|
|
100140
101045
|
import { homedir as homedir8 } from "os";
|
|
100141
|
-
import
|
|
101046
|
+
import path197 from "path";
|
|
100142
101047
|
init_fs();
|
|
100143
101048
|
var AGENT_BOOTSTRAP_START = "<!-- keryx:global-bootstrap -->";
|
|
100144
101049
|
var AGENT_BOOTSTRAP_END = "<!-- /keryx:global-bootstrap -->";
|
|
@@ -100148,35 +101053,35 @@ var AGENT_BOOTSTRAP_RUNTIMES = [
|
|
|
100148
101053
|
aliases: ["claude-code"],
|
|
100149
101054
|
label: "Claude Code",
|
|
100150
101055
|
fileName: "CLAUDE.md",
|
|
100151
|
-
filePath: (homeRoot) =>
|
|
101056
|
+
filePath: (homeRoot) => path197.join(homeRoot, ".claude", "CLAUDE.md")
|
|
100152
101057
|
},
|
|
100153
101058
|
{
|
|
100154
101059
|
id: "opencode",
|
|
100155
101060
|
aliases: ["open-code"],
|
|
100156
101061
|
label: "OpenCode",
|
|
100157
101062
|
fileName: "AGENTS.md",
|
|
100158
|
-
filePath: (homeRoot) =>
|
|
101063
|
+
filePath: (homeRoot) => path197.join(homeRoot, ".config", "opencode", "AGENTS.md")
|
|
100159
101064
|
},
|
|
100160
101065
|
{
|
|
100161
101066
|
id: "zcode",
|
|
100162
101067
|
aliases: ["zed", "zed-code"],
|
|
100163
101068
|
label: "ZCode",
|
|
100164
101069
|
fileName: "AGENTS.md",
|
|
100165
|
-
filePath: (homeRoot) =>
|
|
101070
|
+
filePath: (homeRoot) => path197.join(homeRoot, ".zcode", "AGENTS.md")
|
|
100166
101071
|
},
|
|
100167
101072
|
{
|
|
100168
101073
|
id: "codex",
|
|
100169
101074
|
aliases: [],
|
|
100170
101075
|
label: "Codex",
|
|
100171
101076
|
fileName: "AGENTS.md",
|
|
100172
|
-
filePath: (homeRoot) =>
|
|
101077
|
+
filePath: (homeRoot) => path197.join(homeRoot, ".codex", "AGENTS.md")
|
|
100173
101078
|
},
|
|
100174
101079
|
{
|
|
100175
101080
|
id: "antigravity",
|
|
100176
101081
|
aliases: ["antigravuty", "antigravity-code"],
|
|
100177
101082
|
label: "Antigravity",
|
|
100178
101083
|
fileName: "AGENTS.md",
|
|
100179
|
-
filePath: (homeRoot) =>
|
|
101084
|
+
filePath: (homeRoot) => path197.join(homeRoot, ".config", "antigravity", "AGENTS.md")
|
|
100180
101085
|
}
|
|
100181
101086
|
];
|
|
100182
101087
|
function agentBootstrapRuntimeIds() {
|
|
@@ -100224,7 +101129,7 @@ async function installAgentBootstrap(runtime, options = {}) {
|
|
|
100224
101129
|
const dryRun = options.dryRun === true;
|
|
100225
101130
|
const wrote = next !== current;
|
|
100226
101131
|
if (wrote && !dryRun) {
|
|
100227
|
-
await mkdir63(
|
|
101132
|
+
await mkdir63(path197.dirname(filePath), { recursive: true });
|
|
100228
101133
|
await writeFile55(filePath, next, "utf8");
|
|
100229
101134
|
}
|
|
100230
101135
|
const status = dryRun ? statusFromContent(runtime, filePath, exists, next) : await agentBootstrapStatus(runtime, homeRoot);
|
|
@@ -100761,7 +101666,7 @@ function printBootstrapHelp() {
|
|
|
100761
101666
|
// src/commands/metrics.ts
|
|
100762
101667
|
init_args();
|
|
100763
101668
|
import { readFile as readFile101 } from "fs/promises";
|
|
100764
|
-
import
|
|
101669
|
+
import path199 from "path";
|
|
100765
101670
|
|
|
100766
101671
|
// src/metrics/benchmark.ts
|
|
100767
101672
|
var RELIABILITIES2 = new Set(["exact", "estimated", "unknown"]);
|
|
@@ -101578,7 +102483,7 @@ function buildContainmentManifest(inputs, options = {}) {
|
|
|
101578
102483
|
|
|
101579
102484
|
// src/metrics/oracle-runner.ts
|
|
101580
102485
|
import { mkdir as mkdir64, writeFile as writeFile56 } from "fs/promises";
|
|
101581
|
-
import
|
|
102486
|
+
import path198 from "path";
|
|
101582
102487
|
|
|
101583
102488
|
// src/metrics/ir.ts
|
|
101584
102489
|
function toIdSet(ids) {
|
|
@@ -102024,9 +102929,9 @@ function buildEvidenceBundle(input, options = {}) {
|
|
|
102024
102929
|
async function persistEvidenceBundle(outDir, bundle, ladder = "metastore") {
|
|
102025
102930
|
const safeTarget = bundle.target.replace(/[^A-Za-z0-9._/-]/g, "_");
|
|
102026
102931
|
const safeCase = bundle.caseId.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
102027
|
-
const dir =
|
|
102932
|
+
const dir = path198.join(outDir, "bench", ladder, safeTarget, safeCase, bundle.variant, String(bundle.seed));
|
|
102028
102933
|
await mkdir64(dir, { recursive: true });
|
|
102029
|
-
const write = (name, value) => writeFile56(
|
|
102934
|
+
const write = (name, value) => writeFile56(path198.join(dir, name), `${JSON.stringify(value, null, 2)}
|
|
102030
102935
|
`, "utf8");
|
|
102031
102936
|
await Promise.all([
|
|
102032
102937
|
write("inputs.json", bundle.inputs),
|
|
@@ -102066,7 +102971,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
102066
102971
|
console.log("# metrics status");
|
|
102067
102972
|
console.log("");
|
|
102068
102973
|
console.log(`root: ${root}`);
|
|
102069
|
-
console.log(`enabled: ${await Bun.file(
|
|
102974
|
+
console.log(`enabled: ${await Bun.file(path199.join(projectRoot, ".metaproject", "metaproject.json")).exists() ? "yes" : "no"}`);
|
|
102070
102975
|
const latest = await readLatestPointer(root);
|
|
102071
102976
|
console.log(`latest: ${latest.status}`);
|
|
102072
102977
|
return;
|
|
@@ -102078,7 +102983,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
102078
102983
|
process.exitCode = 1;
|
|
102079
102984
|
return;
|
|
102080
102985
|
}
|
|
102081
|
-
const record = JSON.parse(await readFile101(
|
|
102986
|
+
const record = JSON.parse(await readFile101(path199.resolve(projectRoot, file), "utf8"));
|
|
102082
102987
|
const result = validateRunRecord(record);
|
|
102083
102988
|
console.log(result.valid ? "valid: yes" : "valid: no");
|
|
102084
102989
|
for (const error of result.errors)
|
|
@@ -102103,7 +103008,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
102103
103008
|
process.exitCode = 1;
|
|
102104
103009
|
return;
|
|
102105
103010
|
}
|
|
102106
|
-
const file =
|
|
103011
|
+
const file = path199.join(metricsRoot(projectRoot), "runs", `${runId}.json`);
|
|
102107
103012
|
if (!await Bun.file(file).exists()) {
|
|
102108
103013
|
console.error(`Run not found: ${runId}`);
|
|
102109
103014
|
process.exitCode = 1;
|
|
@@ -102120,8 +103025,8 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
102120
103025
|
process.exitCode = 1;
|
|
102121
103026
|
return;
|
|
102122
103027
|
}
|
|
102123
|
-
const a = JSON.parse(await readFile101(
|
|
102124
|
-
const b = JSON.parse(await readFile101(
|
|
103028
|
+
const a = JSON.parse(await readFile101(path199.join(metricsRoot(projectRoot), "runs", `${runA}.json`), "utf8"));
|
|
103029
|
+
const b = JSON.parse(await readFile101(path199.join(metricsRoot(projectRoot), "runs", `${runB}.json`), "utf8"));
|
|
102125
103030
|
const comparison = compareExecutionRuns(a, b);
|
|
102126
103031
|
console.log(stableJson(comparison));
|
|
102127
103032
|
return;
|
|
@@ -102155,8 +103060,8 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
102155
103060
|
return;
|
|
102156
103061
|
}
|
|
102157
103062
|
const template = createPairedBenchmarkTemplate(taskIds);
|
|
102158
|
-
await Bun.write(
|
|
102159
|
-
console.log(`manifest: ${
|
|
103063
|
+
await Bun.write(path199.resolve(projectRoot, out), stableJson(template));
|
|
103064
|
+
console.log(`manifest: ${path199.relative(projectRoot, path199.resolve(projectRoot, out))}`);
|
|
102160
103065
|
return;
|
|
102161
103066
|
}
|
|
102162
103067
|
if (subcommand === "benchmark" && args[1] === "run") {
|
|
@@ -102170,7 +103075,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
102170
103075
|
process.exitCode = 1;
|
|
102171
103076
|
return;
|
|
102172
103077
|
}
|
|
102173
|
-
const raw = JSON.parse(await readFile101(
|
|
103078
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, file), "utf8"));
|
|
102174
103079
|
const input = Array.isArray(raw) ? raw : raw.runs ?? [];
|
|
102175
103080
|
const result = validatePairedBenchmark(input);
|
|
102176
103081
|
console.log(stableJson(result));
|
|
@@ -102182,7 +103087,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
102182
103087
|
process.exitCode = 1;
|
|
102183
103088
|
}
|
|
102184
103089
|
async function loadAffectedSets(projectRoot, file) {
|
|
102185
|
-
const raw = JSON.parse(await readFile101(
|
|
103090
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, file), "utf8"));
|
|
102186
103091
|
const map = new Map;
|
|
102187
103092
|
for (const entry of raw.targets ?? []) {
|
|
102188
103093
|
if (typeof entry.target === "string")
|
|
@@ -102253,7 +103158,7 @@ async function runHarnessLayer(projectRoot, args, ladder) {
|
|
|
102253
103158
|
let tasks;
|
|
102254
103159
|
let model;
|
|
102255
103160
|
try {
|
|
102256
|
-
const raw = JSON.parse(await readFile101(
|
|
103161
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, resultsPath), "utf8"));
|
|
102257
103162
|
tasks = raw.tasks ?? [];
|
|
102258
103163
|
model = raw.model;
|
|
102259
103164
|
} catch (error) {
|
|
@@ -102284,7 +103189,7 @@ async function runSafetyCompletionHonestyLayer(projectRoot, args, ladder) {
|
|
|
102284
103189
|
let cases;
|
|
102285
103190
|
let model;
|
|
102286
103191
|
try {
|
|
102287
|
-
const raw = JSON.parse(await readFile101(
|
|
103192
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, resultsPath), "utf8"));
|
|
102288
103193
|
cases = raw.cases ?? [];
|
|
102289
103194
|
model = raw.model;
|
|
102290
103195
|
} catch (error) {
|
|
@@ -102309,7 +103214,7 @@ async function runSafetyFalsePremiseLayer(projectRoot, args, ladder) {
|
|
|
102309
103214
|
let cases;
|
|
102310
103215
|
let model;
|
|
102311
103216
|
try {
|
|
102312
|
-
const raw = JSON.parse(await readFile101(
|
|
103217
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, resultsPath), "utf8"));
|
|
102313
103218
|
cases = raw.cases ?? [];
|
|
102314
103219
|
model = raw.model;
|
|
102315
103220
|
} catch (error) {
|
|
@@ -102334,7 +103239,7 @@ async function runSafetyContainmentLayer(projectRoot, args, ladder, caseClass) {
|
|
|
102334
103239
|
let cases;
|
|
102335
103240
|
let model;
|
|
102336
103241
|
try {
|
|
102337
|
-
const raw = JSON.parse(await readFile101(
|
|
103242
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, resultsPath), "utf8"));
|
|
102338
103243
|
cases = raw.cases ?? [];
|
|
102339
103244
|
model = raw.model;
|
|
102340
103245
|
} catch (error) {
|
|
@@ -102396,12 +103301,12 @@ async function runGdgraphLayer(projectRoot, args, ladder) {
|
|
|
102396
103301
|
}
|
|
102397
103302
|
const manifests = buildOracleManifestsByGold(inputs, { ladder });
|
|
102398
103303
|
if (outDir) {
|
|
102399
|
-
const resolvedOut =
|
|
103304
|
+
const resolvedOut = path199.resolve(projectRoot, outDir);
|
|
102400
103305
|
for (const input of inputs) {
|
|
102401
103306
|
for (const named of input.golds) {
|
|
102402
103307
|
const bundle = buildEvidenceBundle({ target: input.target, system: input.system, gold: named.gold }, { ladder, goldReference: goldPathFor(named.kind), timestamp: new Date().toISOString() });
|
|
102403
|
-
const dir = await persistEvidenceBundle(
|
|
102404
|
-
console.error(`bundle[${named.kind}]: ${
|
|
103308
|
+
const dir = await persistEvidenceBundle(path199.join(resolvedOut, named.kind), bundle, ladder);
|
|
103309
|
+
console.error(`bundle[${named.kind}]: ${path199.relative(projectRoot, dir)}`);
|
|
102405
103310
|
}
|
|
102406
103311
|
}
|
|
102407
103312
|
}
|
|
@@ -102425,7 +103330,7 @@ async function runGdgraphLayer(projectRoot, args, ladder) {
|
|
|
102425
103330
|
return allValid;
|
|
102426
103331
|
}
|
|
102427
103332
|
async function loadCoverageMap2(projectRoot, file) {
|
|
102428
|
-
const raw = JSON.parse(await readFile101(
|
|
103333
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, file), "utf8"));
|
|
102429
103334
|
return raw.coverageMap ?? {};
|
|
102430
103335
|
}
|
|
102431
103336
|
async function runTestingLayer(projectRoot, args, ladder) {
|
|
@@ -102462,7 +103367,7 @@ async function runTestingLayer(projectRoot, args, ladder) {
|
|
|
102462
103367
|
return result.valid;
|
|
102463
103368
|
}
|
|
102464
103369
|
async function loadMemoryGoldK(projectRoot, file) {
|
|
102465
|
-
const raw = JSON.parse(await readFile101(
|
|
103370
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, file), "utf8"));
|
|
102466
103371
|
return typeof raw.k === "number" && raw.k > 0 ? raw.k : 3;
|
|
102467
103372
|
}
|
|
102468
103373
|
async function runMemoryLayer(projectRoot, args, ladder) {
|
|
@@ -102499,11 +103404,11 @@ async function runMemoryLayer(projectRoot, args, ladder) {
|
|
|
102499
103404
|
return result.valid;
|
|
102500
103405
|
}
|
|
102501
103406
|
async function loadWikiGoldK(projectRoot, file) {
|
|
102502
|
-
const raw = JSON.parse(await readFile101(
|
|
103407
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, file), "utf8"));
|
|
102503
103408
|
return typeof raw.k === "number" && raw.k > 0 ? raw.k : 5;
|
|
102504
103409
|
}
|
|
102505
103410
|
async function loadWikiGroundedness(projectRoot, file) {
|
|
102506
|
-
const raw = JSON.parse(await readFile101(
|
|
103411
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, file), "utf8"));
|
|
102507
103412
|
const map = new Map;
|
|
102508
103413
|
for (const entry of raw.targets ?? []) {
|
|
102509
103414
|
if (typeof entry.target !== "string" || !Array.isArray(entry.scores) || entry.scores.length !== 3)
|
|
@@ -102559,7 +103464,7 @@ async function runWikiLayer(projectRoot, args, ladder) {
|
|
|
102559
103464
|
return result.valid;
|
|
102560
103465
|
}
|
|
102561
103466
|
async function loadGdctxFacts(projectRoot, file) {
|
|
102562
|
-
const raw = JSON.parse(await readFile101(
|
|
103467
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, file), "utf8"));
|
|
102563
103468
|
const inputs = [];
|
|
102564
103469
|
for (const entry of raw.inputs ?? []) {
|
|
102565
103470
|
if (typeof entry.input === "string") {
|
|
@@ -102597,7 +103502,7 @@ async function collect(projectRoot, args) {
|
|
|
102597
103502
|
process.exitCode = 1;
|
|
102598
103503
|
return;
|
|
102599
103504
|
}
|
|
102600
|
-
const raw = JSON.parse(await readFile101(
|
|
103505
|
+
const raw = JSON.parse(await readFile101(path199.resolve(projectRoot, eventFile), "utf8"));
|
|
102601
103506
|
const events = Array.isArray(raw) ? raw : raw.events;
|
|
102602
103507
|
const startedAt = optionValue(args, "--started-at") ?? events[0]?.timestamp_utc ?? new Date().toISOString();
|
|
102603
103508
|
const finishedAt = optionValue(args, "--finished-at") ?? events.at(-1)?.timestamp_utc ?? startedAt;
|
|
@@ -102613,11 +103518,11 @@ async function collect(projectRoot, args) {
|
|
|
102613
103518
|
parentRunId: optionValue(args, "--parent-run-id") ?? null
|
|
102614
103519
|
});
|
|
102615
103520
|
const result = await writeRunArtifacts(metricsRoot(projectRoot), record, { cwd: projectRoot });
|
|
102616
|
-
console.log(`json: ${
|
|
102617
|
-
console.log(`markdown: ${
|
|
103521
|
+
console.log(`json: ${path199.relative(projectRoot, result.jsonPath)}`);
|
|
103522
|
+
console.log(`markdown: ${path199.relative(projectRoot, result.markdownPath)}`);
|
|
102618
103523
|
}
|
|
102619
103524
|
function metricsRoot(projectRoot) {
|
|
102620
|
-
return
|
|
103525
|
+
return path199.join(projectRoot, ".metaproject", "data", "metrics");
|
|
102621
103526
|
}
|
|
102622
103527
|
function printMetricsHelp() {
|
|
102623
103528
|
console.log(`keryx metrics
|
|
@@ -103090,7 +103995,7 @@ Providers with a sanctioned subscription login: grok (SuperGrok), openai (ChatGP
|
|
|
103090
103995
|
Claude Pro/Max, Gemini Google-account login, and DeepSeek OAuth are not offered.
|
|
103091
103996
|
`);
|
|
103092
103997
|
}
|
|
103093
|
-
async function
|
|
103998
|
+
async function authCommand2(args) {
|
|
103094
103999
|
const command = args[0];
|
|
103095
104000
|
if (!command || command === "--help" || command === "-h") {
|
|
103096
104001
|
printAuthHelp();
|
|
@@ -103484,7 +104389,7 @@ var CLI_ROUTES = {
|
|
|
103484
104389
|
modules: modulesCommand,
|
|
103485
104390
|
projects: projectsCommand,
|
|
103486
104391
|
providers: providersCommand,
|
|
103487
|
-
auth:
|
|
104392
|
+
auth: authCommand2,
|
|
103488
104393
|
serve: serveCommand,
|
|
103489
104394
|
update: updateCommand,
|
|
103490
104395
|
dashboard: dashboardCommand,
|