@askexenow/exe-os 0.9.37 → 0.9.39
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 +36 -9
- package/dist/bin/backfill-responses.js +36 -9
- package/dist/bin/backfill-vectors.js +36 -9
- package/dist/bin/cleanup-stale-review-tasks.js +37 -10
- package/dist/bin/cli.js +624 -204
- package/dist/bin/exe-agent.js +13 -5
- package/dist/bin/exe-assign.js +36 -9
- package/dist/bin/exe-boot.js +50 -20
- package/dist/bin/exe-call.js +134 -342
- package/dist/bin/exe-dispatch.js +36 -9
- package/dist/bin/exe-doctor.js +39 -12
- package/dist/bin/exe-export-behaviors.js +38 -11
- package/dist/bin/exe-forget.js +36 -9
- package/dist/bin/exe-gateway.js +64 -15
- package/dist/bin/exe-heartbeat.js +37 -10
- package/dist/bin/exe-kill.js +36 -9
- package/dist/bin/exe-launch-agent.js +287 -1081
- package/dist/bin/exe-new-employee.js +100 -14
- package/dist/bin/exe-pending-messages.js +36 -9
- package/dist/bin/exe-pending-notifications.js +36 -9
- package/dist/bin/exe-pending-reviews.js +36 -9
- package/dist/bin/exe-rename.js +1780 -204
- package/dist/bin/exe-review.js +36 -9
- package/dist/bin/exe-search.js +38 -11
- package/dist/bin/exe-session-cleanup.js +38 -11
- package/dist/bin/exe-start-codex.js +38 -11
- package/dist/bin/exe-start-opencode.js +38 -11
- package/dist/bin/exe-status.js +37 -10
- package/dist/bin/exe-team.js +36 -9
- package/dist/bin/git-sweep.js +36 -9
- package/dist/bin/graph-backfill.js +36 -9
- package/dist/bin/graph-export.js +36 -9
- package/dist/bin/install.js +70 -3
- package/dist/bin/intercom-check.js +38 -11
- package/dist/bin/scan-tasks.js +36 -9
- package/dist/bin/setup.js +20 -19
- package/dist/bin/shard-migrate.js +36 -9
- package/dist/bin/stack-update.js +308 -0
- package/dist/gateway/index.js +62 -13
- package/dist/hooks/bug-report-worker.js +40 -12
- package/dist/hooks/codex-stop-task-finalizer.js +38 -11
- package/dist/hooks/commit-complete.js +36 -9
- package/dist/hooks/error-recall.js +38 -11
- package/dist/hooks/ingest.js +38 -10
- package/dist/hooks/instructions-loaded.js +44 -12
- package/dist/hooks/notification.js +36 -9
- package/dist/hooks/post-compact.js +36 -9
- package/dist/hooks/post-tool-combined.js +39 -12
- package/dist/hooks/pre-compact.js +37 -10
- package/dist/hooks/pre-tool-use.js +38 -10
- package/dist/hooks/prompt-submit.js +43 -15
- package/dist/hooks/session-end.js +37 -10
- package/dist/hooks/session-start.js +49 -16
- package/dist/hooks/stop.js +37 -10
- package/dist/hooks/subagent-stop.js +36 -9
- package/dist/hooks/summary-worker.js +45 -18
- package/dist/index.js +60 -11
- package/dist/lib/consolidation.js +2 -1
- package/dist/lib/employee-templates.js +4 -3
- package/dist/lib/employees.js +2 -1
- package/dist/lib/exe-daemon.js +11229 -10537
- package/dist/lib/hybrid-search.js +38 -11
- package/dist/lib/identity.js +8 -3
- package/dist/lib/schedules.js +36 -9
- package/dist/lib/store.js +36 -9
- package/dist/mcp/server.js +6873 -6249
- package/dist/mcp/tools/create-task.js +10 -4
- package/dist/runtime/index.js +36 -9
- package/dist/tui/App.js +42 -13
- package/package.json +4 -1
- package/stack.release.json +31 -0
- package/stack.release.schema.json +31 -0
package/dist/bin/cli.js
CHANGED
|
@@ -540,7 +540,8 @@ function isMultiInstance(agentName, employees) {
|
|
|
540
540
|
return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
|
|
541
541
|
}
|
|
542
542
|
function addEmployee(employees, employee) {
|
|
543
|
-
const
|
|
543
|
+
const { systemPrompt: _legacyPrompt, ...rest } = employee;
|
|
544
|
+
const normalized = { ...rest, name: employee.name.toLowerCase() };
|
|
544
545
|
if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
|
|
545
546
|
throw new Error(`Employee '${normalized.name}' already exists`);
|
|
546
547
|
}
|
|
@@ -814,6 +815,7 @@ var installer_exports = {};
|
|
|
814
815
|
__export(installer_exports, {
|
|
815
816
|
cleanOldShellFunctions: () => cleanOldShellFunctions,
|
|
816
817
|
copySlashCommands: () => copySlashCommands,
|
|
818
|
+
detectMcpNameCollisions: () => detectMcpNameCollisions,
|
|
817
819
|
installStatusLine: () => installStatusLine,
|
|
818
820
|
mergeHooks: () => mergeHooks,
|
|
819
821
|
registerMcpServer: () => registerMcpServer,
|
|
@@ -899,6 +901,60 @@ name: ${skillName}
|
|
|
899
901
|
await writeFile3(destPath, content);
|
|
900
902
|
return true;
|
|
901
903
|
}
|
|
904
|
+
function readJsonFile(filePath) {
|
|
905
|
+
try {
|
|
906
|
+
return JSON.parse(readFileSync5(filePath, "utf-8"));
|
|
907
|
+
} catch {
|
|
908
|
+
return null;
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
function findAncestorMcpJsons(startDir, homeDir) {
|
|
912
|
+
const files = [];
|
|
913
|
+
let dir = path6.resolve(startDir);
|
|
914
|
+
const root = path6.parse(dir).root;
|
|
915
|
+
const stop = path6.resolve(homeDir);
|
|
916
|
+
while (dir !== root) {
|
|
917
|
+
const candidate = path6.join(dir, ".mcp.json");
|
|
918
|
+
if (existsSync7(candidate)) files.push(candidate);
|
|
919
|
+
if (dir === stop) break;
|
|
920
|
+
dir = path6.dirname(dir);
|
|
921
|
+
}
|
|
922
|
+
return files;
|
|
923
|
+
}
|
|
924
|
+
function pathApplies(projectPath, cwd2) {
|
|
925
|
+
const project = path6.resolve(projectPath);
|
|
926
|
+
const current = path6.resolve(cwd2);
|
|
927
|
+
return current === project || current.startsWith(project + path6.sep);
|
|
928
|
+
}
|
|
929
|
+
function detectMcpNameCollisions(homeDir = os5.homedir(), cwd2 = process.cwd()) {
|
|
930
|
+
const claudeJsonPath = path6.join(homeDir, ".claude.json");
|
|
931
|
+
if (!existsSync7(claudeJsonPath)) return [];
|
|
932
|
+
const claudeJson = readJsonFile(claudeJsonPath);
|
|
933
|
+
if (!claudeJson?.projects) return [];
|
|
934
|
+
const collisions = [];
|
|
935
|
+
const mcpJsons = findAncestorMcpJsons(cwd2, homeDir);
|
|
936
|
+
if (mcpJsons.length === 0) return [];
|
|
937
|
+
for (const [projectPath, projectConfig] of Object.entries(claudeJson.projects)) {
|
|
938
|
+
if (!pathApplies(projectPath, cwd2)) continue;
|
|
939
|
+
const projectServerNames = new Set(Object.keys(projectConfig.mcpServers ?? {}));
|
|
940
|
+
if (projectServerNames.size === 0) continue;
|
|
941
|
+
const enabled = new Set(projectConfig.enabledMcpjsonServers ?? []);
|
|
942
|
+
for (const mcpJsonPath of mcpJsons) {
|
|
943
|
+
const mcpJson = readJsonFile(mcpJsonPath);
|
|
944
|
+
const mcpServerNames = Object.keys(mcpJson?.mcpServers ?? {});
|
|
945
|
+
for (const serverName of mcpServerNames) {
|
|
946
|
+
if (!projectServerNames.has(serverName)) continue;
|
|
947
|
+
collisions.push({
|
|
948
|
+
mcpJsonPath,
|
|
949
|
+
projectPath,
|
|
950
|
+
serverName,
|
|
951
|
+
disabledInMcpJson: !enabled.has(serverName)
|
|
952
|
+
});
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
return collisions;
|
|
957
|
+
}
|
|
902
958
|
async function registerMcpServer(packageRoot, homeDir = os5.homedir()) {
|
|
903
959
|
const claudeJsonPath = path6.join(homeDir, ".claude.json");
|
|
904
960
|
let claudeJson = {};
|
|
@@ -927,12 +983,26 @@ async function registerMcpServer(packageRoot, homeDir = os5.homedir()) {
|
|
|
927
983
|
if (osMatches) {
|
|
928
984
|
await cleanSettingsJsonMcp(path6.join(homeDir, ".claude", "settings.json"));
|
|
929
985
|
await migratePermissionsToExeOs(path6.join(homeDir, ".claude", "settings.json"));
|
|
986
|
+
const collisions2 = detectMcpNameCollisions(homeDir, packageRoot).filter((c) => c.serverName === MCP_PRIMARY_KEY || c.serverName === MCP_LEGACY_KEY);
|
|
987
|
+
for (const c of collisions2) {
|
|
988
|
+
process.stderr.write(
|
|
989
|
+
`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.
|
|
990
|
+
`
|
|
991
|
+
);
|
|
992
|
+
}
|
|
930
993
|
return false;
|
|
931
994
|
}
|
|
932
995
|
claudeJson.mcpServers[MCP_PRIMARY_KEY] = newEntry;
|
|
933
996
|
await writeFile3(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
934
997
|
await cleanSettingsJsonMcp(path6.join(homeDir, ".claude", "settings.json"));
|
|
935
998
|
await migratePermissionsToExeOs(path6.join(homeDir, ".claude", "settings.json"));
|
|
999
|
+
const collisions = detectMcpNameCollisions(homeDir, packageRoot).filter((c) => c.serverName === MCP_PRIMARY_KEY || c.serverName === MCP_LEGACY_KEY);
|
|
1000
|
+
for (const c of collisions) {
|
|
1001
|
+
process.stderr.write(
|
|
1002
|
+
`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.
|
|
1003
|
+
`
|
|
1004
|
+
);
|
|
1005
|
+
}
|
|
936
1006
|
return true;
|
|
937
1007
|
}
|
|
938
1008
|
async function cleanSettingsJsonMcp(settingsPath) {
|
|
@@ -1234,8 +1304,6 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1234
1304
|
"purge_document",
|
|
1235
1305
|
"rerank_documents",
|
|
1236
1306
|
"set_document_importance",
|
|
1237
|
-
"create_wiki_page",
|
|
1238
|
-
"update_wiki_page",
|
|
1239
1307
|
"get_wiki_page",
|
|
1240
1308
|
"list_wiki_pages",
|
|
1241
1309
|
// System
|
|
@@ -1773,8 +1841,8 @@ function deriveMachineKey() {
|
|
|
1773
1841
|
}
|
|
1774
1842
|
function readMachineId() {
|
|
1775
1843
|
try {
|
|
1776
|
-
const { readFileSync:
|
|
1777
|
-
return
|
|
1844
|
+
const { readFileSync: readFileSync30 } = __require("fs");
|
|
1845
|
+
return readFileSync30("/etc/machine-id", "utf-8").trim();
|
|
1778
1846
|
} catch {
|
|
1779
1847
|
return "";
|
|
1780
1848
|
}
|
|
@@ -4797,8 +4865,8 @@ async function validateLicense(apiKey, deviceId) {
|
|
|
4797
4865
|
}
|
|
4798
4866
|
function getCacheAgeMs() {
|
|
4799
4867
|
try {
|
|
4800
|
-
const { statSync:
|
|
4801
|
-
const s =
|
|
4868
|
+
const { statSync: statSync5 } = __require("fs");
|
|
4869
|
+
const s = statSync5(CACHE_PATH);
|
|
4802
4870
|
return Date.now() - s.mtimeMs;
|
|
4803
4871
|
} catch {
|
|
4804
4872
|
return Infinity;
|
|
@@ -7035,7 +7103,7 @@ __export(shard_manager_exports, {
|
|
|
7035
7103
|
shardExists: () => shardExists
|
|
7036
7104
|
});
|
|
7037
7105
|
import path15 from "path";
|
|
7038
|
-
import { existsSync as existsSync15, mkdirSync as mkdirSync8, readdirSync as readdirSync3 } from "fs";
|
|
7106
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync8, readdirSync as readdirSync3, renameSync as renameSync3, statSync as statSync4 } from "fs";
|
|
7039
7107
|
import { createClient as createClient2 } from "@libsql/client";
|
|
7040
7108
|
function initShardManager(encryptionKey) {
|
|
7041
7109
|
_encryptionKey = encryptionKey;
|
|
@@ -7057,7 +7125,7 @@ function getShardClient(projectName) {
|
|
|
7057
7125
|
if (!_encryptionKey) {
|
|
7058
7126
|
throw new Error("Shard manager not initialized. Call initShardManager() first.");
|
|
7059
7127
|
}
|
|
7060
|
-
const safeName = projectName
|
|
7128
|
+
const safeName = safeShardName(projectName);
|
|
7061
7129
|
if (!safeName || safeName === "unknown") {
|
|
7062
7130
|
throw new Error(`Invalid project name for shard: "${projectName}" (resolved to "${safeName}")`);
|
|
7063
7131
|
}
|
|
@@ -7079,9 +7147,12 @@ function getShardClient(projectName) {
|
|
|
7079
7147
|
return client;
|
|
7080
7148
|
}
|
|
7081
7149
|
function shardExists(projectName) {
|
|
7082
|
-
const safeName = projectName
|
|
7150
|
+
const safeName = safeShardName(projectName);
|
|
7083
7151
|
return existsSync15(path15.join(SHARDS_DIR, `${safeName}.db`));
|
|
7084
7152
|
}
|
|
7153
|
+
function safeShardName(projectName) {
|
|
7154
|
+
return projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
7155
|
+
}
|
|
7085
7156
|
function listShards() {
|
|
7086
7157
|
if (!existsSync15(SHARDS_DIR)) return [];
|
|
7087
7158
|
return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
@@ -7175,7 +7246,8 @@ async function ensureShardSchema(client) {
|
|
|
7175
7246
|
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
7176
7247
|
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
7177
7248
|
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
7178
|
-
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
7249
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT",
|
|
7250
|
+
"ALTER TABLE memories ADD COLUMN deleted_at TEXT"
|
|
7179
7251
|
]) {
|
|
7180
7252
|
try {
|
|
7181
7253
|
await client.execute(col);
|
|
@@ -7271,9 +7343,32 @@ async function ensureShardSchema(client) {
|
|
|
7271
7343
|
}
|
|
7272
7344
|
}
|
|
7273
7345
|
async function getReadyShardClient(projectName) {
|
|
7274
|
-
const
|
|
7275
|
-
|
|
7276
|
-
|
|
7346
|
+
const safeName = safeShardName(projectName);
|
|
7347
|
+
let client = getShardClient(projectName);
|
|
7348
|
+
try {
|
|
7349
|
+
await ensureShardSchema(client);
|
|
7350
|
+
return client;
|
|
7351
|
+
} catch (err) {
|
|
7352
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
7353
|
+
if (!/SQLITE_NOTADB|file is not a database/i.test(message)) throw err;
|
|
7354
|
+
client.close();
|
|
7355
|
+
_shards.delete(safeName);
|
|
7356
|
+
_shardLastAccess.delete(safeName);
|
|
7357
|
+
const dbPath = path15.join(SHARDS_DIR, `${safeName}.db`);
|
|
7358
|
+
if (existsSync15(dbPath)) {
|
|
7359
|
+
const stat2 = statSync4(dbPath);
|
|
7360
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
7361
|
+
const archivedPath = path15.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
|
|
7362
|
+
renameSync3(dbPath, archivedPath);
|
|
7363
|
+
process.stderr.write(
|
|
7364
|
+
`[shard-manager] Archived unreadable shard ${safeName}: ${archivedPath} (${stat2.size} bytes, mtime ${stat2.mtime.toISOString()})
|
|
7365
|
+
`
|
|
7366
|
+
);
|
|
7367
|
+
}
|
|
7368
|
+
client = getShardClient(projectName);
|
|
7369
|
+
await ensureShardSchema(client);
|
|
7370
|
+
return client;
|
|
7371
|
+
}
|
|
7277
7372
|
}
|
|
7278
7373
|
function evictLRU() {
|
|
7279
7374
|
let oldest = null;
|
|
@@ -7494,7 +7589,7 @@ var init_platform_procedures = __esm({
|
|
|
7494
7589
|
title: "MCP tools \u2014 wiki, documents, and content",
|
|
7495
7590
|
domain: "tool-use",
|
|
7496
7591
|
priority: "p1",
|
|
7497
|
-
content: "
|
|
7592
|
+
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."
|
|
7498
7593
|
},
|
|
7499
7594
|
{
|
|
7500
7595
|
title: "MCP tools \u2014 system, operations, and admin",
|
|
@@ -7512,7 +7607,7 @@ var init_platform_procedures = __esm({
|
|
|
7512
7607
|
title: "MCP tools \u2014 advanced (triggers, skills, orchestration)",
|
|
7513
7608
|
domain: "tool-use",
|
|
7514
7609
|
priority: "p1",
|
|
7515
|
-
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
|
|
7610
|
+
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."
|
|
7516
7611
|
}
|
|
7517
7612
|
];
|
|
7518
7613
|
PLATFORM_PROCEDURE_TITLES = new Set(
|
|
@@ -8994,9 +9089,9 @@ Unclassified: ${unclassified}
|
|
|
8994
9089
|
}
|
|
8995
9090
|
async function exportBatches(options) {
|
|
8996
9091
|
const fs8 = await import("fs");
|
|
8997
|
-
const
|
|
9092
|
+
const path49 = await import("path");
|
|
8998
9093
|
const client = getClient();
|
|
8999
|
-
const outDir =
|
|
9094
|
+
const outDir = path49.join(process.cwd(), "exe/output/classifications/input");
|
|
9000
9095
|
fs8.mkdirSync(outDir, { recursive: true });
|
|
9001
9096
|
const countResult = await client.execute({
|
|
9002
9097
|
sql: "SELECT COUNT(*) as cnt FROM memories WHERE intent IS NULL AND outcome IS NULL AND domain IS NULL",
|
|
@@ -9020,7 +9115,7 @@ async function exportBatches(options) {
|
|
|
9020
9115
|
const text = String(row.text || "").replace(/\n/g, " ");
|
|
9021
9116
|
return JSON.stringify({ id: row.id, text });
|
|
9022
9117
|
});
|
|
9023
|
-
const batchFile =
|
|
9118
|
+
const batchFile = path49.join(outDir, `batch-${String(batchNum).padStart(4, "0")}.jsonl`);
|
|
9024
9119
|
fs8.writeFileSync(batchFile, lines.join("\n") + "\n");
|
|
9025
9120
|
exported += batch.rows.length;
|
|
9026
9121
|
offset += options.batchSize;
|
|
@@ -9036,7 +9131,7 @@ async function exportBatches(options) {
|
|
|
9036
9131
|
}
|
|
9037
9132
|
async function importClassifications(importDir) {
|
|
9038
9133
|
const fs8 = await import("fs");
|
|
9039
|
-
const
|
|
9134
|
+
const path49 = await import("path");
|
|
9040
9135
|
const client = getClient();
|
|
9041
9136
|
const files = fs8.readdirSync(importDir).filter((f) => f.endsWith(".jsonl")).sort();
|
|
9042
9137
|
process.stderr.write(`[backfill-metadata] Found ${files.length} JSONL files to import from ${importDir}
|
|
@@ -9044,7 +9139,7 @@ async function importClassifications(importDir) {
|
|
|
9044
9139
|
let imported = 0;
|
|
9045
9140
|
let invalid = 0;
|
|
9046
9141
|
for (const file of files) {
|
|
9047
|
-
const lines = fs8.readFileSync(
|
|
9142
|
+
const lines = fs8.readFileSync(path49.join(importDir, file), "utf-8").split("\n").filter(Boolean);
|
|
9048
9143
|
for (const line of lines) {
|
|
9049
9144
|
try {
|
|
9050
9145
|
const rec = JSON.parse(line);
|
|
@@ -9197,6 +9292,9 @@ function ensureDir() {
|
|
|
9197
9292
|
function identityPath(agentId) {
|
|
9198
9293
|
return path17.join(IDENTITY_DIR2, `${agentId}.md`);
|
|
9199
9294
|
}
|
|
9295
|
+
function sanitizeIdentityBody(body) {
|
|
9296
|
+
return body.replace(/<!--[\s\S]*?-->/g, "").trim();
|
|
9297
|
+
}
|
|
9200
9298
|
function parseFrontmatter(raw) {
|
|
9201
9299
|
const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
9202
9300
|
if (!match) {
|
|
@@ -9209,11 +9307,11 @@ function parseFrontmatter(raw) {
|
|
|
9209
9307
|
created_by: "system",
|
|
9210
9308
|
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
9211
9309
|
},
|
|
9212
|
-
body: raw
|
|
9310
|
+
body: sanitizeIdentityBody(raw)
|
|
9213
9311
|
};
|
|
9214
9312
|
}
|
|
9215
9313
|
const yamlStr = match[1];
|
|
9216
|
-
const body = match[2]
|
|
9314
|
+
const body = sanitizeIdentityBody(match[2]);
|
|
9217
9315
|
const fm = {};
|
|
9218
9316
|
for (const line of yamlStr.split("\n")) {
|
|
9219
9317
|
const kv = line.match(/^(\w+):\s*(.+)$/);
|
|
@@ -9278,7 +9376,9 @@ function listIdentities() {
|
|
|
9278
9376
|
const summary = lines[0]?.trim().slice(0, 120) ?? identity.frontmatter.title;
|
|
9279
9377
|
results.push({
|
|
9280
9378
|
agentId,
|
|
9281
|
-
title
|
|
9379
|
+
// User-facing/team-facing title only. `frontmatter.role` is internal
|
|
9380
|
+
// routing metadata and must not leak as an external title.
|
|
9381
|
+
title: identity.frontmatter.title,
|
|
9282
9382
|
summary
|
|
9283
9383
|
});
|
|
9284
9384
|
}
|
|
@@ -10109,7 +10209,7 @@ __export(intercom_queue_exports, {
|
|
|
10109
10209
|
queueIntercom: () => queueIntercom,
|
|
10110
10210
|
readQueue: () => readQueue
|
|
10111
10211
|
});
|
|
10112
|
-
import { readFileSync as readFileSync15, writeFileSync as writeFileSync13, renameSync as
|
|
10212
|
+
import { readFileSync as readFileSync15, writeFileSync as writeFileSync13, renameSync as renameSync4, existsSync as existsSync19, mkdirSync as mkdirSync13 } from "fs";
|
|
10113
10213
|
import path21 from "path";
|
|
10114
10214
|
import os12 from "os";
|
|
10115
10215
|
function ensureDir2() {
|
|
@@ -10128,7 +10228,7 @@ function writeQueue(queue) {
|
|
|
10128
10228
|
ensureDir2();
|
|
10129
10229
|
const tmp = `${QUEUE_PATH}.tmp`;
|
|
10130
10230
|
writeFileSync13(tmp, JSON.stringify(queue, null, 2));
|
|
10131
|
-
|
|
10231
|
+
renameSync4(tmp, QUEUE_PATH);
|
|
10132
10232
|
}
|
|
10133
10233
|
function queueIntercom(targetSession, reason) {
|
|
10134
10234
|
const queue = readQueue();
|
|
@@ -11677,10 +11777,10 @@ async function disposeEmbedder() {
|
|
|
11677
11777
|
async function embedDirect(text) {
|
|
11678
11778
|
const llamaCpp = await import("node-llama-cpp");
|
|
11679
11779
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
11680
|
-
const { existsSync:
|
|
11681
|
-
const
|
|
11682
|
-
const modelPath =
|
|
11683
|
-
if (!
|
|
11780
|
+
const { existsSync: existsSync35 } = await import("fs");
|
|
11781
|
+
const path49 = await import("path");
|
|
11782
|
+
const modelPath = path49.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
11783
|
+
if (!existsSync35(modelPath)) {
|
|
11684
11784
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
11685
11785
|
}
|
|
11686
11786
|
const llama = await llamaCpp.getLlama();
|
|
@@ -13808,7 +13908,8 @@ __export(employee_templates_exports, {
|
|
|
13808
13908
|
});
|
|
13809
13909
|
function getSessionPrompt(storedPrompt) {
|
|
13810
13910
|
const markerIndex = storedPrompt.indexOf(PROCEDURES_MARKER);
|
|
13811
|
-
const
|
|
13911
|
+
const withoutProcedures = markerIndex >= 0 ? storedPrompt.slice(0, markerIndex).trimEnd() : storedPrompt;
|
|
13912
|
+
const rolePrompt = withoutProcedures.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "").replace(/<!--[\s\S]*?-->/g, "").trimStart();
|
|
13812
13913
|
const globalBlock = getGlobalProceduresBlock();
|
|
13813
13914
|
return `${globalBlock}${rolePrompt}
|
|
13814
13915
|
${BASE_OPERATING_PROCEDURES}`;
|
|
@@ -14416,7 +14517,7 @@ __export(exe_rename_exports, {
|
|
|
14416
14517
|
main: () => main2,
|
|
14417
14518
|
renameEmployee: () => renameEmployee
|
|
14418
14519
|
});
|
|
14419
|
-
import { readFileSync as readFileSync21, writeFileSync as writeFileSync17, renameSync as
|
|
14520
|
+
import { readFileSync as readFileSync21, writeFileSync as writeFileSync17, renameSync as renameSync5, unlinkSync as unlinkSync11, existsSync as existsSync25 } from "fs";
|
|
14420
14521
|
import { execSync as execSync11 } from "child_process";
|
|
14421
14522
|
import path31 from "path";
|
|
14422
14523
|
import { homedir as homedir4 } from "os";
|
|
@@ -14445,7 +14546,8 @@ async function renameEmployee(oldName, newName, opts = {}) {
|
|
|
14445
14546
|
const originalName = employee.name;
|
|
14446
14547
|
const originalPrompt = employee.systemPrompt;
|
|
14447
14548
|
employee.name = newName;
|
|
14448
|
-
employee.systemPrompt = personalizePrompt(originalPrompt, rosterOldName, newName);
|
|
14549
|
+
if (originalPrompt) employee.systemPrompt = personalizePrompt(originalPrompt, rosterOldName, newName);
|
|
14550
|
+
else delete employee.systemPrompt;
|
|
14449
14551
|
await saveEmployees(employees, rosterPath);
|
|
14450
14552
|
rollbackStack.push({
|
|
14451
14553
|
description: "restore roster",
|
|
@@ -14463,14 +14565,14 @@ async function renameEmployee(oldName, newName, opts = {}) {
|
|
|
14463
14565
|
/^(agent_id:\s*)\S+/m,
|
|
14464
14566
|
`$1${newName}`
|
|
14465
14567
|
);
|
|
14466
|
-
|
|
14568
|
+
renameSync5(oldIdentityPath, newIdentityPath);
|
|
14467
14569
|
writeFileSync17(newIdentityPath, updatedContent, "utf-8");
|
|
14468
14570
|
rollbackStack.push({
|
|
14469
14571
|
description: "restore identity file",
|
|
14470
14572
|
undo: () => {
|
|
14471
14573
|
if (existsSync25(newIdentityPath)) {
|
|
14472
14574
|
writeFileSync17(newIdentityPath, content, "utf-8");
|
|
14473
|
-
|
|
14575
|
+
renameSync5(newIdentityPath, oldIdentityPath);
|
|
14474
14576
|
}
|
|
14475
14577
|
}
|
|
14476
14578
|
});
|
|
@@ -14479,12 +14581,12 @@ async function renameEmployee(oldName, newName, opts = {}) {
|
|
|
14479
14581
|
const newAgentPath = path31.join(agentsDir, `${newName}.md`);
|
|
14480
14582
|
if (existsSync25(oldAgentPath)) {
|
|
14481
14583
|
const agentContent = readFileSync21(oldAgentPath, "utf-8");
|
|
14482
|
-
|
|
14584
|
+
renameSync5(oldAgentPath, newAgentPath);
|
|
14483
14585
|
rollbackStack.push({
|
|
14484
14586
|
description: "restore agent file",
|
|
14485
14587
|
undo: () => {
|
|
14486
14588
|
if (existsSync25(newAgentPath)) {
|
|
14487
|
-
|
|
14589
|
+
renameSync5(newAgentPath, oldAgentPath);
|
|
14488
14590
|
writeFileSync17(oldAgentPath, agentContent, "utf-8");
|
|
14489
14591
|
}
|
|
14490
14592
|
}
|
|
@@ -14583,6 +14685,8 @@ async function main2() {
|
|
|
14583
14685
|
process.exit(1);
|
|
14584
14686
|
}
|
|
14585
14687
|
const [oldName, newName] = args2;
|
|
14688
|
+
const { initStore: initStore2 } = await Promise.resolve().then(() => (init_store(), store_exports));
|
|
14689
|
+
await initStore2({ lightweight: true });
|
|
14586
14690
|
const result = await renameEmployee(oldName, newName);
|
|
14587
14691
|
if (!result.success) {
|
|
14588
14692
|
console.error(`Error: ${result.error}`);
|
|
@@ -14611,7 +14715,7 @@ var init_exe_rename = __esm({
|
|
|
14611
14715
|
});
|
|
14612
14716
|
|
|
14613
14717
|
// src/lib/model-downloader.ts
|
|
14614
|
-
import { createWriteStream, createReadStream as createReadStream2, existsSync as existsSync26, unlinkSync as unlinkSync12, renameSync as
|
|
14718
|
+
import { createWriteStream, createReadStream as createReadStream2, existsSync as existsSync26, unlinkSync as unlinkSync12, renameSync as renameSync6 } from "fs";
|
|
14615
14719
|
import { mkdir as mkdir6 } from "fs/promises";
|
|
14616
14720
|
import { createHash as createHash4 } from "crypto";
|
|
14617
14721
|
import path32 from "path";
|
|
@@ -14669,7 +14773,7 @@ async function downloadModel(opts) {
|
|
|
14669
14773
|
`SHA256 mismatch: expected ${EXPECTED_SHA256}, got ${actualHash}`
|
|
14670
14774
|
);
|
|
14671
14775
|
}
|
|
14672
|
-
|
|
14776
|
+
renameSync6(tmpPath, destPath);
|
|
14673
14777
|
return destPath;
|
|
14674
14778
|
} catch (err) {
|
|
14675
14779
|
lastErr = err instanceof Error ? err : new Error(String(err));
|
|
@@ -15473,8 +15577,8 @@ async function validateModel(log) {
|
|
|
15473
15577
|
log("Skipping in-memory model validation (low memory \u2014 will validate on first use).");
|
|
15474
15578
|
const modelPath = path34.join(MODELS_DIR, LOCAL_FILENAME);
|
|
15475
15579
|
if (existsSync28(modelPath)) {
|
|
15476
|
-
const { statSync:
|
|
15477
|
-
const size =
|
|
15580
|
+
const { statSync: statSync5 } = await import("fs");
|
|
15581
|
+
const size = statSync5(modelPath).size;
|
|
15478
15582
|
if (size > 300 * 1e6) {
|
|
15479
15583
|
log(`Model file verified (${(size / 1e6).toFixed(0)} MB).`);
|
|
15480
15584
|
return;
|
|
@@ -15895,15 +15999,9 @@ async function runSetupWizard(opts = {}) {
|
|
|
15895
15999
|
cooName = (cooNameInput || DEFAULT_COORDINATOR_TEMPLATE_NAME2).toLowerCase();
|
|
15896
16000
|
let employees = await loadEmployees2(EMPLOYEES_PATH2).catch(() => []);
|
|
15897
16001
|
if (!employees.some((e) => e.name === cooName)) {
|
|
15898
|
-
const { DEFAULT_EXE: DEFAULT_EXE2, personalizePrompt: personalizePrompt2 } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
15899
16002
|
const cooEmployee = {
|
|
15900
16003
|
name: cooName,
|
|
15901
16004
|
role: "COO",
|
|
15902
|
-
systemPrompt: personalizePrompt2(
|
|
15903
|
-
DEFAULT_EXE2.systemPrompt,
|
|
15904
|
-
DEFAULT_COORDINATOR_TEMPLATE_NAME2,
|
|
15905
|
-
cooName
|
|
15906
|
-
),
|
|
15907
16005
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
15908
16006
|
templateName: DEFAULT_COORDINATOR_TEMPLATE_NAME2,
|
|
15909
16007
|
templateVersion: 1
|
|
@@ -15997,12 +16095,12 @@ async function runSetupWizard(opts = {}) {
|
|
|
15997
16095
|
const ctoNameInput = await ask2(rl, `Name your CTO (default: ${ctoDefault}): `);
|
|
15998
16096
|
const ctoName = (ctoNameInput || ctoDefault).toLowerCase();
|
|
15999
16097
|
if (!employees.some((e) => e.name === ctoName)) {
|
|
16000
|
-
const { personalizePrompt: personalizeCto } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
16001
16098
|
const ctoEmployee = {
|
|
16002
16099
|
name: ctoName,
|
|
16003
16100
|
role: "CTO",
|
|
16004
|
-
|
|
16005
|
-
|
|
16101
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
16102
|
+
templateName: ctoDefault,
|
|
16103
|
+
templateVersion: 1
|
|
16006
16104
|
};
|
|
16007
16105
|
employees = addEmployee2(employees, ctoEmployee);
|
|
16008
16106
|
await saveEmployees2(employees, EMPLOYEES_PATH2);
|
|
@@ -16020,12 +16118,12 @@ async function runSetupWizard(opts = {}) {
|
|
|
16020
16118
|
const cmoNameInput = await ask2(rl, `Name your CMO (default: ${cmoDefault}): `);
|
|
16021
16119
|
const cmoName = (cmoNameInput || cmoDefault).toLowerCase();
|
|
16022
16120
|
if (!employees.some((e) => e.name === cmoName)) {
|
|
16023
|
-
const { personalizePrompt: personalizeCmo } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
16024
16121
|
const cmoEmployee = {
|
|
16025
16122
|
name: cmoName,
|
|
16026
16123
|
role: "CMO",
|
|
16027
|
-
|
|
16028
|
-
|
|
16124
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
16125
|
+
templateName: cmoDefault,
|
|
16126
|
+
templateVersion: 1
|
|
16029
16127
|
};
|
|
16030
16128
|
employees = addEmployee2(employees, cmoEmployee);
|
|
16031
16129
|
await saveEmployees2(employees, EMPLOYEES_PATH2);
|
|
@@ -16465,6 +16563,309 @@ var init_update = __esm({
|
|
|
16465
16563
|
}
|
|
16466
16564
|
});
|
|
16467
16565
|
|
|
16566
|
+
// src/lib/stack-update.ts
|
|
16567
|
+
import { execFileSync as execFileSync3 } from "child_process";
|
|
16568
|
+
import { existsSync as existsSync30, mkdirSync as mkdirSync19, readFileSync as readFileSync25, renameSync as renameSync7, writeFileSync as writeFileSync20 } from "fs";
|
|
16569
|
+
import http from "http";
|
|
16570
|
+
import https from "https";
|
|
16571
|
+
import path37 from "path";
|
|
16572
|
+
function parseStackManifest(raw) {
|
|
16573
|
+
const parsed = JSON.parse(raw);
|
|
16574
|
+
if (parsed.schemaVersion !== 1) throw new Error("Unsupported stack manifest schemaVersion");
|
|
16575
|
+
if (!parsed.latest || !parsed.stacks || typeof parsed.stacks !== "object") {
|
|
16576
|
+
throw new Error("Invalid stack manifest: latest and stacks are required");
|
|
16577
|
+
}
|
|
16578
|
+
for (const [version, release] of Object.entries(parsed.stacks)) {
|
|
16579
|
+
if (!release.version) release.version = version;
|
|
16580
|
+
if (!release.services || typeof release.services !== "object") {
|
|
16581
|
+
throw new Error(`Invalid stack manifest: release ${version} has no services`);
|
|
16582
|
+
}
|
|
16583
|
+
for (const [serviceName, service] of Object.entries(release.services)) {
|
|
16584
|
+
if (!service.image || !service.env) {
|
|
16585
|
+
throw new Error(`Invalid stack manifest: ${version}.${serviceName} requires image and env`);
|
|
16586
|
+
}
|
|
16587
|
+
}
|
|
16588
|
+
}
|
|
16589
|
+
return parsed;
|
|
16590
|
+
}
|
|
16591
|
+
async function loadStackManifest(ref, fetchText = defaultFetchText) {
|
|
16592
|
+
if (/^https?:\/\//.test(ref)) return parseStackManifest(await fetchText(ref));
|
|
16593
|
+
return parseStackManifest(readFileSync25(ref, "utf8"));
|
|
16594
|
+
}
|
|
16595
|
+
function parseEnv(raw) {
|
|
16596
|
+
const env = /* @__PURE__ */ new Map();
|
|
16597
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
16598
|
+
const trimmed = line.trim();
|
|
16599
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
16600
|
+
const idx = line.indexOf("=");
|
|
16601
|
+
if (idx <= 0) continue;
|
|
16602
|
+
env.set(line.slice(0, idx).trim(), line.slice(idx + 1));
|
|
16603
|
+
}
|
|
16604
|
+
return env;
|
|
16605
|
+
}
|
|
16606
|
+
function patchEnv(raw, updates) {
|
|
16607
|
+
const seen = /* @__PURE__ */ new Set();
|
|
16608
|
+
const lines = raw.replace(/\n$/, "").split(/\r?\n/);
|
|
16609
|
+
const patched = lines.map((line) => {
|
|
16610
|
+
const idx = line.indexOf("=");
|
|
16611
|
+
if (idx <= 0 || line.trim().startsWith("#")) return line;
|
|
16612
|
+
const key = line.slice(0, idx).trim();
|
|
16613
|
+
if (!(key in updates)) return line;
|
|
16614
|
+
seen.add(key);
|
|
16615
|
+
return `${key}=${updates[key]}`;
|
|
16616
|
+
});
|
|
16617
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
16618
|
+
if (!seen.has(key)) patched.push(`${key}=${value}`);
|
|
16619
|
+
}
|
|
16620
|
+
return patched.join("\n").replace(/\n*$/, "\n");
|
|
16621
|
+
}
|
|
16622
|
+
function createStackUpdatePlan(manifest, envRaw, targetVersion) {
|
|
16623
|
+
const version = targetVersion ?? manifest.latest;
|
|
16624
|
+
const release = manifest.stacks[version];
|
|
16625
|
+
if (!release) throw new Error(`Stack version ${version} not found in manifest`);
|
|
16626
|
+
const env = parseEnv(envRaw);
|
|
16627
|
+
const changes = [];
|
|
16628
|
+
for (const [serviceName, service] of Object.entries(release.services)) {
|
|
16629
|
+
const before = env.get(service.env);
|
|
16630
|
+
if (before !== service.image) {
|
|
16631
|
+
changes.push({ key: service.env, before, after: service.image, service: serviceName });
|
|
16632
|
+
}
|
|
16633
|
+
}
|
|
16634
|
+
return {
|
|
16635
|
+
manifest,
|
|
16636
|
+
release,
|
|
16637
|
+
targetVersion: version,
|
|
16638
|
+
changes,
|
|
16639
|
+
breakingChanges: release.breakingChanges ?? []
|
|
16640
|
+
};
|
|
16641
|
+
}
|
|
16642
|
+
function assertBreakingChangesAllowed(plan, allowedIds) {
|
|
16643
|
+
const required = plan.breakingChanges.filter((c) => c.requiresConfirmation !== false);
|
|
16644
|
+
const missing = required.filter((c) => !allowedIds.includes(c.id));
|
|
16645
|
+
if (missing.length > 0) {
|
|
16646
|
+
const details = missing.map((c) => `- ${c.id}: ${c.title}
|
|
16647
|
+
${c.description}
|
|
16648
|
+
Action: ${c.requiredAction ?? "Review release notes."}`).join("\n");
|
|
16649
|
+
throw new Error(
|
|
16650
|
+
`Stack ${plan.targetVersion} has breaking changes that require confirmation:
|
|
16651
|
+
${details}
|
|
16652
|
+
Re-run with --allow-breaking ${missing.map((c) => c.id).join(",")}`
|
|
16653
|
+
);
|
|
16654
|
+
}
|
|
16655
|
+
}
|
|
16656
|
+
async function runStackUpdate(options) {
|
|
16657
|
+
const exec2 = options.exec ?? defaultExec;
|
|
16658
|
+
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
16659
|
+
const manifest = await loadStackManifest(options.manifestRef, options.fetchText);
|
|
16660
|
+
const envRaw = readFileSync25(options.envFile, "utf8");
|
|
16661
|
+
const plan = createStackUpdatePlan(manifest, envRaw, options.targetVersion);
|
|
16662
|
+
assertBreakingChangesAllowed(plan, options.allowedBreakingChangeIds ?? []);
|
|
16663
|
+
const lockFile = options.lockFile ?? path37.join(path37.dirname(options.envFile), ".exe-stack-lock.json");
|
|
16664
|
+
if (options.dryRun || plan.changes.length === 0) {
|
|
16665
|
+
return { status: "planned", targetVersion: plan.targetVersion, changes: plan.changes, lockFile };
|
|
16666
|
+
}
|
|
16667
|
+
const backupDir = path37.join(path37.dirname(options.envFile), ".exe-stack-backups");
|
|
16668
|
+
mkdirSync19(backupDir, { recursive: true });
|
|
16669
|
+
const stamp = now().toISOString().replace(/[:.]/g, "-");
|
|
16670
|
+
const backupEnvFile = path37.join(backupDir, `env-${stamp}.bak`);
|
|
16671
|
+
writeFileSync20(backupEnvFile, envRaw, { mode: 384 });
|
|
16672
|
+
const updates = Object.fromEntries(plan.changes.map((c) => [c.key, c.after]));
|
|
16673
|
+
const patched = patchEnv(envRaw, updates);
|
|
16674
|
+
const tmp = `${options.envFile}.tmp-${process.pid}`;
|
|
16675
|
+
writeFileSync20(tmp, patched, { mode: 384 });
|
|
16676
|
+
renameSync7(tmp, options.envFile);
|
|
16677
|
+
const composeArgs = ["compose", "--file", options.composeFile, "--env-file", options.envFile];
|
|
16678
|
+
try {
|
|
16679
|
+
exec2("docker", [...composeArgs, "pull"]);
|
|
16680
|
+
exec2("docker", [...composeArgs, "up", "-d"]);
|
|
16681
|
+
await verifyReleaseHealth(plan.release, options.healthRetries ?? 12, options.healthDelayMs ?? 5e3);
|
|
16682
|
+
writeFileSync20(lockFile, JSON.stringify({ stackVersion: plan.targetVersion, updatedAt: now().toISOString(), services: plan.release.services }, null, 2) + "\n");
|
|
16683
|
+
return { status: "updated", targetVersion: plan.targetVersion, changes: plan.changes, backupEnvFile, lockFile };
|
|
16684
|
+
} catch (err) {
|
|
16685
|
+
writeFileSync20(options.envFile, envRaw, { mode: 384 });
|
|
16686
|
+
try {
|
|
16687
|
+
exec2("docker", [...composeArgs, "up", "-d"]);
|
|
16688
|
+
} catch {
|
|
16689
|
+
}
|
|
16690
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
16691
|
+
throw new Error(`Stack update failed and rollback was attempted: ${reason}`);
|
|
16692
|
+
}
|
|
16693
|
+
}
|
|
16694
|
+
async function verifyReleaseHealth(release, retries, delayMs) {
|
|
16695
|
+
for (const [serviceName, service] of Object.entries(release.services)) {
|
|
16696
|
+
if (!service.healthUrl) continue;
|
|
16697
|
+
await waitForHttpOk(service.healthUrl, retries, delayMs, serviceName);
|
|
16698
|
+
}
|
|
16699
|
+
}
|
|
16700
|
+
async function waitForHttpOk(url, retries, delayMs, label) {
|
|
16701
|
+
let last = "";
|
|
16702
|
+
for (let i = 0; i < retries; i++) {
|
|
16703
|
+
try {
|
|
16704
|
+
const status = await httpStatus(url);
|
|
16705
|
+
if (status >= 200 && status < 300) return;
|
|
16706
|
+
last = `HTTP ${status}`;
|
|
16707
|
+
} catch (err) {
|
|
16708
|
+
last = err instanceof Error ? err.message : String(err);
|
|
16709
|
+
}
|
|
16710
|
+
if (i < retries - 1) await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
16711
|
+
}
|
|
16712
|
+
throw new Error(`Health check failed for ${label} (${url}): ${last}`);
|
|
16713
|
+
}
|
|
16714
|
+
function httpStatus(urlString) {
|
|
16715
|
+
return new Promise((resolve, reject) => {
|
|
16716
|
+
const url = new URL(urlString);
|
|
16717
|
+
const mod = url.protocol === "https:" ? https : http;
|
|
16718
|
+
const req = mod.request(url, { method: "GET", timeout: 5e3 }, (res) => {
|
|
16719
|
+
res.resume();
|
|
16720
|
+
resolve(res.statusCode ?? 0);
|
|
16721
|
+
});
|
|
16722
|
+
req.on("timeout", () => req.destroy(new Error("timeout")));
|
|
16723
|
+
req.on("error", reject);
|
|
16724
|
+
req.end();
|
|
16725
|
+
});
|
|
16726
|
+
}
|
|
16727
|
+
function defaultExec(cmd, args2, opts) {
|
|
16728
|
+
execFileSync3(cmd, args2, { stdio: "inherit", cwd: opts?.cwd });
|
|
16729
|
+
}
|
|
16730
|
+
async function defaultFetchText(ref) {
|
|
16731
|
+
const res = await fetch(ref);
|
|
16732
|
+
if (!res.ok) throw new Error(`Failed to fetch ${ref}: HTTP ${res.status}`);
|
|
16733
|
+
return res.text();
|
|
16734
|
+
}
|
|
16735
|
+
function defaultStackPaths() {
|
|
16736
|
+
const cwdCompose = path37.resolve("docker-compose.yml");
|
|
16737
|
+
const cwdEnv = path37.resolve(".env");
|
|
16738
|
+
return {
|
|
16739
|
+
composeFile: process.env.EXE_STACK_COMPOSE_FILE || (existsSync30(cwdCompose) ? cwdCompose : "/opt/exe-stack/docker-compose.yml"),
|
|
16740
|
+
envFile: process.env.EXE_STACK_ENV_FILE || (existsSync30(cwdEnv) ? cwdEnv : "/opt/exe-stack/.env"),
|
|
16741
|
+
manifestRef: process.env.EXE_STACK_MANIFEST || "https://updates.askexe.com/stack-manifest.json"
|
|
16742
|
+
};
|
|
16743
|
+
}
|
|
16744
|
+
var init_stack_update = __esm({
|
|
16745
|
+
"src/lib/stack-update.ts"() {
|
|
16746
|
+
"use strict";
|
|
16747
|
+
}
|
|
16748
|
+
});
|
|
16749
|
+
|
|
16750
|
+
// src/bin/stack-update.ts
|
|
16751
|
+
var stack_update_exports = {};
|
|
16752
|
+
__export(stack_update_exports, {
|
|
16753
|
+
runStackUpdateCli: () => main3
|
|
16754
|
+
});
|
|
16755
|
+
import { readFileSync as readFileSync26 } from "fs";
|
|
16756
|
+
function parseArgs4(args2) {
|
|
16757
|
+
const defaults = defaultStackPaths();
|
|
16758
|
+
const opts = {
|
|
16759
|
+
manifestRef: defaults.manifestRef,
|
|
16760
|
+
composeFile: defaults.composeFile,
|
|
16761
|
+
envFile: defaults.envFile,
|
|
16762
|
+
dryRun: false,
|
|
16763
|
+
check: false,
|
|
16764
|
+
yes: false,
|
|
16765
|
+
allowedBreakingChangeIds: []
|
|
16766
|
+
};
|
|
16767
|
+
for (let i = 0; i < args2.length; i++) {
|
|
16768
|
+
const arg = args2[i];
|
|
16769
|
+
const next = () => args2[++i] ?? "";
|
|
16770
|
+
if (arg === "--manifest") opts.manifestRef = next();
|
|
16771
|
+
else if (arg.startsWith("--manifest=")) opts.manifestRef = arg.split("=").slice(1).join("=");
|
|
16772
|
+
else if (arg === "--target") opts.targetVersion = next();
|
|
16773
|
+
else if (arg.startsWith("--target=")) opts.targetVersion = arg.split("=")[1];
|
|
16774
|
+
else if (arg === "--compose-file") opts.composeFile = next();
|
|
16775
|
+
else if (arg.startsWith("--compose-file=")) opts.composeFile = arg.split("=").slice(1).join("=");
|
|
16776
|
+
else if (arg === "--env-file") opts.envFile = next();
|
|
16777
|
+
else if (arg.startsWith("--env-file=")) opts.envFile = arg.split("=").slice(1).join("=");
|
|
16778
|
+
else if (arg === "--lock-file") opts.lockFile = next();
|
|
16779
|
+
else if (arg === "--dry-run") opts.dryRun = true;
|
|
16780
|
+
else if (arg === "--check") opts.check = true;
|
|
16781
|
+
else if (arg === "--yes" || arg === "-y") opts.yes = true;
|
|
16782
|
+
else if (arg === "--allow-breaking") opts.allowedBreakingChangeIds.push(...next().split(",").map((s) => s.trim()).filter(Boolean));
|
|
16783
|
+
else if (arg.startsWith("--allow-breaking=")) opts.allowedBreakingChangeIds.push(...arg.split("=")[1].split(",").map((s) => s.trim()).filter(Boolean));
|
|
16784
|
+
else if (arg === "--help" || arg === "-h") {
|
|
16785
|
+
printHelp();
|
|
16786
|
+
process.exit(0);
|
|
16787
|
+
} else {
|
|
16788
|
+
throw new Error(`Unknown option: ${arg}`);
|
|
16789
|
+
}
|
|
16790
|
+
}
|
|
16791
|
+
return opts;
|
|
16792
|
+
}
|
|
16793
|
+
function printHelp() {
|
|
16794
|
+
console.log(`exe-os stack-update \u2014 update a self-hosted Exe OS stack from a pinned manifest
|
|
16795
|
+
|
|
16796
|
+
Usage:
|
|
16797
|
+
exe-os stack-update [--manifest <path-or-url>] [--target <version>] [--yes]
|
|
16798
|
+
|
|
16799
|
+
Options:
|
|
16800
|
+
--manifest <ref> Stack manifest JSON path or URL (default: updates.askexe.com)
|
|
16801
|
+
--target <version> Stack version to install (default: manifest.latest)
|
|
16802
|
+
--compose-file <path> docker-compose.yml path (default: ./docker-compose.yml or /opt/exe-stack/docker-compose.yml)
|
|
16803
|
+
--env-file <path> .env path (default: ./.env or /opt/exe-stack/.env)
|
|
16804
|
+
--lock-file <path> Lock file path (default: beside .env)
|
|
16805
|
+
--check Print available changes only
|
|
16806
|
+
--dry-run Plan only; do not run Docker
|
|
16807
|
+
--allow-breaking <ids> Confirm breaking changes, comma-separated
|
|
16808
|
+
-y, --yes Non-interactive confirmation
|
|
16809
|
+
`);
|
|
16810
|
+
}
|
|
16811
|
+
function printChanges(changes) {
|
|
16812
|
+
if (changes.length === 0) {
|
|
16813
|
+
console.log("\u2705 Stack already matches target manifest.");
|
|
16814
|
+
return;
|
|
16815
|
+
}
|
|
16816
|
+
console.log("Planned image tag changes:");
|
|
16817
|
+
for (const c of changes) {
|
|
16818
|
+
console.log(` - ${c.service}: ${c.key}`);
|
|
16819
|
+
console.log(` ${c.before ?? "<unset>"} \u2192 ${c.after}`);
|
|
16820
|
+
}
|
|
16821
|
+
}
|
|
16822
|
+
function printBreaking(changes) {
|
|
16823
|
+
if (changes.length === 0) return;
|
|
16824
|
+
console.log("\nBreaking-change notices:");
|
|
16825
|
+
for (const c of changes) {
|
|
16826
|
+
console.log(` - ${c.id}: ${c.title}`);
|
|
16827
|
+
console.log(` ${c.description}`);
|
|
16828
|
+
if (c.requiredAction) console.log(` Action: ${c.requiredAction}`);
|
|
16829
|
+
if (c.expectedDowntimeMinutes) console.log(` Expected downtime: ${c.expectedDowntimeMinutes} minutes`);
|
|
16830
|
+
}
|
|
16831
|
+
}
|
|
16832
|
+
async function main3() {
|
|
16833
|
+
const opts = parseArgs4(process.argv.slice(2));
|
|
16834
|
+
const manifest = await loadStackManifest(opts.manifestRef);
|
|
16835
|
+
const envRaw = readFileSync26(opts.envFile, "utf8");
|
|
16836
|
+
const plan = createStackUpdatePlan(manifest, envRaw, opts.targetVersion);
|
|
16837
|
+
console.log(`Exe OS stack target: ${plan.targetVersion}`);
|
|
16838
|
+
console.log(`Manifest: ${opts.manifestRef}`);
|
|
16839
|
+
console.log(`Compose: ${opts.composeFile}`);
|
|
16840
|
+
console.log(`Env: ${opts.envFile}
|
|
16841
|
+
`);
|
|
16842
|
+
printChanges(plan.changes);
|
|
16843
|
+
printBreaking(plan.breakingChanges);
|
|
16844
|
+
if (opts.check || opts.dryRun) return;
|
|
16845
|
+
if (!opts.yes) {
|
|
16846
|
+
console.error("\nRefusing to update without --yes. Re-run with --yes after reviewing the plan.");
|
|
16847
|
+
process.exit(2);
|
|
16848
|
+
}
|
|
16849
|
+
const result = await runStackUpdate(opts);
|
|
16850
|
+
console.log(`
|
|
16851
|
+
\u2705 Stack ${result.status}: ${result.targetVersion}`);
|
|
16852
|
+
if (result.backupEnvFile) console.log(`Backup env: ${result.backupEnvFile}`);
|
|
16853
|
+
console.log(`Lock file: ${result.lockFile}`);
|
|
16854
|
+
}
|
|
16855
|
+
var init_stack_update2 = __esm({
|
|
16856
|
+
"src/bin/stack-update.ts"() {
|
|
16857
|
+
"use strict";
|
|
16858
|
+
init_is_main();
|
|
16859
|
+
init_stack_update();
|
|
16860
|
+
if (isMainModule(import.meta.url)) {
|
|
16861
|
+
main3().catch((err) => {
|
|
16862
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
16863
|
+
process.exit(1);
|
|
16864
|
+
});
|
|
16865
|
+
}
|
|
16866
|
+
}
|
|
16867
|
+
});
|
|
16868
|
+
|
|
16468
16869
|
// node_modules/es-toolkit/dist/function/debounce.mjs
|
|
16469
16870
|
function debounce(func, debounceMs, { signal, edges } = {}) {
|
|
16470
16871
|
let pendingThis = void 0;
|
|
@@ -18266,7 +18667,7 @@ var init_src = __esm({
|
|
|
18266
18667
|
|
|
18267
18668
|
// node_modules/terminal-size/index.js
|
|
18268
18669
|
import process2 from "process";
|
|
18269
|
-
import { execFileSync as
|
|
18670
|
+
import { execFileSync as execFileSync4 } from "child_process";
|
|
18270
18671
|
import fs from "fs";
|
|
18271
18672
|
import tty from "tty";
|
|
18272
18673
|
function terminalSize() {
|
|
@@ -18298,7 +18699,7 @@ var init_terminal_size = __esm({
|
|
|
18298
18699
|
"use strict";
|
|
18299
18700
|
defaultColumns = 80;
|
|
18300
18701
|
defaultRows = 24;
|
|
18301
|
-
exec = (command, arguments_, { shell, env } = {}) =>
|
|
18702
|
+
exec = (command, arguments_, { shell, env } = {}) => execFileSync4(command, arguments_, {
|
|
18302
18703
|
encoding: "utf8",
|
|
18303
18704
|
stdio: ["ignore", "pipe", "ignore"],
|
|
18304
18705
|
timeout: 500,
|
|
@@ -20823,8 +21224,8 @@ var init_ErrorOverview = __esm({
|
|
|
20823
21224
|
"use strict";
|
|
20824
21225
|
init_Box();
|
|
20825
21226
|
init_Text();
|
|
20826
|
-
cleanupPath = (
|
|
20827
|
-
return
|
|
21227
|
+
cleanupPath = (path49) => {
|
|
21228
|
+
return path49?.replace(`file://${cwd()}/`, "");
|
|
20828
21229
|
};
|
|
20829
21230
|
stackUtils = new StackUtils({
|
|
20830
21231
|
cwd: cwd(),
|
|
@@ -23232,11 +23633,11 @@ function Footer() {
|
|
|
23232
23633
|
} catch {
|
|
23233
23634
|
}
|
|
23234
23635
|
try {
|
|
23235
|
-
const { existsSync:
|
|
23636
|
+
const { existsSync: existsSync35 } = await import("fs");
|
|
23236
23637
|
const { join } = await import("path");
|
|
23237
23638
|
const home = process.env.HOME ?? "";
|
|
23238
23639
|
const pidPath = join(home, ".exe-os", "exed.pid");
|
|
23239
|
-
setDaemon(
|
|
23640
|
+
setDaemon(existsSync35(pidPath) ? "running" : "stopped");
|
|
23240
23641
|
} catch {
|
|
23241
23642
|
setDaemon("unknown");
|
|
23242
23643
|
}
|
|
@@ -25287,10 +25688,10 @@ var init_hooks = __esm({
|
|
|
25287
25688
|
});
|
|
25288
25689
|
|
|
25289
25690
|
// src/runtime/safety-checks.ts
|
|
25290
|
-
import
|
|
25691
|
+
import path38 from "path";
|
|
25291
25692
|
import os18 from "os";
|
|
25292
25693
|
function checkPathSafety(filePath) {
|
|
25293
|
-
const resolved =
|
|
25694
|
+
const resolved = path38.resolve(filePath);
|
|
25294
25695
|
for (const { pattern, reason } of BYPASS_IMMUNE_PATTERNS) {
|
|
25295
25696
|
const matches = typeof pattern === "function" ? pattern(resolved) : pattern.test(resolved);
|
|
25296
25697
|
if (matches) {
|
|
@@ -25300,7 +25701,7 @@ function checkPathSafety(filePath) {
|
|
|
25300
25701
|
return { safe: true, bypassImmune: true };
|
|
25301
25702
|
}
|
|
25302
25703
|
function checkReadPathSafety(filePath) {
|
|
25303
|
-
const resolved =
|
|
25704
|
+
const resolved = path38.resolve(filePath);
|
|
25304
25705
|
const credPatterns = BYPASS_IMMUNE_PATTERNS.filter(
|
|
25305
25706
|
(p) => typeof p.pattern !== "function" && (p.reason.includes("secrets") || p.reason.includes("Private key") || p.reason.includes("Credential"))
|
|
25306
25707
|
);
|
|
@@ -25326,11 +25727,11 @@ var init_safety_checks = __esm({
|
|
|
25326
25727
|
reason: "Git config can set hooks and command execution"
|
|
25327
25728
|
},
|
|
25328
25729
|
{
|
|
25329
|
-
pattern: (p) => p.startsWith(
|
|
25730
|
+
pattern: (p) => p.startsWith(path38.join(HOME, ".claude")),
|
|
25330
25731
|
reason: "Claude configuration files are protected"
|
|
25331
25732
|
},
|
|
25332
25733
|
{
|
|
25333
|
-
pattern: (p) => p.startsWith(
|
|
25734
|
+
pattern: (p) => p.startsWith(path38.join(HOME, ".exe-os")),
|
|
25334
25735
|
reason: "exe-os configuration files are protected"
|
|
25335
25736
|
},
|
|
25336
25737
|
{
|
|
@@ -25347,7 +25748,7 @@ var init_safety_checks = __esm({
|
|
|
25347
25748
|
},
|
|
25348
25749
|
{
|
|
25349
25750
|
pattern: (p) => {
|
|
25350
|
-
const name =
|
|
25751
|
+
const name = path38.basename(p);
|
|
25351
25752
|
return [".bashrc", ".zshrc", ".profile", ".bash_profile", ".zprofile", ".zshenv"].includes(name);
|
|
25352
25753
|
},
|
|
25353
25754
|
reason: "Shell configuration files can execute arbitrary code on login"
|
|
@@ -25374,7 +25775,7 @@ __export(file_read_exports, {
|
|
|
25374
25775
|
FileReadTool: () => FileReadTool
|
|
25375
25776
|
});
|
|
25376
25777
|
import fs3 from "fs/promises";
|
|
25377
|
-
import
|
|
25778
|
+
import path39 from "path";
|
|
25378
25779
|
import { z } from "zod";
|
|
25379
25780
|
function isBinary(buf) {
|
|
25380
25781
|
for (let i = 0; i < buf.length; i++) {
|
|
@@ -25410,7 +25811,7 @@ var init_file_read = __esm({
|
|
|
25410
25811
|
return { behavior: "allow" };
|
|
25411
25812
|
},
|
|
25412
25813
|
async call(input, context) {
|
|
25413
|
-
const filePath =
|
|
25814
|
+
const filePath = path39.isAbsolute(input.file_path) ? input.file_path : path39.resolve(context.cwd, input.file_path);
|
|
25414
25815
|
let stat2;
|
|
25415
25816
|
try {
|
|
25416
25817
|
stat2 = await fs3.stat(filePath);
|
|
@@ -25450,7 +25851,7 @@ __export(glob_exports, {
|
|
|
25450
25851
|
GlobTool: () => GlobTool
|
|
25451
25852
|
});
|
|
25452
25853
|
import fs4 from "fs/promises";
|
|
25453
|
-
import
|
|
25854
|
+
import path40 from "path";
|
|
25454
25855
|
import { z as z2 } from "zod";
|
|
25455
25856
|
async function walkDir(dir, maxDepth = 10) {
|
|
25456
25857
|
const results = [];
|
|
@@ -25466,7 +25867,7 @@ async function walkDir(dir, maxDepth = 10) {
|
|
|
25466
25867
|
if (entry.isDirectory() && (entry.name === "node_modules" || entry.name === ".git")) {
|
|
25467
25868
|
continue;
|
|
25468
25869
|
}
|
|
25469
|
-
const fullPath =
|
|
25870
|
+
const fullPath = path40.join(current, entry.name);
|
|
25470
25871
|
if (entry.isDirectory()) {
|
|
25471
25872
|
await walk(fullPath, depth + 1);
|
|
25472
25873
|
} else {
|
|
@@ -25500,11 +25901,11 @@ var init_glob = __esm({
|
|
|
25500
25901
|
inputSchema: inputSchema2,
|
|
25501
25902
|
isReadOnly: true,
|
|
25502
25903
|
async call(input, context) {
|
|
25503
|
-
const baseDir = input.path ?
|
|
25904
|
+
const baseDir = input.path ? path40.isAbsolute(input.path) ? input.path : path40.resolve(context.cwd, input.path) : context.cwd;
|
|
25504
25905
|
try {
|
|
25505
25906
|
const entries = await walkDir(baseDir);
|
|
25506
25907
|
const matched = entries.filter(
|
|
25507
|
-
(e) => simpleGlobMatch(
|
|
25908
|
+
(e) => simpleGlobMatch(path40.relative(baseDir, e.path), input.pattern)
|
|
25508
25909
|
);
|
|
25509
25910
|
matched.sort((a, b) => b.mtime - a.mtime);
|
|
25510
25911
|
if (matched.length === 0) {
|
|
@@ -25530,7 +25931,7 @@ __export(grep_exports, {
|
|
|
25530
25931
|
});
|
|
25531
25932
|
import { spawn as spawn2 } from "child_process";
|
|
25532
25933
|
import fs5 from "fs/promises";
|
|
25533
|
-
import
|
|
25934
|
+
import path41 from "path";
|
|
25534
25935
|
import { z as z3 } from "zod";
|
|
25535
25936
|
function runRipgrep(input, searchPath, context) {
|
|
25536
25937
|
return new Promise((resolve, reject) => {
|
|
@@ -25584,7 +25985,7 @@ async function nodeGrep(input, searchPath) {
|
|
|
25584
25985
|
}
|
|
25585
25986
|
for (const entry of entries) {
|
|
25586
25987
|
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
25587
|
-
const fullPath =
|
|
25988
|
+
const fullPath = path41.join(dir, entry.name);
|
|
25588
25989
|
if (entry.isDirectory()) {
|
|
25589
25990
|
await walk(fullPath);
|
|
25590
25991
|
} else {
|
|
@@ -25630,7 +26031,7 @@ var init_grep = __esm({
|
|
|
25630
26031
|
inputSchema: inputSchema3,
|
|
25631
26032
|
isReadOnly: true,
|
|
25632
26033
|
async call(input, context) {
|
|
25633
|
-
const searchPath = input.path ?
|
|
26034
|
+
const searchPath = input.path ? path41.isAbsolute(input.path) ? input.path : path41.resolve(context.cwd, input.path) : context.cwd;
|
|
25634
26035
|
try {
|
|
25635
26036
|
const result = await runRipgrep(input, searchPath, context);
|
|
25636
26037
|
return result;
|
|
@@ -25655,7 +26056,7 @@ __export(file_write_exports, {
|
|
|
25655
26056
|
FileWriteTool: () => FileWriteTool
|
|
25656
26057
|
});
|
|
25657
26058
|
import fs6 from "fs/promises";
|
|
25658
|
-
import
|
|
26059
|
+
import path42 from "path";
|
|
25659
26060
|
import { z as z4 } from "zod";
|
|
25660
26061
|
var inputSchema4, FileWriteTool;
|
|
25661
26062
|
var init_file_write = __esm({
|
|
@@ -25683,8 +26084,8 @@ var init_file_write = __esm({
|
|
|
25683
26084
|
return { behavior: "allow" };
|
|
25684
26085
|
},
|
|
25685
26086
|
async call(input, context) {
|
|
25686
|
-
const filePath =
|
|
25687
|
-
const dir =
|
|
26087
|
+
const filePath = path42.isAbsolute(input.file_path) ? input.file_path : path42.resolve(context.cwd, input.file_path);
|
|
26088
|
+
const dir = path42.dirname(filePath);
|
|
25688
26089
|
await fs6.mkdir(dir, { recursive: true });
|
|
25689
26090
|
await fs6.writeFile(filePath, input.content, "utf-8");
|
|
25690
26091
|
return {
|
|
@@ -25702,7 +26103,7 @@ __export(file_edit_exports, {
|
|
|
25702
26103
|
FileEditTool: () => FileEditTool
|
|
25703
26104
|
});
|
|
25704
26105
|
import fs7 from "fs/promises";
|
|
25705
|
-
import
|
|
26106
|
+
import path43 from "path";
|
|
25706
26107
|
import { z as z5 } from "zod";
|
|
25707
26108
|
function countOccurrences(haystack, needle) {
|
|
25708
26109
|
let count = 0;
|
|
@@ -25743,7 +26144,7 @@ var init_file_edit = __esm({
|
|
|
25743
26144
|
return { behavior: "allow" };
|
|
25744
26145
|
},
|
|
25745
26146
|
async call(input, context) {
|
|
25746
|
-
const filePath =
|
|
26147
|
+
const filePath = path43.isAbsolute(input.file_path) ? input.file_path : path43.resolve(context.cwd, input.file_path);
|
|
25747
26148
|
let content;
|
|
25748
26149
|
try {
|
|
25749
26150
|
content = await fs7.readFile(filePath, "utf-8");
|
|
@@ -25985,7 +26386,7 @@ var init_bash = __esm({
|
|
|
25985
26386
|
// src/tui/views/CommandCenter.tsx
|
|
25986
26387
|
import { useState as useState6, useEffect as useEffect8, useMemo as useMemo4, useCallback as useCallback4, useRef as useRef4 } from "react";
|
|
25987
26388
|
import TextInput from "ink-text-input";
|
|
25988
|
-
import
|
|
26389
|
+
import path44 from "path";
|
|
25989
26390
|
import { homedir as homedir6 } from "os";
|
|
25990
26391
|
import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
25991
26392
|
function CommandCenterView({
|
|
@@ -26020,15 +26421,15 @@ function CommandCenterView({
|
|
|
26020
26421
|
const { createPermissionsFromPreset: createPermissionsFromPreset2, EMPLOYEE_PERMISSIONS: EMPLOYEE_PERMISSIONS2 } = await Promise.resolve().then(() => (init_permissions(), permissions_exports));
|
|
26021
26422
|
const { getPresetByRole: getPresetByRole2 } = await Promise.resolve().then(() => (init_permission_presets(), permission_presets_exports));
|
|
26022
26423
|
const { createDefaultHooks: createDefaultHooks2 } = await Promise.resolve().then(() => (init_hooks(), hooks_exports));
|
|
26023
|
-
const { readFileSync:
|
|
26424
|
+
const { readFileSync: readFileSync30, existsSync: existsSync35 } = await import("fs");
|
|
26024
26425
|
const { join } = await import("path");
|
|
26025
26426
|
const { homedir: homedir8 } = await import("os");
|
|
26026
26427
|
const configPath = join(homedir8(), ".exe-os", "config.json");
|
|
26027
26428
|
let failoverChain = ["anthropic", "opencode", "gemini", "openai"];
|
|
26028
26429
|
let providerConfigs = {};
|
|
26029
|
-
if (
|
|
26430
|
+
if (existsSync35(configPath)) {
|
|
26030
26431
|
try {
|
|
26031
|
-
const raw = JSON.parse(
|
|
26432
|
+
const raw = JSON.parse(readFileSync30(configPath, "utf8"));
|
|
26032
26433
|
if (Array.isArray(raw.failoverChain)) failoverChain = raw.failoverChain;
|
|
26033
26434
|
if (raw.providers && typeof raw.providers === "object") {
|
|
26034
26435
|
providerConfigs = raw.providers;
|
|
@@ -26089,7 +26490,7 @@ function CommandCenterView({
|
|
|
26089
26490
|
const markerDir = join(homedir8(), ".exe-os", "session-cache");
|
|
26090
26491
|
const agentFiles = (await import("fs")).readdirSync(markerDir).filter((f) => f.startsWith("active-agent-"));
|
|
26091
26492
|
for (const f of agentFiles) {
|
|
26092
|
-
const data = JSON.parse(
|
|
26493
|
+
const data = JSON.parse(readFileSync30(join(markerDir, f), "utf8"));
|
|
26093
26494
|
if (data.agentRole) {
|
|
26094
26495
|
agentRole = data.agentRole;
|
|
26095
26496
|
break;
|
|
@@ -26234,7 +26635,7 @@ function CommandCenterView({
|
|
|
26234
26635
|
const demoEntries = DEMO_PROJECTS.map((p) => ({
|
|
26235
26636
|
projectName: p.projectName,
|
|
26236
26637
|
exeSession: p.exeSession,
|
|
26237
|
-
projectDir:
|
|
26638
|
+
projectDir: path44.join(homedir6(), p.projectName),
|
|
26238
26639
|
employeeCount: p.employees.length,
|
|
26239
26640
|
activeCount: p.employees.filter((e) => e.status === "active").length,
|
|
26240
26641
|
memoryCount: p.employees.length * 4e3,
|
|
@@ -26272,7 +26673,7 @@ function CommandCenterView({
|
|
|
26272
26673
|
const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session_registry(), session_registry_exports));
|
|
26273
26674
|
const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
|
|
26274
26675
|
const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
26275
|
-
const { existsSync:
|
|
26676
|
+
const { existsSync: existsSync35 } = await import("fs");
|
|
26276
26677
|
const { join } = await import("path");
|
|
26277
26678
|
const client = getClient2();
|
|
26278
26679
|
if (!client) {
|
|
@@ -26343,7 +26744,7 @@ function CommandCenterView({
|
|
|
26343
26744
|
}
|
|
26344
26745
|
const memoryCount = memoryCounts.get(name) ?? 0;
|
|
26345
26746
|
const openTaskCount = openTaskCounts.get(name) ?? 0;
|
|
26346
|
-
const hasGit = projectDir ?
|
|
26747
|
+
const hasGit = projectDir ? existsSync35(join(projectDir, ".git")) : false;
|
|
26347
26748
|
const type = hasGit ? "code" : memoryCount > 0 ? "code" : "automation";
|
|
26348
26749
|
projectList.push({
|
|
26349
26750
|
projectName: name,
|
|
@@ -26368,7 +26769,7 @@ function CommandCenterView({
|
|
|
26368
26769
|
setHealth((h) => ({ ...h, memories: Number(totalResult.rows[0]?.cnt ?? 0) }));
|
|
26369
26770
|
try {
|
|
26370
26771
|
const pidPath = join(process.env.HOME ?? "", ".exe-os", "exed.pid");
|
|
26371
|
-
setHealth((h) => ({ ...h, daemon:
|
|
26772
|
+
setHealth((h) => ({ ...h, daemon: existsSync35(pidPath) ? "running" : "stopped" }));
|
|
26372
26773
|
} catch {
|
|
26373
26774
|
}
|
|
26374
26775
|
const activityResult = await client.execute(
|
|
@@ -27238,7 +27639,7 @@ var init_useOrchestrator = __esm({
|
|
|
27238
27639
|
|
|
27239
27640
|
// src/tui/views/Sessions.tsx
|
|
27240
27641
|
import React19, { useState as useState9, useEffect as useEffect11, useCallback as useCallback6 } from "react";
|
|
27241
|
-
import
|
|
27642
|
+
import path45 from "path";
|
|
27242
27643
|
import { homedir as homedir7 } from "os";
|
|
27243
27644
|
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
27244
27645
|
function isCoordinatorEntry(entry) {
|
|
@@ -27276,7 +27677,7 @@ function SessionsView({
|
|
|
27276
27677
|
if (demo) {
|
|
27277
27678
|
setProjects(DEMO_PROJECTS.map((p) => ({
|
|
27278
27679
|
...p,
|
|
27279
|
-
projectDir:
|
|
27680
|
+
projectDir: path45.join(homedir7(), p.projectName),
|
|
27280
27681
|
employees: p.employees.map((e) => ({ ...e, attached: e.status === "active" }))
|
|
27281
27682
|
})));
|
|
27282
27683
|
return;
|
|
@@ -28491,12 +28892,12 @@ async function loadGatewayConfig() {
|
|
|
28491
28892
|
state.running = false;
|
|
28492
28893
|
}
|
|
28493
28894
|
try {
|
|
28494
|
-
const { existsSync:
|
|
28895
|
+
const { existsSync: existsSync35, readFileSync: readFileSync30 } = await import("fs");
|
|
28495
28896
|
const { join } = await import("path");
|
|
28496
28897
|
const home = process.env.HOME ?? "";
|
|
28497
28898
|
const configPath = join(home, ".exe-os", "gateway.json");
|
|
28498
|
-
if (
|
|
28499
|
-
const raw = JSON.parse(
|
|
28899
|
+
if (existsSync35(configPath)) {
|
|
28900
|
+
const raw = JSON.parse(readFileSync30(configPath, "utf8"));
|
|
28500
28901
|
state.port = raw.port ?? 3100;
|
|
28501
28902
|
state.gatewayUrl = raw.gatewayUrl ?? "";
|
|
28502
28903
|
if (raw.adapters) {
|
|
@@ -29094,12 +29495,12 @@ function TeamView({ onBack, onViewSessions }) {
|
|
|
29094
29495
|
setMembers(teamData);
|
|
29095
29496
|
setDbError(null);
|
|
29096
29497
|
try {
|
|
29097
|
-
const { existsSync:
|
|
29498
|
+
const { existsSync: existsSync35, readFileSync: readFileSync30 } = await import("fs");
|
|
29098
29499
|
const { join } = await import("path");
|
|
29099
29500
|
const home = process.env.HOME ?? "";
|
|
29100
29501
|
const gatewayConfig = join(home, ".exe-os", "gateway.json");
|
|
29101
|
-
if (
|
|
29102
|
-
const raw = JSON.parse(
|
|
29502
|
+
if (existsSync35(gatewayConfig)) {
|
|
29503
|
+
const raw = JSON.parse(readFileSync30(gatewayConfig, "utf8"));
|
|
29103
29504
|
if (raw.agents && raw.agents.length > 0) {
|
|
29104
29505
|
setExternals(raw.agents.map((a) => ({
|
|
29105
29506
|
name: a.name,
|
|
@@ -29279,8 +29680,8 @@ __export(wiki_client_exports, {
|
|
|
29279
29680
|
listDocuments: () => listDocuments,
|
|
29280
29681
|
listWorkspaces: () => listWorkspaces
|
|
29281
29682
|
});
|
|
29282
|
-
async function wikiFetch(config,
|
|
29283
|
-
const url = `${config.baseUrl}/api/v1${
|
|
29683
|
+
async function wikiFetch(config, path49, method = "GET", body) {
|
|
29684
|
+
const url = `${config.baseUrl}/api/v1${path49}`;
|
|
29284
29685
|
const headers = {
|
|
29285
29686
|
Authorization: `Bearer ${config.apiKey}`,
|
|
29286
29687
|
"Content-Type": "application/json"
|
|
@@ -29313,7 +29714,7 @@ async function wikiFetch(config, path48, method = "GET", body) {
|
|
|
29313
29714
|
}
|
|
29314
29715
|
}
|
|
29315
29716
|
if (!response.ok) {
|
|
29316
|
-
throw new Error(`Wiki API ${method} ${
|
|
29717
|
+
throw new Error(`Wiki API ${method} ${path49}: ${response.status} ${response.statusText}`);
|
|
29317
29718
|
}
|
|
29318
29719
|
return response.json();
|
|
29319
29720
|
} finally {
|
|
@@ -29907,12 +30308,12 @@ function SettingsView({ onBack }) {
|
|
|
29907
30308
|
}
|
|
29908
30309
|
setProviders(providerList);
|
|
29909
30310
|
try {
|
|
29910
|
-
const { existsSync:
|
|
30311
|
+
const { existsSync: existsSync35 } = await import("fs");
|
|
29911
30312
|
const { join } = await import("path");
|
|
29912
30313
|
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
29913
30314
|
const cfg = await loadConfig2();
|
|
29914
30315
|
const home = process.env.HOME ?? "";
|
|
29915
|
-
const hasKey =
|
|
30316
|
+
const hasKey = existsSync35(join(home, ".exe-os", "master.key"));
|
|
29916
30317
|
if (cfg.cloud) {
|
|
29917
30318
|
setCloud({
|
|
29918
30319
|
configured: true,
|
|
@@ -29925,22 +30326,22 @@ function SettingsView({ onBack }) {
|
|
|
29925
30326
|
const pidPath = join(home, ".exe-os", "exed.pid");
|
|
29926
30327
|
let daemon = "unknown";
|
|
29927
30328
|
try {
|
|
29928
|
-
daemon =
|
|
30329
|
+
daemon = existsSync35(pidPath) ? "running" : "stopped";
|
|
29929
30330
|
} catch {
|
|
29930
30331
|
}
|
|
29931
30332
|
let version = "unknown";
|
|
29932
30333
|
try {
|
|
29933
|
-
const { readFileSync:
|
|
30334
|
+
const { readFileSync: readFileSync30 } = await import("fs");
|
|
29934
30335
|
const { createRequire: createRequire3 } = await import("module");
|
|
29935
30336
|
const require2 = createRequire3(import.meta.url);
|
|
29936
30337
|
const pkgPath = require2.resolve("@askexenow/exe-os/package.json");
|
|
29937
|
-
const pkg = JSON.parse(
|
|
30338
|
+
const pkg = JSON.parse(readFileSync30(pkgPath, "utf8"));
|
|
29938
30339
|
version = pkg.version;
|
|
29939
30340
|
} catch {
|
|
29940
30341
|
try {
|
|
29941
|
-
const { readFileSync:
|
|
30342
|
+
const { readFileSync: readFileSync30 } = await import("fs");
|
|
29942
30343
|
const { join: joinPath } = await import("path");
|
|
29943
|
-
const pkg = JSON.parse(
|
|
30344
|
+
const pkg = JSON.parse(readFileSync30(joinPath(process.cwd(), "package.json"), "utf8"));
|
|
29944
30345
|
version = pkg.version;
|
|
29945
30346
|
} catch {
|
|
29946
30347
|
}
|
|
@@ -30741,15 +31142,15 @@ __export(installer_exports2, {
|
|
|
30741
31142
|
verifyOpenCodeHooks: () => verifyOpenCodeHooks
|
|
30742
31143
|
});
|
|
30743
31144
|
import { readFile as readFile7, writeFile as writeFile8, mkdir as mkdir8 } from "fs/promises";
|
|
30744
|
-
import { existsSync as
|
|
30745
|
-
import
|
|
31145
|
+
import { existsSync as existsSync32, readFileSync as readFileSync28 } from "fs";
|
|
31146
|
+
import path46 from "path";
|
|
30746
31147
|
import os19 from "os";
|
|
30747
31148
|
async function registerOpenCodeMcp(packageRoot, homeDir = os19.homedir()) {
|
|
30748
|
-
const configDir =
|
|
30749
|
-
const configPath =
|
|
31149
|
+
const configDir = path46.join(homeDir, ".config", "opencode");
|
|
31150
|
+
const configPath = path46.join(configDir, "opencode.json");
|
|
30750
31151
|
await mkdir8(configDir, { recursive: true });
|
|
30751
31152
|
let config = {};
|
|
30752
|
-
if (
|
|
31153
|
+
if (existsSync32(configPath)) {
|
|
30753
31154
|
try {
|
|
30754
31155
|
config = JSON.parse(await readFile7(configPath, "utf-8"));
|
|
30755
31156
|
} catch {
|
|
@@ -30761,7 +31162,7 @@ async function registerOpenCodeMcp(packageRoot, homeDir = os19.homedir()) {
|
|
|
30761
31162
|
}
|
|
30762
31163
|
const newEntry = {
|
|
30763
31164
|
type: "local",
|
|
30764
|
-
command: ["node",
|
|
31165
|
+
command: ["node", path46.join(packageRoot, "dist", "mcp", "server.js")],
|
|
30765
31166
|
enabled: true
|
|
30766
31167
|
};
|
|
30767
31168
|
const current = config.mcp["exe-os"];
|
|
@@ -30776,14 +31177,14 @@ async function registerOpenCodeMcp(packageRoot, homeDir = os19.homedir()) {
|
|
|
30776
31177
|
return true;
|
|
30777
31178
|
}
|
|
30778
31179
|
async function installOpenCodePlugin(packageRoot, homeDir = os19.homedir()) {
|
|
30779
|
-
const pluginDir =
|
|
30780
|
-
const pluginPath =
|
|
31180
|
+
const pluginDir = path46.join(homeDir, ".config", "opencode", "plugins");
|
|
31181
|
+
const pluginPath = path46.join(pluginDir, "exe-os.mjs");
|
|
30781
31182
|
await mkdir8(pluginDir, { recursive: true });
|
|
30782
31183
|
const pluginContent = PLUGIN_TEMPLATE.replace(
|
|
30783
31184
|
/__PACKAGE_ROOT__/g,
|
|
30784
31185
|
packageRoot.replace(/\\/g, "\\\\")
|
|
30785
31186
|
);
|
|
30786
|
-
if (
|
|
31187
|
+
if (existsSync32(pluginPath)) {
|
|
30787
31188
|
const existing = await readFile7(pluginPath, "utf-8");
|
|
30788
31189
|
if (existing === pluginContent) {
|
|
30789
31190
|
return false;
|
|
@@ -30793,16 +31194,16 @@ async function installOpenCodePlugin(packageRoot, homeDir = os19.homedir()) {
|
|
|
30793
31194
|
return true;
|
|
30794
31195
|
}
|
|
30795
31196
|
function verifyOpenCodeHooks(homeDir = os19.homedir()) {
|
|
30796
|
-
const configPath =
|
|
30797
|
-
const pluginPath =
|
|
30798
|
-
if (!
|
|
31197
|
+
const configPath = path46.join(homeDir, ".config", "opencode", "opencode.json");
|
|
31198
|
+
const pluginPath = path46.join(homeDir, ".config", "opencode", "plugins", "exe-os.mjs");
|
|
31199
|
+
if (!existsSync32(configPath)) return false;
|
|
30799
31200
|
try {
|
|
30800
|
-
const config = JSON.parse(
|
|
31201
|
+
const config = JSON.parse(readFileSync28(configPath, "utf-8"));
|
|
30801
31202
|
if (!config.mcp?.["exe-os"]?.enabled) return false;
|
|
30802
31203
|
} catch {
|
|
30803
31204
|
return false;
|
|
30804
31205
|
}
|
|
30805
|
-
if (!
|
|
31206
|
+
if (!existsSync32(pluginPath)) return false;
|
|
30806
31207
|
return true;
|
|
30807
31208
|
}
|
|
30808
31209
|
async function runOpenCodeInstaller(homeDir) {
|
|
@@ -30837,19 +31238,19 @@ __export(installer_exports3, {
|
|
|
30837
31238
|
verifyCodexHooks: () => verifyCodexHooks
|
|
30838
31239
|
});
|
|
30839
31240
|
import { readFile as readFile8, writeFile as writeFile9, mkdir as mkdir9 } from "fs/promises";
|
|
30840
|
-
import { existsSync as
|
|
30841
|
-
import
|
|
31241
|
+
import { existsSync as existsSync33 } from "fs";
|
|
31242
|
+
import path47 from "path";
|
|
30842
31243
|
import os20 from "os";
|
|
30843
31244
|
async function mergeCodexHooks(packageRoot, homeDir = os20.homedir()) {
|
|
30844
|
-
const codexDir =
|
|
30845
|
-
const hooksPath =
|
|
30846
|
-
const logsDir =
|
|
30847
|
-
const hookLogPath =
|
|
31245
|
+
const codexDir = path47.join(homeDir, ".codex");
|
|
31246
|
+
const hooksPath = path47.join(codexDir, "hooks.json");
|
|
31247
|
+
const logsDir = path47.join(homeDir, ".exe-os", "logs");
|
|
31248
|
+
const hookLogPath = path47.join(logsDir, "hooks.log");
|
|
30848
31249
|
const logSuffix = ` 2>> "${hookLogPath}"`;
|
|
30849
31250
|
await mkdir9(codexDir, { recursive: true });
|
|
30850
31251
|
await mkdir9(logsDir, { recursive: true });
|
|
30851
31252
|
let hooksJson = {};
|
|
30852
|
-
if (
|
|
31253
|
+
if (existsSync33(hooksPath)) {
|
|
30853
31254
|
try {
|
|
30854
31255
|
hooksJson = JSON.parse(await readFile8(hooksPath, "utf-8"));
|
|
30855
31256
|
} catch {
|
|
@@ -30866,7 +31267,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os20.homedir()) {
|
|
|
30866
31267
|
hooks: [
|
|
30867
31268
|
{
|
|
30868
31269
|
type: "command",
|
|
30869
|
-
command: `node "${
|
|
31270
|
+
command: `node "${path47.join(packageRoot, "dist", "hooks", "session-start.js")}"${logSuffix}`,
|
|
30870
31271
|
timeout: 30
|
|
30871
31272
|
}
|
|
30872
31273
|
]
|
|
@@ -30882,7 +31283,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os20.homedir()) {
|
|
|
30882
31283
|
// Combined hook: runs ingest + error-recall in one Node process.
|
|
30883
31284
|
// Eliminates a cold-start cycle per tool call (~3-6s savings on Codex).
|
|
30884
31285
|
type: "command",
|
|
30885
|
-
command: `node "${
|
|
31286
|
+
command: `node "${path47.join(packageRoot, "dist", "hooks", "post-tool-combined.js")}"${logSuffix}`
|
|
30886
31287
|
}
|
|
30887
31288
|
]
|
|
30888
31289
|
},
|
|
@@ -30896,7 +31297,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os20.homedir()) {
|
|
|
30896
31297
|
// Single hook: prompt-submit handles memory retrieval + entity boost.
|
|
30897
31298
|
// exe-heartbeat-hook is CC-specific (intercom) — omitted on Codex.
|
|
30898
31299
|
type: "command",
|
|
30899
|
-
command: `node "${
|
|
31300
|
+
command: `node "${path47.join(packageRoot, "dist", "hooks", "prompt-submit.js")}"${logSuffix}`
|
|
30900
31301
|
}
|
|
30901
31302
|
]
|
|
30902
31303
|
},
|
|
@@ -30908,7 +31309,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os20.homedir()) {
|
|
|
30908
31309
|
hooks: [
|
|
30909
31310
|
{
|
|
30910
31311
|
type: "command",
|
|
30911
|
-
command: `node "${
|
|
31312
|
+
command: `node "${path47.join(packageRoot, "dist", "hooks", "stop.js")}"${logSuffix}`
|
|
30912
31313
|
}
|
|
30913
31314
|
]
|
|
30914
31315
|
},
|
|
@@ -30921,7 +31322,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os20.homedir()) {
|
|
|
30921
31322
|
hooks: [
|
|
30922
31323
|
{
|
|
30923
31324
|
type: "command",
|
|
30924
|
-
command: `node "${
|
|
31325
|
+
command: `node "${path47.join(packageRoot, "dist", "hooks", "pre-tool-use.js")}"${logSuffix}`
|
|
30925
31326
|
}
|
|
30926
31327
|
]
|
|
30927
31328
|
},
|
|
@@ -30953,8 +31354,8 @@ async function mergeCodexHooks(packageRoot, homeDir = os20.homedir()) {
|
|
|
30953
31354
|
return { added, skipped };
|
|
30954
31355
|
}
|
|
30955
31356
|
function verifyCodexHooks(homeDir = os20.homedir()) {
|
|
30956
|
-
const hooksPath =
|
|
30957
|
-
if (!
|
|
31357
|
+
const hooksPath = path47.join(homeDir, ".codex", "hooks.json");
|
|
31358
|
+
if (!existsSync33(hooksPath)) return false;
|
|
30958
31359
|
try {
|
|
30959
31360
|
const hooksJson = JSON.parse(
|
|
30960
31361
|
__require("fs").readFileSync(hooksPath, "utf-8")
|
|
@@ -30977,11 +31378,11 @@ function verifyCodexHooks(homeDir = os20.homedir()) {
|
|
|
30977
31378
|
async function installCodexStatusLine(homeDir = os20.homedir()) {
|
|
30978
31379
|
const prefs = loadPreferences(homeDir);
|
|
30979
31380
|
if (prefs.codexStatusLine === false) return "opted-out";
|
|
30980
|
-
const codexDir =
|
|
30981
|
-
const configPath =
|
|
31381
|
+
const codexDir = path47.join(homeDir, ".codex");
|
|
31382
|
+
const configPath = path47.join(codexDir, "config.toml");
|
|
30982
31383
|
await mkdir9(codexDir, { recursive: true });
|
|
30983
31384
|
let content = "";
|
|
30984
|
-
if (
|
|
31385
|
+
if (existsSync33(configPath)) {
|
|
30985
31386
|
content = await readFile8(configPath, "utf-8");
|
|
30986
31387
|
if (/\[tui\][\s\S]*?status_line\s*=/.test(content)) {
|
|
30987
31388
|
return "already-configured";
|
|
@@ -31039,12 +31440,12 @@ ${desired}
|
|
|
31039
31440
|
return { content: next, changed };
|
|
31040
31441
|
}
|
|
31041
31442
|
async function registerCodexMcpServer(packageRoot, homeDir = os20.homedir()) {
|
|
31042
|
-
const codexDir =
|
|
31043
|
-
const configPath =
|
|
31044
|
-
const serverJsPath =
|
|
31443
|
+
const codexDir = path47.join(homeDir, ".codex");
|
|
31444
|
+
const configPath = path47.join(codexDir, "config.toml");
|
|
31445
|
+
const serverJsPath = path47.join(packageRoot, "dist", "mcp", "server.js");
|
|
31045
31446
|
await mkdir9(codexDir, { recursive: true });
|
|
31046
31447
|
let content = "";
|
|
31047
|
-
if (
|
|
31448
|
+
if (existsSync33(configPath)) {
|
|
31048
31449
|
content = await readFile8(configPath, "utf-8");
|
|
31049
31450
|
}
|
|
31050
31451
|
const sectionHeader = "[mcp_servers.exe-os]";
|
|
@@ -31069,10 +31470,10 @@ async function registerCodexMcpServer(packageRoot, homeDir = os20.homedir()) {
|
|
|
31069
31470
|
return "registered";
|
|
31070
31471
|
}
|
|
31071
31472
|
async function ensureCodexHooksFeature(homeDir = os20.homedir()) {
|
|
31072
|
-
const configPath =
|
|
31073
|
-
await mkdir9(
|
|
31473
|
+
const configPath = path47.join(homeDir, ".codex", "config.toml");
|
|
31474
|
+
await mkdir9(path47.join(homeDir, ".codex"), { recursive: true });
|
|
31074
31475
|
let content = "";
|
|
31075
|
-
if (
|
|
31476
|
+
if (existsSync33(configPath)) {
|
|
31076
31477
|
content = await readFile8(configPath, "utf-8");
|
|
31077
31478
|
}
|
|
31078
31479
|
if (/\[features\][\s\S]*?codex_hooks\s*=\s*true/.test(content)) {
|
|
@@ -31140,14 +31541,14 @@ var init_installer3 = __esm({
|
|
|
31140
31541
|
});
|
|
31141
31542
|
|
|
31142
31543
|
// src/bin/cli.ts
|
|
31143
|
-
import { existsSync as
|
|
31144
|
-
import
|
|
31544
|
+
import { existsSync as existsSync34, readFileSync as readFileSync29, writeFileSync as writeFileSync21, readdirSync as readdirSync10, rmSync } from "fs";
|
|
31545
|
+
import path48 from "path";
|
|
31145
31546
|
import os21 from "os";
|
|
31146
31547
|
var args = process.argv.slice(2);
|
|
31147
31548
|
if (args.includes("--version") || args.includes("-v")) {
|
|
31148
31549
|
try {
|
|
31149
|
-
const pkgPath =
|
|
31150
|
-
const pkg = JSON.parse(
|
|
31550
|
+
const pkgPath = path48.join(path48.dirname(new URL(import.meta.url).pathname), "..", "..", "package.json");
|
|
31551
|
+
const pkg = JSON.parse(readFileSync29(pkgPath, "utf8"));
|
|
31151
31552
|
console.log(pkg.version);
|
|
31152
31553
|
} catch {
|
|
31153
31554
|
console.log("unknown");
|
|
@@ -31306,16 +31707,19 @@ ID: ${result.id}`);
|
|
|
31306
31707
|
} else if (args[0] === "update") {
|
|
31307
31708
|
const { runUpdate: runUpdate2 } = await Promise.resolve().then(() => (init_update(), update_exports));
|
|
31308
31709
|
await runUpdate2(args.slice(1));
|
|
31710
|
+
} else if (args[0] === "stack-update") {
|
|
31711
|
+
const { runStackUpdateCli } = await Promise.resolve().then(() => (init_stack_update2(), stack_update_exports));
|
|
31712
|
+
await runStackUpdateCli();
|
|
31309
31713
|
} else if (args.includes("--tui") || args.includes("--demo") || args[0] === "tui") {
|
|
31310
31714
|
checkForUpdateOnBoot().catch(() => {
|
|
31311
31715
|
});
|
|
31312
31716
|
await init_App2().then(() => App_exports);
|
|
31313
31717
|
} else {
|
|
31314
|
-
const claudeDir =
|
|
31315
|
-
const settingsPath =
|
|
31316
|
-
const hasClaudeCode =
|
|
31718
|
+
const claudeDir = path48.join(os21.homedir(), ".claude");
|
|
31719
|
+
const settingsPath = path48.join(claudeDir, "settings.json");
|
|
31720
|
+
const hasClaudeCode = existsSync34(settingsPath) && (() => {
|
|
31317
31721
|
try {
|
|
31318
|
-
const raw =
|
|
31722
|
+
const raw = readFileSync29(settingsPath, "utf8");
|
|
31319
31723
|
return raw.includes("exe-os") || raw.includes("exe-mem");
|
|
31320
31724
|
} catch {
|
|
31321
31725
|
return false;
|
|
@@ -31325,9 +31729,9 @@ ID: ${result.id}`);
|
|
|
31325
31729
|
const { DEFAULT_COORDINATOR_TEMPLATE_NAME: DEFAULT_COORDINATOR_TEMPLATE_NAME2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
31326
31730
|
let cooName = DEFAULT_COORDINATOR_TEMPLATE_NAME2;
|
|
31327
31731
|
try {
|
|
31328
|
-
const rosterPath =
|
|
31329
|
-
if (
|
|
31330
|
-
const roster = JSON.parse(
|
|
31732
|
+
const rosterPath = path48.join(os21.homedir(), ".exe-os", "exe-employees.json");
|
|
31733
|
+
if (existsSync34(rosterPath)) {
|
|
31734
|
+
const roster = JSON.parse(readFileSync29(rosterPath, "utf8"));
|
|
31331
31735
|
const coo = roster.find((e) => e.role === "COO");
|
|
31332
31736
|
if (coo) cooName = coo.name;
|
|
31333
31737
|
}
|
|
@@ -31391,14 +31795,15 @@ async function runCodexInstall() {
|
|
|
31391
31795
|
}
|
|
31392
31796
|
}
|
|
31393
31797
|
async function runClaudeCheck() {
|
|
31394
|
-
const
|
|
31395
|
-
const
|
|
31396
|
-
const
|
|
31798
|
+
const { detectMcpNameCollisions: detectMcpNameCollisions2 } = await Promise.resolve().then(() => (init_installer(), installer_exports));
|
|
31799
|
+
const claudeDir = path48.join(os21.homedir(), ".claude");
|
|
31800
|
+
const settingsPath = path48.join(claudeDir, "settings.json");
|
|
31801
|
+
const claudeJsonPath = path48.join(os21.homedir(), ".claude.json");
|
|
31397
31802
|
let ok = true;
|
|
31398
|
-
if (
|
|
31803
|
+
if (existsSync34(settingsPath)) {
|
|
31399
31804
|
let settings;
|
|
31400
31805
|
try {
|
|
31401
|
-
settings = JSON.parse(
|
|
31806
|
+
settings = JSON.parse(readFileSync29(settingsPath, "utf8"));
|
|
31402
31807
|
} catch {
|
|
31403
31808
|
console.log("\x1B[31m\u2717\x1B[0m settings.json is malformed (invalid JSON)");
|
|
31404
31809
|
ok = false;
|
|
@@ -31424,10 +31829,10 @@ async function runClaudeCheck() {
|
|
|
31424
31829
|
console.log("\x1B[31m\u2717\x1B[0m settings.json not found");
|
|
31425
31830
|
ok = false;
|
|
31426
31831
|
}
|
|
31427
|
-
if (
|
|
31832
|
+
if (existsSync34(claudeJsonPath)) {
|
|
31428
31833
|
let claudeJson;
|
|
31429
31834
|
try {
|
|
31430
|
-
claudeJson = JSON.parse(
|
|
31835
|
+
claudeJson = JSON.parse(readFileSync29(claudeJsonPath, "utf8"));
|
|
31431
31836
|
} catch {
|
|
31432
31837
|
console.log("\x1B[31m\u2717\x1B[0m claude.json is malformed (invalid JSON)");
|
|
31433
31838
|
ok = false;
|
|
@@ -31446,8 +31851,22 @@ async function runClaudeCheck() {
|
|
|
31446
31851
|
console.log("\x1B[31m\u2717\x1B[0m claude.json not found");
|
|
31447
31852
|
ok = false;
|
|
31448
31853
|
}
|
|
31449
|
-
const
|
|
31450
|
-
|
|
31854
|
+
const collisions = detectMcpNameCollisions2(os21.homedir(), process.cwd());
|
|
31855
|
+
const disabledCollisions = collisions.filter((c) => c.disabledInMcpJson);
|
|
31856
|
+
if (disabledCollisions.length > 0) {
|
|
31857
|
+
console.log("\x1B[31m\u2717\x1B[0m MCP name collision: disabled .mcp.json entries shadow project MCP servers");
|
|
31858
|
+
for (const c of disabledCollisions) {
|
|
31859
|
+
console.log(` - ${c.serverName}: ${c.mcpJsonPath} shadows ~/.claude.json project ${c.projectPath}`);
|
|
31860
|
+
}
|
|
31861
|
+
console.log(" Fix: remove/rename the duplicate .mcp.json server entry, or use one MCP config source of truth.");
|
|
31862
|
+
ok = false;
|
|
31863
|
+
} else if (collisions.length > 0) {
|
|
31864
|
+
console.log("\x1B[33m!\x1B[0m MCP server names duplicated between .mcp.json and project config");
|
|
31865
|
+
} else {
|
|
31866
|
+
console.log("\x1B[32m\u2713\x1B[0m No .mcp.json/project MCP name collisions detected");
|
|
31867
|
+
}
|
|
31868
|
+
const skillsDir = path48.join(claudeDir, "skills");
|
|
31869
|
+
if (existsSync34(skillsDir)) {
|
|
31451
31870
|
console.log("\x1B[32m\u2713\x1B[0m Slash skills directory exists");
|
|
31452
31871
|
} else {
|
|
31453
31872
|
console.log("\x1B[31m\u2717\x1B[0m Slash skills directory missing");
|
|
@@ -31464,16 +31883,16 @@ async function runClaudeUninstall(flags = []) {
|
|
|
31464
31883
|
const dryRun = flags.includes("--dry-run");
|
|
31465
31884
|
const purge = flags.includes("--purge");
|
|
31466
31885
|
const homeDir = os21.homedir();
|
|
31467
|
-
const claudeDir =
|
|
31468
|
-
const settingsPath =
|
|
31469
|
-
const claudeJsonPath =
|
|
31470
|
-
const exeOsDir =
|
|
31886
|
+
const claudeDir = path48.join(homeDir, ".claude");
|
|
31887
|
+
const settingsPath = path48.join(claudeDir, "settings.json");
|
|
31888
|
+
const claudeJsonPath = path48.join(homeDir, ".claude.json");
|
|
31889
|
+
const exeOsDir = path48.join(homeDir, ".exe-os");
|
|
31471
31890
|
let removed = 0;
|
|
31472
31891
|
const log = (msg) => console.log(dryRun ? `[dry-run] ${msg}` : msg);
|
|
31473
31892
|
let settings = {};
|
|
31474
|
-
if (
|
|
31893
|
+
if (existsSync34(settingsPath)) {
|
|
31475
31894
|
try {
|
|
31476
|
-
settings = JSON.parse(
|
|
31895
|
+
settings = JSON.parse(readFileSync29(settingsPath, "utf8"));
|
|
31477
31896
|
} catch {
|
|
31478
31897
|
console.error("Your ~/.claude/settings.json appears malformed.");
|
|
31479
31898
|
if (purge) {
|
|
@@ -31511,15 +31930,15 @@ async function runClaudeUninstall(flags = []) {
|
|
|
31511
31930
|
permCount = before - settings.permissions.allow.length;
|
|
31512
31931
|
}
|
|
31513
31932
|
if (!dryRun) {
|
|
31514
|
-
|
|
31933
|
+
writeFileSync21(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
31515
31934
|
}
|
|
31516
31935
|
log("\u2713 Removed exe-os hooks from settings.json");
|
|
31517
31936
|
if (permCount > 0) log(`\u2713 Removed ${permCount} MCP permission entries`);
|
|
31518
31937
|
removed++;
|
|
31519
31938
|
}
|
|
31520
31939
|
}
|
|
31521
|
-
if (
|
|
31522
|
-
const raw =
|
|
31940
|
+
if (existsSync34(claudeJsonPath)) {
|
|
31941
|
+
const raw = readFileSync29(claudeJsonPath, "utf8");
|
|
31523
31942
|
if (raw.length > 1e6) {
|
|
31524
31943
|
console.error("claude.json exceeds 1 MB \u2014 skipping parse.");
|
|
31525
31944
|
} else {
|
|
@@ -31540,7 +31959,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
31540
31959
|
}
|
|
31541
31960
|
if (removedMcp) {
|
|
31542
31961
|
if (!dryRun) {
|
|
31543
|
-
|
|
31962
|
+
writeFileSync21(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
31544
31963
|
}
|
|
31545
31964
|
log("\u2713 Removed exe-os MCP server from claude.json");
|
|
31546
31965
|
removed++;
|
|
@@ -31548,14 +31967,14 @@ async function runClaudeUninstall(flags = []) {
|
|
|
31548
31967
|
}
|
|
31549
31968
|
}
|
|
31550
31969
|
}
|
|
31551
|
-
const skillsDir =
|
|
31552
|
-
if (
|
|
31970
|
+
const skillsDir = path48.join(claudeDir, "skills");
|
|
31971
|
+
if (existsSync34(skillsDir)) {
|
|
31553
31972
|
let skillCount = 0;
|
|
31554
31973
|
try {
|
|
31555
31974
|
const entries = readdirSync10(skillsDir);
|
|
31556
31975
|
for (const entry of entries) {
|
|
31557
31976
|
if (entry.startsWith("exe")) {
|
|
31558
|
-
const fullPath =
|
|
31977
|
+
const fullPath = path48.join(skillsDir, entry);
|
|
31559
31978
|
if (!dryRun) rmSync(fullPath, { recursive: true, force: true });
|
|
31560
31979
|
skillCount++;
|
|
31561
31980
|
}
|
|
@@ -31567,30 +31986,30 @@ async function runClaudeUninstall(flags = []) {
|
|
|
31567
31986
|
removed++;
|
|
31568
31987
|
}
|
|
31569
31988
|
}
|
|
31570
|
-
const claudeMdPath =
|
|
31571
|
-
if (
|
|
31572
|
-
const content =
|
|
31989
|
+
const claudeMdPath = path48.join(claudeDir, "CLAUDE.md");
|
|
31990
|
+
if (existsSync34(claudeMdPath)) {
|
|
31991
|
+
const content = readFileSync29(claudeMdPath, "utf8");
|
|
31573
31992
|
const startMarker = "<!-- exe-os:orchestration-start -->";
|
|
31574
31993
|
const endMarker = "<!-- exe-os:orchestration-end -->";
|
|
31575
31994
|
const startIdx = content.indexOf(startMarker);
|
|
31576
31995
|
const endIdx = content.indexOf(endMarker);
|
|
31577
31996
|
if (startIdx !== -1 && endIdx !== -1) {
|
|
31578
31997
|
const cleaned = (content.slice(0, startIdx) + content.slice(endIdx + endMarker.length)).replace(/\n{3,}/g, "\n\n").trim() + "\n";
|
|
31579
|
-
if (!dryRun)
|
|
31998
|
+
if (!dryRun) writeFileSync21(claudeMdPath, cleaned);
|
|
31580
31999
|
log("\u2713 Removed orchestration block from CLAUDE.md");
|
|
31581
32000
|
removed++;
|
|
31582
32001
|
}
|
|
31583
32002
|
}
|
|
31584
|
-
const agentsDir =
|
|
31585
|
-
if (
|
|
32003
|
+
const agentsDir = path48.join(claudeDir, "agents");
|
|
32004
|
+
if (existsSync34(agentsDir)) {
|
|
31586
32005
|
let agentCount = 0;
|
|
31587
32006
|
try {
|
|
31588
32007
|
const entries = readdirSync10(agentsDir).filter((f) => f.endsWith(".md"));
|
|
31589
32008
|
let knownNames = /* @__PURE__ */ new Set();
|
|
31590
|
-
const rosterPath =
|
|
31591
|
-
if (
|
|
32009
|
+
const rosterPath = path48.join(exeOsDir, "exe-employees.json");
|
|
32010
|
+
if (existsSync34(rosterPath)) {
|
|
31592
32011
|
try {
|
|
31593
|
-
const roster = JSON.parse(
|
|
32012
|
+
const roster = JSON.parse(readFileSync29(rosterPath, "utf8"));
|
|
31594
32013
|
knownNames = new Set(roster.map((e) => e.name));
|
|
31595
32014
|
} catch {
|
|
31596
32015
|
}
|
|
@@ -31598,7 +32017,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
31598
32017
|
for (const entry of entries) {
|
|
31599
32018
|
const name = entry.replace(/\.md$/, "");
|
|
31600
32019
|
if (knownNames.has(name)) {
|
|
31601
|
-
if (!dryRun) rmSync(
|
|
32020
|
+
if (!dryRun) rmSync(path48.join(agentsDir, entry), { force: true });
|
|
31602
32021
|
agentCount++;
|
|
31603
32022
|
}
|
|
31604
32023
|
}
|
|
@@ -31609,16 +32028,16 @@ async function runClaudeUninstall(flags = []) {
|
|
|
31609
32028
|
removed++;
|
|
31610
32029
|
}
|
|
31611
32030
|
}
|
|
31612
|
-
const projectsDir =
|
|
31613
|
-
if (
|
|
32031
|
+
const projectsDir = path48.join(claudeDir, "projects");
|
|
32032
|
+
if (existsSync34(projectsDir)) {
|
|
31614
32033
|
let projectCount = 0;
|
|
31615
32034
|
try {
|
|
31616
32035
|
const projects = readdirSync10(projectsDir);
|
|
31617
32036
|
for (const proj of projects) {
|
|
31618
|
-
const projSettings =
|
|
31619
|
-
if (!
|
|
32037
|
+
const projSettings = path48.join(projectsDir, proj, "settings.json");
|
|
32038
|
+
if (!existsSync34(projSettings)) continue;
|
|
31620
32039
|
try {
|
|
31621
|
-
const pSettings = JSON.parse(
|
|
32040
|
+
const pSettings = JSON.parse(readFileSync29(projSettings, "utf8"));
|
|
31622
32041
|
let changed = false;
|
|
31623
32042
|
if (Array.isArray(pSettings.permissions?.allow)) {
|
|
31624
32043
|
const before = pSettings.permissions.allow.length;
|
|
@@ -31628,7 +32047,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
31628
32047
|
if (pSettings.permissions.allow.length < before) changed = true;
|
|
31629
32048
|
}
|
|
31630
32049
|
if (changed && !dryRun) {
|
|
31631
|
-
|
|
32050
|
+
writeFileSync21(projSettings, JSON.stringify(pSettings, null, 2) + "\n");
|
|
31632
32051
|
}
|
|
31633
32052
|
if (changed) projectCount++;
|
|
31634
32053
|
} catch {
|
|
@@ -31652,18 +32071,18 @@ async function runClaudeUninstall(flags = []) {
|
|
|
31652
32071
|
};
|
|
31653
32072
|
const exeBinPath = findExeBin3();
|
|
31654
32073
|
if (!exeBinPath) throw new Error("exe-os not found in PATH");
|
|
31655
|
-
const binDir =
|
|
32074
|
+
const binDir = path48.dirname(exeBinPath);
|
|
31656
32075
|
let symlinkCount = 0;
|
|
31657
|
-
const rosterPath =
|
|
31658
|
-
if (
|
|
31659
|
-
const roster = JSON.parse(
|
|
32076
|
+
const rosterPath = path48.join(exeOsDir, "exe-employees.json");
|
|
32077
|
+
if (existsSync34(rosterPath)) {
|
|
32078
|
+
const roster = JSON.parse(readFileSync29(rosterPath, "utf8"));
|
|
31660
32079
|
const { DEFAULT_COORDINATOR_TEMPLATE_NAME: DEFAULT_COORDINATOR_TEMPLATE_NAME2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
31661
32080
|
const coordinatorName = roster.find((e) => e.role?.toLowerCase() === "coo")?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME2;
|
|
31662
32081
|
for (const emp of roster) {
|
|
31663
32082
|
if (emp.name === coordinatorName) continue;
|
|
31664
32083
|
for (const suffix of ["", "-opencode"]) {
|
|
31665
|
-
const linkPath =
|
|
31666
|
-
if (
|
|
32084
|
+
const linkPath = path48.join(binDir, `${emp.name}${suffix}`);
|
|
32085
|
+
if (existsSync34(linkPath)) {
|
|
31667
32086
|
if (!dryRun) rmSync(linkPath, { force: true });
|
|
31668
32087
|
symlinkCount++;
|
|
31669
32088
|
}
|
|
@@ -31676,7 +32095,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
31676
32095
|
}
|
|
31677
32096
|
} catch {
|
|
31678
32097
|
}
|
|
31679
|
-
if (purge &&
|
|
32098
|
+
if (purge && existsSync34(exeOsDir)) {
|
|
31680
32099
|
if (!dryRun) {
|
|
31681
32100
|
process.stdout.write("\x1B[33m\u26A0 This will delete all memories, identities, and agent data.\x1B[0m\n");
|
|
31682
32101
|
process.stdout.write(" Removing ~/.exe-os...\n");
|
|
@@ -31701,7 +32120,7 @@ async function checkForUpdateOnBoot() {
|
|
|
31701
32120
|
const config = await loadConfig2();
|
|
31702
32121
|
if (!config.autoUpdate.checkOnBoot) return;
|
|
31703
32122
|
const { checkForUpdate: checkForUpdate2 } = await Promise.resolve().then(() => (init_update(), update_exports));
|
|
31704
|
-
const packageRoot =
|
|
32123
|
+
const packageRoot = path48.resolve(
|
|
31705
32124
|
new URL("../..", import.meta.url).pathname
|
|
31706
32125
|
);
|
|
31707
32126
|
const result = checkForUpdate2(packageRoot);
|
|
@@ -31721,7 +32140,7 @@ async function runActivate(key) {
|
|
|
31721
32140
|
}
|
|
31722
32141
|
const { saveLicense: saveLicense2, mirrorLicenseKey: mirrorLicenseKey2, validateLicense: validateLicense2 } = await Promise.resolve().then(() => (init_license(), license_exports));
|
|
31723
32142
|
const { loadEmployees: loadEmployees2, saveEmployees: saveEmployees2, addEmployee: addEmployee2, registerBinSymlinks: registerBinSymlinks2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
31724
|
-
const { getTemplateByRole: getTemplateByRole2
|
|
32143
|
+
const { getTemplateByRole: getTemplateByRole2 } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
31725
32144
|
const { getTemplate: getIdentityTemplate } = await Promise.resolve().then(() => (init_identity_templates(), identity_templates_exports));
|
|
31726
32145
|
const { identityPath: identityPath2 } = await Promise.resolve().then(() => (init_identity(), identity_exports));
|
|
31727
32146
|
const { createInterface: createInterface5 } = await import("readline");
|
|
@@ -31753,7 +32172,8 @@ async function runActivate(key) {
|
|
|
31753
32172
|
if (create3.toLowerCase() === "n") return;
|
|
31754
32173
|
const nameInput = await ask3(`Name your ${role} [${defaultName}]: `);
|
|
31755
32174
|
const name = nameInput || defaultName;
|
|
31756
|
-
const
|
|
32175
|
+
const { systemPrompt: _templatePrompt, ...templateMeta } = template;
|
|
32176
|
+
const emp = { ...templateMeta, name, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
31757
32177
|
employees = addEmployee2(employees, emp);
|
|
31758
32178
|
await saveEmployees2(employees);
|
|
31759
32179
|
registerBinSymlinks2(name);
|
|
@@ -31761,7 +32181,7 @@ async function runActivate(key) {
|
|
|
31761
32181
|
const idTemplate = getIdentityTemplate(identityKey);
|
|
31762
32182
|
if (idTemplate) {
|
|
31763
32183
|
const idPath = identityPath2(name);
|
|
31764
|
-
const dir =
|
|
32184
|
+
const dir = path48.dirname(idPath);
|
|
31765
32185
|
if (!fs8.existsSync(dir)) fs8.mkdirSync(dir, { recursive: true });
|
|
31766
32186
|
fs8.writeFileSync(idPath, idTemplate.replace(/^agent_id: \w+/m, `agent_id: ${name}`), "utf-8");
|
|
31767
32187
|
}
|