@askexenow/exe-os 0.9.93 → 0.9.95
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/compose/docker-compose.yml +1 -0
- package/dist/bin/agentic-ontology-backfill.js +65 -8
- package/dist/bin/agentic-reflection-backfill.js +54 -3
- package/dist/bin/agentic-semantic-label.js +54 -3
- package/dist/bin/backfill-conversations.js +69 -9
- package/dist/bin/backfill-responses.js +69 -9
- package/dist/bin/backfill-vectors.js +54 -3
- package/dist/bin/bulk-sync-postgres.js +66 -8
- package/dist/bin/cleanup-stale-review-tasks.js +121 -13
- package/dist/bin/cli.js +1561 -466
- package/dist/bin/customer-readiness.js +61 -0
- package/dist/bin/exe-agent.js +17 -3
- package/dist/bin/exe-assign.js +75 -9
- package/dist/bin/exe-boot.js +114 -12
- package/dist/bin/exe-call.js +17 -3
- package/dist/bin/exe-cloud.js +76 -10
- package/dist/bin/exe-dispatch.js +136 -18
- package/dist/bin/exe-doctor.js +75 -9
- package/dist/bin/exe-export-behaviors.js +75 -9
- package/dist/bin/exe-forget.js +94 -9
- package/dist/bin/exe-gateway.js +135 -18
- package/dist/bin/exe-heartbeat.js +121 -13
- package/dist/bin/exe-kill.js +75 -9
- package/dist/bin/exe-launch-agent.js +75 -9
- package/dist/bin/exe-new-employee.js +18 -4
- package/dist/bin/exe-pending-messages.js +121 -13
- package/dist/bin/exe-pending-notifications.js +121 -13
- package/dist/bin/exe-pending-reviews.js +121 -13
- package/dist/bin/exe-rename.js +75 -9
- package/dist/bin/exe-review.js +75 -9
- package/dist/bin/exe-search.js +100 -9
- package/dist/bin/exe-session-cleanup.js +136 -18
- package/dist/bin/exe-settings.js +1 -0
- package/dist/bin/exe-start-codex.js +65 -8
- package/dist/bin/exe-start-opencode.js +65 -8
- package/dist/bin/exe-status.js +121 -13
- package/dist/bin/exe-support.js +1 -0
- package/dist/bin/exe-team.js +75 -9
- package/dist/bin/git-sweep.js +136 -18
- package/dist/bin/graph-backfill.js +65 -8
- package/dist/bin/graph-export.js +75 -9
- package/dist/bin/intercom-check.js +136 -18
- package/dist/bin/scan-tasks.js +136 -18
- package/dist/bin/setup.js +55 -4
- package/dist/bin/shard-migrate.js +65 -8
- package/dist/bin/stack-update.js +5 -6
- package/dist/bin/update.js +1 -1
- package/dist/gateway/index.js +136 -18
- package/dist/hooks/bug-report-worker.js +136 -18
- package/dist/hooks/codex-stop-task-finalizer.js +126 -14
- package/dist/hooks/commit-complete.js +136 -18
- package/dist/hooks/error-recall.js +100 -9
- package/dist/hooks/ingest.js +75 -9
- package/dist/hooks/instructions-loaded.js +75 -9
- package/dist/hooks/notification.js +75 -9
- package/dist/hooks/post-compact.js +313 -50
- package/dist/hooks/post-tool-combined.js +436 -13
- package/dist/hooks/pre-compact.js +136 -18
- package/dist/hooks/pre-tool-use.js +121 -13
- package/dist/hooks/prompt-submit.js +194 -19
- package/dist/hooks/session-end.js +136 -18
- package/dist/hooks/session-start.js +146 -13
- package/dist/hooks/stop.js +121 -13
- package/dist/hooks/subagent-stop.js +121 -13
- package/dist/hooks/summary-worker.js +99 -7
- package/dist/index.js +136 -18
- package/dist/lib/cloud-sync.js +38 -0
- package/dist/lib/consolidation.js +3 -1
- package/dist/lib/database.js +37 -0
- package/dist/lib/db.js +37 -0
- package/dist/lib/device-registry.js +37 -0
- package/dist/lib/employee-templates.js +17 -3
- package/dist/lib/exe-daemon.js +916 -42
- package/dist/lib/hybrid-search.js +100 -9
- package/dist/lib/license.js +1 -1
- package/dist/lib/messaging.js +43 -4
- package/dist/lib/schedules.js +54 -3
- package/dist/lib/store.js +75 -9
- package/dist/lib/tasks.js +61 -9
- package/dist/lib/tmux-routing.js +61 -9
- package/dist/mcp/server.js +878 -42
- package/dist/mcp/tools/create-task.js +70 -12
- package/dist/mcp/tools/list-tasks.js +49 -5
- package/dist/mcp/tools/send-message.js +43 -4
- package/dist/mcp/tools/update-task.js +61 -9
- package/dist/runtime/index.js +136 -18
- package/dist/tui/App.js +135 -18
- package/package.json +1 -1
package/dist/bin/cli.js
CHANGED
|
@@ -2660,8 +2660,8 @@ function deriveMachineKey() {
|
|
|
2660
2660
|
}
|
|
2661
2661
|
function readMachineId() {
|
|
2662
2662
|
try {
|
|
2663
|
-
const { readFileSync:
|
|
2664
|
-
return
|
|
2663
|
+
const { readFileSync: readFileSync39 } = __require("fs");
|
|
2664
|
+
return readFileSync39("/etc/machine-id", "utf-8").trim();
|
|
2665
2665
|
} catch {
|
|
2666
2666
|
return "";
|
|
2667
2667
|
}
|
|
@@ -3751,8 +3751,8 @@ function findPackageRoot() {
|
|
|
3751
3751
|
function getAvailableMemoryGB() {
|
|
3752
3752
|
if (process.platform === "darwin") {
|
|
3753
3753
|
try {
|
|
3754
|
-
const { execSync:
|
|
3755
|
-
const vmstat =
|
|
3754
|
+
const { execSync: execSync20 } = __require("child_process");
|
|
3755
|
+
const vmstat = execSync20("vm_stat", { encoding: "utf8" });
|
|
3756
3756
|
const pageSize = 16384;
|
|
3757
3757
|
const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
|
|
3758
3758
|
const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
|
|
@@ -5412,6 +5412,20 @@ async function ensureSchema() {
|
|
|
5412
5412
|
});
|
|
5413
5413
|
} catch {
|
|
5414
5414
|
}
|
|
5415
|
+
try {
|
|
5416
|
+
await client.execute({
|
|
5417
|
+
sql: `ALTER TABLE tasks ADD COLUMN spawn_runtime TEXT`,
|
|
5418
|
+
args: []
|
|
5419
|
+
});
|
|
5420
|
+
} catch {
|
|
5421
|
+
}
|
|
5422
|
+
try {
|
|
5423
|
+
await client.execute({
|
|
5424
|
+
sql: `ALTER TABLE tasks ADD COLUMN spawn_model TEXT`,
|
|
5425
|
+
args: []
|
|
5426
|
+
});
|
|
5427
|
+
} catch {
|
|
5428
|
+
}
|
|
5415
5429
|
await client.executeMultiple(`
|
|
5416
5430
|
CREATE VIRTUAL TABLE IF NOT EXISTS conversations_fts USING fts5(
|
|
5417
5431
|
content_text,
|
|
@@ -5663,6 +5677,22 @@ async function ensureSchema() {
|
|
|
5663
5677
|
);
|
|
5664
5678
|
} catch {
|
|
5665
5679
|
}
|
|
5680
|
+
for (const col of [
|
|
5681
|
+
"ALTER TABLE memories ADD COLUMN valid_from TEXT",
|
|
5682
|
+
"ALTER TABLE memories ADD COLUMN invalid_at TEXT"
|
|
5683
|
+
]) {
|
|
5684
|
+
try {
|
|
5685
|
+
await client.execute(col);
|
|
5686
|
+
} catch {
|
|
5687
|
+
}
|
|
5688
|
+
}
|
|
5689
|
+
try {
|
|
5690
|
+
await client.execute({
|
|
5691
|
+
sql: `UPDATE memories SET valid_from = timestamp WHERE valid_from IS NULL`,
|
|
5692
|
+
args: []
|
|
5693
|
+
});
|
|
5694
|
+
} catch {
|
|
5695
|
+
}
|
|
5666
5696
|
try {
|
|
5667
5697
|
await client.execute({
|
|
5668
5698
|
sql: `ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'`,
|
|
@@ -5705,6 +5735,13 @@ async function ensureSchema() {
|
|
|
5705
5735
|
} catch {
|
|
5706
5736
|
}
|
|
5707
5737
|
}
|
|
5738
|
+
try {
|
|
5739
|
+
await client.execute({
|
|
5740
|
+
sql: `ALTER TABLE memories ADD COLUMN procedure_for TEXT`,
|
|
5741
|
+
args: []
|
|
5742
|
+
});
|
|
5743
|
+
} catch {
|
|
5744
|
+
}
|
|
5708
5745
|
try {
|
|
5709
5746
|
await client.execute({
|
|
5710
5747
|
sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
|
|
@@ -6146,8 +6183,8 @@ async function validateLicense(apiKey, deviceId) {
|
|
|
6146
6183
|
}
|
|
6147
6184
|
function getCacheAgeMs() {
|
|
6148
6185
|
try {
|
|
6149
|
-
const { statSync:
|
|
6150
|
-
const s =
|
|
6186
|
+
const { statSync: statSync9 } = __require("fs");
|
|
6187
|
+
const s = statSync9(CACHE_PATH);
|
|
6151
6188
|
return Date.now() - s.mtimeMs;
|
|
6152
6189
|
} catch {
|
|
6153
6190
|
return Infinity;
|
|
@@ -6321,7 +6358,7 @@ var init_license = __esm({
|
|
|
6321
6358
|
LICENSE_PATH = path13.join(EXE_AI_DIR, "license.key");
|
|
6322
6359
|
CACHE_PATH = path13.join(EXE_AI_DIR, "license-cache.json");
|
|
6323
6360
|
DEVICE_ID_PATH = path13.join(EXE_AI_DIR, "device-id");
|
|
6324
|
-
API_BASE = "https://askexe.com/cloud";
|
|
6361
|
+
API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com/cloud";
|
|
6325
6362
|
RETRY_DELAY_MS = 500;
|
|
6326
6363
|
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
6327
6364
|
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
@@ -8894,6 +8931,20 @@ var init_platform_procedures = __esm({
|
|
|
8894
8931
|
priority: "p1",
|
|
8895
8932
|
content: "Once per session (COO boot only, never repeat), call list_my_feature_requests to check if any previously filed feature requests have been shipped by AskExe. If any request has status 'shipped' with a shipped_version, surface it to the founder immediately: '\u{1F680} N feature(s) shipped \u2014 run exe-os update to get version X.Y.Z'. This is a one-time check at boot, not a recurring poll. If no requests exist or none are shipped, skip silently. If the MCP tool is unavailable or the network call fails, skip silently \u2014 this is informational, not blocking."
|
|
8896
8933
|
},
|
|
8934
|
+
// --- Tool guidance ---
|
|
8935
|
+
{
|
|
8936
|
+
title: "How to use company_actions \u2014 execute business actions through gateway connectors",
|
|
8937
|
+
domain: "tools",
|
|
8938
|
+
priority: "p2",
|
|
8939
|
+
content: "The company_actions tool executes business actions through gateway connectors (e.g. send WhatsApp, trigger workflows, update CRM). It routes through the exe-gateway on the VPS. Actions are defined by the customer's gateway configuration \u2014 each connector (WhatsApp, Shopify, email, etc.) exposes specific actions. Use query_company_brain to find available data first, then company_actions to act on it. Requires gateway auth token. Read-only founders should NOT use this \u2014 it mutates external state."
|
|
8940
|
+
},
|
|
8941
|
+
// --- Release awareness ---
|
|
8942
|
+
{
|
|
8943
|
+
title: "What's New check \u2014 surface new features after update",
|
|
8944
|
+
domain: "support",
|
|
8945
|
+
priority: "p1",
|
|
8946
|
+
content: "Once per session (COO boot only, never repeat), check if the installed exe-os version is newer than the last session. If it is, read the bundled release-notes.json (at the package root) and surface a brief summary to the founder: 'Updated to exe-os vX.Y.Z \u2014 N new features, M fixes.' List the top 3 features by name. This helps the founder know what they got from the update. If release-notes.json doesn't exist or the version hasn't changed, skip silently. Never repeat this check in the same session."
|
|
8947
|
+
},
|
|
8897
8948
|
// --- Platform vs Customer ownership ---
|
|
8898
8949
|
{
|
|
8899
8950
|
title: "What the platform provides vs what you customize",
|
|
@@ -8982,13 +9033,13 @@ var init_platform_procedures = __esm({
|
|
|
8982
9033
|
title: "MCP tools \u2014 memory, decision, and search",
|
|
8983
9034
|
domain: "tool-use",
|
|
8984
9035
|
priority: "p1",
|
|
8985
|
-
content: `memory(action="recall") / recall_my_memory: search
|
|
9036
|
+
content: `memory(action="recall") / recall_my_memory: search memories (semantic + FTS). Params: as_of (bi-temporal \u2014 what did I know at time X?), kind (decision|procedure|observation|raw|conversation|behavior), retrieval_mode (all|decisions_only|procedures_only|operational|recent_high_value). memory(action="ask_team") / ask_team_memory: search a colleague's memories. memory(action="store") / store_memory: persist a memory. Params: kind, procedure_for (domain tag for procedures). memory(action="commit") / commit_memory: high-importance, survives consolidation. Requires summary. memory(action="search") / search_everything: unified search across memories, tasks, entities, conversations. memory(action="session_context") / get_session_context: temporal window. Requires session_id + target_timestamp. memory(action="get_by_id"): fetch one memory by UUID with full untruncated text. memory(action="consolidate") / consolidate_memories: merge duplicate/related memories. memory(action="cardinality") / get_memory_cardinality: count memories per agent. memory(action="supersede"): replace an old memory with a new version (old_id + new text). decision(action="store") / store_decision: record an architectural decision (domain, decision, rationale). decision(action="get") / get_decision: retrieve a past decision by domain or query.`
|
|
8986
9037
|
},
|
|
8987
9038
|
{
|
|
8988
9039
|
title: "MCP tools \u2014 task orchestration",
|
|
8989
9040
|
domain: "tool-use",
|
|
8990
9041
|
priority: "p1",
|
|
8991
|
-
content: 'task(action="create") / create_task: dispatch work (title, assigned_to, context). The ONLY dispatch path. Auto-spawns session. task(action="list") / list_tasks: query tasks by status, assignee, project. task(action="get") / get_task: fetch full task details by task_id. task(action="update") / update_task: change status (in_progress, done, blocked, cancelled) + result summary. task(action="close") / close_task: finalize a reviewed task (COO only). task(action="checkpoint") / checkpoint_task: save progress state for crash recovery. task(action="resume") / resume_employee: re-spawn an employee session for an existing task.'
|
|
9042
|
+
content: 'task(action="create") / create_task: dispatch work (title, assigned_to, context). The ONLY dispatch path. Auto-spawns session. Params: blocked_by (task ID for dependency), parent_task_id (subtask hierarchy), reviewer, complexity (routine|standard|complex|critical), budget_tokens (max token cap), budget_fallback_model, spawn_runtime (override runtime: claude|codex|opencode), spawn_model (override model). task(action="list") / list_tasks: query tasks by status, assignee, project. task(action="get") / get_task: fetch full task details by task_id. task(action="update") / update_task: change status (in_progress, done, blocked, cancelled) + result summary. task(action="close") / close_task: finalize a reviewed task (COO only). task(action="checkpoint") / checkpoint_task: save progress state for crash recovery. task(action="resume") / resume_employee: re-spawn an employee session for an existing task.'
|
|
8992
9043
|
},
|
|
8993
9044
|
{
|
|
8994
9045
|
title: "MCP tools \u2014 knowledge graph (GraphRAG)",
|
|
@@ -9018,7 +9069,7 @@ var init_platform_procedures = __esm({
|
|
|
9018
9069
|
title: "MCP tools \u2014 admin, config, and operations",
|
|
9019
9070
|
domain: "tool-use",
|
|
9020
9071
|
priority: "p1",
|
|
9021
|
-
content: 'config(action="list_employees"): view roster. config(action="agent_spend"): token usage per agent. config(action="daemon_health"): check exed status. config(action="license_status"): check license. config(action="cloud_sync"): force sync. config(action="memory_audit"): health check (dupes, null vectors). config(action="run_consolidation"): trigger memory consolidation. config(action="company_procedure", subaction="store|list|deactivate"): manage company procedures. config(action="global_procedure"): list all procedures (platform + company). config(action="create_trigger|list_triggers"): scheduled agent jobs. config(action="export_orchestration|import_orchestration"): portable org state. diagnostics(action="healthcheck|doctor|status_brief|check_update|cloud_status"): system diagnostics. mcp_ping(): daemon health + license status + tool usage stats.'
|
|
9072
|
+
content: 'config(action="list_employees"): view roster. config(action="set_agent_config"): view or change per-agent runtime + model. Call with no args to show all agents. config(action="agent_spend"): token usage per agent. config(action="daemon_health"): check exed status. config(action="license_status"): check license. config(action="cloud_sync"): force sync. Supports cloud_action param: status|sync|reupload. config(action="memory_audit"): health check (dupes, null vectors). config(action="run_consolidation"): trigger memory consolidation. config(action="worker_gate"): check spawn slot availability \u2014 alive/stale/reserved counts vs max. Use before dispatching. config(action="auto_wake_status"): orphaned tasks, blocked tasks, auto-wake retry status. config(action="orchestration_phase"): view/change org phase (phase_1_coo|phase_2_executives|phase_3_parallel_org). config(action="company_procedure", subaction="store|list|deactivate"): manage company procedures. config(action="global_procedure"): list all procedures (platform + company). config(action="create_trigger|list_triggers"): scheduled agent jobs. config(action="export_orchestration|import_orchestration"): portable org state. diagnostics(action="healthcheck|doctor|status_brief|check_update|cloud_status"): system diagnostics. diagnostics(action="pending_work_summary"): pending reviews + messages + notifications in one call. diagnostics(action="rename_employee"): rename an agent across all systems (roster, identity, DB, symlinks). diagnostics(action="tool_search"): semantic tool discovery \u2014 find relevant MCP tools by natural language query. diagnostics(action="drift"): identity drift detection \u2014 score how far an agent has drifted from its role. mcp_ping(): daemon health + license status + tool usage stats.'
|
|
9022
9073
|
}
|
|
9023
9074
|
];
|
|
9024
9075
|
PLATFORM_PROCEDURE_TITLES = new Set(
|
|
@@ -9702,6 +9753,8 @@ async function writeMemory(record) {
|
|
|
9702
9753
|
source_type: record.source_type ?? null,
|
|
9703
9754
|
tier: record.tier ?? classifyTier(record),
|
|
9704
9755
|
supersedes_id: record.supersedes_id ?? null,
|
|
9756
|
+
valid_from: record.valid_from ?? record.timestamp,
|
|
9757
|
+
invalid_at: record.invalid_at ?? null,
|
|
9705
9758
|
draft: record.draft ? 1 : 0,
|
|
9706
9759
|
memory_type: memoryType,
|
|
9707
9760
|
trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
|
|
@@ -9720,7 +9773,8 @@ async function writeMemory(record) {
|
|
|
9720
9773
|
token_cost: record.token_cost ?? null,
|
|
9721
9774
|
audience: record.audience ?? null,
|
|
9722
9775
|
language_type: record.language_type ?? inferLanguageType(record),
|
|
9723
|
-
parent_memory_id: record.parent_memory_id ?? null
|
|
9776
|
+
parent_memory_id: record.parent_memory_id ?? null,
|
|
9777
|
+
procedure_for: record.procedure_for ?? null
|
|
9724
9778
|
};
|
|
9725
9779
|
_pendingRecords.push(dbRow);
|
|
9726
9780
|
orgBus.emit({
|
|
@@ -9775,6 +9829,8 @@ async function flushBatch() {
|
|
|
9775
9829
|
const sourceType = row.source_type ?? null;
|
|
9776
9830
|
const tier = row.tier ?? 3;
|
|
9777
9831
|
const supersedesId = row.supersedes_id ?? null;
|
|
9832
|
+
const validFrom = row.valid_from ?? row.timestamp;
|
|
9833
|
+
const invalidAt = row.invalid_at ?? null;
|
|
9778
9834
|
const draft = row.draft ? 1 : 0;
|
|
9779
9835
|
const memoryType = row.memory_type ?? "raw";
|
|
9780
9836
|
const trajectory = row.trajectory ?? null;
|
|
@@ -9794,15 +9850,16 @@ async function flushBatch() {
|
|
|
9794
9850
|
const audience = row.audience ?? null;
|
|
9795
9851
|
const languageType = row.language_type ?? null;
|
|
9796
9852
|
const parentMemoryId = row.parent_memory_id ?? null;
|
|
9853
|
+
const procedureFor = row.procedure_for ?? null;
|
|
9797
9854
|
const cols = `id, agent_id, agent_role, session_id, timestamp,
|
|
9798
9855
|
tool_name, project_name,
|
|
9799
9856
|
has_error, raw_text, vector, version, task_id, importance, status,
|
|
9800
9857
|
confidence, last_accessed,
|
|
9801
9858
|
workspace_id, document_id, user_id, char_offset, page_number,
|
|
9802
|
-
source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory, content_hash,
|
|
9859
|
+
source_path, source_type, tier, supersedes_id, valid_from, invalid_at, draft, memory_type, trajectory, content_hash,
|
|
9803
9860
|
intent, outcome, domain, referenced_entities, retrieval_count,
|
|
9804
9861
|
chain_position, review_status, context_window_pct, file_paths, commit_hash,
|
|
9805
|
-
duration_ms, token_cost, audience, language_type, parent_memory_id`;
|
|
9862
|
+
duration_ms, token_cost, audience, language_type, parent_memory_id, procedure_for`;
|
|
9806
9863
|
const metaArgs = [
|
|
9807
9864
|
intent,
|
|
9808
9865
|
outcome,
|
|
@@ -9818,7 +9875,8 @@ async function flushBatch() {
|
|
|
9818
9875
|
tokenCost,
|
|
9819
9876
|
audience,
|
|
9820
9877
|
languageType,
|
|
9821
|
-
parentMemoryId
|
|
9878
|
+
parentMemoryId,
|
|
9879
|
+
procedureFor
|
|
9822
9880
|
];
|
|
9823
9881
|
const baseArgs = [
|
|
9824
9882
|
row.id,
|
|
@@ -9847,6 +9905,8 @@ async function flushBatch() {
|
|
|
9847
9905
|
sourceType,
|
|
9848
9906
|
tier,
|
|
9849
9907
|
supersedesId,
|
|
9908
|
+
validFrom,
|
|
9909
|
+
invalidAt,
|
|
9850
9910
|
draft,
|
|
9851
9911
|
memoryType,
|
|
9852
9912
|
trajectory,
|
|
@@ -9854,8 +9914,8 @@ async function flushBatch() {
|
|
|
9854
9914
|
];
|
|
9855
9915
|
return {
|
|
9856
9916
|
sql: hasVector ? `INSERT OR IGNORE INTO memories (${cols})
|
|
9857
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
|
|
9858
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
9917
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
|
|
9918
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
9859
9919
|
args: hasVector ? [...baseArgs, vectorToBlob(row.vector), ...sharedArgs, ...metaArgs] : [...baseArgs, ...sharedArgs, ...metaArgs]
|
|
9860
9920
|
};
|
|
9861
9921
|
};
|
|
@@ -9971,6 +10031,12 @@ async function searchMemories(queryVector, agentId, options) {
|
|
|
9971
10031
|
AND vector IS NOT NULL${statusFilter}${draftFilter}
|
|
9972
10032
|
AND COALESCE(confidence, 0.7) >= 0.3`;
|
|
9973
10033
|
const args2 = [agentId];
|
|
10034
|
+
if (options?.asOf) {
|
|
10035
|
+
sql += ` AND (valid_from IS NULL OR valid_from <= ?) AND (invalid_at IS NULL OR invalid_at > ?)`;
|
|
10036
|
+
args2.push(options.asOf, options.asOf);
|
|
10037
|
+
} else {
|
|
10038
|
+
sql += ` AND invalid_at IS NULL`;
|
|
10039
|
+
}
|
|
9974
10040
|
const scope = buildWikiScopeFilter(options, "");
|
|
9975
10041
|
sql += scope.clause;
|
|
9976
10042
|
args2.push(...scope.args);
|
|
@@ -12351,9 +12417,9 @@ Unclassified: ${unclassified}
|
|
|
12351
12417
|
}
|
|
12352
12418
|
async function exportBatches(options) {
|
|
12353
12419
|
const fs8 = await import("fs");
|
|
12354
|
-
const
|
|
12420
|
+
const path58 = await import("path");
|
|
12355
12421
|
const client = getClient();
|
|
12356
|
-
const outDir =
|
|
12422
|
+
const outDir = path58.join(process.cwd(), "exe/output/classifications/input");
|
|
12357
12423
|
fs8.mkdirSync(outDir, { recursive: true });
|
|
12358
12424
|
const countResult = await client.execute({
|
|
12359
12425
|
sql: "SELECT COUNT(*) as cnt FROM memories WHERE intent IS NULL AND outcome IS NULL AND domain IS NULL",
|
|
@@ -12377,7 +12443,7 @@ async function exportBatches(options) {
|
|
|
12377
12443
|
const text = String(row.text || "").replace(/\n/g, " ");
|
|
12378
12444
|
return JSON.stringify({ id: row.id, text });
|
|
12379
12445
|
});
|
|
12380
|
-
const batchFile =
|
|
12446
|
+
const batchFile = path58.join(outDir, `batch-${String(batchNum).padStart(4, "0")}.jsonl`);
|
|
12381
12447
|
fs8.writeFileSync(batchFile, lines.join("\n") + "\n");
|
|
12382
12448
|
exported += batch.rows.length;
|
|
12383
12449
|
offset += options.batchSize;
|
|
@@ -12393,7 +12459,7 @@ async function exportBatches(options) {
|
|
|
12393
12459
|
}
|
|
12394
12460
|
async function importClassifications(importDir) {
|
|
12395
12461
|
const fs8 = await import("fs");
|
|
12396
|
-
const
|
|
12462
|
+
const path58 = await import("path");
|
|
12397
12463
|
const client = getClient();
|
|
12398
12464
|
const files = fs8.readdirSync(importDir).filter((f) => f.endsWith(".jsonl")).sort();
|
|
12399
12465
|
process.stderr.write(`[backfill-metadata] Found ${files.length} JSONL files to import from ${importDir}
|
|
@@ -12401,7 +12467,7 @@ async function importClassifications(importDir) {
|
|
|
12401
12467
|
let imported = 0;
|
|
12402
12468
|
let invalid = 0;
|
|
12403
12469
|
for (const file of files) {
|
|
12404
|
-
const lines = fs8.readFileSync(
|
|
12470
|
+
const lines = fs8.readFileSync(path58.join(importDir, file), "utf-8").split("\n").filter(Boolean);
|
|
12405
12471
|
for (const line of lines) {
|
|
12406
12472
|
try {
|
|
12407
12473
|
const rec = JSON.parse(line);
|
|
@@ -13572,8 +13638,8 @@ function logQueue(msg) {
|
|
|
13572
13638
|
process.stderr.write(`[intercom-queue] ${msg}
|
|
13573
13639
|
`);
|
|
13574
13640
|
try {
|
|
13575
|
-
const { appendFileSync:
|
|
13576
|
-
|
|
13641
|
+
const { appendFileSync: appendFileSync4 } = __require("fs");
|
|
13642
|
+
appendFileSync4(INTERCOM_LOG, line);
|
|
13577
13643
|
} catch {
|
|
13578
13644
|
}
|
|
13579
13645
|
}
|
|
@@ -14131,8 +14197,8 @@ ${scopeMismatchWarning}` : scopeMismatchWarning;
|
|
|
14131
14197
|
const complexity = input.complexity ?? "standard";
|
|
14132
14198
|
const sessionScope = earlySessionScope;
|
|
14133
14199
|
await client.execute({
|
|
14134
|
-
sql: `INSERT INTO tasks (id, title, assigned_to, assigned_by, project_name, priority, status, task_file, blocked_by, parent_task_id, reviewer, context, complexity, budget_tokens, budget_fallback_model, tokens_used, tokens_warned_at, session_scope, created_at, updated_at)
|
|
14135
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
14200
|
+
sql: `INSERT INTO tasks (id, title, assigned_to, assigned_by, project_name, priority, status, task_file, blocked_by, parent_task_id, reviewer, context, complexity, budget_tokens, budget_fallback_model, tokens_used, tokens_warned_at, session_scope, spawn_runtime, spawn_model, created_at, updated_at)
|
|
14201
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
14136
14202
|
args: [
|
|
14137
14203
|
id,
|
|
14138
14204
|
input.title,
|
|
@@ -14152,6 +14218,8 @@ ${scopeMismatchWarning}` : scopeMismatchWarning;
|
|
|
14152
14218
|
0,
|
|
14153
14219
|
null,
|
|
14154
14220
|
sessionScope,
|
|
14221
|
+
input.spawnRuntime ?? null,
|
|
14222
|
+
input.spawnModel ?? null,
|
|
14155
14223
|
now2,
|
|
14156
14224
|
now2
|
|
14157
14225
|
]
|
|
@@ -14208,7 +14276,9 @@ ${input.context}
|
|
|
14208
14276
|
budgetTokens: input.budgetTokens ?? null,
|
|
14209
14277
|
budgetFallbackModel: input.budgetFallbackModel ?? null,
|
|
14210
14278
|
tokensUsed: 0,
|
|
14211
|
-
tokensWarnedAt: null
|
|
14279
|
+
tokensWarnedAt: null,
|
|
14280
|
+
spawnRuntime: input.spawnRuntime ?? null,
|
|
14281
|
+
spawnModel: input.spawnModel ?? null
|
|
14212
14282
|
};
|
|
14213
14283
|
}
|
|
14214
14284
|
async function listTasks(input) {
|
|
@@ -14258,7 +14328,9 @@ async function listTasks(input) {
|
|
|
14258
14328
|
budgetTokens: r.budget_tokens !== null ? Number(r.budget_tokens) : null,
|
|
14259
14329
|
budgetFallbackModel: r.budget_fallback_model !== null ? String(r.budget_fallback_model) : null,
|
|
14260
14330
|
tokensUsed: Number(r.tokens_used ?? 0),
|
|
14261
|
-
tokensWarnedAt: r.tokens_warned_at !== null ? Number(r.tokens_warned_at) : null
|
|
14331
|
+
tokensWarnedAt: r.tokens_warned_at !== null ? Number(r.tokens_warned_at) : null,
|
|
14332
|
+
spawnRuntime: r.spawn_runtime !== null && r.spawn_runtime !== void 0 ? String(r.spawn_runtime) : null,
|
|
14333
|
+
spawnModel: r.spawn_model !== null && r.spawn_model !== void 0 ? String(r.spawn_model) : null
|
|
14262
14334
|
}));
|
|
14263
14335
|
}
|
|
14264
14336
|
function isTmuxSessionAlive(identifier) {
|
|
@@ -15039,10 +15111,10 @@ async function disposeEmbedder() {
|
|
|
15039
15111
|
async function embedDirect(text) {
|
|
15040
15112
|
const llamaCpp = await import("node-llama-cpp");
|
|
15041
15113
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
15042
|
-
const { existsSync:
|
|
15043
|
-
const
|
|
15044
|
-
const modelPath =
|
|
15045
|
-
if (!
|
|
15114
|
+
const { existsSync: existsSync43 } = await import("fs");
|
|
15115
|
+
const path58 = await import("path");
|
|
15116
|
+
const modelPath = path58.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
15117
|
+
if (!existsSync43(modelPath)) {
|
|
15046
15118
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
15047
15119
|
}
|
|
15048
15120
|
const llama = await llamaCpp.getLlama();
|
|
@@ -15555,6 +15627,8 @@ async function updateTask(input) {
|
|
|
15555
15627
|
budgetFallbackModel: row.budget_fallback_model !== void 0 && row.budget_fallback_model !== null ? String(row.budget_fallback_model) : null,
|
|
15556
15628
|
tokensUsed: Number(row.tokens_used ?? 0),
|
|
15557
15629
|
tokensWarnedAt: row.tokens_warned_at !== void 0 && row.tokens_warned_at !== null ? Number(row.tokens_warned_at) : null,
|
|
15630
|
+
spawnRuntime: row.spawn_runtime !== void 0 && row.spawn_runtime !== null ? String(row.spawn_runtime) : null,
|
|
15631
|
+
spawnModel: row.spawn_model !== void 0 && row.spawn_model !== null ? String(row.spawn_model) : null,
|
|
15558
15632
|
nextTask
|
|
15559
15633
|
};
|
|
15560
15634
|
}
|
|
@@ -16047,9 +16121,13 @@ function getDispatchedBy(sessionKey) {
|
|
|
16047
16121
|
}
|
|
16048
16122
|
}
|
|
16049
16123
|
function resolveExeSession() {
|
|
16124
|
+
if (process.env.EXE_SESSION_NAME) {
|
|
16125
|
+
return extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
|
|
16126
|
+
}
|
|
16050
16127
|
const mySession = getMySession();
|
|
16051
16128
|
if (!mySession) return null;
|
|
16052
16129
|
const fromSessionName = extractRootExe(mySession);
|
|
16130
|
+
let candidate = null;
|
|
16053
16131
|
try {
|
|
16054
16132
|
const key = getSessionKey();
|
|
16055
16133
|
const parentExe = getParentExe(key);
|
|
@@ -16060,13 +16138,47 @@ function resolveExeSession() {
|
|
|
16060
16138
|
`[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
|
|
16061
16139
|
`
|
|
16062
16140
|
);
|
|
16063
|
-
|
|
16141
|
+
candidate = fromSessionName;
|
|
16142
|
+
} else {
|
|
16143
|
+
candidate = fromCache;
|
|
16064
16144
|
}
|
|
16065
|
-
return fromCache;
|
|
16066
16145
|
}
|
|
16067
16146
|
} catch {
|
|
16068
16147
|
}
|
|
16069
|
-
|
|
16148
|
+
if (!candidate) {
|
|
16149
|
+
candidate = fromSessionName ?? mySession;
|
|
16150
|
+
}
|
|
16151
|
+
if (candidate && isRootSession(candidate)) {
|
|
16152
|
+
try {
|
|
16153
|
+
const transport = getTransport();
|
|
16154
|
+
const liveSessions = transport.listSessions();
|
|
16155
|
+
if (!liveSessions.includes(candidate)) {
|
|
16156
|
+
const liveRoots = liveSessions.filter((s) => isRootSession(s));
|
|
16157
|
+
if (liveRoots.length === 1) {
|
|
16158
|
+
process.stderr.write(
|
|
16159
|
+
`[tmux-routing] WARN: resolved session "${candidate}" is dead. Using live coordinator "${liveRoots[0]}".
|
|
16160
|
+
`
|
|
16161
|
+
);
|
|
16162
|
+
return liveRoots[0];
|
|
16163
|
+
} else if (liveRoots.length > 1) {
|
|
16164
|
+
const base = candidate.replace(/\d+$/, "");
|
|
16165
|
+
const match = liveRoots.find((s) => s.startsWith(base));
|
|
16166
|
+
const chosen = match ?? liveRoots[0];
|
|
16167
|
+
process.stderr.write(
|
|
16168
|
+
`[tmux-routing] WARN: resolved session "${candidate}" is dead. ${liveRoots.length} live roots found, using "${chosen}".
|
|
16169
|
+
`
|
|
16170
|
+
);
|
|
16171
|
+
return chosen;
|
|
16172
|
+
}
|
|
16173
|
+
process.stderr.write(
|
|
16174
|
+
`[tmux-routing] WARN: resolved session "${candidate}" is dead and no live coordinator found.
|
|
16175
|
+
`
|
|
16176
|
+
);
|
|
16177
|
+
}
|
|
16178
|
+
} catch {
|
|
16179
|
+
}
|
|
16180
|
+
}
|
|
16181
|
+
return candidate;
|
|
16070
16182
|
}
|
|
16071
16183
|
function isEmployeeAlive(sessionName) {
|
|
16072
16184
|
return getTransport().isAlive(sessionName);
|
|
@@ -16468,7 +16580,12 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
16468
16580
|
}
|
|
16469
16581
|
const spawnCwd = opts?.cwd ?? projectDir;
|
|
16470
16582
|
const useExeAgent = !!(opts?.model && opts?.provider);
|
|
16471
|
-
const
|
|
16583
|
+
const baseRtConfig = getAgentRuntime(employeeName);
|
|
16584
|
+
const agentRtConfig = {
|
|
16585
|
+
...baseRtConfig,
|
|
16586
|
+
...opts?.runtimeOverride ? { runtime: opts.runtimeOverride } : {},
|
|
16587
|
+
...opts?.modelOverride ? { model: opts.modelOverride } : {}
|
|
16588
|
+
};
|
|
16472
16589
|
const useCodex = !useExeAgent && agentRtConfig.runtime === "codex";
|
|
16473
16590
|
const useOpencode = !useExeAgent && !useCodex && agentRtConfig.runtime === "opencode";
|
|
16474
16591
|
const ccProvider = useExeAgent || useCodex || useOpencode ? DEFAULT_PROVIDER : detectActiveProvider();
|
|
@@ -18012,17 +18129,980 @@ var init_exe_rename = __esm({
|
|
|
18012
18129
|
}
|
|
18013
18130
|
});
|
|
18014
18131
|
|
|
18132
|
+
// src/lib/drift-probes.ts
|
|
18133
|
+
async function getRecentMemories(agentId, limit) {
|
|
18134
|
+
try {
|
|
18135
|
+
const { getClient: getClient2, isInitialized: isInitialized2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
18136
|
+
if (!isInitialized2()) return { count: 0, texts: [] };
|
|
18137
|
+
const client = getClient2();
|
|
18138
|
+
const result = await client.execute({
|
|
18139
|
+
sql: `SELECT text FROM memories WHERE agent_id = ? ORDER BY created_at DESC LIMIT ?`,
|
|
18140
|
+
args: [agentId, limit]
|
|
18141
|
+
});
|
|
18142
|
+
const texts = result.rows.map((r) => String(r.text ?? ""));
|
|
18143
|
+
return { count: texts.length, texts };
|
|
18144
|
+
} catch {
|
|
18145
|
+
return { count: 0, texts: [] };
|
|
18146
|
+
}
|
|
18147
|
+
}
|
|
18148
|
+
async function getDecisionCount(agentId) {
|
|
18149
|
+
try {
|
|
18150
|
+
const { getClient: getClient2, isInitialized: isInitialized2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
18151
|
+
if (!isInitialized2()) return 0;
|
|
18152
|
+
const client = getClient2();
|
|
18153
|
+
const result = await client.execute({
|
|
18154
|
+
sql: `SELECT COUNT(*) as cnt FROM memories WHERE agent_id = ? AND memory_type = 'decision'`,
|
|
18155
|
+
args: [agentId]
|
|
18156
|
+
});
|
|
18157
|
+
return Number(result.rows[0]?.cnt ?? 0);
|
|
18158
|
+
} catch {
|
|
18159
|
+
return 0;
|
|
18160
|
+
}
|
|
18161
|
+
}
|
|
18162
|
+
function probeContinuity(ctx) {
|
|
18163
|
+
const memCount = ctx.recentMemoryCount;
|
|
18164
|
+
let score;
|
|
18165
|
+
let detail;
|
|
18166
|
+
if (memCount >= 20) {
|
|
18167
|
+
score = 95;
|
|
18168
|
+
detail = `${memCount} recent memories \u2014 strong continuity`;
|
|
18169
|
+
} else if (memCount >= 10) {
|
|
18170
|
+
score = 85;
|
|
18171
|
+
detail = `${memCount} recent memories \u2014 adequate continuity`;
|
|
18172
|
+
} else if (memCount >= 3) {
|
|
18173
|
+
score = 70;
|
|
18174
|
+
detail = `${memCount} recent memories \u2014 limited continuity`;
|
|
18175
|
+
} else if (memCount >= 1) {
|
|
18176
|
+
score = 50;
|
|
18177
|
+
detail = `${memCount} recent memories \u2014 weak continuity`;
|
|
18178
|
+
} else {
|
|
18179
|
+
score = 30;
|
|
18180
|
+
detail = "No recent memories \u2014 agent may lack session context";
|
|
18181
|
+
}
|
|
18182
|
+
if (ctx.storedDecisionCount > 0) {
|
|
18183
|
+
score = Math.min(100, score + 5);
|
|
18184
|
+
detail += ` (+${ctx.storedDecisionCount} decisions)`;
|
|
18185
|
+
}
|
|
18186
|
+
return { axis: "continuity", score, detail };
|
|
18187
|
+
}
|
|
18188
|
+
function probeConsistency(ctx) {
|
|
18189
|
+
if (!ctx.identityBody || ctx.recentMemoryTexts.length === 0) {
|
|
18190
|
+
return {
|
|
18191
|
+
axis: "consistency",
|
|
18192
|
+
score: ctx.identityBody ? 80 : 50,
|
|
18193
|
+
detail: ctx.identityBody ? "No memories to check against identity" : "No identity file \u2014 cannot assess consistency"
|
|
18194
|
+
};
|
|
18195
|
+
}
|
|
18196
|
+
const identityTerms = extractKeyTerms(ctx.identityBody);
|
|
18197
|
+
if (identityTerms.length === 0) {
|
|
18198
|
+
return { axis: "consistency", score: 75, detail: "Identity has no extractable key terms" };
|
|
18199
|
+
}
|
|
18200
|
+
const memoryText = ctx.recentMemoryTexts.join(" ").toLowerCase();
|
|
18201
|
+
let matched = 0;
|
|
18202
|
+
for (const term of identityTerms) {
|
|
18203
|
+
if (memoryText.includes(term.toLowerCase())) matched++;
|
|
18204
|
+
}
|
|
18205
|
+
const ratio = matched / identityTerms.length;
|
|
18206
|
+
const score = Math.round(50 + ratio * 50);
|
|
18207
|
+
const detail = `${matched}/${identityTerms.length} identity terms found in recent memories`;
|
|
18208
|
+
return { axis: "consistency", score, detail };
|
|
18209
|
+
}
|
|
18210
|
+
function probeRoleFidelity(ctx) {
|
|
18211
|
+
const flags = [];
|
|
18212
|
+
if (!ctx.identityBody) {
|
|
18213
|
+
return {
|
|
18214
|
+
axis: "role-fidelity",
|
|
18215
|
+
score: 40,
|
|
18216
|
+
detail: `No identity file for ${ctx.agent.name}`
|
|
18217
|
+
};
|
|
18218
|
+
}
|
|
18219
|
+
let score = 100;
|
|
18220
|
+
const identityLower = ctx.identityBody.toLowerCase();
|
|
18221
|
+
const rosterRole = ctx.agent.role.toLowerCase();
|
|
18222
|
+
if (!identityLower.includes(rosterRole)) {
|
|
18223
|
+
score -= 15;
|
|
18224
|
+
flags.push(`Identity does not mention role "${ctx.agent.role}"`);
|
|
18225
|
+
}
|
|
18226
|
+
if (!identityLower.includes(ctx.agent.name.toLowerCase())) {
|
|
18227
|
+
score -= 10;
|
|
18228
|
+
flags.push(`Identity does not mention agent name "${ctx.agent.name}"`);
|
|
18229
|
+
}
|
|
18230
|
+
const hasScopeBoundary = identityLower.includes("do not") || identityLower.includes("don't") || identityLower.includes("not your") || identityLower.includes("outside your") || identityLower.includes("you do not");
|
|
18231
|
+
if (!hasScopeBoundary) {
|
|
18232
|
+
score -= 10;
|
|
18233
|
+
flags.push("Identity lacks explicit scope boundaries (what NOT to do)");
|
|
18234
|
+
}
|
|
18235
|
+
if (ctx.agent.systemPrompt) {
|
|
18236
|
+
const promptTerms = extractKeyTerms(ctx.agent.systemPrompt);
|
|
18237
|
+
const identityText = ctx.identityBody.toLowerCase();
|
|
18238
|
+
let promptMatched = 0;
|
|
18239
|
+
for (const term of promptTerms.slice(0, 20)) {
|
|
18240
|
+
if (identityText.includes(term.toLowerCase())) promptMatched++;
|
|
18241
|
+
}
|
|
18242
|
+
const promptRatio = promptTerms.length > 0 ? promptMatched / Math.min(promptTerms.length, 20) : 1;
|
|
18243
|
+
if (promptRatio < 0.3) {
|
|
18244
|
+
score -= 15;
|
|
18245
|
+
flags.push("Identity content diverges significantly from roster systemPrompt");
|
|
18246
|
+
}
|
|
18247
|
+
}
|
|
18248
|
+
if (ctx.recentMemoryTexts.length > 5) {
|
|
18249
|
+
const roleKeywords = extractRoleKeywords(ctx.agent.role);
|
|
18250
|
+
const memoryText = ctx.recentMemoryTexts.join(" ").toLowerCase();
|
|
18251
|
+
let domainHits = 0;
|
|
18252
|
+
for (const kw of roleKeywords) {
|
|
18253
|
+
if (memoryText.includes(kw)) domainHits++;
|
|
18254
|
+
}
|
|
18255
|
+
const domainRatio = roleKeywords.length > 0 ? domainHits / roleKeywords.length : 1;
|
|
18256
|
+
if (domainRatio < 0.2) {
|
|
18257
|
+
score -= 10;
|
|
18258
|
+
flags.push(`Recent work shows little overlap with ${ctx.agent.role} domain keywords`);
|
|
18259
|
+
}
|
|
18260
|
+
}
|
|
18261
|
+
score = Math.max(0, Math.min(100, score));
|
|
18262
|
+
const detail = flags.length > 0 ? flags.join("; ") : "Identity aligns with roster definition";
|
|
18263
|
+
return { axis: "role-fidelity", score, detail };
|
|
18264
|
+
}
|
|
18265
|
+
function probeWorldModel(ctx) {
|
|
18266
|
+
if (!ctx.identityBody) {
|
|
18267
|
+
return {
|
|
18268
|
+
axis: "world-model",
|
|
18269
|
+
score: 50,
|
|
18270
|
+
detail: "No identity file \u2014 cannot assess org awareness"
|
|
18271
|
+
};
|
|
18272
|
+
}
|
|
18273
|
+
const identityLower = ctx.identityBody.toLowerCase();
|
|
18274
|
+
const flags = [];
|
|
18275
|
+
let score = 100;
|
|
18276
|
+
const otherAgents = ctx.allEmployees.filter((e) => e.name !== ctx.agent.name);
|
|
18277
|
+
let mentionedAgents = 0;
|
|
18278
|
+
for (const other of otherAgents) {
|
|
18279
|
+
if (identityLower.includes(other.name.toLowerCase())) mentionedAgents++;
|
|
18280
|
+
}
|
|
18281
|
+
if (otherAgents.length > 0) {
|
|
18282
|
+
const mentionRatio = mentionedAgents / otherAgents.length;
|
|
18283
|
+
if (mentionRatio < 0.3) {
|
|
18284
|
+
score -= 15;
|
|
18285
|
+
flags.push(`Identity mentions ${mentionedAgents}/${otherAgents.length} team members`);
|
|
18286
|
+
}
|
|
18287
|
+
}
|
|
18288
|
+
const hasReportingChain = identityLower.includes("report") || identityLower.includes("manager") || identityLower.includes("coo") || identityLower.includes("coordinator");
|
|
18289
|
+
if (!hasReportingChain) {
|
|
18290
|
+
score -= 10;
|
|
18291
|
+
flags.push("Identity does not reference reporting chain");
|
|
18292
|
+
}
|
|
18293
|
+
const orgTerms = ["exe-os", "exe-wiki", "exe-crm", "askexe"];
|
|
18294
|
+
let orgMentions = 0;
|
|
18295
|
+
for (const term of orgTerms) {
|
|
18296
|
+
if (identityLower.includes(term)) orgMentions++;
|
|
18297
|
+
}
|
|
18298
|
+
if (orgMentions === 0) {
|
|
18299
|
+
score -= 5;
|
|
18300
|
+
flags.push("Identity does not reference any org products");
|
|
18301
|
+
}
|
|
18302
|
+
score = Math.max(0, Math.min(100, score));
|
|
18303
|
+
const detail = flags.length > 0 ? flags.join("; ") : "Identity reflects org structure";
|
|
18304
|
+
return { axis: "world-model", score, detail };
|
|
18305
|
+
}
|
|
18306
|
+
function extractKeyTerms(text) {
|
|
18307
|
+
const stopwords = /* @__PURE__ */ new Set([
|
|
18308
|
+
"the",
|
|
18309
|
+
"and",
|
|
18310
|
+
"for",
|
|
18311
|
+
"are",
|
|
18312
|
+
"but",
|
|
18313
|
+
"not",
|
|
18314
|
+
"you",
|
|
18315
|
+
"all",
|
|
18316
|
+
"any",
|
|
18317
|
+
"can",
|
|
18318
|
+
"had",
|
|
18319
|
+
"her",
|
|
18320
|
+
"was",
|
|
18321
|
+
"one",
|
|
18322
|
+
"our",
|
|
18323
|
+
"out",
|
|
18324
|
+
"has",
|
|
18325
|
+
"his",
|
|
18326
|
+
"how",
|
|
18327
|
+
"its",
|
|
18328
|
+
"may",
|
|
18329
|
+
"new",
|
|
18330
|
+
"now",
|
|
18331
|
+
"old",
|
|
18332
|
+
"see",
|
|
18333
|
+
"way",
|
|
18334
|
+
"who",
|
|
18335
|
+
"did",
|
|
18336
|
+
"get",
|
|
18337
|
+
"got",
|
|
18338
|
+
"let",
|
|
18339
|
+
"say",
|
|
18340
|
+
"she",
|
|
18341
|
+
"too",
|
|
18342
|
+
"use",
|
|
18343
|
+
"with",
|
|
18344
|
+
"this",
|
|
18345
|
+
"that",
|
|
18346
|
+
"from",
|
|
18347
|
+
"they",
|
|
18348
|
+
"been",
|
|
18349
|
+
"have",
|
|
18350
|
+
"will",
|
|
18351
|
+
"your",
|
|
18352
|
+
"what",
|
|
18353
|
+
"when",
|
|
18354
|
+
"make",
|
|
18355
|
+
"like",
|
|
18356
|
+
"just",
|
|
18357
|
+
"over",
|
|
18358
|
+
"such",
|
|
18359
|
+
"take",
|
|
18360
|
+
"than",
|
|
18361
|
+
"them",
|
|
18362
|
+
"very",
|
|
18363
|
+
"some",
|
|
18364
|
+
"could",
|
|
18365
|
+
"into",
|
|
18366
|
+
"then",
|
|
18367
|
+
"more",
|
|
18368
|
+
"also",
|
|
18369
|
+
"after",
|
|
18370
|
+
"should",
|
|
18371
|
+
"would",
|
|
18372
|
+
"about",
|
|
18373
|
+
"their",
|
|
18374
|
+
"which",
|
|
18375
|
+
"these",
|
|
18376
|
+
"other",
|
|
18377
|
+
"every",
|
|
18378
|
+
"does",
|
|
18379
|
+
"being",
|
|
18380
|
+
"those",
|
|
18381
|
+
"never",
|
|
18382
|
+
"before",
|
|
18383
|
+
"through"
|
|
18384
|
+
]);
|
|
18385
|
+
const words = text.replace(/[^\w\s-]/g, " ").split(/\s+/).filter((w) => w.length > 3 && !stopwords.has(w.toLowerCase()));
|
|
18386
|
+
return [...new Set(words.map((w) => w.toLowerCase()))].slice(0, 50);
|
|
18387
|
+
}
|
|
18388
|
+
function extractRoleKeywords(role) {
|
|
18389
|
+
const roleKeywordMap = {
|
|
18390
|
+
"coo": ["coordinate", "review", "status", "team", "task", "priority", "dispatch"],
|
|
18391
|
+
"cto": ["architecture", "code", "technical", "system", "design", "review", "security"],
|
|
18392
|
+
"cmo": ["marketing", "brand", "content", "design", "seo", "social", "campaign"],
|
|
18393
|
+
"principal engineer": ["code", "implement", "test", "fix", "feature", "refactor", "build"],
|
|
18394
|
+
"staff code reviewer": ["review", "code", "quality", "issue", "fix", "pattern"],
|
|
18395
|
+
"content production specialist": ["video", "image", "render", "content", "produce", "media"],
|
|
18396
|
+
"ai product lead": ["competitive", "analysis", "feature", "product", "research", "repo"]
|
|
18397
|
+
};
|
|
18398
|
+
const normalized = role.toLowerCase();
|
|
18399
|
+
return roleKeywordMap[normalized] ?? normalized.split(/\s+/).filter((w) => w.length > 2);
|
|
18400
|
+
}
|
|
18401
|
+
async function runDriftProbes(options = {}) {
|
|
18402
|
+
const employees = loadEmployeesSync();
|
|
18403
|
+
const targetAgents = options.agentId ? employees.filter((e) => e.name === options.agentId) : employees;
|
|
18404
|
+
if (targetAgents.length === 0) {
|
|
18405
|
+
return [];
|
|
18406
|
+
}
|
|
18407
|
+
const axes = options.axes ?? ["continuity", "consistency", "role-fidelity", "world-model"];
|
|
18408
|
+
const results = [];
|
|
18409
|
+
for (const agent of targetAgents) {
|
|
18410
|
+
const identity = getIdentity(agent.name);
|
|
18411
|
+
const { count: memCount, texts: memTexts } = await getRecentMemories(agent.name, 50);
|
|
18412
|
+
const decisionCount = await getDecisionCount(agent.name);
|
|
18413
|
+
const ctx = {
|
|
18414
|
+
agent,
|
|
18415
|
+
identityBody: identity?.body ?? null,
|
|
18416
|
+
identityRole: identity?.frontmatter.role ?? null,
|
|
18417
|
+
recentMemoryCount: memCount,
|
|
18418
|
+
recentMemoryTexts: memTexts,
|
|
18419
|
+
storedDecisionCount: decisionCount,
|
|
18420
|
+
allEmployees: employees
|
|
18421
|
+
};
|
|
18422
|
+
const probes = [];
|
|
18423
|
+
const probeFns = {
|
|
18424
|
+
continuity: probeContinuity,
|
|
18425
|
+
consistency: probeConsistency,
|
|
18426
|
+
"role-fidelity": probeRoleFidelity,
|
|
18427
|
+
"world-model": probeWorldModel
|
|
18428
|
+
};
|
|
18429
|
+
for (const axis of axes) {
|
|
18430
|
+
probes.push(probeFns[axis](ctx));
|
|
18431
|
+
}
|
|
18432
|
+
const scores = {
|
|
18433
|
+
continuity: 0,
|
|
18434
|
+
consistency: 0,
|
|
18435
|
+
"role-fidelity": 0,
|
|
18436
|
+
"world-model": 0
|
|
18437
|
+
};
|
|
18438
|
+
for (const probe of probes) {
|
|
18439
|
+
scores[probe.axis] = probe.score;
|
|
18440
|
+
}
|
|
18441
|
+
const weights = {
|
|
18442
|
+
continuity: 0.2,
|
|
18443
|
+
consistency: 0.2,
|
|
18444
|
+
"role-fidelity": 0.35,
|
|
18445
|
+
"world-model": 0.25
|
|
18446
|
+
};
|
|
18447
|
+
let weightedSum = 0;
|
|
18448
|
+
let weightTotal = 0;
|
|
18449
|
+
for (const axis of axes) {
|
|
18450
|
+
weightedSum += scores[axis] * weights[axis];
|
|
18451
|
+
weightTotal += weights[axis];
|
|
18452
|
+
}
|
|
18453
|
+
const overall = Math.round(weightTotal > 0 ? weightedSum / weightTotal : 0);
|
|
18454
|
+
const flags = [];
|
|
18455
|
+
for (const probe of probes) {
|
|
18456
|
+
if (probe.score < DRIFT_THRESHOLD) {
|
|
18457
|
+
flags.push(`${probe.axis} at ${probe.score}% \u2014 ${probe.detail}`);
|
|
18458
|
+
}
|
|
18459
|
+
}
|
|
18460
|
+
results.push({
|
|
18461
|
+
agent: agent.name,
|
|
18462
|
+
scores,
|
|
18463
|
+
overall,
|
|
18464
|
+
drifting: flags.length > 0,
|
|
18465
|
+
flags
|
|
18466
|
+
});
|
|
18467
|
+
}
|
|
18468
|
+
return results;
|
|
18469
|
+
}
|
|
18470
|
+
var DRIFT_THRESHOLD;
|
|
18471
|
+
var init_drift_probes = __esm({
|
|
18472
|
+
"src/lib/drift-probes.ts"() {
|
|
18473
|
+
"use strict";
|
|
18474
|
+
init_identity();
|
|
18475
|
+
init_employees();
|
|
18476
|
+
DRIFT_THRESHOLD = 80;
|
|
18477
|
+
}
|
|
18478
|
+
});
|
|
18479
|
+
|
|
18480
|
+
// src/bin/exe-drift.ts
|
|
18481
|
+
var exe_drift_exports = {};
|
|
18482
|
+
__export(exe_drift_exports, {
|
|
18483
|
+
runDrift: () => runDrift
|
|
18484
|
+
});
|
|
18485
|
+
function statusIcon(overall) {
|
|
18486
|
+
if (overall >= 80) return "\x1B[32m\u{1F7E2} OK\x1B[0m";
|
|
18487
|
+
if (overall >= 60) return "\x1B[33m\u{1F7E0} WARN\x1B[0m";
|
|
18488
|
+
return "\x1B[31m\u{1F534} DRIFTING\x1B[0m";
|
|
18489
|
+
}
|
|
18490
|
+
function padRight(str, len) {
|
|
18491
|
+
return str.length >= len ? str.slice(0, len) : str + " ".repeat(len - str.length);
|
|
18492
|
+
}
|
|
18493
|
+
function padLeft(str, len) {
|
|
18494
|
+
return str.length >= len ? str : " ".repeat(len - str.length) + str;
|
|
18495
|
+
}
|
|
18496
|
+
function formatTable(results) {
|
|
18497
|
+
const lines = [];
|
|
18498
|
+
const header = padRight("Agent", 10) + AXES.map((a) => padLeft(AXIS_HEADERS[a], 9)).join("") + padLeft("Overall", 9) + " Status";
|
|
18499
|
+
lines.push(header);
|
|
18500
|
+
lines.push("-".repeat(header.length + 10));
|
|
18501
|
+
for (const r of results) {
|
|
18502
|
+
const line = padRight(r.agent, 10) + AXES.map((a) => padLeft(String(r.scores[a]), 9)).join("") + padLeft(String(r.overall), 9) + " " + statusIcon(r.overall);
|
|
18503
|
+
lines.push(line);
|
|
18504
|
+
}
|
|
18505
|
+
return lines.join("\n");
|
|
18506
|
+
}
|
|
18507
|
+
async function runDrift(args2) {
|
|
18508
|
+
const agentIdx = args2.indexOf("--agent");
|
|
18509
|
+
const agentId = agentIdx >= 0 ? args2[agentIdx + 1] : void 0;
|
|
18510
|
+
console.log("\nexe-os doctor --drift\n");
|
|
18511
|
+
const results = await runDriftProbes({ agentId });
|
|
18512
|
+
if (results.length === 0) {
|
|
18513
|
+
console.log("No agents found. Run /exe-team to see available employees.");
|
|
18514
|
+
return;
|
|
18515
|
+
}
|
|
18516
|
+
console.log(formatTable(results));
|
|
18517
|
+
console.log();
|
|
18518
|
+
const flagged = results.filter((r) => r.flags.length > 0);
|
|
18519
|
+
if (flagged.length > 0) {
|
|
18520
|
+
for (const r of flagged) {
|
|
18521
|
+
for (const flag of r.flags) {
|
|
18522
|
+
console.log(`\u26A0 ${r.agent}: ${flag}`);
|
|
18523
|
+
}
|
|
18524
|
+
}
|
|
18525
|
+
console.log();
|
|
18526
|
+
} else {
|
|
18527
|
+
console.log("All agents within role-fidelity thresholds.\n");
|
|
18528
|
+
}
|
|
18529
|
+
}
|
|
18530
|
+
var AXES, AXIS_HEADERS;
|
|
18531
|
+
var init_exe_drift = __esm({
|
|
18532
|
+
"src/bin/exe-drift.ts"() {
|
|
18533
|
+
"use strict";
|
|
18534
|
+
init_drift_probes();
|
|
18535
|
+
AXES = ["continuity", "consistency", "role-fidelity", "world-model"];
|
|
18536
|
+
AXIS_HEADERS = {
|
|
18537
|
+
continuity: "Cont.",
|
|
18538
|
+
consistency: "Consist.",
|
|
18539
|
+
"role-fidelity": "Role",
|
|
18540
|
+
"world-model": "World"
|
|
18541
|
+
};
|
|
18542
|
+
}
|
|
18543
|
+
});
|
|
18544
|
+
|
|
18545
|
+
// src/lib/mcp-transport-health.ts
|
|
18546
|
+
import { appendFileSync as appendFileSync3, closeSync as closeSync3, existsSync as existsSync30, mkdirSync as mkdirSync22, openSync as openSync3, readFileSync as readFileSync26, readSync, statSync as statSync7, writeFileSync as writeFileSync23 } from "fs";
|
|
18547
|
+
import path37 from "path";
|
|
18548
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
18549
|
+
function parsePositiveInt(value, fallback) {
|
|
18550
|
+
if (!value) return fallback;
|
|
18551
|
+
const parsed = Number.parseInt(value, 10);
|
|
18552
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
18553
|
+
}
|
|
18554
|
+
function readTailText(filePath, maxBytes) {
|
|
18555
|
+
const stats = statSync7(filePath);
|
|
18556
|
+
if (stats.size <= maxBytes) return readFileSync26(filePath, "utf8");
|
|
18557
|
+
const fd = openSync3(filePath, "r");
|
|
18558
|
+
try {
|
|
18559
|
+
const buffer = Buffer.allocUnsafe(maxBytes);
|
|
18560
|
+
readSync(fd, buffer, 0, maxBytes, stats.size - maxBytes);
|
|
18561
|
+
const text = buffer.toString("utf8");
|
|
18562
|
+
const firstNewline = text.indexOf("\n");
|
|
18563
|
+
return firstNewline >= 0 ? text.slice(firstNewline + 1) : text;
|
|
18564
|
+
} finally {
|
|
18565
|
+
closeSync3(fd);
|
|
18566
|
+
}
|
|
18567
|
+
}
|
|
18568
|
+
function readPackageVersion2() {
|
|
18569
|
+
let dir = path37.dirname(fileURLToPath5(import.meta.url));
|
|
18570
|
+
const { root } = path37.parse(dir);
|
|
18571
|
+
while (dir !== root) {
|
|
18572
|
+
const pkgPath = path37.join(dir, "package.json");
|
|
18573
|
+
if (existsSync30(pkgPath)) {
|
|
18574
|
+
try {
|
|
18575
|
+
const parsed = JSON.parse(readFileSync26(pkgPath, "utf8"));
|
|
18576
|
+
return parsed.version ?? "unknown";
|
|
18577
|
+
} catch {
|
|
18578
|
+
return "unknown";
|
|
18579
|
+
}
|
|
18580
|
+
}
|
|
18581
|
+
dir = path37.dirname(dir);
|
|
18582
|
+
}
|
|
18583
|
+
return "unknown";
|
|
18584
|
+
}
|
|
18585
|
+
function parseMcpHttpEventLines(raw, limit = 200) {
|
|
18586
|
+
const lines = raw.trim().split("\n").filter(Boolean).slice(-limit);
|
|
18587
|
+
const events = [];
|
|
18588
|
+
for (const line of lines) {
|
|
18589
|
+
try {
|
|
18590
|
+
events.push(JSON.parse(line));
|
|
18591
|
+
} catch {
|
|
18592
|
+
}
|
|
18593
|
+
}
|
|
18594
|
+
return events;
|
|
18595
|
+
}
|
|
18596
|
+
function readMcpHttpEvents(limit = 200) {
|
|
18597
|
+
if (!existsSync30(MCP_HTTP_EVENTS_PATH)) return [];
|
|
18598
|
+
const raw = readTailText(MCP_HTTP_EVENTS_PATH, EVENT_READ_BYTES);
|
|
18599
|
+
return parseMcpHttpEventLines(raw, limit);
|
|
18600
|
+
}
|
|
18601
|
+
function summarizeMcpTransport(events = readMcpHttpEvents()) {
|
|
18602
|
+
const lastServerStartIndex = events.map((event) => event.message).lastIndexOf("server_started");
|
|
18603
|
+
const scopedEvents = lastServerStartIndex >= 0 ? events.slice(lastServerStartIndex) : events;
|
|
18604
|
+
const activeSessionEvent = [...scopedEvents].reverse().find((event) => typeof event.activeSessions === "number");
|
|
18605
|
+
const successfulToolCalls = scopedEvents.filter((event) => event.message === "tool_call_ok");
|
|
18606
|
+
const lastSuccessfulToolCall = successfulToolCalls.at(-1) ?? null;
|
|
18607
|
+
const summary = {
|
|
18608
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
18609
|
+
version: readPackageVersion2(),
|
|
18610
|
+
scope: "local",
|
|
18611
|
+
privacy: "metadata_only",
|
|
18612
|
+
eventLogPath: MCP_HTTP_EVENTS_PATH,
|
|
18613
|
+
activeSessions: activeSessionEvent?.activeSessions ?? null,
|
|
18614
|
+
sessionsCreated: scopedEvents.filter((event) => event.message === "session_initialized").length,
|
|
18615
|
+
sessionsClosed: scopedEvents.filter((event) => event.message === "session_closed").length,
|
|
18616
|
+
missingSessionErrors: scopedEvents.filter((event) => event.message?.includes("missing MCP session")).length,
|
|
18617
|
+
staleSessionErrors: scopedEvents.filter((event) => event.message?.includes("stale or unknown")).length,
|
|
18618
|
+
transportErrors: scopedEvents.filter((event) => event.level === "warn" || event.level === "error").length,
|
|
18619
|
+
lastSuccessfulHandshake: scopedEvents.filter((event) => event.message === "session_initialized").at(-1)?.timestamp ?? null,
|
|
18620
|
+
lastSuccessfulToolCall: lastSuccessfulToolCall?.timestamp ?? null,
|
|
18621
|
+
lastSuccessfulToolCallByRuntime: {},
|
|
18622
|
+
lastSuccessfulToolCallByAgent: {},
|
|
18623
|
+
lastSuccessfulToolCallByFamily: {},
|
|
18624
|
+
activeSessionsByRuntime: {},
|
|
18625
|
+
recentWarnings: scopedEvents.filter((event) => event.level === "warn" || event.level === "error").slice(-10),
|
|
18626
|
+
remediation: [
|
|
18627
|
+
"If MCP tools fail with missing/stale session, reconnect the MCP client (/mcp or restart the runtime).",
|
|
18628
|
+
"Do not access the database directly; MCP is the only data interface.",
|
|
18629
|
+
"Check get_daemon_health or exe-healthcheck for daemon/MCP endpoint status before retrying work."
|
|
18630
|
+
]
|
|
18631
|
+
};
|
|
18632
|
+
const createdBySession = /* @__PURE__ */ new Map();
|
|
18633
|
+
for (const event of scopedEvents) {
|
|
18634
|
+
if (event.message === "session_initialized" && event.sessionId) createdBySession.set(event.sessionId, event);
|
|
18635
|
+
if ((event.message === "session_closed" || event.message === "session_expired") && event.sessionId) {
|
|
18636
|
+
createdBySession.delete(event.sessionId);
|
|
18637
|
+
}
|
|
18638
|
+
}
|
|
18639
|
+
for (const event of createdBySession.values()) {
|
|
18640
|
+
const runtime = event.runtime ?? "Unknown";
|
|
18641
|
+
summary.activeSessionsByRuntime[runtime] = (summary.activeSessionsByRuntime[runtime] ?? 0) + 1;
|
|
18642
|
+
}
|
|
18643
|
+
for (const event of successfulToolCalls) {
|
|
18644
|
+
if (event.runtime) summary.lastSuccessfulToolCallByRuntime[event.runtime] = event.timestamp ?? "";
|
|
18645
|
+
if (event.agentId) summary.lastSuccessfulToolCallByAgent[event.agentId] = event.timestamp ?? "";
|
|
18646
|
+
if (event.toolFamily) summary.lastSuccessfulToolCallByFamily[event.toolFamily] = event.timestamp ?? "";
|
|
18647
|
+
}
|
|
18648
|
+
return summary;
|
|
18649
|
+
}
|
|
18650
|
+
var MCP_HTTP_EVENTS_PATH, MCP_MONITOR_DIR, MCP_TRANSPORT_SUMMARY_PATH, DEFAULT_EVENT_READ_BYTES, DEFAULT_EVENT_MAX_BYTES, EVENT_READ_BYTES, EVENT_MAX_BYTES;
|
|
18651
|
+
var init_mcp_transport_health = __esm({
|
|
18652
|
+
"src/lib/mcp-transport-health.ts"() {
|
|
18653
|
+
"use strict";
|
|
18654
|
+
init_config();
|
|
18655
|
+
MCP_HTTP_EVENTS_PATH = path37.join(EXE_AI_DIR, "mcp-http-events.jsonl");
|
|
18656
|
+
MCP_MONITOR_DIR = path37.join(EXE_AI_DIR, "monitor");
|
|
18657
|
+
MCP_TRANSPORT_SUMMARY_PATH = path37.join(MCP_MONITOR_DIR, "mcp-transport-summary.json");
|
|
18658
|
+
DEFAULT_EVENT_READ_BYTES = 256 * 1024;
|
|
18659
|
+
DEFAULT_EVENT_MAX_BYTES = 1024 * 1024;
|
|
18660
|
+
EVENT_READ_BYTES = parsePositiveInt(process.env.EXE_MCP_HTTP_EVENT_READ_BYTES, DEFAULT_EVENT_READ_BYTES);
|
|
18661
|
+
EVENT_MAX_BYTES = parsePositiveInt(process.env.EXE_MCP_HTTP_EVENT_MAX_BYTES, DEFAULT_EVENT_MAX_BYTES);
|
|
18662
|
+
}
|
|
18663
|
+
});
|
|
18664
|
+
|
|
18665
|
+
// src/bin/exe-healthcheck.ts
|
|
18666
|
+
import { existsSync as existsSync31, readFileSync as readFileSync27, readdirSync as readdirSync9 } from "fs";
|
|
18667
|
+
import path38 from "path";
|
|
18668
|
+
import { execSync as execSync14 } from "child_process";
|
|
18669
|
+
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
18670
|
+
function findPackageRoot2() {
|
|
18671
|
+
let dir = path38.dirname(fileURLToPath6(import.meta.url));
|
|
18672
|
+
const { root } = path38.parse(dir);
|
|
18673
|
+
while (dir !== root) {
|
|
18674
|
+
if (existsSync31(path38.join(dir, "package.json"))) return dir;
|
|
18675
|
+
dir = path38.dirname(dir);
|
|
18676
|
+
}
|
|
18677
|
+
throw new Error("Cannot find package root");
|
|
18678
|
+
}
|
|
18679
|
+
function checkBuildIntegrity(pkgRoot) {
|
|
18680
|
+
const results = [];
|
|
18681
|
+
const tsupConfig = path38.join(pkgRoot, "tsup.config.ts");
|
|
18682
|
+
if (!existsSync31(tsupConfig)) {
|
|
18683
|
+
return [{ name: "build/tsup-config", pass: false, detail: "tsup.config.ts not found" }];
|
|
18684
|
+
}
|
|
18685
|
+
const configContent = readFileSync27(tsupConfig, "utf-8");
|
|
18686
|
+
const entryMatches = configContent.matchAll(/"([^"]+)":\s*"src\//g);
|
|
18687
|
+
const missing = [];
|
|
18688
|
+
let total = 0;
|
|
18689
|
+
for (const match of entryMatches) {
|
|
18690
|
+
const outputKey = match[1];
|
|
18691
|
+
const expectedPath = path38.join(pkgRoot, "dist", `${outputKey}.js`);
|
|
18692
|
+
total++;
|
|
18693
|
+
if (!existsSync31(expectedPath)) {
|
|
18694
|
+
missing.push(`dist/${outputKey}.js`);
|
|
18695
|
+
}
|
|
18696
|
+
}
|
|
18697
|
+
if (missing.length > 0) {
|
|
18698
|
+
results.push({
|
|
18699
|
+
name: "build/entry-points",
|
|
18700
|
+
pass: false,
|
|
18701
|
+
detail: `${missing.length}/${total} entry points missing:
|
|
18702
|
+
${missing.join("\n ")}`
|
|
18703
|
+
});
|
|
18704
|
+
} else {
|
|
18705
|
+
results.push({
|
|
18706
|
+
name: "build/entry-points",
|
|
18707
|
+
pass: true,
|
|
18708
|
+
detail: `${total} entry points verified`
|
|
18709
|
+
});
|
|
18710
|
+
}
|
|
18711
|
+
return results;
|
|
18712
|
+
}
|
|
18713
|
+
function checkEmbedPipeline(pkgRoot) {
|
|
18714
|
+
const results = [];
|
|
18715
|
+
const daemonPath = path38.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
18716
|
+
if (!existsSync31(daemonPath)) {
|
|
18717
|
+
results.push({
|
|
18718
|
+
name: "exed/daemon-exists",
|
|
18719
|
+
pass: false,
|
|
18720
|
+
detail: `exe-daemon.js not found at ${daemonPath}`
|
|
18721
|
+
});
|
|
18722
|
+
return results;
|
|
18723
|
+
}
|
|
18724
|
+
results.push({ name: "exed/daemon-exists", pass: true, detail: "dist/lib/exe-daemon.js exists" });
|
|
18725
|
+
const entryDirs = ["dist/hooks", "dist/bin", "dist/mcp"];
|
|
18726
|
+
for (const dir of entryDirs) {
|
|
18727
|
+
const fullDir = path38.join(pkgRoot, dir);
|
|
18728
|
+
if (!existsSync31(fullDir)) continue;
|
|
18729
|
+
let walkDir2 = fullDir;
|
|
18730
|
+
const { root } = path38.parse(walkDir2);
|
|
18731
|
+
let foundRoot = null;
|
|
18732
|
+
while (walkDir2 !== root) {
|
|
18733
|
+
if (existsSync31(path38.join(walkDir2, "package.json"))) {
|
|
18734
|
+
foundRoot = walkDir2;
|
|
18735
|
+
break;
|
|
18736
|
+
}
|
|
18737
|
+
walkDir2 = path38.dirname(walkDir2);
|
|
18738
|
+
}
|
|
18739
|
+
if (!foundRoot) {
|
|
18740
|
+
results.push({
|
|
18741
|
+
name: `exed/reachable-from-${dir}`,
|
|
18742
|
+
pass: false,
|
|
18743
|
+
detail: `Cannot find package root from ${dir}`
|
|
18744
|
+
});
|
|
18745
|
+
continue;
|
|
18746
|
+
}
|
|
18747
|
+
const resolvedDaemon = path38.join(foundRoot, "dist", "lib", "exe-daemon.js");
|
|
18748
|
+
const reachable = existsSync31(resolvedDaemon);
|
|
18749
|
+
results.push({
|
|
18750
|
+
name: `exed/reachable-from-${dir}`,
|
|
18751
|
+
pass: reachable,
|
|
18752
|
+
detail: reachable ? `Resolves to ${resolvedDaemon}` : `NOT FOUND: ${resolvedDaemon}`
|
|
18753
|
+
});
|
|
18754
|
+
}
|
|
18755
|
+
return results;
|
|
18756
|
+
}
|
|
18757
|
+
function checkTaskSystem(pkgRoot) {
|
|
18758
|
+
const results = [];
|
|
18759
|
+
const scannerPath = path38.join(pkgRoot, "dist", "bin", "scan-tasks.js");
|
|
18760
|
+
if (!existsSync31(scannerPath)) {
|
|
18761
|
+
results.push({ name: "tasks/scanner", pass: false, detail: "scan-tasks.js not found" });
|
|
18762
|
+
return results;
|
|
18763
|
+
}
|
|
18764
|
+
try {
|
|
18765
|
+
execSync14(`node "${scannerPath}" /tmp/nonexistent-healthcheck-test --format=json 2>/dev/null`, {
|
|
18766
|
+
timeout: 1e4,
|
|
18767
|
+
encoding: "utf-8"
|
|
18768
|
+
});
|
|
18769
|
+
results.push({ name: "tasks/scanner", pass: true, detail: "scan-tasks.js runs without import errors" });
|
|
18770
|
+
} catch (err) {
|
|
18771
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
18772
|
+
if (msg.includes("Cannot find module") || msg.includes("ERR_MODULE_NOT_FOUND") || msg.includes("SyntaxError")) {
|
|
18773
|
+
results.push({ name: "tasks/scanner", pass: false, detail: `Import error: ${msg.slice(0, 200)}` });
|
|
18774
|
+
} else {
|
|
18775
|
+
results.push({ name: "tasks/scanner", pass: true, detail: "scan-tasks.js runs (no import errors)" });
|
|
18776
|
+
}
|
|
18777
|
+
}
|
|
18778
|
+
return results;
|
|
18779
|
+
}
|
|
18780
|
+
function checkWorkerSpawning(pkgRoot) {
|
|
18781
|
+
const results = [];
|
|
18782
|
+
const workerPath = path38.join(pkgRoot, "dist", "hooks", "ingest-worker.js");
|
|
18783
|
+
if (!existsSync31(workerPath)) {
|
|
18784
|
+
results.push({ name: "workers/ingest-worker", pass: false, detail: "ingest-worker.js not found" });
|
|
18785
|
+
return results;
|
|
18786
|
+
}
|
|
18787
|
+
try {
|
|
18788
|
+
execSync14(`node --check "${workerPath}" 2>&1`, { timeout: 1e4, encoding: "utf-8" });
|
|
18789
|
+
results.push({ name: "workers/ingest-worker", pass: true, detail: "ingest-worker.js parses OK" });
|
|
18790
|
+
} catch (err) {
|
|
18791
|
+
results.push({
|
|
18792
|
+
name: "workers/ingest-worker",
|
|
18793
|
+
pass: false,
|
|
18794
|
+
detail: `Parse error: ${err instanceof Error ? err.message.slice(0, 200) : String(err)}`
|
|
18795
|
+
});
|
|
18796
|
+
}
|
|
18797
|
+
const hooksDir = path38.join(pkgRoot, "dist", "hooks");
|
|
18798
|
+
if (existsSync31(hooksDir)) {
|
|
18799
|
+
const hookFiles = readdirSync9(hooksDir).filter((f) => f.endsWith(".js") && !f.endsWith(".js.map"));
|
|
18800
|
+
let hooksPassed = 0;
|
|
18801
|
+
const hooksFailed = [];
|
|
18802
|
+
for (const hook of hookFiles) {
|
|
18803
|
+
try {
|
|
18804
|
+
execSync14(`node --check "${path38.join(hooksDir, hook)}" 2>&1`, { timeout: 1e4, encoding: "utf-8" });
|
|
18805
|
+
hooksPassed++;
|
|
18806
|
+
} catch {
|
|
18807
|
+
hooksFailed.push(hook);
|
|
18808
|
+
}
|
|
18809
|
+
}
|
|
18810
|
+
if (hooksFailed.length > 0) {
|
|
18811
|
+
results.push({
|
|
18812
|
+
name: "workers/hooks-parse",
|
|
18813
|
+
pass: false,
|
|
18814
|
+
detail: `${hooksFailed.length}/${hookFiles.length} hooks fail to parse: ${hooksFailed.join(", ")}`
|
|
18815
|
+
});
|
|
18816
|
+
} else {
|
|
18817
|
+
results.push({
|
|
18818
|
+
name: "workers/hooks-parse",
|
|
18819
|
+
pass: true,
|
|
18820
|
+
detail: `${hooksPassed} hook entry points parse OK`
|
|
18821
|
+
});
|
|
18822
|
+
}
|
|
18823
|
+
}
|
|
18824
|
+
return results;
|
|
18825
|
+
}
|
|
18826
|
+
function checkMcpTransport() {
|
|
18827
|
+
const results = [];
|
|
18828
|
+
const pidPath = path38.join(EXE_AI_DIR, "exed.pid");
|
|
18829
|
+
const tokenPath = path38.join(EXE_AI_DIR, "exed.token");
|
|
18830
|
+
let daemonAlive = false;
|
|
18831
|
+
if (existsSync31(pidPath)) {
|
|
18832
|
+
try {
|
|
18833
|
+
const pid = parseInt(readFileSync27(pidPath, "utf8").trim(), 10);
|
|
18834
|
+
process.kill(pid, 0);
|
|
18835
|
+
daemonAlive = true;
|
|
18836
|
+
results.push({ name: "mcp/daemon-process", pass: true, detail: `exed process alive (pid ${pid})` });
|
|
18837
|
+
} catch {
|
|
18838
|
+
results.push({ name: "mcp/daemon-process", pass: false, detail: "exed.pid exists but process is not alive; restart exe-os/exed" });
|
|
18839
|
+
}
|
|
18840
|
+
} else {
|
|
18841
|
+
results.push({ name: "mcp/daemon-process", pass: false, detail: "No exed.pid found; daemon is not running" });
|
|
18842
|
+
}
|
|
18843
|
+
if (daemonAlive && existsSync31(tokenPath)) {
|
|
18844
|
+
try {
|
|
18845
|
+
const token = readFileSync27(tokenPath, "utf8").trim();
|
|
18846
|
+
const response = execSync14(
|
|
18847
|
+
`curl -sS -i -m 2 -H "Authorization: Bearer ${token}" -H 'Accept: application/json, text/event-stream' http://127.0.0.1:48739/mcp`,
|
|
18848
|
+
{ encoding: "utf8", timeout: 3e3 }
|
|
18849
|
+
);
|
|
18850
|
+
const jsonRpcError = response.includes("Content-Type: application/json") && response.includes("missing MCP session");
|
|
18851
|
+
results.push({
|
|
18852
|
+
name: "mcp/http-endpoint",
|
|
18853
|
+
pass: jsonRpcError,
|
|
18854
|
+
detail: jsonRpcError ? "MCP HTTP endpoint reachable; missing-session probe returns JSON-RPC (expected)" : "MCP HTTP endpoint reachable but did not return expected JSON-RPC missing-session response"
|
|
18855
|
+
});
|
|
18856
|
+
} catch (err) {
|
|
18857
|
+
results.push({
|
|
18858
|
+
name: "mcp/http-endpoint",
|
|
18859
|
+
pass: false,
|
|
18860
|
+
detail: `MCP HTTP endpoint not reachable: ${err instanceof Error ? err.message.slice(0, 180) : String(err)}`
|
|
18861
|
+
});
|
|
18862
|
+
}
|
|
18863
|
+
} else if (daemonAlive) {
|
|
18864
|
+
results.push({ name: "mcp/http-endpoint", pass: false, detail: "Daemon is alive but exed.token is missing; cannot probe MCP HTTP" });
|
|
18865
|
+
}
|
|
18866
|
+
const events = readMcpHttpEvents(200);
|
|
18867
|
+
const summary = summarizeMcpTransport(events);
|
|
18868
|
+
results.push({
|
|
18869
|
+
name: "mcp/local-event-log",
|
|
18870
|
+
pass: true,
|
|
18871
|
+
detail: `${events.length} recent event(s); activeSessions=${summary.activeSessions ?? "unknown"}; lastHandshake=${summary.lastSuccessfulHandshake ?? "none"}; lastTool=${summary.lastSuccessfulToolCall ?? "none"}`
|
|
18872
|
+
});
|
|
18873
|
+
results.push({
|
|
18874
|
+
name: "mcp/monitor-summary",
|
|
18875
|
+
pass: true,
|
|
18876
|
+
detail: `Privacy-safe local monitor summary: ${path38.join(EXE_AI_DIR, "monitor", "mcp-transport-summary.json")}`
|
|
18877
|
+
});
|
|
18878
|
+
return results;
|
|
18879
|
+
}
|
|
18880
|
+
function checkClaudeCodeInstall() {
|
|
18881
|
+
const results = [];
|
|
18882
|
+
const execPath = process.env.CLAUDE_CODE_EXECPATH ?? "";
|
|
18883
|
+
if (execPath.length > 0 && (execPath.includes("claude/versions/") || execPath.includes("claude.exe") || execPath.includes("claude-native"))) {
|
|
18884
|
+
results.push({
|
|
18885
|
+
name: "cc/execpath-clean",
|
|
18886
|
+
pass: false,
|
|
18887
|
+
detail: `CLAUDE_CODE_EXECPATH points to native binary: "${execPath}" \u2014 20K phantom billing risk. Unset it: unset CLAUDE_CODE_EXECPATH`
|
|
18888
|
+
});
|
|
18889
|
+
} else {
|
|
18890
|
+
results.push({
|
|
18891
|
+
name: "cc/execpath-clean",
|
|
18892
|
+
pass: true,
|
|
18893
|
+
detail: execPath ? `CLAUDE_CODE_EXECPATH=${execPath} (node runtime, OK)` : "CLAUDE_CODE_EXECPATH is unset"
|
|
18894
|
+
});
|
|
18895
|
+
}
|
|
18896
|
+
try {
|
|
18897
|
+
const claudePath = execSync14("which claude 2>/dev/null || true", { encoding: "utf8", timeout: 5e3 }).trim();
|
|
18898
|
+
if (!claudePath) {
|
|
18899
|
+
results.push({
|
|
18900
|
+
name: "cc/cli-binary",
|
|
18901
|
+
pass: false,
|
|
18902
|
+
detail: "claude not found in PATH"
|
|
18903
|
+
});
|
|
18904
|
+
} else {
|
|
18905
|
+
let resolved = claudePath;
|
|
18906
|
+
try {
|
|
18907
|
+
resolved = execSync14(`readlink -f "${claudePath}" 2>/dev/null || readlink "${claudePath}" 2>/dev/null || echo "${claudePath}"`, {
|
|
18908
|
+
encoding: "utf8",
|
|
18909
|
+
timeout: 5e3
|
|
18910
|
+
}).trim();
|
|
18911
|
+
} catch {
|
|
18912
|
+
}
|
|
18913
|
+
if (resolved.includes("bin/claude.exe") || resolved.includes("bin/claude-native")) {
|
|
18914
|
+
results.push({
|
|
18915
|
+
name: "cc/cli-binary",
|
|
18916
|
+
pass: false,
|
|
18917
|
+
detail: `claude resolves to native binary: ${resolved}. Should be cli.js. Run: npm install -g @anthropic-ai/claude-code@2.1.98`
|
|
18918
|
+
});
|
|
18919
|
+
} else {
|
|
18920
|
+
results.push({
|
|
18921
|
+
name: "cc/cli-binary",
|
|
18922
|
+
pass: true,
|
|
18923
|
+
detail: `claude resolves to: ${resolved}`
|
|
18924
|
+
});
|
|
18925
|
+
}
|
|
18926
|
+
}
|
|
18927
|
+
} catch {
|
|
18928
|
+
results.push({
|
|
18929
|
+
name: "cc/cli-binary",
|
|
18930
|
+
pass: false,
|
|
18931
|
+
detail: "Failed to check claude binary path"
|
|
18932
|
+
});
|
|
18933
|
+
}
|
|
18934
|
+
const versionsDir = path38.join(
|
|
18935
|
+
process.env.HOME ?? process.env.USERPROFILE ?? "",
|
|
18936
|
+
".local",
|
|
18937
|
+
"share",
|
|
18938
|
+
"claude",
|
|
18939
|
+
"versions"
|
|
18940
|
+
);
|
|
18941
|
+
if (existsSync31(versionsDir)) {
|
|
18942
|
+
try {
|
|
18943
|
+
const entries = readdirSync9(versionsDir);
|
|
18944
|
+
if (entries.length > 0) {
|
|
18945
|
+
results.push({
|
|
18946
|
+
name: "cc/native-cache-clean",
|
|
18947
|
+
pass: false,
|
|
18948
|
+
detail: `${entries.length} cached native version(s) found in ${versionsDir}: ${entries.slice(0, 3).join(", ")}${entries.length > 3 ? "..." : ""}. Remove: rm -rf "${versionsDir}"`
|
|
18949
|
+
});
|
|
18950
|
+
} else {
|
|
18951
|
+
results.push({
|
|
18952
|
+
name: "cc/native-cache-clean",
|
|
18953
|
+
pass: true,
|
|
18954
|
+
detail: `${versionsDir} is empty`
|
|
18955
|
+
});
|
|
18956
|
+
}
|
|
18957
|
+
} catch {
|
|
18958
|
+
results.push({
|
|
18959
|
+
name: "cc/native-cache-clean",
|
|
18960
|
+
pass: true,
|
|
18961
|
+
detail: `${versionsDir} not readable (OK)`
|
|
18962
|
+
});
|
|
18963
|
+
}
|
|
18964
|
+
} else {
|
|
18965
|
+
results.push({
|
|
18966
|
+
name: "cc/native-cache-clean",
|
|
18967
|
+
pass: true,
|
|
18968
|
+
detail: `${versionsDir} does not exist`
|
|
18969
|
+
});
|
|
18970
|
+
}
|
|
18971
|
+
const disableOld = process.env.DISABLE_AUTOUPDATER === "1";
|
|
18972
|
+
const disableNew = process.env.CLAUDE_CODE_AUTOUPDATER_DISABLED === "1";
|
|
18973
|
+
if (!disableOld && !disableNew) {
|
|
18974
|
+
results.push({
|
|
18975
|
+
name: "cc/autoupdater-disabled",
|
|
18976
|
+
pass: false,
|
|
18977
|
+
detail: "Neither DISABLE_AUTOUPDATER=1 nor CLAUDE_CODE_AUTOUPDATER_DISABLED=1 is set. Auto-updater may install infected native binary. Export both in your shell profile."
|
|
18978
|
+
});
|
|
18979
|
+
} else {
|
|
18980
|
+
const which = [
|
|
18981
|
+
disableOld ? "DISABLE_AUTOUPDATER=1" : null,
|
|
18982
|
+
disableNew ? "CLAUDE_CODE_AUTOUPDATER_DISABLED=1" : null
|
|
18983
|
+
].filter(Boolean).join(" + ");
|
|
18984
|
+
results.push({
|
|
18985
|
+
name: "cc/autoupdater-disabled",
|
|
18986
|
+
pass: true,
|
|
18987
|
+
detail: `Auto-updater disabled via ${which}`
|
|
18988
|
+
});
|
|
18989
|
+
}
|
|
18990
|
+
try {
|
|
18991
|
+
const ccVersion = execSync14("claude --version 2>/dev/null || echo unknown", {
|
|
18992
|
+
encoding: "utf8",
|
|
18993
|
+
timeout: 5e3
|
|
18994
|
+
}).trim();
|
|
18995
|
+
const versionMatch = ccVersion.match(/(\d+\.\d+\.\d+)/);
|
|
18996
|
+
if (versionMatch) {
|
|
18997
|
+
const ver = versionMatch[1];
|
|
18998
|
+
const [, minor] = ver.split(".").map(Number);
|
|
18999
|
+
const isRisky = minor !== void 0 && minor >= 1 && parseInt(ver.split(".")[2] ?? "0") >= 119;
|
|
19000
|
+
results.push({
|
|
19001
|
+
name: "cc/version",
|
|
19002
|
+
pass: !isRisky,
|
|
19003
|
+
detail: isRisky ? `CC version ${ver} \u2014 \u22652.1.119 ships native binary only. Pin: npm install -g @anthropic-ai/claude-code@2.1.98` : `CC version ${ver}`
|
|
19004
|
+
});
|
|
19005
|
+
}
|
|
19006
|
+
} catch {
|
|
19007
|
+
}
|
|
19008
|
+
return results;
|
|
19009
|
+
}
|
|
19010
|
+
function runHealthCheck() {
|
|
19011
|
+
const pkgRoot = findPackageRoot2();
|
|
19012
|
+
const results = [
|
|
19013
|
+
...checkBuildIntegrity(pkgRoot),
|
|
19014
|
+
...checkEmbedPipeline(pkgRoot),
|
|
19015
|
+
...checkTaskSystem(pkgRoot),
|
|
19016
|
+
...checkWorkerSpawning(pkgRoot),
|
|
19017
|
+
...checkMcpTransport(),
|
|
19018
|
+
...checkClaudeCodeInstall()
|
|
19019
|
+
];
|
|
19020
|
+
const passed = results.filter((r) => r.pass).length;
|
|
19021
|
+
const failed = results.filter((r) => !r.pass).length;
|
|
19022
|
+
return { results, passed, failed };
|
|
19023
|
+
}
|
|
19024
|
+
var init_exe_healthcheck = __esm({
|
|
19025
|
+
"src/bin/exe-healthcheck.ts"() {
|
|
19026
|
+
"use strict";
|
|
19027
|
+
init_is_main();
|
|
19028
|
+
init_config();
|
|
19029
|
+
init_mcp_transport_health();
|
|
19030
|
+
if (isMainModule(import.meta.url) && (process.argv[1] ?? "").includes("exe-healthcheck")) {
|
|
19031
|
+
const { results, passed, failed } = runHealthCheck();
|
|
19032
|
+
console.log("\n exe-os Health Check\n");
|
|
19033
|
+
for (const r of results) {
|
|
19034
|
+
const icon = r.pass ? "\x1B[32m\u2713\x1B[0m" : "\x1B[31m\u2717\x1B[0m";
|
|
19035
|
+
console.log(` ${icon} ${r.name}`);
|
|
19036
|
+
console.log(` ${r.detail}`);
|
|
19037
|
+
}
|
|
19038
|
+
console.log(`
|
|
19039
|
+
${passed} passed, ${failed} failed
|
|
19040
|
+
`);
|
|
19041
|
+
process.exit(failed > 0 ? 1 : 0);
|
|
19042
|
+
}
|
|
19043
|
+
}
|
|
19044
|
+
});
|
|
19045
|
+
|
|
19046
|
+
// src/bin/cc-doctor.ts
|
|
19047
|
+
var cc_doctor_exports = {};
|
|
19048
|
+
__export(cc_doctor_exports, {
|
|
19049
|
+
runCcDoctor: () => runCcDoctor
|
|
19050
|
+
});
|
|
19051
|
+
function formatResult(r) {
|
|
19052
|
+
const icon = r.pass ? "\x1B[32m\u2713\x1B[0m" : "\x1B[31m\u2717\x1B[0m";
|
|
19053
|
+
const label = LABELS[r.name] ?? r.name;
|
|
19054
|
+
return `${icon} ${label}: ${r.detail}`;
|
|
19055
|
+
}
|
|
19056
|
+
function runCcDoctor() {
|
|
19057
|
+
const results = checkClaudeCodeInstall();
|
|
19058
|
+
const passed = results.filter((r) => r.pass).length;
|
|
19059
|
+
const failed = results.filter((r) => !r.pass).length;
|
|
19060
|
+
return { results, passed, failed };
|
|
19061
|
+
}
|
|
19062
|
+
var LABELS;
|
|
19063
|
+
var init_cc_doctor = __esm({
|
|
19064
|
+
"src/bin/cc-doctor.ts"() {
|
|
19065
|
+
"use strict";
|
|
19066
|
+
init_exe_healthcheck();
|
|
19067
|
+
init_is_main();
|
|
19068
|
+
LABELS = {
|
|
19069
|
+
"cc/execpath-clean": "EXECPATH",
|
|
19070
|
+
"cc/cli-binary": "Symlink",
|
|
19071
|
+
"cc/native-cache-clean": "Native cache",
|
|
19072
|
+
"cc/autoupdater-disabled": "Auto-updater",
|
|
19073
|
+
"cc/version": "Version"
|
|
19074
|
+
};
|
|
19075
|
+
if (isMainModule(import.meta.url) && (process.argv[1] ?? "").includes("cc-doctor")) {
|
|
19076
|
+
const { results, passed, failed } = runCcDoctor();
|
|
19077
|
+
console.log("\n CC Install Health Check\n");
|
|
19078
|
+
for (const r of results) {
|
|
19079
|
+
console.log(` ${formatResult(r)}`);
|
|
19080
|
+
}
|
|
19081
|
+
if (failed === 0) {
|
|
19082
|
+
console.log(`
|
|
19083
|
+
\x1B[32mStatus: CLEAN\x1B[0m \u2014 no 20K billing risk (${passed} checks passed)
|
|
19084
|
+
`);
|
|
19085
|
+
} else {
|
|
19086
|
+
console.log(`
|
|
19087
|
+
\x1B[31mStatus: ${failed} ISSUE${failed > 1 ? "S" : ""} FOUND\x1B[0m \u2014 ${passed} passed, ${failed} failed`);
|
|
19088
|
+
console.log(" Fix: npm install -g @anthropic-ai/claude-code@2.1.98\n");
|
|
19089
|
+
}
|
|
19090
|
+
process.exit(failed > 0 ? 1 : 0);
|
|
19091
|
+
}
|
|
19092
|
+
}
|
|
19093
|
+
});
|
|
19094
|
+
|
|
18015
19095
|
// src/lib/model-downloader.ts
|
|
18016
|
-
import { createWriteStream, createReadStream as createReadStream2, existsSync as
|
|
19096
|
+
import { createWriteStream, createReadStream as createReadStream2, existsSync as existsSync32, unlinkSync as unlinkSync14, renameSync as renameSync7 } from "fs";
|
|
18017
19097
|
import { mkdir as mkdir6 } from "fs/promises";
|
|
18018
19098
|
import { createHash as createHash6 } from "crypto";
|
|
18019
|
-
import
|
|
19099
|
+
import path39 from "path";
|
|
18020
19100
|
async function downloadModel(opts) {
|
|
18021
19101
|
const { destDir, onProgress, fetchFn = globalThis.fetch } = opts;
|
|
18022
|
-
const destPath =
|
|
19102
|
+
const destPath = path39.join(destDir, LOCAL_FILENAME);
|
|
18023
19103
|
const tmpPath = destPath + ".tmp";
|
|
18024
19104
|
await mkdir6(destDir, { recursive: true });
|
|
18025
|
-
if (
|
|
19105
|
+
if (existsSync32(destPath)) {
|
|
18026
19106
|
const hash = await fileHash(destPath);
|
|
18027
19107
|
if (hash === EXPECTED_SHA256) {
|
|
18028
19108
|
return destPath;
|
|
@@ -18034,7 +19114,7 @@ async function downloadModel(opts) {
|
|
|
18034
19114
|
let downloaded = 0;
|
|
18035
19115
|
for (let attempt = 1; attempt <= MAX_RETRIES4; attempt++) {
|
|
18036
19116
|
try {
|
|
18037
|
-
if (
|
|
19117
|
+
if (existsSync32(tmpPath)) unlinkSync14(tmpPath);
|
|
18038
19118
|
const response = await fetchFn(GGUF_URL, {
|
|
18039
19119
|
redirect: "follow",
|
|
18040
19120
|
signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS)
|
|
@@ -18079,7 +19159,7 @@ async function downloadModel(opts) {
|
|
|
18079
19159
|
process.stderr.write(`
|
|
18080
19160
|
Download attempt ${attempt} failed, retrying...
|
|
18081
19161
|
`);
|
|
18082
|
-
if (
|
|
19162
|
+
if (existsSync32(tmpPath)) unlinkSync14(tmpPath);
|
|
18083
19163
|
}
|
|
18084
19164
|
}
|
|
18085
19165
|
}
|
|
@@ -18656,38 +19736,38 @@ __export(session_wrappers_exports, {
|
|
|
18656
19736
|
generateSessionWrappers: () => generateSessionWrappers
|
|
18657
19737
|
});
|
|
18658
19738
|
import {
|
|
18659
|
-
existsSync as
|
|
18660
|
-
readFileSync as
|
|
18661
|
-
writeFileSync as
|
|
18662
|
-
mkdirSync as
|
|
19739
|
+
existsSync as existsSync33,
|
|
19740
|
+
readFileSync as readFileSync28,
|
|
19741
|
+
writeFileSync as writeFileSync24,
|
|
19742
|
+
mkdirSync as mkdirSync23,
|
|
18663
19743
|
chmodSync as chmodSync4,
|
|
18664
|
-
readdirSync as
|
|
19744
|
+
readdirSync as readdirSync10,
|
|
18665
19745
|
unlinkSync as unlinkSync15
|
|
18666
19746
|
} from "fs";
|
|
18667
|
-
import { execSync as
|
|
18668
|
-
import
|
|
19747
|
+
import { execSync as execSync15 } from "child_process";
|
|
19748
|
+
import path40 from "path";
|
|
18669
19749
|
import { homedir as homedir5 } from "os";
|
|
18670
19750
|
function generateSessionWrappers(packageRoot, homeDir) {
|
|
18671
19751
|
const home = homeDir ?? homedir5();
|
|
18672
|
-
const binDir =
|
|
18673
|
-
const rosterPath =
|
|
19752
|
+
const binDir = path40.join(home, ".exe-os", "bin");
|
|
19753
|
+
const rosterPath = path40.join(home, ".exe-os", "exe-employees.json");
|
|
18674
19754
|
const shouldMirrorToGlobalBin = homeDir === void 0;
|
|
18675
|
-
|
|
18676
|
-
const exeStartDst =
|
|
19755
|
+
mkdirSync23(binDir, { recursive: true });
|
|
19756
|
+
const exeStartDst = path40.join(binDir, "exe-start");
|
|
18677
19757
|
const candidates = [
|
|
18678
|
-
|
|
18679
|
-
|
|
19758
|
+
path40.join(packageRoot, "dist", "bin", "exe-start.sh"),
|
|
19759
|
+
path40.join(packageRoot, "src", "bin", "exe-start.sh")
|
|
18680
19760
|
];
|
|
18681
19761
|
for (const src of candidates) {
|
|
18682
|
-
if (
|
|
18683
|
-
|
|
19762
|
+
if (existsSync33(src)) {
|
|
19763
|
+
writeFileSync24(exeStartDst, readFileSync28(src));
|
|
18684
19764
|
chmodSync4(exeStartDst, 493);
|
|
18685
19765
|
break;
|
|
18686
19766
|
}
|
|
18687
19767
|
}
|
|
18688
19768
|
let employees = [];
|
|
18689
19769
|
try {
|
|
18690
|
-
employees = JSON.parse(
|
|
19770
|
+
employees = JSON.parse(readFileSync28(rosterPath, "utf8"));
|
|
18691
19771
|
} catch {
|
|
18692
19772
|
return { created: 0, pathConfigured: false };
|
|
18693
19773
|
}
|
|
@@ -18695,11 +19775,11 @@ function generateSessionWrappers(packageRoot, homeDir) {
|
|
|
18695
19775
|
return { created: 0, pathConfigured: false };
|
|
18696
19776
|
}
|
|
18697
19777
|
try {
|
|
18698
|
-
for (const f of
|
|
19778
|
+
for (const f of readdirSync10(binDir)) {
|
|
18699
19779
|
if (f === "exe-start") continue;
|
|
18700
|
-
const fPath =
|
|
19780
|
+
const fPath = path40.join(binDir, f);
|
|
18701
19781
|
try {
|
|
18702
|
-
const content =
|
|
19782
|
+
const content = readFileSync28(fPath, "utf8");
|
|
18703
19783
|
if (content.includes("exe-start")) {
|
|
18704
19784
|
unlinkSync15(fPath);
|
|
18705
19785
|
}
|
|
@@ -18715,36 +19795,36 @@ exec "${exeStartDst}" "$0" "$@"
|
|
|
18715
19795
|
const globalBinDir = shouldMirrorToGlobalBin ? resolveGlobalBinDir() : null;
|
|
18716
19796
|
for (const emp of employees) {
|
|
18717
19797
|
for (let n = 1; n <= MAX_N; n++) {
|
|
18718
|
-
writeWrapper(
|
|
19798
|
+
writeWrapper(path40.join(binDir, `${emp.name}${n}`), wrapperContent);
|
|
18719
19799
|
if (globalBinDir) {
|
|
18720
|
-
writeWrapper(
|
|
19800
|
+
writeWrapper(path40.join(globalBinDir, `${emp.name}${n}`), wrapperContent);
|
|
18721
19801
|
}
|
|
18722
19802
|
created++;
|
|
18723
|
-
writeWrapper(
|
|
19803
|
+
writeWrapper(path40.join(binDir, `${emp.name}${n}-codex`), wrapperContent);
|
|
18724
19804
|
if (globalBinDir) {
|
|
18725
|
-
writeWrapper(
|
|
19805
|
+
writeWrapper(path40.join(globalBinDir, `${emp.name}${n}-codex`), wrapperContent);
|
|
18726
19806
|
}
|
|
18727
19807
|
created++;
|
|
18728
19808
|
}
|
|
18729
19809
|
}
|
|
18730
19810
|
const codexLauncherCandidates = [
|
|
18731
|
-
|
|
18732
|
-
|
|
19811
|
+
path40.join(packageRoot, "dist", "bin", "exe-start-codex.js"),
|
|
19812
|
+
path40.join(packageRoot, "src", "bin", "exe-start-codex.ts")
|
|
18733
19813
|
];
|
|
18734
19814
|
let codexLauncher = null;
|
|
18735
19815
|
for (const c of codexLauncherCandidates) {
|
|
18736
|
-
if (
|
|
19816
|
+
if (existsSync33(c)) {
|
|
18737
19817
|
codexLauncher = c;
|
|
18738
19818
|
break;
|
|
18739
19819
|
}
|
|
18740
19820
|
}
|
|
18741
19821
|
if (codexLauncher) {
|
|
18742
19822
|
for (const emp of employees) {
|
|
18743
|
-
const wrapperPath =
|
|
19823
|
+
const wrapperPath = path40.join(binDir, `${emp.name}-codex`);
|
|
18744
19824
|
const content = `#!/bin/bash
|
|
18745
19825
|
exec node "${codexLauncher}" --agent ${emp.name} "$@"
|
|
18746
19826
|
`;
|
|
18747
|
-
|
|
19827
|
+
writeFileSync24(wrapperPath, content);
|
|
18748
19828
|
chmodSync4(wrapperPath, 493);
|
|
18749
19829
|
created++;
|
|
18750
19830
|
}
|
|
@@ -18754,20 +19834,20 @@ exec node "${codexLauncher}" --agent ${emp.name} "$@"
|
|
|
18754
19834
|
}
|
|
18755
19835
|
function writeWrapper(wrapperPath, content) {
|
|
18756
19836
|
try {
|
|
18757
|
-
|
|
19837
|
+
writeFileSync24(wrapperPath, content);
|
|
18758
19838
|
chmodSync4(wrapperPath, 493);
|
|
18759
19839
|
} catch {
|
|
18760
19840
|
}
|
|
18761
19841
|
}
|
|
18762
19842
|
function resolveGlobalBinDir() {
|
|
18763
19843
|
try {
|
|
18764
|
-
const exeOsPath =
|
|
18765
|
-
if (exeOsPath) return
|
|
19844
|
+
const exeOsPath = execSync15("command -v exe-os", { encoding: "utf8", timeout: 3e3 }).trim().split("\n")[0];
|
|
19845
|
+
if (exeOsPath) return path40.dirname(exeOsPath);
|
|
18766
19846
|
} catch {
|
|
18767
19847
|
}
|
|
18768
19848
|
try {
|
|
18769
|
-
const prefix =
|
|
18770
|
-
if (prefix) return
|
|
19849
|
+
const prefix = execSync15("npm prefix -g", { encoding: "utf8", timeout: 3e3 }).trim();
|
|
19850
|
+
if (prefix) return path40.join(prefix, "bin");
|
|
18771
19851
|
} catch {
|
|
18772
19852
|
}
|
|
18773
19853
|
return null;
|
|
@@ -18783,24 +19863,24 @@ export PATH="${binDir}:$PATH"
|
|
|
18783
19863
|
const shell = process.env.SHELL ?? "/bin/bash";
|
|
18784
19864
|
const profilePaths = [];
|
|
18785
19865
|
if (shell.includes("zsh")) {
|
|
18786
|
-
profilePaths.push(
|
|
19866
|
+
profilePaths.push(path40.join(home, ".zshrc"));
|
|
18787
19867
|
} else if (shell.includes("bash")) {
|
|
18788
|
-
profilePaths.push(
|
|
18789
|
-
profilePaths.push(
|
|
19868
|
+
profilePaths.push(path40.join(home, ".bashrc"));
|
|
19869
|
+
profilePaths.push(path40.join(home, ".bash_profile"));
|
|
18790
19870
|
} else {
|
|
18791
|
-
profilePaths.push(
|
|
19871
|
+
profilePaths.push(path40.join(home, ".profile"));
|
|
18792
19872
|
}
|
|
18793
19873
|
for (const profilePath of profilePaths) {
|
|
18794
19874
|
try {
|
|
18795
19875
|
let content = "";
|
|
18796
19876
|
try {
|
|
18797
|
-
content =
|
|
19877
|
+
content = readFileSync28(profilePath, "utf8");
|
|
18798
19878
|
} catch {
|
|
18799
19879
|
}
|
|
18800
19880
|
if (content.includes(".exe-os/bin")) {
|
|
18801
19881
|
return false;
|
|
18802
19882
|
}
|
|
18803
|
-
|
|
19883
|
+
writeFileSync24(profilePath, content + exportLine);
|
|
18804
19884
|
return true;
|
|
18805
19885
|
} catch {
|
|
18806
19886
|
continue;
|
|
@@ -18823,36 +19903,36 @@ __export(setup_wizard_exports, {
|
|
|
18823
19903
|
validateModel: () => validateModel
|
|
18824
19904
|
});
|
|
18825
19905
|
import crypto13 from "crypto";
|
|
18826
|
-
import { existsSync as
|
|
19906
|
+
import { existsSync as existsSync34, mkdirSync as mkdirSync24, readFileSync as readFileSync29, writeFileSync as writeFileSync25, unlinkSync as unlinkSync16 } from "fs";
|
|
18827
19907
|
import os19 from "os";
|
|
18828
|
-
import
|
|
19908
|
+
import path41 from "path";
|
|
18829
19909
|
import { createInterface as createInterface4 } from "readline";
|
|
18830
|
-
function
|
|
18831
|
-
let dir =
|
|
18832
|
-
const root =
|
|
19910
|
+
function findPackageRoot3() {
|
|
19911
|
+
let dir = path41.dirname(new URL(import.meta.url).pathname);
|
|
19912
|
+
const root = path41.parse(dir).root;
|
|
18833
19913
|
while (dir !== root) {
|
|
18834
|
-
const pkgPath =
|
|
18835
|
-
if (
|
|
19914
|
+
const pkgPath = path41.join(dir, "package.json");
|
|
19915
|
+
if (existsSync34(pkgPath)) {
|
|
18836
19916
|
try {
|
|
18837
|
-
const pkg = JSON.parse(
|
|
19917
|
+
const pkg = JSON.parse(readFileSync29(pkgPath, "utf-8"));
|
|
18838
19918
|
if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
|
|
18839
19919
|
} catch {
|
|
18840
19920
|
}
|
|
18841
19921
|
}
|
|
18842
|
-
dir =
|
|
19922
|
+
dir = path41.dirname(dir);
|
|
18843
19923
|
}
|
|
18844
19924
|
return null;
|
|
18845
19925
|
}
|
|
18846
19926
|
function loadSetupState() {
|
|
18847
19927
|
try {
|
|
18848
|
-
return JSON.parse(
|
|
19928
|
+
return JSON.parse(readFileSync29(SETUP_STATE_PATH, "utf8"));
|
|
18849
19929
|
} catch {
|
|
18850
19930
|
return { completedSteps: [], startedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
18851
19931
|
}
|
|
18852
19932
|
}
|
|
18853
19933
|
function saveSetupState(state) {
|
|
18854
|
-
|
|
18855
|
-
|
|
19934
|
+
mkdirSync24(path41.dirname(SETUP_STATE_PATH), { recursive: true });
|
|
19935
|
+
writeFileSync25(SETUP_STATE_PATH, JSON.stringify(state, null, 2));
|
|
18856
19936
|
}
|
|
18857
19937
|
function clearSetupState() {
|
|
18858
19938
|
try {
|
|
@@ -18873,8 +19953,8 @@ function ask3(rl, prompt) {
|
|
|
18873
19953
|
function getAvailableMemoryGB2() {
|
|
18874
19954
|
if (process.platform === "darwin") {
|
|
18875
19955
|
try {
|
|
18876
|
-
const { execSync:
|
|
18877
|
-
const vmstat =
|
|
19956
|
+
const { execSync: execSync20 } = __require("child_process");
|
|
19957
|
+
const vmstat = execSync20("vm_stat", { encoding: "utf8" });
|
|
18878
19958
|
const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
|
|
18879
19959
|
const pageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : 16384;
|
|
18880
19960
|
const free = vmstat.match(/Pages free:\s+(\d+)/);
|
|
@@ -18902,10 +19982,10 @@ async function validateModel(log) {
|
|
|
18902
19982
|
if (totalGB <= 8 || isLowMemory()) {
|
|
18903
19983
|
log(`System memory: ${totalGB.toFixed(0)}GB total, ${freeGB.toFixed(1)}GB free`);
|
|
18904
19984
|
log("Skipping in-memory model validation (low memory \u2014 will validate on first use).");
|
|
18905
|
-
const modelPath =
|
|
18906
|
-
if (
|
|
18907
|
-
const { statSync:
|
|
18908
|
-
const size =
|
|
19985
|
+
const modelPath = path41.join(MODELS_DIR, LOCAL_FILENAME);
|
|
19986
|
+
if (existsSync34(modelPath)) {
|
|
19987
|
+
const { statSync: statSync9 } = await import("fs");
|
|
19988
|
+
const size = statSync9(modelPath).size;
|
|
18909
19989
|
if (size > 300 * 1e6) {
|
|
18910
19990
|
log(`Model file verified (${(size / 1e6).toFixed(0)} MB).`);
|
|
18911
19991
|
return;
|
|
@@ -18998,7 +20078,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
18998
20078
|
if (state.completedSteps.length > 0) {
|
|
18999
20079
|
log(`Resuming setup from step ${Math.max(...state.completedSteps) + 1}...`);
|
|
19000
20080
|
}
|
|
19001
|
-
if (
|
|
20081
|
+
if (existsSync34(LEGACY_LANCE_PATH)) {
|
|
19002
20082
|
log("\u26A0 Found v1.0 LanceDB at ~/.exe-os/local.lance");
|
|
19003
20083
|
log(" v1.1 uses libSQL (SQLite). Your existing memories are not automatically migrated.");
|
|
19004
20084
|
log(" The old directory will not be modified or deleted.");
|
|
@@ -19189,10 +20269,10 @@ async function runSetupWizard(opts = {}) {
|
|
|
19189
20269
|
await saveConfig(config);
|
|
19190
20270
|
log("");
|
|
19191
20271
|
try {
|
|
19192
|
-
const claudeJsonPath =
|
|
20272
|
+
const claudeJsonPath = path41.join(os19.homedir(), ".claude.json");
|
|
19193
20273
|
let claudeJson = {};
|
|
19194
20274
|
try {
|
|
19195
|
-
claudeJson = JSON.parse(
|
|
20275
|
+
claudeJson = JSON.parse(readFileSync29(claudeJsonPath, "utf8"));
|
|
19196
20276
|
} catch {
|
|
19197
20277
|
}
|
|
19198
20278
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -19201,7 +20281,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
19201
20281
|
if (!projects[dir]) projects[dir] = {};
|
|
19202
20282
|
projects[dir].hasTrustDialogAccepted = true;
|
|
19203
20283
|
}
|
|
19204
|
-
|
|
20284
|
+
writeFileSync25(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
19205
20285
|
} catch {
|
|
19206
20286
|
}
|
|
19207
20287
|
state.completedSteps.push(5);
|
|
@@ -19215,7 +20295,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
19215
20295
|
const prefs = { ...existingPrefs };
|
|
19216
20296
|
log("=== Config Defaults ===");
|
|
19217
20297
|
log("");
|
|
19218
|
-
const ghosttyDetected =
|
|
20298
|
+
const ghosttyDetected = existsSync34(path41.join(os19.homedir(), ".config", "ghostty")) || existsSync34(path41.join(os19.homedir(), "Library", "Application Support", "com.mitchellh.ghostty"));
|
|
19219
20299
|
if (ghosttyDetected) {
|
|
19220
20300
|
const ghosttyAnswer = await ask3(rl, "Detected Ghostty terminal. Use exe-os Ghostty defaults? (Y/n) ");
|
|
19221
20301
|
prefs.ghostty = ghosttyAnswer.toLowerCase() !== "n";
|
|
@@ -19262,7 +20342,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
19262
20342
|
let missingIdentities = [];
|
|
19263
20343
|
for (const emp of roster) {
|
|
19264
20344
|
const idPath = identityPath2(emp.name);
|
|
19265
|
-
if (!
|
|
20345
|
+
if (!existsSync34(idPath)) {
|
|
19266
20346
|
missingIdentities.push(emp.name);
|
|
19267
20347
|
}
|
|
19268
20348
|
}
|
|
@@ -19294,7 +20374,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
19294
20374
|
}
|
|
19295
20375
|
missingIdentities = [];
|
|
19296
20376
|
for (const emp of roster) {
|
|
19297
|
-
if (!
|
|
20377
|
+
if (!existsSync34(identityPath2(emp.name))) {
|
|
19298
20378
|
missingIdentities.push(emp.name);
|
|
19299
20379
|
}
|
|
19300
20380
|
}
|
|
@@ -19312,7 +20392,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
19312
20392
|
}
|
|
19313
20393
|
try {
|
|
19314
20394
|
const { generateSessionWrappers: generateSessionWrappers2 } = await Promise.resolve().then(() => (init_session_wrappers(), session_wrappers_exports));
|
|
19315
|
-
const pkgRoot =
|
|
20395
|
+
const pkgRoot = findPackageRoot3();
|
|
19316
20396
|
if (pkgRoot) {
|
|
19317
20397
|
const wrapResult = generateSessionWrappers2(pkgRoot);
|
|
19318
20398
|
if (wrapResult.created > 0) {
|
|
@@ -19353,9 +20433,9 @@ async function runSetupWizard(opts = {}) {
|
|
|
19353
20433
|
const cooIdentityContent = getIdentityTemplate("coo");
|
|
19354
20434
|
if (cooIdentityContent) {
|
|
19355
20435
|
const cooIdPath = identityPath2(cooName);
|
|
19356
|
-
|
|
20436
|
+
mkdirSync24(path41.dirname(cooIdPath), { recursive: true });
|
|
19357
20437
|
const replaced = cooIdentityContent.replace(/agent_id:\s*exe/g, `agent_id: ${cooName}`).replace(/\$\{agent_id\}/g, cooName);
|
|
19358
|
-
|
|
20438
|
+
writeFileSync25(cooIdPath, replaced, "utf-8");
|
|
19359
20439
|
}
|
|
19360
20440
|
registerBinSymlinks2(cooName);
|
|
19361
20441
|
createdEmployees.push({ name: cooName, role: "COO" });
|
|
@@ -19471,9 +20551,9 @@ async function runSetupWizard(opts = {}) {
|
|
|
19471
20551
|
const ctoIdentityContent = getIdentityTemplate("cto");
|
|
19472
20552
|
if (ctoIdentityContent) {
|
|
19473
20553
|
const ctoIdPath = identityPath2(ctoName);
|
|
19474
|
-
|
|
20554
|
+
mkdirSync24(path41.dirname(ctoIdPath), { recursive: true });
|
|
19475
20555
|
const replaced = ctoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${ctoName}`).replace(/\$\{agent_id\}/g, ctoName);
|
|
19476
|
-
|
|
20556
|
+
writeFileSync25(ctoIdPath, replaced, "utf-8");
|
|
19477
20557
|
}
|
|
19478
20558
|
registerBinSymlinks2(ctoName);
|
|
19479
20559
|
createdEmployees.push({ name: ctoName, role: "CTO" });
|
|
@@ -19494,9 +20574,9 @@ async function runSetupWizard(opts = {}) {
|
|
|
19494
20574
|
const cmoIdentityContent = getIdentityTemplate("cmo");
|
|
19495
20575
|
if (cmoIdentityContent) {
|
|
19496
20576
|
const cmoIdPath = identityPath2(cmoName);
|
|
19497
|
-
|
|
20577
|
+
mkdirSync24(path41.dirname(cmoIdPath), { recursive: true });
|
|
19498
20578
|
const replaced = cmoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${cmoName}`).replace(/\$\{agent_id\}/g, cmoName);
|
|
19499
|
-
|
|
20579
|
+
writeFileSync25(cmoIdPath, replaced, "utf-8");
|
|
19500
20580
|
}
|
|
19501
20581
|
registerBinSymlinks2(cmoName);
|
|
19502
20582
|
createdEmployees.push({ name: cmoName, role: "CMO" });
|
|
@@ -19512,14 +20592,14 @@ async function runSetupWizard(opts = {}) {
|
|
|
19512
20592
|
if (!pairingRosterPulled) {
|
|
19513
20593
|
try {
|
|
19514
20594
|
const { generateSessionWrappers: generateSessionWrappers2 } = await Promise.resolve().then(() => (init_session_wrappers(), session_wrappers_exports));
|
|
19515
|
-
const pkgRoot =
|
|
20595
|
+
const pkgRoot = findPackageRoot3();
|
|
19516
20596
|
if (pkgRoot) {
|
|
19517
20597
|
const wrapResult = generateSessionWrappers2(pkgRoot);
|
|
19518
20598
|
if (wrapResult.created > 0) {
|
|
19519
20599
|
log(`Session shortcuts generated (${cooName}1, ${cooName}2, ...)`);
|
|
19520
20600
|
}
|
|
19521
20601
|
if (wrapResult.pathConfigured) {
|
|
19522
|
-
const binDir =
|
|
20602
|
+
const binDir = path41.join(os19.homedir(), ".exe-os", "bin");
|
|
19523
20603
|
process.env.PATH = `${binDir}:${process.env.PATH ?? ""}`;
|
|
19524
20604
|
pathJustConfigured = true;
|
|
19525
20605
|
}
|
|
@@ -19559,10 +20639,10 @@ async function runSetupWizard(opts = {}) {
|
|
|
19559
20639
|
log("Team: " + createdEmployees.map((e) => `${e.name} (${e.role})`).join(", "));
|
|
19560
20640
|
}
|
|
19561
20641
|
let version = "";
|
|
19562
|
-
const pkgRoot2 =
|
|
20642
|
+
const pkgRoot2 = findPackageRoot3();
|
|
19563
20643
|
if (pkgRoot2) {
|
|
19564
20644
|
try {
|
|
19565
|
-
version = JSON.parse(
|
|
20645
|
+
version = JSON.parse(readFileSync29(path41.join(pkgRoot2, "package.json"), "utf-8")).version;
|
|
19566
20646
|
} catch {
|
|
19567
20647
|
}
|
|
19568
20648
|
}
|
|
@@ -19601,46 +20681,46 @@ var init_setup_wizard = __esm({
|
|
|
19601
20681
|
init_config();
|
|
19602
20682
|
init_keychain();
|
|
19603
20683
|
init_model_downloader();
|
|
19604
|
-
SETUP_STATE_PATH =
|
|
20684
|
+
SETUP_STATE_PATH = path41.join(os19.homedir(), ".exe-os", "setup-state.json");
|
|
19605
20685
|
}
|
|
19606
20686
|
});
|
|
19607
20687
|
|
|
19608
20688
|
// src/lib/update-backup.ts
|
|
19609
20689
|
import { copyFile, readFile as readFile6, readdir as readdir3, writeFile as writeFile7, rm as rm2, mkdir as mkdir7, cp } from "fs/promises";
|
|
19610
|
-
import { existsSync as
|
|
19611
|
-
import
|
|
20690
|
+
import { existsSync as existsSync35 } from "fs";
|
|
20691
|
+
import path42 from "path";
|
|
19612
20692
|
import os20 from "os";
|
|
19613
20693
|
function resolveDataDir2() {
|
|
19614
20694
|
if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
|
|
19615
20695
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
19616
|
-
return
|
|
20696
|
+
return path42.join(os20.homedir(), ".exe-os");
|
|
19617
20697
|
}
|
|
19618
20698
|
function externalBackupTargets(homeDir) {
|
|
19619
20699
|
return [
|
|
19620
|
-
{ name: "claude.json", path:
|
|
19621
|
-
{ name: "claude-settings.json", path:
|
|
19622
|
-
{ name: "claude-CLAUDE.md", path:
|
|
19623
|
-
{ name: "tmux.conf", path:
|
|
19624
|
-
{ name: "zshrc", path:
|
|
19625
|
-
{ name: "bashrc", path:
|
|
19626
|
-
{ name: "ghostty-config", path:
|
|
19627
|
-
{ name: "codex-config.toml", path:
|
|
19628
|
-
{ name: "codex-hooks.json", path:
|
|
19629
|
-
{ name: "opencode-config.json", path:
|
|
20700
|
+
{ name: "claude.json", path: path42.join(homeDir, ".claude.json") },
|
|
20701
|
+
{ name: "claude-settings.json", path: path42.join(homeDir, ".claude", "settings.json") },
|
|
20702
|
+
{ name: "claude-CLAUDE.md", path: path42.join(homeDir, ".claude", "CLAUDE.md") },
|
|
20703
|
+
{ name: "tmux.conf", path: path42.join(homeDir, ".tmux.conf") },
|
|
20704
|
+
{ name: "zshrc", path: path42.join(homeDir, ".zshrc") },
|
|
20705
|
+
{ name: "bashrc", path: path42.join(homeDir, ".bashrc") },
|
|
20706
|
+
{ name: "ghostty-config", path: path42.join(homeDir, ".config", "ghostty", "config") },
|
|
20707
|
+
{ name: "codex-config.toml", path: path42.join(homeDir, ".codex", "config.toml") },
|
|
20708
|
+
{ name: "codex-hooks.json", path: path42.join(homeDir, ".codex", "hooks.json") },
|
|
20709
|
+
{ name: "opencode-config.json", path: path42.join(homeDir, ".config", "opencode", "opencode.json") }
|
|
19630
20710
|
];
|
|
19631
20711
|
}
|
|
19632
20712
|
async function createUpdateBackup(currentVersion, dataDir2, homeDir = os20.homedir()) {
|
|
19633
20713
|
const dir = dataDir2 ?? resolveDataDir2();
|
|
19634
|
-
const backupDir =
|
|
19635
|
-
if (
|
|
20714
|
+
const backupDir = path42.join(dir, BACKUP_DIR_NAME);
|
|
20715
|
+
if (existsSync35(backupDir)) {
|
|
19636
20716
|
await rm2(backupDir, { recursive: true, force: true });
|
|
19637
20717
|
}
|
|
19638
20718
|
await mkdir7(backupDir, { recursive: true });
|
|
19639
20719
|
const backedUpFiles = [];
|
|
19640
20720
|
for (const target of BACKUP_TARGETS) {
|
|
19641
|
-
const src =
|
|
19642
|
-
if (!
|
|
19643
|
-
const dest =
|
|
20721
|
+
const src = path42.join(dir, target.name);
|
|
20722
|
+
if (!existsSync35(src)) continue;
|
|
20723
|
+
const dest = path42.join(backupDir, target.name);
|
|
19644
20724
|
if (target.type === "file") {
|
|
19645
20725
|
await copyFile(src, dest);
|
|
19646
20726
|
} else {
|
|
@@ -19651,18 +20731,18 @@ async function createUpdateBackup(currentVersion, dataDir2, homeDir = os20.homed
|
|
|
19651
20731
|
const entries = await readdir3(dir, { withFileTypes: true });
|
|
19652
20732
|
for (const entry of entries) {
|
|
19653
20733
|
if (entry.isFile() && entry.name.endsWith(".db") && entry.name !== BACKUP_DIR_NAME) {
|
|
19654
|
-
const src =
|
|
19655
|
-
const dest =
|
|
20734
|
+
const src = path42.join(dir, entry.name);
|
|
20735
|
+
const dest = path42.join(backupDir, entry.name);
|
|
19656
20736
|
await copyFile(src, dest);
|
|
19657
20737
|
backedUpFiles.push(entry.name);
|
|
19658
20738
|
}
|
|
19659
20739
|
}
|
|
19660
20740
|
const externalFiles = [];
|
|
19661
|
-
const externalDir =
|
|
20741
|
+
const externalDir = path42.join(backupDir, "external");
|
|
19662
20742
|
for (const target of externalBackupTargets(homeDir)) {
|
|
19663
|
-
if (!
|
|
20743
|
+
if (!existsSync35(target.path)) continue;
|
|
19664
20744
|
await mkdir7(externalDir, { recursive: true });
|
|
19665
|
-
await copyFile(target.path,
|
|
20745
|
+
await copyFile(target.path, path42.join(externalDir, target.name));
|
|
19666
20746
|
externalFiles.push(target);
|
|
19667
20747
|
}
|
|
19668
20748
|
const manifest = {
|
|
@@ -19672,16 +20752,16 @@ async function createUpdateBackup(currentVersion, dataDir2, homeDir = os20.homed
|
|
|
19672
20752
|
...externalFiles.length > 0 ? { externalFiles } : {}
|
|
19673
20753
|
};
|
|
19674
20754
|
await writeFile7(
|
|
19675
|
-
|
|
20755
|
+
path42.join(backupDir, "manifest.json"),
|
|
19676
20756
|
JSON.stringify(manifest, null, 2) + "\n"
|
|
19677
20757
|
);
|
|
19678
20758
|
return manifest;
|
|
19679
20759
|
}
|
|
19680
20760
|
async function restoreFromBackup(dataDir2) {
|
|
19681
20761
|
const dir = dataDir2 ?? resolveDataDir2();
|
|
19682
|
-
const backupDir =
|
|
19683
|
-
const manifestPath =
|
|
19684
|
-
if (!
|
|
20762
|
+
const backupDir = path42.join(dir, BACKUP_DIR_NAME);
|
|
20763
|
+
const manifestPath = path42.join(backupDir, "manifest.json");
|
|
20764
|
+
if (!existsSync35(manifestPath)) {
|
|
19685
20765
|
throw new Error(
|
|
19686
20766
|
`No backup found at ${backupDir}. Nothing to restore.`
|
|
19687
20767
|
);
|
|
@@ -19690,9 +20770,9 @@ async function restoreFromBackup(dataDir2) {
|
|
|
19690
20770
|
await readFile6(manifestPath, "utf-8")
|
|
19691
20771
|
);
|
|
19692
20772
|
for (const fileName of manifest.files) {
|
|
19693
|
-
const src =
|
|
19694
|
-
const dest =
|
|
19695
|
-
if (!
|
|
20773
|
+
const src = path42.join(backupDir, fileName);
|
|
20774
|
+
const dest = path42.join(dir, fileName);
|
|
20775
|
+
if (!existsSync35(src)) continue;
|
|
19696
20776
|
const stat2 = await import("fs/promises").then((m) => m.stat(src));
|
|
19697
20777
|
if (stat2.isDirectory()) {
|
|
19698
20778
|
await cp(src, dest, { recursive: true, force: true });
|
|
@@ -19701,17 +20781,17 @@ async function restoreFromBackup(dataDir2) {
|
|
|
19701
20781
|
}
|
|
19702
20782
|
}
|
|
19703
20783
|
for (const external of manifest.externalFiles ?? []) {
|
|
19704
|
-
const src =
|
|
19705
|
-
if (!
|
|
19706
|
-
await mkdir7(
|
|
20784
|
+
const src = path42.join(backupDir, "external", external.name);
|
|
20785
|
+
if (!existsSync35(src)) continue;
|
|
20786
|
+
await mkdir7(path42.dirname(external.path), { recursive: true });
|
|
19707
20787
|
await copyFile(src, external.path);
|
|
19708
20788
|
}
|
|
19709
20789
|
return manifest;
|
|
19710
20790
|
}
|
|
19711
20791
|
async function deleteBackup(dataDir2) {
|
|
19712
20792
|
const dir = dataDir2 ?? resolveDataDir2();
|
|
19713
|
-
const backupDir =
|
|
19714
|
-
if (
|
|
20793
|
+
const backupDir = path42.join(dir, BACKUP_DIR_NAME);
|
|
20794
|
+
if (existsSync35(backupDir)) {
|
|
19715
20795
|
await rm2(backupDir, { recursive: true, force: true });
|
|
19716
20796
|
}
|
|
19717
20797
|
}
|
|
@@ -19735,17 +20815,17 @@ var init_update_backup = __esm({
|
|
|
19735
20815
|
});
|
|
19736
20816
|
|
|
19737
20817
|
// src/lib/update-check.ts
|
|
19738
|
-
import { execSync as
|
|
19739
|
-
import { readFileSync as
|
|
19740
|
-
import
|
|
20818
|
+
import { execSync as execSync16 } from "child_process";
|
|
20819
|
+
import { readFileSync as readFileSync30 } from "fs";
|
|
20820
|
+
import path43 from "path";
|
|
19741
20821
|
function getLocalVersion(packageRoot) {
|
|
19742
|
-
const pkgPath =
|
|
19743
|
-
const pkg = JSON.parse(
|
|
20822
|
+
const pkgPath = path43.join(packageRoot, "package.json");
|
|
20823
|
+
const pkg = JSON.parse(readFileSync30(pkgPath, "utf-8"));
|
|
19744
20824
|
return pkg.version;
|
|
19745
20825
|
}
|
|
19746
20826
|
function getRemoteVersion() {
|
|
19747
20827
|
try {
|
|
19748
|
-
const output2 =
|
|
20828
|
+
const output2 = execSync16("npm view @askexenow/exe-os version", {
|
|
19749
20829
|
encoding: "utf-8",
|
|
19750
20830
|
timeout: 15e3,
|
|
19751
20831
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -19784,7 +20864,7 @@ __export(update_exports, {
|
|
|
19784
20864
|
getRemoteVersion: () => getRemoteVersion,
|
|
19785
20865
|
runUpdate: () => runUpdate
|
|
19786
20866
|
});
|
|
19787
|
-
import { execSync as
|
|
20867
|
+
import { execSync as execSync17 } from "child_process";
|
|
19788
20868
|
import { createInterface as createInterface5 } from "readline";
|
|
19789
20869
|
async function runRestore() {
|
|
19790
20870
|
console.log("\n\u{1F504} Restoring from update backup...");
|
|
@@ -19795,7 +20875,7 @@ async function runRestore() {
|
|
|
19795
20875
|
console.log(`
|
|
19796
20876
|
\u{1F4E5} Reinstalling @askexenow/exe-os@${manifest.version}...`);
|
|
19797
20877
|
try {
|
|
19798
|
-
|
|
20878
|
+
execSync17(`npm install -g @askexenow/exe-os@${manifest.version}`, {
|
|
19799
20879
|
stdio: ["pipe", "pipe", "inherit"],
|
|
19800
20880
|
timeout: 3e5
|
|
19801
20881
|
});
|
|
@@ -19876,7 +20956,7 @@ async function runUpdate(cliArgs) {
|
|
|
19876
20956
|
}
|
|
19877
20957
|
console.log("\u{1F9F9} Clearing npm cache...");
|
|
19878
20958
|
try {
|
|
19879
|
-
|
|
20959
|
+
execSync17("npm cache clean --force", { stdio: "pipe" });
|
|
19880
20960
|
console.log(" Done");
|
|
19881
20961
|
} catch {
|
|
19882
20962
|
console.log(" Skipped (non-critical)");
|
|
@@ -19884,7 +20964,7 @@ async function runUpdate(cliArgs) {
|
|
|
19884
20964
|
console.log("\u{1F4E5} Installing @askexenow/exe-os@latest...");
|
|
19885
20965
|
console.log(" This may take a minute...\n");
|
|
19886
20966
|
try {
|
|
19887
|
-
|
|
20967
|
+
execSync17("npm install -g @askexenow/exe-os@latest", {
|
|
19888
20968
|
stdio: ["pipe", "pipe", "inherit"],
|
|
19889
20969
|
timeout: 3e5
|
|
19890
20970
|
});
|
|
@@ -19902,7 +20982,7 @@ async function runUpdate(cliArgs) {
|
|
|
19902
20982
|
newVersion = getLocalVersion(packageRoot);
|
|
19903
20983
|
} catch {
|
|
19904
20984
|
try {
|
|
19905
|
-
const out =
|
|
20985
|
+
const out = execSync17("npm list -g @askexenow/exe-os --depth=0 2>/dev/null", { encoding: "utf8" });
|
|
19906
20986
|
const match = out.match(/@askexenow\/exe-os@(\S+)/);
|
|
19907
20987
|
newVersion = match?.[1] ?? "unknown";
|
|
19908
20988
|
} catch {
|
|
@@ -19931,7 +21011,7 @@ async function runUpdate(cliArgs) {
|
|
|
19931
21011
|
}
|
|
19932
21012
|
console.log("\u{1F527} Re-registering MCP, hooks, wrappers, and daemon...");
|
|
19933
21013
|
try {
|
|
19934
|
-
|
|
21014
|
+
execSync17("exe-os-install --global", {
|
|
19935
21015
|
stdio: ["pipe", "inherit", "inherit"],
|
|
19936
21016
|
timeout: 3e5
|
|
19937
21017
|
});
|
|
@@ -19966,7 +21046,7 @@ async function runUpdate(cliArgs) {
|
|
|
19966
21046
|
}
|
|
19967
21047
|
try {
|
|
19968
21048
|
console.log("\u{1FA7A} Checking AskExe support intake...");
|
|
19969
|
-
|
|
21049
|
+
execSync17("exe-os support health", {
|
|
19970
21050
|
stdio: ["pipe", "inherit", "inherit"],
|
|
19971
21051
|
timeout: 3e4
|
|
19972
21052
|
});
|
|
@@ -19994,11 +21074,11 @@ var init_update = __esm({
|
|
|
19994
21074
|
// src/lib/stack-update.ts
|
|
19995
21075
|
import { execFileSync as execFileSync4, spawnSync } from "child_process";
|
|
19996
21076
|
import { createVerify, randomBytes as randomBytes2, verify as verifySignature } from "crypto";
|
|
19997
|
-
import { copyFileSync as copyFileSync5, existsSync as
|
|
21077
|
+
import { copyFileSync as copyFileSync5, existsSync as existsSync36, mkdirSync as mkdirSync25, readdirSync as readdirSync11, readFileSync as readFileSync31, renameSync as renameSync8, writeFileSync as writeFileSync26 } from "fs";
|
|
19998
21078
|
import http from "http";
|
|
19999
21079
|
import https from "https";
|
|
20000
|
-
import
|
|
20001
|
-
import { fileURLToPath as
|
|
21080
|
+
import path44 from "path";
|
|
21081
|
+
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
20002
21082
|
function isSignedEnvelope(value) {
|
|
20003
21083
|
return !!value && typeof value === "object" && "manifest" in value && "signature" in value;
|
|
20004
21084
|
}
|
|
@@ -20032,21 +21112,21 @@ function stableJson(value) {
|
|
|
20032
21112
|
return `{${Object.keys(obj).sort().map((key) => `${JSON.stringify(key)}:${stableJson(obj[key])}`).join(",")}}`;
|
|
20033
21113
|
}
|
|
20034
21114
|
function findLatestBackupEnvFile(envFile) {
|
|
20035
|
-
const backupDir =
|
|
20036
|
-
if (!
|
|
20037
|
-
const backups =
|
|
21115
|
+
const backupDir = path44.join(path44.dirname(envFile), ".exe-stack-backups");
|
|
21116
|
+
if (!existsSync36(backupDir)) return null;
|
|
21117
|
+
const backups = readdirSync11(backupDir).filter((name) => name.startsWith("env-") && name.endsWith(".bak")).sort();
|
|
20038
21118
|
const latest = backups.at(-1);
|
|
20039
|
-
return latest ?
|
|
21119
|
+
return latest ? path44.join(backupDir, latest) : null;
|
|
20040
21120
|
}
|
|
20041
21121
|
async function rollbackStackUpdate(options) {
|
|
20042
21122
|
const exec2 = options.exec ?? defaultExec;
|
|
20043
|
-
const backupEnvFile = options.lockFile &&
|
|
20044
|
-
const rollbackEnv = backupEnvFile &&
|
|
21123
|
+
const backupEnvFile = options.lockFile && existsSync36(options.lockFile) ? JSON.parse(readFileSync31(options.lockFile, "utf8")).backupEnvFile : void 0;
|
|
21124
|
+
const rollbackEnv = backupEnvFile && existsSync36(backupEnvFile) ? backupEnvFile : findLatestBackupEnvFile(options.envFile);
|
|
20045
21125
|
if (!rollbackEnv) throw new Error(`No stack backup env found beside ${options.envFile}`);
|
|
20046
|
-
|
|
21126
|
+
writeFileSync26(options.envFile, readFileSync31(rollbackEnv), { mode: 384 });
|
|
20047
21127
|
const composeArgs = ["compose", "--file", options.composeFile, "--env-file", options.envFile];
|
|
20048
21128
|
exec2("docker", [...composeArgs, "up", "-d"]);
|
|
20049
|
-
return { status: "rolled_back", targetVersion: "previous", changes: [], backupEnvFile: rollbackEnv, lockFile: options.lockFile ??
|
|
21129
|
+
return { status: "rolled_back", targetVersion: "previous", changes: [], backupEnvFile: rollbackEnv, lockFile: options.lockFile ?? path44.join(path44.dirname(options.envFile), ".exe-stack-lock.json") };
|
|
20050
21130
|
}
|
|
20051
21131
|
function parseStackManifest(raw, publicKey) {
|
|
20052
21132
|
const parsedRaw = JSON.parse(raw);
|
|
@@ -20071,7 +21151,7 @@ function parseStackManifest(raw, publicKey) {
|
|
|
20071
21151
|
}
|
|
20072
21152
|
async function loadStackManifest(ref, fetchText = defaultFetchText, publicKey, authToken) {
|
|
20073
21153
|
if (/^https?:\/\//.test(ref)) return parseStackManifest(await fetchTextWithAuth(ref, fetchText, authToken), publicKey);
|
|
20074
|
-
return parseStackManifest(
|
|
21154
|
+
return parseStackManifest(readFileSync31(ref, "utf8"), publicKey);
|
|
20075
21155
|
}
|
|
20076
21156
|
async function fetchTextWithAuth(ref, fetchText, authToken) {
|
|
20077
21157
|
if (!authToken || fetchText !== defaultFetchText) return fetchText(ref);
|
|
@@ -20200,9 +21280,9 @@ Emergency override requires --break-glass <reason> and writes an audit file.`
|
|
|
20200
21280
|
function writeBreakGlassAudit(plan, issues, options) {
|
|
20201
21281
|
const now2 = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
20202
21282
|
const stamp = now2().toISOString().replace(/[:.]/g, "-");
|
|
20203
|
-
const defaultDir =
|
|
20204
|
-
const auditFile = options.breakGlassAuditFile ??
|
|
20205
|
-
|
|
21283
|
+
const defaultDir = existsSync36("exe/output") ? "exe/output" : path44.dirname(options.envFile ?? ".");
|
|
21284
|
+
const auditFile = options.breakGlassAuditFile ?? path44.join(defaultDir, `stack-update-break-glass-${stamp}.md`);
|
|
21285
|
+
mkdirSync25(path44.dirname(auditFile), { recursive: true });
|
|
20206
21286
|
const body = [
|
|
20207
21287
|
`# Stack Update Break-Glass Audit \u2014 ${now2().toISOString()}`,
|
|
20208
21288
|
"",
|
|
@@ -20216,7 +21296,7 @@ function writeBreakGlassAudit(plan, issues, options) {
|
|
|
20216
21296
|
"Return this deployment to the standard pinned GHCR image path immediately after the emergency is resolved.",
|
|
20217
21297
|
""
|
|
20218
21298
|
].join("\n");
|
|
20219
|
-
|
|
21299
|
+
writeFileSync26(auditFile, body, { mode: 384 });
|
|
20220
21300
|
console.warn(`[stack-update] BREAK-GLASS deploy override recorded: ${auditFile}`);
|
|
20221
21301
|
}
|
|
20222
21302
|
function assertBreakingChangesAllowed(plan, allowedIds) {
|
|
@@ -20242,26 +21322,26 @@ function shellSucceeds(command) {
|
|
|
20242
21322
|
return res.status === 0;
|
|
20243
21323
|
}
|
|
20244
21324
|
function resolvePackageRoot2() {
|
|
20245
|
-
const here =
|
|
21325
|
+
const here = path44.dirname(fileURLToPath7(import.meta.url));
|
|
20246
21326
|
const candidates = [
|
|
20247
|
-
|
|
20248
|
-
|
|
21327
|
+
path44.resolve(here, "..", ".."),
|
|
21328
|
+
path44.resolve(here, ".."),
|
|
20249
21329
|
process.cwd()
|
|
20250
21330
|
];
|
|
20251
21331
|
for (const c of candidates) {
|
|
20252
|
-
if (
|
|
21332
|
+
if (existsSync36(path44.join(c, "package.json")) && existsSync36(path44.join(c, "deploy", "compose", "docker-compose.yml"))) return c;
|
|
20253
21333
|
}
|
|
20254
21334
|
return process.cwd();
|
|
20255
21335
|
}
|
|
20256
21336
|
function copyTemplateIfMissing(srcRel, dest, created) {
|
|
20257
|
-
if (
|
|
20258
|
-
const src =
|
|
20259
|
-
if (!
|
|
21337
|
+
if (existsSync36(dest)) return;
|
|
21338
|
+
const src = path44.join(resolvePackageRoot2(), srcRel);
|
|
21339
|
+
if (!existsSync36(src)) throw new Error(`Missing packaged stack template: ${srcRel}. Reinstall/update exe-os and retry.`);
|
|
20260
21340
|
try {
|
|
20261
|
-
|
|
21341
|
+
mkdirSync25(path44.dirname(dest), { recursive: true });
|
|
20262
21342
|
} catch (err) {
|
|
20263
21343
|
if (err.code === "EACCES") {
|
|
20264
|
-
const dir =
|
|
21344
|
+
const dir = path44.dirname(dest);
|
|
20265
21345
|
throw new Error(
|
|
20266
21346
|
`Permission denied creating ${dir}. Run this first:
|
|
20267
21347
|
|
|
@@ -20277,8 +21357,8 @@ Then re-run stack-update.`
|
|
|
20277
21357
|
}
|
|
20278
21358
|
function installDockerUbuntu(exec2) {
|
|
20279
21359
|
if (process.platform !== "linux") throw new Error("Docker auto-install is only supported on Linux. Install Docker manually, then retry.");
|
|
20280
|
-
if (!
|
|
20281
|
-
const osRelease =
|
|
21360
|
+
if (!existsSync36("/etc/os-release")) throw new Error("Cannot detect Linux distro; install Docker manually, then retry.");
|
|
21361
|
+
const osRelease = readFileSync31("/etc/os-release", "utf8");
|
|
20282
21362
|
if (!/ID=(ubuntu|debian)|ID_LIKE=.*debian/.test(osRelease)) {
|
|
20283
21363
|
throw new Error("Docker auto-install currently supports Ubuntu/Debian only. Install Docker manually, then retry.");
|
|
20284
21364
|
}
|
|
@@ -20350,14 +21430,14 @@ async function pairMonitorAgent(hubUrl, licenseKey, domain, envFile) {
|
|
|
20350
21430
|
if (!data.token || !data.key) {
|
|
20351
21431
|
return { paired: false, error: "Monitor hub response missing token or key" };
|
|
20352
21432
|
}
|
|
20353
|
-
if (
|
|
20354
|
-
const envRaw =
|
|
21433
|
+
if (existsSync36(envFile)) {
|
|
21434
|
+
const envRaw = readFileSync31(envFile, "utf8");
|
|
20355
21435
|
const patched = patchEnv(envRaw, {
|
|
20356
21436
|
MONITOR_AGENT_TOKEN: data.token,
|
|
20357
21437
|
MONITOR_AGENT_KEY: data.key
|
|
20358
21438
|
});
|
|
20359
21439
|
if (patched !== envRaw) {
|
|
20360
|
-
|
|
21440
|
+
writeFileSync26(envFile, patched, { mode: 384 });
|
|
20361
21441
|
}
|
|
20362
21442
|
}
|
|
20363
21443
|
return { paired: true, systemName: data.name ?? domain };
|
|
@@ -20380,26 +21460,26 @@ function bootstrapStackHost(options) {
|
|
|
20380
21460
|
}
|
|
20381
21461
|
copyTemplateIfMissing("deploy/compose/docker-compose.yml", options.composeFile, createdFiles);
|
|
20382
21462
|
copyTemplateIfMissing("deploy/compose/.env.customer.example", options.envFile, createdFiles);
|
|
20383
|
-
const brandingDest =
|
|
20384
|
-
copyTemplateIfMissing("deploy/compose/gateway.json",
|
|
20385
|
-
if (!
|
|
21463
|
+
const brandingDest = path44.join(path44.dirname(options.envFile), "branding.json");
|
|
21464
|
+
copyTemplateIfMissing("deploy/compose/gateway.json", path44.join(path44.dirname(options.envFile), "gateway.json"), createdFiles);
|
|
21465
|
+
if (!existsSync36(brandingDest)) writeFileSync26(brandingDest, JSON.stringify({ brandName: "Hygo", productName: "Hygo OS" }, null, 2) + "\n", { mode: 384 });
|
|
20386
21466
|
let envHadPlaceholders = false;
|
|
20387
21467
|
let envRemainingPlaceholders = [];
|
|
20388
|
-
if (
|
|
20389
|
-
const envRaw =
|
|
21468
|
+
if (existsSync36(options.envFile)) {
|
|
21469
|
+
const envRaw = readFileSync31(options.envFile, "utf8");
|
|
20390
21470
|
const hydrated = hydrateEnv(envRaw, options);
|
|
20391
21471
|
envHadPlaceholders = hydrated.hadPlaceholders;
|
|
20392
21472
|
envRemainingPlaceholders = hydrated.remaining;
|
|
20393
21473
|
if (hydrated.raw !== envRaw) {
|
|
20394
|
-
|
|
21474
|
+
writeFileSync26(options.envFile, hydrated.raw, { mode: 384 });
|
|
20395
21475
|
actions.push("hydrate_env_secrets");
|
|
20396
21476
|
}
|
|
20397
21477
|
}
|
|
20398
21478
|
return {
|
|
20399
21479
|
dockerInstalled,
|
|
20400
21480
|
dockerComposeInstalled,
|
|
20401
|
-
composeFileExists:
|
|
20402
|
-
envFileExists:
|
|
21481
|
+
composeFileExists: existsSync36(options.composeFile),
|
|
21482
|
+
envFileExists: existsSync36(options.envFile),
|
|
20403
21483
|
envHadPlaceholders,
|
|
20404
21484
|
envRemainingPlaceholders,
|
|
20405
21485
|
licensePresent: !!(options.licenseKey || process.env.EXE_LICENSE_KEY || loadLicense()),
|
|
@@ -20438,53 +21518,51 @@ async function runStackUpdate(options) {
|
|
|
20438
21518
|
const report = bootstrapStackHost({ ...options, installDocker: options.installDocker ?? !!options.yes });
|
|
20439
21519
|
if (!options.dryRun) assertHostReadyForApply(report);
|
|
20440
21520
|
}
|
|
20441
|
-
const hubUrl = options.monitorHubUrl || parseEnv(
|
|
21521
|
+
const hubUrl = options.monitorHubUrl || parseEnv(readFileSync31(options.envFile, "utf8")).get("MONITOR_HUB_URL") || "";
|
|
20442
21522
|
const pairLicense = options.licenseKey || process.env.EXE_LICENSE_KEY || loadLicense() || "";
|
|
20443
21523
|
const pairDomain = options.domain || process.env.EXE_STACK_DOMAIN || process.env.CUSTOMER_DOMAIN || "";
|
|
20444
21524
|
if (hubUrl && pairLicense && pairDomain) {
|
|
20445
|
-
const envBefore =
|
|
21525
|
+
const envBefore = readFileSync31(options.envFile, "utf8");
|
|
20446
21526
|
const hasPlaceholder = /CHANGEME/.test(parseEnv(envBefore).get("MONITOR_AGENT_TOKEN") ?? "");
|
|
20447
21527
|
if (hasPlaceholder) {
|
|
20448
21528
|
const pair = options.pairMonitor ? options.pairMonitor(hubUrl, pairLicense, pairDomain, options.envFile) : pairMonitorAgent(hubUrl, pairLicense, pairDomain, options.envFile);
|
|
20449
21529
|
const result = await pair;
|
|
20450
|
-
if (
|
|
20451
|
-
|
|
20452
|
-
|
|
20453
|
-
|
|
20454
|
-
console.warn(`[stack-update] Monitor pairing skipped: ${result.error}`);
|
|
20455
|
-
}
|
|
21530
|
+
if (result.paired) {
|
|
21531
|
+
console.log(`[stack-update] Monitor agent paired: ${result.systemName}`);
|
|
21532
|
+
} else {
|
|
21533
|
+
console.warn(`[stack-update] Monitor pairing skipped: ${result.error}`);
|
|
20456
21534
|
}
|
|
20457
21535
|
}
|
|
20458
21536
|
}
|
|
20459
21537
|
const manifest = await loadStackManifest(options.manifestRef, options.fetchText, options.manifestPublicKey, options.manifestAuthToken);
|
|
20460
|
-
const envRaw =
|
|
21538
|
+
const envRaw = readFileSync31(options.envFile, "utf8");
|
|
20461
21539
|
const plan = createStackUpdatePlan(manifest, envRaw, options.targetVersion);
|
|
20462
21540
|
assertBreakingChangesAllowed(plan, options.allowedBreakingChangeIds ?? []);
|
|
20463
21541
|
assertDeploymentScopeAllowed(plan, options.deploymentPersona ?? "customer");
|
|
20464
21542
|
const plannedEnvRaw = patchEnv(envRaw, Object.fromEntries(plan.changes.map((c) => [c.key, c.after])));
|
|
20465
|
-
const composeRaw =
|
|
21543
|
+
const composeRaw = readFileSync31(options.composeFile, "utf8");
|
|
20466
21544
|
assertProductionDeployGate(plan, plannedEnvRaw, composeRaw, {
|
|
20467
21545
|
breakGlassReason: options.breakGlassReason,
|
|
20468
21546
|
breakGlassAuditFile: options.breakGlassAuditFile,
|
|
20469
21547
|
now: now2,
|
|
20470
21548
|
envFile: options.envFile
|
|
20471
21549
|
});
|
|
20472
|
-
const lockFile = options.lockFile ??
|
|
21550
|
+
const lockFile = options.lockFile ?? path44.join(path44.dirname(options.envFile), ".exe-stack-lock.json");
|
|
20473
21551
|
const previousVersion = readCurrentStackVersion(lockFile);
|
|
20474
21552
|
const containersRunning = plan.changes.length === 0 ? areStackContainersRunning(options.composeFile, options.envFile) : true;
|
|
20475
21553
|
if (options.dryRun || plan.changes.length === 0 && containersRunning) {
|
|
20476
21554
|
return { status: "planned", targetVersion: plan.targetVersion, changes: plan.changes, lockFile };
|
|
20477
21555
|
}
|
|
20478
21556
|
await postDeployAudit(options, "started", plan.targetVersion, previousVersion);
|
|
20479
|
-
const backupDir =
|
|
20480
|
-
|
|
21557
|
+
const backupDir = path44.join(path44.dirname(options.envFile), ".exe-stack-backups");
|
|
21558
|
+
mkdirSync25(backupDir, { recursive: true });
|
|
20481
21559
|
const stamp = now2().toISOString().replace(/[:.]/g, "-");
|
|
20482
|
-
const backupEnvFile =
|
|
20483
|
-
|
|
21560
|
+
const backupEnvFile = path44.join(backupDir, `env-${stamp}.bak`);
|
|
21561
|
+
writeFileSync26(backupEnvFile, envRaw, { mode: 384 });
|
|
20484
21562
|
const updates = Object.fromEntries(plan.changes.map((c) => [c.key, c.after]));
|
|
20485
21563
|
const patched = patchEnv(envRaw, updates);
|
|
20486
21564
|
const tmp = `${options.envFile}.tmp-${process.pid}`;
|
|
20487
|
-
|
|
21565
|
+
writeFileSync26(tmp, patched, { mode: 384 });
|
|
20488
21566
|
renameSync8(tmp, options.envFile);
|
|
20489
21567
|
const composeArgs = ["compose", "--file", options.composeFile, "--env-file", options.envFile];
|
|
20490
21568
|
let registryForLogout;
|
|
@@ -20497,11 +21575,11 @@ async function runStackUpdate(options) {
|
|
|
20497
21575
|
exec2("docker", [...composeArgs, "pull"]);
|
|
20498
21576
|
exec2("docker", [...composeArgs, "up", "-d"]);
|
|
20499
21577
|
await verifyReleaseHealth(plan.release, options.healthRetries ?? 12, options.healthDelayMs ?? 5e3);
|
|
20500
|
-
|
|
21578
|
+
writeFileSync26(lockFile, JSON.stringify({ stackVersion: plan.targetVersion, updatedAt: now2().toISOString(), backupEnvFile, services: plan.release.services }, null, 2) + "\n");
|
|
20501
21579
|
await postDeployAudit(options, "success", plan.targetVersion, previousVersion, void 0, { changes: plan.changes.length });
|
|
20502
21580
|
return { status: "updated", targetVersion: plan.targetVersion, changes: plan.changes, backupEnvFile, lockFile };
|
|
20503
21581
|
} catch (err) {
|
|
20504
|
-
|
|
21582
|
+
writeFileSync26(options.envFile, envRaw, { mode: 384 });
|
|
20505
21583
|
try {
|
|
20506
21584
|
exec2("docker", [...composeArgs, "up", "-d"]);
|
|
20507
21585
|
} catch {
|
|
@@ -20532,9 +21610,9 @@ async function fetchImageCredentials(options) {
|
|
|
20532
21610
|
return await res.json();
|
|
20533
21611
|
}
|
|
20534
21612
|
function readCurrentStackVersion(lockFile) {
|
|
20535
|
-
if (!
|
|
21613
|
+
if (!existsSync36(lockFile)) return void 0;
|
|
20536
21614
|
try {
|
|
20537
|
-
const parsed = JSON.parse(
|
|
21615
|
+
const parsed = JSON.parse(readFileSync31(lockFile, "utf8"));
|
|
20538
21616
|
return parsed.stackVersion;
|
|
20539
21617
|
} catch {
|
|
20540
21618
|
return void 0;
|
|
@@ -20622,13 +21700,13 @@ async function defaultPostJson(url, body, authToken) {
|
|
|
20622
21700
|
if (!res.ok) throw new Error(`Failed to POST ${url}: HTTP ${res.status}`);
|
|
20623
21701
|
}
|
|
20624
21702
|
function defaultStackPaths() {
|
|
20625
|
-
const cwdCompose =
|
|
20626
|
-
const cwdEnv =
|
|
20627
|
-
const packagedManifest =
|
|
20628
|
-
const manifestRef = process.env.EXE_STACK_MANIFEST || (
|
|
21703
|
+
const cwdCompose = path44.resolve("docker-compose.yml");
|
|
21704
|
+
const cwdEnv = path44.resolve(".env");
|
|
21705
|
+
const packagedManifest = path44.join(resolvePackageRoot2(), "deploy", "stack-manifests", "v0.9.json");
|
|
21706
|
+
const manifestRef = process.env.EXE_STACK_MANIFEST || (existsSync36(packagedManifest) ? packagedManifest : "https://update.askexe.com/stack-manifest.json");
|
|
20629
21707
|
return {
|
|
20630
|
-
composeFile: process.env.EXE_STACK_COMPOSE_FILE || (
|
|
20631
|
-
envFile: process.env.EXE_STACK_ENV_FILE || (
|
|
21708
|
+
composeFile: process.env.EXE_STACK_COMPOSE_FILE || (existsSync36(cwdCompose) ? cwdCompose : "/opt/exe-stack/docker-compose.yml"),
|
|
21709
|
+
envFile: process.env.EXE_STACK_ENV_FILE || (existsSync36(cwdEnv) ? cwdEnv : "/opt/exe-stack/.env"),
|
|
20632
21710
|
manifestRef,
|
|
20633
21711
|
// Only call update.askexe.com if explicitly configured or if a remote manifest was requested.
|
|
20634
21712
|
// Packaged manifests keep cold-start installs unblocked even before update-service entitlements are provisioned.
|
|
@@ -20642,8 +21720,8 @@ function defaultStackPaths() {
|
|
|
20642
21720
|
}
|
|
20643
21721
|
function loadDefaultPublicKey() {
|
|
20644
21722
|
if (process.env.EXE_STACK_PUBLIC_KEY) return process.env.EXE_STACK_PUBLIC_KEY;
|
|
20645
|
-
if (process.env.EXE_STACK_PUBLIC_KEY_FILE &&
|
|
20646
|
-
return
|
|
21723
|
+
if (process.env.EXE_STACK_PUBLIC_KEY_FILE && existsSync36(process.env.EXE_STACK_PUBLIC_KEY_FILE)) {
|
|
21724
|
+
return readFileSync31(process.env.EXE_STACK_PUBLIC_KEY_FILE, "utf8");
|
|
20647
21725
|
}
|
|
20648
21726
|
return void 0;
|
|
20649
21727
|
}
|
|
@@ -20661,7 +21739,7 @@ var stack_update_exports = {};
|
|
|
20661
21739
|
__export(stack_update_exports, {
|
|
20662
21740
|
runStackUpdateCli: () => main7
|
|
20663
21741
|
});
|
|
20664
|
-
import { readFileSync as
|
|
21742
|
+
import { readFileSync as readFileSync32 } from "fs";
|
|
20665
21743
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
20666
21744
|
function parseArgs4(args2) {
|
|
20667
21745
|
const defaults = defaultStackPaths();
|
|
@@ -20695,8 +21773,8 @@ function parseArgs4(args2) {
|
|
|
20695
21773
|
else if (arg === "--env-file" || arg === "--stack-env-file") opts.envFile = next();
|
|
20696
21774
|
else if (arg.startsWith("--env-file=") || arg.startsWith("--stack-env-file=")) opts.envFile = arg.split("=").slice(1).join("=");
|
|
20697
21775
|
else if (arg === "--lock-file") opts.lockFile = next();
|
|
20698
|
-
else if (arg === "--public-key") opts.manifestPublicKey =
|
|
20699
|
-
else if (arg.startsWith("--public-key=")) opts.manifestPublicKey =
|
|
21776
|
+
else if (arg === "--public-key") opts.manifestPublicKey = readFileSync32(next(), "utf8");
|
|
21777
|
+
else if (arg.startsWith("--public-key=")) opts.manifestPublicKey = readFileSync32(arg.split("=").slice(1).join("="), "utf8");
|
|
20700
21778
|
else if (arg === "--auth-token") opts.manifestAuthToken = next();
|
|
20701
21779
|
else if (arg.startsWith("--auth-token=")) opts.manifestAuthToken = arg.split("=").slice(1).join("=");
|
|
20702
21780
|
else if (arg === "--auth-token-env") opts.manifestAuthToken = process.env[next()] ?? "";
|
|
@@ -20901,11 +21979,11 @@ async function main7(args2 = process.argv.slice(2)) {
|
|
|
20901
21979
|
if (!opts.check && !opts.dryRun) assertHostReadyForApply(hostReport);
|
|
20902
21980
|
}
|
|
20903
21981
|
const manifest = await loadStackManifest(opts.manifestRef, void 0, opts.manifestPublicKey, opts.manifestAuthToken);
|
|
20904
|
-
const envRaw =
|
|
21982
|
+
const envRaw = readFileSync32(opts.envFile, "utf8");
|
|
20905
21983
|
const plan = createStackUpdatePlan(manifest, envRaw, opts.targetVersion);
|
|
20906
21984
|
assertDeploymentScopeAllowed(plan, opts.deploymentPersona);
|
|
20907
21985
|
const plannedEnvRaw = patchEnv(envRaw, Object.fromEntries(plan.changes.map((c) => [c.key, c.after])));
|
|
20908
|
-
assertProductionDeployGate(plan, plannedEnvRaw,
|
|
21986
|
+
assertProductionDeployGate(plan, plannedEnvRaw, readFileSync32(opts.composeFile, "utf8"), {
|
|
20909
21987
|
breakGlassReason: opts.breakGlassReason,
|
|
20910
21988
|
breakGlassAuditFile: opts.breakGlassAuditFile,
|
|
20911
21989
|
envFile: opts.envFile
|
|
@@ -25507,8 +26585,8 @@ var init_ErrorOverview = __esm({
|
|
|
25507
26585
|
"use strict";
|
|
25508
26586
|
init_Box();
|
|
25509
26587
|
init_Text();
|
|
25510
|
-
cleanupPath = (
|
|
25511
|
-
return
|
|
26588
|
+
cleanupPath = (path58) => {
|
|
26589
|
+
return path58?.replace(`file://${cwd()}/`, "");
|
|
25512
26590
|
};
|
|
25513
26591
|
stackUtils = new StackUtils({
|
|
25514
26592
|
cwd: cwd(),
|
|
@@ -27612,13 +28690,13 @@ __export(tmux_status_exports, {
|
|
|
27612
28690
|
parseActivity: () => parseActivity,
|
|
27613
28691
|
parseContextPercentage: () => parseContextPercentage
|
|
27614
28692
|
});
|
|
27615
|
-
import { execSync as
|
|
28693
|
+
import { execSync as execSync18 } from "child_process";
|
|
27616
28694
|
function inTmux() {
|
|
27617
28695
|
if (process.env.TMUX || process.env.TMUX_PANE) return true;
|
|
27618
28696
|
const term = process.env.TERM ?? "";
|
|
27619
28697
|
if (term.startsWith("tmux") || term.startsWith("screen")) return true;
|
|
27620
28698
|
try {
|
|
27621
|
-
|
|
28699
|
+
execSync18("tmux display-message -p '#{session_name}' 2>/dev/null", {
|
|
27622
28700
|
encoding: "utf8",
|
|
27623
28701
|
timeout: 2e3
|
|
27624
28702
|
});
|
|
@@ -27628,12 +28706,12 @@ function inTmux() {
|
|
|
27628
28706
|
try {
|
|
27629
28707
|
let pid = process.ppid;
|
|
27630
28708
|
for (let depth = 0; depth < 8 && pid > 1; depth++) {
|
|
27631
|
-
const comm =
|
|
28709
|
+
const comm = execSync18(`ps -p ${pid} -o comm= 2>/dev/null`, {
|
|
27632
28710
|
encoding: "utf8",
|
|
27633
28711
|
timeout: 1e3
|
|
27634
28712
|
}).trim();
|
|
27635
28713
|
if (/tmux/.test(comm)) return true;
|
|
27636
|
-
const ppid =
|
|
28714
|
+
const ppid = execSync18(`ps -p ${pid} -o ppid= 2>/dev/null`, {
|
|
27637
28715
|
encoding: "utf8",
|
|
27638
28716
|
timeout: 1e3
|
|
27639
28717
|
}).trim();
|
|
@@ -27646,7 +28724,7 @@ function inTmux() {
|
|
|
27646
28724
|
}
|
|
27647
28725
|
function listTmuxSessions() {
|
|
27648
28726
|
try {
|
|
27649
|
-
const out =
|
|
28727
|
+
const out = execSync18("tmux list-sessions -F '#{session_name}' 2>/dev/null", {
|
|
27650
28728
|
encoding: "utf8",
|
|
27651
28729
|
timeout: 3e3
|
|
27652
28730
|
});
|
|
@@ -27657,7 +28735,7 @@ function listTmuxSessions() {
|
|
|
27657
28735
|
}
|
|
27658
28736
|
function capturePaneLines(windowName, lines = 10) {
|
|
27659
28737
|
try {
|
|
27660
|
-
const out =
|
|
28738
|
+
const out = execSync18(
|
|
27661
28739
|
`tmux capture-pane -t ${JSON.stringify(windowName)} -p 2>/dev/null | tail -${lines}`,
|
|
27662
28740
|
{ encoding: "utf8", timeout: 3e3 }
|
|
27663
28741
|
);
|
|
@@ -27668,7 +28746,7 @@ function capturePaneLines(windowName, lines = 10) {
|
|
|
27668
28746
|
}
|
|
27669
28747
|
function getPaneCwd(windowName) {
|
|
27670
28748
|
try {
|
|
27671
|
-
const out =
|
|
28749
|
+
const out = execSync18(
|
|
27672
28750
|
`tmux display-message -t ${JSON.stringify(windowName)} -p '#{pane_current_path}' 2>/dev/null`,
|
|
27673
28751
|
{ encoding: "utf8", timeout: 3e3 }
|
|
27674
28752
|
);
|
|
@@ -27679,7 +28757,7 @@ function getPaneCwd(windowName) {
|
|
|
27679
28757
|
}
|
|
27680
28758
|
function projectFromPath(dir) {
|
|
27681
28759
|
try {
|
|
27682
|
-
const root =
|
|
28760
|
+
const root = execSync18("git -C " + JSON.stringify(dir) + " rev-parse --show-toplevel 2>/dev/null", {
|
|
27683
28761
|
encoding: "utf8",
|
|
27684
28762
|
timeout: 3e3
|
|
27685
28763
|
}).trim();
|
|
@@ -27748,7 +28826,7 @@ function getEmployeeStatuses(employeeNames) {
|
|
|
27748
28826
|
}
|
|
27749
28827
|
let paneAlive = true;
|
|
27750
28828
|
try {
|
|
27751
|
-
const paneStatus =
|
|
28829
|
+
const paneStatus = execSync18(
|
|
27752
28830
|
`tmux list-panes -t ${JSON.stringify(sessionName)} -F '#{pane_dead}' 2>/dev/null`,
|
|
27753
28831
|
{ encoding: "utf8", timeout: 3e3 }
|
|
27754
28832
|
).trim();
|
|
@@ -27916,11 +28994,11 @@ function Footer() {
|
|
|
27916
28994
|
} catch {
|
|
27917
28995
|
}
|
|
27918
28996
|
try {
|
|
27919
|
-
const { existsSync:
|
|
28997
|
+
const { existsSync: existsSync43 } = await import("fs");
|
|
27920
28998
|
const { join } = await import("path");
|
|
27921
28999
|
const home = process.env.HOME ?? "";
|
|
27922
29000
|
const pidPath = join(home, ".exe-os", "exed.pid");
|
|
27923
|
-
setDaemon(
|
|
29001
|
+
setDaemon(existsSync43(pidPath) ? "running" : "stopped");
|
|
27924
29002
|
} catch {
|
|
27925
29003
|
setDaemon("unknown");
|
|
27926
29004
|
}
|
|
@@ -27938,8 +29016,8 @@ function Footer() {
|
|
|
27938
29016
|
setSessions(allSessions.length);
|
|
27939
29017
|
if (!currentSession) {
|
|
27940
29018
|
try {
|
|
27941
|
-
const { execSync:
|
|
27942
|
-
const name =
|
|
29019
|
+
const { execSync: execSync20 } = await import("child_process");
|
|
29020
|
+
const name = execSync20("tmux display-message -p '#{session_name}' 2>/dev/null", {
|
|
27943
29021
|
encoding: "utf8",
|
|
27944
29022
|
timeout: 2e3
|
|
27945
29023
|
}).trim();
|
|
@@ -29971,10 +31049,10 @@ var init_hooks = __esm({
|
|
|
29971
31049
|
});
|
|
29972
31050
|
|
|
29973
31051
|
// src/runtime/safety-checks.ts
|
|
29974
|
-
import
|
|
31052
|
+
import path45 from "path";
|
|
29975
31053
|
import os21 from "os";
|
|
29976
31054
|
function checkPathSafety(filePath) {
|
|
29977
|
-
const resolved =
|
|
31055
|
+
const resolved = path45.resolve(filePath);
|
|
29978
31056
|
for (const { pattern, reason } of BYPASS_IMMUNE_PATTERNS) {
|
|
29979
31057
|
const matches = typeof pattern === "function" ? pattern(resolved) : pattern.test(resolved);
|
|
29980
31058
|
if (matches) {
|
|
@@ -29984,7 +31062,7 @@ function checkPathSafety(filePath) {
|
|
|
29984
31062
|
return { safe: true, bypassImmune: true };
|
|
29985
31063
|
}
|
|
29986
31064
|
function checkReadPathSafety(filePath) {
|
|
29987
|
-
const resolved =
|
|
31065
|
+
const resolved = path45.resolve(filePath);
|
|
29988
31066
|
const credPatterns = BYPASS_IMMUNE_PATTERNS.filter(
|
|
29989
31067
|
(p) => typeof p.pattern !== "function" && (p.reason.includes("secrets") || p.reason.includes("Private key") || p.reason.includes("Credential"))
|
|
29990
31068
|
);
|
|
@@ -30010,11 +31088,11 @@ var init_safety_checks = __esm({
|
|
|
30010
31088
|
reason: "Git config can set hooks and command execution"
|
|
30011
31089
|
},
|
|
30012
31090
|
{
|
|
30013
|
-
pattern: (p) => p.startsWith(
|
|
31091
|
+
pattern: (p) => p.startsWith(path45.join(HOME, ".claude")),
|
|
30014
31092
|
reason: "Claude configuration files are protected"
|
|
30015
31093
|
},
|
|
30016
31094
|
{
|
|
30017
|
-
pattern: (p) => p.startsWith(
|
|
31095
|
+
pattern: (p) => p.startsWith(path45.join(HOME, ".exe-os")),
|
|
30018
31096
|
reason: "exe-os configuration files are protected"
|
|
30019
31097
|
},
|
|
30020
31098
|
{
|
|
@@ -30031,7 +31109,7 @@ var init_safety_checks = __esm({
|
|
|
30031
31109
|
},
|
|
30032
31110
|
{
|
|
30033
31111
|
pattern: (p) => {
|
|
30034
|
-
const name =
|
|
31112
|
+
const name = path45.basename(p);
|
|
30035
31113
|
return [".bashrc", ".zshrc", ".profile", ".bash_profile", ".zprofile", ".zshenv"].includes(name);
|
|
30036
31114
|
},
|
|
30037
31115
|
reason: "Shell configuration files can execute arbitrary code on login"
|
|
@@ -30058,7 +31136,7 @@ __export(file_read_exports, {
|
|
|
30058
31136
|
FileReadTool: () => FileReadTool
|
|
30059
31137
|
});
|
|
30060
31138
|
import fs3 from "fs/promises";
|
|
30061
|
-
import
|
|
31139
|
+
import path46 from "path";
|
|
30062
31140
|
import { z } from "zod";
|
|
30063
31141
|
function isBinary(buf) {
|
|
30064
31142
|
for (let i = 0; i < buf.length; i++) {
|
|
@@ -30094,7 +31172,7 @@ var init_file_read = __esm({
|
|
|
30094
31172
|
return { behavior: "allow" };
|
|
30095
31173
|
},
|
|
30096
31174
|
async call(input, context) {
|
|
30097
|
-
const filePath =
|
|
31175
|
+
const filePath = path46.isAbsolute(input.file_path) ? input.file_path : path46.resolve(context.cwd, input.file_path);
|
|
30098
31176
|
let stat2;
|
|
30099
31177
|
try {
|
|
30100
31178
|
stat2 = await fs3.stat(filePath);
|
|
@@ -30134,7 +31212,7 @@ __export(glob_exports, {
|
|
|
30134
31212
|
GlobTool: () => GlobTool
|
|
30135
31213
|
});
|
|
30136
31214
|
import fs4 from "fs/promises";
|
|
30137
|
-
import
|
|
31215
|
+
import path47 from "path";
|
|
30138
31216
|
import { z as z2 } from "zod";
|
|
30139
31217
|
async function walkDir(dir, maxDepth = 10) {
|
|
30140
31218
|
const results = [];
|
|
@@ -30150,7 +31228,7 @@ async function walkDir(dir, maxDepth = 10) {
|
|
|
30150
31228
|
if (entry.isDirectory() && (entry.name === "node_modules" || entry.name === ".git")) {
|
|
30151
31229
|
continue;
|
|
30152
31230
|
}
|
|
30153
|
-
const fullPath =
|
|
31231
|
+
const fullPath = path47.join(current, entry.name);
|
|
30154
31232
|
if (entry.isDirectory()) {
|
|
30155
31233
|
await walk(fullPath, depth + 1);
|
|
30156
31234
|
} else {
|
|
@@ -30184,11 +31262,11 @@ var init_glob = __esm({
|
|
|
30184
31262
|
inputSchema: inputSchema2,
|
|
30185
31263
|
isReadOnly: true,
|
|
30186
31264
|
async call(input, context) {
|
|
30187
|
-
const baseDir = input.path ?
|
|
31265
|
+
const baseDir = input.path ? path47.isAbsolute(input.path) ? input.path : path47.resolve(context.cwd, input.path) : context.cwd;
|
|
30188
31266
|
try {
|
|
30189
31267
|
const entries = await walkDir(baseDir);
|
|
30190
31268
|
const matched = entries.filter(
|
|
30191
|
-
(e) => simpleGlobMatch(
|
|
31269
|
+
(e) => simpleGlobMatch(path47.relative(baseDir, e.path), input.pattern)
|
|
30192
31270
|
);
|
|
30193
31271
|
matched.sort((a, b) => b.mtime - a.mtime);
|
|
30194
31272
|
if (matched.length === 0) {
|
|
@@ -30214,7 +31292,7 @@ __export(grep_exports, {
|
|
|
30214
31292
|
});
|
|
30215
31293
|
import { spawn as spawn2 } from "child_process";
|
|
30216
31294
|
import fs5 from "fs/promises";
|
|
30217
|
-
import
|
|
31295
|
+
import path48 from "path";
|
|
30218
31296
|
import { z as z3 } from "zod";
|
|
30219
31297
|
function runRipgrep(input, searchPath, context) {
|
|
30220
31298
|
return new Promise((resolve, reject) => {
|
|
@@ -30268,7 +31346,7 @@ async function nodeGrep(input, searchPath) {
|
|
|
30268
31346
|
}
|
|
30269
31347
|
for (const entry of entries) {
|
|
30270
31348
|
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
30271
|
-
const fullPath =
|
|
31349
|
+
const fullPath = path48.join(dir, entry.name);
|
|
30272
31350
|
if (entry.isDirectory()) {
|
|
30273
31351
|
await walk(fullPath);
|
|
30274
31352
|
} else {
|
|
@@ -30314,7 +31392,7 @@ var init_grep = __esm({
|
|
|
30314
31392
|
inputSchema: inputSchema3,
|
|
30315
31393
|
isReadOnly: true,
|
|
30316
31394
|
async call(input, context) {
|
|
30317
|
-
const searchPath = input.path ?
|
|
31395
|
+
const searchPath = input.path ? path48.isAbsolute(input.path) ? input.path : path48.resolve(context.cwd, input.path) : context.cwd;
|
|
30318
31396
|
try {
|
|
30319
31397
|
const result = await runRipgrep(input, searchPath, context);
|
|
30320
31398
|
return result;
|
|
@@ -30339,7 +31417,7 @@ __export(file_write_exports, {
|
|
|
30339
31417
|
FileWriteTool: () => FileWriteTool
|
|
30340
31418
|
});
|
|
30341
31419
|
import fs6 from "fs/promises";
|
|
30342
|
-
import
|
|
31420
|
+
import path49 from "path";
|
|
30343
31421
|
import { z as z4 } from "zod";
|
|
30344
31422
|
var inputSchema4, FileWriteTool;
|
|
30345
31423
|
var init_file_write = __esm({
|
|
@@ -30367,8 +31445,8 @@ var init_file_write = __esm({
|
|
|
30367
31445
|
return { behavior: "allow" };
|
|
30368
31446
|
},
|
|
30369
31447
|
async call(input, context) {
|
|
30370
|
-
const filePath =
|
|
30371
|
-
const dir =
|
|
31448
|
+
const filePath = path49.isAbsolute(input.file_path) ? input.file_path : path49.resolve(context.cwd, input.file_path);
|
|
31449
|
+
const dir = path49.dirname(filePath);
|
|
30372
31450
|
await fs6.mkdir(dir, { recursive: true });
|
|
30373
31451
|
await fs6.writeFile(filePath, input.content, "utf-8");
|
|
30374
31452
|
return {
|
|
@@ -30386,7 +31464,7 @@ __export(file_edit_exports, {
|
|
|
30386
31464
|
FileEditTool: () => FileEditTool
|
|
30387
31465
|
});
|
|
30388
31466
|
import fs7 from "fs/promises";
|
|
30389
|
-
import
|
|
31467
|
+
import path50 from "path";
|
|
30390
31468
|
import { z as z5 } from "zod";
|
|
30391
31469
|
function countOccurrences(haystack, needle) {
|
|
30392
31470
|
let count = 0;
|
|
@@ -30427,7 +31505,7 @@ var init_file_edit = __esm({
|
|
|
30427
31505
|
return { behavior: "allow" };
|
|
30428
31506
|
},
|
|
30429
31507
|
async call(input, context) {
|
|
30430
|
-
const filePath =
|
|
31508
|
+
const filePath = path50.isAbsolute(input.file_path) ? input.file_path : path50.resolve(context.cwd, input.file_path);
|
|
30431
31509
|
let content;
|
|
30432
31510
|
try {
|
|
30433
31511
|
content = await fs7.readFile(filePath, "utf-8");
|
|
@@ -30669,7 +31747,7 @@ var init_bash = __esm({
|
|
|
30669
31747
|
// src/tui/views/CommandCenter.tsx
|
|
30670
31748
|
import { useState as useState6, useEffect as useEffect8, useMemo as useMemo4, useCallback as useCallback4, useRef as useRef4 } from "react";
|
|
30671
31749
|
import TextInput from "ink-text-input";
|
|
30672
|
-
import
|
|
31750
|
+
import path51 from "path";
|
|
30673
31751
|
import { homedir as homedir6 } from "os";
|
|
30674
31752
|
import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
30675
31753
|
function CommandCenterView({
|
|
@@ -30704,15 +31782,15 @@ function CommandCenterView({
|
|
|
30704
31782
|
const { createPermissionsFromPreset: createPermissionsFromPreset2, EMPLOYEE_PERMISSIONS: EMPLOYEE_PERMISSIONS2 } = await Promise.resolve().then(() => (init_permissions(), permissions_exports));
|
|
30705
31783
|
const { getPresetByRole: getPresetByRole2 } = await Promise.resolve().then(() => (init_permission_presets(), permission_presets_exports));
|
|
30706
31784
|
const { createDefaultHooks: createDefaultHooks2 } = await Promise.resolve().then(() => (init_hooks(), hooks_exports));
|
|
30707
|
-
const { readFileSync:
|
|
31785
|
+
const { readFileSync: readFileSync39, existsSync: existsSync43 } = await import("fs");
|
|
30708
31786
|
const { join } = await import("path");
|
|
30709
31787
|
const { homedir: homedir8 } = await import("os");
|
|
30710
31788
|
const configPath = join(homedir8(), ".exe-os", "config.json");
|
|
30711
31789
|
let failoverChain = ["anthropic", "opencode", "gemini", "openai"];
|
|
30712
31790
|
let providerConfigs = {};
|
|
30713
|
-
if (
|
|
31791
|
+
if (existsSync43(configPath)) {
|
|
30714
31792
|
try {
|
|
30715
|
-
const raw = JSON.parse(
|
|
31793
|
+
const raw = JSON.parse(readFileSync39(configPath, "utf8"));
|
|
30716
31794
|
if (Array.isArray(raw.failoverChain)) failoverChain = raw.failoverChain;
|
|
30717
31795
|
if (raw.providers && typeof raw.providers === "object") {
|
|
30718
31796
|
providerConfigs = raw.providers;
|
|
@@ -30773,7 +31851,7 @@ function CommandCenterView({
|
|
|
30773
31851
|
const markerDir = join(homedir8(), ".exe-os", "session-cache");
|
|
30774
31852
|
const agentFiles = (await import("fs")).readdirSync(markerDir).filter((f) => f.startsWith("active-agent-"));
|
|
30775
31853
|
for (const f of agentFiles) {
|
|
30776
|
-
const data = JSON.parse(
|
|
31854
|
+
const data = JSON.parse(readFileSync39(join(markerDir, f), "utf8"));
|
|
30777
31855
|
if (data.agentRole) {
|
|
30778
31856
|
agentRole = data.agentRole;
|
|
30779
31857
|
break;
|
|
@@ -30918,7 +31996,7 @@ function CommandCenterView({
|
|
|
30918
31996
|
const demoEntries = DEMO_PROJECTS.map((p) => ({
|
|
30919
31997
|
projectName: p.projectName,
|
|
30920
31998
|
exeSession: p.exeSession,
|
|
30921
|
-
projectDir:
|
|
31999
|
+
projectDir: path51.join(homedir6(), p.projectName),
|
|
30922
32000
|
employeeCount: p.employees.length,
|
|
30923
32001
|
activeCount: p.employees.filter((e) => e.status === "active").length,
|
|
30924
32002
|
memoryCount: p.employees.length * 4e3,
|
|
@@ -30956,7 +32034,7 @@ function CommandCenterView({
|
|
|
30956
32034
|
const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session_registry(), session_registry_exports));
|
|
30957
32035
|
const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
|
|
30958
32036
|
const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
30959
|
-
const { existsSync:
|
|
32037
|
+
const { existsSync: existsSync43 } = await import("fs");
|
|
30960
32038
|
const { join } = await import("path");
|
|
30961
32039
|
const client = getClient2();
|
|
30962
32040
|
if (!client) {
|
|
@@ -31027,7 +32105,7 @@ function CommandCenterView({
|
|
|
31027
32105
|
}
|
|
31028
32106
|
const memoryCount = memoryCounts.get(name) ?? 0;
|
|
31029
32107
|
const openTaskCount = openTaskCounts.get(name) ?? 0;
|
|
31030
|
-
const hasGit = projectDir ?
|
|
32108
|
+
const hasGit = projectDir ? existsSync43(join(projectDir, ".git")) : false;
|
|
31031
32109
|
const type = hasGit ? "code" : memoryCount > 0 ? "code" : "automation";
|
|
31032
32110
|
projectList.push({
|
|
31033
32111
|
projectName: name,
|
|
@@ -31052,7 +32130,7 @@ function CommandCenterView({
|
|
|
31052
32130
|
setHealth((h) => ({ ...h, memories: Number(totalResult.rows[0]?.cnt ?? 0) }));
|
|
31053
32131
|
try {
|
|
31054
32132
|
const pidPath = join(process.env.HOME ?? "", ".exe-os", "exed.pid");
|
|
31055
|
-
setHealth((h) => ({ ...h, daemon:
|
|
32133
|
+
setHealth((h) => ({ ...h, daemon: existsSync43(pidPath) ? "running" : "stopped" }));
|
|
31056
32134
|
} catch {
|
|
31057
32135
|
}
|
|
31058
32136
|
const activityResult = await client.execute(
|
|
@@ -31293,8 +32371,8 @@ function TmuxPane({ sessionName, employeeName, employeeRole, projectName, onDeta
|
|
|
31293
32371
|
}
|
|
31294
32372
|
const capture = () => {
|
|
31295
32373
|
try {
|
|
31296
|
-
const { execSync:
|
|
31297
|
-
const output2 =
|
|
32374
|
+
const { execSync: execSync20 } = __require("child_process");
|
|
32375
|
+
const output2 = execSync20(
|
|
31298
32376
|
`tmux capture-pane -t ${JSON.stringify(sessionName)} -p -e 2>/dev/null | tail -${CAPTURE_LINES}`,
|
|
31299
32377
|
{ encoding: "utf8", timeout: 3e3 }
|
|
31300
32378
|
);
|
|
@@ -31318,8 +32396,8 @@ function TmuxPane({ sessionName, employeeName, employeeRole, projectName, onDeta
|
|
|
31318
32396
|
if (key.return) {
|
|
31319
32397
|
if (!demo && inputBuffer.trim()) {
|
|
31320
32398
|
try {
|
|
31321
|
-
const { execSync:
|
|
31322
|
-
|
|
32399
|
+
const { execSync: execSync20 } = __require("child_process");
|
|
32400
|
+
execSync20(
|
|
31323
32401
|
`tmux send-keys -t ${JSON.stringify(sessionName)} ${JSON.stringify(inputBuffer)} Enter`,
|
|
31324
32402
|
{ timeout: 2e3 }
|
|
31325
32403
|
);
|
|
@@ -31327,8 +32405,8 @@ function TmuxPane({ sessionName, employeeName, employeeRole, projectName, onDeta
|
|
|
31327
32405
|
}
|
|
31328
32406
|
} else if (!demo) {
|
|
31329
32407
|
try {
|
|
31330
|
-
const { execSync:
|
|
31331
|
-
|
|
32408
|
+
const { execSync: execSync20 } = __require("child_process");
|
|
32409
|
+
execSync20(`tmux send-keys -t ${JSON.stringify(sessionName)} Enter`, { timeout: 2e3 });
|
|
31332
32410
|
} catch {
|
|
31333
32411
|
}
|
|
31334
32412
|
}
|
|
@@ -31808,12 +32886,12 @@ function useOrchestrator(enabled = true) {
|
|
|
31808
32886
|
searchFn: async () => [],
|
|
31809
32887
|
autoApproveP2: true
|
|
31810
32888
|
});
|
|
31811
|
-
await
|
|
32889
|
+
await runHealthCheck2();
|
|
31812
32890
|
} catch {
|
|
31813
32891
|
setIsLoading(false);
|
|
31814
32892
|
}
|
|
31815
32893
|
}
|
|
31816
|
-
async function
|
|
32894
|
+
async function runHealthCheck2() {
|
|
31817
32895
|
if (cancelled || !orchestratorRef.current) return;
|
|
31818
32896
|
try {
|
|
31819
32897
|
const health = await orchestratorRef.current.healthCheck();
|
|
@@ -31842,7 +32920,7 @@ function useOrchestrator(enabled = true) {
|
|
|
31842
32920
|
setIsLoading(false);
|
|
31843
32921
|
}
|
|
31844
32922
|
init();
|
|
31845
|
-
const timer = setInterval(
|
|
32923
|
+
const timer = setInterval(runHealthCheck2, TICK_INTERVAL_MS);
|
|
31846
32924
|
return () => {
|
|
31847
32925
|
cancelled = true;
|
|
31848
32926
|
clearInterval(timer);
|
|
@@ -31922,7 +33000,7 @@ var init_useOrchestrator = __esm({
|
|
|
31922
33000
|
|
|
31923
33001
|
// src/tui/views/Sessions.tsx
|
|
31924
33002
|
import React19, { useState as useState9, useEffect as useEffect11, useCallback as useCallback6 } from "react";
|
|
31925
|
-
import
|
|
33003
|
+
import path52 from "path";
|
|
31926
33004
|
import { homedir as homedir7 } from "os";
|
|
31927
33005
|
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
31928
33006
|
function isCoordinatorEntry(entry) {
|
|
@@ -31960,7 +33038,7 @@ function SessionsView({
|
|
|
31960
33038
|
if (demo) {
|
|
31961
33039
|
setProjects(DEMO_PROJECTS.map((p) => ({
|
|
31962
33040
|
...p,
|
|
31963
|
-
projectDir:
|
|
33041
|
+
projectDir: path52.join(homedir7(), p.projectName),
|
|
31964
33042
|
employees: p.employees.map((e) => ({ ...e, attached: e.status === "active" }))
|
|
31965
33043
|
})));
|
|
31966
33044
|
return;
|
|
@@ -32015,12 +33093,12 @@ function SessionsView({
|
|
|
32015
33093
|
return;
|
|
32016
33094
|
}
|
|
32017
33095
|
} else {
|
|
32018
|
-
const { execSync:
|
|
33096
|
+
const { execSync: execSync20 } = await import("child_process");
|
|
32019
33097
|
const dir = projectDir || process.cwd();
|
|
32020
|
-
|
|
32021
|
-
|
|
33098
|
+
execSync20(`tmux new-session -d -s ${JSON.stringify(entry.sessionName)} -c ${JSON.stringify(dir)}`, { timeout: 5e3 });
|
|
33099
|
+
execSync20(`tmux send-keys -t ${JSON.stringify(entry.sessionName)} "claude --dangerously-skip-permissions" Enter`, { timeout: 3e3 });
|
|
32022
33100
|
await new Promise((r) => setTimeout(r, 3e3));
|
|
32023
|
-
|
|
33101
|
+
execSync20(`tmux send-keys -t ${JSON.stringify(entry.sessionName)} "/exe" Enter`, { timeout: 3e3 });
|
|
32024
33102
|
}
|
|
32025
33103
|
const updated = { ...entry, status: "active", activity: "Starting...", attached: false };
|
|
32026
33104
|
setViewingEmployee(updated);
|
|
@@ -32123,7 +33201,7 @@ function SessionsView({
|
|
|
32123
33201
|
const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2, capturePaneLines: capturePaneLines2, parseActivity: parseActivity2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
|
|
32124
33202
|
const { getCoordinatorName: getCoordinatorName2, isCoordinatorRole: isCoordinatorRole2, loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
32125
33203
|
const { isExeSession: isExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
|
|
32126
|
-
const { execSync:
|
|
33204
|
+
const { execSync: execSync20 } = await import("child_process");
|
|
32127
33205
|
if (!inTmux2()) {
|
|
32128
33206
|
setTmuxAvailable(false);
|
|
32129
33207
|
setProjects([]);
|
|
@@ -32132,7 +33210,7 @@ function SessionsView({
|
|
|
32132
33210
|
setTmuxAvailable(true);
|
|
32133
33211
|
const attachedMap = /* @__PURE__ */ new Map();
|
|
32134
33212
|
try {
|
|
32135
|
-
const out =
|
|
33213
|
+
const out = execSync20("tmux list-sessions -F '#{session_name}:#{session_attached}' 2>/dev/null", {
|
|
32136
33214
|
encoding: "utf8",
|
|
32137
33215
|
timeout: 3e3
|
|
32138
33216
|
});
|
|
@@ -33168,19 +34246,19 @@ function upsertConversation(conversations, platform, senderId, message) {
|
|
|
33168
34246
|
async function loadGatewayConfig() {
|
|
33169
34247
|
const state = { running: false, port: 3100, adapters: [], agents: [], gatewayUrl: "" };
|
|
33170
34248
|
try {
|
|
33171
|
-
const { execSync:
|
|
33172
|
-
const ps =
|
|
34249
|
+
const { execSync: execSync20 } = await import("child_process");
|
|
34250
|
+
const ps = execSync20("pgrep -f exe-gateway 2>/dev/null", { encoding: "utf8", timeout: 3e3 });
|
|
33173
34251
|
state.running = ps.trim().length > 0;
|
|
33174
34252
|
} catch {
|
|
33175
34253
|
state.running = false;
|
|
33176
34254
|
}
|
|
33177
34255
|
try {
|
|
33178
|
-
const { existsSync:
|
|
34256
|
+
const { existsSync: existsSync43, readFileSync: readFileSync39 } = await import("fs");
|
|
33179
34257
|
const { join } = await import("path");
|
|
33180
34258
|
const home = process.env.HOME ?? "";
|
|
33181
34259
|
const configPath = join(home, ".exe-os", "gateway.json");
|
|
33182
|
-
if (
|
|
33183
|
-
const raw = JSON.parse(
|
|
34260
|
+
if (existsSync43(configPath)) {
|
|
34261
|
+
const raw = JSON.parse(readFileSync39(configPath, "utf8"));
|
|
33184
34262
|
state.port = raw.port ?? 3100;
|
|
33185
34263
|
state.gatewayUrl = raw.gatewayUrl ?? "";
|
|
33186
34264
|
if (raw.adapters) {
|
|
@@ -33646,10 +34724,10 @@ var init_Gateway = __esm({
|
|
|
33646
34724
|
});
|
|
33647
34725
|
|
|
33648
34726
|
// src/tui/utils/agent-status.ts
|
|
33649
|
-
import { execSync as
|
|
34727
|
+
import { execSync as execSync19 } from "child_process";
|
|
33650
34728
|
function getAgentStatus(agentId) {
|
|
33651
34729
|
try {
|
|
33652
|
-
const sessions =
|
|
34730
|
+
const sessions = execSync19("tmux list-sessions -F '#{session_name}' 2>/dev/null", {
|
|
33653
34731
|
encoding: "utf8",
|
|
33654
34732
|
timeout: 2e3
|
|
33655
34733
|
}).trim().split("\n");
|
|
@@ -33660,7 +34738,7 @@ function getAgentStatus(agentId) {
|
|
|
33660
34738
|
return /^\d?-/.test(suffix) || /^\d+$/.test(suffix);
|
|
33661
34739
|
});
|
|
33662
34740
|
if (!agentSession) return { label: "offline", color: "gray" };
|
|
33663
|
-
const pane =
|
|
34741
|
+
const pane = execSync19(`tmux capture-pane -t "${agentSession}" -p 2>/dev/null | tail -3`, {
|
|
33664
34742
|
encoding: "utf8",
|
|
33665
34743
|
timeout: 2e3
|
|
33666
34744
|
});
|
|
@@ -33778,12 +34856,12 @@ function TeamView({ onBack, onViewSessions }) {
|
|
|
33778
34856
|
setMembers(teamData);
|
|
33779
34857
|
setDbError(null);
|
|
33780
34858
|
try {
|
|
33781
|
-
const { existsSync:
|
|
34859
|
+
const { existsSync: existsSync43, readFileSync: readFileSync39 } = await import("fs");
|
|
33782
34860
|
const { join } = await import("path");
|
|
33783
34861
|
const home = process.env.HOME ?? "";
|
|
33784
34862
|
const gatewayConfig = join(home, ".exe-os", "gateway.json");
|
|
33785
|
-
if (
|
|
33786
|
-
const raw = JSON.parse(
|
|
34863
|
+
if (existsSync43(gatewayConfig)) {
|
|
34864
|
+
const raw = JSON.parse(readFileSync39(gatewayConfig, "utf8"));
|
|
33787
34865
|
if (raw.agents && raw.agents.length > 0) {
|
|
33788
34866
|
setExternals(raw.agents.map((a) => ({
|
|
33789
34867
|
name: a.name,
|
|
@@ -33963,8 +35041,8 @@ __export(wiki_client_exports, {
|
|
|
33963
35041
|
listDocuments: () => listDocuments,
|
|
33964
35042
|
listWorkspaces: () => listWorkspaces
|
|
33965
35043
|
});
|
|
33966
|
-
async function wikiFetch(config,
|
|
33967
|
-
const url = `${config.baseUrl}/api/v1${
|
|
35044
|
+
async function wikiFetch(config, path58, method = "GET", body) {
|
|
35045
|
+
const url = `${config.baseUrl}/api/v1${path58}`;
|
|
33968
35046
|
const headers = {
|
|
33969
35047
|
Authorization: `Bearer ${config.apiKey}`,
|
|
33970
35048
|
"Content-Type": "application/json"
|
|
@@ -33997,7 +35075,7 @@ async function wikiFetch(config, path56, method = "GET", body) {
|
|
|
33997
35075
|
}
|
|
33998
35076
|
}
|
|
33999
35077
|
if (!response.ok) {
|
|
34000
|
-
throw new Error(`Wiki API ${method} ${
|
|
35078
|
+
throw new Error(`Wiki API ${method} ${path58}: ${response.status} ${response.statusText}`);
|
|
34001
35079
|
}
|
|
34002
35080
|
return response.json();
|
|
34003
35081
|
} finally {
|
|
@@ -34583,20 +35661,20 @@ function SettingsView({ onBack }) {
|
|
|
34583
35661
|
};
|
|
34584
35662
|
});
|
|
34585
35663
|
try {
|
|
34586
|
-
const { execSync:
|
|
34587
|
-
|
|
35664
|
+
const { execSync: execSync20 } = await import("child_process");
|
|
35665
|
+
execSync20("curl -s --max-time 1 http://localhost:11434/api/tags", { timeout: 2e3 });
|
|
34588
35666
|
providerList.push({ name: "Ollama", configured: true, detail: "localhost:11434" });
|
|
34589
35667
|
} catch {
|
|
34590
35668
|
providerList.push({ name: "Ollama", configured: false, detail: "not running" });
|
|
34591
35669
|
}
|
|
34592
35670
|
setProviders(providerList);
|
|
34593
35671
|
try {
|
|
34594
|
-
const { existsSync:
|
|
35672
|
+
const { existsSync: existsSync43 } = await import("fs");
|
|
34595
35673
|
const { join } = await import("path");
|
|
34596
35674
|
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
34597
35675
|
const cfg = await loadConfig2();
|
|
34598
35676
|
const home = process.env.HOME ?? "";
|
|
34599
|
-
const hasKey =
|
|
35677
|
+
const hasKey = existsSync43(join(home, ".exe-os", "master.key"));
|
|
34600
35678
|
if (cfg.cloud) {
|
|
34601
35679
|
setCloud({
|
|
34602
35680
|
configured: true,
|
|
@@ -34609,22 +35687,22 @@ function SettingsView({ onBack }) {
|
|
|
34609
35687
|
const pidPath = join(home, ".exe-os", "exed.pid");
|
|
34610
35688
|
let daemon = "unknown";
|
|
34611
35689
|
try {
|
|
34612
|
-
daemon =
|
|
35690
|
+
daemon = existsSync43(pidPath) ? "running" : "stopped";
|
|
34613
35691
|
} catch {
|
|
34614
35692
|
}
|
|
34615
35693
|
let version = "unknown";
|
|
34616
35694
|
try {
|
|
34617
|
-
const { readFileSync:
|
|
35695
|
+
const { readFileSync: readFileSync39 } = await import("fs");
|
|
34618
35696
|
const { createRequire: createRequire3 } = await import("module");
|
|
34619
35697
|
const require2 = createRequire3(import.meta.url);
|
|
34620
35698
|
const pkgPath = require2.resolve("@askexenow/exe-os/package.json");
|
|
34621
|
-
const pkg = JSON.parse(
|
|
35699
|
+
const pkg = JSON.parse(readFileSync39(pkgPath, "utf8"));
|
|
34622
35700
|
version = pkg.version;
|
|
34623
35701
|
} catch {
|
|
34624
35702
|
try {
|
|
34625
|
-
const { readFileSync:
|
|
35703
|
+
const { readFileSync: readFileSync39 } = await import("fs");
|
|
34626
35704
|
const { join: joinPath } = await import("path");
|
|
34627
|
-
const pkg = JSON.parse(
|
|
35705
|
+
const pkg = JSON.parse(readFileSync39(joinPath(process.cwd(), "package.json"), "utf8"));
|
|
34628
35706
|
version = pkg.version;
|
|
34629
35707
|
} catch {
|
|
34630
35708
|
}
|
|
@@ -35483,18 +36561,18 @@ __export(code_context_index_exports, {
|
|
|
35483
36561
|
traceCodeSymbol: () => traceCodeSymbol
|
|
35484
36562
|
});
|
|
35485
36563
|
import crypto14 from "crypto";
|
|
35486
|
-
import
|
|
35487
|
-
import { existsSync as
|
|
36564
|
+
import path53 from "path";
|
|
36565
|
+
import { existsSync as existsSync38, mkdirSync as mkdirSync26, readFileSync as readFileSync34, readdirSync as readdirSync12, statSync as statSync8, writeFileSync as writeFileSync27 } from "fs";
|
|
35488
36566
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
35489
36567
|
function vectorStorePath(projectRoot) {
|
|
35490
36568
|
const rootHash = hashText(projectRoot).slice(0, 16);
|
|
35491
|
-
return
|
|
36569
|
+
return path53.join(indexDir(), `${rootHash}.vectors.json`);
|
|
35492
36570
|
}
|
|
35493
36571
|
function loadVectorStore(projectRoot) {
|
|
35494
36572
|
const file = vectorStorePath(projectRoot);
|
|
35495
|
-
if (!
|
|
36573
|
+
if (!existsSync38(file)) return null;
|
|
35496
36574
|
try {
|
|
35497
|
-
const parsed = JSON.parse(
|
|
36575
|
+
const parsed = JSON.parse(readFileSync34(file, "utf8"));
|
|
35498
36576
|
if (parsed.version !== VECTOR_STORE_VERSION) return null;
|
|
35499
36577
|
return parsed;
|
|
35500
36578
|
} catch {
|
|
@@ -35502,7 +36580,7 @@ function loadVectorStore(projectRoot) {
|
|
|
35502
36580
|
}
|
|
35503
36581
|
}
|
|
35504
36582
|
function saveVectorStore(projectRoot, store) {
|
|
35505
|
-
|
|
36583
|
+
writeFileSync27(vectorStorePath(projectRoot), JSON.stringify(store));
|
|
35506
36584
|
}
|
|
35507
36585
|
function cosineSimilarity(a, b) {
|
|
35508
36586
|
let dot = 0, normA = 0, normB = 0;
|
|
@@ -35558,20 +36636,20 @@ async function embedSymbols(index) {
|
|
|
35558
36636
|
return store;
|
|
35559
36637
|
}
|
|
35560
36638
|
function normalizeProjectRoot(projectRoot) {
|
|
35561
|
-
return
|
|
36639
|
+
return path53.resolve(projectRoot || process.cwd());
|
|
35562
36640
|
}
|
|
35563
36641
|
function hashText(text) {
|
|
35564
36642
|
return crypto14.createHash("sha256").update(text).digest("hex");
|
|
35565
36643
|
}
|
|
35566
36644
|
function indexDir() {
|
|
35567
|
-
const dir =
|
|
35568
|
-
|
|
36645
|
+
const dir = path53.join(EXE_AI_DIR, "code-context");
|
|
36646
|
+
mkdirSync26(dir, { recursive: true });
|
|
35569
36647
|
return dir;
|
|
35570
36648
|
}
|
|
35571
36649
|
function getCodeContextIndexPath(projectRoot) {
|
|
35572
36650
|
const root = normalizeProjectRoot(projectRoot);
|
|
35573
36651
|
const rootHash = hashText(root).slice(0, 16);
|
|
35574
|
-
return
|
|
36652
|
+
return path53.join(indexDir(), `${rootHash}.json`);
|
|
35575
36653
|
}
|
|
35576
36654
|
function currentBranch(projectRoot) {
|
|
35577
36655
|
const result = spawnSync3("git", ["branch", "--show-current"], { cwd: projectRoot, encoding: "utf8", timeout: 2e3 });
|
|
@@ -35583,9 +36661,9 @@ function shouldIgnore(relPath) {
|
|
|
35583
36661
|
return parts.some((part) => IGNORE_SEGMENTS.has(part));
|
|
35584
36662
|
}
|
|
35585
36663
|
function listRecursive(projectRoot, dir = projectRoot, out = []) {
|
|
35586
|
-
for (const entry of
|
|
35587
|
-
const abs =
|
|
35588
|
-
const rel =
|
|
36664
|
+
for (const entry of readdirSync12(dir, { withFileTypes: true })) {
|
|
36665
|
+
const abs = path53.join(dir, entry.name);
|
|
36666
|
+
const rel = path53.relative(projectRoot, abs).replaceAll(path53.sep, "/");
|
|
35589
36667
|
if (shouldIgnore(rel)) continue;
|
|
35590
36668
|
if (entry.isDirectory()) listRecursive(projectRoot, abs, out);
|
|
35591
36669
|
else if (entry.isFile()) out.push(rel);
|
|
@@ -35601,7 +36679,7 @@ function listCodeFiles(projectRoot, maxFiles) {
|
|
|
35601
36679
|
const rg = spawnSync3("rg", ["--files"], { cwd: projectRoot, encoding: "utf8", timeout: 5e3, maxBuffer: 1024 * 1024 * 16 });
|
|
35602
36680
|
files = rg.status === 0 && rg.stdout.trim() ? rg.stdout.split("\n").map((s) => s.trim()).filter(Boolean) : listRecursive(projectRoot);
|
|
35603
36681
|
}
|
|
35604
|
-
return files.map((file) => file.replaceAll(
|
|
36682
|
+
return files.map((file) => file.replaceAll(path53.sep, "/")).filter((file) => isChunkable(file) && !shouldIgnore(file)).slice(0, maxFiles).sort();
|
|
35605
36683
|
}
|
|
35606
36684
|
function parseImportPaths(importText) {
|
|
35607
36685
|
const paths = [];
|
|
@@ -35614,13 +36692,13 @@ function parseImportPaths(importText) {
|
|
|
35614
36692
|
}
|
|
35615
36693
|
function resolveImport(fromFile, importPath, allFiles) {
|
|
35616
36694
|
if (!importPath.startsWith(".")) return null;
|
|
35617
|
-
const base =
|
|
36695
|
+
const base = path53.posix.normalize(path53.posix.join(path53.posix.dirname(fromFile.replaceAll(path53.sep, "/")), importPath));
|
|
35618
36696
|
const withoutKnownExt = base.replace(/\.(?:[a-z0-9]+)$/i, "");
|
|
35619
36697
|
const candidates = [base];
|
|
35620
36698
|
for (const ext of ["ts", "tsx", "js", "jsx", "py", "rs", "go", "java", "cs", "cpp", "c", "rb", "php", "swift", "kt", "scala", "sql", "md", "json", "yaml", "yml"]) {
|
|
35621
36699
|
candidates.push(`${withoutKnownExt}.${ext}`, `${base}.${ext}`);
|
|
35622
36700
|
}
|
|
35623
|
-
for (const indexName of ["index.ts", "index.tsx", "index.js", "mod.rs", "__init__.py"]) candidates.push(
|
|
36701
|
+
for (const indexName of ["index.ts", "index.tsx", "index.js", "mod.rs", "__init__.py"]) candidates.push(path53.posix.join(base, indexName));
|
|
35624
36702
|
return candidates.find((candidate) => allFiles.has(candidate)) ?? null;
|
|
35625
36703
|
}
|
|
35626
36704
|
function symbolId(filePath, chunk) {
|
|
@@ -35628,9 +36706,9 @@ function symbolId(filePath, chunk) {
|
|
|
35628
36706
|
}
|
|
35629
36707
|
function loadIndex(projectRoot) {
|
|
35630
36708
|
const file = getCodeContextIndexPath(projectRoot);
|
|
35631
|
-
if (!
|
|
36709
|
+
if (!existsSync38(file)) return null;
|
|
35632
36710
|
try {
|
|
35633
|
-
const parsed = JSON.parse(
|
|
36711
|
+
const parsed = JSON.parse(readFileSync34(file, "utf8"));
|
|
35634
36712
|
if (parsed.version !== INDEX_VERSION || parsed.projectRoot !== projectRoot) return null;
|
|
35635
36713
|
return parsed;
|
|
35636
36714
|
} catch {
|
|
@@ -35638,20 +36716,20 @@ function loadIndex(projectRoot) {
|
|
|
35638
36716
|
}
|
|
35639
36717
|
}
|
|
35640
36718
|
function saveIndex(index) {
|
|
35641
|
-
|
|
36719
|
+
writeFileSync27(getCodeContextIndexPath(index.projectRoot), JSON.stringify(index, null, 2));
|
|
35642
36720
|
}
|
|
35643
36721
|
function buildFileRecord(projectRoot, relPath, allFiles, previous) {
|
|
35644
|
-
const absPath =
|
|
36722
|
+
const absPath = path53.join(projectRoot, relPath);
|
|
35645
36723
|
let stat2;
|
|
35646
36724
|
try {
|
|
35647
|
-
stat2 =
|
|
36725
|
+
stat2 = statSync8(absPath);
|
|
35648
36726
|
} catch {
|
|
35649
36727
|
return { record: null, reused: false };
|
|
35650
36728
|
}
|
|
35651
36729
|
if (!stat2.isFile()) return { record: null, reused: false };
|
|
35652
36730
|
const language = languageForFile(relPath);
|
|
35653
36731
|
if (!language || !isChunkable(relPath)) return { record: null, reused: false };
|
|
35654
|
-
const source =
|
|
36732
|
+
const source = readFileSync34(absPath, "utf8");
|
|
35655
36733
|
const hash = hashText(source);
|
|
35656
36734
|
if (previous && previous.hash === hash && previous.mtimeMs === stat2.mtimeMs && previous.size === stat2.size && previous.language === language) {
|
|
35657
36735
|
return { record: previous, reused: true };
|
|
@@ -35679,13 +36757,13 @@ function buildCodeContextIndex(options = {}) {
|
|
|
35679
36757
|
const branch = currentBranch(projectRoot);
|
|
35680
36758
|
const previous = options.force ? null : loadIndex(projectRoot);
|
|
35681
36759
|
const files = listCodeFiles(projectRoot, maxFiles);
|
|
35682
|
-
const allFiles = new Set(files.map((file) => file.replaceAll(
|
|
36760
|
+
const allFiles = new Set(files.map((file) => file.replaceAll(path53.sep, "/")));
|
|
35683
36761
|
const fileRecords = {};
|
|
35684
36762
|
let rebuiltFiles = 0;
|
|
35685
36763
|
let reusedFiles = 0;
|
|
35686
36764
|
let skippedFiles = 0;
|
|
35687
36765
|
for (const rel of files) {
|
|
35688
|
-
const normalized = rel.replaceAll(
|
|
36766
|
+
const normalized = rel.replaceAll(path53.sep, "/");
|
|
35689
36767
|
const { record, reused } = buildFileRecord(projectRoot, normalized, allFiles, previous?.files[normalized]);
|
|
35690
36768
|
if (record) {
|
|
35691
36769
|
fileRecords[normalized] = record;
|
|
@@ -35714,11 +36792,11 @@ function loadOrBuildCodeContextIndex(options = {}) {
|
|
|
35714
36792
|
if (loaded) {
|
|
35715
36793
|
const currentFiles = listCodeFiles(projectRoot, options.maxFiles ?? DEFAULT_MAX_FILES);
|
|
35716
36794
|
const unchanged = currentFiles.every((rel) => {
|
|
35717
|
-
const normalized = rel.replaceAll(
|
|
36795
|
+
const normalized = rel.replaceAll(path53.sep, "/");
|
|
35718
36796
|
const existing = loaded.files[normalized];
|
|
35719
36797
|
if (!existing) return false;
|
|
35720
36798
|
try {
|
|
35721
|
-
const stat2 =
|
|
36799
|
+
const stat2 = statSync8(path53.join(projectRoot, normalized));
|
|
35722
36800
|
return stat2.mtimeMs === existing.mtimeMs && stat2.size === existing.size;
|
|
35723
36801
|
} catch {
|
|
35724
36802
|
return false;
|
|
@@ -35771,9 +36849,9 @@ function globToRegex(pattern) {
|
|
|
35771
36849
|
}
|
|
35772
36850
|
function matchesPath(filePath, patterns) {
|
|
35773
36851
|
if (!patterns || patterns.length === 0) return true;
|
|
35774
|
-
const normalized = filePath.replaceAll(
|
|
36852
|
+
const normalized = filePath.replaceAll(path53.sep, "/");
|
|
35775
36853
|
return patterns.some((pattern) => {
|
|
35776
|
-
const p = pattern.replaceAll(
|
|
36854
|
+
const p = pattern.replaceAll(path53.sep, "/").replace(/^\.\//, "");
|
|
35777
36855
|
return normalized === p || normalized.startsWith(`${p}/`) || normalized.endsWith(p) || globToRegex(p).test(normalized);
|
|
35778
36856
|
});
|
|
35779
36857
|
}
|
|
@@ -35966,7 +37044,7 @@ function traceCodeSymbol(symbolName, options = {}) {
|
|
|
35966
37044
|
}
|
|
35967
37045
|
function resolveTargetFile(index, input) {
|
|
35968
37046
|
if (input.filePath) {
|
|
35969
|
-
const normalized = input.filePath.replaceAll(
|
|
37047
|
+
const normalized = input.filePath.replaceAll(path53.sep, "/").replace(/^\.\//, "");
|
|
35970
37048
|
if (index.files[normalized]) return { filePath: normalized, target: normalized };
|
|
35971
37049
|
const suffix = Object.keys(index.files).find((file) => file.endsWith(normalized));
|
|
35972
37050
|
if (suffix) return { filePath: suffix, target: input.filePath };
|
|
@@ -35996,7 +37074,7 @@ function analyzeBlastRadius(input) {
|
|
|
35996
37074
|
}
|
|
35997
37075
|
}
|
|
35998
37076
|
}
|
|
35999
|
-
const targetBase =
|
|
37077
|
+
const targetBase = path53.basename(target.filePath).replace(/\.[^.]+$/, "").toLowerCase();
|
|
36000
37078
|
const symbolLower = input.symbol?.toLowerCase();
|
|
36001
37079
|
const tests = Object.keys(index.files).filter((file) => {
|
|
36002
37080
|
const lower = file.toLowerCase();
|
|
@@ -36233,16 +37311,16 @@ __export(installer_exports2, {
|
|
|
36233
37311
|
verifyOpenCodeHooks: () => verifyOpenCodeHooks
|
|
36234
37312
|
});
|
|
36235
37313
|
import { readFile as readFile7, writeFile as writeFile8, mkdir as mkdir8 } from "fs/promises";
|
|
36236
|
-
import { existsSync as
|
|
36237
|
-
import
|
|
37314
|
+
import { existsSync as existsSync39, readFileSync as readFileSync35 } from "fs";
|
|
37315
|
+
import path54 from "path";
|
|
36238
37316
|
import os22 from "os";
|
|
36239
37317
|
async function registerOpenCodeMcp(packageRoot, homeDir = os22.homedir()) {
|
|
36240
37318
|
void packageRoot;
|
|
36241
|
-
const configDir =
|
|
36242
|
-
const configPath =
|
|
37319
|
+
const configDir = path54.join(homeDir, ".config", "opencode");
|
|
37320
|
+
const configPath = path54.join(configDir, "opencode.json");
|
|
36243
37321
|
await mkdir8(configDir, { recursive: true });
|
|
36244
37322
|
let config = {};
|
|
36245
|
-
if (
|
|
37323
|
+
if (existsSync39(configPath)) {
|
|
36246
37324
|
try {
|
|
36247
37325
|
config = JSON.parse(await readFile7(configPath, "utf-8"));
|
|
36248
37326
|
} catch {
|
|
@@ -36270,14 +37348,14 @@ async function registerOpenCodeMcp(packageRoot, homeDir = os22.homedir()) {
|
|
|
36270
37348
|
return true;
|
|
36271
37349
|
}
|
|
36272
37350
|
async function installOpenCodePlugin(packageRoot, homeDir = os22.homedir()) {
|
|
36273
|
-
const pluginDir =
|
|
36274
|
-
const pluginPath =
|
|
37351
|
+
const pluginDir = path54.join(homeDir, ".config", "opencode", "plugins");
|
|
37352
|
+
const pluginPath = path54.join(pluginDir, "exe-os.mjs");
|
|
36275
37353
|
await mkdir8(pluginDir, { recursive: true });
|
|
36276
37354
|
const pluginContent = PLUGIN_TEMPLATE.replace(
|
|
36277
37355
|
/__PACKAGE_ROOT__/g,
|
|
36278
37356
|
packageRoot.replace(/\\/g, "\\\\")
|
|
36279
37357
|
);
|
|
36280
|
-
if (
|
|
37358
|
+
if (existsSync39(pluginPath)) {
|
|
36281
37359
|
const existing = await readFile7(pluginPath, "utf-8");
|
|
36282
37360
|
if (existing === pluginContent) {
|
|
36283
37361
|
return false;
|
|
@@ -36287,18 +37365,18 @@ async function installOpenCodePlugin(packageRoot, homeDir = os22.homedir()) {
|
|
|
36287
37365
|
return true;
|
|
36288
37366
|
}
|
|
36289
37367
|
function verifyOpenCodeHooks(homeDir = os22.homedir()) {
|
|
36290
|
-
const configPath =
|
|
36291
|
-
const pluginPath =
|
|
36292
|
-
if (!
|
|
37368
|
+
const configPath = path54.join(homeDir, ".config", "opencode", "opencode.json");
|
|
37369
|
+
const pluginPath = path54.join(homeDir, ".config", "opencode", "plugins", "exe-os.mjs");
|
|
37370
|
+
if (!existsSync39(configPath)) return false;
|
|
36293
37371
|
try {
|
|
36294
|
-
const config = JSON.parse(
|
|
37372
|
+
const config = JSON.parse(readFileSync35(configPath, "utf-8"));
|
|
36295
37373
|
if (!config.mcp?.["exe-os"]?.enabled) return false;
|
|
36296
37374
|
} catch {
|
|
36297
37375
|
return false;
|
|
36298
37376
|
}
|
|
36299
|
-
if (!
|
|
37377
|
+
if (!existsSync39(pluginPath)) return false;
|
|
36300
37378
|
try {
|
|
36301
|
-
const plugin =
|
|
37379
|
+
const plugin = readFileSync35(pluginPath, "utf-8");
|
|
36302
37380
|
if (!plugin.includes(EXE_HOOK_FILES.postToolCombined)) return false;
|
|
36303
37381
|
if (textHasLegacySplitPostToolHook(plugin)) return false;
|
|
36304
37382
|
} catch {
|
|
@@ -36340,19 +37418,19 @@ __export(installer_exports3, {
|
|
|
36340
37418
|
verifyCodexHooks: () => verifyCodexHooks
|
|
36341
37419
|
});
|
|
36342
37420
|
import { readFile as readFile8, writeFile as writeFile9, mkdir as mkdir9 } from "fs/promises";
|
|
36343
|
-
import { existsSync as
|
|
36344
|
-
import
|
|
37421
|
+
import { existsSync as existsSync40, readFileSync as readFileSync36 } from "fs";
|
|
37422
|
+
import path55 from "path";
|
|
36345
37423
|
import os23 from "os";
|
|
36346
37424
|
async function mergeCodexHooks(packageRoot, homeDir = os23.homedir()) {
|
|
36347
|
-
const codexDir =
|
|
36348
|
-
const hooksPath =
|
|
36349
|
-
const logsDir =
|
|
36350
|
-
const hookLogPath =
|
|
37425
|
+
const codexDir = path55.join(homeDir, ".codex");
|
|
37426
|
+
const hooksPath = path55.join(codexDir, "hooks.json");
|
|
37427
|
+
const logsDir = path55.join(homeDir, ".exe-os", "logs");
|
|
37428
|
+
const hookLogPath = path55.join(logsDir, "hooks.log");
|
|
36351
37429
|
const logSuffix = ` 2>> "${hookLogPath}"`;
|
|
36352
37430
|
await mkdir9(codexDir, { recursive: true });
|
|
36353
37431
|
await mkdir9(logsDir, { recursive: true });
|
|
36354
37432
|
let hooksJson = {};
|
|
36355
|
-
if (
|
|
37433
|
+
if (existsSync40(hooksPath)) {
|
|
36356
37434
|
try {
|
|
36357
37435
|
hooksJson = JSON.parse(await readFile8(hooksPath, "utf-8"));
|
|
36358
37436
|
} catch {
|
|
@@ -36372,7 +37450,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os23.homedir()) {
|
|
|
36372
37450
|
// Combined hook: runs ingest + error-recall in one Node process.
|
|
36373
37451
|
// Eliminates a cold-start cycle per tool call (~3-6s savings on Codex).
|
|
36374
37452
|
type: "command",
|
|
36375
|
-
command: `node "${
|
|
37453
|
+
command: `node "${path55.join(packageRoot, "dist", "hooks", "post-tool-combined.js")}"${logSuffix}`
|
|
36376
37454
|
}
|
|
36377
37455
|
]
|
|
36378
37456
|
},
|
|
@@ -36386,7 +37464,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os23.homedir()) {
|
|
|
36386
37464
|
// Single hook: prompt-submit handles memory retrieval + entity boost.
|
|
36387
37465
|
// exe-heartbeat-hook is CC-specific (intercom) — omitted on Codex.
|
|
36388
37466
|
type: "command",
|
|
36389
|
-
command: `node "${
|
|
37467
|
+
command: `node "${path55.join(packageRoot, "dist", "hooks", "prompt-submit.js")}"${logSuffix}`
|
|
36390
37468
|
}
|
|
36391
37469
|
]
|
|
36392
37470
|
},
|
|
@@ -36398,7 +37476,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os23.homedir()) {
|
|
|
36398
37476
|
hooks: [
|
|
36399
37477
|
{
|
|
36400
37478
|
type: "command",
|
|
36401
|
-
command: `node "${
|
|
37479
|
+
command: `node "${path55.join(packageRoot, "dist", "hooks", "stop.js")}"${logSuffix}`
|
|
36402
37480
|
}
|
|
36403
37481
|
]
|
|
36404
37482
|
},
|
|
@@ -36411,7 +37489,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os23.homedir()) {
|
|
|
36411
37489
|
hooks: [
|
|
36412
37490
|
{
|
|
36413
37491
|
type: "command",
|
|
36414
|
-
command: `node "${
|
|
37492
|
+
command: `node "${path55.join(packageRoot, "dist", "hooks", "pre-tool-use.js")}"${logSuffix}`
|
|
36415
37493
|
}
|
|
36416
37494
|
]
|
|
36417
37495
|
},
|
|
@@ -36460,10 +37538,10 @@ async function mergeCodexHooks(packageRoot, homeDir = os23.homedir()) {
|
|
|
36460
37538
|
return { added, skipped };
|
|
36461
37539
|
}
|
|
36462
37540
|
function verifyCodexHooks(homeDir = os23.homedir()) {
|
|
36463
|
-
const hooksPath =
|
|
36464
|
-
if (!
|
|
37541
|
+
const hooksPath = path55.join(homeDir, ".codex", "hooks.json");
|
|
37542
|
+
if (!existsSync40(hooksPath)) return false;
|
|
36465
37543
|
try {
|
|
36466
|
-
const hooksJson = JSON.parse(
|
|
37544
|
+
const hooksJson = JSON.parse(readFileSync36(hooksPath, "utf-8"));
|
|
36467
37545
|
if (!hooksJson.hooks) return false;
|
|
36468
37546
|
const required = ["PostToolUse", "UserPromptSubmit", "Stop", "PreToolUse"];
|
|
36469
37547
|
for (const event of required) {
|
|
@@ -36495,11 +37573,11 @@ function verifyCodexHooks(homeDir = os23.homedir()) {
|
|
|
36495
37573
|
async function installCodexStatusLine(homeDir = os23.homedir()) {
|
|
36496
37574
|
const prefs = loadPreferences(homeDir);
|
|
36497
37575
|
if (prefs.codexStatusLine === false) return "opted-out";
|
|
36498
|
-
const codexDir =
|
|
36499
|
-
const configPath =
|
|
37576
|
+
const codexDir = path55.join(homeDir, ".codex");
|
|
37577
|
+
const configPath = path55.join(codexDir, "config.toml");
|
|
36500
37578
|
await mkdir9(codexDir, { recursive: true });
|
|
36501
37579
|
let content = "";
|
|
36502
|
-
if (
|
|
37580
|
+
if (existsSync40(configPath)) {
|
|
36503
37581
|
content = await readFile8(configPath, "utf-8");
|
|
36504
37582
|
if (/\[tui\][\s\S]*?status_line\s*=/.test(content)) {
|
|
36505
37583
|
return "already-configured";
|
|
@@ -36551,12 +37629,12 @@ ${line}
|
|
|
36551
37629
|
return { content: next, changed: next !== sectionContent };
|
|
36552
37630
|
}
|
|
36553
37631
|
async function registerCodexMcpServer(packageRoot, homeDir = os23.homedir()) {
|
|
36554
|
-
const codexDir =
|
|
36555
|
-
const configPath =
|
|
37632
|
+
const codexDir = path55.join(homeDir, ".codex");
|
|
37633
|
+
const configPath = path55.join(codexDir, "config.toml");
|
|
36556
37634
|
void packageRoot;
|
|
36557
37635
|
await mkdir9(codexDir, { recursive: true });
|
|
36558
37636
|
let content = "";
|
|
36559
|
-
if (
|
|
37637
|
+
if (existsSync40(configPath)) {
|
|
36560
37638
|
content = await readFile8(configPath, "utf-8");
|
|
36561
37639
|
}
|
|
36562
37640
|
const sectionHeader = "[mcp_servers.exe-os]";
|
|
@@ -36581,10 +37659,10 @@ async function registerCodexMcpServer(packageRoot, homeDir = os23.homedir()) {
|
|
|
36581
37659
|
return "registered";
|
|
36582
37660
|
}
|
|
36583
37661
|
async function ensureCodexHooksFeature(homeDir = os23.homedir()) {
|
|
36584
|
-
const configPath =
|
|
36585
|
-
await mkdir9(
|
|
37662
|
+
const configPath = path55.join(homeDir, ".codex", "config.toml");
|
|
37663
|
+
await mkdir9(path55.join(homeDir, ".codex"), { recursive: true });
|
|
36586
37664
|
let content = "";
|
|
36587
|
-
if (
|
|
37665
|
+
if (existsSync40(configPath)) {
|
|
36588
37666
|
content = await readFile8(configPath, "utf-8");
|
|
36589
37667
|
}
|
|
36590
37668
|
if (/\[features\][\s\S]*?codex_hooks\s*=\s*true/.test(content)) {
|
|
@@ -36659,32 +37737,32 @@ __export(mcp_diagnostics_exports, {
|
|
|
36659
37737
|
diagnoseClaudeMcpConfig: () => diagnoseClaudeMcpConfig,
|
|
36660
37738
|
formatMcpDiagnosticReport: () => formatMcpDiagnosticReport
|
|
36661
37739
|
});
|
|
36662
|
-
import { existsSync as
|
|
36663
|
-
import
|
|
37740
|
+
import { existsSync as existsSync41, readFileSync as readFileSync37 } from "fs";
|
|
37741
|
+
import path56 from "path";
|
|
36664
37742
|
import os24 from "os";
|
|
36665
37743
|
function readJson(filePath) {
|
|
36666
|
-
if (!
|
|
37744
|
+
if (!existsSync41(filePath)) return null;
|
|
36667
37745
|
try {
|
|
36668
|
-
return JSON.parse(
|
|
37746
|
+
return JSON.parse(readFileSync37(filePath, "utf8"));
|
|
36669
37747
|
} catch {
|
|
36670
37748
|
return null;
|
|
36671
37749
|
}
|
|
36672
37750
|
}
|
|
36673
37751
|
function pathApplies2(projectPath, cwd2) {
|
|
36674
|
-
const project =
|
|
36675
|
-
const current =
|
|
36676
|
-
return current === project || current.startsWith(project +
|
|
37752
|
+
const project = path56.resolve(projectPath);
|
|
37753
|
+
const current = path56.resolve(cwd2);
|
|
37754
|
+
return current === project || current.startsWith(project + path56.sep);
|
|
36677
37755
|
}
|
|
36678
37756
|
function findAncestorMcpJsons2(cwd2, homeDir) {
|
|
36679
37757
|
const files = [];
|
|
36680
|
-
let dir =
|
|
36681
|
-
const root =
|
|
36682
|
-
const stop =
|
|
37758
|
+
let dir = path56.resolve(cwd2);
|
|
37759
|
+
const root = path56.parse(dir).root;
|
|
37760
|
+
const stop = path56.resolve(homeDir);
|
|
36683
37761
|
while (dir !== root) {
|
|
36684
|
-
const candidate =
|
|
36685
|
-
if (
|
|
37762
|
+
const candidate = path56.join(dir, ".mcp.json");
|
|
37763
|
+
if (existsSync41(candidate)) files.push(candidate);
|
|
36686
37764
|
if (dir === stop) break;
|
|
36687
|
-
dir =
|
|
37765
|
+
dir = path56.dirname(dir);
|
|
36688
37766
|
}
|
|
36689
37767
|
return files;
|
|
36690
37768
|
}
|
|
@@ -36701,8 +37779,8 @@ function serverAllowedByPermissions(serverName, prefixes) {
|
|
|
36701
37779
|
return prefixes.has(serverName) || prefixes.has(serverName.replace(/-/g, "_"));
|
|
36702
37780
|
}
|
|
36703
37781
|
function diagnoseClaudeMcpConfig(homeDir = os24.homedir(), cwd2 = process.cwd(), env = process.env) {
|
|
36704
|
-
const claudeJsonPath =
|
|
36705
|
-
const settingsPath =
|
|
37782
|
+
const claudeJsonPath = path56.join(homeDir, ".claude.json");
|
|
37783
|
+
const settingsPath = path56.join(homeDir, ".claude", "settings.json");
|
|
36706
37784
|
const claudeJson = readJson(claudeJsonPath);
|
|
36707
37785
|
const settings = readJson(settingsPath);
|
|
36708
37786
|
const issues = [];
|
|
@@ -36822,7 +37900,7 @@ function diagnoseClaudeMcpConfig(homeDir = os24.homedir(), cwd2 = process.cwd(),
|
|
|
36822
37900
|
});
|
|
36823
37901
|
}
|
|
36824
37902
|
return {
|
|
36825
|
-
cwd:
|
|
37903
|
+
cwd: path56.resolve(cwd2),
|
|
36826
37904
|
globalServers,
|
|
36827
37905
|
projectServers: projectServers.sort((a, b) => a.name.localeCompare(b.name)),
|
|
36828
37906
|
mcpJsonServers: mcpJsonServers.sort((a, b) => a.name.localeCompare(b.name)),
|
|
@@ -36859,14 +37937,14 @@ var init_mcp_diagnostics = __esm({
|
|
|
36859
37937
|
});
|
|
36860
37938
|
|
|
36861
37939
|
// src/bin/cli.ts
|
|
36862
|
-
import { existsSync as
|
|
36863
|
-
import
|
|
37940
|
+
import { existsSync as existsSync42, readFileSync as readFileSync38, writeFileSync as writeFileSync28, readdirSync as readdirSync13, rmSync as rmSync2 } from "fs";
|
|
37941
|
+
import path57 from "path";
|
|
36864
37942
|
import os25 from "os";
|
|
36865
37943
|
var args = process.argv.slice(2);
|
|
36866
37944
|
if (args.includes("--version") || args.includes("-v")) {
|
|
36867
37945
|
try {
|
|
36868
|
-
const pkgPath =
|
|
36869
|
-
const pkg = JSON.parse(
|
|
37946
|
+
const pkgPath = path57.join(path57.dirname(new URL(import.meta.url).pathname), "..", "..", "package.json");
|
|
37947
|
+
const pkg = JSON.parse(readFileSync38(pkgPath, "utf8"));
|
|
36870
37948
|
console.log(pkg.version);
|
|
36871
37949
|
} catch {
|
|
36872
37950
|
console.log("unknown");
|
|
@@ -37077,6 +38155,23 @@ ID: ${result.id}`);
|
|
|
37077
38155
|
await runRename();
|
|
37078
38156
|
} else if (args[0] === "--activate" || args[0] === "activate") {
|
|
37079
38157
|
await runActivate(args[1]);
|
|
38158
|
+
} else if (args[0] === "doctor") {
|
|
38159
|
+
if (args.includes("--drift")) {
|
|
38160
|
+
const { runDrift: runDrift2 } = await Promise.resolve().then(() => (init_exe_drift(), exe_drift_exports));
|
|
38161
|
+
await runDrift2(args.slice(1));
|
|
38162
|
+
} else {
|
|
38163
|
+
const { runCcDoctor: runCcDoctor2 } = await Promise.resolve().then(() => (init_cc_doctor(), cc_doctor_exports));
|
|
38164
|
+
const { results, failed } = runCcDoctor2();
|
|
38165
|
+
console.log("\n exe-os doctor\n");
|
|
38166
|
+
for (const r of results) {
|
|
38167
|
+
const icon = r.pass ? "\x1B[32m\u2713\x1B[0m" : "\x1B[31m\u2717\x1B[0m";
|
|
38168
|
+
console.log(` ${icon} ${r.name}: ${r.detail}`);
|
|
38169
|
+
}
|
|
38170
|
+
console.log(failed === 0 ? "\n \x1B[32mAll checks passed\x1B[0m\n" : `
|
|
38171
|
+
\x1B[31m${failed} issue(s) found\x1B[0m
|
|
38172
|
+
`);
|
|
38173
|
+
process.exit(failed > 0 ? 1 : 0);
|
|
38174
|
+
}
|
|
37080
38175
|
} else if (args[0] === "setup" || args[0] === "-setup" || args[0] === "--setup") {
|
|
37081
38176
|
const { runSetupWizard: runSetupWizard2 } = await Promise.resolve().then(() => (init_setup_wizard(), setup_wizard_exports));
|
|
37082
38177
|
await runSetupWizard2({ skipModel: args.includes("--skip-model") });
|
|
@@ -37094,11 +38189,11 @@ ID: ${result.id}`);
|
|
|
37094
38189
|
});
|
|
37095
38190
|
await init_App2().then(() => App_exports);
|
|
37096
38191
|
} else {
|
|
37097
|
-
const claudeDir =
|
|
37098
|
-
const settingsPath =
|
|
37099
|
-
const hasClaudeCode =
|
|
38192
|
+
const claudeDir = path57.join(os25.homedir(), ".claude");
|
|
38193
|
+
const settingsPath = path57.join(claudeDir, "settings.json");
|
|
38194
|
+
const hasClaudeCode = existsSync42(settingsPath) && (() => {
|
|
37100
38195
|
try {
|
|
37101
|
-
const raw =
|
|
38196
|
+
const raw = readFileSync38(settingsPath, "utf8");
|
|
37102
38197
|
return raw.includes("exe-os") || raw.includes("exe-mem");
|
|
37103
38198
|
} catch {
|
|
37104
38199
|
return false;
|
|
@@ -37108,9 +38203,9 @@ ID: ${result.id}`);
|
|
|
37108
38203
|
const { DEFAULT_COORDINATOR_TEMPLATE_NAME: DEFAULT_COORDINATOR_TEMPLATE_NAME2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
37109
38204
|
let cooName = DEFAULT_COORDINATOR_TEMPLATE_NAME2;
|
|
37110
38205
|
try {
|
|
37111
|
-
const rosterPath =
|
|
37112
|
-
if (
|
|
37113
|
-
const roster = JSON.parse(
|
|
38206
|
+
const rosterPath = path57.join(os25.homedir(), ".exe-os", "exe-employees.json");
|
|
38207
|
+
if (existsSync42(rosterPath)) {
|
|
38208
|
+
const roster = JSON.parse(readFileSync38(rosterPath, "utf8"));
|
|
37114
38209
|
const coo = roster.find((e) => e.role === "COO");
|
|
37115
38210
|
if (coo) cooName = coo.name;
|
|
37116
38211
|
}
|
|
@@ -37263,14 +38358,14 @@ async function runCodexInstall() {
|
|
|
37263
38358
|
}
|
|
37264
38359
|
async function runClaudeCheck() {
|
|
37265
38360
|
const { detectMcpNameCollisions: detectMcpNameCollisions2 } = await Promise.resolve().then(() => (init_installer(), installer_exports));
|
|
37266
|
-
const claudeDir =
|
|
37267
|
-
const settingsPath =
|
|
37268
|
-
const claudeJsonPath =
|
|
38361
|
+
const claudeDir = path57.join(os25.homedir(), ".claude");
|
|
38362
|
+
const settingsPath = path57.join(claudeDir, "settings.json");
|
|
38363
|
+
const claudeJsonPath = path57.join(os25.homedir(), ".claude.json");
|
|
37269
38364
|
let ok = true;
|
|
37270
|
-
if (
|
|
38365
|
+
if (existsSync42(settingsPath)) {
|
|
37271
38366
|
let settings;
|
|
37272
38367
|
try {
|
|
37273
|
-
settings = JSON.parse(
|
|
38368
|
+
settings = JSON.parse(readFileSync38(settingsPath, "utf8"));
|
|
37274
38369
|
} catch {
|
|
37275
38370
|
console.log("\x1B[31m\u2717\x1B[0m settings.json is malformed (invalid JSON)");
|
|
37276
38371
|
ok = false;
|
|
@@ -37296,10 +38391,10 @@ async function runClaudeCheck() {
|
|
|
37296
38391
|
console.log("\x1B[31m\u2717\x1B[0m settings.json not found");
|
|
37297
38392
|
ok = false;
|
|
37298
38393
|
}
|
|
37299
|
-
if (
|
|
38394
|
+
if (existsSync42(claudeJsonPath)) {
|
|
37300
38395
|
let claudeJson;
|
|
37301
38396
|
try {
|
|
37302
|
-
claudeJson = JSON.parse(
|
|
38397
|
+
claudeJson = JSON.parse(readFileSync38(claudeJsonPath, "utf8"));
|
|
37303
38398
|
} catch {
|
|
37304
38399
|
console.log("\x1B[31m\u2717\x1B[0m claude.json is malformed (invalid JSON)");
|
|
37305
38400
|
ok = false;
|
|
@@ -37332,8 +38427,8 @@ async function runClaudeCheck() {
|
|
|
37332
38427
|
} else {
|
|
37333
38428
|
console.log("\x1B[32m\u2713\x1B[0m No .mcp.json/project MCP name collisions detected");
|
|
37334
38429
|
}
|
|
37335
|
-
const skillsDir =
|
|
37336
|
-
if (
|
|
38430
|
+
const skillsDir = path57.join(claudeDir, "skills");
|
|
38431
|
+
if (existsSync42(skillsDir)) {
|
|
37337
38432
|
console.log("\x1B[32m\u2713\x1B[0m Slash skills directory exists");
|
|
37338
38433
|
} else {
|
|
37339
38434
|
console.log("\x1B[31m\u2717\x1B[0m Slash skills directory missing");
|
|
@@ -37357,16 +38452,16 @@ async function runClaudeUninstall(flags = []) {
|
|
|
37357
38452
|
const dryRun = flags.includes("--dry-run");
|
|
37358
38453
|
const purge = flags.includes("--purge");
|
|
37359
38454
|
const homeDir = os25.homedir();
|
|
37360
|
-
const claudeDir =
|
|
37361
|
-
const settingsPath =
|
|
37362
|
-
const claudeJsonPath =
|
|
37363
|
-
const exeOsDir =
|
|
38455
|
+
const claudeDir = path57.join(homeDir, ".claude");
|
|
38456
|
+
const settingsPath = path57.join(claudeDir, "settings.json");
|
|
38457
|
+
const claudeJsonPath = path57.join(homeDir, ".claude.json");
|
|
38458
|
+
const exeOsDir = path57.join(homeDir, ".exe-os");
|
|
37364
38459
|
let removed = 0;
|
|
37365
38460
|
const log = (msg) => console.log(dryRun ? `[dry-run] ${msg}` : msg);
|
|
37366
38461
|
let settings = {};
|
|
37367
|
-
if (
|
|
38462
|
+
if (existsSync42(settingsPath)) {
|
|
37368
38463
|
try {
|
|
37369
|
-
settings = JSON.parse(
|
|
38464
|
+
settings = JSON.parse(readFileSync38(settingsPath, "utf8"));
|
|
37370
38465
|
} catch {
|
|
37371
38466
|
console.error("Your ~/.claude/settings.json appears malformed.");
|
|
37372
38467
|
if (purge) {
|
|
@@ -37404,15 +38499,15 @@ async function runClaudeUninstall(flags = []) {
|
|
|
37404
38499
|
permCount = before - settings.permissions.allow.length;
|
|
37405
38500
|
}
|
|
37406
38501
|
if (!dryRun) {
|
|
37407
|
-
|
|
38502
|
+
writeFileSync28(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
37408
38503
|
}
|
|
37409
38504
|
log("\u2713 Removed exe-os hooks from settings.json");
|
|
37410
38505
|
if (permCount > 0) log(`\u2713 Removed ${permCount} MCP permission entries`);
|
|
37411
38506
|
removed++;
|
|
37412
38507
|
}
|
|
37413
38508
|
}
|
|
37414
|
-
if (
|
|
37415
|
-
const raw =
|
|
38509
|
+
if (existsSync42(claudeJsonPath)) {
|
|
38510
|
+
const raw = readFileSync38(claudeJsonPath, "utf8");
|
|
37416
38511
|
if (raw.length > 1e6) {
|
|
37417
38512
|
console.error("claude.json exceeds 1 MB \u2014 skipping parse.");
|
|
37418
38513
|
} else {
|
|
@@ -37433,7 +38528,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
37433
38528
|
}
|
|
37434
38529
|
if (removedMcp) {
|
|
37435
38530
|
if (!dryRun) {
|
|
37436
|
-
|
|
38531
|
+
writeFileSync28(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
37437
38532
|
}
|
|
37438
38533
|
log("\u2713 Removed exe-os MCP server from claude.json");
|
|
37439
38534
|
removed++;
|
|
@@ -37441,14 +38536,14 @@ async function runClaudeUninstall(flags = []) {
|
|
|
37441
38536
|
}
|
|
37442
38537
|
}
|
|
37443
38538
|
}
|
|
37444
|
-
const skillsDir =
|
|
37445
|
-
if (
|
|
38539
|
+
const skillsDir = path57.join(claudeDir, "skills");
|
|
38540
|
+
if (existsSync42(skillsDir)) {
|
|
37446
38541
|
let skillCount = 0;
|
|
37447
38542
|
try {
|
|
37448
|
-
const entries =
|
|
38543
|
+
const entries = readdirSync13(skillsDir);
|
|
37449
38544
|
for (const entry of entries) {
|
|
37450
38545
|
if (entry.startsWith("exe")) {
|
|
37451
|
-
const fullPath =
|
|
38546
|
+
const fullPath = path57.join(skillsDir, entry);
|
|
37452
38547
|
if (!dryRun) rmSync2(fullPath, { recursive: true, force: true });
|
|
37453
38548
|
skillCount++;
|
|
37454
38549
|
}
|
|
@@ -37460,30 +38555,30 @@ async function runClaudeUninstall(flags = []) {
|
|
|
37460
38555
|
removed++;
|
|
37461
38556
|
}
|
|
37462
38557
|
}
|
|
37463
|
-
const claudeMdPath =
|
|
37464
|
-
if (
|
|
37465
|
-
const content =
|
|
38558
|
+
const claudeMdPath = path57.join(claudeDir, "CLAUDE.md");
|
|
38559
|
+
if (existsSync42(claudeMdPath)) {
|
|
38560
|
+
const content = readFileSync38(claudeMdPath, "utf8");
|
|
37466
38561
|
const startMarker = "<!-- exe-os:orchestration-start -->";
|
|
37467
38562
|
const endMarker = "<!-- exe-os:orchestration-end -->";
|
|
37468
38563
|
const startIdx = content.indexOf(startMarker);
|
|
37469
38564
|
const endIdx = content.indexOf(endMarker);
|
|
37470
38565
|
if (startIdx !== -1 && endIdx !== -1) {
|
|
37471
38566
|
const cleaned = (content.slice(0, startIdx) + content.slice(endIdx + endMarker.length)).replace(/\n{3,}/g, "\n\n").trim() + "\n";
|
|
37472
|
-
if (!dryRun)
|
|
38567
|
+
if (!dryRun) writeFileSync28(claudeMdPath, cleaned);
|
|
37473
38568
|
log("\u2713 Removed orchestration block from CLAUDE.md");
|
|
37474
38569
|
removed++;
|
|
37475
38570
|
}
|
|
37476
38571
|
}
|
|
37477
|
-
const agentsDir =
|
|
37478
|
-
if (
|
|
38572
|
+
const agentsDir = path57.join(claudeDir, "agents");
|
|
38573
|
+
if (existsSync42(agentsDir)) {
|
|
37479
38574
|
let agentCount = 0;
|
|
37480
38575
|
try {
|
|
37481
|
-
const entries =
|
|
38576
|
+
const entries = readdirSync13(agentsDir).filter((f) => f.endsWith(".md"));
|
|
37482
38577
|
let knownNames = /* @__PURE__ */ new Set();
|
|
37483
|
-
const rosterPath =
|
|
37484
|
-
if (
|
|
38578
|
+
const rosterPath = path57.join(exeOsDir, "exe-employees.json");
|
|
38579
|
+
if (existsSync42(rosterPath)) {
|
|
37485
38580
|
try {
|
|
37486
|
-
const roster = JSON.parse(
|
|
38581
|
+
const roster = JSON.parse(readFileSync38(rosterPath, "utf8"));
|
|
37487
38582
|
knownNames = new Set(roster.map((e) => e.name));
|
|
37488
38583
|
} catch {
|
|
37489
38584
|
}
|
|
@@ -37491,7 +38586,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
37491
38586
|
for (const entry of entries) {
|
|
37492
38587
|
const name = entry.replace(/\.md$/, "");
|
|
37493
38588
|
if (knownNames.has(name)) {
|
|
37494
|
-
if (!dryRun) rmSync2(
|
|
38589
|
+
if (!dryRun) rmSync2(path57.join(agentsDir, entry), { force: true });
|
|
37495
38590
|
agentCount++;
|
|
37496
38591
|
}
|
|
37497
38592
|
}
|
|
@@ -37502,16 +38597,16 @@ async function runClaudeUninstall(flags = []) {
|
|
|
37502
38597
|
removed++;
|
|
37503
38598
|
}
|
|
37504
38599
|
}
|
|
37505
|
-
const projectsDir =
|
|
37506
|
-
if (
|
|
38600
|
+
const projectsDir = path57.join(claudeDir, "projects");
|
|
38601
|
+
if (existsSync42(projectsDir)) {
|
|
37507
38602
|
let projectCount = 0;
|
|
37508
38603
|
try {
|
|
37509
|
-
const projects =
|
|
38604
|
+
const projects = readdirSync13(projectsDir);
|
|
37510
38605
|
for (const proj of projects) {
|
|
37511
|
-
const projSettings =
|
|
37512
|
-
if (!
|
|
38606
|
+
const projSettings = path57.join(projectsDir, proj, "settings.json");
|
|
38607
|
+
if (!existsSync42(projSettings)) continue;
|
|
37513
38608
|
try {
|
|
37514
|
-
const pSettings = JSON.parse(
|
|
38609
|
+
const pSettings = JSON.parse(readFileSync38(projSettings, "utf8"));
|
|
37515
38610
|
let changed = false;
|
|
37516
38611
|
if (Array.isArray(pSettings.permissions?.allow)) {
|
|
37517
38612
|
const before = pSettings.permissions.allow.length;
|
|
@@ -37521,7 +38616,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
37521
38616
|
if (pSettings.permissions.allow.length < before) changed = true;
|
|
37522
38617
|
}
|
|
37523
38618
|
if (changed && !dryRun) {
|
|
37524
|
-
|
|
38619
|
+
writeFileSync28(projSettings, JSON.stringify(pSettings, null, 2) + "\n");
|
|
37525
38620
|
}
|
|
37526
38621
|
if (changed) projectCount++;
|
|
37527
38622
|
} catch {
|
|
@@ -37535,28 +38630,28 @@ async function runClaudeUninstall(flags = []) {
|
|
|
37535
38630
|
}
|
|
37536
38631
|
}
|
|
37537
38632
|
try {
|
|
37538
|
-
const { execSync:
|
|
38633
|
+
const { execSync: execSync20 } = await import("child_process");
|
|
37539
38634
|
const findExeBin3 = () => {
|
|
37540
38635
|
try {
|
|
37541
|
-
return
|
|
38636
|
+
return execSync20(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
37542
38637
|
} catch {
|
|
37543
38638
|
return null;
|
|
37544
38639
|
}
|
|
37545
38640
|
};
|
|
37546
38641
|
const exeBinPath = findExeBin3();
|
|
37547
38642
|
if (!exeBinPath) throw new Error("exe-os not found in PATH");
|
|
37548
|
-
const binDir =
|
|
38643
|
+
const binDir = path57.dirname(exeBinPath);
|
|
37549
38644
|
let symlinkCount = 0;
|
|
37550
|
-
const rosterPath =
|
|
37551
|
-
if (
|
|
37552
|
-
const roster = JSON.parse(
|
|
38645
|
+
const rosterPath = path57.join(exeOsDir, "exe-employees.json");
|
|
38646
|
+
if (existsSync42(rosterPath)) {
|
|
38647
|
+
const roster = JSON.parse(readFileSync38(rosterPath, "utf8"));
|
|
37553
38648
|
const { DEFAULT_COORDINATOR_TEMPLATE_NAME: DEFAULT_COORDINATOR_TEMPLATE_NAME2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
37554
38649
|
const coordinatorName = roster.find((e) => e.role?.toLowerCase() === "coo")?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME2;
|
|
37555
38650
|
for (const emp of roster) {
|
|
37556
38651
|
if (emp.name === coordinatorName) continue;
|
|
37557
38652
|
for (const suffix of ["", "-opencode"]) {
|
|
37558
|
-
const linkPath =
|
|
37559
|
-
if (
|
|
38653
|
+
const linkPath = path57.join(binDir, `${emp.name}${suffix}`);
|
|
38654
|
+
if (existsSync42(linkPath)) {
|
|
37560
38655
|
if (!dryRun) rmSync2(linkPath, { force: true });
|
|
37561
38656
|
symlinkCount++;
|
|
37562
38657
|
}
|
|
@@ -37569,7 +38664,7 @@ async function runClaudeUninstall(flags = []) {
|
|
|
37569
38664
|
}
|
|
37570
38665
|
} catch {
|
|
37571
38666
|
}
|
|
37572
|
-
if (purge &&
|
|
38667
|
+
if (purge && existsSync42(exeOsDir)) {
|
|
37573
38668
|
if (!dryRun) {
|
|
37574
38669
|
process.stdout.write("\x1B[33m\u26A0 This will delete all memories, identities, and agent data.\x1B[0m\n");
|
|
37575
38670
|
process.stdout.write(" Removing ~/.exe-os...\n");
|
|
@@ -37594,7 +38689,7 @@ async function checkForUpdateOnBoot() {
|
|
|
37594
38689
|
const config = await loadConfig2();
|
|
37595
38690
|
if (!config.autoUpdate.checkOnBoot) return;
|
|
37596
38691
|
const { checkForUpdate: checkForUpdate2 } = await Promise.resolve().then(() => (init_update(), update_exports));
|
|
37597
|
-
const packageRoot =
|
|
38692
|
+
const packageRoot = path57.resolve(
|
|
37598
38693
|
new URL("../..", import.meta.url).pathname
|
|
37599
38694
|
);
|
|
37600
38695
|
const result = checkForUpdate2(packageRoot);
|
|
@@ -37655,7 +38750,7 @@ async function runActivate(key) {
|
|
|
37655
38750
|
const idTemplate = getIdentityTemplate(identityKey);
|
|
37656
38751
|
if (idTemplate) {
|
|
37657
38752
|
const idPath = identityPath2(name);
|
|
37658
|
-
const dir =
|
|
38753
|
+
const dir = path57.dirname(idPath);
|
|
37659
38754
|
if (!fs8.existsSync(dir)) fs8.mkdirSync(dir, { recursive: true });
|
|
37660
38755
|
fs8.writeFileSync(idPath, idTemplate.replace(/^agent_id: \w+/m, `agent_id: ${name}`), "utf-8");
|
|
37661
38756
|
}
|