@askexenow/exe-os 0.9.69 → 0.9.70
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/deploy/stack-manifests/v0.9.json +96 -16
- package/dist/bin/agentic-ontology-backfill.js +6 -0
- package/dist/bin/agentic-reflection-backfill.js +6 -0
- package/dist/bin/agentic-semantic-label.js +6 -0
- package/dist/bin/backfill-conversations.js +6 -0
- package/dist/bin/backfill-responses.js +6 -0
- package/dist/bin/backfill-vectors.js +6 -0
- package/dist/bin/bulk-sync-postgres.js +6 -0
- package/dist/bin/cleanup-stale-review-tasks.js +6 -0
- package/dist/bin/cli.js +1257 -178
- package/dist/bin/exe-agent.js +6 -0
- package/dist/bin/exe-assign.js +6 -0
- package/dist/bin/exe-boot.js +6 -0
- package/dist/bin/exe-call.js +6 -0
- package/dist/bin/exe-cloud.js +6 -0
- package/dist/bin/exe-dispatch.js +6 -0
- package/dist/bin/exe-doctor.js +6 -0
- package/dist/bin/exe-export-behaviors.js +6 -0
- package/dist/bin/exe-forget.js +6 -0
- package/dist/bin/exe-gateway.js +151 -110
- package/dist/bin/exe-heartbeat.js +6 -0
- package/dist/bin/exe-kill.js +6 -0
- package/dist/bin/exe-launch-agent.js +6 -0
- package/dist/bin/exe-new-employee.js +6 -0
- package/dist/bin/exe-pending-messages.js +6 -0
- package/dist/bin/exe-pending-notifications.js +6 -0
- package/dist/bin/exe-pending-reviews.js +6 -0
- package/dist/bin/exe-rename.js +13 -4
- package/dist/bin/exe-review.js +6 -0
- package/dist/bin/exe-search.js +6 -0
- package/dist/bin/exe-session-cleanup.js +6 -0
- package/dist/bin/exe-start-codex.js +6 -0
- package/dist/bin/exe-start-opencode.js +6 -0
- package/dist/bin/exe-status.js +6 -0
- package/dist/bin/exe-team.js +6 -0
- package/dist/bin/git-sweep.js +6 -0
- package/dist/bin/graph-backfill.js +150 -110
- package/dist/bin/graph-export.js +6 -0
- package/dist/bin/intercom-check.js +6 -0
- package/dist/bin/registry-proxy.js +207 -0
- package/dist/bin/scan-tasks.js +6 -0
- package/dist/bin/setup.js +6 -0
- package/dist/bin/shard-migrate.js +6 -0
- package/dist/bin/stack-update.js +128 -0
- package/dist/gateway/index.js +151 -110
- package/dist/hooks/bug-report-worker.js +6 -0
- package/dist/hooks/codex-stop-task-finalizer.js +6 -0
- package/dist/hooks/commit-complete.js +6 -0
- package/dist/hooks/error-recall.js +6 -0
- package/dist/hooks/ingest.js +6 -0
- package/dist/hooks/instructions-loaded.js +6 -0
- package/dist/hooks/notification.js +6 -0
- package/dist/hooks/post-compact.js +6 -0
- package/dist/hooks/post-tool-combined.js +6 -0
- package/dist/hooks/pre-compact.js +6 -0
- package/dist/hooks/pre-tool-use.js +6 -0
- package/dist/hooks/prompt-submit.js +6 -0
- package/dist/hooks/session-end.js +6 -0
- package/dist/hooks/session-start.js +6 -0
- package/dist/hooks/stop.js +6 -0
- package/dist/hooks/subagent-stop.js +6 -0
- package/dist/hooks/summary-worker.js +6 -0
- package/dist/index.js +151 -110
- package/dist/lib/employee-templates.js +6 -0
- package/dist/lib/exe-daemon.js +382 -234
- package/dist/lib/hybrid-search.js +6 -0
- package/dist/lib/registry-proxy.js +162 -0
- package/dist/lib/schedules.js +6 -0
- package/dist/lib/store.js +6 -0
- package/dist/mcp/server.js +318 -222
- package/dist/runtime/index.js +6 -0
- package/dist/tui/App.js +6 -0
- package/package.json +3 -2
- package/stack.release.json +6 -4
- package/stack.release.schema.json +89 -18
package/dist/bin/cli.js
CHANGED
|
@@ -2391,12 +2391,12 @@ function enforceBackgroundJobGuardrails(options = {}) {
|
|
|
2391
2391
|
const jobs = readJobsRaw();
|
|
2392
2392
|
const actions = [];
|
|
2393
2393
|
let changed = false;
|
|
2394
|
-
const
|
|
2394
|
+
const ts2 = now();
|
|
2395
2395
|
for (const job of jobs) {
|
|
2396
2396
|
if (job.status !== "running") continue;
|
|
2397
2397
|
if (!isAlive(job.pid)) {
|
|
2398
2398
|
job.status = "stale";
|
|
2399
|
-
job.updatedAt =
|
|
2399
|
+
job.updatedAt = ts2;
|
|
2400
2400
|
actions.push({ jobId: job.id, name: job.name, pid: job.pid, action: "marked-stale" });
|
|
2401
2401
|
changed = true;
|
|
2402
2402
|
continue;
|
|
@@ -2404,7 +2404,7 @@ function enforceBackgroundJobGuardrails(options = {}) {
|
|
|
2404
2404
|
const heartbeatAge = Date.now() - Date.parse(job.lastHeartbeatAt || job.updatedAt || job.startedAt);
|
|
2405
2405
|
if (Number.isFinite(heartbeatAge) && heartbeatAge > staleAfter) {
|
|
2406
2406
|
job.status = "stale";
|
|
2407
|
-
job.updatedAt =
|
|
2407
|
+
job.updatedAt = ts2;
|
|
2408
2408
|
actions.push({ jobId: job.id, name: job.name, pid: job.pid, action: "marked-stale" });
|
|
2409
2409
|
changed = true;
|
|
2410
2410
|
continue;
|
|
@@ -2420,7 +2420,7 @@ function enforceBackgroundJobGuardrails(options = {}) {
|
|
|
2420
2420
|
} catch {
|
|
2421
2421
|
}
|
|
2422
2422
|
job.status = "cancelled";
|
|
2423
|
-
job.updatedAt =
|
|
2423
|
+
job.updatedAt = ts2;
|
|
2424
2424
|
job.progressLabel = `Cancelled by heat guardrail after sustained high CPU (${cpu.toFixed(1)}%).`;
|
|
2425
2425
|
releaseJobLock(job.type);
|
|
2426
2426
|
actions.push({ jobId: job.id, name: job.name, pid: job.pid, action: "cancelled-hot", cpuPercent: cpu, heatStrikes: job.heatStrikes });
|
|
@@ -2432,7 +2432,7 @@ function enforceBackgroundJobGuardrails(options = {}) {
|
|
|
2432
2432
|
}
|
|
2433
2433
|
if (cpu >= reniceAt) {
|
|
2434
2434
|
renicePid(job.pid, 15);
|
|
2435
|
-
job.updatedAt =
|
|
2435
|
+
job.updatedAt = ts2;
|
|
2436
2436
|
job.progressLabel = `Throttled by heat guardrail (${cpu.toFixed(1)}% CPU).`;
|
|
2437
2437
|
actions.push({ jobId: job.id, name: job.name, pid: job.pid, action: "reniced", cpuPercent: cpu, heatStrikes: job.heatStrikes });
|
|
2438
2438
|
changed = true;
|
|
@@ -2600,7 +2600,7 @@ async function tryKeytar() {
|
|
|
2600
2600
|
}
|
|
2601
2601
|
function deriveMachineKey() {
|
|
2602
2602
|
try {
|
|
2603
|
-
const
|
|
2603
|
+
const crypto15 = __require("crypto");
|
|
2604
2604
|
const material = [
|
|
2605
2605
|
os8.hostname(),
|
|
2606
2606
|
os8.userInfo().username,
|
|
@@ -2609,23 +2609,23 @@ function deriveMachineKey() {
|
|
|
2609
2609
|
// Machine ID on Linux (stable across reboots)
|
|
2610
2610
|
process.platform === "linux" ? readMachineId() : ""
|
|
2611
2611
|
].join("|");
|
|
2612
|
-
return
|
|
2612
|
+
return crypto15.createHash("sha256").update(material).digest();
|
|
2613
2613
|
} catch {
|
|
2614
2614
|
return null;
|
|
2615
2615
|
}
|
|
2616
2616
|
}
|
|
2617
2617
|
function readMachineId() {
|
|
2618
2618
|
try {
|
|
2619
|
-
const { readFileSync:
|
|
2620
|
-
return
|
|
2619
|
+
const { readFileSync: readFileSync36 } = __require("fs");
|
|
2620
|
+
return readFileSync36("/etc/machine-id", "utf-8").trim();
|
|
2621
2621
|
} catch {
|
|
2622
2622
|
return "";
|
|
2623
2623
|
}
|
|
2624
2624
|
}
|
|
2625
2625
|
function encryptWithMachineKey(plaintext, machineKey) {
|
|
2626
|
-
const
|
|
2627
|
-
const iv =
|
|
2628
|
-
const cipher =
|
|
2626
|
+
const crypto15 = __require("crypto");
|
|
2627
|
+
const iv = crypto15.randomBytes(12);
|
|
2628
|
+
const cipher = crypto15.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
2629
2629
|
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
2630
2630
|
encrypted += cipher.final("base64");
|
|
2631
2631
|
const authTag = cipher.getAuthTag().toString("base64");
|
|
@@ -2634,13 +2634,13 @@ function encryptWithMachineKey(plaintext, machineKey) {
|
|
|
2634
2634
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
2635
2635
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
2636
2636
|
try {
|
|
2637
|
-
const
|
|
2637
|
+
const crypto15 = __require("crypto");
|
|
2638
2638
|
const parts = encrypted.slice(ENCRYPTED_PREFIX.length).split(":");
|
|
2639
2639
|
if (parts.length !== 3) return null;
|
|
2640
2640
|
const [ivB64, tagB64, cipherB64] = parts;
|
|
2641
2641
|
const iv = Buffer.from(ivB64, "base64");
|
|
2642
2642
|
const authTag = Buffer.from(tagB64, "base64");
|
|
2643
|
-
const decipher =
|
|
2643
|
+
const decipher = crypto15.createDecipheriv("aes-256-gcm", machineKey, iv);
|
|
2644
2644
|
decipher.setAuthTag(authTag);
|
|
2645
2645
|
let decrypted = decipher.update(cipherB64, "base64", "utf-8");
|
|
2646
2646
|
decrypted += decipher.final("utf-8");
|
|
@@ -6044,8 +6044,8 @@ async function validateLicense(apiKey, deviceId) {
|
|
|
6044
6044
|
}
|
|
6045
6045
|
function getCacheAgeMs() {
|
|
6046
6046
|
try {
|
|
6047
|
-
const { statSync:
|
|
6048
|
-
const s =
|
|
6047
|
+
const { statSync: statSync8 } = __require("fs");
|
|
6048
|
+
const s = statSync8(CACHE_PATH);
|
|
6049
6049
|
return Date.now() - s.mtimeMs;
|
|
6050
6050
|
} catch {
|
|
6051
6051
|
return Infinity;
|
|
@@ -6732,8 +6732,8 @@ async function withRosterLock(fn) {
|
|
|
6732
6732
|
} catch (err) {
|
|
6733
6733
|
if (err.code === "EEXIST") {
|
|
6734
6734
|
try {
|
|
6735
|
-
const
|
|
6736
|
-
if (Date.now() -
|
|
6735
|
+
const ts2 = parseInt(readFileSync12(ROSTER_LOCK_PATH, "utf-8"), 10);
|
|
6736
|
+
if (Date.now() - ts2 < LOCK_STALE_MS) {
|
|
6737
6737
|
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
6738
6738
|
}
|
|
6739
6739
|
unlinkSync6(ROSTER_LOCK_PATH);
|
|
@@ -8657,6 +8657,12 @@ var init_platform_procedures = __esm({
|
|
|
8657
8657
|
priority: "p0",
|
|
8658
8658
|
content: "exe-build-adv is MANDATORY for ALL work touching 3+ files. Run /exe-build-adv --auto BEFORE implementation. Pipeline: Spec \u2192 AC \u2192 Tests \u2192 Evaluate \u2192 Fix. No multi-file feature ships without pipeline artifacts. No exceptions \u2014 managers reject work without them."
|
|
8659
8659
|
},
|
|
8660
|
+
{
|
|
8661
|
+
title: "Code context first for repository orientation",
|
|
8662
|
+
domain: "workflow",
|
|
8663
|
+
priority: "p1",
|
|
8664
|
+
content: "Before broad repo exploration, symbol tracing, blast-radius review, or codebase Q&A, agents should use the consolidated code_context MCP tool instead of manual grep/read loops. Use action=index or stats to refresh/check the index; action=search with query, limit, offset, languages, paths, refresh_index for fresh multi-language code/doc search; action=trace for symbol imports/dependents; action=blast_radius for impact analysis before edits. CLI parity exists via exe-os code-context init|index|status|stats|search|doctor. Keep code_context separate from durable employee memory: promote only validated decisions, procedures, or lessons into store_memory/commit_memory."
|
|
8665
|
+
},
|
|
8660
8666
|
{
|
|
8661
8667
|
title: "Commit discipline \u2014 never leave verified work floating",
|
|
8662
8668
|
domain: "workflow",
|
|
@@ -11085,10 +11091,10 @@ async function parseConversation(filePath) {
|
|
|
11085
11091
|
if (entry.cwd && typeof entry.cwd === "string") {
|
|
11086
11092
|
conv.cwd = entry.cwd;
|
|
11087
11093
|
}
|
|
11088
|
-
const
|
|
11089
|
-
if (
|
|
11090
|
-
if (!conv.startTime ||
|
|
11091
|
-
if (!conv.endTime ||
|
|
11094
|
+
const ts2 = entry.timestamp;
|
|
11095
|
+
if (ts2) {
|
|
11096
|
+
if (!conv.startTime || ts2 < conv.startTime) conv.startTime = ts2;
|
|
11097
|
+
if (!conv.endTime || ts2 > conv.endTime) conv.endTime = ts2;
|
|
11092
11098
|
}
|
|
11093
11099
|
const entryType = entry.type;
|
|
11094
11100
|
if (entryType === "user") {
|
|
@@ -11774,9 +11780,9 @@ Unclassified: ${unclassified}
|
|
|
11774
11780
|
}
|
|
11775
11781
|
async function exportBatches(options) {
|
|
11776
11782
|
const fs8 = await import("fs");
|
|
11777
|
-
const
|
|
11783
|
+
const path55 = await import("path");
|
|
11778
11784
|
const client = getClient();
|
|
11779
|
-
const outDir =
|
|
11785
|
+
const outDir = path55.join(process.cwd(), "exe/output/classifications/input");
|
|
11780
11786
|
fs8.mkdirSync(outDir, { recursive: true });
|
|
11781
11787
|
const countResult = await client.execute({
|
|
11782
11788
|
sql: "SELECT COUNT(*) as cnt FROM memories WHERE intent IS NULL AND outcome IS NULL AND domain IS NULL",
|
|
@@ -11800,7 +11806,7 @@ async function exportBatches(options) {
|
|
|
11800
11806
|
const text = String(row.text || "").replace(/\n/g, " ");
|
|
11801
11807
|
return JSON.stringify({ id: row.id, text });
|
|
11802
11808
|
});
|
|
11803
|
-
const batchFile =
|
|
11809
|
+
const batchFile = path55.join(outDir, `batch-${String(batchNum).padStart(4, "0")}.jsonl`);
|
|
11804
11810
|
fs8.writeFileSync(batchFile, lines.join("\n") + "\n");
|
|
11805
11811
|
exported += batch.rows.length;
|
|
11806
11812
|
offset += options.batchSize;
|
|
@@ -11816,7 +11822,7 @@ async function exportBatches(options) {
|
|
|
11816
11822
|
}
|
|
11817
11823
|
async function importClassifications(importDir) {
|
|
11818
11824
|
const fs8 = await import("fs");
|
|
11819
|
-
const
|
|
11825
|
+
const path55 = await import("path");
|
|
11820
11826
|
const client = getClient();
|
|
11821
11827
|
const files = fs8.readdirSync(importDir).filter((f) => f.endsWith(".jsonl")).sort();
|
|
11822
11828
|
process.stderr.write(`[backfill-metadata] Found ${files.length} JSONL files to import from ${importDir}
|
|
@@ -11824,7 +11830,7 @@ async function importClassifications(importDir) {
|
|
|
11824
11830
|
let imported = 0;
|
|
11825
11831
|
let invalid = 0;
|
|
11826
11832
|
for (const file of files) {
|
|
11827
|
-
const lines = fs8.readFileSync(
|
|
11833
|
+
const lines = fs8.readFileSync(path55.join(importDir, file), "utf-8").split("\n").filter(Boolean);
|
|
11828
11834
|
for (const line of lines) {
|
|
11829
11835
|
try {
|
|
11830
11836
|
const rec = JSON.parse(line);
|
|
@@ -14462,10 +14468,10 @@ async function disposeEmbedder() {
|
|
|
14462
14468
|
async function embedDirect(text) {
|
|
14463
14469
|
const llamaCpp = await import("node-llama-cpp");
|
|
14464
14470
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
14465
|
-
const { existsSync:
|
|
14466
|
-
const
|
|
14467
|
-
const modelPath =
|
|
14468
|
-
if (!
|
|
14471
|
+
const { existsSync: existsSync41 } = await import("fs");
|
|
14472
|
+
const path55 = await import("path");
|
|
14473
|
+
const modelPath = path55.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
14474
|
+
if (!existsSync41(modelPath)) {
|
|
14469
14475
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
14470
14476
|
}
|
|
14471
14477
|
const llama = await llamaCpp.getLlama();
|
|
@@ -17279,10 +17285,7 @@ async function renameEmployee(oldName, newName, opts = {}) {
|
|
|
17279
17285
|
const newIdentityPath = path35.join(identityDir, `${newName}.md`);
|
|
17280
17286
|
if (existsSync29(oldIdentityPath)) {
|
|
17281
17287
|
const content = readFileSync24(oldIdentityPath, "utf-8");
|
|
17282
|
-
const updatedContent = content
|
|
17283
|
-
/^(agent_id:\s*)\S+/m,
|
|
17284
|
-
`$1${newName}`
|
|
17285
|
-
);
|
|
17288
|
+
const updatedContent = rewriteRenamedEmployeeContent(content, rosterOldName, newName);
|
|
17286
17289
|
renameSync6(oldIdentityPath, newIdentityPath);
|
|
17287
17290
|
writeFileSync21(newIdentityPath, updatedContent, "utf-8");
|
|
17288
17291
|
rollbackStack.push({
|
|
@@ -17299,7 +17302,9 @@ async function renameEmployee(oldName, newName, opts = {}) {
|
|
|
17299
17302
|
const newAgentPath = path35.join(agentsDir, `${newName}.md`);
|
|
17300
17303
|
if (existsSync29(oldAgentPath)) {
|
|
17301
17304
|
const agentContent = readFileSync24(oldAgentPath, "utf-8");
|
|
17305
|
+
const updatedAgentContent = rewriteRenamedEmployeeContent(agentContent, rosterOldName, newName);
|
|
17302
17306
|
renameSync6(oldAgentPath, newAgentPath);
|
|
17307
|
+
writeFileSync21(newAgentPath, updatedAgentContent, "utf-8");
|
|
17303
17308
|
rollbackStack.push({
|
|
17304
17309
|
description: "restore agent file",
|
|
17305
17310
|
undo: () => {
|
|
@@ -17372,6 +17377,10 @@ async function renameEmployee(oldName, newName, opts = {}) {
|
|
|
17372
17377
|
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
17373
17378
|
}
|
|
17374
17379
|
}
|
|
17380
|
+
function rewriteRenamedEmployeeContent(content, oldName, newName) {
|
|
17381
|
+
const withAgentId = content.replace(/^(agent_id:\s*)\S+/m, `$1${newName}`);
|
|
17382
|
+
return personalizePrompt(withAgentId, oldName, newName);
|
|
17383
|
+
}
|
|
17375
17384
|
function findExeBin2() {
|
|
17376
17385
|
try {
|
|
17377
17386
|
return execSync12(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
@@ -18295,8 +18304,8 @@ async function validateModel(log) {
|
|
|
18295
18304
|
log("Skipping in-memory model validation (low memory \u2014 will validate on first use).");
|
|
18296
18305
|
const modelPath = path38.join(MODELS_DIR, LOCAL_FILENAME);
|
|
18297
18306
|
if (existsSync32(modelPath)) {
|
|
18298
|
-
const { statSync:
|
|
18299
|
-
const size =
|
|
18307
|
+
const { statSync: statSync8 } = await import("fs");
|
|
18308
|
+
const size = statSync8(modelPath).size;
|
|
18300
18309
|
if (size > 300 * 1e6) {
|
|
18301
18310
|
log(`Model file verified (${(size / 1e6).toFixed(0)} MB).`);
|
|
18302
18311
|
return;
|
|
@@ -19500,6 +19509,101 @@ function createStackUpdatePlan(manifest, envRaw, targetVersion) {
|
|
|
19500
19509
|
breakingChanges: release.breakingChanges ?? []
|
|
19501
19510
|
};
|
|
19502
19511
|
}
|
|
19512
|
+
function validatePinnedGhcrImage(image, label) {
|
|
19513
|
+
const trimmed = image.trim().replace(/^['"]|['"]$/g, "");
|
|
19514
|
+
if (!trimmed) return `${label} is empty`;
|
|
19515
|
+
if (trimmed.includes("${")) return null;
|
|
19516
|
+
if (!trimmed.startsWith("ghcr.io/askexe/") && !trimmed.startsWith("registry.askexe.com/askexe/")) return `${label} must use ghcr.io/askexe/* or registry.askexe.com/askexe/*, got ${trimmed}`;
|
|
19517
|
+
if (/:latest(?:$|[\s#])/.test(trimmed)) return `${label} must not use :latest (${trimmed})`;
|
|
19518
|
+
if (!ASKEXE_GHCR_IMAGE.test(trimmed)) return `${label} must be pinned with an explicit tag or sha256 digest from ghcr.io/askexe or registry.askexe.com/askexe, got ${trimmed}`;
|
|
19519
|
+
return null;
|
|
19520
|
+
}
|
|
19521
|
+
function validateComposeImageLiteral(image, label) {
|
|
19522
|
+
const trimmed = image.trim().replace(/^['"]|['"]$/g, "");
|
|
19523
|
+
if (!trimmed) return `${label} is empty`;
|
|
19524
|
+
if (trimmed.startsWith("ghcr.io/askexe/") || trimmed.startsWith("registry.askexe.com/askexe/")) return validatePinnedGhcrImage(trimmed, label);
|
|
19525
|
+
if (/^(postgres|pgvector\/pgvector|clickhouse\/clickhouse-server|redis|nginx|postgrest\/postgrest|supabase\/gotrue):[^:]+$/i.test(trimmed)) return null;
|
|
19526
|
+
return `${label} uses unsupported non-AskExe image ${trimmed}; customer app images must come from pinned ghcr.io/askexe images`;
|
|
19527
|
+
}
|
|
19528
|
+
function collectProductionDeployGateIssues(plan, envRaw, composeRaw) {
|
|
19529
|
+
const issues = [];
|
|
19530
|
+
const env = parseEnv(envRaw);
|
|
19531
|
+
for (const [serviceName, service] of Object.entries(plan.release.services)) {
|
|
19532
|
+
const manifestIssue = validatePinnedGhcrImage(service.image, `manifest ${plan.targetVersion}.${serviceName}.image`);
|
|
19533
|
+
if (manifestIssue) issues.push({ kind: "manifest-image", message: manifestIssue });
|
|
19534
|
+
const envImage = env.get(service.env);
|
|
19535
|
+
if (envImage) {
|
|
19536
|
+
const envIssue = validatePinnedGhcrImage(envImage, `env ${service.env}`);
|
|
19537
|
+
if (envIssue) issues.push({ kind: "env-image", message: envIssue });
|
|
19538
|
+
}
|
|
19539
|
+
}
|
|
19540
|
+
const lines = composeRaw.split(/\r?\n/);
|
|
19541
|
+
lines.forEach((line, index) => {
|
|
19542
|
+
if (/^\s*build\s*:/.test(line)) {
|
|
19543
|
+
issues.push({ kind: "compose-build", message: `compose line ${index + 1} contains build:, production deploys must pull images` });
|
|
19544
|
+
}
|
|
19545
|
+
const imageMatch = line.match(/^\s*image\s*:\s*(.+?)\s*(?:#.*)?$/);
|
|
19546
|
+
if (imageMatch) {
|
|
19547
|
+
const image = imageMatch[1].trim();
|
|
19548
|
+
if (image.includes("${")) {
|
|
19549
|
+
const fallback = image.match(/:-([^}]+)}/)?.[1];
|
|
19550
|
+
if (fallback) {
|
|
19551
|
+
const composeIssue = validateComposeImageLiteral(fallback, `compose image fallback on line ${index + 1}`);
|
|
19552
|
+
if (composeIssue) issues.push({ kind: "compose-image", message: composeIssue });
|
|
19553
|
+
}
|
|
19554
|
+
} else {
|
|
19555
|
+
const composeIssue = validateComposeImageLiteral(image, `compose image on line ${index + 1}`);
|
|
19556
|
+
if (composeIssue) issues.push({ kind: "compose-image", message: composeIssue });
|
|
19557
|
+
}
|
|
19558
|
+
}
|
|
19559
|
+
});
|
|
19560
|
+
return issues;
|
|
19561
|
+
}
|
|
19562
|
+
function assertDeploymentScopeAllowed(plan, persona = "customer") {
|
|
19563
|
+
if (persona === "askexe-control-plane") return;
|
|
19564
|
+
const blocked = Object.entries(plan.release.services).filter(([, service]) => service.deploymentScope === "askexe-control-plane").map(([name]) => name);
|
|
19565
|
+
if (blocked.length > 0) {
|
|
19566
|
+
throw new Error(
|
|
19567
|
+
`Customer deployment manifest includes AskExe control-plane service(s): ${blocked.join(", ")}. Customer VPSs may deploy customer services and optional agents only.`
|
|
19568
|
+
);
|
|
19569
|
+
}
|
|
19570
|
+
}
|
|
19571
|
+
function assertProductionDeployGate(plan, envRaw, composeRaw, options = {}) {
|
|
19572
|
+
const issues = collectProductionDeployGateIssues(plan, envRaw, composeRaw);
|
|
19573
|
+
if (issues.length === 0) return;
|
|
19574
|
+
if (options.breakGlassReason?.trim()) {
|
|
19575
|
+
writeBreakGlassAudit(plan, issues, options);
|
|
19576
|
+
return;
|
|
19577
|
+
}
|
|
19578
|
+
const details = issues.map((issue) => `- [${issue.kind}] ${issue.message}`).join("\n");
|
|
19579
|
+
throw new Error(
|
|
19580
|
+
`Production deploy gate failed. Exe OS deploys must use pinned ghcr.io/askexe or registry.askexe.com/askexe images and must not build from source on the VPS.
|
|
19581
|
+
${details}
|
|
19582
|
+
Emergency override requires --break-glass <reason> and writes an audit file.`
|
|
19583
|
+
);
|
|
19584
|
+
}
|
|
19585
|
+
function writeBreakGlassAudit(plan, issues, options) {
|
|
19586
|
+
const now2 = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
19587
|
+
const stamp = now2().toISOString().replace(/[:.]/g, "-");
|
|
19588
|
+
const defaultDir = existsSync34("exe/output") ? "exe/output" : path41.dirname(options.envFile ?? ".");
|
|
19589
|
+
const auditFile = options.breakGlassAuditFile ?? path41.join(defaultDir, `stack-update-break-glass-${stamp}.md`);
|
|
19590
|
+
mkdirSync23(path41.dirname(auditFile), { recursive: true });
|
|
19591
|
+
const body = [
|
|
19592
|
+
`# Stack Update Break-Glass Audit \u2014 ${now2().toISOString()}`,
|
|
19593
|
+
"",
|
|
19594
|
+
`Target version: ${plan.targetVersion}`,
|
|
19595
|
+
`Reason: ${options.breakGlassReason?.trim()}`,
|
|
19596
|
+
"",
|
|
19597
|
+
"## Gate failures overridden",
|
|
19598
|
+
...issues.map((issue) => `- [${issue.kind}] ${issue.message}`),
|
|
19599
|
+
"",
|
|
19600
|
+
"## Required follow-up",
|
|
19601
|
+
"Return this deployment to the standard pinned GHCR image path immediately after the emergency is resolved.",
|
|
19602
|
+
""
|
|
19603
|
+
].join("\n");
|
|
19604
|
+
writeFileSync24(auditFile, body, { mode: 384 });
|
|
19605
|
+
console.warn(`[stack-update] BREAK-GLASS deploy override recorded: ${auditFile}`);
|
|
19606
|
+
}
|
|
19503
19607
|
function assertBreakingChangesAllowed(plan, allowedIds) {
|
|
19504
19608
|
const required = plan.breakingChanges.filter((c) => c.requiresConfirmation !== false);
|
|
19505
19609
|
const missing = required.filter((c) => !allowedIds.includes(c.id));
|
|
@@ -19522,6 +19626,15 @@ async function runStackUpdate(options) {
|
|
|
19522
19626
|
const envRaw = readFileSync28(options.envFile, "utf8");
|
|
19523
19627
|
const plan = createStackUpdatePlan(manifest, envRaw, options.targetVersion);
|
|
19524
19628
|
assertBreakingChangesAllowed(plan, options.allowedBreakingChangeIds ?? []);
|
|
19629
|
+
assertDeploymentScopeAllowed(plan, options.deploymentPersona ?? "customer");
|
|
19630
|
+
const plannedEnvRaw = patchEnv(envRaw, Object.fromEntries(plan.changes.map((c) => [c.key, c.after])));
|
|
19631
|
+
const composeRaw = readFileSync28(options.composeFile, "utf8");
|
|
19632
|
+
assertProductionDeployGate(plan, plannedEnvRaw, composeRaw, {
|
|
19633
|
+
breakGlassReason: options.breakGlassReason,
|
|
19634
|
+
breakGlassAuditFile: options.breakGlassAuditFile,
|
|
19635
|
+
now: now2,
|
|
19636
|
+
envFile: options.envFile
|
|
19637
|
+
});
|
|
19525
19638
|
const lockFile = options.lockFile ?? path41.join(path41.dirname(options.envFile), ".exe-stack-lock.json");
|
|
19526
19639
|
const previousVersion = readCurrentStackVersion(lockFile);
|
|
19527
19640
|
if (options.dryRun || plan.changes.length === 0) {
|
|
@@ -19693,9 +19806,11 @@ function loadDefaultPublicKey() {
|
|
|
19693
19806
|
}
|
|
19694
19807
|
return void 0;
|
|
19695
19808
|
}
|
|
19809
|
+
var ASKEXE_GHCR_IMAGE;
|
|
19696
19810
|
var init_stack_update = __esm({
|
|
19697
19811
|
"src/lib/stack-update.ts"() {
|
|
19698
19812
|
"use strict";
|
|
19813
|
+
ASKEXE_GHCR_IMAGE = /^(?:ghcr\.io\/askexe|registry\.askexe\.com\/askexe)\/[a-z0-9._/-]+(?::[^:@$/{]+|@sha256:[a-f0-9]{64})$/i;
|
|
19699
19814
|
}
|
|
19700
19815
|
});
|
|
19701
19816
|
|
|
@@ -19720,6 +19835,7 @@ function parseArgs4(args2) {
|
|
|
19720
19835
|
dryRun: false,
|
|
19721
19836
|
check: false,
|
|
19722
19837
|
rollback: false,
|
|
19838
|
+
deploymentPersona: process.env.EXE_STACK_DEPLOYMENT_PERSONA === "askexe-control-plane" ? "askexe-control-plane" : "customer",
|
|
19723
19839
|
yes: false,
|
|
19724
19840
|
allowedBreakingChangeIds: []
|
|
19725
19841
|
};
|
|
@@ -19750,6 +19866,18 @@ function parseArgs4(args2) {
|
|
|
19750
19866
|
else if (arg === "--license-key") opts.licenseKey = next();
|
|
19751
19867
|
else if (arg.startsWith("--license-key=")) opts.licenseKey = arg.split("=").slice(1).join("=");
|
|
19752
19868
|
else if (arg === "--rollback") opts.rollback = true;
|
|
19869
|
+
else if (arg === "--deployment-persona") {
|
|
19870
|
+
const value = next();
|
|
19871
|
+
if (value !== "customer" && value !== "askexe-control-plane") throw new Error(`Invalid --deployment-persona: ${value}`);
|
|
19872
|
+
opts.deploymentPersona = value;
|
|
19873
|
+
} else if (arg.startsWith("--deployment-persona=")) {
|
|
19874
|
+
const value = arg.split("=").slice(1).join("=");
|
|
19875
|
+
if (value !== "customer" && value !== "askexe-control-plane") throw new Error(`Invalid --deployment-persona: ${value}`);
|
|
19876
|
+
opts.deploymentPersona = value;
|
|
19877
|
+
} else if (arg === "--break-glass") opts.breakGlassReason = next();
|
|
19878
|
+
else if (arg.startsWith("--break-glass=")) opts.breakGlassReason = arg.split("=").slice(1).join("=");
|
|
19879
|
+
else if (arg === "--break-glass-audit-file") opts.breakGlassAuditFile = next();
|
|
19880
|
+
else if (arg.startsWith("--break-glass-audit-file=")) opts.breakGlassAuditFile = arg.split("=").slice(1).join("=");
|
|
19753
19881
|
else if (arg === "--dry-run") opts.dryRun = true;
|
|
19754
19882
|
else if (arg === "--check") opts.check = true;
|
|
19755
19883
|
else if (arg === "--yes" || arg === "-y") opts.yes = true;
|
|
@@ -19787,6 +19915,9 @@ Options:
|
|
|
19787
19915
|
--device-id <id> Device ID to include in deploy audit
|
|
19788
19916
|
--license-key <key> License key to include in deploy audit
|
|
19789
19917
|
--rollback Restore latest backed-up .env and restart stack
|
|
19918
|
+
--deployment-persona <customer|askexe-control-plane> Scope gate (default: customer)
|
|
19919
|
+
--break-glass <reason> Emergency override for production deploy gate; writes audit file
|
|
19920
|
+
--break-glass-audit-file <path> Audit file path for --break-glass
|
|
19790
19921
|
--allow-breaking <ids> Confirm breaking changes, comma-separated
|
|
19791
19922
|
-y, --yes Non-interactive confirmation
|
|
19792
19923
|
`);
|
|
@@ -19826,6 +19957,13 @@ async function main6() {
|
|
|
19826
19957
|
const manifest = await loadStackManifest(opts.manifestRef, void 0, opts.manifestPublicKey, opts.manifestAuthToken);
|
|
19827
19958
|
const envRaw = readFileSync29(opts.envFile, "utf8");
|
|
19828
19959
|
const plan = createStackUpdatePlan(manifest, envRaw, opts.targetVersion);
|
|
19960
|
+
assertDeploymentScopeAllowed(plan, opts.deploymentPersona);
|
|
19961
|
+
const plannedEnvRaw = patchEnv(envRaw, Object.fromEntries(plan.changes.map((c) => [c.key, c.after])));
|
|
19962
|
+
assertProductionDeployGate(plan, plannedEnvRaw, readFileSync29(opts.composeFile, "utf8"), {
|
|
19963
|
+
breakGlassReason: opts.breakGlassReason,
|
|
19964
|
+
breakGlassAuditFile: opts.breakGlassAuditFile,
|
|
19965
|
+
envFile: opts.envFile
|
|
19966
|
+
});
|
|
19829
19967
|
console.log(`Exe OS stack target: ${plan.targetVersion}`);
|
|
19830
19968
|
console.log(`Manifest: ${opts.manifestRef}`);
|
|
19831
19969
|
console.log(`Compose: ${opts.composeFile}`);
|
|
@@ -19858,6 +19996,210 @@ var init_stack_update2 = __esm({
|
|
|
19858
19996
|
}
|
|
19859
19997
|
});
|
|
19860
19998
|
|
|
19999
|
+
// src/lib/registry-proxy.ts
|
|
20000
|
+
import { createServer } from "http";
|
|
20001
|
+
import { Readable } from "stream";
|
|
20002
|
+
function parsePullTokens(raw) {
|
|
20003
|
+
return (raw ?? "").split(/[\n,]/).map((s) => s.trim()).filter(Boolean);
|
|
20004
|
+
}
|
|
20005
|
+
function registryProxyOptionsFromEnv(env = process.env) {
|
|
20006
|
+
const upstreamToken = env.EXE_REGISTRY_PROXY_UPSTREAM_TOKEN || env.GHCR_TOKEN || "";
|
|
20007
|
+
const pullTokens = parsePullTokens(env.EXE_REGISTRY_PROXY_PULL_TOKENS);
|
|
20008
|
+
return {
|
|
20009
|
+
port: Number(env.EXE_REGISTRY_PROXY_PORT || 3201),
|
|
20010
|
+
host: env.EXE_REGISTRY_PROXY_HOST || "0.0.0.0",
|
|
20011
|
+
upstream: env.EXE_REGISTRY_PROXY_UPSTREAM || "https://ghcr.io",
|
|
20012
|
+
upstreamUsername: env.EXE_REGISTRY_PROXY_UPSTREAM_USERNAME || env.GHCR_USERNAME || "askexe",
|
|
20013
|
+
upstreamToken,
|
|
20014
|
+
pullTokens,
|
|
20015
|
+
allowedNamespace: env.EXE_REGISTRY_PROXY_ALLOWED_NAMESPACE || "askexe"
|
|
20016
|
+
};
|
|
20017
|
+
}
|
|
20018
|
+
function assertRegistryProxyConfig(options) {
|
|
20019
|
+
if (!options.upstreamToken) throw new Error("EXE_REGISTRY_PROXY_UPSTREAM_TOKEN or GHCR_TOKEN is required");
|
|
20020
|
+
if (options.pullTokens.length === 0) throw new Error("EXE_REGISTRY_PROXY_PULL_TOKENS is required");
|
|
20021
|
+
if (!options.allowedNamespace || !/^[a-z0-9._-]+$/i.test(options.allowedNamespace)) {
|
|
20022
|
+
throw new Error("EXE_REGISTRY_PROXY_ALLOWED_NAMESPACE must be a registry-safe namespace");
|
|
20023
|
+
}
|
|
20024
|
+
}
|
|
20025
|
+
function timingSafeIncludes(values, candidate) {
|
|
20026
|
+
return values.some((value) => value === candidate);
|
|
20027
|
+
}
|
|
20028
|
+
function parseBasicAuth(header) {
|
|
20029
|
+
if (!header?.startsWith("Basic ")) return null;
|
|
20030
|
+
try {
|
|
20031
|
+
const decoded = Buffer.from(header.slice("Basic ".length), "base64").toString("utf8");
|
|
20032
|
+
const idx = decoded.indexOf(":");
|
|
20033
|
+
if (idx < 0) return null;
|
|
20034
|
+
return { username: decoded.slice(0, idx), password: decoded.slice(idx + 1) };
|
|
20035
|
+
} catch {
|
|
20036
|
+
return null;
|
|
20037
|
+
}
|
|
20038
|
+
}
|
|
20039
|
+
function unauthorized(res) {
|
|
20040
|
+
res.writeHead(401, {
|
|
20041
|
+
"www-authenticate": 'Basic realm="AskExe Registry Proxy"',
|
|
20042
|
+
"content-type": "application/json"
|
|
20043
|
+
});
|
|
20044
|
+
res.end(JSON.stringify({ error: "registry proxy authentication required" }));
|
|
20045
|
+
}
|
|
20046
|
+
function isAllowedPath(pathname, namespace) {
|
|
20047
|
+
if (pathname === "/health") return true;
|
|
20048
|
+
if (pathname === "/v2/" || pathname === "/v2") return true;
|
|
20049
|
+
const prefix = `/v2/${namespace}/`;
|
|
20050
|
+
if (!pathname.startsWith(prefix)) return false;
|
|
20051
|
+
if (pathname.includes("..") || /%2f/i.test(pathname)) return false;
|
|
20052
|
+
return true;
|
|
20053
|
+
}
|
|
20054
|
+
function parseBearerChallenge(header) {
|
|
20055
|
+
if (!header?.toLowerCase().startsWith("bearer ")) return null;
|
|
20056
|
+
const raw = header.slice("Bearer ".length);
|
|
20057
|
+
const params = new URLSearchParams();
|
|
20058
|
+
for (const part of raw.match(/(?:[^,"]+|"[^"]*")+/g) ?? []) {
|
|
20059
|
+
const idx = part.indexOf("=");
|
|
20060
|
+
if (idx < 0) continue;
|
|
20061
|
+
const key = part.slice(0, idx).trim();
|
|
20062
|
+
const value = part.slice(idx + 1).trim().replace(/^"|"$/g, "");
|
|
20063
|
+
params.set(key, value);
|
|
20064
|
+
}
|
|
20065
|
+
const realm = params.get("realm");
|
|
20066
|
+
if (!realm) return null;
|
|
20067
|
+
params.delete("realm");
|
|
20068
|
+
return { realm, params };
|
|
20069
|
+
}
|
|
20070
|
+
async function fetchUpstreamWithRegistryAuth(url, init, upstreamBasicAuth) {
|
|
20071
|
+
const firstHeaders = new Headers(init.headers);
|
|
20072
|
+
firstHeaders.set("authorization", `Basic ${upstreamBasicAuth}`);
|
|
20073
|
+
const first = await fetch(url, { ...init, headers: firstHeaders });
|
|
20074
|
+
if (first.status !== 401) return first;
|
|
20075
|
+
const challenge = parseBearerChallenge(first.headers.get("www-authenticate"));
|
|
20076
|
+
if (!challenge) return first;
|
|
20077
|
+
const tokenUrl = new URL(challenge.realm);
|
|
20078
|
+
for (const [key, value] of challenge.params.entries()) tokenUrl.searchParams.set(key, value);
|
|
20079
|
+
const tokenRes = await fetch(tokenUrl, { headers: { authorization: `Basic ${upstreamBasicAuth}` } });
|
|
20080
|
+
if (!tokenRes.ok) return first;
|
|
20081
|
+
const tokenJson = await tokenRes.json();
|
|
20082
|
+
const token = tokenJson.token ?? tokenJson.access_token;
|
|
20083
|
+
if (!token) return first;
|
|
20084
|
+
const retryHeaders = new Headers(init.headers);
|
|
20085
|
+
retryHeaders.set("authorization", `Bearer ${token}`);
|
|
20086
|
+
return fetch(url, { ...init, headers: retryHeaders });
|
|
20087
|
+
}
|
|
20088
|
+
function copyResponseHeaders(from, to) {
|
|
20089
|
+
for (const [key, value] of from.entries()) {
|
|
20090
|
+
const lower = key.toLowerCase();
|
|
20091
|
+
if (["connection", "keep-alive", "transfer-encoding", "content-encoding"].includes(lower)) continue;
|
|
20092
|
+
to.setHeader(key, value);
|
|
20093
|
+
}
|
|
20094
|
+
}
|
|
20095
|
+
function createRegistryProxyServer(options) {
|
|
20096
|
+
assertRegistryProxyConfig(options);
|
|
20097
|
+
const upstream = new URL(options.upstream ?? "https://ghcr.io");
|
|
20098
|
+
const namespace = options.allowedNamespace ?? "askexe";
|
|
20099
|
+
const upstreamAuth = Buffer.from(`${options.upstreamUsername ?? "askexe"}:${options.upstreamToken}`).toString("base64");
|
|
20100
|
+
return createServer(async (req, res) => {
|
|
20101
|
+
const requestUrl = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
|
20102
|
+
if (requestUrl.pathname === "/health") {
|
|
20103
|
+
res.writeHead(200, { "content-type": "application/json" });
|
|
20104
|
+
res.end(JSON.stringify({ ok: true, service: "exe-registry-proxy", upstream: upstream.origin, namespace }));
|
|
20105
|
+
return;
|
|
20106
|
+
}
|
|
20107
|
+
if (!isAllowedPath(requestUrl.pathname, namespace)) {
|
|
20108
|
+
res.writeHead(404, { "content-type": "application/json" });
|
|
20109
|
+
res.end(JSON.stringify({ error: "registry path not allowed" }));
|
|
20110
|
+
return;
|
|
20111
|
+
}
|
|
20112
|
+
const auth = parseBasicAuth(req.headers.authorization);
|
|
20113
|
+
if (!auth || !timingSafeIncludes(options.pullTokens, auth.password)) {
|
|
20114
|
+
unauthorized(res);
|
|
20115
|
+
return;
|
|
20116
|
+
}
|
|
20117
|
+
const upstreamUrl = new URL(requestUrl.pathname + requestUrl.search, upstream.origin);
|
|
20118
|
+
const headers = new Headers();
|
|
20119
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
20120
|
+
if (!value) continue;
|
|
20121
|
+
const lower = key.toLowerCase();
|
|
20122
|
+
if (["host", "connection", "authorization", "content-length"].includes(lower)) continue;
|
|
20123
|
+
headers.set(key, Array.isArray(value) ? value.join(",") : value);
|
|
20124
|
+
}
|
|
20125
|
+
headers.set("authorization", `Basic ${upstreamAuth}`);
|
|
20126
|
+
try {
|
|
20127
|
+
const upstreamRes = await fetchUpstreamWithRegistryAuth(upstreamUrl, {
|
|
20128
|
+
method: req.method,
|
|
20129
|
+
headers,
|
|
20130
|
+
body: req.method === "GET" || req.method === "HEAD" ? void 0 : req,
|
|
20131
|
+
// Required by undici when streaming request bodies.
|
|
20132
|
+
duplex: "half"
|
|
20133
|
+
}, upstreamAuth);
|
|
20134
|
+
res.statusCode = upstreamRes.status;
|
|
20135
|
+
res.statusMessage = upstreamRes.statusText;
|
|
20136
|
+
copyResponseHeaders(upstreamRes.headers, res);
|
|
20137
|
+
if (!upstreamRes.body || req.method === "HEAD") {
|
|
20138
|
+
res.end();
|
|
20139
|
+
return;
|
|
20140
|
+
}
|
|
20141
|
+
Readable.fromWeb(upstreamRes.body).pipe(res);
|
|
20142
|
+
} catch (err) {
|
|
20143
|
+
res.writeHead(502, { "content-type": "application/json" });
|
|
20144
|
+
res.end(JSON.stringify({ error: "upstream registry proxy failed", detail: err instanceof Error ? err.message : String(err) }));
|
|
20145
|
+
}
|
|
20146
|
+
});
|
|
20147
|
+
}
|
|
20148
|
+
async function runRegistryProxy(options = registryProxyOptionsFromEnv()) {
|
|
20149
|
+
const server = createRegistryProxyServer(options);
|
|
20150
|
+
await new Promise((resolve) => server.listen(options.port, options.host, resolve));
|
|
20151
|
+
console.log(`exe-registry-proxy listening on ${options.host ?? "0.0.0.0"}:${options.port}`);
|
|
20152
|
+
console.log(`proxying /v2/${options.allowedNamespace ?? "askexe"}/* -> ${options.upstream ?? "https://ghcr.io"}`);
|
|
20153
|
+
}
|
|
20154
|
+
var init_registry_proxy = __esm({
|
|
20155
|
+
"src/lib/registry-proxy.ts"() {
|
|
20156
|
+
"use strict";
|
|
20157
|
+
}
|
|
20158
|
+
});
|
|
20159
|
+
|
|
20160
|
+
// src/bin/registry-proxy.ts
|
|
20161
|
+
var registry_proxy_exports = {};
|
|
20162
|
+
__export(registry_proxy_exports, {
|
|
20163
|
+
main: () => main7
|
|
20164
|
+
});
|
|
20165
|
+
function printHelp2() {
|
|
20166
|
+
console.log(`exe-os registry-proxy \u2014 authenticated pull-through proxy for AskExe customer images
|
|
20167
|
+
|
|
20168
|
+
Environment:
|
|
20169
|
+
EXE_REGISTRY_PROXY_PORT=3201
|
|
20170
|
+
EXE_REGISTRY_PROXY_HOST=0.0.0.0
|
|
20171
|
+
EXE_REGISTRY_PROXY_UPSTREAM=https://ghcr.io
|
|
20172
|
+
EXE_REGISTRY_PROXY_UPSTREAM_USERNAME=<github-user>
|
|
20173
|
+
EXE_REGISTRY_PROXY_UPSTREAM_TOKEN=<askexe-ghcr-read-token>
|
|
20174
|
+
EXE_REGISTRY_PROXY_PULL_TOKENS=<customer-token-1,customer-token-2>
|
|
20175
|
+
EXE_REGISTRY_PROXY_ALLOWED_NAMESPACE=askexe
|
|
20176
|
+
|
|
20177
|
+
Docker image shape for clients:
|
|
20178
|
+
registry.askexe.com/askexe/exed:v0.9.3
|
|
20179
|
+
registry.askexe.com/askexe/exe-crm:v0.9.3
|
|
20180
|
+
`);
|
|
20181
|
+
}
|
|
20182
|
+
async function main7(args2 = process.argv.slice(2)) {
|
|
20183
|
+
if (args2.includes("--help") || args2.includes("-h")) {
|
|
20184
|
+
printHelp2();
|
|
20185
|
+
return;
|
|
20186
|
+
}
|
|
20187
|
+
await runRegistryProxy(registryProxyOptionsFromEnv());
|
|
20188
|
+
}
|
|
20189
|
+
var init_registry_proxy2 = __esm({
|
|
20190
|
+
"src/bin/registry-proxy.ts"() {
|
|
20191
|
+
"use strict";
|
|
20192
|
+
init_is_main();
|
|
20193
|
+
init_registry_proxy();
|
|
20194
|
+
if (isMainModule(import.meta.url)) {
|
|
20195
|
+
main7().catch((err) => {
|
|
20196
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
20197
|
+
process.exit(1);
|
|
20198
|
+
});
|
|
20199
|
+
}
|
|
20200
|
+
}
|
|
20201
|
+
});
|
|
20202
|
+
|
|
19861
20203
|
// node_modules/es-toolkit/dist/function/debounce.mjs
|
|
19862
20204
|
function debounce(func, debounceMs, { signal, edges } = {}) {
|
|
19863
20205
|
let pendingThis = void 0;
|
|
@@ -24216,8 +24558,8 @@ var init_ErrorOverview = __esm({
|
|
|
24216
24558
|
"use strict";
|
|
24217
24559
|
init_Box();
|
|
24218
24560
|
init_Text();
|
|
24219
|
-
cleanupPath = (
|
|
24220
|
-
return
|
|
24561
|
+
cleanupPath = (path55) => {
|
|
24562
|
+
return path55?.replace(`file://${cwd()}/`, "");
|
|
24221
24563
|
};
|
|
24222
24564
|
stackUtils = new StackUtils({
|
|
24223
24565
|
cwd: cwd(),
|
|
@@ -26625,11 +26967,11 @@ function Footer() {
|
|
|
26625
26967
|
} catch {
|
|
26626
26968
|
}
|
|
26627
26969
|
try {
|
|
26628
|
-
const { existsSync:
|
|
26970
|
+
const { existsSync: existsSync41 } = await import("fs");
|
|
26629
26971
|
const { join } = await import("path");
|
|
26630
26972
|
const home = process.env.HOME ?? "";
|
|
26631
26973
|
const pidPath = join(home, ".exe-os", "exed.pid");
|
|
26632
|
-
setDaemon(
|
|
26974
|
+
setDaemon(existsSync41(pidPath) ? "running" : "stopped");
|
|
26633
26975
|
} catch {
|
|
26634
26976
|
setDaemon("unknown");
|
|
26635
26977
|
}
|
|
@@ -29413,15 +29755,15 @@ function CommandCenterView({
|
|
|
29413
29755
|
const { createPermissionsFromPreset: createPermissionsFromPreset2, EMPLOYEE_PERMISSIONS: EMPLOYEE_PERMISSIONS2 } = await Promise.resolve().then(() => (init_permissions(), permissions_exports));
|
|
29414
29756
|
const { getPresetByRole: getPresetByRole2 } = await Promise.resolve().then(() => (init_permission_presets(), permission_presets_exports));
|
|
29415
29757
|
const { createDefaultHooks: createDefaultHooks2 } = await Promise.resolve().then(() => (init_hooks(), hooks_exports));
|
|
29416
|
-
const { readFileSync:
|
|
29758
|
+
const { readFileSync: readFileSync36, existsSync: existsSync41 } = await import("fs");
|
|
29417
29759
|
const { join } = await import("path");
|
|
29418
29760
|
const { homedir: homedir8 } = await import("os");
|
|
29419
29761
|
const configPath = join(homedir8(), ".exe-os", "config.json");
|
|
29420
29762
|
let failoverChain = ["anthropic", "opencode", "gemini", "openai"];
|
|
29421
29763
|
let providerConfigs = {};
|
|
29422
|
-
if (
|
|
29764
|
+
if (existsSync41(configPath)) {
|
|
29423
29765
|
try {
|
|
29424
|
-
const raw = JSON.parse(
|
|
29766
|
+
const raw = JSON.parse(readFileSync36(configPath, "utf8"));
|
|
29425
29767
|
if (Array.isArray(raw.failoverChain)) failoverChain = raw.failoverChain;
|
|
29426
29768
|
if (raw.providers && typeof raw.providers === "object") {
|
|
29427
29769
|
providerConfigs = raw.providers;
|
|
@@ -29482,7 +29824,7 @@ function CommandCenterView({
|
|
|
29482
29824
|
const markerDir = join(homedir8(), ".exe-os", "session-cache");
|
|
29483
29825
|
const agentFiles = (await import("fs")).readdirSync(markerDir).filter((f) => f.startsWith("active-agent-"));
|
|
29484
29826
|
for (const f of agentFiles) {
|
|
29485
|
-
const data = JSON.parse(
|
|
29827
|
+
const data = JSON.parse(readFileSync36(join(markerDir, f), "utf8"));
|
|
29486
29828
|
if (data.agentRole) {
|
|
29487
29829
|
agentRole = data.agentRole;
|
|
29488
29830
|
break;
|
|
@@ -29665,7 +30007,7 @@ function CommandCenterView({
|
|
|
29665
30007
|
const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session_registry(), session_registry_exports));
|
|
29666
30008
|
const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
|
|
29667
30009
|
const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
29668
|
-
const { existsSync:
|
|
30010
|
+
const { existsSync: existsSync41 } = await import("fs");
|
|
29669
30011
|
const { join } = await import("path");
|
|
29670
30012
|
const client = getClient2();
|
|
29671
30013
|
if (!client) {
|
|
@@ -29736,7 +30078,7 @@ function CommandCenterView({
|
|
|
29736
30078
|
}
|
|
29737
30079
|
const memoryCount = memoryCounts.get(name) ?? 0;
|
|
29738
30080
|
const openTaskCount = openTaskCounts.get(name) ?? 0;
|
|
29739
|
-
const hasGit = projectDir ?
|
|
30081
|
+
const hasGit = projectDir ? existsSync41(join(projectDir, ".git")) : false;
|
|
29740
30082
|
const type = hasGit ? "code" : memoryCount > 0 ? "code" : "automation";
|
|
29741
30083
|
projectList.push({
|
|
29742
30084
|
projectName: name,
|
|
@@ -29761,7 +30103,7 @@ function CommandCenterView({
|
|
|
29761
30103
|
setHealth((h) => ({ ...h, memories: Number(totalResult.rows[0]?.cnt ?? 0) }));
|
|
29762
30104
|
try {
|
|
29763
30105
|
const pidPath = join(process.env.HOME ?? "", ".exe-os", "exed.pid");
|
|
29764
|
-
setHealth((h) => ({ ...h, daemon:
|
|
30106
|
+
setHealth((h) => ({ ...h, daemon: existsSync41(pidPath) ? "running" : "stopped" }));
|
|
29765
30107
|
} catch {
|
|
29766
30108
|
}
|
|
29767
30109
|
const activityResult = await client.execute(
|
|
@@ -31884,12 +32226,12 @@ async function loadGatewayConfig() {
|
|
|
31884
32226
|
state.running = false;
|
|
31885
32227
|
}
|
|
31886
32228
|
try {
|
|
31887
|
-
const { existsSync:
|
|
32229
|
+
const { existsSync: existsSync41, readFileSync: readFileSync36 } = await import("fs");
|
|
31888
32230
|
const { join } = await import("path");
|
|
31889
32231
|
const home = process.env.HOME ?? "";
|
|
31890
32232
|
const configPath = join(home, ".exe-os", "gateway.json");
|
|
31891
|
-
if (
|
|
31892
|
-
const raw = JSON.parse(
|
|
32233
|
+
if (existsSync41(configPath)) {
|
|
32234
|
+
const raw = JSON.parse(readFileSync36(configPath, "utf8"));
|
|
31893
32235
|
state.port = raw.port ?? 3100;
|
|
31894
32236
|
state.gatewayUrl = raw.gatewayUrl ?? "";
|
|
31895
32237
|
if (raw.adapters) {
|
|
@@ -32487,12 +32829,12 @@ function TeamView({ onBack, onViewSessions }) {
|
|
|
32487
32829
|
setMembers(teamData);
|
|
32488
32830
|
setDbError(null);
|
|
32489
32831
|
try {
|
|
32490
|
-
const { existsSync:
|
|
32832
|
+
const { existsSync: existsSync41, readFileSync: readFileSync36 } = await import("fs");
|
|
32491
32833
|
const { join } = await import("path");
|
|
32492
32834
|
const home = process.env.HOME ?? "";
|
|
32493
32835
|
const gatewayConfig = join(home, ".exe-os", "gateway.json");
|
|
32494
|
-
if (
|
|
32495
|
-
const raw = JSON.parse(
|
|
32836
|
+
if (existsSync41(gatewayConfig)) {
|
|
32837
|
+
const raw = JSON.parse(readFileSync36(gatewayConfig, "utf8"));
|
|
32496
32838
|
if (raw.agents && raw.agents.length > 0) {
|
|
32497
32839
|
setExternals(raw.agents.map((a) => ({
|
|
32498
32840
|
name: a.name,
|
|
@@ -32672,8 +33014,8 @@ __export(wiki_client_exports, {
|
|
|
32672
33014
|
listDocuments: () => listDocuments,
|
|
32673
33015
|
listWorkspaces: () => listWorkspaces
|
|
32674
33016
|
});
|
|
32675
|
-
async function wikiFetch(config,
|
|
32676
|
-
const url = `${config.baseUrl}/api/v1${
|
|
33017
|
+
async function wikiFetch(config, path55, method = "GET", body) {
|
|
33018
|
+
const url = `${config.baseUrl}/api/v1${path55}`;
|
|
32677
33019
|
const headers = {
|
|
32678
33020
|
Authorization: `Bearer ${config.apiKey}`,
|
|
32679
33021
|
"Content-Type": "application/json"
|
|
@@ -32706,7 +33048,7 @@ async function wikiFetch(config, path54, method = "GET", body) {
|
|
|
32706
33048
|
}
|
|
32707
33049
|
}
|
|
32708
33050
|
if (!response.ok) {
|
|
32709
|
-
throw new Error(`Wiki API ${method} ${
|
|
33051
|
+
throw new Error(`Wiki API ${method} ${path55}: ${response.status} ${response.statusText}`);
|
|
32710
33052
|
}
|
|
32711
33053
|
return response.json();
|
|
32712
33054
|
} finally {
|
|
@@ -33300,12 +33642,12 @@ function SettingsView({ onBack }) {
|
|
|
33300
33642
|
}
|
|
33301
33643
|
setProviders(providerList);
|
|
33302
33644
|
try {
|
|
33303
|
-
const { existsSync:
|
|
33645
|
+
const { existsSync: existsSync41 } = await import("fs");
|
|
33304
33646
|
const { join } = await import("path");
|
|
33305
33647
|
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
33306
33648
|
const cfg = await loadConfig2();
|
|
33307
33649
|
const home = process.env.HOME ?? "";
|
|
33308
|
-
const hasKey =
|
|
33650
|
+
const hasKey = existsSync41(join(home, ".exe-os", "master.key"));
|
|
33309
33651
|
if (cfg.cloud) {
|
|
33310
33652
|
setCloud({
|
|
33311
33653
|
configured: true,
|
|
@@ -33318,22 +33660,22 @@ function SettingsView({ onBack }) {
|
|
|
33318
33660
|
const pidPath = join(home, ".exe-os", "exed.pid");
|
|
33319
33661
|
let daemon = "unknown";
|
|
33320
33662
|
try {
|
|
33321
|
-
daemon =
|
|
33663
|
+
daemon = existsSync41(pidPath) ? "running" : "stopped";
|
|
33322
33664
|
} catch {
|
|
33323
33665
|
}
|
|
33324
33666
|
let version = "unknown";
|
|
33325
33667
|
try {
|
|
33326
|
-
const { readFileSync:
|
|
33668
|
+
const { readFileSync: readFileSync36 } = await import("fs");
|
|
33327
33669
|
const { createRequire: createRequire3 } = await import("module");
|
|
33328
33670
|
const require2 = createRequire3(import.meta.url);
|
|
33329
33671
|
const pkgPath = require2.resolve("@askexenow/exe-os/package.json");
|
|
33330
|
-
const pkg = JSON.parse(
|
|
33672
|
+
const pkg = JSON.parse(readFileSync36(pkgPath, "utf8"));
|
|
33331
33673
|
version = pkg.version;
|
|
33332
33674
|
} catch {
|
|
33333
33675
|
try {
|
|
33334
|
-
const { readFileSync:
|
|
33676
|
+
const { readFileSync: readFileSync36 } = await import("fs");
|
|
33335
33677
|
const { join: joinPath } = await import("path");
|
|
33336
|
-
const pkg = JSON.parse(
|
|
33678
|
+
const pkg = JSON.parse(readFileSync36(joinPath(process.cwd(), "package.json"), "utf8"));
|
|
33337
33679
|
version = pkg.version;
|
|
33338
33680
|
} catch {
|
|
33339
33681
|
}
|
|
@@ -33941,6 +34283,662 @@ Unhandled rejection: ${reason}
|
|
|
33941
34283
|
}
|
|
33942
34284
|
});
|
|
33943
34285
|
|
|
34286
|
+
// src/lib/code-chunker.ts
|
|
34287
|
+
import ts from "typescript";
|
|
34288
|
+
function languageForFile(filePath) {
|
|
34289
|
+
const base = filePath.split(/[\\/]/).pop()?.toLowerCase() ?? "";
|
|
34290
|
+
if (["dockerfile", "makefile", "rakefile", "gemfile"].includes(base)) return base;
|
|
34291
|
+
const ext = base.includes(".") ? base.split(".").pop() : void 0;
|
|
34292
|
+
return ext ? LANGUAGE_BY_EXTENSION[ext] : void 0;
|
|
34293
|
+
}
|
|
34294
|
+
function chunkSourceFile(source, fileName = "file.ts") {
|
|
34295
|
+
const language = languageForFile(fileName);
|
|
34296
|
+
if (language === "typescript" || language === "javascript") {
|
|
34297
|
+
return chunkTypeScriptLike(source, fileName);
|
|
34298
|
+
}
|
|
34299
|
+
return chunkGenericSource(source, fileName, language);
|
|
34300
|
+
}
|
|
34301
|
+
function chunkTypeScriptLike(source, fileName) {
|
|
34302
|
+
const sourceFile = ts.createSourceFile(
|
|
34303
|
+
fileName,
|
|
34304
|
+
source,
|
|
34305
|
+
ts.ScriptTarget.Latest,
|
|
34306
|
+
true,
|
|
34307
|
+
fileName.endsWith(".tsx") || fileName.endsWith(".jsx") ? ts.ScriptKind.TSX : ts.ScriptKind.TS
|
|
34308
|
+
);
|
|
34309
|
+
const chunks = [];
|
|
34310
|
+
const lines = source.split("\n");
|
|
34311
|
+
const importLines = [];
|
|
34312
|
+
function getLineNumber(pos) {
|
|
34313
|
+
return sourceFile.getLineAndCharacterOfPosition(pos).line + 1;
|
|
34314
|
+
}
|
|
34315
|
+
function getLeadingComment(node) {
|
|
34316
|
+
const fullText = sourceFile.getFullText();
|
|
34317
|
+
const ranges = ts.getLeadingCommentRanges(fullText, node.getFullStart());
|
|
34318
|
+
if (!ranges || ranges.length === 0) return void 0;
|
|
34319
|
+
return ranges.map((r) => fullText.slice(r.pos, r.end)).join("\n");
|
|
34320
|
+
}
|
|
34321
|
+
function getNodeText(node) {
|
|
34322
|
+
return node.getText(sourceFile);
|
|
34323
|
+
}
|
|
34324
|
+
function getName(node) {
|
|
34325
|
+
if (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node)) {
|
|
34326
|
+
return node.name?.getText(sourceFile) ?? "(anonymous)";
|
|
34327
|
+
}
|
|
34328
|
+
if (ts.isClassDeclaration(node)) return node.name?.getText(sourceFile) ?? "(anonymous class)";
|
|
34329
|
+
if (ts.isInterfaceDeclaration(node)) return node.name.getText(sourceFile);
|
|
34330
|
+
if (ts.isTypeAliasDeclaration(node)) return node.name.getText(sourceFile);
|
|
34331
|
+
if (ts.isEnumDeclaration(node)) return node.name.getText(sourceFile);
|
|
34332
|
+
if (ts.isVariableStatement(node)) return node.declarationList.declarations.map((d) => d.name.getText(sourceFile)).join(", ");
|
|
34333
|
+
if (ts.isExportAssignment(node)) return "default export";
|
|
34334
|
+
return "(unknown)";
|
|
34335
|
+
}
|
|
34336
|
+
function visitTopLevel(node) {
|
|
34337
|
+
if (ts.isImportDeclaration(node)) {
|
|
34338
|
+
importLines.push({ start: getLineNumber(node.getStart(sourceFile)), end: getLineNumber(node.getEnd()) });
|
|
34339
|
+
return;
|
|
34340
|
+
}
|
|
34341
|
+
if (ts.isFunctionDeclaration(node)) {
|
|
34342
|
+
chunks.push({ kind: "function", name: getName(node), text: getNodeText(node), startLine: getLineNumber(node.getStart(sourceFile)), endLine: getLineNumber(node.getEnd()), comment: getLeadingComment(node) });
|
|
34343
|
+
return;
|
|
34344
|
+
}
|
|
34345
|
+
if (ts.isClassDeclaration(node)) {
|
|
34346
|
+
chunks.push({ kind: "class", name: getName(node), text: getNodeText(node), startLine: getLineNumber(node.getStart(sourceFile)), endLine: getLineNumber(node.getEnd()), comment: getLeadingComment(node) });
|
|
34347
|
+
return;
|
|
34348
|
+
}
|
|
34349
|
+
if (ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node) || ts.isEnumDeclaration(node)) {
|
|
34350
|
+
chunks.push({ kind: "type", name: getName(node), text: getNodeText(node), startLine: getLineNumber(node.getStart(sourceFile)), endLine: getLineNumber(node.getEnd()), comment: getLeadingComment(node) });
|
|
34351
|
+
return;
|
|
34352
|
+
}
|
|
34353
|
+
if (ts.isVariableStatement(node)) {
|
|
34354
|
+
const isFnLike = node.declarationList.declarations.some((d) => d.initializer && (ts.isArrowFunction(d.initializer) || ts.isFunctionExpression(d.initializer)));
|
|
34355
|
+
chunks.push({ kind: isFnLike ? "function" : "variable", name: getName(node), text: getNodeText(node), startLine: getLineNumber(node.getStart(sourceFile)), endLine: getLineNumber(node.getEnd()), comment: getLeadingComment(node) });
|
|
34356
|
+
return;
|
|
34357
|
+
}
|
|
34358
|
+
if (ts.isExportAssignment(node)) {
|
|
34359
|
+
chunks.push({ kind: "export", name: "default export", text: getNodeText(node), startLine: getLineNumber(node.getStart(sourceFile)), endLine: getLineNumber(node.getEnd()), comment: getLeadingComment(node) });
|
|
34360
|
+
return;
|
|
34361
|
+
}
|
|
34362
|
+
if (ts.isExpressionStatement(node)) {
|
|
34363
|
+
const text = getNodeText(node);
|
|
34364
|
+
if (text.length > 10) chunks.push({ kind: "other", name: text.slice(0, 40).replace(/\n/g, " "), text, startLine: getLineNumber(node.getStart(sourceFile)), endLine: getLineNumber(node.getEnd()) });
|
|
34365
|
+
}
|
|
34366
|
+
}
|
|
34367
|
+
sourceFile.statements.forEach(visitTopLevel);
|
|
34368
|
+
if (importLines.length > 0) {
|
|
34369
|
+
const startLine = importLines[0].start;
|
|
34370
|
+
const endLine = importLines[importLines.length - 1].end;
|
|
34371
|
+
chunks.unshift({ kind: "import", name: `${importLines.length} imports`, text: lines.slice(startLine - 1, endLine).join("\n"), startLine, endLine });
|
|
34372
|
+
}
|
|
34373
|
+
chunks.sort((a, b) => a.startLine - b.startLine);
|
|
34374
|
+
return chunks.length > 0 ? chunks : chunkByWindows(source, 80);
|
|
34375
|
+
}
|
|
34376
|
+
function chunkGenericSource(source, _fileName, language) {
|
|
34377
|
+
const lines = source.split("\n");
|
|
34378
|
+
if (source.trim().length === 0) return [];
|
|
34379
|
+
if (language && TEXT_LIKE_LANGUAGES.has(language)) return chunkTextLike(lines, language);
|
|
34380
|
+
const chunks = [];
|
|
34381
|
+
const patterns = [
|
|
34382
|
+
{ kind: "function", regex: /^\s*(?:async\s+)?def\s+([A-Za-z_][\w]*)\s*\(/, name: (m) => m[1] },
|
|
34383
|
+
{ kind: "class", regex: /^\s*class\s+([A-Za-z_][\w]*)\b/, name: (m) => m[1] },
|
|
34384
|
+
{ kind: "function", regex: /^\s*(?:pub\s+)?fn\s+([A-Za-z_][\w]*)\s*[<(]/, name: (m) => m[1] },
|
|
34385
|
+
{ kind: "class", regex: /^\s*(?:pub\s+)?(?:struct|enum|trait)\s+([A-Za-z_][\w]*)\b/, name: (m) => m[1] },
|
|
34386
|
+
{ kind: "function", regex: /^\s*func\s+(?:\([^)]*\)\s*)?([A-Za-z_][\w]*)\s*\(/, name: (m) => m[1] },
|
|
34387
|
+
{ kind: "function", regex: /^\s*(?:public|private|protected|static|final|suspend|fun|override|open|internal|export|async|func|function|subroutine)\s+.*?([A-Za-z_][\w]*)\s*\(/, name: (m) => m[1] },
|
|
34388
|
+
{ kind: "function", regex: /^\s*function\s+([A-Za-z_][\w]*)\s*\(/, name: (m) => m[1] },
|
|
34389
|
+
{ kind: "type", regex: /^\s*(?:interface|type|enum|record|data\s+class|case\s+class|contract|library)\s+([A-Za-z_][\w]*)\b/, name: (m) => m[1] },
|
|
34390
|
+
{ kind: "section", regex: /^\s{0,3}#{1,6}\s+(.+)$/, name: (m) => m[1].trim() }
|
|
34391
|
+
];
|
|
34392
|
+
const starts = [];
|
|
34393
|
+
for (let i = 0; i < lines.length; i++) {
|
|
34394
|
+
const line = lines[i];
|
|
34395
|
+
for (const pattern of patterns) {
|
|
34396
|
+
const match = line.match(pattern.regex);
|
|
34397
|
+
if (match) {
|
|
34398
|
+
starts.push({ line: i + 1, kind: pattern.kind, name: pattern.name(match) });
|
|
34399
|
+
break;
|
|
34400
|
+
}
|
|
34401
|
+
}
|
|
34402
|
+
}
|
|
34403
|
+
if (starts.length === 0) return chunkByWindows(source, 80);
|
|
34404
|
+
for (let i = 0; i < starts.length; i++) {
|
|
34405
|
+
const start = starts[i];
|
|
34406
|
+
const next = starts[i + 1]?.line ?? lines.length + 1;
|
|
34407
|
+
const endLine = Math.max(start.line, next - 1);
|
|
34408
|
+
chunks.push({
|
|
34409
|
+
kind: start.kind,
|
|
34410
|
+
name: start.name,
|
|
34411
|
+
text: lines.slice(start.line - 1, endLine).join("\n"),
|
|
34412
|
+
startLine: start.line,
|
|
34413
|
+
endLine
|
|
34414
|
+
});
|
|
34415
|
+
}
|
|
34416
|
+
return chunks;
|
|
34417
|
+
}
|
|
34418
|
+
function chunkTextLike(lines, language) {
|
|
34419
|
+
if (language === "markdown" || language === "mdx") {
|
|
34420
|
+
const starts = lines.map((line, i) => ({ line, i })).filter(({ line }) => /^\s{0,3}#{1,6}\s+/.test(line));
|
|
34421
|
+
if (starts.length > 0) {
|
|
34422
|
+
return starts.map((start, idx) => {
|
|
34423
|
+
const end = starts[idx + 1]?.i ?? lines.length;
|
|
34424
|
+
const title = start.line.replace(/^\s{0,3}#{1,6}\s+/, "").trim();
|
|
34425
|
+
return { kind: "section", name: title, text: lines.slice(start.i, end).join("\n"), startLine: start.i + 1, endLine: end };
|
|
34426
|
+
});
|
|
34427
|
+
}
|
|
34428
|
+
}
|
|
34429
|
+
return chunkByWindows(lines.join("\n"), 80);
|
|
34430
|
+
}
|
|
34431
|
+
function chunkByWindows(source, windowLines) {
|
|
34432
|
+
const lines = source.split("\n");
|
|
34433
|
+
const chunks = [];
|
|
34434
|
+
for (let i = 0; i < lines.length; i += windowLines) {
|
|
34435
|
+
const end = Math.min(lines.length, i + windowLines);
|
|
34436
|
+
const text = lines.slice(i, end).join("\n");
|
|
34437
|
+
if (text.trim()) chunks.push({ kind: "other", name: `lines ${i + 1}-${end}`, text, startLine: i + 1, endLine: end });
|
|
34438
|
+
}
|
|
34439
|
+
return chunks;
|
|
34440
|
+
}
|
|
34441
|
+
function summarizeChunk(chunk, filePath) {
|
|
34442
|
+
const location = `${filePath}:${chunk.startLine}-${chunk.endLine}`;
|
|
34443
|
+
const comment = chunk.comment ? chunk.comment.replace(/\/\*\*|\*\/|\*\s?/g, "").trim().split("\n")[0] : "";
|
|
34444
|
+
switch (chunk.kind) {
|
|
34445
|
+
case "function":
|
|
34446
|
+
return `Function ${chunk.name} in ${location}${comment ? ` \u2014 ${comment}` : ""}`;
|
|
34447
|
+
case "class":
|
|
34448
|
+
return `Class ${chunk.name} in ${location}${comment ? ` \u2014 ${comment}` : ""}`;
|
|
34449
|
+
case "type":
|
|
34450
|
+
return `Type ${chunk.name} in ${location}`;
|
|
34451
|
+
case "import":
|
|
34452
|
+
return `Imports (${chunk.name}) in ${filePath}`;
|
|
34453
|
+
case "variable":
|
|
34454
|
+
return `Variable ${chunk.name} in ${location}`;
|
|
34455
|
+
case "export":
|
|
34456
|
+
return `Default export in ${location}`;
|
|
34457
|
+
case "section":
|
|
34458
|
+
return `Section ${chunk.name} in ${location}`;
|
|
34459
|
+
default:
|
|
34460
|
+
return `${chunk.kind} in ${location}`;
|
|
34461
|
+
}
|
|
34462
|
+
}
|
|
34463
|
+
function isChunkable(filePath) {
|
|
34464
|
+
return Boolean(languageForFile(filePath));
|
|
34465
|
+
}
|
|
34466
|
+
var LANGUAGE_BY_EXTENSION, TEXT_LIKE_LANGUAGES;
|
|
34467
|
+
var init_code_chunker = __esm({
|
|
34468
|
+
"src/lib/code-chunker.ts"() {
|
|
34469
|
+
"use strict";
|
|
34470
|
+
LANGUAGE_BY_EXTENSION = {
|
|
34471
|
+
c: "c",
|
|
34472
|
+
cc: "cpp",
|
|
34473
|
+
cpp: "cpp",
|
|
34474
|
+
cxx: "cpp",
|
|
34475
|
+
h: "c",
|
|
34476
|
+
hh: "cpp",
|
|
34477
|
+
hpp: "cpp",
|
|
34478
|
+
cs: "csharp",
|
|
34479
|
+
css: "css",
|
|
34480
|
+
scss: "scss",
|
|
34481
|
+
f: "fortran",
|
|
34482
|
+
f90: "fortran",
|
|
34483
|
+
f95: "fortran",
|
|
34484
|
+
go: "go",
|
|
34485
|
+
html: "html",
|
|
34486
|
+
htm: "html",
|
|
34487
|
+
java: "java",
|
|
34488
|
+
js: "javascript",
|
|
34489
|
+
jsx: "javascript",
|
|
34490
|
+
json: "json",
|
|
34491
|
+
kt: "kotlin",
|
|
34492
|
+
kts: "kotlin",
|
|
34493
|
+
lua: "lua",
|
|
34494
|
+
md: "markdown",
|
|
34495
|
+
mdx: "mdx",
|
|
34496
|
+
pas: "pascal",
|
|
34497
|
+
php: "php",
|
|
34498
|
+
py: "python",
|
|
34499
|
+
r: "r",
|
|
34500
|
+
rb: "ruby",
|
|
34501
|
+
rs: "rust",
|
|
34502
|
+
scala: "scala",
|
|
34503
|
+
sc: "scala",
|
|
34504
|
+
sol: "solidity",
|
|
34505
|
+
sql: "sql",
|
|
34506
|
+
svelte: "svelte",
|
|
34507
|
+
swift: "swift",
|
|
34508
|
+
toml: "toml",
|
|
34509
|
+
ts: "typescript",
|
|
34510
|
+
tsx: "typescript",
|
|
34511
|
+
vue: "vue",
|
|
34512
|
+
xml: "xml",
|
|
34513
|
+
yaml: "yaml",
|
|
34514
|
+
yml: "yaml",
|
|
34515
|
+
sh: "shell",
|
|
34516
|
+
bash: "shell",
|
|
34517
|
+
zsh: "shell"
|
|
34518
|
+
};
|
|
34519
|
+
TEXT_LIKE_LANGUAGES = /* @__PURE__ */ new Set(["json", "markdown", "mdx", "toml", "yaml", "xml", "html", "css", "scss", "sql"]);
|
|
34520
|
+
}
|
|
34521
|
+
});
|
|
34522
|
+
|
|
34523
|
+
// src/lib/code-context-index.ts
|
|
34524
|
+
var code_context_index_exports = {};
|
|
34525
|
+
__export(code_context_index_exports, {
|
|
34526
|
+
analyzeBlastRadius: () => analyzeBlastRadius,
|
|
34527
|
+
buildCodeContextIndex: () => buildCodeContextIndex,
|
|
34528
|
+
getCodeContextIndexPath: () => getCodeContextIndexPath,
|
|
34529
|
+
getCodeContextStats: () => getCodeContextStats,
|
|
34530
|
+
loadOrBuildCodeContextIndex: () => loadOrBuildCodeContextIndex,
|
|
34531
|
+
searchCodeContext: () => searchCodeContext,
|
|
34532
|
+
traceCodeSymbol: () => traceCodeSymbol
|
|
34533
|
+
});
|
|
34534
|
+
import crypto14 from "crypto";
|
|
34535
|
+
import path50 from "path";
|
|
34536
|
+
import { existsSync as existsSync36, mkdirSync as mkdirSync24, readFileSync as readFileSync31, readdirSync as readdirSync11, statSync as statSync7, writeFileSync as writeFileSync25 } from "fs";
|
|
34537
|
+
import { spawnSync } from "child_process";
|
|
34538
|
+
function normalizeProjectRoot(projectRoot) {
|
|
34539
|
+
return path50.resolve(projectRoot || process.cwd());
|
|
34540
|
+
}
|
|
34541
|
+
function hashText(text) {
|
|
34542
|
+
return crypto14.createHash("sha256").update(text).digest("hex");
|
|
34543
|
+
}
|
|
34544
|
+
function indexDir() {
|
|
34545
|
+
const dir = path50.join(EXE_AI_DIR, "code-context");
|
|
34546
|
+
mkdirSync24(dir, { recursive: true });
|
|
34547
|
+
return dir;
|
|
34548
|
+
}
|
|
34549
|
+
function getCodeContextIndexPath(projectRoot) {
|
|
34550
|
+
const root = normalizeProjectRoot(projectRoot);
|
|
34551
|
+
const rootHash = hashText(root).slice(0, 16);
|
|
34552
|
+
return path50.join(indexDir(), `${rootHash}.json`);
|
|
34553
|
+
}
|
|
34554
|
+
function currentBranch(projectRoot) {
|
|
34555
|
+
const result = spawnSync("git", ["branch", "--show-current"], { cwd: projectRoot, encoding: "utf8", timeout: 2e3 });
|
|
34556
|
+
const branch = result.status === 0 ? result.stdout.trim() : "";
|
|
34557
|
+
return branch || "detached-or-unknown";
|
|
34558
|
+
}
|
|
34559
|
+
function shouldIgnore(relPath) {
|
|
34560
|
+
const parts = relPath.split(/[\\/]/);
|
|
34561
|
+
return parts.some((part) => IGNORE_SEGMENTS.has(part));
|
|
34562
|
+
}
|
|
34563
|
+
function listRecursive(projectRoot, dir = projectRoot, out = []) {
|
|
34564
|
+
for (const entry of readdirSync11(dir, { withFileTypes: true })) {
|
|
34565
|
+
const abs = path50.join(dir, entry.name);
|
|
34566
|
+
const rel = path50.relative(projectRoot, abs).replaceAll(path50.sep, "/");
|
|
34567
|
+
if (shouldIgnore(rel)) continue;
|
|
34568
|
+
if (entry.isDirectory()) listRecursive(projectRoot, abs, out);
|
|
34569
|
+
else if (entry.isFile()) out.push(rel);
|
|
34570
|
+
}
|
|
34571
|
+
return out;
|
|
34572
|
+
}
|
|
34573
|
+
function listCodeFiles(projectRoot, maxFiles) {
|
|
34574
|
+
const git = spawnSync("git", ["ls-files"], { cwd: projectRoot, encoding: "utf8", timeout: 5e3, maxBuffer: 1024 * 1024 * 16 });
|
|
34575
|
+
let files = [];
|
|
34576
|
+
if (git.status === 0 && git.stdout.trim()) {
|
|
34577
|
+
files = git.stdout.split("\n").map((s) => s.trim()).filter(Boolean);
|
|
34578
|
+
} else {
|
|
34579
|
+
const rg = spawnSync("rg", ["--files"], { cwd: projectRoot, encoding: "utf8", timeout: 5e3, maxBuffer: 1024 * 1024 * 16 });
|
|
34580
|
+
files = rg.status === 0 && rg.stdout.trim() ? rg.stdout.split("\n").map((s) => s.trim()).filter(Boolean) : listRecursive(projectRoot);
|
|
34581
|
+
}
|
|
34582
|
+
return files.map((file) => file.replaceAll(path50.sep, "/")).filter((file) => isChunkable(file) && !shouldIgnore(file)).slice(0, maxFiles).sort();
|
|
34583
|
+
}
|
|
34584
|
+
function parseImportPaths(importText) {
|
|
34585
|
+
const paths = [];
|
|
34586
|
+
const patterns = [/from\s+["']([^"']+)["']/g, /^import\s+["']([^"']+)["']/gm, /require\(["']([^"']+)["']\)/g, /use\s+([A-Za-z0-9_:]+)::/g, /#include\s+[<"]([^>"]+)[>"]/g];
|
|
34587
|
+
for (const regex of patterns) {
|
|
34588
|
+
let match;
|
|
34589
|
+
while ((match = regex.exec(importText)) !== null) if (!paths.includes(match[1])) paths.push(match[1]);
|
|
34590
|
+
}
|
|
34591
|
+
return paths;
|
|
34592
|
+
}
|
|
34593
|
+
function resolveImport(fromFile, importPath, allFiles) {
|
|
34594
|
+
if (!importPath.startsWith(".")) return null;
|
|
34595
|
+
const base = path50.posix.normalize(path50.posix.join(path50.posix.dirname(fromFile.replaceAll(path50.sep, "/")), importPath));
|
|
34596
|
+
const withoutKnownExt = base.replace(/\.(?:[a-z0-9]+)$/i, "");
|
|
34597
|
+
const candidates = [base];
|
|
34598
|
+
for (const ext of ["ts", "tsx", "js", "jsx", "py", "rs", "go", "java", "cs", "cpp", "c", "rb", "php", "swift", "kt", "scala", "sql", "md", "json", "yaml", "yml"]) {
|
|
34599
|
+
candidates.push(`${withoutKnownExt}.${ext}`, `${base}.${ext}`);
|
|
34600
|
+
}
|
|
34601
|
+
for (const indexName of ["index.ts", "index.tsx", "index.js", "mod.rs", "__init__.py"]) candidates.push(path50.posix.join(base, indexName));
|
|
34602
|
+
return candidates.find((candidate) => allFiles.has(candidate)) ?? null;
|
|
34603
|
+
}
|
|
34604
|
+
function symbolId(filePath, chunk) {
|
|
34605
|
+
return hashText(`${filePath}:${chunk.kind}:${chunk.name}:${chunk.startLine}:${chunk.endLine}`).slice(0, 24);
|
|
34606
|
+
}
|
|
34607
|
+
function loadIndex(projectRoot) {
|
|
34608
|
+
const file = getCodeContextIndexPath(projectRoot);
|
|
34609
|
+
if (!existsSync36(file)) return null;
|
|
34610
|
+
try {
|
|
34611
|
+
const parsed = JSON.parse(readFileSync31(file, "utf8"));
|
|
34612
|
+
if (parsed.version !== INDEX_VERSION || parsed.projectRoot !== projectRoot) return null;
|
|
34613
|
+
return parsed;
|
|
34614
|
+
} catch {
|
|
34615
|
+
return null;
|
|
34616
|
+
}
|
|
34617
|
+
}
|
|
34618
|
+
function saveIndex(index) {
|
|
34619
|
+
writeFileSync25(getCodeContextIndexPath(index.projectRoot), JSON.stringify(index, null, 2));
|
|
34620
|
+
}
|
|
34621
|
+
function buildFileRecord(projectRoot, relPath, allFiles, previous) {
|
|
34622
|
+
const absPath = path50.join(projectRoot, relPath);
|
|
34623
|
+
let stat2;
|
|
34624
|
+
try {
|
|
34625
|
+
stat2 = statSync7(absPath);
|
|
34626
|
+
} catch {
|
|
34627
|
+
return { record: null, reused: false };
|
|
34628
|
+
}
|
|
34629
|
+
if (!stat2.isFile()) return { record: null, reused: false };
|
|
34630
|
+
const language = languageForFile(relPath);
|
|
34631
|
+
if (!language || !isChunkable(relPath)) return { record: null, reused: false };
|
|
34632
|
+
const source = readFileSync31(absPath, "utf8");
|
|
34633
|
+
const hash = hashText(source);
|
|
34634
|
+
if (previous && previous.hash === hash && previous.mtimeMs === stat2.mtimeMs && previous.size === stat2.size && previous.language === language) {
|
|
34635
|
+
return { record: previous, reused: true };
|
|
34636
|
+
}
|
|
34637
|
+
const chunks = chunkSourceFile(source, relPath);
|
|
34638
|
+
const imports = chunks.filter((chunk) => chunk.kind === "import").flatMap((chunk) => parseImportPaths(chunk.text));
|
|
34639
|
+
const resolvedImports = imports.map((importPath) => resolveImport(relPath, importPath, allFiles)).filter((file) => Boolean(file));
|
|
34640
|
+
const symbols = chunks.filter((chunk) => chunk.name !== "(unknown)").map((chunk) => ({
|
|
34641
|
+
id: symbolId(relPath, chunk),
|
|
34642
|
+
name: chunk.name,
|
|
34643
|
+
kind: chunk.kind,
|
|
34644
|
+
filePath: relPath,
|
|
34645
|
+
language,
|
|
34646
|
+
startLine: chunk.startLine,
|
|
34647
|
+
endLine: chunk.endLine,
|
|
34648
|
+
summary: summarizeChunk(chunk, relPath),
|
|
34649
|
+
text: chunk.text.slice(0, 8e3),
|
|
34650
|
+
comment: chunk.comment
|
|
34651
|
+
}));
|
|
34652
|
+
return { record: { path: relPath, absPath, language, hash, mtimeMs: stat2.mtimeMs, size: stat2.size, imports, resolvedImports, symbols }, reused: false };
|
|
34653
|
+
}
|
|
34654
|
+
function buildCodeContextIndex(options = {}) {
|
|
34655
|
+
const projectRoot = normalizeProjectRoot(options.projectRoot);
|
|
34656
|
+
const maxFiles = options.maxFiles ?? DEFAULT_MAX_FILES;
|
|
34657
|
+
const branch = currentBranch(projectRoot);
|
|
34658
|
+
const previous = options.force ? null : loadIndex(projectRoot);
|
|
34659
|
+
const files = listCodeFiles(projectRoot, maxFiles);
|
|
34660
|
+
const allFiles = new Set(files.map((file) => file.replaceAll(path50.sep, "/")));
|
|
34661
|
+
const fileRecords = {};
|
|
34662
|
+
let rebuiltFiles = 0;
|
|
34663
|
+
let reusedFiles = 0;
|
|
34664
|
+
let skippedFiles = 0;
|
|
34665
|
+
for (const rel of files) {
|
|
34666
|
+
const normalized = rel.replaceAll(path50.sep, "/");
|
|
34667
|
+
const { record, reused } = buildFileRecord(projectRoot, normalized, allFiles, previous?.files[normalized]);
|
|
34668
|
+
if (record) {
|
|
34669
|
+
fileRecords[normalized] = record;
|
|
34670
|
+
if (reused) reusedFiles++;
|
|
34671
|
+
else rebuiltFiles++;
|
|
34672
|
+
} else skippedFiles++;
|
|
34673
|
+
}
|
|
34674
|
+
const languageBreakdown = {};
|
|
34675
|
+
for (const file of Object.values(fileRecords)) languageBreakdown[file.language] = (languageBreakdown[file.language] ?? 0) + 1;
|
|
34676
|
+
const index = {
|
|
34677
|
+
version: INDEX_VERSION,
|
|
34678
|
+
projectRoot,
|
|
34679
|
+
rootHash: hashText(projectRoot).slice(0, 16),
|
|
34680
|
+
branch,
|
|
34681
|
+
indexedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
34682
|
+
stats: { filesSeen: files.length, filesIndexed: Object.keys(fileRecords).length, rebuiltFiles, reusedFiles, skippedFiles, languageBreakdown },
|
|
34683
|
+
files: fileRecords
|
|
34684
|
+
};
|
|
34685
|
+
saveIndex(index);
|
|
34686
|
+
return index;
|
|
34687
|
+
}
|
|
34688
|
+
function loadOrBuildCodeContextIndex(options = {}) {
|
|
34689
|
+
const projectRoot = normalizeProjectRoot(options.projectRoot);
|
|
34690
|
+
if (!options.force) {
|
|
34691
|
+
const loaded = loadIndex(projectRoot);
|
|
34692
|
+
if (loaded) {
|
|
34693
|
+
const currentFiles = listCodeFiles(projectRoot, options.maxFiles ?? DEFAULT_MAX_FILES);
|
|
34694
|
+
const unchanged = currentFiles.every((rel) => {
|
|
34695
|
+
const normalized = rel.replaceAll(path50.sep, "/");
|
|
34696
|
+
const existing = loaded.files[normalized];
|
|
34697
|
+
if (!existing) return false;
|
|
34698
|
+
try {
|
|
34699
|
+
const stat2 = statSync7(path50.join(projectRoot, normalized));
|
|
34700
|
+
return stat2.mtimeMs === existing.mtimeMs && stat2.size === existing.size;
|
|
34701
|
+
} catch {
|
|
34702
|
+
return false;
|
|
34703
|
+
}
|
|
34704
|
+
});
|
|
34705
|
+
if (unchanged && Object.keys(loaded.files).length === currentFiles.length) return loaded;
|
|
34706
|
+
}
|
|
34707
|
+
}
|
|
34708
|
+
return buildCodeContextIndex(options);
|
|
34709
|
+
}
|
|
34710
|
+
function normalizeLanguage(language) {
|
|
34711
|
+
return language.toLowerCase().replace(/^c\+\+$/, "cpp").replace(/^c#$/, "csharp").replace(/^js$/, "javascript").replace(/^ts$/, "typescript");
|
|
34712
|
+
}
|
|
34713
|
+
function tokenize2(query) {
|
|
34714
|
+
const raw = query.toLowerCase().replace(/([a-z])([A-Z])/g, "$1 $2").split(/[^a-z0-9_.$/-]+/).map((s) => s.trim()).filter((s) => s.length >= 2);
|
|
34715
|
+
const expanded = /* @__PURE__ */ new Set();
|
|
34716
|
+
for (const term of raw) {
|
|
34717
|
+
expanded.add(term);
|
|
34718
|
+
for (const part of term.split(/[_.$/-]+/)) if (part.length >= 2) expanded.add(part);
|
|
34719
|
+
const dashed = term.replace(/[_.$/]+/g, "-");
|
|
34720
|
+
if (dashed.length >= 2) expanded.add(dashed);
|
|
34721
|
+
if (!term.includes("-")) expanded.add(`${term}s`);
|
|
34722
|
+
if (term.endsWith("s") && term.length > 3) expanded.add(term.slice(0, -1));
|
|
34723
|
+
}
|
|
34724
|
+
return [...expanded];
|
|
34725
|
+
}
|
|
34726
|
+
function ngrams(terms) {
|
|
34727
|
+
const grams = [...terms];
|
|
34728
|
+
for (let i = 0; i < terms.length - 1; i++) grams.push(`${terms[i]} ${terms[i + 1]}`);
|
|
34729
|
+
return grams;
|
|
34730
|
+
}
|
|
34731
|
+
function globToRegex(pattern) {
|
|
34732
|
+
let out = "";
|
|
34733
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
34734
|
+
const ch = pattern[i];
|
|
34735
|
+
const next = pattern[i + 1];
|
|
34736
|
+
if (ch === "*" && next === "*") {
|
|
34737
|
+
out += ".*";
|
|
34738
|
+
i++;
|
|
34739
|
+
continue;
|
|
34740
|
+
}
|
|
34741
|
+
if (ch === "*") {
|
|
34742
|
+
out += "[^/]*";
|
|
34743
|
+
continue;
|
|
34744
|
+
}
|
|
34745
|
+
if (".+^${}()|[]\\".includes(ch)) out += `\\${ch}`;
|
|
34746
|
+
else out += ch;
|
|
34747
|
+
}
|
|
34748
|
+
return new RegExp(`^${out}$`);
|
|
34749
|
+
}
|
|
34750
|
+
function matchesPath(filePath, patterns) {
|
|
34751
|
+
if (!patterns || patterns.length === 0) return true;
|
|
34752
|
+
const normalized = filePath.replaceAll(path50.sep, "/");
|
|
34753
|
+
return patterns.some((pattern) => {
|
|
34754
|
+
const p = pattern.replaceAll(path50.sep, "/").replace(/^\.\//, "");
|
|
34755
|
+
return normalized === p || normalized.startsWith(`${p}/`) || normalized.endsWith(p) || globToRegex(p).test(normalized);
|
|
34756
|
+
});
|
|
34757
|
+
}
|
|
34758
|
+
function scoreSymbol(symbol, terms) {
|
|
34759
|
+
const grams = ngrams(terms);
|
|
34760
|
+
const haystacks = {
|
|
34761
|
+
name: symbol.name.toLowerCase(),
|
|
34762
|
+
path: symbol.filePath.toLowerCase(),
|
|
34763
|
+
language: symbol.language.toLowerCase(),
|
|
34764
|
+
summary: symbol.summary.toLowerCase(),
|
|
34765
|
+
text: symbol.text.toLowerCase()
|
|
34766
|
+
};
|
|
34767
|
+
let score = 0;
|
|
34768
|
+
const matches = [];
|
|
34769
|
+
for (const term of terms) {
|
|
34770
|
+
if (haystacks.name === term) {
|
|
34771
|
+
score += 100;
|
|
34772
|
+
matches.push(`name=${term}`);
|
|
34773
|
+
continue;
|
|
34774
|
+
}
|
|
34775
|
+
if (haystacks.name.includes(term)) {
|
|
34776
|
+
score += 45;
|
|
34777
|
+
matches.push(`name~${term}`);
|
|
34778
|
+
}
|
|
34779
|
+
if (haystacks.path.includes(term)) {
|
|
34780
|
+
score += 18;
|
|
34781
|
+
matches.push(`path~${term}`);
|
|
34782
|
+
}
|
|
34783
|
+
if (haystacks.language === term) {
|
|
34784
|
+
score += 18;
|
|
34785
|
+
matches.push(`language=${term}`);
|
|
34786
|
+
}
|
|
34787
|
+
if (haystacks.summary.includes(term)) {
|
|
34788
|
+
score += 16;
|
|
34789
|
+
matches.push(`summary~${term}`);
|
|
34790
|
+
}
|
|
34791
|
+
if (haystacks.text.includes(term)) {
|
|
34792
|
+
score += 5;
|
|
34793
|
+
matches.push(`text~${term}`);
|
|
34794
|
+
}
|
|
34795
|
+
}
|
|
34796
|
+
for (const gram of grams.filter((g) => g.includes(" "))) {
|
|
34797
|
+
if (haystacks.text.includes(gram) || haystacks.summary.includes(gram)) {
|
|
34798
|
+
score += 20;
|
|
34799
|
+
matches.push(`phrase~${gram}`);
|
|
34800
|
+
}
|
|
34801
|
+
}
|
|
34802
|
+
const uniqueMatches = new Set(matches.map((m) => m.replace(/^[^=~]+[=~]/, ""))).size;
|
|
34803
|
+
score += uniqueMatches * 3;
|
|
34804
|
+
return { score, matches };
|
|
34805
|
+
}
|
|
34806
|
+
function filteredFiles(index, options = {}) {
|
|
34807
|
+
const languages = options.languages?.map(normalizeLanguage).filter(Boolean);
|
|
34808
|
+
return Object.values(index.files).filter((file) => {
|
|
34809
|
+
if (languages && languages.length > 0 && !languages.includes(normalizeLanguage(file.language))) return false;
|
|
34810
|
+
return matchesPath(file.path, options.paths);
|
|
34811
|
+
});
|
|
34812
|
+
}
|
|
34813
|
+
function searchCodeContext(query, options = {}) {
|
|
34814
|
+
const terms = tokenize2(query);
|
|
34815
|
+
if (terms.length === 0) return [];
|
|
34816
|
+
const index = loadOrBuildCodeContextIndex({ projectRoot: options.projectRoot, force: options.force || options.refreshIndex, maxFiles: options.maxFiles });
|
|
34817
|
+
const results = [];
|
|
34818
|
+
for (const file of filteredFiles(index, options)) {
|
|
34819
|
+
for (const symbol of file.symbols) {
|
|
34820
|
+
const scored = scoreSymbol(symbol, terms);
|
|
34821
|
+
if (scored.score > 0) {
|
|
34822
|
+
results.push({
|
|
34823
|
+
symbol,
|
|
34824
|
+
score: scored.score,
|
|
34825
|
+
matches: scored.matches,
|
|
34826
|
+
filePath: symbol.filePath,
|
|
34827
|
+
language: symbol.language,
|
|
34828
|
+
content: symbol.text,
|
|
34829
|
+
startLine: symbol.startLine,
|
|
34830
|
+
endLine: symbol.endLine
|
|
34831
|
+
});
|
|
34832
|
+
}
|
|
34833
|
+
}
|
|
34834
|
+
}
|
|
34835
|
+
const offset = Math.max(0, options.offset ?? 0);
|
|
34836
|
+
const limit = options.limit ?? 20;
|
|
34837
|
+
return results.sort((a, b) => b.score - a.score || a.filePath.localeCompare(b.filePath)).slice(offset, offset + limit);
|
|
34838
|
+
}
|
|
34839
|
+
function dependentsMap(index) {
|
|
34840
|
+
const map = /* @__PURE__ */ new Map();
|
|
34841
|
+
for (const file of Object.values(index.files)) {
|
|
34842
|
+
for (const dep of file.resolvedImports) {
|
|
34843
|
+
if (!map.has(dep)) map.set(dep, /* @__PURE__ */ new Set());
|
|
34844
|
+
map.get(dep).add(file.path);
|
|
34845
|
+
}
|
|
34846
|
+
}
|
|
34847
|
+
return map;
|
|
34848
|
+
}
|
|
34849
|
+
function findSymbols(index, symbolName, limit = 20) {
|
|
34850
|
+
const q = symbolName.toLowerCase();
|
|
34851
|
+
const exact = [];
|
|
34852
|
+
const fuzzy = [];
|
|
34853
|
+
for (const file of Object.values(index.files)) {
|
|
34854
|
+
for (const symbol of file.symbols) {
|
|
34855
|
+
if (symbol.name.toLowerCase() === q) exact.push(symbol);
|
|
34856
|
+
else if (symbol.name.toLowerCase().includes(q) || symbol.summary.toLowerCase().includes(q)) fuzzy.push(symbol);
|
|
34857
|
+
}
|
|
34858
|
+
}
|
|
34859
|
+
return [...exact, ...fuzzy].slice(0, limit);
|
|
34860
|
+
}
|
|
34861
|
+
function traceCodeSymbol(symbolName, options = {}) {
|
|
34862
|
+
const index = loadOrBuildCodeContextIndex(options);
|
|
34863
|
+
const matches = findSymbols(index, symbolName, options.limit ?? 20);
|
|
34864
|
+
const dependents = dependentsMap(index);
|
|
34865
|
+
return { query: symbolName, matches, definitions: matches.map((symbol) => {
|
|
34866
|
+
const file = index.files[symbol.filePath];
|
|
34867
|
+
return { symbol, imports: file.resolvedImports, dependents: [...dependents.get(symbol.filePath) ?? /* @__PURE__ */ new Set()].sort(), relatedSymbols: file.symbols.filter((s) => s.id !== symbol.id).slice(0, 20) };
|
|
34868
|
+
}) };
|
|
34869
|
+
}
|
|
34870
|
+
function resolveTargetFile(index, input) {
|
|
34871
|
+
if (input.filePath) {
|
|
34872
|
+
const normalized = input.filePath.replaceAll(path50.sep, "/").replace(/^\.\//, "");
|
|
34873
|
+
if (index.files[normalized]) return { filePath: normalized, target: normalized };
|
|
34874
|
+
const suffix = Object.keys(index.files).find((file) => file.endsWith(normalized));
|
|
34875
|
+
if (suffix) return { filePath: suffix, target: input.filePath };
|
|
34876
|
+
}
|
|
34877
|
+
if (input.symbol) {
|
|
34878
|
+
const match = findSymbols(index, input.symbol, 1)[0];
|
|
34879
|
+
if (match) return { filePath: match.filePath, target: input.symbol };
|
|
34880
|
+
}
|
|
34881
|
+
return null;
|
|
34882
|
+
}
|
|
34883
|
+
function analyzeBlastRadius(input) {
|
|
34884
|
+
const index = loadOrBuildCodeContextIndex({ projectRoot: input.projectRoot, force: input.force });
|
|
34885
|
+
const target = resolveTargetFile(index, { filePath: input.filePath, symbol: input.symbol });
|
|
34886
|
+
if (!target) return null;
|
|
34887
|
+
const dependents = dependentsMap(index);
|
|
34888
|
+
const maxDepth = input.depth ?? 2;
|
|
34889
|
+
const impacted = /* @__PURE__ */ new Map();
|
|
34890
|
+
const queue = [{ filePath: target.filePath, distance: 0 }];
|
|
34891
|
+
impacted.set(target.filePath, { distance: 0, reason: "target" });
|
|
34892
|
+
while (queue.length > 0) {
|
|
34893
|
+
const item = queue.shift();
|
|
34894
|
+
if (item.distance >= maxDepth) continue;
|
|
34895
|
+
for (const dep of dependents.get(item.filePath) ?? []) {
|
|
34896
|
+
if (!impacted.has(dep)) {
|
|
34897
|
+
impacted.set(dep, { distance: item.distance + 1, reason: `imports ${item.filePath}` });
|
|
34898
|
+
queue.push({ filePath: dep, distance: item.distance + 1 });
|
|
34899
|
+
}
|
|
34900
|
+
}
|
|
34901
|
+
}
|
|
34902
|
+
const targetBase = path50.basename(target.filePath).replace(/\.[^.]+$/, "").toLowerCase();
|
|
34903
|
+
const symbolLower = input.symbol?.toLowerCase();
|
|
34904
|
+
const tests = Object.keys(index.files).filter((file) => {
|
|
34905
|
+
const lower = file.toLowerCase();
|
|
34906
|
+
return (lower.includes("test") || lower.includes("spec")) && (lower.includes(targetBase) || (symbolLower ? index.files[file].symbols.some((s) => s.text.toLowerCase().includes(symbolLower)) : false));
|
|
34907
|
+
});
|
|
34908
|
+
for (const test of tests) if (!impacted.has(test)) impacted.set(test, { distance: 1, reason: "related test/spec" });
|
|
34909
|
+
const impactedFiles = [...impacted.entries()].map(([filePath, value]) => ({ filePath, distance: value.distance, reason: value.reason })).sort((a, b) => a.distance - b.distance || a.filePath.localeCompare(b.filePath));
|
|
34910
|
+
const nonTestImpacted = impactedFiles.filter((f) => !f.filePath.match(/(test|spec)\./i)).length;
|
|
34911
|
+
return { target: target.target, targetFile: target.filePath, impactedFiles, tests, symbolsInTarget: index.files[target.filePath]?.symbols ?? [], riskLevel: nonTestImpacted >= 8 ? "high" : nonTestImpacted >= 4 ? "medium" : "low" };
|
|
34912
|
+
}
|
|
34913
|
+
function getCodeContextStats(options = {}) {
|
|
34914
|
+
const index = loadOrBuildCodeContextIndex(options);
|
|
34915
|
+
return {
|
|
34916
|
+
projectRoot: index.projectRoot,
|
|
34917
|
+
branch: index.branch,
|
|
34918
|
+
indexedAt: index.indexedAt,
|
|
34919
|
+
indexAgeMs: Math.max(0, Date.now() - Date.parse(index.indexedAt)),
|
|
34920
|
+
files: Object.keys(index.files).length,
|
|
34921
|
+
symbols: Object.values(index.files).reduce((sum, file) => sum + file.symbols.length, 0),
|
|
34922
|
+
imports: Object.values(index.files).reduce((sum, file) => sum + file.resolvedImports.length, 0),
|
|
34923
|
+
languageBreakdown: index.stats.languageBreakdown,
|
|
34924
|
+
rebuiltFiles: index.stats.rebuiltFiles,
|
|
34925
|
+
reusedFiles: index.stats.reusedFiles,
|
|
34926
|
+
skippedFiles: index.stats.skippedFiles,
|
|
34927
|
+
indexPath: getCodeContextIndexPath(index.projectRoot)
|
|
34928
|
+
};
|
|
34929
|
+
}
|
|
34930
|
+
var INDEX_VERSION, DEFAULT_MAX_FILES, IGNORE_SEGMENTS;
|
|
34931
|
+
var init_code_context_index = __esm({
|
|
34932
|
+
"src/lib/code-context-index.ts"() {
|
|
34933
|
+
"use strict";
|
|
34934
|
+
init_config();
|
|
34935
|
+
init_code_chunker();
|
|
34936
|
+
INDEX_VERSION = 2;
|
|
34937
|
+
DEFAULT_MAX_FILES = 5e3;
|
|
34938
|
+
IGNORE_SEGMENTS = /* @__PURE__ */ new Set(["node_modules", "dist", ".git", "coverage", ".worktrees", ".next", "build", "target", "vendor"]);
|
|
34939
|
+
}
|
|
34940
|
+
});
|
|
34941
|
+
|
|
33944
34942
|
// src/adapters/opencode/plugin-template.ts
|
|
33945
34943
|
var PLUGIN_TEMPLATE;
|
|
33946
34944
|
var init_plugin_template = __esm({
|
|
@@ -34134,16 +35132,16 @@ __export(installer_exports2, {
|
|
|
34134
35132
|
verifyOpenCodeHooks: () => verifyOpenCodeHooks
|
|
34135
35133
|
});
|
|
34136
35134
|
import { readFile as readFile7, writeFile as writeFile8, mkdir as mkdir8 } from "fs/promises";
|
|
34137
|
-
import { existsSync as
|
|
34138
|
-
import
|
|
35135
|
+
import { existsSync as existsSync37, readFileSync as readFileSync32 } from "fs";
|
|
35136
|
+
import path51 from "path";
|
|
34139
35137
|
import os22 from "os";
|
|
34140
35138
|
async function registerOpenCodeMcp(packageRoot, homeDir = os22.homedir()) {
|
|
34141
35139
|
void packageRoot;
|
|
34142
|
-
const configDir =
|
|
34143
|
-
const configPath =
|
|
35140
|
+
const configDir = path51.join(homeDir, ".config", "opencode");
|
|
35141
|
+
const configPath = path51.join(configDir, "opencode.json");
|
|
34144
35142
|
await mkdir8(configDir, { recursive: true });
|
|
34145
35143
|
let config = {};
|
|
34146
|
-
if (
|
|
35144
|
+
if (existsSync37(configPath)) {
|
|
34147
35145
|
try {
|
|
34148
35146
|
config = JSON.parse(await readFile7(configPath, "utf-8"));
|
|
34149
35147
|
} catch {
|
|
@@ -34171,14 +35169,14 @@ async function registerOpenCodeMcp(packageRoot, homeDir = os22.homedir()) {
|
|
|
34171
35169
|
return true;
|
|
34172
35170
|
}
|
|
34173
35171
|
async function installOpenCodePlugin(packageRoot, homeDir = os22.homedir()) {
|
|
34174
|
-
const pluginDir =
|
|
34175
|
-
const pluginPath =
|
|
35172
|
+
const pluginDir = path51.join(homeDir, ".config", "opencode", "plugins");
|
|
35173
|
+
const pluginPath = path51.join(pluginDir, "exe-os.mjs");
|
|
34176
35174
|
await mkdir8(pluginDir, { recursive: true });
|
|
34177
35175
|
const pluginContent = PLUGIN_TEMPLATE.replace(
|
|
34178
35176
|
/__PACKAGE_ROOT__/g,
|
|
34179
35177
|
packageRoot.replace(/\\/g, "\\\\")
|
|
34180
35178
|
);
|
|
34181
|
-
if (
|
|
35179
|
+
if (existsSync37(pluginPath)) {
|
|
34182
35180
|
const existing = await readFile7(pluginPath, "utf-8");
|
|
34183
35181
|
if (existing === pluginContent) {
|
|
34184
35182
|
return false;
|
|
@@ -34188,18 +35186,18 @@ async function installOpenCodePlugin(packageRoot, homeDir = os22.homedir()) {
|
|
|
34188
35186
|
return true;
|
|
34189
35187
|
}
|
|
34190
35188
|
function verifyOpenCodeHooks(homeDir = os22.homedir()) {
|
|
34191
|
-
const configPath =
|
|
34192
|
-
const pluginPath =
|
|
34193
|
-
if (!
|
|
35189
|
+
const configPath = path51.join(homeDir, ".config", "opencode", "opencode.json");
|
|
35190
|
+
const pluginPath = path51.join(homeDir, ".config", "opencode", "plugins", "exe-os.mjs");
|
|
35191
|
+
if (!existsSync37(configPath)) return false;
|
|
34194
35192
|
try {
|
|
34195
|
-
const config = JSON.parse(
|
|
35193
|
+
const config = JSON.parse(readFileSync32(configPath, "utf-8"));
|
|
34196
35194
|
if (!config.mcp?.["exe-os"]?.enabled) return false;
|
|
34197
35195
|
} catch {
|
|
34198
35196
|
return false;
|
|
34199
35197
|
}
|
|
34200
|
-
if (!
|
|
35198
|
+
if (!existsSync37(pluginPath)) return false;
|
|
34201
35199
|
try {
|
|
34202
|
-
const plugin =
|
|
35200
|
+
const plugin = readFileSync32(pluginPath, "utf-8");
|
|
34203
35201
|
if (!plugin.includes(EXE_HOOK_FILES.postToolCombined)) return false;
|
|
34204
35202
|
if (textHasLegacySplitPostToolHook(plugin)) return false;
|
|
34205
35203
|
} catch {
|
|
@@ -34241,19 +35239,19 @@ __export(installer_exports3, {
|
|
|
34241
35239
|
verifyCodexHooks: () => verifyCodexHooks
|
|
34242
35240
|
});
|
|
34243
35241
|
import { readFile as readFile8, writeFile as writeFile9, mkdir as mkdir9 } from "fs/promises";
|
|
34244
|
-
import { existsSync as
|
|
34245
|
-
import
|
|
35242
|
+
import { existsSync as existsSync38, readFileSync as readFileSync33 } from "fs";
|
|
35243
|
+
import path52 from "path";
|
|
34246
35244
|
import os23 from "os";
|
|
34247
35245
|
async function mergeCodexHooks(packageRoot, homeDir = os23.homedir()) {
|
|
34248
|
-
const codexDir =
|
|
34249
|
-
const hooksPath =
|
|
34250
|
-
const logsDir =
|
|
34251
|
-
const hookLogPath =
|
|
35246
|
+
const codexDir = path52.join(homeDir, ".codex");
|
|
35247
|
+
const hooksPath = path52.join(codexDir, "hooks.json");
|
|
35248
|
+
const logsDir = path52.join(homeDir, ".exe-os", "logs");
|
|
35249
|
+
const hookLogPath = path52.join(logsDir, "hooks.log");
|
|
34252
35250
|
const logSuffix = ` 2>> "${hookLogPath}"`;
|
|
34253
35251
|
await mkdir9(codexDir, { recursive: true });
|
|
34254
35252
|
await mkdir9(logsDir, { recursive: true });
|
|
34255
35253
|
let hooksJson = {};
|
|
34256
|
-
if (
|
|
35254
|
+
if (existsSync38(hooksPath)) {
|
|
34257
35255
|
try {
|
|
34258
35256
|
hooksJson = JSON.parse(await readFile8(hooksPath, "utf-8"));
|
|
34259
35257
|
} catch {
|
|
@@ -34273,7 +35271,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os23.homedir()) {
|
|
|
34273
35271
|
// Combined hook: runs ingest + error-recall in one Node process.
|
|
34274
35272
|
// Eliminates a cold-start cycle per tool call (~3-6s savings on Codex).
|
|
34275
35273
|
type: "command",
|
|
34276
|
-
command: `node "${
|
|
35274
|
+
command: `node "${path52.join(packageRoot, "dist", "hooks", "post-tool-combined.js")}"${logSuffix}`
|
|
34277
35275
|
}
|
|
34278
35276
|
]
|
|
34279
35277
|
},
|
|
@@ -34287,7 +35285,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os23.homedir()) {
|
|
|
34287
35285
|
// Single hook: prompt-submit handles memory retrieval + entity boost.
|
|
34288
35286
|
// exe-heartbeat-hook is CC-specific (intercom) — omitted on Codex.
|
|
34289
35287
|
type: "command",
|
|
34290
|
-
command: `node "${
|
|
35288
|
+
command: `node "${path52.join(packageRoot, "dist", "hooks", "prompt-submit.js")}"${logSuffix}`
|
|
34291
35289
|
}
|
|
34292
35290
|
]
|
|
34293
35291
|
},
|
|
@@ -34299,7 +35297,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os23.homedir()) {
|
|
|
34299
35297
|
hooks: [
|
|
34300
35298
|
{
|
|
34301
35299
|
type: "command",
|
|
34302
|
-
command: `node "${
|
|
35300
|
+
command: `node "${path52.join(packageRoot, "dist", "hooks", "stop.js")}"${logSuffix}`
|
|
34303
35301
|
}
|
|
34304
35302
|
]
|
|
34305
35303
|
},
|
|
@@ -34312,7 +35310,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os23.homedir()) {
|
|
|
34312
35310
|
hooks: [
|
|
34313
35311
|
{
|
|
34314
35312
|
type: "command",
|
|
34315
|
-
command: `node "${
|
|
35313
|
+
command: `node "${path52.join(packageRoot, "dist", "hooks", "pre-tool-use.js")}"${logSuffix}`
|
|
34316
35314
|
}
|
|
34317
35315
|
]
|
|
34318
35316
|
},
|
|
@@ -34361,10 +35359,10 @@ async function mergeCodexHooks(packageRoot, homeDir = os23.homedir()) {
|
|
|
34361
35359
|
return { added, skipped };
|
|
34362
35360
|
}
|
|
34363
35361
|
function verifyCodexHooks(homeDir = os23.homedir()) {
|
|
34364
|
-
const hooksPath =
|
|
34365
|
-
if (!
|
|
35362
|
+
const hooksPath = path52.join(homeDir, ".codex", "hooks.json");
|
|
35363
|
+
if (!existsSync38(hooksPath)) return false;
|
|
34366
35364
|
try {
|
|
34367
|
-
const hooksJson = JSON.parse(
|
|
35365
|
+
const hooksJson = JSON.parse(readFileSync33(hooksPath, "utf-8"));
|
|
34368
35366
|
if (!hooksJson.hooks) return false;
|
|
34369
35367
|
const required = ["PostToolUse", "UserPromptSubmit", "Stop", "PreToolUse"];
|
|
34370
35368
|
for (const event of required) {
|
|
@@ -34396,11 +35394,11 @@ function verifyCodexHooks(homeDir = os23.homedir()) {
|
|
|
34396
35394
|
async function installCodexStatusLine(homeDir = os23.homedir()) {
|
|
34397
35395
|
const prefs = loadPreferences(homeDir);
|
|
34398
35396
|
if (prefs.codexStatusLine === false) return "opted-out";
|
|
34399
|
-
const codexDir =
|
|
34400
|
-
const configPath =
|
|
35397
|
+
const codexDir = path52.join(homeDir, ".codex");
|
|
35398
|
+
const configPath = path52.join(codexDir, "config.toml");
|
|
34401
35399
|
await mkdir9(codexDir, { recursive: true });
|
|
34402
35400
|
let content = "";
|
|
34403
|
-
if (
|
|
35401
|
+
if (existsSync38(configPath)) {
|
|
34404
35402
|
content = await readFile8(configPath, "utf-8");
|
|
34405
35403
|
if (/\[tui\][\s\S]*?status_line\s*=/.test(content)) {
|
|
34406
35404
|
return "already-configured";
|
|
@@ -34452,12 +35450,12 @@ ${line}
|
|
|
34452
35450
|
return { content: next, changed: next !== sectionContent };
|
|
34453
35451
|
}
|
|
34454
35452
|
async function registerCodexMcpServer(packageRoot, homeDir = os23.homedir()) {
|
|
34455
|
-
const codexDir =
|
|
34456
|
-
const configPath =
|
|
35453
|
+
const codexDir = path52.join(homeDir, ".codex");
|
|
35454
|
+
const configPath = path52.join(codexDir, "config.toml");
|
|
34457
35455
|
void packageRoot;
|
|
34458
35456
|
await mkdir9(codexDir, { recursive: true });
|
|
34459
35457
|
let content = "";
|
|
34460
|
-
if (
|
|
35458
|
+
if (existsSync38(configPath)) {
|
|
34461
35459
|
content = await readFile8(configPath, "utf-8");
|
|
34462
35460
|
}
|
|
34463
35461
|
const sectionHeader = "[mcp_servers.exe-os]";
|
|
@@ -34482,10 +35480,10 @@ async function registerCodexMcpServer(packageRoot, homeDir = os23.homedir()) {
|
|
|
34482
35480
|
return "registered";
|
|
34483
35481
|
}
|
|
34484
35482
|
async function ensureCodexHooksFeature(homeDir = os23.homedir()) {
|
|
34485
|
-
const configPath =
|
|
34486
|
-
await mkdir9(
|
|
35483
|
+
const configPath = path52.join(homeDir, ".codex", "config.toml");
|
|
35484
|
+
await mkdir9(path52.join(homeDir, ".codex"), { recursive: true });
|
|
34487
35485
|
let content = "";
|
|
34488
|
-
if (
|
|
35486
|
+
if (existsSync38(configPath)) {
|
|
34489
35487
|
content = await readFile8(configPath, "utf-8");
|
|
34490
35488
|
}
|
|
34491
35489
|
if (/\[features\][\s\S]*?codex_hooks\s*=\s*true/.test(content)) {
|
|
@@ -34560,32 +35558,32 @@ __export(mcp_diagnostics_exports, {
|
|
|
34560
35558
|
diagnoseClaudeMcpConfig: () => diagnoseClaudeMcpConfig,
|
|
34561
35559
|
formatMcpDiagnosticReport: () => formatMcpDiagnosticReport
|
|
34562
35560
|
});
|
|
34563
|
-
import { existsSync as
|
|
34564
|
-
import
|
|
35561
|
+
import { existsSync as existsSync39, readFileSync as readFileSync34 } from "fs";
|
|
35562
|
+
import path53 from "path";
|
|
34565
35563
|
import os24 from "os";
|
|
34566
35564
|
function readJson(filePath) {
|
|
34567
|
-
if (!
|
|
35565
|
+
if (!existsSync39(filePath)) return null;
|
|
34568
35566
|
try {
|
|
34569
|
-
return JSON.parse(
|
|
35567
|
+
return JSON.parse(readFileSync34(filePath, "utf8"));
|
|
34570
35568
|
} catch {
|
|
34571
35569
|
return null;
|
|
34572
35570
|
}
|
|
34573
35571
|
}
|
|
34574
35572
|
function pathApplies2(projectPath, cwd2) {
|
|
34575
|
-
const project =
|
|
34576
|
-
const current =
|
|
34577
|
-
return current === project || current.startsWith(project +
|
|
35573
|
+
const project = path53.resolve(projectPath);
|
|
35574
|
+
const current = path53.resolve(cwd2);
|
|
35575
|
+
return current === project || current.startsWith(project + path53.sep);
|
|
34578
35576
|
}
|
|
34579
35577
|
function findAncestorMcpJsons2(cwd2, homeDir) {
|
|
34580
35578
|
const files = [];
|
|
34581
|
-
let dir =
|
|
34582
|
-
const root =
|
|
34583
|
-
const stop =
|
|
35579
|
+
let dir = path53.resolve(cwd2);
|
|
35580
|
+
const root = path53.parse(dir).root;
|
|
35581
|
+
const stop = path53.resolve(homeDir);
|
|
34584
35582
|
while (dir !== root) {
|
|
34585
|
-
const candidate =
|
|
34586
|
-
if (
|
|
35583
|
+
const candidate = path53.join(dir, ".mcp.json");
|
|
35584
|
+
if (existsSync39(candidate)) files.push(candidate);
|
|
34587
35585
|
if (dir === stop) break;
|
|
34588
|
-
dir =
|
|
35586
|
+
dir = path53.dirname(dir);
|
|
34589
35587
|
}
|
|
34590
35588
|
return files;
|
|
34591
35589
|
}
|
|
@@ -34602,8 +35600,8 @@ function serverAllowedByPermissions(serverName, prefixes) {
|
|
|
34602
35600
|
return prefixes.has(serverName) || prefixes.has(serverName.replace(/-/g, "_"));
|
|
34603
35601
|
}
|
|
34604
35602
|
function diagnoseClaudeMcpConfig(homeDir = os24.homedir(), cwd2 = process.cwd(), env = process.env) {
|
|
34605
|
-
const claudeJsonPath =
|
|
34606
|
-
const settingsPath =
|
|
35603
|
+
const claudeJsonPath = path53.join(homeDir, ".claude.json");
|
|
35604
|
+
const settingsPath = path53.join(homeDir, ".claude", "settings.json");
|
|
34607
35605
|
const claudeJson = readJson(claudeJsonPath);
|
|
34608
35606
|
const settings = readJson(settingsPath);
|
|
34609
35607
|
const issues = [];
|
|
@@ -34723,7 +35721,7 @@ function diagnoseClaudeMcpConfig(homeDir = os24.homedir(), cwd2 = process.cwd(),
|
|
|
34723
35721
|
});
|
|
34724
35722
|
}
|
|
34725
35723
|
return {
|
|
34726
|
-
cwd:
|
|
35724
|
+
cwd: path53.resolve(cwd2),
|
|
34727
35725
|
globalServers,
|
|
34728
35726
|
projectServers: projectServers.sort((a, b) => a.name.localeCompare(b.name)),
|
|
34729
35727
|
mcpJsonServers: mcpJsonServers.sort((a, b) => a.name.localeCompare(b.name)),
|
|
@@ -34760,14 +35758,14 @@ var init_mcp_diagnostics = __esm({
|
|
|
34760
35758
|
});
|
|
34761
35759
|
|
|
34762
35760
|
// src/bin/cli.ts
|
|
34763
|
-
import { existsSync as
|
|
34764
|
-
import
|
|
35761
|
+
import { existsSync as existsSync40, readFileSync as readFileSync35, writeFileSync as writeFileSync26, readdirSync as readdirSync12, rmSync as rmSync2 } from "fs";
|
|
35762
|
+
import path54 from "path";
|
|
34765
35763
|
import os25 from "os";
|
|
34766
35764
|
var args = process.argv.slice(2);
|
|
34767
35765
|
if (args.includes("--version") || args.includes("-v")) {
|
|
34768
35766
|
try {
|
|
34769
|
-
const pkgPath =
|
|
34770
|
-
const pkg = JSON.parse(
|
|
35767
|
+
const pkgPath = path54.join(path54.dirname(new URL(import.meta.url).pathname), "..", "..", "package.json");
|
|
35768
|
+
const pkg = JSON.parse(readFileSync35(pkgPath, "utf8"));
|
|
34771
35769
|
console.log(pkg.version);
|
|
34772
35770
|
} catch {
|
|
34773
35771
|
console.log("unknown");
|
|
@@ -34842,6 +35840,8 @@ if (args.includes("--global")) {
|
|
|
34842
35840
|
} else if (args[0] === "org") {
|
|
34843
35841
|
const { main: runOrg } = await Promise.resolve().then(() => (init_exe_org(), exe_org_exports));
|
|
34844
35842
|
await runOrg(args.slice(1));
|
|
35843
|
+
} else if (args[0] === "code-context") {
|
|
35844
|
+
await runCodeContext(args.slice(1));
|
|
34845
35845
|
} else if (args[0] === "jobs") {
|
|
34846
35846
|
const { cancelBackgroundJob: cancelBackgroundJob2, formatJobs: formatJobs2, listBackgroundJobs: listBackgroundJobs2 } = await Promise.resolve().then(() => (init_background_jobs(), background_jobs_exports));
|
|
34847
35847
|
const sub = args[1] ?? "status";
|
|
@@ -34982,16 +35982,19 @@ ID: ${result.id}`);
|
|
|
34982
35982
|
} else if (args[0] === "stack-update") {
|
|
34983
35983
|
const { runStackUpdateCli } = await Promise.resolve().then(() => (init_stack_update2(), stack_update_exports));
|
|
34984
35984
|
await runStackUpdateCli();
|
|
35985
|
+
} else if (args[0] === "registry-proxy") {
|
|
35986
|
+
const { main: runRegistryProxy2 } = await Promise.resolve().then(() => (init_registry_proxy2(), registry_proxy_exports));
|
|
35987
|
+
await runRegistryProxy2(args.slice(1));
|
|
34985
35988
|
} else if (args.includes("--tui") || args.includes("--demo") || args[0] === "tui") {
|
|
34986
35989
|
checkForUpdateOnBoot().catch(() => {
|
|
34987
35990
|
});
|
|
34988
35991
|
await init_App2().then(() => App_exports);
|
|
34989
35992
|
} else {
|
|
34990
|
-
const claudeDir =
|
|
34991
|
-
const settingsPath =
|
|
34992
|
-
const hasClaudeCode =
|
|
35993
|
+
const claudeDir = path54.join(os25.homedir(), ".claude");
|
|
35994
|
+
const settingsPath = path54.join(claudeDir, "settings.json");
|
|
35995
|
+
const hasClaudeCode = existsSync40(settingsPath) && (() => {
|
|
34993
35996
|
try {
|
|
34994
|
-
const raw =
|
|
35997
|
+
const raw = readFileSync35(settingsPath, "utf8");
|
|
34995
35998
|
return raw.includes("exe-os") || raw.includes("exe-mem");
|
|
34996
35999
|
} catch {
|
|
34997
36000
|
return false;
|
|
@@ -35001,9 +36004,9 @@ ID: ${result.id}`);
|
|
|
35001
36004
|
const { DEFAULT_COORDINATOR_TEMPLATE_NAME: DEFAULT_COORDINATOR_TEMPLATE_NAME2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
35002
36005
|
let cooName = DEFAULT_COORDINATOR_TEMPLATE_NAME2;
|
|
35003
36006
|
try {
|
|
35004
|
-
const rosterPath =
|
|
35005
|
-
if (
|
|
35006
|
-
const roster = JSON.parse(
|
|
36007
|
+
const rosterPath = path54.join(os25.homedir(), ".exe-os", "exe-employees.json");
|
|
36008
|
+
if (existsSync40(rosterPath)) {
|
|
36009
|
+
const roster = JSON.parse(readFileSync35(rosterPath, "utf8"));
|
|
35007
36010
|
const coo = roster.find((e) => e.role === "COO");
|
|
35008
36011
|
if (coo) cooName = coo.name;
|
|
35009
36012
|
}
|
|
@@ -35042,6 +36045,82 @@ cd into a project folder and run \x1B[1m${cooName}1\x1B[0m to boot your COO.
|
|
|
35042
36045
|
await init_App2().then(() => App_exports);
|
|
35043
36046
|
}
|
|
35044
36047
|
}
|
|
36048
|
+
async function runCodeContext(ccArgs) {
|
|
36049
|
+
const { buildCodeContextIndex: buildCodeContextIndex2, getCodeContextStats: getCodeContextStats2, searchCodeContext: searchCodeContext2 } = await Promise.resolve().then(() => (init_code_context_index(), code_context_index_exports));
|
|
36050
|
+
const sub = ccArgs[0] ?? "status";
|
|
36051
|
+
const projectRoot = valueAfter(ccArgs, "--project-root") ?? process.cwd();
|
|
36052
|
+
if (sub === "init" || sub === "index") {
|
|
36053
|
+
const force = ccArgs.includes("--force") || ccArgs.includes("--refresh");
|
|
36054
|
+
const index = buildCodeContextIndex2({ projectRoot, force });
|
|
36055
|
+
console.log(JSON.stringify({ projectRoot: index.projectRoot, branch: index.branch, indexedAt: index.indexedAt, files: Object.keys(index.files).length, symbols: Object.values(index.files).reduce((sum, f) => sum + f.symbols.length, 0), languageBreakdown: index.stats.languageBreakdown, rebuiltFiles: index.stats.rebuiltFiles, reusedFiles: index.stats.reusedFiles }, null, 2));
|
|
36056
|
+
return;
|
|
36057
|
+
}
|
|
36058
|
+
if (sub === "status" || sub === "stats") {
|
|
36059
|
+
console.log(JSON.stringify(getCodeContextStats2({ projectRoot }), null, 2));
|
|
36060
|
+
return;
|
|
36061
|
+
}
|
|
36062
|
+
if (sub === "search") {
|
|
36063
|
+
const query = valueAfter(ccArgs, "--query") ?? positionalSearchQuery(ccArgs.slice(1));
|
|
36064
|
+
if (!query) {
|
|
36065
|
+
console.error("Usage: exe-os code-context search <query> [--limit N] [--offset N] [--language python] [--path 'src/**'] [--refresh]");
|
|
36066
|
+
process.exit(1);
|
|
36067
|
+
}
|
|
36068
|
+
const languageFilters = [...valuesAfter(ccArgs, "--language") ?? [], ...valuesAfter(ccArgs, "--languages") ?? []];
|
|
36069
|
+
const pathFilters = [...valuesAfter(ccArgs, "--path") ?? [], ...valuesAfter(ccArgs, "--paths") ?? []];
|
|
36070
|
+
const results = searchCodeContext2(query, {
|
|
36071
|
+
projectRoot,
|
|
36072
|
+
limit: numberAfter(ccArgs, "--limit") ?? 20,
|
|
36073
|
+
offset: numberAfter(ccArgs, "--offset") ?? 0,
|
|
36074
|
+
refreshIndex: ccArgs.includes("--refresh") || ccArgs.includes("--refresh-index"),
|
|
36075
|
+
languages: languageFilters.length > 0 ? languageFilters : void 0,
|
|
36076
|
+
paths: pathFilters.length > 0 ? pathFilters : void 0
|
|
36077
|
+
});
|
|
36078
|
+
console.log(JSON.stringify({ query, results }, null, 2));
|
|
36079
|
+
return;
|
|
36080
|
+
}
|
|
36081
|
+
if (sub === "doctor") {
|
|
36082
|
+
const stats = getCodeContextStats2({ projectRoot });
|
|
36083
|
+
console.log(JSON.stringify({ ok: stats.files > 0, ...stats }, null, 2));
|
|
36084
|
+
process.exit(stats.files > 0 ? 0 : 1);
|
|
36085
|
+
}
|
|
36086
|
+
console.error("Usage: exe-os code-context init|index|status|stats|search|doctor");
|
|
36087
|
+
process.exit(1);
|
|
36088
|
+
}
|
|
36089
|
+
function positionalSearchQuery(args2) {
|
|
36090
|
+
const valueFlags = /* @__PURE__ */ new Set(["--project-root", "--limit", "--offset", "--language", "--languages", "--path", "--paths", "--query"]);
|
|
36091
|
+
const parts = [];
|
|
36092
|
+
for (let i = 0; i < args2.length; i++) {
|
|
36093
|
+
const arg = args2[i];
|
|
36094
|
+
if (valueFlags.has(arg)) {
|
|
36095
|
+
i++;
|
|
36096
|
+
continue;
|
|
36097
|
+
}
|
|
36098
|
+
if (arg.startsWith("--")) continue;
|
|
36099
|
+
parts.push(arg);
|
|
36100
|
+
}
|
|
36101
|
+
return parts.join(" ").trim();
|
|
36102
|
+
}
|
|
36103
|
+
function valueAfter(args2, flag) {
|
|
36104
|
+
const i = args2.indexOf(flag);
|
|
36105
|
+
if (i >= 0) return args2[i + 1];
|
|
36106
|
+
const prefixed = args2.find((arg) => arg.startsWith(`${flag}=`));
|
|
36107
|
+
return prefixed?.slice(flag.length + 1);
|
|
36108
|
+
}
|
|
36109
|
+
function valuesAfter(args2, flag) {
|
|
36110
|
+
const values = [];
|
|
36111
|
+
for (let i = 0; i < args2.length; i++) {
|
|
36112
|
+
const arg = args2[i];
|
|
36113
|
+
if (arg === flag && args2[i + 1]) values.push(args2[++i]);
|
|
36114
|
+
else if (arg.startsWith(`${flag}=`)) values.push(arg.slice(flag.length + 1));
|
|
36115
|
+
}
|
|
36116
|
+
return values.length > 0 ? values.flatMap((v) => v.split(",").map((s) => s.trim()).filter(Boolean)) : void 0;
|
|
36117
|
+
}
|
|
36118
|
+
function numberAfter(args2, flag) {
|
|
36119
|
+
const value = valueAfter(args2, flag);
|
|
36120
|
+
if (!value) return void 0;
|
|
36121
|
+
const parsed = Number(value);
|
|
36122
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
36123
|
+
}
|
|
35045
36124
|
async function runClaudeInstall() {
|
|
35046
36125
|
const { runInstaller: runInstaller2 } = await Promise.resolve().then(() => (init_installer(), installer_exports));
|
|
35047
36126
|
try {
|
|
@@ -35080,14 +36159,14 @@ async function runCodexInstall() {
|
|
|
35080
36159
|
}
|
|
35081
36160
|
async function runClaudeCheck() {
|
|
35082
36161
|
const { detectMcpNameCollisions: detectMcpNameCollisions2 } = await Promise.resolve().then(() => (init_installer(), installer_exports));
|
|
35083
|
-
const claudeDir =
|
|
35084
|
-
const settingsPath =
|
|
35085
|
-
const claudeJsonPath =
|
|
36162
|
+
const claudeDir = path54.join(os25.homedir(), ".claude");
|
|
36163
|
+
const settingsPath = path54.join(claudeDir, "settings.json");
|
|
36164
|
+
const claudeJsonPath = path54.join(os25.homedir(), ".claude.json");
|
|
35086
36165
|
let ok = true;
|
|
35087
|
-
if (
|
|
36166
|
+
if (existsSync40(settingsPath)) {
|
|
35088
36167
|
let settings;
|
|
35089
36168
|
try {
|
|
35090
|
-
settings = JSON.parse(
|
|
36169
|
+
settings = JSON.parse(readFileSync35(settingsPath, "utf8"));
|
|
35091
36170
|
} catch {
|
|
35092
36171
|
console.log("\x1B[31m\u2717\x1B[0m settings.json is malformed (invalid JSON)");
|
|
35093
36172
|
ok = false;
|
|
@@ -35113,10 +36192,10 @@ async function runClaudeCheck() {
|
|
|
35113
36192
|
console.log("\x1B[31m\u2717\x1B[0m settings.json not found");
|
|
35114
36193
|
ok = false;
|
|
35115
36194
|
}
|
|
35116
|
-
if (
|
|
36195
|
+
if (existsSync40(claudeJsonPath)) {
|
|
35117
36196
|
let claudeJson;
|
|
35118
36197
|
try {
|
|
35119
|
-
claudeJson = JSON.parse(
|
|
36198
|
+
claudeJson = JSON.parse(readFileSync35(claudeJsonPath, "utf8"));
|
|
35120
36199
|
} catch {
|
|
35121
36200
|
console.log("\x1B[31m\u2717\x1B[0m claude.json is malformed (invalid JSON)");
|
|
35122
36201
|
ok = false;
|
|
@@ -35149,8 +36228,8 @@ async function runClaudeCheck() {
|
|
|
35149
36228
|
} else {
|
|
35150
36229
|
console.log("\x1B[32m\u2713\x1B[0m No .mcp.json/project MCP name collisions detected");
|
|
35151
36230
|
}
|
|
35152
|
-
const skillsDir =
|
|
35153
|
-
if (
|
|
36231
|
+
const skillsDir = path54.join(claudeDir, "skills");
|
|
36232
|
+
if (existsSync40(skillsDir)) {
|
|
35154
36233
|
console.log("\x1B[32m\u2713\x1B[0m Slash skills directory exists");
|
|
35155
36234
|
} else {
|
|
35156
36235
|
console.log("\x1B[31m\u2717\x1B[0m Slash skills directory missing");
|
|
@@ -35174,16 +36253,16 @@ async function runClaudeUninstall(flags = []) {
|
|
|
35174
36253
|
const dryRun = flags.includes("--dry-run");
|
|
35175
36254
|
const purge = flags.includes("--purge");
|
|
35176
36255
|
const homeDir = os25.homedir();
|
|
35177
|
-
const claudeDir =
|
|
35178
|
-
const settingsPath =
|
|
35179
|
-
const claudeJsonPath =
|
|
35180
|
-
const exeOsDir =
|
|
36256
|
+
const claudeDir = path54.join(homeDir, ".claude");
|
|
36257
|
+
const settingsPath = path54.join(claudeDir, "settings.json");
|
|
36258
|
+
const claudeJsonPath = path54.join(homeDir, ".claude.json");
|
|
36259
|
+
const exeOsDir = path54.join(homeDir, ".exe-os");
|
|
35181
36260
|
let removed = 0;
|
|
35182
36261
|
const log = (msg) => console.log(dryRun ? `[dry-run] ${msg}` : msg);
|
|
35183
36262
|
let settings = {};
|
|
35184
|
-
if (
|
|
36263
|
+
if (existsSync40(settingsPath)) {
|
|
35185
36264
|
try {
|
|
35186
|
-
settings = JSON.parse(
|
|
36265
|
+
settings = JSON.parse(readFileSync35(settingsPath, "utf8"));
|
|
35187
36266
|
} catch {
|
|
35188
36267
|
console.error("Your ~/.claude/settings.json appears malformed.");
|
|
35189
36268
|
if (purge) {
|
|
@@ -35221,15 +36300,15 @@ async function runClaudeUninstall(flags = []) {
|
|
|
35221
36300
|
permCount = before - settings.permissions.allow.length;
|
|
35222
36301
|
}
|
|
35223
36302
|
if (!dryRun) {
|
|
35224
|
-
|
|
36303
|
+
writeFileSync26(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
35225
36304
|
}
|
|
35226
36305
|
log("\u2713 Removed exe-os hooks from settings.json");
|
|
35227
36306
|
if (permCount > 0) log(`\u2713 Removed ${permCount} MCP permission entries`);
|
|
35228
36307
|
removed++;
|
|
35229
36308
|
}
|
|
35230
36309
|
}
|
|
35231
|
-
if (
|
|
35232
|
-
const raw =
|
|
36310
|
+
if (existsSync40(claudeJsonPath)) {
|
|
36311
|
+
const raw = readFileSync35(claudeJsonPath, "utf8");
|
|
35233
36312
|
if (raw.length > 1e6) {
|
|
35234
36313
|
console.error("claude.json exceeds 1 MB \u2014 skipping parse.");
|
|
35235
36314
|
} else {
|
|
@@ -35250,7 +36329,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
35250
36329
|
}
|
|
35251
36330
|
if (removedMcp) {
|
|
35252
36331
|
if (!dryRun) {
|
|
35253
|
-
|
|
36332
|
+
writeFileSync26(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
35254
36333
|
}
|
|
35255
36334
|
log("\u2713 Removed exe-os MCP server from claude.json");
|
|
35256
36335
|
removed++;
|
|
@@ -35258,14 +36337,14 @@ async function runClaudeUninstall(flags = []) {
|
|
|
35258
36337
|
}
|
|
35259
36338
|
}
|
|
35260
36339
|
}
|
|
35261
|
-
const skillsDir =
|
|
35262
|
-
if (
|
|
36340
|
+
const skillsDir = path54.join(claudeDir, "skills");
|
|
36341
|
+
if (existsSync40(skillsDir)) {
|
|
35263
36342
|
let skillCount = 0;
|
|
35264
36343
|
try {
|
|
35265
|
-
const entries =
|
|
36344
|
+
const entries = readdirSync12(skillsDir);
|
|
35266
36345
|
for (const entry of entries) {
|
|
35267
36346
|
if (entry.startsWith("exe")) {
|
|
35268
|
-
const fullPath =
|
|
36347
|
+
const fullPath = path54.join(skillsDir, entry);
|
|
35269
36348
|
if (!dryRun) rmSync2(fullPath, { recursive: true, force: true });
|
|
35270
36349
|
skillCount++;
|
|
35271
36350
|
}
|
|
@@ -35277,30 +36356,30 @@ async function runClaudeUninstall(flags = []) {
|
|
|
35277
36356
|
removed++;
|
|
35278
36357
|
}
|
|
35279
36358
|
}
|
|
35280
|
-
const claudeMdPath =
|
|
35281
|
-
if (
|
|
35282
|
-
const content =
|
|
36359
|
+
const claudeMdPath = path54.join(claudeDir, "CLAUDE.md");
|
|
36360
|
+
if (existsSync40(claudeMdPath)) {
|
|
36361
|
+
const content = readFileSync35(claudeMdPath, "utf8");
|
|
35283
36362
|
const startMarker = "<!-- exe-os:orchestration-start -->";
|
|
35284
36363
|
const endMarker = "<!-- exe-os:orchestration-end -->";
|
|
35285
36364
|
const startIdx = content.indexOf(startMarker);
|
|
35286
36365
|
const endIdx = content.indexOf(endMarker);
|
|
35287
36366
|
if (startIdx !== -1 && endIdx !== -1) {
|
|
35288
36367
|
const cleaned = (content.slice(0, startIdx) + content.slice(endIdx + endMarker.length)).replace(/\n{3,}/g, "\n\n").trim() + "\n";
|
|
35289
|
-
if (!dryRun)
|
|
36368
|
+
if (!dryRun) writeFileSync26(claudeMdPath, cleaned);
|
|
35290
36369
|
log("\u2713 Removed orchestration block from CLAUDE.md");
|
|
35291
36370
|
removed++;
|
|
35292
36371
|
}
|
|
35293
36372
|
}
|
|
35294
|
-
const agentsDir =
|
|
35295
|
-
if (
|
|
36373
|
+
const agentsDir = path54.join(claudeDir, "agents");
|
|
36374
|
+
if (existsSync40(agentsDir)) {
|
|
35296
36375
|
let agentCount = 0;
|
|
35297
36376
|
try {
|
|
35298
|
-
const entries =
|
|
36377
|
+
const entries = readdirSync12(agentsDir).filter((f) => f.endsWith(".md"));
|
|
35299
36378
|
let knownNames = /* @__PURE__ */ new Set();
|
|
35300
|
-
const rosterPath =
|
|
35301
|
-
if (
|
|
36379
|
+
const rosterPath = path54.join(exeOsDir, "exe-employees.json");
|
|
36380
|
+
if (existsSync40(rosterPath)) {
|
|
35302
36381
|
try {
|
|
35303
|
-
const roster = JSON.parse(
|
|
36382
|
+
const roster = JSON.parse(readFileSync35(rosterPath, "utf8"));
|
|
35304
36383
|
knownNames = new Set(roster.map((e) => e.name));
|
|
35305
36384
|
} catch {
|
|
35306
36385
|
}
|
|
@@ -35308,7 +36387,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
35308
36387
|
for (const entry of entries) {
|
|
35309
36388
|
const name = entry.replace(/\.md$/, "");
|
|
35310
36389
|
if (knownNames.has(name)) {
|
|
35311
|
-
if (!dryRun) rmSync2(
|
|
36390
|
+
if (!dryRun) rmSync2(path54.join(agentsDir, entry), { force: true });
|
|
35312
36391
|
agentCount++;
|
|
35313
36392
|
}
|
|
35314
36393
|
}
|
|
@@ -35319,16 +36398,16 @@ async function runClaudeUninstall(flags = []) {
|
|
|
35319
36398
|
removed++;
|
|
35320
36399
|
}
|
|
35321
36400
|
}
|
|
35322
|
-
const projectsDir =
|
|
35323
|
-
if (
|
|
36401
|
+
const projectsDir = path54.join(claudeDir, "projects");
|
|
36402
|
+
if (existsSync40(projectsDir)) {
|
|
35324
36403
|
let projectCount = 0;
|
|
35325
36404
|
try {
|
|
35326
|
-
const projects =
|
|
36405
|
+
const projects = readdirSync12(projectsDir);
|
|
35327
36406
|
for (const proj of projects) {
|
|
35328
|
-
const projSettings =
|
|
35329
|
-
if (!
|
|
36407
|
+
const projSettings = path54.join(projectsDir, proj, "settings.json");
|
|
36408
|
+
if (!existsSync40(projSettings)) continue;
|
|
35330
36409
|
try {
|
|
35331
|
-
const pSettings = JSON.parse(
|
|
36410
|
+
const pSettings = JSON.parse(readFileSync35(projSettings, "utf8"));
|
|
35332
36411
|
let changed = false;
|
|
35333
36412
|
if (Array.isArray(pSettings.permissions?.allow)) {
|
|
35334
36413
|
const before = pSettings.permissions.allow.length;
|
|
@@ -35338,7 +36417,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
35338
36417
|
if (pSettings.permissions.allow.length < before) changed = true;
|
|
35339
36418
|
}
|
|
35340
36419
|
if (changed && !dryRun) {
|
|
35341
|
-
|
|
36420
|
+
writeFileSync26(projSettings, JSON.stringify(pSettings, null, 2) + "\n");
|
|
35342
36421
|
}
|
|
35343
36422
|
if (changed) projectCount++;
|
|
35344
36423
|
} catch {
|
|
@@ -35362,18 +36441,18 @@ async function runClaudeUninstall(flags = []) {
|
|
|
35362
36441
|
};
|
|
35363
36442
|
const exeBinPath = findExeBin3();
|
|
35364
36443
|
if (!exeBinPath) throw new Error("exe-os not found in PATH");
|
|
35365
|
-
const binDir =
|
|
36444
|
+
const binDir = path54.dirname(exeBinPath);
|
|
35366
36445
|
let symlinkCount = 0;
|
|
35367
|
-
const rosterPath =
|
|
35368
|
-
if (
|
|
35369
|
-
const roster = JSON.parse(
|
|
36446
|
+
const rosterPath = path54.join(exeOsDir, "exe-employees.json");
|
|
36447
|
+
if (existsSync40(rosterPath)) {
|
|
36448
|
+
const roster = JSON.parse(readFileSync35(rosterPath, "utf8"));
|
|
35370
36449
|
const { DEFAULT_COORDINATOR_TEMPLATE_NAME: DEFAULT_COORDINATOR_TEMPLATE_NAME2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
35371
36450
|
const coordinatorName = roster.find((e) => e.role?.toLowerCase() === "coo")?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME2;
|
|
35372
36451
|
for (const emp of roster) {
|
|
35373
36452
|
if (emp.name === coordinatorName) continue;
|
|
35374
36453
|
for (const suffix of ["", "-opencode"]) {
|
|
35375
|
-
const linkPath =
|
|
35376
|
-
if (
|
|
36454
|
+
const linkPath = path54.join(binDir, `${emp.name}${suffix}`);
|
|
36455
|
+
if (existsSync40(linkPath)) {
|
|
35377
36456
|
if (!dryRun) rmSync2(linkPath, { force: true });
|
|
35378
36457
|
symlinkCount++;
|
|
35379
36458
|
}
|
|
@@ -35386,7 +36465,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
35386
36465
|
}
|
|
35387
36466
|
} catch {
|
|
35388
36467
|
}
|
|
35389
|
-
if (purge &&
|
|
36468
|
+
if (purge && existsSync40(exeOsDir)) {
|
|
35390
36469
|
if (!dryRun) {
|
|
35391
36470
|
process.stdout.write("\x1B[33m\u26A0 This will delete all memories, identities, and agent data.\x1B[0m\n");
|
|
35392
36471
|
process.stdout.write(" Removing ~/.exe-os...\n");
|
|
@@ -35411,7 +36490,7 @@ async function checkForUpdateOnBoot() {
|
|
|
35411
36490
|
const config = await loadConfig2();
|
|
35412
36491
|
if (!config.autoUpdate.checkOnBoot) return;
|
|
35413
36492
|
const { checkForUpdate: checkForUpdate2 } = await Promise.resolve().then(() => (init_update(), update_exports));
|
|
35414
|
-
const packageRoot =
|
|
36493
|
+
const packageRoot = path54.resolve(
|
|
35415
36494
|
new URL("../..", import.meta.url).pathname
|
|
35416
36495
|
);
|
|
35417
36496
|
const result = checkForUpdate2(packageRoot);
|
|
@@ -35472,7 +36551,7 @@ async function runActivate(key) {
|
|
|
35472
36551
|
const idTemplate = getIdentityTemplate(identityKey);
|
|
35473
36552
|
if (idTemplate) {
|
|
35474
36553
|
const idPath = identityPath2(name);
|
|
35475
|
-
const dir =
|
|
36554
|
+
const dir = path54.dirname(idPath);
|
|
35476
36555
|
if (!fs8.existsSync(dir)) fs8.mkdirSync(dir, { recursive: true });
|
|
35477
36556
|
fs8.writeFileSync(idPath, idTemplate.replace(/^agent_id: \w+/m, `agent_id: ${name}`), "utf-8");
|
|
35478
36557
|
}
|