@askexenow/exe-os 0.9.36 → 0.9.38
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 +55 -0
- package/dist/bin/backfill-conversations.js +5 -2
- package/dist/bin/backfill-responses.js +5 -2
- package/dist/bin/backfill-vectors.js +2 -2
- package/dist/bin/cleanup-stale-review-tasks.js +5 -2
- package/dist/bin/cli.js +557 -164
- package/dist/bin/exe-agent.js +2 -2
- package/dist/bin/exe-assign.js +5 -2
- package/dist/bin/exe-boot.js +2 -2
- package/dist/bin/exe-call.js +2 -2
- package/dist/bin/exe-dispatch.js +5 -2
- package/dist/bin/exe-doctor.js +2 -2
- package/dist/bin/exe-export-behaviors.js +5 -2
- package/dist/bin/exe-forget.js +5 -2
- package/dist/bin/exe-gateway.js +27 -3
- package/dist/bin/exe-heartbeat.js +5 -2
- package/dist/bin/exe-kill.js +5 -2
- package/dist/bin/exe-launch-agent.js +2 -2
- package/dist/bin/exe-new-employee.js +71 -4
- package/dist/bin/exe-pending-messages.js +5 -2
- package/dist/bin/exe-pending-notifications.js +5 -2
- package/dist/bin/exe-pending-reviews.js +5 -2
- package/dist/bin/exe-rename.js +1747 -199
- package/dist/bin/exe-review.js +5 -2
- package/dist/bin/exe-search.js +11 -10
- package/dist/bin/exe-session-cleanup.js +9 -3
- package/dist/bin/exe-start-codex.js +2 -2
- package/dist/bin/exe-start-opencode.js +2 -2
- package/dist/bin/exe-status.js +5 -2
- package/dist/bin/exe-team.js +5 -2
- package/dist/bin/git-sweep.js +5 -2
- package/dist/bin/graph-backfill.js +2 -2
- package/dist/bin/graph-export.js +5 -2
- package/dist/bin/install.js +68 -2
- package/dist/bin/intercom-check.js +5 -2
- package/dist/bin/scan-tasks.js +5 -2
- package/dist/bin/setup.js +2 -2
- package/dist/bin/shard-migrate.js +2 -2
- package/dist/bin/stack-update.js +308 -0
- package/dist/gateway/index.js +27 -3
- package/dist/hooks/bug-report-worker.js +5 -2
- package/dist/hooks/codex-stop-task-finalizer.js +5 -2
- package/dist/hooks/commit-complete.js +5 -2
- package/dist/hooks/error-recall.js +11 -10
- package/dist/hooks/ingest-worker.js +9 -2
- package/dist/hooks/ingest.js +5 -2
- package/dist/hooks/instructions-loaded.js +5 -2
- package/dist/hooks/notification.js +5 -2
- package/dist/hooks/post-compact.js +5 -2
- package/dist/hooks/post-tool-combined.js +20 -12
- package/dist/hooks/pre-compact.js +7 -3
- package/dist/hooks/pre-tool-use.js +5 -2
- package/dist/hooks/prompt-submit.js +11 -10
- package/dist/hooks/session-end.js +133 -5
- package/dist/hooks/session-start.js +11 -10
- package/dist/hooks/stop.js +9 -4
- package/dist/hooks/subagent-stop.js +5 -2
- package/dist/hooks/summary-worker.js +122 -20
- package/dist/index.js +49 -17
- package/dist/lib/employee-templates.js +2 -2
- package/dist/lib/exe-daemon.js +11011 -10418
- package/dist/lib/hybrid-search.js +11 -10
- package/dist/lib/schedules.js +2 -2
- package/dist/lib/store.js +5 -2
- package/dist/mcp/server.js +6881 -6356
- package/dist/runtime/index.js +27 -16
- package/dist/tui/App.js +5 -2
- package/package.json +4 -1
- package/src/commands/exe/save.md +9 -5
- package/stack.release.json +31 -0
- package/stack.release.schema.json +31 -0
package/dist/bin/cli.js
CHANGED
|
@@ -814,6 +814,7 @@ var installer_exports = {};
|
|
|
814
814
|
__export(installer_exports, {
|
|
815
815
|
cleanOldShellFunctions: () => cleanOldShellFunctions,
|
|
816
816
|
copySlashCommands: () => copySlashCommands,
|
|
817
|
+
detectMcpNameCollisions: () => detectMcpNameCollisions,
|
|
817
818
|
installStatusLine: () => installStatusLine,
|
|
818
819
|
mergeHooks: () => mergeHooks,
|
|
819
820
|
registerMcpServer: () => registerMcpServer,
|
|
@@ -899,6 +900,60 @@ name: ${skillName}
|
|
|
899
900
|
await writeFile3(destPath, content);
|
|
900
901
|
return true;
|
|
901
902
|
}
|
|
903
|
+
function readJsonFile(filePath) {
|
|
904
|
+
try {
|
|
905
|
+
return JSON.parse(readFileSync5(filePath, "utf-8"));
|
|
906
|
+
} catch {
|
|
907
|
+
return null;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
function findAncestorMcpJsons(startDir, homeDir) {
|
|
911
|
+
const files = [];
|
|
912
|
+
let dir = path6.resolve(startDir);
|
|
913
|
+
const root = path6.parse(dir).root;
|
|
914
|
+
const stop = path6.resolve(homeDir);
|
|
915
|
+
while (dir !== root) {
|
|
916
|
+
const candidate = path6.join(dir, ".mcp.json");
|
|
917
|
+
if (existsSync7(candidate)) files.push(candidate);
|
|
918
|
+
if (dir === stop) break;
|
|
919
|
+
dir = path6.dirname(dir);
|
|
920
|
+
}
|
|
921
|
+
return files;
|
|
922
|
+
}
|
|
923
|
+
function pathApplies(projectPath, cwd2) {
|
|
924
|
+
const project = path6.resolve(projectPath);
|
|
925
|
+
const current = path6.resolve(cwd2);
|
|
926
|
+
return current === project || current.startsWith(project + path6.sep);
|
|
927
|
+
}
|
|
928
|
+
function detectMcpNameCollisions(homeDir = os5.homedir(), cwd2 = process.cwd()) {
|
|
929
|
+
const claudeJsonPath = path6.join(homeDir, ".claude.json");
|
|
930
|
+
if (!existsSync7(claudeJsonPath)) return [];
|
|
931
|
+
const claudeJson = readJsonFile(claudeJsonPath);
|
|
932
|
+
if (!claudeJson?.projects) return [];
|
|
933
|
+
const collisions = [];
|
|
934
|
+
const mcpJsons = findAncestorMcpJsons(cwd2, homeDir);
|
|
935
|
+
if (mcpJsons.length === 0) return [];
|
|
936
|
+
for (const [projectPath, projectConfig] of Object.entries(claudeJson.projects)) {
|
|
937
|
+
if (!pathApplies(projectPath, cwd2)) continue;
|
|
938
|
+
const projectServerNames = new Set(Object.keys(projectConfig.mcpServers ?? {}));
|
|
939
|
+
if (projectServerNames.size === 0) continue;
|
|
940
|
+
const enabled = new Set(projectConfig.enabledMcpjsonServers ?? []);
|
|
941
|
+
for (const mcpJsonPath of mcpJsons) {
|
|
942
|
+
const mcpJson = readJsonFile(mcpJsonPath);
|
|
943
|
+
const mcpServerNames = Object.keys(mcpJson?.mcpServers ?? {});
|
|
944
|
+
for (const serverName of mcpServerNames) {
|
|
945
|
+
if (!projectServerNames.has(serverName)) continue;
|
|
946
|
+
collisions.push({
|
|
947
|
+
mcpJsonPath,
|
|
948
|
+
projectPath,
|
|
949
|
+
serverName,
|
|
950
|
+
disabledInMcpJson: !enabled.has(serverName)
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
return collisions;
|
|
956
|
+
}
|
|
902
957
|
async function registerMcpServer(packageRoot, homeDir = os5.homedir()) {
|
|
903
958
|
const claudeJsonPath = path6.join(homeDir, ".claude.json");
|
|
904
959
|
let claudeJson = {};
|
|
@@ -927,12 +982,26 @@ async function registerMcpServer(packageRoot, homeDir = os5.homedir()) {
|
|
|
927
982
|
if (osMatches) {
|
|
928
983
|
await cleanSettingsJsonMcp(path6.join(homeDir, ".claude", "settings.json"));
|
|
929
984
|
await migratePermissionsToExeOs(path6.join(homeDir, ".claude", "settings.json"));
|
|
985
|
+
const collisions2 = detectMcpNameCollisions(homeDir, packageRoot).filter((c) => c.serverName === MCP_PRIMARY_KEY || c.serverName === MCP_LEGACY_KEY);
|
|
986
|
+
for (const c of collisions2) {
|
|
987
|
+
process.stderr.write(
|
|
988
|
+
`exe-os: WARNING Claude Code MCP name collision: ${c.serverName} exists in ${c.mcpJsonPath} and ~/.claude.json project ${c.projectPath}. Remove or rename the .mcp.json entry if tools do not surface.
|
|
989
|
+
`
|
|
990
|
+
);
|
|
991
|
+
}
|
|
930
992
|
return false;
|
|
931
993
|
}
|
|
932
994
|
claudeJson.mcpServers[MCP_PRIMARY_KEY] = newEntry;
|
|
933
995
|
await writeFile3(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
934
996
|
await cleanSettingsJsonMcp(path6.join(homeDir, ".claude", "settings.json"));
|
|
935
997
|
await migratePermissionsToExeOs(path6.join(homeDir, ".claude", "settings.json"));
|
|
998
|
+
const collisions = detectMcpNameCollisions(homeDir, packageRoot).filter((c) => c.serverName === MCP_PRIMARY_KEY || c.serverName === MCP_LEGACY_KEY);
|
|
999
|
+
for (const c of collisions) {
|
|
1000
|
+
process.stderr.write(
|
|
1001
|
+
`exe-os: WARNING Claude Code MCP name collision: ${c.serverName} exists in ${c.mcpJsonPath} and ~/.claude.json project ${c.projectPath}. Remove or rename the .mcp.json entry if tools do not surface.
|
|
1002
|
+
`
|
|
1003
|
+
);
|
|
1004
|
+
}
|
|
936
1005
|
return true;
|
|
937
1006
|
}
|
|
938
1007
|
async function cleanSettingsJsonMcp(settingsPath) {
|
|
@@ -1234,8 +1303,6 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1234
1303
|
"purge_document",
|
|
1235
1304
|
"rerank_documents",
|
|
1236
1305
|
"set_document_importance",
|
|
1237
|
-
"create_wiki_page",
|
|
1238
|
-
"update_wiki_page",
|
|
1239
1306
|
"get_wiki_page",
|
|
1240
1307
|
"list_wiki_pages",
|
|
1241
1308
|
// System
|
|
@@ -1773,8 +1840,8 @@ function deriveMachineKey() {
|
|
|
1773
1840
|
}
|
|
1774
1841
|
function readMachineId() {
|
|
1775
1842
|
try {
|
|
1776
|
-
const { readFileSync:
|
|
1777
|
-
return
|
|
1843
|
+
const { readFileSync: readFileSync30 } = __require("fs");
|
|
1844
|
+
return readFileSync30("/etc/machine-id", "utf-8").trim();
|
|
1778
1845
|
} catch {
|
|
1779
1846
|
return "";
|
|
1780
1847
|
}
|
|
@@ -6839,6 +6906,9 @@ function classifyMemoryType(input) {
|
|
|
6839
6906
|
if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
|
|
6840
6907
|
if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
|
|
6841
6908
|
if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
|
|
6909
|
+
if (tool.includes("checkpoint") || text.startsWith("context checkpoint")) return "checkpoint";
|
|
6910
|
+
if (tool.includes("sessionsummary") || tool.includes("session-summary")) return "summary";
|
|
6911
|
+
if (tool.includes("sessionend") || text.startsWith("session ended")) return "summary";
|
|
6842
6912
|
if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
|
|
6843
6913
|
if (tool === "store_memory" || tool === "manual") return "observation";
|
|
6844
6914
|
return "raw";
|
|
@@ -7491,7 +7561,7 @@ var init_platform_procedures = __esm({
|
|
|
7491
7561
|
title: "MCP tools \u2014 wiki, documents, and content",
|
|
7492
7562
|
domain: "tool-use",
|
|
7493
7563
|
priority: "p1",
|
|
7494
|
-
content: "
|
|
7564
|
+
content: "wiki: read/list wiki pages only. Direct wiki write tools are removed; wiki updates flow through raw-data ingestion/projection into the curated wiki store. Legacy aliases: list_wiki_pages/get_wiki_page. crm: read/list/get CRM records from exe-db. raw_data: read capped raw landing-pad events from exe-db with payload opt-in. ingest_document: import a file (PDF, MD, etc.) into memory as chunks. list_documents: browse ingested documents by workspace. purge_document: remove a document and its memory chunks. set_document_importance: adjust chunk importance scores. rerank_documents: re-score document relevance for a query."
|
|
7495
7565
|
},
|
|
7496
7566
|
{
|
|
7497
7567
|
title: "MCP tools \u2014 system, operations, and admin",
|
|
@@ -7509,7 +7579,7 @@ var init_platform_procedures = __esm({
|
|
|
7509
7579
|
title: "MCP tools \u2014 advanced (triggers, skills, orchestration)",
|
|
7510
7580
|
domain: "tool-use",
|
|
7511
7581
|
priority: "p1",
|
|
7512
|
-
content: "create_trigger: set up a scheduled recurring agent job (cron). list_triggers: view active triggers. load_skill: load a slash-command skill dynamically. apply_starter_pack: import a pre-built behavior + identity pack for a role. export_orchestration: export full org state (tasks, behaviors, identities) as portable JSON. import_orchestration: import org state into a new instance. deploy_client: deploy a customer client instance. query_company_brain: unified RAG query across all company knowledge. create_reminder: set a text reminder (shown in boot brief). list_reminders: view pending reminders. complete_reminder: mark a reminder done. global_procedure: manage
|
|
7582
|
+
content: "create_trigger: set up a scheduled recurring agent job (cron). list_triggers: view active triggers. load_skill: load a slash-command skill dynamically. apply_starter_pack: import a pre-built behavior + identity pack for a role. export_orchestration: export full org state (tasks, behaviors, identities) as portable JSON. import_orchestration: import org state into a new instance. deploy_client: deploy a customer client instance. query_company_brain: unified RAG query across all company knowledge. create_reminder: set a text reminder (shown in boot brief). list_reminders: view pending reminders. complete_reminder: mark a reminder done. global_procedure: manage customer-owned company procedures (Layer 0; actions: store, list, deactivate). Legacy aliases: store_global_procedure, list_global_procedures, deactivate_global_procedure."
|
|
7513
7583
|
}
|
|
7514
7584
|
];
|
|
7515
7585
|
PLATFORM_PROCEDURE_TITLES = new Set(
|
|
@@ -8991,9 +9061,9 @@ Unclassified: ${unclassified}
|
|
|
8991
9061
|
}
|
|
8992
9062
|
async function exportBatches(options) {
|
|
8993
9063
|
const fs8 = await import("fs");
|
|
8994
|
-
const
|
|
9064
|
+
const path49 = await import("path");
|
|
8995
9065
|
const client = getClient();
|
|
8996
|
-
const outDir =
|
|
9066
|
+
const outDir = path49.join(process.cwd(), "exe/output/classifications/input");
|
|
8997
9067
|
fs8.mkdirSync(outDir, { recursive: true });
|
|
8998
9068
|
const countResult = await client.execute({
|
|
8999
9069
|
sql: "SELECT COUNT(*) as cnt FROM memories WHERE intent IS NULL AND outcome IS NULL AND domain IS NULL",
|
|
@@ -9017,7 +9087,7 @@ async function exportBatches(options) {
|
|
|
9017
9087
|
const text = String(row.text || "").replace(/\n/g, " ");
|
|
9018
9088
|
return JSON.stringify({ id: row.id, text });
|
|
9019
9089
|
});
|
|
9020
|
-
const batchFile =
|
|
9090
|
+
const batchFile = path49.join(outDir, `batch-${String(batchNum).padStart(4, "0")}.jsonl`);
|
|
9021
9091
|
fs8.writeFileSync(batchFile, lines.join("\n") + "\n");
|
|
9022
9092
|
exported += batch.rows.length;
|
|
9023
9093
|
offset += options.batchSize;
|
|
@@ -9033,7 +9103,7 @@ async function exportBatches(options) {
|
|
|
9033
9103
|
}
|
|
9034
9104
|
async function importClassifications(importDir) {
|
|
9035
9105
|
const fs8 = await import("fs");
|
|
9036
|
-
const
|
|
9106
|
+
const path49 = await import("path");
|
|
9037
9107
|
const client = getClient();
|
|
9038
9108
|
const files = fs8.readdirSync(importDir).filter((f) => f.endsWith(".jsonl")).sort();
|
|
9039
9109
|
process.stderr.write(`[backfill-metadata] Found ${files.length} JSONL files to import from ${importDir}
|
|
@@ -9041,7 +9111,7 @@ async function importClassifications(importDir) {
|
|
|
9041
9111
|
let imported = 0;
|
|
9042
9112
|
let invalid = 0;
|
|
9043
9113
|
for (const file of files) {
|
|
9044
|
-
const lines = fs8.readFileSync(
|
|
9114
|
+
const lines = fs8.readFileSync(path49.join(importDir, file), "utf-8").split("\n").filter(Boolean);
|
|
9045
9115
|
for (const line of lines) {
|
|
9046
9116
|
try {
|
|
9047
9117
|
const rec = JSON.parse(line);
|
|
@@ -11674,10 +11744,10 @@ async function disposeEmbedder() {
|
|
|
11674
11744
|
async function embedDirect(text) {
|
|
11675
11745
|
const llamaCpp = await import("node-llama-cpp");
|
|
11676
11746
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
11677
|
-
const { existsSync:
|
|
11678
|
-
const
|
|
11679
|
-
const modelPath =
|
|
11680
|
-
if (!
|
|
11747
|
+
const { existsSync: existsSync35 } = await import("fs");
|
|
11748
|
+
const path49 = await import("path");
|
|
11749
|
+
const modelPath = path49.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
11750
|
+
if (!existsSync35(modelPath)) {
|
|
11681
11751
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
11682
11752
|
}
|
|
11683
11753
|
const llama = await llamaCpp.getLlama();
|
|
@@ -14580,6 +14650,8 @@ async function main2() {
|
|
|
14580
14650
|
process.exit(1);
|
|
14581
14651
|
}
|
|
14582
14652
|
const [oldName, newName] = args2;
|
|
14653
|
+
const { initStore: initStore2 } = await Promise.resolve().then(() => (init_store(), store_exports));
|
|
14654
|
+
await initStore2({ lightweight: true });
|
|
14583
14655
|
const result = await renameEmployee(oldName, newName);
|
|
14584
14656
|
if (!result.success) {
|
|
14585
14657
|
console.error(`Error: ${result.error}`);
|
|
@@ -16462,6 +16534,309 @@ var init_update = __esm({
|
|
|
16462
16534
|
}
|
|
16463
16535
|
});
|
|
16464
16536
|
|
|
16537
|
+
// src/lib/stack-update.ts
|
|
16538
|
+
import { execFileSync as execFileSync3 } from "child_process";
|
|
16539
|
+
import { existsSync as existsSync30, mkdirSync as mkdirSync19, readFileSync as readFileSync25, renameSync as renameSync6, writeFileSync as writeFileSync20 } from "fs";
|
|
16540
|
+
import http from "http";
|
|
16541
|
+
import https from "https";
|
|
16542
|
+
import path37 from "path";
|
|
16543
|
+
function parseStackManifest(raw) {
|
|
16544
|
+
const parsed = JSON.parse(raw);
|
|
16545
|
+
if (parsed.schemaVersion !== 1) throw new Error("Unsupported stack manifest schemaVersion");
|
|
16546
|
+
if (!parsed.latest || !parsed.stacks || typeof parsed.stacks !== "object") {
|
|
16547
|
+
throw new Error("Invalid stack manifest: latest and stacks are required");
|
|
16548
|
+
}
|
|
16549
|
+
for (const [version, release] of Object.entries(parsed.stacks)) {
|
|
16550
|
+
if (!release.version) release.version = version;
|
|
16551
|
+
if (!release.services || typeof release.services !== "object") {
|
|
16552
|
+
throw new Error(`Invalid stack manifest: release ${version} has no services`);
|
|
16553
|
+
}
|
|
16554
|
+
for (const [serviceName, service] of Object.entries(release.services)) {
|
|
16555
|
+
if (!service.image || !service.env) {
|
|
16556
|
+
throw new Error(`Invalid stack manifest: ${version}.${serviceName} requires image and env`);
|
|
16557
|
+
}
|
|
16558
|
+
}
|
|
16559
|
+
}
|
|
16560
|
+
return parsed;
|
|
16561
|
+
}
|
|
16562
|
+
async function loadStackManifest(ref, fetchText = defaultFetchText) {
|
|
16563
|
+
if (/^https?:\/\//.test(ref)) return parseStackManifest(await fetchText(ref));
|
|
16564
|
+
return parseStackManifest(readFileSync25(ref, "utf8"));
|
|
16565
|
+
}
|
|
16566
|
+
function parseEnv(raw) {
|
|
16567
|
+
const env = /* @__PURE__ */ new Map();
|
|
16568
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
16569
|
+
const trimmed = line.trim();
|
|
16570
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
16571
|
+
const idx = line.indexOf("=");
|
|
16572
|
+
if (idx <= 0) continue;
|
|
16573
|
+
env.set(line.slice(0, idx).trim(), line.slice(idx + 1));
|
|
16574
|
+
}
|
|
16575
|
+
return env;
|
|
16576
|
+
}
|
|
16577
|
+
function patchEnv(raw, updates) {
|
|
16578
|
+
const seen = /* @__PURE__ */ new Set();
|
|
16579
|
+
const lines = raw.replace(/\n$/, "").split(/\r?\n/);
|
|
16580
|
+
const patched = lines.map((line) => {
|
|
16581
|
+
const idx = line.indexOf("=");
|
|
16582
|
+
if (idx <= 0 || line.trim().startsWith("#")) return line;
|
|
16583
|
+
const key = line.slice(0, idx).trim();
|
|
16584
|
+
if (!(key in updates)) return line;
|
|
16585
|
+
seen.add(key);
|
|
16586
|
+
return `${key}=${updates[key]}`;
|
|
16587
|
+
});
|
|
16588
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
16589
|
+
if (!seen.has(key)) patched.push(`${key}=${value}`);
|
|
16590
|
+
}
|
|
16591
|
+
return patched.join("\n").replace(/\n*$/, "\n");
|
|
16592
|
+
}
|
|
16593
|
+
function createStackUpdatePlan(manifest, envRaw, targetVersion) {
|
|
16594
|
+
const version = targetVersion ?? manifest.latest;
|
|
16595
|
+
const release = manifest.stacks[version];
|
|
16596
|
+
if (!release) throw new Error(`Stack version ${version} not found in manifest`);
|
|
16597
|
+
const env = parseEnv(envRaw);
|
|
16598
|
+
const changes = [];
|
|
16599
|
+
for (const [serviceName, service] of Object.entries(release.services)) {
|
|
16600
|
+
const before = env.get(service.env);
|
|
16601
|
+
if (before !== service.image) {
|
|
16602
|
+
changes.push({ key: service.env, before, after: service.image, service: serviceName });
|
|
16603
|
+
}
|
|
16604
|
+
}
|
|
16605
|
+
return {
|
|
16606
|
+
manifest,
|
|
16607
|
+
release,
|
|
16608
|
+
targetVersion: version,
|
|
16609
|
+
changes,
|
|
16610
|
+
breakingChanges: release.breakingChanges ?? []
|
|
16611
|
+
};
|
|
16612
|
+
}
|
|
16613
|
+
function assertBreakingChangesAllowed(plan, allowedIds) {
|
|
16614
|
+
const required = plan.breakingChanges.filter((c) => c.requiresConfirmation !== false);
|
|
16615
|
+
const missing = required.filter((c) => !allowedIds.includes(c.id));
|
|
16616
|
+
if (missing.length > 0) {
|
|
16617
|
+
const details = missing.map((c) => `- ${c.id}: ${c.title}
|
|
16618
|
+
${c.description}
|
|
16619
|
+
Action: ${c.requiredAction ?? "Review release notes."}`).join("\n");
|
|
16620
|
+
throw new Error(
|
|
16621
|
+
`Stack ${plan.targetVersion} has breaking changes that require confirmation:
|
|
16622
|
+
${details}
|
|
16623
|
+
Re-run with --allow-breaking ${missing.map((c) => c.id).join(",")}`
|
|
16624
|
+
);
|
|
16625
|
+
}
|
|
16626
|
+
}
|
|
16627
|
+
async function runStackUpdate(options) {
|
|
16628
|
+
const exec2 = options.exec ?? defaultExec;
|
|
16629
|
+
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
16630
|
+
const manifest = await loadStackManifest(options.manifestRef, options.fetchText);
|
|
16631
|
+
const envRaw = readFileSync25(options.envFile, "utf8");
|
|
16632
|
+
const plan = createStackUpdatePlan(manifest, envRaw, options.targetVersion);
|
|
16633
|
+
assertBreakingChangesAllowed(plan, options.allowedBreakingChangeIds ?? []);
|
|
16634
|
+
const lockFile = options.lockFile ?? path37.join(path37.dirname(options.envFile), ".exe-stack-lock.json");
|
|
16635
|
+
if (options.dryRun || plan.changes.length === 0) {
|
|
16636
|
+
return { status: "planned", targetVersion: plan.targetVersion, changes: plan.changes, lockFile };
|
|
16637
|
+
}
|
|
16638
|
+
const backupDir = path37.join(path37.dirname(options.envFile), ".exe-stack-backups");
|
|
16639
|
+
mkdirSync19(backupDir, { recursive: true });
|
|
16640
|
+
const stamp = now().toISOString().replace(/[:.]/g, "-");
|
|
16641
|
+
const backupEnvFile = path37.join(backupDir, `env-${stamp}.bak`);
|
|
16642
|
+
writeFileSync20(backupEnvFile, envRaw, { mode: 384 });
|
|
16643
|
+
const updates = Object.fromEntries(plan.changes.map((c) => [c.key, c.after]));
|
|
16644
|
+
const patched = patchEnv(envRaw, updates);
|
|
16645
|
+
const tmp = `${options.envFile}.tmp-${process.pid}`;
|
|
16646
|
+
writeFileSync20(tmp, patched, { mode: 384 });
|
|
16647
|
+
renameSync6(tmp, options.envFile);
|
|
16648
|
+
const composeArgs = ["compose", "--file", options.composeFile, "--env-file", options.envFile];
|
|
16649
|
+
try {
|
|
16650
|
+
exec2("docker", [...composeArgs, "pull"]);
|
|
16651
|
+
exec2("docker", [...composeArgs, "up", "-d"]);
|
|
16652
|
+
await verifyReleaseHealth(plan.release, options.healthRetries ?? 12, options.healthDelayMs ?? 5e3);
|
|
16653
|
+
writeFileSync20(lockFile, JSON.stringify({ stackVersion: plan.targetVersion, updatedAt: now().toISOString(), services: plan.release.services }, null, 2) + "\n");
|
|
16654
|
+
return { status: "updated", targetVersion: plan.targetVersion, changes: plan.changes, backupEnvFile, lockFile };
|
|
16655
|
+
} catch (err) {
|
|
16656
|
+
writeFileSync20(options.envFile, envRaw, { mode: 384 });
|
|
16657
|
+
try {
|
|
16658
|
+
exec2("docker", [...composeArgs, "up", "-d"]);
|
|
16659
|
+
} catch {
|
|
16660
|
+
}
|
|
16661
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
16662
|
+
throw new Error(`Stack update failed and rollback was attempted: ${reason}`);
|
|
16663
|
+
}
|
|
16664
|
+
}
|
|
16665
|
+
async function verifyReleaseHealth(release, retries, delayMs) {
|
|
16666
|
+
for (const [serviceName, service] of Object.entries(release.services)) {
|
|
16667
|
+
if (!service.healthUrl) continue;
|
|
16668
|
+
await waitForHttpOk(service.healthUrl, retries, delayMs, serviceName);
|
|
16669
|
+
}
|
|
16670
|
+
}
|
|
16671
|
+
async function waitForHttpOk(url, retries, delayMs, label) {
|
|
16672
|
+
let last = "";
|
|
16673
|
+
for (let i = 0; i < retries; i++) {
|
|
16674
|
+
try {
|
|
16675
|
+
const status = await httpStatus(url);
|
|
16676
|
+
if (status >= 200 && status < 300) return;
|
|
16677
|
+
last = `HTTP ${status}`;
|
|
16678
|
+
} catch (err) {
|
|
16679
|
+
last = err instanceof Error ? err.message : String(err);
|
|
16680
|
+
}
|
|
16681
|
+
if (i < retries - 1) await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
16682
|
+
}
|
|
16683
|
+
throw new Error(`Health check failed for ${label} (${url}): ${last}`);
|
|
16684
|
+
}
|
|
16685
|
+
function httpStatus(urlString) {
|
|
16686
|
+
return new Promise((resolve, reject) => {
|
|
16687
|
+
const url = new URL(urlString);
|
|
16688
|
+
const mod = url.protocol === "https:" ? https : http;
|
|
16689
|
+
const req = mod.request(url, { method: "GET", timeout: 5e3 }, (res) => {
|
|
16690
|
+
res.resume();
|
|
16691
|
+
resolve(res.statusCode ?? 0);
|
|
16692
|
+
});
|
|
16693
|
+
req.on("timeout", () => req.destroy(new Error("timeout")));
|
|
16694
|
+
req.on("error", reject);
|
|
16695
|
+
req.end();
|
|
16696
|
+
});
|
|
16697
|
+
}
|
|
16698
|
+
function defaultExec(cmd, args2, opts) {
|
|
16699
|
+
execFileSync3(cmd, args2, { stdio: "inherit", cwd: opts?.cwd });
|
|
16700
|
+
}
|
|
16701
|
+
async function defaultFetchText(ref) {
|
|
16702
|
+
const res = await fetch(ref);
|
|
16703
|
+
if (!res.ok) throw new Error(`Failed to fetch ${ref}: HTTP ${res.status}`);
|
|
16704
|
+
return res.text();
|
|
16705
|
+
}
|
|
16706
|
+
function defaultStackPaths() {
|
|
16707
|
+
const cwdCompose = path37.resolve("docker-compose.yml");
|
|
16708
|
+
const cwdEnv = path37.resolve(".env");
|
|
16709
|
+
return {
|
|
16710
|
+
composeFile: process.env.EXE_STACK_COMPOSE_FILE || (existsSync30(cwdCompose) ? cwdCompose : "/opt/exe-stack/docker-compose.yml"),
|
|
16711
|
+
envFile: process.env.EXE_STACK_ENV_FILE || (existsSync30(cwdEnv) ? cwdEnv : "/opt/exe-stack/.env"),
|
|
16712
|
+
manifestRef: process.env.EXE_STACK_MANIFEST || "https://updates.askexe.com/stack-manifest.json"
|
|
16713
|
+
};
|
|
16714
|
+
}
|
|
16715
|
+
var init_stack_update = __esm({
|
|
16716
|
+
"src/lib/stack-update.ts"() {
|
|
16717
|
+
"use strict";
|
|
16718
|
+
}
|
|
16719
|
+
});
|
|
16720
|
+
|
|
16721
|
+
// src/bin/stack-update.ts
|
|
16722
|
+
var stack_update_exports = {};
|
|
16723
|
+
__export(stack_update_exports, {
|
|
16724
|
+
runStackUpdateCli: () => main3
|
|
16725
|
+
});
|
|
16726
|
+
import { readFileSync as readFileSync26 } from "fs";
|
|
16727
|
+
function parseArgs4(args2) {
|
|
16728
|
+
const defaults = defaultStackPaths();
|
|
16729
|
+
const opts = {
|
|
16730
|
+
manifestRef: defaults.manifestRef,
|
|
16731
|
+
composeFile: defaults.composeFile,
|
|
16732
|
+
envFile: defaults.envFile,
|
|
16733
|
+
dryRun: false,
|
|
16734
|
+
check: false,
|
|
16735
|
+
yes: false,
|
|
16736
|
+
allowedBreakingChangeIds: []
|
|
16737
|
+
};
|
|
16738
|
+
for (let i = 0; i < args2.length; i++) {
|
|
16739
|
+
const arg = args2[i];
|
|
16740
|
+
const next = () => args2[++i] ?? "";
|
|
16741
|
+
if (arg === "--manifest") opts.manifestRef = next();
|
|
16742
|
+
else if (arg.startsWith("--manifest=")) opts.manifestRef = arg.split("=").slice(1).join("=");
|
|
16743
|
+
else if (arg === "--target") opts.targetVersion = next();
|
|
16744
|
+
else if (arg.startsWith("--target=")) opts.targetVersion = arg.split("=")[1];
|
|
16745
|
+
else if (arg === "--compose-file") opts.composeFile = next();
|
|
16746
|
+
else if (arg.startsWith("--compose-file=")) opts.composeFile = arg.split("=").slice(1).join("=");
|
|
16747
|
+
else if (arg === "--env-file") opts.envFile = next();
|
|
16748
|
+
else if (arg.startsWith("--env-file=")) opts.envFile = arg.split("=").slice(1).join("=");
|
|
16749
|
+
else if (arg === "--lock-file") opts.lockFile = next();
|
|
16750
|
+
else if (arg === "--dry-run") opts.dryRun = true;
|
|
16751
|
+
else if (arg === "--check") opts.check = true;
|
|
16752
|
+
else if (arg === "--yes" || arg === "-y") opts.yes = true;
|
|
16753
|
+
else if (arg === "--allow-breaking") opts.allowedBreakingChangeIds.push(...next().split(",").map((s) => s.trim()).filter(Boolean));
|
|
16754
|
+
else if (arg.startsWith("--allow-breaking=")) opts.allowedBreakingChangeIds.push(...arg.split("=")[1].split(",").map((s) => s.trim()).filter(Boolean));
|
|
16755
|
+
else if (arg === "--help" || arg === "-h") {
|
|
16756
|
+
printHelp();
|
|
16757
|
+
process.exit(0);
|
|
16758
|
+
} else {
|
|
16759
|
+
throw new Error(`Unknown option: ${arg}`);
|
|
16760
|
+
}
|
|
16761
|
+
}
|
|
16762
|
+
return opts;
|
|
16763
|
+
}
|
|
16764
|
+
function printHelp() {
|
|
16765
|
+
console.log(`exe-os stack-update \u2014 update a self-hosted Exe OS stack from a pinned manifest
|
|
16766
|
+
|
|
16767
|
+
Usage:
|
|
16768
|
+
exe-os stack-update [--manifest <path-or-url>] [--target <version>] [--yes]
|
|
16769
|
+
|
|
16770
|
+
Options:
|
|
16771
|
+
--manifest <ref> Stack manifest JSON path or URL (default: updates.askexe.com)
|
|
16772
|
+
--target <version> Stack version to install (default: manifest.latest)
|
|
16773
|
+
--compose-file <path> docker-compose.yml path (default: ./docker-compose.yml or /opt/exe-stack/docker-compose.yml)
|
|
16774
|
+
--env-file <path> .env path (default: ./.env or /opt/exe-stack/.env)
|
|
16775
|
+
--lock-file <path> Lock file path (default: beside .env)
|
|
16776
|
+
--check Print available changes only
|
|
16777
|
+
--dry-run Plan only; do not run Docker
|
|
16778
|
+
--allow-breaking <ids> Confirm breaking changes, comma-separated
|
|
16779
|
+
-y, --yes Non-interactive confirmation
|
|
16780
|
+
`);
|
|
16781
|
+
}
|
|
16782
|
+
function printChanges(changes) {
|
|
16783
|
+
if (changes.length === 0) {
|
|
16784
|
+
console.log("\u2705 Stack already matches target manifest.");
|
|
16785
|
+
return;
|
|
16786
|
+
}
|
|
16787
|
+
console.log("Planned image tag changes:");
|
|
16788
|
+
for (const c of changes) {
|
|
16789
|
+
console.log(` - ${c.service}: ${c.key}`);
|
|
16790
|
+
console.log(` ${c.before ?? "<unset>"} \u2192 ${c.after}`);
|
|
16791
|
+
}
|
|
16792
|
+
}
|
|
16793
|
+
function printBreaking(changes) {
|
|
16794
|
+
if (changes.length === 0) return;
|
|
16795
|
+
console.log("\nBreaking-change notices:");
|
|
16796
|
+
for (const c of changes) {
|
|
16797
|
+
console.log(` - ${c.id}: ${c.title}`);
|
|
16798
|
+
console.log(` ${c.description}`);
|
|
16799
|
+
if (c.requiredAction) console.log(` Action: ${c.requiredAction}`);
|
|
16800
|
+
if (c.expectedDowntimeMinutes) console.log(` Expected downtime: ${c.expectedDowntimeMinutes} minutes`);
|
|
16801
|
+
}
|
|
16802
|
+
}
|
|
16803
|
+
async function main3() {
|
|
16804
|
+
const opts = parseArgs4(process.argv.slice(2));
|
|
16805
|
+
const manifest = await loadStackManifest(opts.manifestRef);
|
|
16806
|
+
const envRaw = readFileSync26(opts.envFile, "utf8");
|
|
16807
|
+
const plan = createStackUpdatePlan(manifest, envRaw, opts.targetVersion);
|
|
16808
|
+
console.log(`Exe OS stack target: ${plan.targetVersion}`);
|
|
16809
|
+
console.log(`Manifest: ${opts.manifestRef}`);
|
|
16810
|
+
console.log(`Compose: ${opts.composeFile}`);
|
|
16811
|
+
console.log(`Env: ${opts.envFile}
|
|
16812
|
+
`);
|
|
16813
|
+
printChanges(plan.changes);
|
|
16814
|
+
printBreaking(plan.breakingChanges);
|
|
16815
|
+
if (opts.check || opts.dryRun) return;
|
|
16816
|
+
if (!opts.yes) {
|
|
16817
|
+
console.error("\nRefusing to update without --yes. Re-run with --yes after reviewing the plan.");
|
|
16818
|
+
process.exit(2);
|
|
16819
|
+
}
|
|
16820
|
+
const result = await runStackUpdate(opts);
|
|
16821
|
+
console.log(`
|
|
16822
|
+
\u2705 Stack ${result.status}: ${result.targetVersion}`);
|
|
16823
|
+
if (result.backupEnvFile) console.log(`Backup env: ${result.backupEnvFile}`);
|
|
16824
|
+
console.log(`Lock file: ${result.lockFile}`);
|
|
16825
|
+
}
|
|
16826
|
+
var init_stack_update2 = __esm({
|
|
16827
|
+
"src/bin/stack-update.ts"() {
|
|
16828
|
+
"use strict";
|
|
16829
|
+
init_is_main();
|
|
16830
|
+
init_stack_update();
|
|
16831
|
+
if (isMainModule(import.meta.url)) {
|
|
16832
|
+
main3().catch((err) => {
|
|
16833
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
16834
|
+
process.exit(1);
|
|
16835
|
+
});
|
|
16836
|
+
}
|
|
16837
|
+
}
|
|
16838
|
+
});
|
|
16839
|
+
|
|
16465
16840
|
// node_modules/es-toolkit/dist/function/debounce.mjs
|
|
16466
16841
|
function debounce(func, debounceMs, { signal, edges } = {}) {
|
|
16467
16842
|
let pendingThis = void 0;
|
|
@@ -18263,7 +18638,7 @@ var init_src = __esm({
|
|
|
18263
18638
|
|
|
18264
18639
|
// node_modules/terminal-size/index.js
|
|
18265
18640
|
import process2 from "process";
|
|
18266
|
-
import { execFileSync as
|
|
18641
|
+
import { execFileSync as execFileSync4 } from "child_process";
|
|
18267
18642
|
import fs from "fs";
|
|
18268
18643
|
import tty from "tty";
|
|
18269
18644
|
function terminalSize() {
|
|
@@ -18295,7 +18670,7 @@ var init_terminal_size = __esm({
|
|
|
18295
18670
|
"use strict";
|
|
18296
18671
|
defaultColumns = 80;
|
|
18297
18672
|
defaultRows = 24;
|
|
18298
|
-
exec = (command, arguments_, { shell, env } = {}) =>
|
|
18673
|
+
exec = (command, arguments_, { shell, env } = {}) => execFileSync4(command, arguments_, {
|
|
18299
18674
|
encoding: "utf8",
|
|
18300
18675
|
stdio: ["ignore", "pipe", "ignore"],
|
|
18301
18676
|
timeout: 500,
|
|
@@ -20820,8 +21195,8 @@ var init_ErrorOverview = __esm({
|
|
|
20820
21195
|
"use strict";
|
|
20821
21196
|
init_Box();
|
|
20822
21197
|
init_Text();
|
|
20823
|
-
cleanupPath = (
|
|
20824
|
-
return
|
|
21198
|
+
cleanupPath = (path49) => {
|
|
21199
|
+
return path49?.replace(`file://${cwd()}/`, "");
|
|
20825
21200
|
};
|
|
20826
21201
|
stackUtils = new StackUtils({
|
|
20827
21202
|
cwd: cwd(),
|
|
@@ -23229,11 +23604,11 @@ function Footer() {
|
|
|
23229
23604
|
} catch {
|
|
23230
23605
|
}
|
|
23231
23606
|
try {
|
|
23232
|
-
const { existsSync:
|
|
23607
|
+
const { existsSync: existsSync35 } = await import("fs");
|
|
23233
23608
|
const { join } = await import("path");
|
|
23234
23609
|
const home = process.env.HOME ?? "";
|
|
23235
23610
|
const pidPath = join(home, ".exe-os", "exed.pid");
|
|
23236
|
-
setDaemon(
|
|
23611
|
+
setDaemon(existsSync35(pidPath) ? "running" : "stopped");
|
|
23237
23612
|
} catch {
|
|
23238
23613
|
setDaemon("unknown");
|
|
23239
23614
|
}
|
|
@@ -25284,10 +25659,10 @@ var init_hooks = __esm({
|
|
|
25284
25659
|
});
|
|
25285
25660
|
|
|
25286
25661
|
// src/runtime/safety-checks.ts
|
|
25287
|
-
import
|
|
25662
|
+
import path38 from "path";
|
|
25288
25663
|
import os18 from "os";
|
|
25289
25664
|
function checkPathSafety(filePath) {
|
|
25290
|
-
const resolved =
|
|
25665
|
+
const resolved = path38.resolve(filePath);
|
|
25291
25666
|
for (const { pattern, reason } of BYPASS_IMMUNE_PATTERNS) {
|
|
25292
25667
|
const matches = typeof pattern === "function" ? pattern(resolved) : pattern.test(resolved);
|
|
25293
25668
|
if (matches) {
|
|
@@ -25297,7 +25672,7 @@ function checkPathSafety(filePath) {
|
|
|
25297
25672
|
return { safe: true, bypassImmune: true };
|
|
25298
25673
|
}
|
|
25299
25674
|
function checkReadPathSafety(filePath) {
|
|
25300
|
-
const resolved =
|
|
25675
|
+
const resolved = path38.resolve(filePath);
|
|
25301
25676
|
const credPatterns = BYPASS_IMMUNE_PATTERNS.filter(
|
|
25302
25677
|
(p) => typeof p.pattern !== "function" && (p.reason.includes("secrets") || p.reason.includes("Private key") || p.reason.includes("Credential"))
|
|
25303
25678
|
);
|
|
@@ -25323,11 +25698,11 @@ var init_safety_checks = __esm({
|
|
|
25323
25698
|
reason: "Git config can set hooks and command execution"
|
|
25324
25699
|
},
|
|
25325
25700
|
{
|
|
25326
|
-
pattern: (p) => p.startsWith(
|
|
25701
|
+
pattern: (p) => p.startsWith(path38.join(HOME, ".claude")),
|
|
25327
25702
|
reason: "Claude configuration files are protected"
|
|
25328
25703
|
},
|
|
25329
25704
|
{
|
|
25330
|
-
pattern: (p) => p.startsWith(
|
|
25705
|
+
pattern: (p) => p.startsWith(path38.join(HOME, ".exe-os")),
|
|
25331
25706
|
reason: "exe-os configuration files are protected"
|
|
25332
25707
|
},
|
|
25333
25708
|
{
|
|
@@ -25344,7 +25719,7 @@ var init_safety_checks = __esm({
|
|
|
25344
25719
|
},
|
|
25345
25720
|
{
|
|
25346
25721
|
pattern: (p) => {
|
|
25347
|
-
const name =
|
|
25722
|
+
const name = path38.basename(p);
|
|
25348
25723
|
return [".bashrc", ".zshrc", ".profile", ".bash_profile", ".zprofile", ".zshenv"].includes(name);
|
|
25349
25724
|
},
|
|
25350
25725
|
reason: "Shell configuration files can execute arbitrary code on login"
|
|
@@ -25371,7 +25746,7 @@ __export(file_read_exports, {
|
|
|
25371
25746
|
FileReadTool: () => FileReadTool
|
|
25372
25747
|
});
|
|
25373
25748
|
import fs3 from "fs/promises";
|
|
25374
|
-
import
|
|
25749
|
+
import path39 from "path";
|
|
25375
25750
|
import { z } from "zod";
|
|
25376
25751
|
function isBinary(buf) {
|
|
25377
25752
|
for (let i = 0; i < buf.length; i++) {
|
|
@@ -25407,7 +25782,7 @@ var init_file_read = __esm({
|
|
|
25407
25782
|
return { behavior: "allow" };
|
|
25408
25783
|
},
|
|
25409
25784
|
async call(input, context) {
|
|
25410
|
-
const filePath =
|
|
25785
|
+
const filePath = path39.isAbsolute(input.file_path) ? input.file_path : path39.resolve(context.cwd, input.file_path);
|
|
25411
25786
|
let stat2;
|
|
25412
25787
|
try {
|
|
25413
25788
|
stat2 = await fs3.stat(filePath);
|
|
@@ -25447,7 +25822,7 @@ __export(glob_exports, {
|
|
|
25447
25822
|
GlobTool: () => GlobTool
|
|
25448
25823
|
});
|
|
25449
25824
|
import fs4 from "fs/promises";
|
|
25450
|
-
import
|
|
25825
|
+
import path40 from "path";
|
|
25451
25826
|
import { z as z2 } from "zod";
|
|
25452
25827
|
async function walkDir(dir, maxDepth = 10) {
|
|
25453
25828
|
const results = [];
|
|
@@ -25463,7 +25838,7 @@ async function walkDir(dir, maxDepth = 10) {
|
|
|
25463
25838
|
if (entry.isDirectory() && (entry.name === "node_modules" || entry.name === ".git")) {
|
|
25464
25839
|
continue;
|
|
25465
25840
|
}
|
|
25466
|
-
const fullPath =
|
|
25841
|
+
const fullPath = path40.join(current, entry.name);
|
|
25467
25842
|
if (entry.isDirectory()) {
|
|
25468
25843
|
await walk(fullPath, depth + 1);
|
|
25469
25844
|
} else {
|
|
@@ -25497,11 +25872,11 @@ var init_glob = __esm({
|
|
|
25497
25872
|
inputSchema: inputSchema2,
|
|
25498
25873
|
isReadOnly: true,
|
|
25499
25874
|
async call(input, context) {
|
|
25500
|
-
const baseDir = input.path ?
|
|
25875
|
+
const baseDir = input.path ? path40.isAbsolute(input.path) ? input.path : path40.resolve(context.cwd, input.path) : context.cwd;
|
|
25501
25876
|
try {
|
|
25502
25877
|
const entries = await walkDir(baseDir);
|
|
25503
25878
|
const matched = entries.filter(
|
|
25504
|
-
(e) => simpleGlobMatch(
|
|
25879
|
+
(e) => simpleGlobMatch(path40.relative(baseDir, e.path), input.pattern)
|
|
25505
25880
|
);
|
|
25506
25881
|
matched.sort((a, b) => b.mtime - a.mtime);
|
|
25507
25882
|
if (matched.length === 0) {
|
|
@@ -25527,7 +25902,7 @@ __export(grep_exports, {
|
|
|
25527
25902
|
});
|
|
25528
25903
|
import { spawn as spawn2 } from "child_process";
|
|
25529
25904
|
import fs5 from "fs/promises";
|
|
25530
|
-
import
|
|
25905
|
+
import path41 from "path";
|
|
25531
25906
|
import { z as z3 } from "zod";
|
|
25532
25907
|
function runRipgrep(input, searchPath, context) {
|
|
25533
25908
|
return new Promise((resolve, reject) => {
|
|
@@ -25581,7 +25956,7 @@ async function nodeGrep(input, searchPath) {
|
|
|
25581
25956
|
}
|
|
25582
25957
|
for (const entry of entries) {
|
|
25583
25958
|
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
25584
|
-
const fullPath =
|
|
25959
|
+
const fullPath = path41.join(dir, entry.name);
|
|
25585
25960
|
if (entry.isDirectory()) {
|
|
25586
25961
|
await walk(fullPath);
|
|
25587
25962
|
} else {
|
|
@@ -25627,7 +26002,7 @@ var init_grep = __esm({
|
|
|
25627
26002
|
inputSchema: inputSchema3,
|
|
25628
26003
|
isReadOnly: true,
|
|
25629
26004
|
async call(input, context) {
|
|
25630
|
-
const searchPath = input.path ?
|
|
26005
|
+
const searchPath = input.path ? path41.isAbsolute(input.path) ? input.path : path41.resolve(context.cwd, input.path) : context.cwd;
|
|
25631
26006
|
try {
|
|
25632
26007
|
const result = await runRipgrep(input, searchPath, context);
|
|
25633
26008
|
return result;
|
|
@@ -25652,7 +26027,7 @@ __export(file_write_exports, {
|
|
|
25652
26027
|
FileWriteTool: () => FileWriteTool
|
|
25653
26028
|
});
|
|
25654
26029
|
import fs6 from "fs/promises";
|
|
25655
|
-
import
|
|
26030
|
+
import path42 from "path";
|
|
25656
26031
|
import { z as z4 } from "zod";
|
|
25657
26032
|
var inputSchema4, FileWriteTool;
|
|
25658
26033
|
var init_file_write = __esm({
|
|
@@ -25680,8 +26055,8 @@ var init_file_write = __esm({
|
|
|
25680
26055
|
return { behavior: "allow" };
|
|
25681
26056
|
},
|
|
25682
26057
|
async call(input, context) {
|
|
25683
|
-
const filePath =
|
|
25684
|
-
const dir =
|
|
26058
|
+
const filePath = path42.isAbsolute(input.file_path) ? input.file_path : path42.resolve(context.cwd, input.file_path);
|
|
26059
|
+
const dir = path42.dirname(filePath);
|
|
25685
26060
|
await fs6.mkdir(dir, { recursive: true });
|
|
25686
26061
|
await fs6.writeFile(filePath, input.content, "utf-8");
|
|
25687
26062
|
return {
|
|
@@ -25699,7 +26074,7 @@ __export(file_edit_exports, {
|
|
|
25699
26074
|
FileEditTool: () => FileEditTool
|
|
25700
26075
|
});
|
|
25701
26076
|
import fs7 from "fs/promises";
|
|
25702
|
-
import
|
|
26077
|
+
import path43 from "path";
|
|
25703
26078
|
import { z as z5 } from "zod";
|
|
25704
26079
|
function countOccurrences(haystack, needle) {
|
|
25705
26080
|
let count = 0;
|
|
@@ -25740,7 +26115,7 @@ var init_file_edit = __esm({
|
|
|
25740
26115
|
return { behavior: "allow" };
|
|
25741
26116
|
},
|
|
25742
26117
|
async call(input, context) {
|
|
25743
|
-
const filePath =
|
|
26118
|
+
const filePath = path43.isAbsolute(input.file_path) ? input.file_path : path43.resolve(context.cwd, input.file_path);
|
|
25744
26119
|
let content;
|
|
25745
26120
|
try {
|
|
25746
26121
|
content = await fs7.readFile(filePath, "utf-8");
|
|
@@ -25982,7 +26357,7 @@ var init_bash = __esm({
|
|
|
25982
26357
|
// src/tui/views/CommandCenter.tsx
|
|
25983
26358
|
import { useState as useState6, useEffect as useEffect8, useMemo as useMemo4, useCallback as useCallback4, useRef as useRef4 } from "react";
|
|
25984
26359
|
import TextInput from "ink-text-input";
|
|
25985
|
-
import
|
|
26360
|
+
import path44 from "path";
|
|
25986
26361
|
import { homedir as homedir6 } from "os";
|
|
25987
26362
|
import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
25988
26363
|
function CommandCenterView({
|
|
@@ -26017,15 +26392,15 @@ function CommandCenterView({
|
|
|
26017
26392
|
const { createPermissionsFromPreset: createPermissionsFromPreset2, EMPLOYEE_PERMISSIONS: EMPLOYEE_PERMISSIONS2 } = await Promise.resolve().then(() => (init_permissions(), permissions_exports));
|
|
26018
26393
|
const { getPresetByRole: getPresetByRole2 } = await Promise.resolve().then(() => (init_permission_presets(), permission_presets_exports));
|
|
26019
26394
|
const { createDefaultHooks: createDefaultHooks2 } = await Promise.resolve().then(() => (init_hooks(), hooks_exports));
|
|
26020
|
-
const { readFileSync:
|
|
26395
|
+
const { readFileSync: readFileSync30, existsSync: existsSync35 } = await import("fs");
|
|
26021
26396
|
const { join } = await import("path");
|
|
26022
26397
|
const { homedir: homedir8 } = await import("os");
|
|
26023
26398
|
const configPath = join(homedir8(), ".exe-os", "config.json");
|
|
26024
26399
|
let failoverChain = ["anthropic", "opencode", "gemini", "openai"];
|
|
26025
26400
|
let providerConfigs = {};
|
|
26026
|
-
if (
|
|
26401
|
+
if (existsSync35(configPath)) {
|
|
26027
26402
|
try {
|
|
26028
|
-
const raw = JSON.parse(
|
|
26403
|
+
const raw = JSON.parse(readFileSync30(configPath, "utf8"));
|
|
26029
26404
|
if (Array.isArray(raw.failoverChain)) failoverChain = raw.failoverChain;
|
|
26030
26405
|
if (raw.providers && typeof raw.providers === "object") {
|
|
26031
26406
|
providerConfigs = raw.providers;
|
|
@@ -26086,7 +26461,7 @@ function CommandCenterView({
|
|
|
26086
26461
|
const markerDir = join(homedir8(), ".exe-os", "session-cache");
|
|
26087
26462
|
const agentFiles = (await import("fs")).readdirSync(markerDir).filter((f) => f.startsWith("active-agent-"));
|
|
26088
26463
|
for (const f of agentFiles) {
|
|
26089
|
-
const data = JSON.parse(
|
|
26464
|
+
const data = JSON.parse(readFileSync30(join(markerDir, f), "utf8"));
|
|
26090
26465
|
if (data.agentRole) {
|
|
26091
26466
|
agentRole = data.agentRole;
|
|
26092
26467
|
break;
|
|
@@ -26231,7 +26606,7 @@ function CommandCenterView({
|
|
|
26231
26606
|
const demoEntries = DEMO_PROJECTS.map((p) => ({
|
|
26232
26607
|
projectName: p.projectName,
|
|
26233
26608
|
exeSession: p.exeSession,
|
|
26234
|
-
projectDir:
|
|
26609
|
+
projectDir: path44.join(homedir6(), p.projectName),
|
|
26235
26610
|
employeeCount: p.employees.length,
|
|
26236
26611
|
activeCount: p.employees.filter((e) => e.status === "active").length,
|
|
26237
26612
|
memoryCount: p.employees.length * 4e3,
|
|
@@ -26269,7 +26644,7 @@ function CommandCenterView({
|
|
|
26269
26644
|
const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session_registry(), session_registry_exports));
|
|
26270
26645
|
const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
|
|
26271
26646
|
const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
26272
|
-
const { existsSync:
|
|
26647
|
+
const { existsSync: existsSync35 } = await import("fs");
|
|
26273
26648
|
const { join } = await import("path");
|
|
26274
26649
|
const client = getClient2();
|
|
26275
26650
|
if (!client) {
|
|
@@ -26340,7 +26715,7 @@ function CommandCenterView({
|
|
|
26340
26715
|
}
|
|
26341
26716
|
const memoryCount = memoryCounts.get(name) ?? 0;
|
|
26342
26717
|
const openTaskCount = openTaskCounts.get(name) ?? 0;
|
|
26343
|
-
const hasGit = projectDir ?
|
|
26718
|
+
const hasGit = projectDir ? existsSync35(join(projectDir, ".git")) : false;
|
|
26344
26719
|
const type = hasGit ? "code" : memoryCount > 0 ? "code" : "automation";
|
|
26345
26720
|
projectList.push({
|
|
26346
26721
|
projectName: name,
|
|
@@ -26365,7 +26740,7 @@ function CommandCenterView({
|
|
|
26365
26740
|
setHealth((h) => ({ ...h, memories: Number(totalResult.rows[0]?.cnt ?? 0) }));
|
|
26366
26741
|
try {
|
|
26367
26742
|
const pidPath = join(process.env.HOME ?? "", ".exe-os", "exed.pid");
|
|
26368
|
-
setHealth((h) => ({ ...h, daemon:
|
|
26743
|
+
setHealth((h) => ({ ...h, daemon: existsSync35(pidPath) ? "running" : "stopped" }));
|
|
26369
26744
|
} catch {
|
|
26370
26745
|
}
|
|
26371
26746
|
const activityResult = await client.execute(
|
|
@@ -27235,7 +27610,7 @@ var init_useOrchestrator = __esm({
|
|
|
27235
27610
|
|
|
27236
27611
|
// src/tui/views/Sessions.tsx
|
|
27237
27612
|
import React19, { useState as useState9, useEffect as useEffect11, useCallback as useCallback6 } from "react";
|
|
27238
|
-
import
|
|
27613
|
+
import path45 from "path";
|
|
27239
27614
|
import { homedir as homedir7 } from "os";
|
|
27240
27615
|
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
27241
27616
|
function isCoordinatorEntry(entry) {
|
|
@@ -27273,7 +27648,7 @@ function SessionsView({
|
|
|
27273
27648
|
if (demo) {
|
|
27274
27649
|
setProjects(DEMO_PROJECTS.map((p) => ({
|
|
27275
27650
|
...p,
|
|
27276
|
-
projectDir:
|
|
27651
|
+
projectDir: path45.join(homedir7(), p.projectName),
|
|
27277
27652
|
employees: p.employees.map((e) => ({ ...e, attached: e.status === "active" }))
|
|
27278
27653
|
})));
|
|
27279
27654
|
return;
|
|
@@ -28488,12 +28863,12 @@ async function loadGatewayConfig() {
|
|
|
28488
28863
|
state.running = false;
|
|
28489
28864
|
}
|
|
28490
28865
|
try {
|
|
28491
|
-
const { existsSync:
|
|
28866
|
+
const { existsSync: existsSync35, readFileSync: readFileSync30 } = await import("fs");
|
|
28492
28867
|
const { join } = await import("path");
|
|
28493
28868
|
const home = process.env.HOME ?? "";
|
|
28494
28869
|
const configPath = join(home, ".exe-os", "gateway.json");
|
|
28495
|
-
if (
|
|
28496
|
-
const raw = JSON.parse(
|
|
28870
|
+
if (existsSync35(configPath)) {
|
|
28871
|
+
const raw = JSON.parse(readFileSync30(configPath, "utf8"));
|
|
28497
28872
|
state.port = raw.port ?? 3100;
|
|
28498
28873
|
state.gatewayUrl = raw.gatewayUrl ?? "";
|
|
28499
28874
|
if (raw.adapters) {
|
|
@@ -29091,12 +29466,12 @@ function TeamView({ onBack, onViewSessions }) {
|
|
|
29091
29466
|
setMembers(teamData);
|
|
29092
29467
|
setDbError(null);
|
|
29093
29468
|
try {
|
|
29094
|
-
const { existsSync:
|
|
29469
|
+
const { existsSync: existsSync35, readFileSync: readFileSync30 } = await import("fs");
|
|
29095
29470
|
const { join } = await import("path");
|
|
29096
29471
|
const home = process.env.HOME ?? "";
|
|
29097
29472
|
const gatewayConfig = join(home, ".exe-os", "gateway.json");
|
|
29098
|
-
if (
|
|
29099
|
-
const raw = JSON.parse(
|
|
29473
|
+
if (existsSync35(gatewayConfig)) {
|
|
29474
|
+
const raw = JSON.parse(readFileSync30(gatewayConfig, "utf8"));
|
|
29100
29475
|
if (raw.agents && raw.agents.length > 0) {
|
|
29101
29476
|
setExternals(raw.agents.map((a) => ({
|
|
29102
29477
|
name: a.name,
|
|
@@ -29276,8 +29651,8 @@ __export(wiki_client_exports, {
|
|
|
29276
29651
|
listDocuments: () => listDocuments,
|
|
29277
29652
|
listWorkspaces: () => listWorkspaces
|
|
29278
29653
|
});
|
|
29279
|
-
async function wikiFetch(config,
|
|
29280
|
-
const url = `${config.baseUrl}/api/v1${
|
|
29654
|
+
async function wikiFetch(config, path49, method = "GET", body) {
|
|
29655
|
+
const url = `${config.baseUrl}/api/v1${path49}`;
|
|
29281
29656
|
const headers = {
|
|
29282
29657
|
Authorization: `Bearer ${config.apiKey}`,
|
|
29283
29658
|
"Content-Type": "application/json"
|
|
@@ -29310,7 +29685,7 @@ async function wikiFetch(config, path48, method = "GET", body) {
|
|
|
29310
29685
|
}
|
|
29311
29686
|
}
|
|
29312
29687
|
if (!response.ok) {
|
|
29313
|
-
throw new Error(`Wiki API ${method} ${
|
|
29688
|
+
throw new Error(`Wiki API ${method} ${path49}: ${response.status} ${response.statusText}`);
|
|
29314
29689
|
}
|
|
29315
29690
|
return response.json();
|
|
29316
29691
|
} finally {
|
|
@@ -29904,12 +30279,12 @@ function SettingsView({ onBack }) {
|
|
|
29904
30279
|
}
|
|
29905
30280
|
setProviders(providerList);
|
|
29906
30281
|
try {
|
|
29907
|
-
const { existsSync:
|
|
30282
|
+
const { existsSync: existsSync35 } = await import("fs");
|
|
29908
30283
|
const { join } = await import("path");
|
|
29909
30284
|
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
29910
30285
|
const cfg = await loadConfig2();
|
|
29911
30286
|
const home = process.env.HOME ?? "";
|
|
29912
|
-
const hasKey =
|
|
30287
|
+
const hasKey = existsSync35(join(home, ".exe-os", "master.key"));
|
|
29913
30288
|
if (cfg.cloud) {
|
|
29914
30289
|
setCloud({
|
|
29915
30290
|
configured: true,
|
|
@@ -29922,22 +30297,22 @@ function SettingsView({ onBack }) {
|
|
|
29922
30297
|
const pidPath = join(home, ".exe-os", "exed.pid");
|
|
29923
30298
|
let daemon = "unknown";
|
|
29924
30299
|
try {
|
|
29925
|
-
daemon =
|
|
30300
|
+
daemon = existsSync35(pidPath) ? "running" : "stopped";
|
|
29926
30301
|
} catch {
|
|
29927
30302
|
}
|
|
29928
30303
|
let version = "unknown";
|
|
29929
30304
|
try {
|
|
29930
|
-
const { readFileSync:
|
|
30305
|
+
const { readFileSync: readFileSync30 } = await import("fs");
|
|
29931
30306
|
const { createRequire: createRequire3 } = await import("module");
|
|
29932
30307
|
const require2 = createRequire3(import.meta.url);
|
|
29933
30308
|
const pkgPath = require2.resolve("@askexenow/exe-os/package.json");
|
|
29934
|
-
const pkg = JSON.parse(
|
|
30309
|
+
const pkg = JSON.parse(readFileSync30(pkgPath, "utf8"));
|
|
29935
30310
|
version = pkg.version;
|
|
29936
30311
|
} catch {
|
|
29937
30312
|
try {
|
|
29938
|
-
const { readFileSync:
|
|
30313
|
+
const { readFileSync: readFileSync30 } = await import("fs");
|
|
29939
30314
|
const { join: joinPath } = await import("path");
|
|
29940
|
-
const pkg = JSON.parse(
|
|
30315
|
+
const pkg = JSON.parse(readFileSync30(joinPath(process.cwd(), "package.json"), "utf8"));
|
|
29941
30316
|
version = pkg.version;
|
|
29942
30317
|
} catch {
|
|
29943
30318
|
}
|
|
@@ -30738,15 +31113,15 @@ __export(installer_exports2, {
|
|
|
30738
31113
|
verifyOpenCodeHooks: () => verifyOpenCodeHooks
|
|
30739
31114
|
});
|
|
30740
31115
|
import { readFile as readFile7, writeFile as writeFile8, mkdir as mkdir8 } from "fs/promises";
|
|
30741
|
-
import { existsSync as
|
|
30742
|
-
import
|
|
31116
|
+
import { existsSync as existsSync32, readFileSync as readFileSync28 } from "fs";
|
|
31117
|
+
import path46 from "path";
|
|
30743
31118
|
import os19 from "os";
|
|
30744
31119
|
async function registerOpenCodeMcp(packageRoot, homeDir = os19.homedir()) {
|
|
30745
|
-
const configDir =
|
|
30746
|
-
const configPath =
|
|
31120
|
+
const configDir = path46.join(homeDir, ".config", "opencode");
|
|
31121
|
+
const configPath = path46.join(configDir, "opencode.json");
|
|
30747
31122
|
await mkdir8(configDir, { recursive: true });
|
|
30748
31123
|
let config = {};
|
|
30749
|
-
if (
|
|
31124
|
+
if (existsSync32(configPath)) {
|
|
30750
31125
|
try {
|
|
30751
31126
|
config = JSON.parse(await readFile7(configPath, "utf-8"));
|
|
30752
31127
|
} catch {
|
|
@@ -30758,7 +31133,7 @@ async function registerOpenCodeMcp(packageRoot, homeDir = os19.homedir()) {
|
|
|
30758
31133
|
}
|
|
30759
31134
|
const newEntry = {
|
|
30760
31135
|
type: "local",
|
|
30761
|
-
command: ["node",
|
|
31136
|
+
command: ["node", path46.join(packageRoot, "dist", "mcp", "server.js")],
|
|
30762
31137
|
enabled: true
|
|
30763
31138
|
};
|
|
30764
31139
|
const current = config.mcp["exe-os"];
|
|
@@ -30773,14 +31148,14 @@ async function registerOpenCodeMcp(packageRoot, homeDir = os19.homedir()) {
|
|
|
30773
31148
|
return true;
|
|
30774
31149
|
}
|
|
30775
31150
|
async function installOpenCodePlugin(packageRoot, homeDir = os19.homedir()) {
|
|
30776
|
-
const pluginDir =
|
|
30777
|
-
const pluginPath =
|
|
31151
|
+
const pluginDir = path46.join(homeDir, ".config", "opencode", "plugins");
|
|
31152
|
+
const pluginPath = path46.join(pluginDir, "exe-os.mjs");
|
|
30778
31153
|
await mkdir8(pluginDir, { recursive: true });
|
|
30779
31154
|
const pluginContent = PLUGIN_TEMPLATE.replace(
|
|
30780
31155
|
/__PACKAGE_ROOT__/g,
|
|
30781
31156
|
packageRoot.replace(/\\/g, "\\\\")
|
|
30782
31157
|
);
|
|
30783
|
-
if (
|
|
31158
|
+
if (existsSync32(pluginPath)) {
|
|
30784
31159
|
const existing = await readFile7(pluginPath, "utf-8");
|
|
30785
31160
|
if (existing === pluginContent) {
|
|
30786
31161
|
return false;
|
|
@@ -30790,16 +31165,16 @@ async function installOpenCodePlugin(packageRoot, homeDir = os19.homedir()) {
|
|
|
30790
31165
|
return true;
|
|
30791
31166
|
}
|
|
30792
31167
|
function verifyOpenCodeHooks(homeDir = os19.homedir()) {
|
|
30793
|
-
const configPath =
|
|
30794
|
-
const pluginPath =
|
|
30795
|
-
if (!
|
|
31168
|
+
const configPath = path46.join(homeDir, ".config", "opencode", "opencode.json");
|
|
31169
|
+
const pluginPath = path46.join(homeDir, ".config", "opencode", "plugins", "exe-os.mjs");
|
|
31170
|
+
if (!existsSync32(configPath)) return false;
|
|
30796
31171
|
try {
|
|
30797
|
-
const config = JSON.parse(
|
|
31172
|
+
const config = JSON.parse(readFileSync28(configPath, "utf-8"));
|
|
30798
31173
|
if (!config.mcp?.["exe-os"]?.enabled) return false;
|
|
30799
31174
|
} catch {
|
|
30800
31175
|
return false;
|
|
30801
31176
|
}
|
|
30802
|
-
if (!
|
|
31177
|
+
if (!existsSync32(pluginPath)) return false;
|
|
30803
31178
|
return true;
|
|
30804
31179
|
}
|
|
30805
31180
|
async function runOpenCodeInstaller(homeDir) {
|
|
@@ -30834,19 +31209,19 @@ __export(installer_exports3, {
|
|
|
30834
31209
|
verifyCodexHooks: () => verifyCodexHooks
|
|
30835
31210
|
});
|
|
30836
31211
|
import { readFile as readFile8, writeFile as writeFile9, mkdir as mkdir9 } from "fs/promises";
|
|
30837
|
-
import { existsSync as
|
|
30838
|
-
import
|
|
31212
|
+
import { existsSync as existsSync33 } from "fs";
|
|
31213
|
+
import path47 from "path";
|
|
30839
31214
|
import os20 from "os";
|
|
30840
31215
|
async function mergeCodexHooks(packageRoot, homeDir = os20.homedir()) {
|
|
30841
|
-
const codexDir =
|
|
30842
|
-
const hooksPath =
|
|
30843
|
-
const logsDir =
|
|
30844
|
-
const hookLogPath =
|
|
31216
|
+
const codexDir = path47.join(homeDir, ".codex");
|
|
31217
|
+
const hooksPath = path47.join(codexDir, "hooks.json");
|
|
31218
|
+
const logsDir = path47.join(homeDir, ".exe-os", "logs");
|
|
31219
|
+
const hookLogPath = path47.join(logsDir, "hooks.log");
|
|
30845
31220
|
const logSuffix = ` 2>> "${hookLogPath}"`;
|
|
30846
31221
|
await mkdir9(codexDir, { recursive: true });
|
|
30847
31222
|
await mkdir9(logsDir, { recursive: true });
|
|
30848
31223
|
let hooksJson = {};
|
|
30849
|
-
if (
|
|
31224
|
+
if (existsSync33(hooksPath)) {
|
|
30850
31225
|
try {
|
|
30851
31226
|
hooksJson = JSON.parse(await readFile8(hooksPath, "utf-8"));
|
|
30852
31227
|
} catch {
|
|
@@ -30863,7 +31238,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os20.homedir()) {
|
|
|
30863
31238
|
hooks: [
|
|
30864
31239
|
{
|
|
30865
31240
|
type: "command",
|
|
30866
|
-
command: `node "${
|
|
31241
|
+
command: `node "${path47.join(packageRoot, "dist", "hooks", "session-start.js")}"${logSuffix}`,
|
|
30867
31242
|
timeout: 30
|
|
30868
31243
|
}
|
|
30869
31244
|
]
|
|
@@ -30879,7 +31254,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os20.homedir()) {
|
|
|
30879
31254
|
// Combined hook: runs ingest + error-recall in one Node process.
|
|
30880
31255
|
// Eliminates a cold-start cycle per tool call (~3-6s savings on Codex).
|
|
30881
31256
|
type: "command",
|
|
30882
|
-
command: `node "${
|
|
31257
|
+
command: `node "${path47.join(packageRoot, "dist", "hooks", "post-tool-combined.js")}"${logSuffix}`
|
|
30883
31258
|
}
|
|
30884
31259
|
]
|
|
30885
31260
|
},
|
|
@@ -30893,7 +31268,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os20.homedir()) {
|
|
|
30893
31268
|
// Single hook: prompt-submit handles memory retrieval + entity boost.
|
|
30894
31269
|
// exe-heartbeat-hook is CC-specific (intercom) — omitted on Codex.
|
|
30895
31270
|
type: "command",
|
|
30896
|
-
command: `node "${
|
|
31271
|
+
command: `node "${path47.join(packageRoot, "dist", "hooks", "prompt-submit.js")}"${logSuffix}`
|
|
30897
31272
|
}
|
|
30898
31273
|
]
|
|
30899
31274
|
},
|
|
@@ -30905,7 +31280,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os20.homedir()) {
|
|
|
30905
31280
|
hooks: [
|
|
30906
31281
|
{
|
|
30907
31282
|
type: "command",
|
|
30908
|
-
command: `node "${
|
|
31283
|
+
command: `node "${path47.join(packageRoot, "dist", "hooks", "stop.js")}"${logSuffix}`
|
|
30909
31284
|
}
|
|
30910
31285
|
]
|
|
30911
31286
|
},
|
|
@@ -30918,7 +31293,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os20.homedir()) {
|
|
|
30918
31293
|
hooks: [
|
|
30919
31294
|
{
|
|
30920
31295
|
type: "command",
|
|
30921
|
-
command: `node "${
|
|
31296
|
+
command: `node "${path47.join(packageRoot, "dist", "hooks", "pre-tool-use.js")}"${logSuffix}`
|
|
30922
31297
|
}
|
|
30923
31298
|
]
|
|
30924
31299
|
},
|
|
@@ -30950,8 +31325,8 @@ async function mergeCodexHooks(packageRoot, homeDir = os20.homedir()) {
|
|
|
30950
31325
|
return { added, skipped };
|
|
30951
31326
|
}
|
|
30952
31327
|
function verifyCodexHooks(homeDir = os20.homedir()) {
|
|
30953
|
-
const hooksPath =
|
|
30954
|
-
if (!
|
|
31328
|
+
const hooksPath = path47.join(homeDir, ".codex", "hooks.json");
|
|
31329
|
+
if (!existsSync33(hooksPath)) return false;
|
|
30955
31330
|
try {
|
|
30956
31331
|
const hooksJson = JSON.parse(
|
|
30957
31332
|
__require("fs").readFileSync(hooksPath, "utf-8")
|
|
@@ -30974,11 +31349,11 @@ function verifyCodexHooks(homeDir = os20.homedir()) {
|
|
|
30974
31349
|
async function installCodexStatusLine(homeDir = os20.homedir()) {
|
|
30975
31350
|
const prefs = loadPreferences(homeDir);
|
|
30976
31351
|
if (prefs.codexStatusLine === false) return "opted-out";
|
|
30977
|
-
const codexDir =
|
|
30978
|
-
const configPath =
|
|
31352
|
+
const codexDir = path47.join(homeDir, ".codex");
|
|
31353
|
+
const configPath = path47.join(codexDir, "config.toml");
|
|
30979
31354
|
await mkdir9(codexDir, { recursive: true });
|
|
30980
31355
|
let content = "";
|
|
30981
|
-
if (
|
|
31356
|
+
if (existsSync33(configPath)) {
|
|
30982
31357
|
content = await readFile8(configPath, "utf-8");
|
|
30983
31358
|
if (/\[tui\][\s\S]*?status_line\s*=/.test(content)) {
|
|
30984
31359
|
return "already-configured";
|
|
@@ -31036,12 +31411,12 @@ ${desired}
|
|
|
31036
31411
|
return { content: next, changed };
|
|
31037
31412
|
}
|
|
31038
31413
|
async function registerCodexMcpServer(packageRoot, homeDir = os20.homedir()) {
|
|
31039
|
-
const codexDir =
|
|
31040
|
-
const configPath =
|
|
31041
|
-
const serverJsPath =
|
|
31414
|
+
const codexDir = path47.join(homeDir, ".codex");
|
|
31415
|
+
const configPath = path47.join(codexDir, "config.toml");
|
|
31416
|
+
const serverJsPath = path47.join(packageRoot, "dist", "mcp", "server.js");
|
|
31042
31417
|
await mkdir9(codexDir, { recursive: true });
|
|
31043
31418
|
let content = "";
|
|
31044
|
-
if (
|
|
31419
|
+
if (existsSync33(configPath)) {
|
|
31045
31420
|
content = await readFile8(configPath, "utf-8");
|
|
31046
31421
|
}
|
|
31047
31422
|
const sectionHeader = "[mcp_servers.exe-os]";
|
|
@@ -31066,10 +31441,10 @@ async function registerCodexMcpServer(packageRoot, homeDir = os20.homedir()) {
|
|
|
31066
31441
|
return "registered";
|
|
31067
31442
|
}
|
|
31068
31443
|
async function ensureCodexHooksFeature(homeDir = os20.homedir()) {
|
|
31069
|
-
const configPath =
|
|
31070
|
-
await mkdir9(
|
|
31444
|
+
const configPath = path47.join(homeDir, ".codex", "config.toml");
|
|
31445
|
+
await mkdir9(path47.join(homeDir, ".codex"), { recursive: true });
|
|
31071
31446
|
let content = "";
|
|
31072
|
-
if (
|
|
31447
|
+
if (existsSync33(configPath)) {
|
|
31073
31448
|
content = await readFile8(configPath, "utf-8");
|
|
31074
31449
|
}
|
|
31075
31450
|
if (/\[features\][\s\S]*?codex_hooks\s*=\s*true/.test(content)) {
|
|
@@ -31137,14 +31512,14 @@ var init_installer3 = __esm({
|
|
|
31137
31512
|
});
|
|
31138
31513
|
|
|
31139
31514
|
// src/bin/cli.ts
|
|
31140
|
-
import { existsSync as
|
|
31141
|
-
import
|
|
31515
|
+
import { existsSync as existsSync34, readFileSync as readFileSync29, writeFileSync as writeFileSync21, readdirSync as readdirSync10, rmSync } from "fs";
|
|
31516
|
+
import path48 from "path";
|
|
31142
31517
|
import os21 from "os";
|
|
31143
31518
|
var args = process.argv.slice(2);
|
|
31144
31519
|
if (args.includes("--version") || args.includes("-v")) {
|
|
31145
31520
|
try {
|
|
31146
|
-
const pkgPath =
|
|
31147
|
-
const pkg = JSON.parse(
|
|
31521
|
+
const pkgPath = path48.join(path48.dirname(new URL(import.meta.url).pathname), "..", "..", "package.json");
|
|
31522
|
+
const pkg = JSON.parse(readFileSync29(pkgPath, "utf8"));
|
|
31148
31523
|
console.log(pkg.version);
|
|
31149
31524
|
} catch {
|
|
31150
31525
|
console.log("unknown");
|
|
@@ -31303,16 +31678,19 @@ ID: ${result.id}`);
|
|
|
31303
31678
|
} else if (args[0] === "update") {
|
|
31304
31679
|
const { runUpdate: runUpdate2 } = await Promise.resolve().then(() => (init_update(), update_exports));
|
|
31305
31680
|
await runUpdate2(args.slice(1));
|
|
31681
|
+
} else if (args[0] === "stack-update") {
|
|
31682
|
+
const { runStackUpdateCli } = await Promise.resolve().then(() => (init_stack_update2(), stack_update_exports));
|
|
31683
|
+
await runStackUpdateCli();
|
|
31306
31684
|
} else if (args.includes("--tui") || args.includes("--demo") || args[0] === "tui") {
|
|
31307
31685
|
checkForUpdateOnBoot().catch(() => {
|
|
31308
31686
|
});
|
|
31309
31687
|
await init_App2().then(() => App_exports);
|
|
31310
31688
|
} else {
|
|
31311
|
-
const claudeDir =
|
|
31312
|
-
const settingsPath =
|
|
31313
|
-
const hasClaudeCode =
|
|
31689
|
+
const claudeDir = path48.join(os21.homedir(), ".claude");
|
|
31690
|
+
const settingsPath = path48.join(claudeDir, "settings.json");
|
|
31691
|
+
const hasClaudeCode = existsSync34(settingsPath) && (() => {
|
|
31314
31692
|
try {
|
|
31315
|
-
const raw =
|
|
31693
|
+
const raw = readFileSync29(settingsPath, "utf8");
|
|
31316
31694
|
return raw.includes("exe-os") || raw.includes("exe-mem");
|
|
31317
31695
|
} catch {
|
|
31318
31696
|
return false;
|
|
@@ -31322,9 +31700,9 @@ ID: ${result.id}`);
|
|
|
31322
31700
|
const { DEFAULT_COORDINATOR_TEMPLATE_NAME: DEFAULT_COORDINATOR_TEMPLATE_NAME2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
31323
31701
|
let cooName = DEFAULT_COORDINATOR_TEMPLATE_NAME2;
|
|
31324
31702
|
try {
|
|
31325
|
-
const rosterPath =
|
|
31326
|
-
if (
|
|
31327
|
-
const roster = JSON.parse(
|
|
31703
|
+
const rosterPath = path48.join(os21.homedir(), ".exe-os", "exe-employees.json");
|
|
31704
|
+
if (existsSync34(rosterPath)) {
|
|
31705
|
+
const roster = JSON.parse(readFileSync29(rosterPath, "utf8"));
|
|
31328
31706
|
const coo = roster.find((e) => e.role === "COO");
|
|
31329
31707
|
if (coo) cooName = coo.name;
|
|
31330
31708
|
}
|
|
@@ -31388,14 +31766,15 @@ async function runCodexInstall() {
|
|
|
31388
31766
|
}
|
|
31389
31767
|
}
|
|
31390
31768
|
async function runClaudeCheck() {
|
|
31391
|
-
const
|
|
31392
|
-
const
|
|
31393
|
-
const
|
|
31769
|
+
const { detectMcpNameCollisions: detectMcpNameCollisions2 } = await Promise.resolve().then(() => (init_installer(), installer_exports));
|
|
31770
|
+
const claudeDir = path48.join(os21.homedir(), ".claude");
|
|
31771
|
+
const settingsPath = path48.join(claudeDir, "settings.json");
|
|
31772
|
+
const claudeJsonPath = path48.join(os21.homedir(), ".claude.json");
|
|
31394
31773
|
let ok = true;
|
|
31395
|
-
if (
|
|
31774
|
+
if (existsSync34(settingsPath)) {
|
|
31396
31775
|
let settings;
|
|
31397
31776
|
try {
|
|
31398
|
-
settings = JSON.parse(
|
|
31777
|
+
settings = JSON.parse(readFileSync29(settingsPath, "utf8"));
|
|
31399
31778
|
} catch {
|
|
31400
31779
|
console.log("\x1B[31m\u2717\x1B[0m settings.json is malformed (invalid JSON)");
|
|
31401
31780
|
ok = false;
|
|
@@ -31421,10 +31800,10 @@ async function runClaudeCheck() {
|
|
|
31421
31800
|
console.log("\x1B[31m\u2717\x1B[0m settings.json not found");
|
|
31422
31801
|
ok = false;
|
|
31423
31802
|
}
|
|
31424
|
-
if (
|
|
31803
|
+
if (existsSync34(claudeJsonPath)) {
|
|
31425
31804
|
let claudeJson;
|
|
31426
31805
|
try {
|
|
31427
|
-
claudeJson = JSON.parse(
|
|
31806
|
+
claudeJson = JSON.parse(readFileSync29(claudeJsonPath, "utf8"));
|
|
31428
31807
|
} catch {
|
|
31429
31808
|
console.log("\x1B[31m\u2717\x1B[0m claude.json is malformed (invalid JSON)");
|
|
31430
31809
|
ok = false;
|
|
@@ -31443,8 +31822,22 @@ async function runClaudeCheck() {
|
|
|
31443
31822
|
console.log("\x1B[31m\u2717\x1B[0m claude.json not found");
|
|
31444
31823
|
ok = false;
|
|
31445
31824
|
}
|
|
31446
|
-
const
|
|
31447
|
-
|
|
31825
|
+
const collisions = detectMcpNameCollisions2(os21.homedir(), process.cwd());
|
|
31826
|
+
const disabledCollisions = collisions.filter((c) => c.disabledInMcpJson);
|
|
31827
|
+
if (disabledCollisions.length > 0) {
|
|
31828
|
+
console.log("\x1B[31m\u2717\x1B[0m MCP name collision: disabled .mcp.json entries shadow project MCP servers");
|
|
31829
|
+
for (const c of disabledCollisions) {
|
|
31830
|
+
console.log(` - ${c.serverName}: ${c.mcpJsonPath} shadows ~/.claude.json project ${c.projectPath}`);
|
|
31831
|
+
}
|
|
31832
|
+
console.log(" Fix: remove/rename the duplicate .mcp.json server entry, or use one MCP config source of truth.");
|
|
31833
|
+
ok = false;
|
|
31834
|
+
} else if (collisions.length > 0) {
|
|
31835
|
+
console.log("\x1B[33m!\x1B[0m MCP server names duplicated between .mcp.json and project config");
|
|
31836
|
+
} else {
|
|
31837
|
+
console.log("\x1B[32m\u2713\x1B[0m No .mcp.json/project MCP name collisions detected");
|
|
31838
|
+
}
|
|
31839
|
+
const skillsDir = path48.join(claudeDir, "skills");
|
|
31840
|
+
if (existsSync34(skillsDir)) {
|
|
31448
31841
|
console.log("\x1B[32m\u2713\x1B[0m Slash skills directory exists");
|
|
31449
31842
|
} else {
|
|
31450
31843
|
console.log("\x1B[31m\u2717\x1B[0m Slash skills directory missing");
|
|
@@ -31461,16 +31854,16 @@ async function runClaudeUninstall(flags = []) {
|
|
|
31461
31854
|
const dryRun = flags.includes("--dry-run");
|
|
31462
31855
|
const purge = flags.includes("--purge");
|
|
31463
31856
|
const homeDir = os21.homedir();
|
|
31464
|
-
const claudeDir =
|
|
31465
|
-
const settingsPath =
|
|
31466
|
-
const claudeJsonPath =
|
|
31467
|
-
const exeOsDir =
|
|
31857
|
+
const claudeDir = path48.join(homeDir, ".claude");
|
|
31858
|
+
const settingsPath = path48.join(claudeDir, "settings.json");
|
|
31859
|
+
const claudeJsonPath = path48.join(homeDir, ".claude.json");
|
|
31860
|
+
const exeOsDir = path48.join(homeDir, ".exe-os");
|
|
31468
31861
|
let removed = 0;
|
|
31469
31862
|
const log = (msg) => console.log(dryRun ? `[dry-run] ${msg}` : msg);
|
|
31470
31863
|
let settings = {};
|
|
31471
|
-
if (
|
|
31864
|
+
if (existsSync34(settingsPath)) {
|
|
31472
31865
|
try {
|
|
31473
|
-
settings = JSON.parse(
|
|
31866
|
+
settings = JSON.parse(readFileSync29(settingsPath, "utf8"));
|
|
31474
31867
|
} catch {
|
|
31475
31868
|
console.error("Your ~/.claude/settings.json appears malformed.");
|
|
31476
31869
|
if (purge) {
|
|
@@ -31508,15 +31901,15 @@ async function runClaudeUninstall(flags = []) {
|
|
|
31508
31901
|
permCount = before - settings.permissions.allow.length;
|
|
31509
31902
|
}
|
|
31510
31903
|
if (!dryRun) {
|
|
31511
|
-
|
|
31904
|
+
writeFileSync21(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
31512
31905
|
}
|
|
31513
31906
|
log("\u2713 Removed exe-os hooks from settings.json");
|
|
31514
31907
|
if (permCount > 0) log(`\u2713 Removed ${permCount} MCP permission entries`);
|
|
31515
31908
|
removed++;
|
|
31516
31909
|
}
|
|
31517
31910
|
}
|
|
31518
|
-
if (
|
|
31519
|
-
const raw =
|
|
31911
|
+
if (existsSync34(claudeJsonPath)) {
|
|
31912
|
+
const raw = readFileSync29(claudeJsonPath, "utf8");
|
|
31520
31913
|
if (raw.length > 1e6) {
|
|
31521
31914
|
console.error("claude.json exceeds 1 MB \u2014 skipping parse.");
|
|
31522
31915
|
} else {
|
|
@@ -31537,7 +31930,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
31537
31930
|
}
|
|
31538
31931
|
if (removedMcp) {
|
|
31539
31932
|
if (!dryRun) {
|
|
31540
|
-
|
|
31933
|
+
writeFileSync21(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
31541
31934
|
}
|
|
31542
31935
|
log("\u2713 Removed exe-os MCP server from claude.json");
|
|
31543
31936
|
removed++;
|
|
@@ -31545,14 +31938,14 @@ async function runClaudeUninstall(flags = []) {
|
|
|
31545
31938
|
}
|
|
31546
31939
|
}
|
|
31547
31940
|
}
|
|
31548
|
-
const skillsDir =
|
|
31549
|
-
if (
|
|
31941
|
+
const skillsDir = path48.join(claudeDir, "skills");
|
|
31942
|
+
if (existsSync34(skillsDir)) {
|
|
31550
31943
|
let skillCount = 0;
|
|
31551
31944
|
try {
|
|
31552
31945
|
const entries = readdirSync10(skillsDir);
|
|
31553
31946
|
for (const entry of entries) {
|
|
31554
31947
|
if (entry.startsWith("exe")) {
|
|
31555
|
-
const fullPath =
|
|
31948
|
+
const fullPath = path48.join(skillsDir, entry);
|
|
31556
31949
|
if (!dryRun) rmSync(fullPath, { recursive: true, force: true });
|
|
31557
31950
|
skillCount++;
|
|
31558
31951
|
}
|
|
@@ -31564,30 +31957,30 @@ async function runClaudeUninstall(flags = []) {
|
|
|
31564
31957
|
removed++;
|
|
31565
31958
|
}
|
|
31566
31959
|
}
|
|
31567
|
-
const claudeMdPath =
|
|
31568
|
-
if (
|
|
31569
|
-
const content =
|
|
31960
|
+
const claudeMdPath = path48.join(claudeDir, "CLAUDE.md");
|
|
31961
|
+
if (existsSync34(claudeMdPath)) {
|
|
31962
|
+
const content = readFileSync29(claudeMdPath, "utf8");
|
|
31570
31963
|
const startMarker = "<!-- exe-os:orchestration-start -->";
|
|
31571
31964
|
const endMarker = "<!-- exe-os:orchestration-end -->";
|
|
31572
31965
|
const startIdx = content.indexOf(startMarker);
|
|
31573
31966
|
const endIdx = content.indexOf(endMarker);
|
|
31574
31967
|
if (startIdx !== -1 && endIdx !== -1) {
|
|
31575
31968
|
const cleaned = (content.slice(0, startIdx) + content.slice(endIdx + endMarker.length)).replace(/\n{3,}/g, "\n\n").trim() + "\n";
|
|
31576
|
-
if (!dryRun)
|
|
31969
|
+
if (!dryRun) writeFileSync21(claudeMdPath, cleaned);
|
|
31577
31970
|
log("\u2713 Removed orchestration block from CLAUDE.md");
|
|
31578
31971
|
removed++;
|
|
31579
31972
|
}
|
|
31580
31973
|
}
|
|
31581
|
-
const agentsDir =
|
|
31582
|
-
if (
|
|
31974
|
+
const agentsDir = path48.join(claudeDir, "agents");
|
|
31975
|
+
if (existsSync34(agentsDir)) {
|
|
31583
31976
|
let agentCount = 0;
|
|
31584
31977
|
try {
|
|
31585
31978
|
const entries = readdirSync10(agentsDir).filter((f) => f.endsWith(".md"));
|
|
31586
31979
|
let knownNames = /* @__PURE__ */ new Set();
|
|
31587
|
-
const rosterPath =
|
|
31588
|
-
if (
|
|
31980
|
+
const rosterPath = path48.join(exeOsDir, "exe-employees.json");
|
|
31981
|
+
if (existsSync34(rosterPath)) {
|
|
31589
31982
|
try {
|
|
31590
|
-
const roster = JSON.parse(
|
|
31983
|
+
const roster = JSON.parse(readFileSync29(rosterPath, "utf8"));
|
|
31591
31984
|
knownNames = new Set(roster.map((e) => e.name));
|
|
31592
31985
|
} catch {
|
|
31593
31986
|
}
|
|
@@ -31595,7 +31988,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
31595
31988
|
for (const entry of entries) {
|
|
31596
31989
|
const name = entry.replace(/\.md$/, "");
|
|
31597
31990
|
if (knownNames.has(name)) {
|
|
31598
|
-
if (!dryRun) rmSync(
|
|
31991
|
+
if (!dryRun) rmSync(path48.join(agentsDir, entry), { force: true });
|
|
31599
31992
|
agentCount++;
|
|
31600
31993
|
}
|
|
31601
31994
|
}
|
|
@@ -31606,16 +31999,16 @@ async function runClaudeUninstall(flags = []) {
|
|
|
31606
31999
|
removed++;
|
|
31607
32000
|
}
|
|
31608
32001
|
}
|
|
31609
|
-
const projectsDir =
|
|
31610
|
-
if (
|
|
32002
|
+
const projectsDir = path48.join(claudeDir, "projects");
|
|
32003
|
+
if (existsSync34(projectsDir)) {
|
|
31611
32004
|
let projectCount = 0;
|
|
31612
32005
|
try {
|
|
31613
32006
|
const projects = readdirSync10(projectsDir);
|
|
31614
32007
|
for (const proj of projects) {
|
|
31615
|
-
const projSettings =
|
|
31616
|
-
if (!
|
|
32008
|
+
const projSettings = path48.join(projectsDir, proj, "settings.json");
|
|
32009
|
+
if (!existsSync34(projSettings)) continue;
|
|
31617
32010
|
try {
|
|
31618
|
-
const pSettings = JSON.parse(
|
|
32011
|
+
const pSettings = JSON.parse(readFileSync29(projSettings, "utf8"));
|
|
31619
32012
|
let changed = false;
|
|
31620
32013
|
if (Array.isArray(pSettings.permissions?.allow)) {
|
|
31621
32014
|
const before = pSettings.permissions.allow.length;
|
|
@@ -31625,7 +32018,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
31625
32018
|
if (pSettings.permissions.allow.length < before) changed = true;
|
|
31626
32019
|
}
|
|
31627
32020
|
if (changed && !dryRun) {
|
|
31628
|
-
|
|
32021
|
+
writeFileSync21(projSettings, JSON.stringify(pSettings, null, 2) + "\n");
|
|
31629
32022
|
}
|
|
31630
32023
|
if (changed) projectCount++;
|
|
31631
32024
|
} catch {
|
|
@@ -31649,18 +32042,18 @@ async function runClaudeUninstall(flags = []) {
|
|
|
31649
32042
|
};
|
|
31650
32043
|
const exeBinPath = findExeBin3();
|
|
31651
32044
|
if (!exeBinPath) throw new Error("exe-os not found in PATH");
|
|
31652
|
-
const binDir =
|
|
32045
|
+
const binDir = path48.dirname(exeBinPath);
|
|
31653
32046
|
let symlinkCount = 0;
|
|
31654
|
-
const rosterPath =
|
|
31655
|
-
if (
|
|
31656
|
-
const roster = JSON.parse(
|
|
32047
|
+
const rosterPath = path48.join(exeOsDir, "exe-employees.json");
|
|
32048
|
+
if (existsSync34(rosterPath)) {
|
|
32049
|
+
const roster = JSON.parse(readFileSync29(rosterPath, "utf8"));
|
|
31657
32050
|
const { DEFAULT_COORDINATOR_TEMPLATE_NAME: DEFAULT_COORDINATOR_TEMPLATE_NAME2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
31658
32051
|
const coordinatorName = roster.find((e) => e.role?.toLowerCase() === "coo")?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME2;
|
|
31659
32052
|
for (const emp of roster) {
|
|
31660
32053
|
if (emp.name === coordinatorName) continue;
|
|
31661
32054
|
for (const suffix of ["", "-opencode"]) {
|
|
31662
|
-
const linkPath =
|
|
31663
|
-
if (
|
|
32055
|
+
const linkPath = path48.join(binDir, `${emp.name}${suffix}`);
|
|
32056
|
+
if (existsSync34(linkPath)) {
|
|
31664
32057
|
if (!dryRun) rmSync(linkPath, { force: true });
|
|
31665
32058
|
symlinkCount++;
|
|
31666
32059
|
}
|
|
@@ -31673,7 +32066,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
31673
32066
|
}
|
|
31674
32067
|
} catch {
|
|
31675
32068
|
}
|
|
31676
|
-
if (purge &&
|
|
32069
|
+
if (purge && existsSync34(exeOsDir)) {
|
|
31677
32070
|
if (!dryRun) {
|
|
31678
32071
|
process.stdout.write("\x1B[33m\u26A0 This will delete all memories, identities, and agent data.\x1B[0m\n");
|
|
31679
32072
|
process.stdout.write(" Removing ~/.exe-os...\n");
|
|
@@ -31698,7 +32091,7 @@ async function checkForUpdateOnBoot() {
|
|
|
31698
32091
|
const config = await loadConfig2();
|
|
31699
32092
|
if (!config.autoUpdate.checkOnBoot) return;
|
|
31700
32093
|
const { checkForUpdate: checkForUpdate2 } = await Promise.resolve().then(() => (init_update(), update_exports));
|
|
31701
|
-
const packageRoot =
|
|
32094
|
+
const packageRoot = path48.resolve(
|
|
31702
32095
|
new URL("../..", import.meta.url).pathname
|
|
31703
32096
|
);
|
|
31704
32097
|
const result = checkForUpdate2(packageRoot);
|
|
@@ -31758,7 +32151,7 @@ async function runActivate(key) {
|
|
|
31758
32151
|
const idTemplate = getIdentityTemplate(identityKey);
|
|
31759
32152
|
if (idTemplate) {
|
|
31760
32153
|
const idPath = identityPath2(name);
|
|
31761
|
-
const dir =
|
|
32154
|
+
const dir = path48.dirname(idPath);
|
|
31762
32155
|
if (!fs8.existsSync(dir)) fs8.mkdirSync(dir, { recursive: true });
|
|
31763
32156
|
fs8.writeFileSync(idPath, idTemplate.replace(/^agent_id: \w+/m, `agent_id: ${name}`), "utf-8");
|
|
31764
32157
|
}
|