@askexenow/exe-os 0.9.65 → 0.9.67
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/deploy/stack-manifests/v0.9.json +54 -5
- package/dist/bin/age-ontology-load.js +61 -0
- package/dist/bin/agentic-ontology-backfill.js +4708 -0
- package/dist/bin/agentic-reflection-backfill.js +4144 -0
- package/dist/bin/{exe-link.js → agentic-semantic-label.js} +1532 -2173
- package/dist/bin/backfill-conversations.js +528 -20
- package/dist/bin/backfill-responses.js +528 -20
- package/dist/bin/backfill-vectors.js +255 -20
- package/dist/bin/bulk-sync-postgres.js +4876 -0
- package/dist/bin/cleanup-stale-review-tasks.js +529 -21
- package/dist/bin/cli.js +3471 -1491
- package/dist/bin/exe-agent-config.js +4 -0
- package/dist/bin/exe-agent.js +16 -0
- package/dist/bin/exe-assign.js +528 -20
- package/dist/bin/exe-boot.js +492 -54
- package/dist/bin/exe-call.js +16 -0
- package/dist/bin/exe-cloud.js +7415 -518
- package/dist/bin/exe-dispatch.js +540 -22
- package/dist/bin/exe-doctor.js +3404 -1225
- package/dist/bin/exe-export-behaviors.js +542 -24
- package/dist/bin/exe-forget.js +529 -21
- package/dist/bin/exe-gateway.js +595 -25
- package/dist/bin/exe-heartbeat.js +541 -24
- package/dist/bin/exe-kill.js +529 -21
- package/dist/bin/exe-launch-agent.js +2334 -1067
- package/dist/bin/exe-new-employee.js +324 -166
- package/dist/bin/exe-pending-messages.js +529 -21
- package/dist/bin/exe-pending-notifications.js +529 -21
- package/dist/bin/exe-pending-reviews.js +529 -21
- package/dist/bin/exe-rename.js +529 -21
- package/dist/bin/exe-review.js +529 -21
- package/dist/bin/exe-search.js +542 -24
- package/dist/bin/exe-session-cleanup.js +540 -22
- package/dist/bin/exe-settings.js +14 -0
- package/dist/bin/exe-start-codex.js +817 -144
- package/dist/bin/exe-start-opencode.js +776 -80
- package/dist/bin/exe-status.js +529 -21
- package/dist/bin/exe-team.js +529 -21
- package/dist/bin/git-sweep.js +540 -22
- package/dist/bin/graph-backfill.js +580 -21
- package/dist/bin/graph-export.js +529 -21
- package/dist/bin/graph-layer-benchmark.js +109 -0
- package/dist/bin/install.js +420 -289
- package/dist/bin/intercom-check.js +540 -22
- package/dist/bin/postgres-agentic-reflection-backfill.js +187 -0
- package/dist/bin/postgres-agentic-semantic-backfill.js +237 -0
- package/dist/bin/scan-tasks.js +540 -22
- package/dist/bin/setup.js +790 -206
- package/dist/bin/shard-migrate.js +528 -20
- package/dist/bin/update.js +4 -0
- package/dist/gateway/index.js +593 -23
- package/dist/hooks/bug-report-worker.js +651 -64
- package/dist/hooks/codex-stop-task-finalizer.js +540 -22
- package/dist/hooks/commit-complete.js +540 -22
- package/dist/hooks/error-recall.js +542 -24
- package/dist/hooks/exe-heartbeat-hook.js +4 -0
- package/dist/hooks/ingest-worker.js +4 -0
- package/dist/hooks/ingest.js +539 -22
- package/dist/hooks/instructions-loaded.js +529 -21
- package/dist/hooks/notification.js +529 -21
- package/dist/hooks/post-compact.js +529 -21
- package/dist/hooks/post-tool-combined.js +543 -25
- package/dist/hooks/pre-compact.js +772 -127
- package/dist/hooks/pre-tool-use.js +529 -21
- package/dist/hooks/prompt-submit.js +543 -25
- package/dist/hooks/session-end.js +673 -140
- package/dist/hooks/session-start.js +662 -26
- package/dist/hooks/stop.js +540 -23
- package/dist/hooks/subagent-stop.js +529 -21
- package/dist/hooks/summary-worker.js +571 -126
- package/dist/index.js +593 -23
- package/dist/lib/agent-config.js +4 -0
- package/dist/lib/cloud-sync.js +408 -47
- package/dist/lib/config.js +25 -1
- package/dist/lib/consolidation.js +5 -1
- package/dist/lib/database.js +128 -0
- package/dist/lib/db-daemon-client.js +4 -0
- package/dist/lib/db.js +128 -0
- package/dist/lib/device-registry.js +128 -0
- package/dist/lib/embedder.js +25 -1
- package/dist/lib/employee-templates.js +16 -0
- package/dist/lib/employees.js +4 -0
- package/dist/lib/exe-daemon-client.js +4 -0
- package/dist/lib/exe-daemon.js +3158 -930
- package/dist/lib/hybrid-search.js +542 -24
- package/dist/lib/identity.js +7 -0
- package/dist/lib/keychain.js +178 -22
- package/dist/lib/license.js +4 -0
- package/dist/lib/messaging.js +7 -0
- package/dist/lib/reminders.js +7 -0
- package/dist/lib/schedules.js +255 -20
- package/dist/lib/skill-learning.js +28 -1
- package/dist/lib/status-brief.js +39 -0
- package/dist/lib/store.js +528 -20
- package/dist/lib/task-router.js +4 -0
- package/dist/lib/tasks.js +28 -1
- package/dist/lib/tmux-routing.js +28 -1
- package/dist/lib/token-spend.js +7 -0
- package/dist/mcp/server.js +2739 -813
- package/dist/mcp/tools/complete-reminder.js +7 -0
- package/dist/mcp/tools/create-reminder.js +7 -0
- package/dist/mcp/tools/create-task.js +28 -1
- package/dist/mcp/tools/deactivate-behavior.js +7 -0
- package/dist/mcp/tools/list-reminders.js +7 -0
- package/dist/mcp/tools/list-tasks.js +7 -0
- package/dist/mcp/tools/send-message.js +7 -0
- package/dist/mcp/tools/update-task.js +28 -1
- package/dist/runtime/index.js +540 -22
- package/dist/tui/App.js +618 -29
- package/package.json +9 -5
- package/src/commands/exe/cloud.md +11 -8
- package/stack.release.json +3 -3
- package/src/commands/exe/link.md +0 -17
package/dist/bin/install.js
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
5
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
6
|
-
}) : x)(function(x) {
|
|
7
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
8
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
9
|
-
});
|
|
10
4
|
var __esm = (fn, res) => function __init() {
|
|
11
5
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
12
6
|
};
|
|
@@ -127,6 +121,10 @@ var init_config = __esm({
|
|
|
127
121
|
checkOnBoot: true,
|
|
128
122
|
autoInstall: false,
|
|
129
123
|
checkIntervalMs: 24 * 60 * 60 * 1e3
|
|
124
|
+
},
|
|
125
|
+
orchestration: {
|
|
126
|
+
phase: "phase_1_coo",
|
|
127
|
+
phaseSetBy: "default"
|
|
130
128
|
}
|
|
131
129
|
};
|
|
132
130
|
}
|
|
@@ -608,6 +606,63 @@ var init_preferences = __esm({
|
|
|
608
606
|
}
|
|
609
607
|
});
|
|
610
608
|
|
|
609
|
+
// src/adapters/mcp-http-config.ts
|
|
610
|
+
import { chmodSync as chmodSync2, existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
611
|
+
import { randomBytes } from "crypto";
|
|
612
|
+
import path6 from "path";
|
|
613
|
+
import os5 from "os";
|
|
614
|
+
function mcpHttpPort() {
|
|
615
|
+
return process.env.EXE_MCP_PORT || DEFAULT_MCP_HTTP_PORT;
|
|
616
|
+
}
|
|
617
|
+
function mcpHttpUrl() {
|
|
618
|
+
return `http://127.0.0.1:${mcpHttpPort()}/mcp`;
|
|
619
|
+
}
|
|
620
|
+
function readOrCreateDaemonToken(homeDir = os5.homedir()) {
|
|
621
|
+
const exeDir = path6.join(homeDir, ".exe-os");
|
|
622
|
+
const tokenPath = path6.join(exeDir, "exed.token");
|
|
623
|
+
if (existsSync7(tokenPath)) {
|
|
624
|
+
try {
|
|
625
|
+
const token2 = readFileSync5(tokenPath, "utf-8").trim();
|
|
626
|
+
if (/^[a-f0-9]{64}$/i.test(token2)) return token2;
|
|
627
|
+
} catch {
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
const token = randomBytes(32).toString("hex");
|
|
631
|
+
mkdirSync3(exeDir, { recursive: true });
|
|
632
|
+
writeFileSync4(tokenPath, `${token}
|
|
633
|
+
`, "utf-8");
|
|
634
|
+
try {
|
|
635
|
+
chmodSync2(tokenPath, 384);
|
|
636
|
+
} catch {
|
|
637
|
+
}
|
|
638
|
+
return token;
|
|
639
|
+
}
|
|
640
|
+
function buildMcpHttpHeaders(homeDir = os5.homedir(), opts = {}) {
|
|
641
|
+
const agentId = opts.useShellPlaceholders ? "${AGENT_ID:-exe}" : opts.agentId ?? DEFAULT_MCP_HTTP_AGENT_ID;
|
|
642
|
+
const agentRole = opts.useShellPlaceholders ? "${AGENT_ROLE:-COO}" : opts.agentRole ?? DEFAULT_MCP_HTTP_AGENT_ROLE;
|
|
643
|
+
return {
|
|
644
|
+
Authorization: `Bearer ${readOrCreateDaemonToken(homeDir)}`,
|
|
645
|
+
"X-Agent-Id": agentId,
|
|
646
|
+
"X-Agent-Role": agentRole
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
function buildClaudeHttpMcpEntry(homeDir = os5.homedir()) {
|
|
650
|
+
return {
|
|
651
|
+
type: "http",
|
|
652
|
+
url: mcpHttpUrl(),
|
|
653
|
+
headers: buildMcpHttpHeaders(homeDir, { useShellPlaceholders: true })
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
var DEFAULT_MCP_HTTP_PORT, DEFAULT_MCP_HTTP_AGENT_ID, DEFAULT_MCP_HTTP_AGENT_ROLE;
|
|
657
|
+
var init_mcp_http_config = __esm({
|
|
658
|
+
"src/adapters/mcp-http-config.ts"() {
|
|
659
|
+
"use strict";
|
|
660
|
+
DEFAULT_MCP_HTTP_PORT = "48739";
|
|
661
|
+
DEFAULT_MCP_HTTP_AGENT_ID = "exe";
|
|
662
|
+
DEFAULT_MCP_HTTP_AGENT_ROLE = "COO";
|
|
663
|
+
}
|
|
664
|
+
});
|
|
665
|
+
|
|
611
666
|
// src/adapters/runtime-hook-manifest.ts
|
|
612
667
|
function commandHasAnyMarker(command, markers) {
|
|
613
668
|
return markers.some((marker) => command.includes(marker));
|
|
@@ -615,7 +670,7 @@ function commandHasAnyMarker(command, markers) {
|
|
|
615
670
|
function isLegacySplitPostToolCommand(command) {
|
|
616
671
|
return commandHasAnyMarker(command, LEGACY_SPLIT_POST_TOOL_HOOK_MARKERS);
|
|
617
672
|
}
|
|
618
|
-
var EXE_HOOKS, LEGACY_SPLIT_POST_TOOL_HOOK_MARKERS;
|
|
673
|
+
var EXE_HOOKS, EXE_HOOK_MANIFEST, LEGACY_SPLIT_POST_TOOL_HOOK_MARKERS;
|
|
619
674
|
var init_runtime_hook_manifest = __esm({
|
|
620
675
|
"src/adapters/runtime-hook-manifest.ts"() {
|
|
621
676
|
"use strict";
|
|
@@ -633,6 +688,116 @@ var init_runtime_hook_manifest = __esm({
|
|
|
633
688
|
notification: "dist/hooks/notification.js",
|
|
634
689
|
instructionsLoaded: "dist/hooks/instructions-loaded.js"
|
|
635
690
|
};
|
|
691
|
+
EXE_HOOK_MANIFEST = [
|
|
692
|
+
{
|
|
693
|
+
key: "postToolCombined",
|
|
694
|
+
event: "PostToolUse",
|
|
695
|
+
commandMarker: EXE_HOOKS.postToolCombined,
|
|
696
|
+
owner: "exe-os",
|
|
697
|
+
purpose: "Single PostToolUse entrypoint for ingestion, error recall, summaries, and bug detection.",
|
|
698
|
+
runtimes: ["claude", "codex", "opencode"],
|
|
699
|
+
checkpointRole: "none"
|
|
700
|
+
},
|
|
701
|
+
{
|
|
702
|
+
key: "sessionStart",
|
|
703
|
+
event: "SessionStart",
|
|
704
|
+
commandMarker: EXE_HOOKS.sessionStart,
|
|
705
|
+
owner: "exe-os",
|
|
706
|
+
purpose: "Loads agent identity, procedures, and boot context.",
|
|
707
|
+
runtimes: ["claude", "codex", "opencode"],
|
|
708
|
+
checkpointRole: "none"
|
|
709
|
+
},
|
|
710
|
+
{
|
|
711
|
+
key: "promptSubmit",
|
|
712
|
+
event: "UserPromptSubmit",
|
|
713
|
+
commandMarker: EXE_HOOKS.promptSubmit,
|
|
714
|
+
owner: "exe-os",
|
|
715
|
+
purpose: "Injects current tasks, pending reviews, and local context before each prompt.",
|
|
716
|
+
runtimes: ["claude", "codex", "opencode"],
|
|
717
|
+
checkpointRole: "none"
|
|
718
|
+
},
|
|
719
|
+
{
|
|
720
|
+
key: "heartbeat",
|
|
721
|
+
event: "UserPromptSubmit",
|
|
722
|
+
commandMarker: EXE_HOOKS.heartbeat,
|
|
723
|
+
owner: "exe-os",
|
|
724
|
+
purpose: "Lightweight heartbeat/status sidecar for Claude Code sessions.",
|
|
725
|
+
runtimes: ["claude"],
|
|
726
|
+
checkpointRole: "none"
|
|
727
|
+
},
|
|
728
|
+
{
|
|
729
|
+
key: "stop",
|
|
730
|
+
event: "Stop",
|
|
731
|
+
commandMarker: EXE_HOOKS.stop,
|
|
732
|
+
owner: "exe-os",
|
|
733
|
+
purpose: "Finalizes task state, capacity signals, and emergency checkpointing.",
|
|
734
|
+
runtimes: ["claude", "codex", "opencode"],
|
|
735
|
+
checkpointRole: "capacity_checkpoint"
|
|
736
|
+
},
|
|
737
|
+
{
|
|
738
|
+
key: "preToolUse",
|
|
739
|
+
event: "PreToolUse",
|
|
740
|
+
commandMarker: EXE_HOOKS.preToolUse,
|
|
741
|
+
owner: "exe-os",
|
|
742
|
+
purpose: "Preflight guardrails before shell/tool execution.",
|
|
743
|
+
runtimes: ["claude", "codex", "opencode"],
|
|
744
|
+
checkpointRole: "none"
|
|
745
|
+
},
|
|
746
|
+
{
|
|
747
|
+
key: "subagentStop",
|
|
748
|
+
event: "SubagentStop",
|
|
749
|
+
commandMarker: EXE_HOOKS.subagentStop,
|
|
750
|
+
owner: "exe-os",
|
|
751
|
+
purpose: "Captures subagent completion context.",
|
|
752
|
+
runtimes: ["claude"],
|
|
753
|
+
checkpointRole: "none"
|
|
754
|
+
},
|
|
755
|
+
{
|
|
756
|
+
key: "preCompact",
|
|
757
|
+
event: "PreCompact",
|
|
758
|
+
commandMarker: EXE_HOOKS.preCompact,
|
|
759
|
+
owner: "exe-os",
|
|
760
|
+
purpose: "Writes active-task snapshot and compaction recovery context.",
|
|
761
|
+
runtimes: ["claude"],
|
|
762
|
+
checkpointRole: "recovery_context"
|
|
763
|
+
},
|
|
764
|
+
{
|
|
765
|
+
key: "postCompact",
|
|
766
|
+
event: "PostCompact",
|
|
767
|
+
commandMarker: EXE_HOOKS.postCompact,
|
|
768
|
+
owner: "exe-os",
|
|
769
|
+
purpose: "Rehydrates recovery context after compaction.",
|
|
770
|
+
runtimes: ["claude"],
|
|
771
|
+
checkpointRole: "recovery_context"
|
|
772
|
+
},
|
|
773
|
+
{
|
|
774
|
+
key: "sessionEnd",
|
|
775
|
+
event: "SessionEnd",
|
|
776
|
+
commandMarker: EXE_HOOKS.sessionEnd,
|
|
777
|
+
owner: "exe-os",
|
|
778
|
+
purpose: "Stores session-end checkpoint and triages orphaned in-progress tasks.",
|
|
779
|
+
runtimes: ["claude"],
|
|
780
|
+
checkpointRole: "session_summary"
|
|
781
|
+
},
|
|
782
|
+
{
|
|
783
|
+
key: "notification",
|
|
784
|
+
event: "Notification",
|
|
785
|
+
commandMarker: EXE_HOOKS.notification,
|
|
786
|
+
owner: "exe-os",
|
|
787
|
+
purpose: "Captures runtime notifications and nudges.",
|
|
788
|
+
runtimes: ["claude"],
|
|
789
|
+
checkpointRole: "none"
|
|
790
|
+
},
|
|
791
|
+
{
|
|
792
|
+
key: "instructionsLoaded",
|
|
793
|
+
event: "InstructionsLoaded",
|
|
794
|
+
commandMarker: EXE_HOOKS.instructionsLoaded,
|
|
795
|
+
owner: "exe-os",
|
|
796
|
+
purpose: "Applies runtime instruction post-processing.",
|
|
797
|
+
runtimes: ["claude"],
|
|
798
|
+
checkpointRole: "none"
|
|
799
|
+
}
|
|
800
|
+
];
|
|
636
801
|
LEGACY_SPLIT_POST_TOOL_HOOK_MARKERS = [
|
|
637
802
|
"dist/hooks/ingest.js",
|
|
638
803
|
"dist/hooks/error-recall.js",
|
|
@@ -642,54 +807,58 @@ var init_runtime_hook_manifest = __esm({
|
|
|
642
807
|
});
|
|
643
808
|
|
|
644
809
|
// src/adapters/claude/installer.ts
|
|
645
|
-
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3, readdir } from "fs/promises";
|
|
646
|
-
import { existsSync as
|
|
647
|
-
import { createHash
|
|
648
|
-
import
|
|
649
|
-
import
|
|
810
|
+
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3, readdir, rm } from "fs/promises";
|
|
811
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6, writeFileSync as writeFileSync5, copyFileSync, mkdirSync as mkdirSync4 } from "fs";
|
|
812
|
+
import { createHash } from "crypto";
|
|
813
|
+
import path7 from "path";
|
|
814
|
+
import os6 from "os";
|
|
650
815
|
import { execSync as execSync2 } from "child_process";
|
|
651
816
|
import { fileURLToPath } from "url";
|
|
652
817
|
function resolvePackageRoot() {
|
|
653
818
|
const thisFile = fileURLToPath(import.meta.url);
|
|
654
|
-
let dir =
|
|
655
|
-
const root =
|
|
819
|
+
let dir = path7.dirname(thisFile);
|
|
820
|
+
const root = path7.parse(dir).root;
|
|
656
821
|
while (dir !== root) {
|
|
657
|
-
const pkgPath =
|
|
658
|
-
if (
|
|
822
|
+
const pkgPath = path7.join(dir, "package.json");
|
|
823
|
+
if (existsSync8(pkgPath)) {
|
|
659
824
|
try {
|
|
660
|
-
const pkg = JSON.parse(
|
|
825
|
+
const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
|
|
661
826
|
if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
|
|
662
827
|
} catch {
|
|
663
828
|
}
|
|
664
829
|
}
|
|
665
|
-
dir =
|
|
830
|
+
dir = path7.dirname(dir);
|
|
666
831
|
}
|
|
667
|
-
return
|
|
832
|
+
return path7.resolve(path7.dirname(thisFile), "..", "..", "..");
|
|
668
833
|
}
|
|
669
|
-
async function copySlashCommands(packageRoot, homeDir =
|
|
834
|
+
async function copySlashCommands(packageRoot, homeDir = os6.homedir()) {
|
|
670
835
|
let copied = 0;
|
|
671
836
|
let skipped = 0;
|
|
672
|
-
const skillsBase =
|
|
673
|
-
const
|
|
674
|
-
if (
|
|
837
|
+
const skillsBase = path7.join(homeDir, ".claude", "skills");
|
|
838
|
+
const deprecatedExeLink = path7.join(skillsBase, "exe-link");
|
|
839
|
+
if (existsSync8(deprecatedExeLink)) {
|
|
840
|
+
await rm(deprecatedExeLink, { recursive: true, force: true });
|
|
841
|
+
}
|
|
842
|
+
const exeDir = path7.join(packageRoot, "src", "commands", "exe");
|
|
843
|
+
if (existsSync8(exeDir)) {
|
|
675
844
|
const entries = await readdir(exeDir);
|
|
676
845
|
const mdFiles = entries.filter((f) => f.endsWith(".md"));
|
|
677
846
|
for (const file of mdFiles) {
|
|
678
847
|
const name = file.replace(".md", "");
|
|
679
|
-
const destDir =
|
|
848
|
+
const destDir = path7.join(skillsBase, `exe-${name}`);
|
|
680
849
|
await mkdir3(destDir, { recursive: true });
|
|
681
|
-
const srcPath =
|
|
682
|
-
const destPath =
|
|
850
|
+
const srcPath = path7.join(exeDir, file);
|
|
851
|
+
const destPath = path7.join(destDir, "SKILL.md");
|
|
683
852
|
const result = await copyAsSkill(srcPath, destPath, `exe-${name}`);
|
|
684
853
|
if (result) copied++;
|
|
685
854
|
else skipped++;
|
|
686
855
|
}
|
|
687
856
|
}
|
|
688
|
-
const topLevelSrc =
|
|
689
|
-
if (
|
|
690
|
-
const destDir =
|
|
857
|
+
const topLevelSrc = path7.join(packageRoot, "src", "commands", "exe.md");
|
|
858
|
+
if (existsSync8(topLevelSrc)) {
|
|
859
|
+
const destDir = path7.join(skillsBase, "exe");
|
|
691
860
|
await mkdir3(destDir, { recursive: true });
|
|
692
|
-
const destPath =
|
|
861
|
+
const destPath = path7.join(destDir, "SKILL.md");
|
|
693
862
|
const result = await copyAsSkill(topLevelSrc, destPath, "exe");
|
|
694
863
|
if (result) copied++;
|
|
695
864
|
else skipped++;
|
|
@@ -712,7 +881,7 @@ name: ${skillName}
|
|
|
712
881
|
`);
|
|
713
882
|
}
|
|
714
883
|
}
|
|
715
|
-
if (
|
|
884
|
+
if (existsSync8(destPath)) {
|
|
716
885
|
const existing = await readFile3(destPath, "utf-8");
|
|
717
886
|
if (existing === content) return false;
|
|
718
887
|
}
|
|
@@ -721,32 +890,32 @@ name: ${skillName}
|
|
|
721
890
|
}
|
|
722
891
|
function readJsonFile(filePath) {
|
|
723
892
|
try {
|
|
724
|
-
return JSON.parse(
|
|
893
|
+
return JSON.parse(readFileSync6(filePath, "utf-8"));
|
|
725
894
|
} catch {
|
|
726
895
|
return null;
|
|
727
896
|
}
|
|
728
897
|
}
|
|
729
898
|
function findAncestorMcpJsons(startDir, homeDir) {
|
|
730
899
|
const files = [];
|
|
731
|
-
let dir =
|
|
732
|
-
const root =
|
|
733
|
-
const stop =
|
|
900
|
+
let dir = path7.resolve(startDir);
|
|
901
|
+
const root = path7.parse(dir).root;
|
|
902
|
+
const stop = path7.resolve(homeDir);
|
|
734
903
|
while (dir !== root) {
|
|
735
|
-
const candidate =
|
|
736
|
-
if (
|
|
904
|
+
const candidate = path7.join(dir, ".mcp.json");
|
|
905
|
+
if (existsSync8(candidate)) files.push(candidate);
|
|
737
906
|
if (dir === stop) break;
|
|
738
|
-
dir =
|
|
907
|
+
dir = path7.dirname(dir);
|
|
739
908
|
}
|
|
740
909
|
return files;
|
|
741
910
|
}
|
|
742
911
|
function pathApplies(projectPath, cwd) {
|
|
743
|
-
const project =
|
|
744
|
-
const current =
|
|
745
|
-
return current === project || current.startsWith(project +
|
|
912
|
+
const project = path7.resolve(projectPath);
|
|
913
|
+
const current = path7.resolve(cwd);
|
|
914
|
+
return current === project || current.startsWith(project + path7.sep);
|
|
746
915
|
}
|
|
747
|
-
function detectMcpNameCollisions(homeDir =
|
|
748
|
-
const claudeJsonPath =
|
|
749
|
-
if (!
|
|
916
|
+
function detectMcpNameCollisions(homeDir = os6.homedir(), cwd = process.cwd()) {
|
|
917
|
+
const claudeJsonPath = path7.join(homeDir, ".claude.json");
|
|
918
|
+
if (!existsSync8(claudeJsonPath)) return [];
|
|
750
919
|
const claudeJson = readJsonFile(claudeJsonPath);
|
|
751
920
|
if (!claudeJson?.projects) return [];
|
|
752
921
|
const collisions = [];
|
|
@@ -773,44 +942,11 @@ function detectMcpNameCollisions(homeDir = os5.homedir(), cwd = process.cwd()) {
|
|
|
773
942
|
}
|
|
774
943
|
return collisions;
|
|
775
944
|
}
|
|
776
|
-
function readOrCreateDaemonToken(homeDir) {
|
|
777
|
-
const exeDir = path6.join(homeDir, ".exe-os");
|
|
778
|
-
const tokenPath = path6.join(exeDir, "exed.token");
|
|
779
|
-
try {
|
|
780
|
-
if (existsSync7(tokenPath)) {
|
|
781
|
-
const token2 = readFileSync5(tokenPath, "utf-8").trim();
|
|
782
|
-
if (token2) return token2;
|
|
783
|
-
}
|
|
784
|
-
} catch {
|
|
785
|
-
}
|
|
786
|
-
const token = randomBytes(32).toString("hex");
|
|
787
|
-
mkdirSync3(exeDir, { recursive: true });
|
|
788
|
-
writeFileSync4(tokenPath, `${token}
|
|
789
|
-
`, "utf-8");
|
|
790
|
-
try {
|
|
791
|
-
chmodSync2(tokenPath, 384);
|
|
792
|
-
} catch {
|
|
793
|
-
}
|
|
794
|
-
return token;
|
|
795
|
-
}
|
|
796
|
-
function buildHttpMcpEntry(homeDir) {
|
|
797
|
-
const port = process.env.EXE_MCP_PORT || "48739";
|
|
798
|
-
const token = readOrCreateDaemonToken(homeDir);
|
|
799
|
-
return {
|
|
800
|
-
type: "http",
|
|
801
|
-
url: `http://127.0.0.1:${port}/mcp`,
|
|
802
|
-
headers: {
|
|
803
|
-
Authorization: `Bearer ${token}`,
|
|
804
|
-
"X-Agent-Id": "${AGENT_ID:-exe}",
|
|
805
|
-
"X-Agent-Role": "${AGENT_ROLE:-COO}"
|
|
806
|
-
}
|
|
807
|
-
};
|
|
808
|
-
}
|
|
809
945
|
function buildStdioMcpEntry(packageRoot) {
|
|
810
946
|
return {
|
|
811
947
|
type: "stdio",
|
|
812
948
|
command: "node",
|
|
813
|
-
args: [
|
|
949
|
+
args: [path7.join(packageRoot, "dist", "mcp", "server.js")],
|
|
814
950
|
env: {}
|
|
815
951
|
};
|
|
816
952
|
}
|
|
@@ -818,14 +954,14 @@ function mcpTransportMode() {
|
|
|
818
954
|
const value = process.env.EXE_OS_MCP_TRANSPORT?.trim().toLowerCase();
|
|
819
955
|
if (value === "stdio") return "stdio";
|
|
820
956
|
if (value === "http") return "http";
|
|
821
|
-
const totalGB =
|
|
957
|
+
const totalGB = os6.totalmem() / (1024 * 1024 * 1024);
|
|
822
958
|
if (totalGB <= 8) return "stdio";
|
|
823
959
|
return "http";
|
|
824
960
|
}
|
|
825
|
-
async function registerMcpServer(packageRoot, homeDir =
|
|
826
|
-
const claudeJsonPath =
|
|
961
|
+
async function registerMcpServer(packageRoot, homeDir = os6.homedir()) {
|
|
962
|
+
const claudeJsonPath = path7.join(homeDir, ".claude.json");
|
|
827
963
|
let claudeJson = {};
|
|
828
|
-
if (
|
|
964
|
+
if (existsSync8(claudeJsonPath)) {
|
|
829
965
|
try {
|
|
830
966
|
claudeJson = JSON.parse(await readFile3(claudeJsonPath, "utf-8"));
|
|
831
967
|
} catch {
|
|
@@ -835,7 +971,7 @@ async function registerMcpServer(packageRoot, homeDir = os5.homedir()) {
|
|
|
835
971
|
if (!claudeJson.mcpServers) {
|
|
836
972
|
claudeJson.mcpServers = {};
|
|
837
973
|
}
|
|
838
|
-
const newEntry = mcpTransportMode() === "stdio" ? buildStdioMcpEntry(packageRoot) :
|
|
974
|
+
const newEntry = mcpTransportMode() === "stdio" ? buildStdioMcpEntry(packageRoot) : buildClaudeHttpMcpEntry(homeDir);
|
|
839
975
|
if (claudeJson.mcpServers[MCP_LEGACY_KEY]) {
|
|
840
976
|
delete claudeJson.mcpServers[MCP_LEGACY_KEY];
|
|
841
977
|
process.stderr.write("exe-os: migrated MCP server key exe-mem \u2192 exe-os\n");
|
|
@@ -843,8 +979,8 @@ async function registerMcpServer(packageRoot, homeDir = os5.homedir()) {
|
|
|
843
979
|
const currentOs = claudeJson.mcpServers[MCP_PRIMARY_KEY];
|
|
844
980
|
const osMatches = currentOs && JSON.stringify(currentOs) === JSON.stringify(newEntry);
|
|
845
981
|
if (osMatches) {
|
|
846
|
-
await cleanSettingsJsonMcp(
|
|
847
|
-
await migratePermissionsToExeOs(
|
|
982
|
+
await cleanSettingsJsonMcp(path7.join(homeDir, ".claude", "settings.json"));
|
|
983
|
+
await migratePermissionsToExeOs(path7.join(homeDir, ".claude", "settings.json"));
|
|
848
984
|
const collisions2 = detectMcpNameCollisions(homeDir, packageRoot).filter((c) => c.serverName === MCP_PRIMARY_KEY || c.serverName === MCP_LEGACY_KEY);
|
|
849
985
|
for (const c of collisions2) {
|
|
850
986
|
process.stderr.write(
|
|
@@ -856,8 +992,8 @@ async function registerMcpServer(packageRoot, homeDir = os5.homedir()) {
|
|
|
856
992
|
}
|
|
857
993
|
claudeJson.mcpServers[MCP_PRIMARY_KEY] = newEntry;
|
|
858
994
|
await writeFile3(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
859
|
-
await cleanSettingsJsonMcp(
|
|
860
|
-
await migratePermissionsToExeOs(
|
|
995
|
+
await cleanSettingsJsonMcp(path7.join(homeDir, ".claude", "settings.json"));
|
|
996
|
+
await migratePermissionsToExeOs(path7.join(homeDir, ".claude", "settings.json"));
|
|
861
997
|
const collisions = detectMcpNameCollisions(homeDir, packageRoot).filter((c) => c.serverName === MCP_PRIMARY_KEY || c.serverName === MCP_LEGACY_KEY);
|
|
862
998
|
for (const c of collisions) {
|
|
863
999
|
process.stderr.write(
|
|
@@ -868,7 +1004,7 @@ async function registerMcpServer(packageRoot, homeDir = os5.homedir()) {
|
|
|
868
1004
|
return true;
|
|
869
1005
|
}
|
|
870
1006
|
async function cleanSettingsJsonMcp(settingsPath) {
|
|
871
|
-
if (!
|
|
1007
|
+
if (!existsSync8(settingsPath)) return;
|
|
872
1008
|
try {
|
|
873
1009
|
const settings = JSON.parse(await readFile3(settingsPath, "utf-8"));
|
|
874
1010
|
const servers = settings.mcpServers;
|
|
@@ -889,7 +1025,7 @@ async function cleanSettingsJsonMcp(settingsPath) {
|
|
|
889
1025
|
}
|
|
890
1026
|
}
|
|
891
1027
|
async function migratePermissionsToExeOs(settingsPath) {
|
|
892
|
-
if (!
|
|
1028
|
+
if (!existsSync8(settingsPath)) return;
|
|
893
1029
|
try {
|
|
894
1030
|
const settings = JSON.parse(await readFile3(settingsPath, "utf-8"));
|
|
895
1031
|
const permissions = settings.permissions;
|
|
@@ -917,14 +1053,14 @@ async function migratePermissionsToExeOs(settingsPath) {
|
|
|
917
1053
|
} catch {
|
|
918
1054
|
}
|
|
919
1055
|
}
|
|
920
|
-
async function mergeHooks(packageRoot, homeDir =
|
|
921
|
-
const settingsPath =
|
|
922
|
-
const logsDir =
|
|
923
|
-
const hookLogPath =
|
|
1056
|
+
async function mergeHooks(packageRoot, homeDir = os6.homedir()) {
|
|
1057
|
+
const settingsPath = path7.join(homeDir, ".claude", "settings.json");
|
|
1058
|
+
const logsDir = path7.join(homeDir, ".exe-os", "logs");
|
|
1059
|
+
const hookLogPath = path7.join(logsDir, "hooks.log");
|
|
924
1060
|
const logSuffix = ` 2>> "${hookLogPath}"`;
|
|
925
1061
|
await mkdir3(logsDir, { recursive: true });
|
|
926
1062
|
let settings = {};
|
|
927
|
-
if (
|
|
1063
|
+
if (existsSync8(settingsPath)) {
|
|
928
1064
|
try {
|
|
929
1065
|
settings = JSON.parse(await readFile3(settingsPath, "utf-8"));
|
|
930
1066
|
} catch {
|
|
@@ -946,7 +1082,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
946
1082
|
hooks: [
|
|
947
1083
|
{
|
|
948
1084
|
type: "command",
|
|
949
|
-
command: `node "${
|
|
1085
|
+
command: `node "${path7.join(packageRoot, "dist", "hooks", "post-tool-combined.js")}"${logSuffix}`
|
|
950
1086
|
}
|
|
951
1087
|
]
|
|
952
1088
|
},
|
|
@@ -958,7 +1094,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
958
1094
|
hooks: [
|
|
959
1095
|
{
|
|
960
1096
|
type: "command",
|
|
961
|
-
command: `node "${
|
|
1097
|
+
command: `node "${path7.join(packageRoot, "dist", "hooks", "session-start.js")}"${logSuffix}`,
|
|
962
1098
|
timeout: 1e4
|
|
963
1099
|
}
|
|
964
1100
|
]
|
|
@@ -971,7 +1107,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
971
1107
|
hooks: [
|
|
972
1108
|
{
|
|
973
1109
|
type: "command",
|
|
974
|
-
command: `node "${
|
|
1110
|
+
command: `node "${path7.join(packageRoot, "dist", "hooks", "prompt-submit.js")}"${logSuffix}`
|
|
975
1111
|
}
|
|
976
1112
|
]
|
|
977
1113
|
},
|
|
@@ -983,7 +1119,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
983
1119
|
hooks: [
|
|
984
1120
|
{
|
|
985
1121
|
type: "command",
|
|
986
|
-
command: `node "${
|
|
1122
|
+
command: `node "${path7.join(packageRoot, "dist", "hooks", "exe-heartbeat-hook.js")}"${logSuffix}`,
|
|
987
1123
|
timeout: 5e3
|
|
988
1124
|
}
|
|
989
1125
|
]
|
|
@@ -996,7 +1132,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
996
1132
|
hooks: [
|
|
997
1133
|
{
|
|
998
1134
|
type: "command",
|
|
999
|
-
command: `node "${
|
|
1135
|
+
command: `node "${path7.join(packageRoot, "dist", "hooks", "stop.js")}"${logSuffix}`
|
|
1000
1136
|
}
|
|
1001
1137
|
]
|
|
1002
1138
|
},
|
|
@@ -1009,7 +1145,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1009
1145
|
hooks: [
|
|
1010
1146
|
{
|
|
1011
1147
|
type: "command",
|
|
1012
|
-
command: `node "${
|
|
1148
|
+
command: `node "${path7.join(packageRoot, "dist", "hooks", "pre-tool-use.js")}"${logSuffix}`
|
|
1013
1149
|
}
|
|
1014
1150
|
]
|
|
1015
1151
|
},
|
|
@@ -1021,7 +1157,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1021
1157
|
hooks: [
|
|
1022
1158
|
{
|
|
1023
1159
|
type: "command",
|
|
1024
|
-
command: `node "${
|
|
1160
|
+
command: `node "${path7.join(packageRoot, "dist", "hooks", "subagent-stop.js")}"${logSuffix}`
|
|
1025
1161
|
}
|
|
1026
1162
|
]
|
|
1027
1163
|
},
|
|
@@ -1033,7 +1169,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1033
1169
|
hooks: [
|
|
1034
1170
|
{
|
|
1035
1171
|
type: "command",
|
|
1036
|
-
command: `node "${
|
|
1172
|
+
command: `node "${path7.join(packageRoot, "dist", "hooks", "pre-compact.js")}"${logSuffix}`,
|
|
1037
1173
|
timeout: 1e4
|
|
1038
1174
|
}
|
|
1039
1175
|
]
|
|
@@ -1046,7 +1182,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1046
1182
|
hooks: [
|
|
1047
1183
|
{
|
|
1048
1184
|
type: "command",
|
|
1049
|
-
command: `node "${
|
|
1185
|
+
command: `node "${path7.join(packageRoot, "dist", "hooks", "session-end.js")}"${logSuffix}`
|
|
1050
1186
|
}
|
|
1051
1187
|
]
|
|
1052
1188
|
},
|
|
@@ -1058,7 +1194,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1058
1194
|
hooks: [
|
|
1059
1195
|
{
|
|
1060
1196
|
type: "command",
|
|
1061
|
-
command: `node "${
|
|
1197
|
+
command: `node "${path7.join(packageRoot, "dist", "hooks", "notification.js")}"${logSuffix}`
|
|
1062
1198
|
}
|
|
1063
1199
|
]
|
|
1064
1200
|
},
|
|
@@ -1070,7 +1206,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1070
1206
|
hooks: [
|
|
1071
1207
|
{
|
|
1072
1208
|
type: "command",
|
|
1073
|
-
command: `node "${
|
|
1209
|
+
command: `node "${path7.join(packageRoot, "dist", "hooks", "post-compact.js")}"${logSuffix}`,
|
|
1074
1210
|
timeout: 1e4
|
|
1075
1211
|
}
|
|
1076
1212
|
]
|
|
@@ -1083,7 +1219,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1083
1219
|
hooks: [
|
|
1084
1220
|
{
|
|
1085
1221
|
type: "command",
|
|
1086
|
-
command: `node "${
|
|
1222
|
+
command: `node "${path7.join(packageRoot, "dist", "hooks", "instructions-loaded.js")}"${logSuffix}`
|
|
1087
1223
|
}
|
|
1088
1224
|
]
|
|
1089
1225
|
},
|
|
@@ -1188,13 +1324,13 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1188
1324
|
allowList.push(fullName);
|
|
1189
1325
|
}
|
|
1190
1326
|
}
|
|
1191
|
-
await mkdir3(
|
|
1327
|
+
await mkdir3(path7.dirname(settingsPath), { recursive: true });
|
|
1192
1328
|
await writeFile3(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
1193
1329
|
return { added, skipped };
|
|
1194
1330
|
}
|
|
1195
|
-
async function cleanOldShellFunctions(homeDir =
|
|
1196
|
-
const rosterPath =
|
|
1197
|
-
if (!
|
|
1331
|
+
async function cleanOldShellFunctions(homeDir = os6.homedir()) {
|
|
1332
|
+
const rosterPath = path7.join(homeDir, ".exe-os", "exe-employees.json");
|
|
1333
|
+
if (!existsSync8(rosterPath)) return 0;
|
|
1198
1334
|
let employees;
|
|
1199
1335
|
try {
|
|
1200
1336
|
employees = JSON.parse(await readFile3(rosterPath, "utf-8"));
|
|
@@ -1209,13 +1345,13 @@ async function cleanOldShellFunctions(homeDir = os5.homedir()) {
|
|
|
1209
1345
|
return { name: n, funcDef, forLoop };
|
|
1210
1346
|
});
|
|
1211
1347
|
const rcFiles = [
|
|
1212
|
-
|
|
1213
|
-
|
|
1348
|
+
path7.join(homeDir, ".zshrc"),
|
|
1349
|
+
path7.join(homeDir, ".bashrc")
|
|
1214
1350
|
];
|
|
1215
1351
|
const REMOVED_MARKER = "# Removed by exe-os \u2014 wrappers now at ~/.exe-os/bin/";
|
|
1216
1352
|
let totalRemoved = 0;
|
|
1217
1353
|
for (const rcPath of rcFiles) {
|
|
1218
|
-
if (!
|
|
1354
|
+
if (!existsSync8(rcPath)) continue;
|
|
1219
1355
|
let content;
|
|
1220
1356
|
try {
|
|
1221
1357
|
content = await readFile3(rcPath, "utf-8");
|
|
@@ -1319,8 +1455,8 @@ function escapeRegExp(s) {
|
|
|
1319
1455
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1320
1456
|
}
|
|
1321
1457
|
async function injectOrchestrationRules(homeDir) {
|
|
1322
|
-
const claudeDir =
|
|
1323
|
-
const claudeMdPath =
|
|
1458
|
+
const claudeDir = path7.join(homeDir, ".claude");
|
|
1459
|
+
const claudeMdPath = path7.join(claudeDir, "CLAUDE.md");
|
|
1324
1460
|
await mkdir3(claudeDir, { recursive: true });
|
|
1325
1461
|
let existing = "";
|
|
1326
1462
|
try {
|
|
@@ -1342,19 +1478,19 @@ async function injectOrchestrationRules(homeDir) {
|
|
|
1342
1478
|
await writeFile3(claudeMdPath, existing + separator + ORCHESTRATION_RULES + "\n", "utf-8");
|
|
1343
1479
|
return "injected";
|
|
1344
1480
|
}
|
|
1345
|
-
async function installStatusLine(packageRoot, homeDir =
|
|
1481
|
+
async function installStatusLine(packageRoot, homeDir = os6.homedir()) {
|
|
1346
1482
|
const prefs = loadPreferences(homeDir);
|
|
1347
1483
|
if (prefs.ccStatusLine === false) return "opted-out";
|
|
1348
|
-
const claudeDir =
|
|
1484
|
+
const claudeDir = path7.join(homeDir, ".claude");
|
|
1349
1485
|
await mkdir3(claudeDir, { recursive: true });
|
|
1350
|
-
const assetPath =
|
|
1351
|
-
if (!
|
|
1352
|
-
const destScript =
|
|
1486
|
+
const assetPath = path7.join(packageRoot, "dist", "assets", "statusline-command.sh");
|
|
1487
|
+
if (!existsSync8(assetPath)) return "asset-missing";
|
|
1488
|
+
const destScript = path7.join(claudeDir, "statusline-command.sh");
|
|
1353
1489
|
const assetContent = await readFile3(assetPath, "utf-8");
|
|
1354
1490
|
await writeFile3(destScript, assetContent, { mode: 493 });
|
|
1355
|
-
const settingsPath =
|
|
1491
|
+
const settingsPath = path7.join(claudeDir, "settings.json");
|
|
1356
1492
|
let settings = {};
|
|
1357
|
-
if (
|
|
1493
|
+
if (existsSync8(settingsPath)) {
|
|
1358
1494
|
try {
|
|
1359
1495
|
settings = JSON.parse(await readFile3(settingsPath, "utf-8"));
|
|
1360
1496
|
} catch {
|
|
@@ -1391,13 +1527,13 @@ async function runInstaller(homeDir) {
|
|
|
1391
1527
|
`Hooks: ${hookResult.added} added, ${hookResult.skipped} unchanged
|
|
1392
1528
|
`
|
|
1393
1529
|
);
|
|
1394
|
-
const resolvedHome = homeDir ??
|
|
1395
|
-
const exeWorkspace =
|
|
1396
|
-
if (!
|
|
1530
|
+
const resolvedHome = homeDir ?? os6.homedir();
|
|
1531
|
+
const exeWorkspace = path7.join(resolvedHome, "exe");
|
|
1532
|
+
if (!existsSync8(exeWorkspace)) {
|
|
1397
1533
|
try {
|
|
1398
|
-
await mkdir3(
|
|
1399
|
-
await mkdir3(
|
|
1400
|
-
await mkdir3(
|
|
1534
|
+
await mkdir3(path7.join(exeWorkspace, "content"), { recursive: true });
|
|
1535
|
+
await mkdir3(path7.join(exeWorkspace, "operations"), { recursive: true });
|
|
1536
|
+
await mkdir3(path7.join(exeWorkspace, "output"), { recursive: true });
|
|
1401
1537
|
process.stderr.write(
|
|
1402
1538
|
`Created ~/exe/ \u2014 your automation workspace for non-code projects
|
|
1403
1539
|
`
|
|
@@ -1432,57 +1568,57 @@ exe-os installed successfully.
|
|
|
1432
1568
|
`);
|
|
1433
1569
|
}
|
|
1434
1570
|
function setupTmux(home) {
|
|
1435
|
-
const homeDir = home ??
|
|
1436
|
-
const exeDir =
|
|
1437
|
-
const exeTmuxConf =
|
|
1438
|
-
const userTmuxConf =
|
|
1439
|
-
const backupPath =
|
|
1571
|
+
const homeDir = home ?? os6.homedir();
|
|
1572
|
+
const exeDir = path7.join(homeDir, ".exe-os");
|
|
1573
|
+
const exeTmuxConf = path7.join(exeDir, "tmux.conf");
|
|
1574
|
+
const userTmuxConf = path7.join(homeDir, ".tmux.conf");
|
|
1575
|
+
const backupPath = path7.join(homeDir, ".tmux.conf.backup");
|
|
1440
1576
|
const sourceLine = "source-file ~/.exe-os/tmux.conf";
|
|
1441
1577
|
const pkgRoot = resolvePackageRoot();
|
|
1442
|
-
const assetPath =
|
|
1443
|
-
if (!
|
|
1578
|
+
const assetPath = path7.join(pkgRoot, "dist", "assets", "tmux.conf");
|
|
1579
|
+
if (!existsSync8(assetPath)) {
|
|
1444
1580
|
process.stderr.write(`exe-os: tmux.conf asset not found at ${assetPath} \u2014 skipping tmux setup
|
|
1445
1581
|
`);
|
|
1446
1582
|
return;
|
|
1447
1583
|
}
|
|
1448
|
-
|
|
1449
|
-
if (
|
|
1450
|
-
const currentContent =
|
|
1451
|
-
const newContent =
|
|
1584
|
+
mkdirSync4(exeDir, { recursive: true });
|
|
1585
|
+
if (existsSync8(exeTmuxConf)) {
|
|
1586
|
+
const currentContent = readFileSync6(exeTmuxConf, "utf8");
|
|
1587
|
+
const newContent = readFileSync6(assetPath, "utf8");
|
|
1452
1588
|
const currentHash = createHash("sha256").update(currentContent).digest("hex");
|
|
1453
1589
|
const newHash = createHash("sha256").update(newContent).digest("hex");
|
|
1454
1590
|
if (currentHash !== newHash) {
|
|
1455
|
-
const shippedPath =
|
|
1456
|
-
const lastShippedHash =
|
|
1591
|
+
const shippedPath = path7.join(exeDir, ".tmux.conf.shipped-hash");
|
|
1592
|
+
const lastShippedHash = existsSync8(shippedPath) ? readFileSync6(shippedPath, "utf8").trim() : "";
|
|
1457
1593
|
if (lastShippedHash && currentHash !== lastShippedHash) {
|
|
1458
1594
|
process.stderr.write("exe-os: tmux config has user customizations \u2014 skipping overwrite\n");
|
|
1459
1595
|
} else {
|
|
1460
1596
|
copyFileSync(assetPath, exeTmuxConf);
|
|
1461
1597
|
process.stderr.write("exe-os: tmux config updated\n");
|
|
1462
1598
|
}
|
|
1463
|
-
|
|
1599
|
+
writeFileSync5(shippedPath, newHash, "utf8");
|
|
1464
1600
|
} else {
|
|
1465
1601
|
process.stderr.write("exe-os: tmux config already up to date\n");
|
|
1466
1602
|
}
|
|
1467
1603
|
} else {
|
|
1468
1604
|
copyFileSync(assetPath, exeTmuxConf);
|
|
1469
|
-
const newContent =
|
|
1605
|
+
const newContent = readFileSync6(assetPath, "utf8");
|
|
1470
1606
|
const newHash = createHash("sha256").update(newContent).digest("hex");
|
|
1471
|
-
|
|
1607
|
+
writeFileSync5(path7.join(exeDir, ".tmux.conf.shipped-hash"), newHash, "utf8");
|
|
1472
1608
|
}
|
|
1473
|
-
if (
|
|
1474
|
-
const existing =
|
|
1609
|
+
if (existsSync8(userTmuxConf)) {
|
|
1610
|
+
const existing = readFileSync6(userTmuxConf, "utf8");
|
|
1475
1611
|
if (!existing.includes(sourceLine)) {
|
|
1476
|
-
if (!
|
|
1612
|
+
if (!existsSync8(backupPath)) {
|
|
1477
1613
|
copyFileSync(userTmuxConf, backupPath);
|
|
1478
1614
|
process.stderr.write(`exe-os: backed up existing tmux config to ${backupPath}
|
|
1479
1615
|
`);
|
|
1480
1616
|
}
|
|
1481
|
-
|
|
1617
|
+
writeFileSync5(userTmuxConf, `${sourceLine}
|
|
1482
1618
|
${existing}`);
|
|
1483
1619
|
}
|
|
1484
1620
|
} else {
|
|
1485
|
-
|
|
1621
|
+
writeFileSync5(userTmuxConf, `# Exe OS tmux defaults \u2014 remove this line to use your own config
|
|
1486
1622
|
${sourceLine}
|
|
1487
1623
|
`);
|
|
1488
1624
|
}
|
|
@@ -1493,10 +1629,10 @@ ${sourceLine}
|
|
|
1493
1629
|
process.stderr.write("exe-os: tmux config installed\n");
|
|
1494
1630
|
}
|
|
1495
1631
|
function setupGhostty(home) {
|
|
1496
|
-
const homeDir = home ??
|
|
1497
|
-
const xdgConfig =
|
|
1498
|
-
const macConfig =
|
|
1499
|
-
const ghosttyInstalled =
|
|
1632
|
+
const homeDir = home ?? os6.homedir();
|
|
1633
|
+
const xdgConfig = path7.join(homeDir, ".config", "ghostty");
|
|
1634
|
+
const macConfig = path7.join(homeDir, "Library", "Application Support", "com.mitchellh.ghostty");
|
|
1635
|
+
const ghosttyInstalled = existsSync8(xdgConfig) || existsSync8(macConfig) || (() => {
|
|
1500
1636
|
try {
|
|
1501
1637
|
execSync2("which ghostty 2>/dev/null");
|
|
1502
1638
|
return true;
|
|
@@ -1508,48 +1644,48 @@ function setupGhostty(home) {
|
|
|
1508
1644
|
return;
|
|
1509
1645
|
}
|
|
1510
1646
|
const pkgRoot = resolvePackageRoot();
|
|
1511
|
-
const assetPath =
|
|
1512
|
-
if (!
|
|
1647
|
+
const assetPath = path7.join(pkgRoot, "dist", "assets", "ghostty.conf");
|
|
1648
|
+
if (!existsSync8(assetPath)) {
|
|
1513
1649
|
process.stderr.write("exe-os: ghostty.conf asset not found \u2014 skipping Ghostty setup\n");
|
|
1514
1650
|
return;
|
|
1515
1651
|
}
|
|
1516
1652
|
const configDir = xdgConfig;
|
|
1517
|
-
const configPath =
|
|
1518
|
-
const backupPath =
|
|
1519
|
-
|
|
1653
|
+
const configPath = path7.join(configDir, "config");
|
|
1654
|
+
const backupPath = path7.join(configDir, "config.backup");
|
|
1655
|
+
mkdirSync4(configDir, { recursive: true });
|
|
1520
1656
|
const START_MARKER = "# \u2500\u2500 exe-os:ghostty-start \u2500\u2500";
|
|
1521
1657
|
const END_MARKER = "# \u2500\u2500 exe-os:ghostty-end \u2500\u2500";
|
|
1522
|
-
const assetContent =
|
|
1658
|
+
const assetContent = readFileSync6(assetPath, "utf8").trim();
|
|
1523
1659
|
const markedSection = `${START_MARKER}
|
|
1524
1660
|
${assetContent}
|
|
1525
1661
|
${END_MARKER}`;
|
|
1526
|
-
if (
|
|
1527
|
-
const existing =
|
|
1662
|
+
if (existsSync8(configPath)) {
|
|
1663
|
+
const existing = readFileSync6(configPath, "utf8");
|
|
1528
1664
|
if (existing.includes(START_MARKER) && existing.includes(END_MARKER)) {
|
|
1529
1665
|
process.stderr.write("exe-os: Ghostty config already installed \u2014 preserving local settings\n");
|
|
1530
1666
|
return;
|
|
1531
1667
|
} else if (existing.includes("Exe OS")) {
|
|
1532
|
-
if (!
|
|
1668
|
+
if (!existsSync8(backupPath)) {
|
|
1533
1669
|
copyFileSync(configPath, backupPath);
|
|
1534
1670
|
process.stderr.write(`exe-os: backed up existing Ghostty config to ${backupPath}
|
|
1535
1671
|
`);
|
|
1536
1672
|
}
|
|
1537
|
-
|
|
1673
|
+
writeFileSync5(configPath, `${START_MARKER}
|
|
1538
1674
|
${existing.trim()}
|
|
1539
1675
|
${END_MARKER}
|
|
1540
1676
|
`);
|
|
1541
1677
|
} else {
|
|
1542
|
-
if (!
|
|
1678
|
+
if (!existsSync8(backupPath)) {
|
|
1543
1679
|
copyFileSync(configPath, backupPath);
|
|
1544
1680
|
process.stderr.write(`exe-os: backed up existing Ghostty config to ${backupPath}
|
|
1545
1681
|
`);
|
|
1546
1682
|
}
|
|
1547
|
-
|
|
1683
|
+
writeFileSync5(configPath, `${markedSection}
|
|
1548
1684
|
|
|
1549
1685
|
${existing}`);
|
|
1550
1686
|
}
|
|
1551
1687
|
} else {
|
|
1552
|
-
|
|
1688
|
+
writeFileSync5(configPath, `${markedSection}
|
|
1553
1689
|
`);
|
|
1554
1690
|
}
|
|
1555
1691
|
process.stderr.write("exe-os: Ghostty config installed\n");
|
|
@@ -1573,6 +1709,7 @@ var init_installer = __esm({
|
|
|
1573
1709
|
init_agent_symlinks();
|
|
1574
1710
|
init_mcp_prefix();
|
|
1575
1711
|
init_preferences();
|
|
1712
|
+
init_mcp_http_config();
|
|
1576
1713
|
init_runtime_hook_manifest();
|
|
1577
1714
|
EXE_SECTION_START = "<!-- exe-os:orchestration-start -->";
|
|
1578
1715
|
EXE_SECTION_END = "<!-- exe-os:orchestration-end -->";
|
|
@@ -1601,19 +1738,19 @@ __export(installer_exports, {
|
|
|
1601
1738
|
verifyCodexHooks: () => verifyCodexHooks
|
|
1602
1739
|
});
|
|
1603
1740
|
import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir4 } from "fs/promises";
|
|
1604
|
-
import { existsSync as
|
|
1605
|
-
import
|
|
1606
|
-
import
|
|
1607
|
-
async function mergeCodexHooks(packageRoot, homeDir =
|
|
1608
|
-
const codexDir =
|
|
1609
|
-
const hooksPath =
|
|
1610
|
-
const logsDir =
|
|
1611
|
-
const hookLogPath =
|
|
1741
|
+
import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
|
|
1742
|
+
import path9 from "path";
|
|
1743
|
+
import os7 from "os";
|
|
1744
|
+
async function mergeCodexHooks(packageRoot, homeDir = os7.homedir()) {
|
|
1745
|
+
const codexDir = path9.join(homeDir, ".codex");
|
|
1746
|
+
const hooksPath = path9.join(codexDir, "hooks.json");
|
|
1747
|
+
const logsDir = path9.join(homeDir, ".exe-os", "logs");
|
|
1748
|
+
const hookLogPath = path9.join(logsDir, "hooks.log");
|
|
1612
1749
|
const logSuffix = ` 2>> "${hookLogPath}"`;
|
|
1613
1750
|
await mkdir4(codexDir, { recursive: true });
|
|
1614
1751
|
await mkdir4(logsDir, { recursive: true });
|
|
1615
1752
|
let hooksJson = {};
|
|
1616
|
-
if (
|
|
1753
|
+
if (existsSync10(hooksPath)) {
|
|
1617
1754
|
try {
|
|
1618
1755
|
hooksJson = JSON.parse(await readFile4(hooksPath, "utf-8"));
|
|
1619
1756
|
} catch {
|
|
@@ -1624,19 +1761,6 @@ async function mergeCodexHooks(packageRoot, homeDir = os6.homedir()) {
|
|
|
1624
1761
|
hooksJson.hooks = {};
|
|
1625
1762
|
}
|
|
1626
1763
|
const hooksToRegister = [
|
|
1627
|
-
{
|
|
1628
|
-
event: "SessionStart",
|
|
1629
|
-
group: {
|
|
1630
|
-
hooks: [
|
|
1631
|
-
{
|
|
1632
|
-
type: "command",
|
|
1633
|
-
command: `node "${path8.join(packageRoot, "dist", "hooks", "session-start.js")}"${logSuffix}`,
|
|
1634
|
-
timeout: 30
|
|
1635
|
-
}
|
|
1636
|
-
]
|
|
1637
|
-
},
|
|
1638
|
-
marker: EXE_HOOKS.sessionStart
|
|
1639
|
-
},
|
|
1640
1764
|
{
|
|
1641
1765
|
event: "PostToolUse",
|
|
1642
1766
|
group: {
|
|
@@ -1646,7 +1770,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os6.homedir()) {
|
|
|
1646
1770
|
// Combined hook: runs ingest + error-recall in one Node process.
|
|
1647
1771
|
// Eliminates a cold-start cycle per tool call (~3-6s savings on Codex).
|
|
1648
1772
|
type: "command",
|
|
1649
|
-
command: `node "${
|
|
1773
|
+
command: `node "${path9.join(packageRoot, "dist", "hooks", "post-tool-combined.js")}"${logSuffix}`
|
|
1650
1774
|
}
|
|
1651
1775
|
]
|
|
1652
1776
|
},
|
|
@@ -1660,7 +1784,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os6.homedir()) {
|
|
|
1660
1784
|
// Single hook: prompt-submit handles memory retrieval + entity boost.
|
|
1661
1785
|
// exe-heartbeat-hook is CC-specific (intercom) — omitted on Codex.
|
|
1662
1786
|
type: "command",
|
|
1663
|
-
command: `node "${
|
|
1787
|
+
command: `node "${path9.join(packageRoot, "dist", "hooks", "prompt-submit.js")}"${logSuffix}`
|
|
1664
1788
|
}
|
|
1665
1789
|
]
|
|
1666
1790
|
},
|
|
@@ -1672,7 +1796,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os6.homedir()) {
|
|
|
1672
1796
|
hooks: [
|
|
1673
1797
|
{
|
|
1674
1798
|
type: "command",
|
|
1675
|
-
command: `node "${
|
|
1799
|
+
command: `node "${path9.join(packageRoot, "dist", "hooks", "stop.js")}"${logSuffix}`
|
|
1676
1800
|
}
|
|
1677
1801
|
]
|
|
1678
1802
|
},
|
|
@@ -1685,7 +1809,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os6.homedir()) {
|
|
|
1685
1809
|
hooks: [
|
|
1686
1810
|
{
|
|
1687
1811
|
type: "command",
|
|
1688
|
-
command: `node "${
|
|
1812
|
+
command: `node "${path9.join(packageRoot, "dist", "hooks", "pre-tool-use.js")}"${logSuffix}`
|
|
1689
1813
|
}
|
|
1690
1814
|
]
|
|
1691
1815
|
},
|
|
@@ -1694,6 +1818,16 @@ async function mergeCodexHooks(packageRoot, homeDir = os6.homedir()) {
|
|
|
1694
1818
|
];
|
|
1695
1819
|
let added = 0;
|
|
1696
1820
|
let skipped = 0;
|
|
1821
|
+
const sessionStartGroups = hooksJson.hooks["SessionStart"];
|
|
1822
|
+
if (Array.isArray(sessionStartGroups)) {
|
|
1823
|
+
hooksJson.hooks["SessionStart"] = sessionStartGroups.map((g) => ({
|
|
1824
|
+
...g,
|
|
1825
|
+
hooks: g.hooks.filter((h) => !h.command.includes(EXE_HOOKS.sessionStart))
|
|
1826
|
+
})).filter((g) => g.hooks.length > 0);
|
|
1827
|
+
if (hooksJson.hooks["SessionStart"].length === 0) {
|
|
1828
|
+
delete hooksJson.hooks["SessionStart"];
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1697
1831
|
const postToolGroups = hooksJson.hooks["PostToolUse"];
|
|
1698
1832
|
if (Array.isArray(postToolGroups)) {
|
|
1699
1833
|
hooksJson.hooks["PostToolUse"] = postToolGroups.map((g) => ({
|
|
@@ -1723,15 +1857,13 @@ async function mergeCodexHooks(packageRoot, homeDir = os6.homedir()) {
|
|
|
1723
1857
|
await writeFile4(hooksPath, JSON.stringify(hooksJson, null, 2) + "\n");
|
|
1724
1858
|
return { added, skipped };
|
|
1725
1859
|
}
|
|
1726
|
-
function verifyCodexHooks(homeDir =
|
|
1727
|
-
const hooksPath =
|
|
1728
|
-
if (!
|
|
1860
|
+
function verifyCodexHooks(homeDir = os7.homedir()) {
|
|
1861
|
+
const hooksPath = path9.join(homeDir, ".codex", "hooks.json");
|
|
1862
|
+
if (!existsSync10(hooksPath)) return false;
|
|
1729
1863
|
try {
|
|
1730
|
-
const hooksJson = JSON.parse(
|
|
1731
|
-
__require("fs").readFileSync(hooksPath, "utf-8")
|
|
1732
|
-
);
|
|
1864
|
+
const hooksJson = JSON.parse(readFileSync8(hooksPath, "utf-8"));
|
|
1733
1865
|
if (!hooksJson.hooks) return false;
|
|
1734
|
-
const required = ["
|
|
1866
|
+
const required = ["PostToolUse", "UserPromptSubmit", "Stop", "PreToolUse"];
|
|
1735
1867
|
for (const event of required) {
|
|
1736
1868
|
const groups = hooksJson.hooks[event];
|
|
1737
1869
|
if (!groups || !groups.some(
|
|
@@ -1749,19 +1881,23 @@ function verifyCodexHooks(homeDir = os6.homedir()) {
|
|
|
1749
1881
|
)) {
|
|
1750
1882
|
return false;
|
|
1751
1883
|
}
|
|
1884
|
+
const sessionStartCommands = (hooksJson.hooks.SessionStart ?? []).flatMap((g) => g.hooks.map((h) => h.command));
|
|
1885
|
+
if (sessionStartCommands.some((cmd) => cmd.includes(EXE_HOOKS.sessionStart))) {
|
|
1886
|
+
return false;
|
|
1887
|
+
}
|
|
1752
1888
|
return true;
|
|
1753
1889
|
} catch {
|
|
1754
1890
|
return false;
|
|
1755
1891
|
}
|
|
1756
1892
|
}
|
|
1757
|
-
async function installCodexStatusLine(homeDir =
|
|
1893
|
+
async function installCodexStatusLine(homeDir = os7.homedir()) {
|
|
1758
1894
|
const prefs = loadPreferences(homeDir);
|
|
1759
1895
|
if (prefs.codexStatusLine === false) return "opted-out";
|
|
1760
|
-
const codexDir =
|
|
1761
|
-
const configPath =
|
|
1896
|
+
const codexDir = path9.join(homeDir, ".codex");
|
|
1897
|
+
const configPath = path9.join(codexDir, "config.toml");
|
|
1762
1898
|
await mkdir4(codexDir, { recursive: true });
|
|
1763
1899
|
let content = "";
|
|
1764
|
-
if (
|
|
1900
|
+
if (existsSync10(configPath)) {
|
|
1765
1901
|
content = await readFile4(configPath, "utf-8");
|
|
1766
1902
|
if (/\[tui\][\s\S]*?status_line\s*=/.test(content)) {
|
|
1767
1903
|
return "already-configured";
|
|
@@ -1778,53 +1914,47 @@ status_line = [${DEFAULT_CODEX_STATUS_LINE.map((s) => `"${s}"`).join(", ")}]`;
|
|
|
1778
1914
|
await writeFile4(configPath, content);
|
|
1779
1915
|
return "installed";
|
|
1780
1916
|
}
|
|
1781
|
-
function
|
|
1917
|
+
function tomlString(value) {
|
|
1918
|
+
return JSON.stringify(value);
|
|
1919
|
+
}
|
|
1920
|
+
function codexHttpHeadersToml(homeDir) {
|
|
1921
|
+
const headers = buildMcpHttpHeaders(homeDir);
|
|
1922
|
+
return `{ ${Object.entries(headers).map(([key, value]) => `${tomlString(key)} = ${tomlString(value)}`).join(", ")} }`;
|
|
1923
|
+
}
|
|
1924
|
+
function desiredCodexMcpSection(homeDir) {
|
|
1782
1925
|
return [
|
|
1783
1926
|
"[mcp_servers.exe-os]",
|
|
1784
|
-
`
|
|
1785
|
-
`
|
|
1927
|
+
`url = ${tomlString(mcpHttpUrl())}`,
|
|
1928
|
+
`http_headers = ${codexHttpHeadersToml(homeDir)}`,
|
|
1786
1929
|
`startup_timeout_sec = ${CODEX_EXE_MCP_STARTUP_TIMEOUT_SEC}`,
|
|
1787
1930
|
`tool_timeout_sec = ${CODEX_EXE_MCP_TOOL_TIMEOUT_SEC}`,
|
|
1788
1931
|
""
|
|
1789
1932
|
].join("\n");
|
|
1790
1933
|
}
|
|
1791
|
-
function reconcileCodexMcpSection(sectionContent,
|
|
1934
|
+
function reconcileCodexMcpSection(sectionContent, homeDir) {
|
|
1792
1935
|
const desiredLines = {
|
|
1793
|
-
|
|
1794
|
-
|
|
1936
|
+
url: `url = ${tomlString(mcpHttpUrl())}`,
|
|
1937
|
+
headers: `http_headers = ${codexHttpHeadersToml(homeDir)}`,
|
|
1795
1938
|
startup: `startup_timeout_sec = ${CODEX_EXE_MCP_STARTUP_TIMEOUT_SEC}`,
|
|
1796
1939
|
tool: `tool_timeout_sec = ${CODEX_EXE_MCP_TOOL_TIMEOUT_SEC}`
|
|
1797
1940
|
};
|
|
1798
1941
|
let next = sectionContent;
|
|
1799
|
-
|
|
1800
|
-
const
|
|
1801
|
-
|
|
1802
|
-
const updated = next.replace(regex, desired);
|
|
1803
|
-
if (updated !== next) {
|
|
1804
|
-
next = updated;
|
|
1805
|
-
changed = true;
|
|
1806
|
-
}
|
|
1807
|
-
return;
|
|
1808
|
-
}
|
|
1809
|
-
next = next.endsWith("\n") ? `${next}${desired}
|
|
1942
|
+
next = next.split("\n").filter((line) => !/^(command|args|env|type|url|http_headers|startup_timeout_sec|tool_timeout_sec)\s*=/.test(line.trim())).join("\n").replace(/\n{3,}/g, "\n\n");
|
|
1943
|
+
for (const line of [desiredLines.url, desiredLines.headers, desiredLines.startup, desiredLines.tool]) {
|
|
1944
|
+
next = next.endsWith("\n") ? `${next}${line}
|
|
1810
1945
|
` : `${next}
|
|
1811
|
-
${
|
|
1946
|
+
${line}
|
|
1812
1947
|
`;
|
|
1813
|
-
|
|
1814
|
-
};
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
}
|
|
1821
|
-
async function registerCodexMcpServer(packageRoot, homeDir = os6.homedir()) {
|
|
1822
|
-
const codexDir = path8.join(homeDir, ".codex");
|
|
1823
|
-
const configPath = path8.join(codexDir, "config.toml");
|
|
1824
|
-
const serverJsPath = path8.join(packageRoot, "dist", "mcp", "server.js");
|
|
1948
|
+
}
|
|
1949
|
+
return { content: next, changed: next !== sectionContent };
|
|
1950
|
+
}
|
|
1951
|
+
async function registerCodexMcpServer(packageRoot, homeDir = os7.homedir()) {
|
|
1952
|
+
const codexDir = path9.join(homeDir, ".codex");
|
|
1953
|
+
const configPath = path9.join(codexDir, "config.toml");
|
|
1954
|
+
void packageRoot;
|
|
1825
1955
|
await mkdir4(codexDir, { recursive: true });
|
|
1826
1956
|
let content = "";
|
|
1827
|
-
if (
|
|
1957
|
+
if (existsSync10(configPath)) {
|
|
1828
1958
|
content = await readFile4(configPath, "utf-8");
|
|
1829
1959
|
}
|
|
1830
1960
|
const sectionHeader = "[mcp_servers.exe-os]";
|
|
@@ -1834,7 +1964,7 @@ async function registerCodexMcpServer(packageRoot, homeDir = os6.homedir()) {
|
|
|
1834
1964
|
const nextSectionMatch = afterHeader.match(/\n\[(?!mcp_servers\.exe-os)/);
|
|
1835
1965
|
const sectionEnd = nextSectionMatch ? headerIndex + sectionHeader.length + nextSectionMatch.index : content.length;
|
|
1836
1966
|
const sectionContent = content.slice(headerIndex, sectionEnd);
|
|
1837
|
-
const reconciled = reconcileCodexMcpSection(sectionContent,
|
|
1967
|
+
const reconciled = reconcileCodexMcpSection(sectionContent, homeDir);
|
|
1838
1968
|
if (!reconciled.changed) {
|
|
1839
1969
|
return "already-registered";
|
|
1840
1970
|
}
|
|
@@ -1842,17 +1972,17 @@ async function registerCodexMcpServer(packageRoot, homeDir = os6.homedir()) {
|
|
|
1842
1972
|
await writeFile4(configPath, content);
|
|
1843
1973
|
return "updated";
|
|
1844
1974
|
}
|
|
1845
|
-
const newSection = desiredCodexMcpSection(
|
|
1975
|
+
const newSection = desiredCodexMcpSection(homeDir);
|
|
1846
1976
|
const separator = content.length > 0 && !content.endsWith("\n") ? "\n\n" : content.length > 0 ? "\n" : "";
|
|
1847
1977
|
content = content + separator + newSection;
|
|
1848
1978
|
await writeFile4(configPath, content);
|
|
1849
1979
|
return "registered";
|
|
1850
1980
|
}
|
|
1851
|
-
async function ensureCodexHooksFeature(homeDir =
|
|
1852
|
-
const configPath =
|
|
1853
|
-
await mkdir4(
|
|
1981
|
+
async function ensureCodexHooksFeature(homeDir = os7.homedir()) {
|
|
1982
|
+
const configPath = path9.join(homeDir, ".codex", "config.toml");
|
|
1983
|
+
await mkdir4(path9.join(homeDir, ".codex"), { recursive: true });
|
|
1854
1984
|
let content = "";
|
|
1855
|
-
if (
|
|
1985
|
+
if (existsSync10(configPath)) {
|
|
1856
1986
|
content = await readFile4(configPath, "utf-8");
|
|
1857
1987
|
}
|
|
1858
1988
|
if (/\[features\][\s\S]*?codex_hooks\s*=\s*true/.test(content)) {
|
|
@@ -1907,6 +2037,7 @@ var init_installer2 = __esm({
|
|
|
1907
2037
|
init_installer();
|
|
1908
2038
|
init_preferences();
|
|
1909
2039
|
init_runtime_hook_manifest();
|
|
2040
|
+
init_mcp_http_config();
|
|
1910
2041
|
DEFAULT_CODEX_STATUS_LINE = [
|
|
1911
2042
|
"model-with-reasoning",
|
|
1912
2043
|
"current-dir",
|
|
@@ -1922,44 +2053,44 @@ var init_installer2 = __esm({
|
|
|
1922
2053
|
|
|
1923
2054
|
// src/bin/install.ts
|
|
1924
2055
|
init_installer();
|
|
1925
|
-
import { existsSync as
|
|
2056
|
+
import { existsSync as existsSync11, readFileSync as readFileSync9, writeFileSync as writeFileSync7, unlinkSync as unlinkSync3, readdirSync as readdirSync2, openSync, closeSync } from "fs";
|
|
1926
2057
|
import { spawn, execSync as execSync3 } from "child_process";
|
|
1927
|
-
import
|
|
1928
|
-
import
|
|
2058
|
+
import path10 from "path";
|
|
2059
|
+
import os8 from "os";
|
|
1929
2060
|
|
|
1930
2061
|
// src/lib/session-wrappers.ts
|
|
1931
2062
|
import {
|
|
1932
|
-
existsSync as
|
|
1933
|
-
readFileSync as
|
|
1934
|
-
writeFileSync as
|
|
1935
|
-
mkdirSync as
|
|
2063
|
+
existsSync as existsSync9,
|
|
2064
|
+
readFileSync as readFileSync7,
|
|
2065
|
+
writeFileSync as writeFileSync6,
|
|
2066
|
+
mkdirSync as mkdirSync5,
|
|
1936
2067
|
chmodSync as chmodSync3,
|
|
1937
2068
|
readdirSync,
|
|
1938
2069
|
unlinkSync as unlinkSync2
|
|
1939
2070
|
} from "fs";
|
|
1940
|
-
import
|
|
2071
|
+
import path8 from "path";
|
|
1941
2072
|
import { homedir } from "os";
|
|
1942
2073
|
var MAX_N = 9;
|
|
1943
2074
|
function generateSessionWrappers(packageRoot, homeDir) {
|
|
1944
2075
|
const home = homeDir ?? homedir();
|
|
1945
|
-
const binDir =
|
|
1946
|
-
const rosterPath =
|
|
1947
|
-
|
|
1948
|
-
const exeStartDst =
|
|
2076
|
+
const binDir = path8.join(home, ".exe-os", "bin");
|
|
2077
|
+
const rosterPath = path8.join(home, ".exe-os", "exe-employees.json");
|
|
2078
|
+
mkdirSync5(binDir, { recursive: true });
|
|
2079
|
+
const exeStartDst = path8.join(binDir, "exe-start");
|
|
1949
2080
|
const candidates = [
|
|
1950
|
-
|
|
1951
|
-
|
|
2081
|
+
path8.join(packageRoot, "dist", "bin", "exe-start.sh"),
|
|
2082
|
+
path8.join(packageRoot, "src", "bin", "exe-start.sh")
|
|
1952
2083
|
];
|
|
1953
2084
|
for (const src of candidates) {
|
|
1954
|
-
if (
|
|
1955
|
-
|
|
2085
|
+
if (existsSync9(src)) {
|
|
2086
|
+
writeFileSync6(exeStartDst, readFileSync7(src));
|
|
1956
2087
|
chmodSync3(exeStartDst, 493);
|
|
1957
2088
|
break;
|
|
1958
2089
|
}
|
|
1959
2090
|
}
|
|
1960
2091
|
let employees = [];
|
|
1961
2092
|
try {
|
|
1962
|
-
employees = JSON.parse(
|
|
2093
|
+
employees = JSON.parse(readFileSync7(rosterPath, "utf8"));
|
|
1963
2094
|
} catch {
|
|
1964
2095
|
return { created: 0, pathConfigured: false };
|
|
1965
2096
|
}
|
|
@@ -1969,9 +2100,9 @@ function generateSessionWrappers(packageRoot, homeDir) {
|
|
|
1969
2100
|
try {
|
|
1970
2101
|
for (const f of readdirSync(binDir)) {
|
|
1971
2102
|
if (f === "exe-start") continue;
|
|
1972
|
-
const fPath =
|
|
2103
|
+
const fPath = path8.join(binDir, f);
|
|
1973
2104
|
try {
|
|
1974
|
-
const content =
|
|
2105
|
+
const content = readFileSync7(fPath, "utf8");
|
|
1975
2106
|
if (content.includes("exe-start")) {
|
|
1976
2107
|
unlinkSync2(fPath);
|
|
1977
2108
|
}
|
|
@@ -1986,34 +2117,34 @@ exec "${exeStartDst}" "$0" "$@"
|
|
|
1986
2117
|
`;
|
|
1987
2118
|
for (const emp of employees) {
|
|
1988
2119
|
for (let n = 1; n <= MAX_N; n++) {
|
|
1989
|
-
const wrapperPath =
|
|
1990
|
-
|
|
2120
|
+
const wrapperPath = path8.join(binDir, `${emp.name}${n}`);
|
|
2121
|
+
writeFileSync6(wrapperPath, wrapperContent);
|
|
1991
2122
|
chmodSync3(wrapperPath, 493);
|
|
1992
2123
|
created++;
|
|
1993
|
-
const codexPath =
|
|
1994
|
-
|
|
2124
|
+
const codexPath = path8.join(binDir, `${emp.name}${n}-codex`);
|
|
2125
|
+
writeFileSync6(codexPath, wrapperContent);
|
|
1995
2126
|
chmodSync3(codexPath, 493);
|
|
1996
2127
|
created++;
|
|
1997
2128
|
}
|
|
1998
2129
|
}
|
|
1999
2130
|
const codexLauncherCandidates = [
|
|
2000
|
-
|
|
2001
|
-
|
|
2131
|
+
path8.join(packageRoot, "dist", "bin", "exe-start-codex.js"),
|
|
2132
|
+
path8.join(packageRoot, "src", "bin", "exe-start-codex.ts")
|
|
2002
2133
|
];
|
|
2003
2134
|
let codexLauncher = null;
|
|
2004
2135
|
for (const c of codexLauncherCandidates) {
|
|
2005
|
-
if (
|
|
2136
|
+
if (existsSync9(c)) {
|
|
2006
2137
|
codexLauncher = c;
|
|
2007
2138
|
break;
|
|
2008
2139
|
}
|
|
2009
2140
|
}
|
|
2010
2141
|
if (codexLauncher) {
|
|
2011
2142
|
for (const emp of employees) {
|
|
2012
|
-
const wrapperPath =
|
|
2143
|
+
const wrapperPath = path8.join(binDir, `${emp.name}-codex`);
|
|
2013
2144
|
const content = `#!/bin/bash
|
|
2014
2145
|
exec node "${codexLauncher}" --agent ${emp.name} "$@"
|
|
2015
2146
|
`;
|
|
2016
|
-
|
|
2147
|
+
writeFileSync6(wrapperPath, content);
|
|
2017
2148
|
chmodSync3(wrapperPath, 493);
|
|
2018
2149
|
created++;
|
|
2019
2150
|
}
|
|
@@ -2032,24 +2163,24 @@ export PATH="${binDir}:$PATH"
|
|
|
2032
2163
|
const shell = process.env.SHELL ?? "/bin/bash";
|
|
2033
2164
|
const profilePaths = [];
|
|
2034
2165
|
if (shell.includes("zsh")) {
|
|
2035
|
-
profilePaths.push(
|
|
2166
|
+
profilePaths.push(path8.join(home, ".zshrc"));
|
|
2036
2167
|
} else if (shell.includes("bash")) {
|
|
2037
|
-
profilePaths.push(
|
|
2038
|
-
profilePaths.push(
|
|
2168
|
+
profilePaths.push(path8.join(home, ".bashrc"));
|
|
2169
|
+
profilePaths.push(path8.join(home, ".bash_profile"));
|
|
2039
2170
|
} else {
|
|
2040
|
-
profilePaths.push(
|
|
2171
|
+
profilePaths.push(path8.join(home, ".profile"));
|
|
2041
2172
|
}
|
|
2042
2173
|
for (const profilePath of profilePaths) {
|
|
2043
2174
|
try {
|
|
2044
2175
|
let content = "";
|
|
2045
2176
|
try {
|
|
2046
|
-
content =
|
|
2177
|
+
content = readFileSync7(profilePath, "utf8");
|
|
2047
2178
|
} catch {
|
|
2048
2179
|
}
|
|
2049
2180
|
if (content.includes(".exe-os/bin")) {
|
|
2050
2181
|
return false;
|
|
2051
2182
|
}
|
|
2052
|
-
|
|
2183
|
+
writeFileSync6(profilePath, content + exportLine);
|
|
2053
2184
|
return true;
|
|
2054
2185
|
} catch {
|
|
2055
2186
|
continue;
|
|
@@ -2059,14 +2190,14 @@ export PATH="${binDir}:$PATH"
|
|
|
2059
2190
|
}
|
|
2060
2191
|
|
|
2061
2192
|
// src/bin/install.ts
|
|
2062
|
-
var homedir2 =
|
|
2063
|
-
var EXE_DIR =
|
|
2193
|
+
var homedir2 = os8.homedir;
|
|
2194
|
+
var EXE_DIR = path10.join(homedir2(), ".exe-os");
|
|
2064
2195
|
function restartDaemon() {
|
|
2065
|
-
const pidPath =
|
|
2066
|
-
const sockPath =
|
|
2196
|
+
const pidPath = path10.join(EXE_DIR, "exed.pid");
|
|
2197
|
+
const sockPath = path10.join(EXE_DIR, "exed.sock");
|
|
2067
2198
|
try {
|
|
2068
|
-
if (
|
|
2069
|
-
const pid = parseInt(
|
|
2199
|
+
if (existsSync11(pidPath)) {
|
|
2200
|
+
const pid = parseInt(readFileSync9(pidPath, "utf8").trim(), 10);
|
|
2070
2201
|
if (!isNaN(pid) && pid > 0) {
|
|
2071
2202
|
try {
|
|
2072
2203
|
process.kill(pid, "SIGKILL");
|
|
@@ -2107,18 +2238,18 @@ function restartDaemon() {
|
|
|
2107
2238
|
} catch {
|
|
2108
2239
|
}
|
|
2109
2240
|
try {
|
|
2110
|
-
const versionPath =
|
|
2111
|
-
|
|
2241
|
+
const versionPath = path10.join(EXE_DIR, "mcp-version");
|
|
2242
|
+
writeFileSync7(versionPath, `deploy-${Date.now()}`);
|
|
2112
2243
|
process.stderr.write(`exe-os: MCP version marker updated \u2014 servers will hot-reload within 10s
|
|
2113
2244
|
`);
|
|
2114
2245
|
} catch {
|
|
2115
2246
|
}
|
|
2116
2247
|
try {
|
|
2117
|
-
const wpDir =
|
|
2118
|
-
if (
|
|
2248
|
+
const wpDir = path10.join(EXE_DIR, "worker-pids");
|
|
2249
|
+
if (existsSync11(wpDir)) {
|
|
2119
2250
|
for (const f of readdirSync2(wpDir)) {
|
|
2120
2251
|
try {
|
|
2121
|
-
unlinkSync3(
|
|
2252
|
+
unlinkSync3(path10.join(wpDir, f));
|
|
2122
2253
|
} catch {
|
|
2123
2254
|
}
|
|
2124
2255
|
}
|
|
@@ -2135,7 +2266,7 @@ function restartDaemon() {
|
|
|
2135
2266
|
}
|
|
2136
2267
|
} catch {
|
|
2137
2268
|
}
|
|
2138
|
-
const totalGB =
|
|
2269
|
+
const totalGB = os8.totalmem() / (1024 * 1024 * 1024);
|
|
2139
2270
|
if (totalGB <= 8) {
|
|
2140
2271
|
process.stderr.write(
|
|
2141
2272
|
`exe-os: ${totalGB.toFixed(0)}GB system \u2014 skipping daemon spawn (keyword search mode)
|
|
@@ -2145,13 +2276,13 @@ function restartDaemon() {
|
|
|
2145
2276
|
}
|
|
2146
2277
|
try {
|
|
2147
2278
|
const pkgRoot = resolvePackageRoot();
|
|
2148
|
-
const daemonPath =
|
|
2149
|
-
if (!
|
|
2279
|
+
const daemonPath = path10.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
2280
|
+
if (!existsSync11(daemonPath)) {
|
|
2150
2281
|
process.stderr.write(`exe-os: daemon not found at ${daemonPath} \u2014 skipping respawn
|
|
2151
2282
|
`);
|
|
2152
2283
|
return;
|
|
2153
2284
|
}
|
|
2154
|
-
const logPath =
|
|
2285
|
+
const logPath = path10.join(EXE_DIR, "exed.log");
|
|
2155
2286
|
let stderrFd = "ignore";
|
|
2156
2287
|
try {
|
|
2157
2288
|
stderrFd = openSync(logPath, "a");
|