@askexenow/exe-os 0.9.66 → 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} +1523 -2275
- package/dist/bin/backfill-conversations.js +506 -20
- package/dist/bin/backfill-responses.js +506 -20
- package/dist/bin/backfill-vectors.js +233 -20
- package/dist/bin/bulk-sync-postgres.js +4876 -0
- package/dist/bin/cleanup-stale-review-tasks.js +507 -21
- package/dist/bin/cli.js +2450 -1530
- package/dist/bin/exe-assign.js +506 -20
- package/dist/bin/exe-boot.js +410 -60
- package/dist/bin/exe-cloud.js +795 -105
- package/dist/bin/exe-dispatch.js +516 -22
- package/dist/bin/exe-doctor.js +587 -30
- package/dist/bin/exe-export-behaviors.js +518 -24
- package/dist/bin/exe-forget.js +507 -21
- package/dist/bin/exe-gateway.js +571 -25
- package/dist/bin/exe-heartbeat.js +518 -24
- package/dist/bin/exe-kill.js +507 -21
- package/dist/bin/exe-launch-agent.js +2312 -1069
- package/dist/bin/exe-new-employee.js +197 -165
- package/dist/bin/exe-pending-messages.js +507 -21
- package/dist/bin/exe-pending-notifications.js +507 -21
- package/dist/bin/exe-pending-reviews.js +507 -21
- package/dist/bin/exe-rename.js +507 -21
- package/dist/bin/exe-review.js +507 -21
- package/dist/bin/exe-search.js +518 -24
- package/dist/bin/exe-session-cleanup.js +516 -22
- package/dist/bin/exe-settings.js +4 -0
- package/dist/bin/exe-start-codex.js +682 -143
- package/dist/bin/exe-start-opencode.js +627 -79
- package/dist/bin/exe-status.js +507 -21
- package/dist/bin/exe-team.js +507 -21
- package/dist/bin/git-sweep.js +516 -22
- package/dist/bin/graph-backfill.js +558 -21
- package/dist/bin/graph-export.js +507 -21
- package/dist/bin/graph-layer-benchmark.js +109 -0
- package/dist/bin/install.js +305 -288
- package/dist/bin/intercom-check.js +516 -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 +516 -22
- package/dist/bin/setup.js +412 -62
- package/dist/bin/shard-migrate.js +506 -20
- package/dist/gateway/index.js +569 -23
- package/dist/hooks/bug-report-worker.js +519 -25
- package/dist/hooks/codex-stop-task-finalizer.js +516 -22
- package/dist/hooks/commit-complete.js +516 -22
- package/dist/hooks/error-recall.js +518 -24
- package/dist/hooks/ingest.js +516 -22
- package/dist/hooks/instructions-loaded.js +507 -21
- package/dist/hooks/notification.js +507 -21
- package/dist/hooks/post-compact.js +507 -21
- package/dist/hooks/post-tool-combined.js +519 -25
- package/dist/hooks/pre-compact.js +516 -22
- package/dist/hooks/pre-tool-use.js +507 -21
- package/dist/hooks/prompt-submit.js +519 -25
- package/dist/hooks/session-end.js +516 -22
- package/dist/hooks/session-start.js +520 -26
- package/dist/hooks/stop.js +517 -23
- package/dist/hooks/subagent-stop.js +507 -21
- package/dist/hooks/summary-worker.js +411 -61
- package/dist/index.js +569 -23
- package/dist/lib/cloud-sync.js +391 -53
- package/dist/lib/config.js +13 -1
- package/dist/lib/consolidation.js +1 -1
- package/dist/lib/database.js +124 -0
- package/dist/lib/db.js +124 -0
- package/dist/lib/device-registry.js +124 -0
- package/dist/lib/embedder.js +13 -1
- package/dist/lib/exe-daemon.js +2184 -561
- package/dist/lib/hybrid-search.js +518 -24
- package/dist/lib/identity.js +3 -0
- package/dist/lib/keychain.js +178 -22
- package/dist/lib/messaging.js +3 -0
- package/dist/lib/reminders.js +3 -0
- package/dist/lib/schedules.js +233 -20
- package/dist/lib/skill-learning.js +16 -1
- package/dist/lib/store.js +506 -20
- package/dist/lib/tasks.js +16 -1
- package/dist/lib/tmux-routing.js +16 -1
- package/dist/lib/token-spend.js +3 -0
- package/dist/mcp/server.js +1757 -428
- package/dist/mcp/tools/complete-reminder.js +3 -0
- package/dist/mcp/tools/create-reminder.js +3 -0
- package/dist/mcp/tools/create-task.js +16 -1
- package/dist/mcp/tools/deactivate-behavior.js +3 -0
- package/dist/mcp/tools/list-reminders.js +3 -0
- package/dist/mcp/tools/list-tasks.js +3 -0
- package/dist/mcp/tools/send-message.js +3 -0
- package/dist/mcp/tools/update-task.js +16 -1
- package/dist/runtime/index.js +516 -22
- package/dist/tui/App.js +594 -29
- package/package.json +8 -5
- package/src/commands/exe/cloud.md +6 -10
- package/stack.release.json +3 -3
- package/src/commands/exe/link.md +0 -18
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
|
};
|
|
@@ -612,6 +606,63 @@ var init_preferences = __esm({
|
|
|
612
606
|
}
|
|
613
607
|
});
|
|
614
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
|
+
|
|
615
666
|
// src/adapters/runtime-hook-manifest.ts
|
|
616
667
|
function commandHasAnyMarker(command, markers) {
|
|
617
668
|
return markers.some((marker) => command.includes(marker));
|
|
@@ -756,54 +807,58 @@ var init_runtime_hook_manifest = __esm({
|
|
|
756
807
|
});
|
|
757
808
|
|
|
758
809
|
// src/adapters/claude/installer.ts
|
|
759
|
-
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3, readdir } from "fs/promises";
|
|
760
|
-
import { existsSync as
|
|
761
|
-
import { createHash
|
|
762
|
-
import
|
|
763
|
-
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";
|
|
764
815
|
import { execSync as execSync2 } from "child_process";
|
|
765
816
|
import { fileURLToPath } from "url";
|
|
766
817
|
function resolvePackageRoot() {
|
|
767
818
|
const thisFile = fileURLToPath(import.meta.url);
|
|
768
|
-
let dir =
|
|
769
|
-
const root =
|
|
819
|
+
let dir = path7.dirname(thisFile);
|
|
820
|
+
const root = path7.parse(dir).root;
|
|
770
821
|
while (dir !== root) {
|
|
771
|
-
const pkgPath =
|
|
772
|
-
if (
|
|
822
|
+
const pkgPath = path7.join(dir, "package.json");
|
|
823
|
+
if (existsSync8(pkgPath)) {
|
|
773
824
|
try {
|
|
774
|
-
const pkg = JSON.parse(
|
|
825
|
+
const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
|
|
775
826
|
if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
|
|
776
827
|
} catch {
|
|
777
828
|
}
|
|
778
829
|
}
|
|
779
|
-
dir =
|
|
830
|
+
dir = path7.dirname(dir);
|
|
780
831
|
}
|
|
781
|
-
return
|
|
832
|
+
return path7.resolve(path7.dirname(thisFile), "..", "..", "..");
|
|
782
833
|
}
|
|
783
|
-
async function copySlashCommands(packageRoot, homeDir =
|
|
834
|
+
async function copySlashCommands(packageRoot, homeDir = os6.homedir()) {
|
|
784
835
|
let copied = 0;
|
|
785
836
|
let skipped = 0;
|
|
786
|
-
const skillsBase =
|
|
787
|
-
const
|
|
788
|
-
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)) {
|
|
789
844
|
const entries = await readdir(exeDir);
|
|
790
845
|
const mdFiles = entries.filter((f) => f.endsWith(".md"));
|
|
791
846
|
for (const file of mdFiles) {
|
|
792
847
|
const name = file.replace(".md", "");
|
|
793
|
-
const destDir =
|
|
848
|
+
const destDir = path7.join(skillsBase, `exe-${name}`);
|
|
794
849
|
await mkdir3(destDir, { recursive: true });
|
|
795
|
-
const srcPath =
|
|
796
|
-
const destPath =
|
|
850
|
+
const srcPath = path7.join(exeDir, file);
|
|
851
|
+
const destPath = path7.join(destDir, "SKILL.md");
|
|
797
852
|
const result = await copyAsSkill(srcPath, destPath, `exe-${name}`);
|
|
798
853
|
if (result) copied++;
|
|
799
854
|
else skipped++;
|
|
800
855
|
}
|
|
801
856
|
}
|
|
802
|
-
const topLevelSrc =
|
|
803
|
-
if (
|
|
804
|
-
const destDir =
|
|
857
|
+
const topLevelSrc = path7.join(packageRoot, "src", "commands", "exe.md");
|
|
858
|
+
if (existsSync8(topLevelSrc)) {
|
|
859
|
+
const destDir = path7.join(skillsBase, "exe");
|
|
805
860
|
await mkdir3(destDir, { recursive: true });
|
|
806
|
-
const destPath =
|
|
861
|
+
const destPath = path7.join(destDir, "SKILL.md");
|
|
807
862
|
const result = await copyAsSkill(topLevelSrc, destPath, "exe");
|
|
808
863
|
if (result) copied++;
|
|
809
864
|
else skipped++;
|
|
@@ -826,7 +881,7 @@ name: ${skillName}
|
|
|
826
881
|
`);
|
|
827
882
|
}
|
|
828
883
|
}
|
|
829
|
-
if (
|
|
884
|
+
if (existsSync8(destPath)) {
|
|
830
885
|
const existing = await readFile3(destPath, "utf-8");
|
|
831
886
|
if (existing === content) return false;
|
|
832
887
|
}
|
|
@@ -835,32 +890,32 @@ name: ${skillName}
|
|
|
835
890
|
}
|
|
836
891
|
function readJsonFile(filePath) {
|
|
837
892
|
try {
|
|
838
|
-
return JSON.parse(
|
|
893
|
+
return JSON.parse(readFileSync6(filePath, "utf-8"));
|
|
839
894
|
} catch {
|
|
840
895
|
return null;
|
|
841
896
|
}
|
|
842
897
|
}
|
|
843
898
|
function findAncestorMcpJsons(startDir, homeDir) {
|
|
844
899
|
const files = [];
|
|
845
|
-
let dir =
|
|
846
|
-
const root =
|
|
847
|
-
const stop =
|
|
900
|
+
let dir = path7.resolve(startDir);
|
|
901
|
+
const root = path7.parse(dir).root;
|
|
902
|
+
const stop = path7.resolve(homeDir);
|
|
848
903
|
while (dir !== root) {
|
|
849
|
-
const candidate =
|
|
850
|
-
if (
|
|
904
|
+
const candidate = path7.join(dir, ".mcp.json");
|
|
905
|
+
if (existsSync8(candidate)) files.push(candidate);
|
|
851
906
|
if (dir === stop) break;
|
|
852
|
-
dir =
|
|
907
|
+
dir = path7.dirname(dir);
|
|
853
908
|
}
|
|
854
909
|
return files;
|
|
855
910
|
}
|
|
856
911
|
function pathApplies(projectPath, cwd) {
|
|
857
|
-
const project =
|
|
858
|
-
const current =
|
|
859
|
-
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);
|
|
860
915
|
}
|
|
861
|
-
function detectMcpNameCollisions(homeDir =
|
|
862
|
-
const claudeJsonPath =
|
|
863
|
-
if (!
|
|
916
|
+
function detectMcpNameCollisions(homeDir = os6.homedir(), cwd = process.cwd()) {
|
|
917
|
+
const claudeJsonPath = path7.join(homeDir, ".claude.json");
|
|
918
|
+
if (!existsSync8(claudeJsonPath)) return [];
|
|
864
919
|
const claudeJson = readJsonFile(claudeJsonPath);
|
|
865
920
|
if (!claudeJson?.projects) return [];
|
|
866
921
|
const collisions = [];
|
|
@@ -887,44 +942,11 @@ function detectMcpNameCollisions(homeDir = os5.homedir(), cwd = process.cwd()) {
|
|
|
887
942
|
}
|
|
888
943
|
return collisions;
|
|
889
944
|
}
|
|
890
|
-
function readOrCreateDaemonToken(homeDir) {
|
|
891
|
-
const exeDir = path6.join(homeDir, ".exe-os");
|
|
892
|
-
const tokenPath = path6.join(exeDir, "exed.token");
|
|
893
|
-
try {
|
|
894
|
-
if (existsSync7(tokenPath)) {
|
|
895
|
-
const token2 = readFileSync5(tokenPath, "utf-8").trim();
|
|
896
|
-
if (token2) return token2;
|
|
897
|
-
}
|
|
898
|
-
} catch {
|
|
899
|
-
}
|
|
900
|
-
const token = randomBytes(32).toString("hex");
|
|
901
|
-
mkdirSync3(exeDir, { recursive: true });
|
|
902
|
-
writeFileSync4(tokenPath, `${token}
|
|
903
|
-
`, "utf-8");
|
|
904
|
-
try {
|
|
905
|
-
chmodSync2(tokenPath, 384);
|
|
906
|
-
} catch {
|
|
907
|
-
}
|
|
908
|
-
return token;
|
|
909
|
-
}
|
|
910
|
-
function buildHttpMcpEntry(homeDir) {
|
|
911
|
-
const port = process.env.EXE_MCP_PORT || "48739";
|
|
912
|
-
const token = readOrCreateDaemonToken(homeDir);
|
|
913
|
-
return {
|
|
914
|
-
type: "http",
|
|
915
|
-
url: `http://127.0.0.1:${port}/mcp`,
|
|
916
|
-
headers: {
|
|
917
|
-
Authorization: `Bearer ${token}`,
|
|
918
|
-
"X-Agent-Id": "${AGENT_ID:-exe}",
|
|
919
|
-
"X-Agent-Role": "${AGENT_ROLE:-COO}"
|
|
920
|
-
}
|
|
921
|
-
};
|
|
922
|
-
}
|
|
923
945
|
function buildStdioMcpEntry(packageRoot) {
|
|
924
946
|
return {
|
|
925
947
|
type: "stdio",
|
|
926
948
|
command: "node",
|
|
927
|
-
args: [
|
|
949
|
+
args: [path7.join(packageRoot, "dist", "mcp", "server.js")],
|
|
928
950
|
env: {}
|
|
929
951
|
};
|
|
930
952
|
}
|
|
@@ -932,14 +954,14 @@ function mcpTransportMode() {
|
|
|
932
954
|
const value = process.env.EXE_OS_MCP_TRANSPORT?.trim().toLowerCase();
|
|
933
955
|
if (value === "stdio") return "stdio";
|
|
934
956
|
if (value === "http") return "http";
|
|
935
|
-
const totalGB =
|
|
957
|
+
const totalGB = os6.totalmem() / (1024 * 1024 * 1024);
|
|
936
958
|
if (totalGB <= 8) return "stdio";
|
|
937
959
|
return "http";
|
|
938
960
|
}
|
|
939
|
-
async function registerMcpServer(packageRoot, homeDir =
|
|
940
|
-
const claudeJsonPath =
|
|
961
|
+
async function registerMcpServer(packageRoot, homeDir = os6.homedir()) {
|
|
962
|
+
const claudeJsonPath = path7.join(homeDir, ".claude.json");
|
|
941
963
|
let claudeJson = {};
|
|
942
|
-
if (
|
|
964
|
+
if (existsSync8(claudeJsonPath)) {
|
|
943
965
|
try {
|
|
944
966
|
claudeJson = JSON.parse(await readFile3(claudeJsonPath, "utf-8"));
|
|
945
967
|
} catch {
|
|
@@ -949,7 +971,7 @@ async function registerMcpServer(packageRoot, homeDir = os5.homedir()) {
|
|
|
949
971
|
if (!claudeJson.mcpServers) {
|
|
950
972
|
claudeJson.mcpServers = {};
|
|
951
973
|
}
|
|
952
|
-
const newEntry = mcpTransportMode() === "stdio" ? buildStdioMcpEntry(packageRoot) :
|
|
974
|
+
const newEntry = mcpTransportMode() === "stdio" ? buildStdioMcpEntry(packageRoot) : buildClaudeHttpMcpEntry(homeDir);
|
|
953
975
|
if (claudeJson.mcpServers[MCP_LEGACY_KEY]) {
|
|
954
976
|
delete claudeJson.mcpServers[MCP_LEGACY_KEY];
|
|
955
977
|
process.stderr.write("exe-os: migrated MCP server key exe-mem \u2192 exe-os\n");
|
|
@@ -957,8 +979,8 @@ async function registerMcpServer(packageRoot, homeDir = os5.homedir()) {
|
|
|
957
979
|
const currentOs = claudeJson.mcpServers[MCP_PRIMARY_KEY];
|
|
958
980
|
const osMatches = currentOs && JSON.stringify(currentOs) === JSON.stringify(newEntry);
|
|
959
981
|
if (osMatches) {
|
|
960
|
-
await cleanSettingsJsonMcp(
|
|
961
|
-
await migratePermissionsToExeOs(
|
|
982
|
+
await cleanSettingsJsonMcp(path7.join(homeDir, ".claude", "settings.json"));
|
|
983
|
+
await migratePermissionsToExeOs(path7.join(homeDir, ".claude", "settings.json"));
|
|
962
984
|
const collisions2 = detectMcpNameCollisions(homeDir, packageRoot).filter((c) => c.serverName === MCP_PRIMARY_KEY || c.serverName === MCP_LEGACY_KEY);
|
|
963
985
|
for (const c of collisions2) {
|
|
964
986
|
process.stderr.write(
|
|
@@ -970,8 +992,8 @@ async function registerMcpServer(packageRoot, homeDir = os5.homedir()) {
|
|
|
970
992
|
}
|
|
971
993
|
claudeJson.mcpServers[MCP_PRIMARY_KEY] = newEntry;
|
|
972
994
|
await writeFile3(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
973
|
-
await cleanSettingsJsonMcp(
|
|
974
|
-
await migratePermissionsToExeOs(
|
|
995
|
+
await cleanSettingsJsonMcp(path7.join(homeDir, ".claude", "settings.json"));
|
|
996
|
+
await migratePermissionsToExeOs(path7.join(homeDir, ".claude", "settings.json"));
|
|
975
997
|
const collisions = detectMcpNameCollisions(homeDir, packageRoot).filter((c) => c.serverName === MCP_PRIMARY_KEY || c.serverName === MCP_LEGACY_KEY);
|
|
976
998
|
for (const c of collisions) {
|
|
977
999
|
process.stderr.write(
|
|
@@ -982,7 +1004,7 @@ async function registerMcpServer(packageRoot, homeDir = os5.homedir()) {
|
|
|
982
1004
|
return true;
|
|
983
1005
|
}
|
|
984
1006
|
async function cleanSettingsJsonMcp(settingsPath) {
|
|
985
|
-
if (!
|
|
1007
|
+
if (!existsSync8(settingsPath)) return;
|
|
986
1008
|
try {
|
|
987
1009
|
const settings = JSON.parse(await readFile3(settingsPath, "utf-8"));
|
|
988
1010
|
const servers = settings.mcpServers;
|
|
@@ -1003,7 +1025,7 @@ async function cleanSettingsJsonMcp(settingsPath) {
|
|
|
1003
1025
|
}
|
|
1004
1026
|
}
|
|
1005
1027
|
async function migratePermissionsToExeOs(settingsPath) {
|
|
1006
|
-
if (!
|
|
1028
|
+
if (!existsSync8(settingsPath)) return;
|
|
1007
1029
|
try {
|
|
1008
1030
|
const settings = JSON.parse(await readFile3(settingsPath, "utf-8"));
|
|
1009
1031
|
const permissions = settings.permissions;
|
|
@@ -1031,14 +1053,14 @@ async function migratePermissionsToExeOs(settingsPath) {
|
|
|
1031
1053
|
} catch {
|
|
1032
1054
|
}
|
|
1033
1055
|
}
|
|
1034
|
-
async function mergeHooks(packageRoot, homeDir =
|
|
1035
|
-
const settingsPath =
|
|
1036
|
-
const logsDir =
|
|
1037
|
-
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");
|
|
1038
1060
|
const logSuffix = ` 2>> "${hookLogPath}"`;
|
|
1039
1061
|
await mkdir3(logsDir, { recursive: true });
|
|
1040
1062
|
let settings = {};
|
|
1041
|
-
if (
|
|
1063
|
+
if (existsSync8(settingsPath)) {
|
|
1042
1064
|
try {
|
|
1043
1065
|
settings = JSON.parse(await readFile3(settingsPath, "utf-8"));
|
|
1044
1066
|
} catch {
|
|
@@ -1060,7 +1082,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1060
1082
|
hooks: [
|
|
1061
1083
|
{
|
|
1062
1084
|
type: "command",
|
|
1063
|
-
command: `node "${
|
|
1085
|
+
command: `node "${path7.join(packageRoot, "dist", "hooks", "post-tool-combined.js")}"${logSuffix}`
|
|
1064
1086
|
}
|
|
1065
1087
|
]
|
|
1066
1088
|
},
|
|
@@ -1072,7 +1094,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1072
1094
|
hooks: [
|
|
1073
1095
|
{
|
|
1074
1096
|
type: "command",
|
|
1075
|
-
command: `node "${
|
|
1097
|
+
command: `node "${path7.join(packageRoot, "dist", "hooks", "session-start.js")}"${logSuffix}`,
|
|
1076
1098
|
timeout: 1e4
|
|
1077
1099
|
}
|
|
1078
1100
|
]
|
|
@@ -1085,7 +1107,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1085
1107
|
hooks: [
|
|
1086
1108
|
{
|
|
1087
1109
|
type: "command",
|
|
1088
|
-
command: `node "${
|
|
1110
|
+
command: `node "${path7.join(packageRoot, "dist", "hooks", "prompt-submit.js")}"${logSuffix}`
|
|
1089
1111
|
}
|
|
1090
1112
|
]
|
|
1091
1113
|
},
|
|
@@ -1097,7 +1119,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1097
1119
|
hooks: [
|
|
1098
1120
|
{
|
|
1099
1121
|
type: "command",
|
|
1100
|
-
command: `node "${
|
|
1122
|
+
command: `node "${path7.join(packageRoot, "dist", "hooks", "exe-heartbeat-hook.js")}"${logSuffix}`,
|
|
1101
1123
|
timeout: 5e3
|
|
1102
1124
|
}
|
|
1103
1125
|
]
|
|
@@ -1110,7 +1132,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1110
1132
|
hooks: [
|
|
1111
1133
|
{
|
|
1112
1134
|
type: "command",
|
|
1113
|
-
command: `node "${
|
|
1135
|
+
command: `node "${path7.join(packageRoot, "dist", "hooks", "stop.js")}"${logSuffix}`
|
|
1114
1136
|
}
|
|
1115
1137
|
]
|
|
1116
1138
|
},
|
|
@@ -1123,7 +1145,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1123
1145
|
hooks: [
|
|
1124
1146
|
{
|
|
1125
1147
|
type: "command",
|
|
1126
|
-
command: `node "${
|
|
1148
|
+
command: `node "${path7.join(packageRoot, "dist", "hooks", "pre-tool-use.js")}"${logSuffix}`
|
|
1127
1149
|
}
|
|
1128
1150
|
]
|
|
1129
1151
|
},
|
|
@@ -1135,7 +1157,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1135
1157
|
hooks: [
|
|
1136
1158
|
{
|
|
1137
1159
|
type: "command",
|
|
1138
|
-
command: `node "${
|
|
1160
|
+
command: `node "${path7.join(packageRoot, "dist", "hooks", "subagent-stop.js")}"${logSuffix}`
|
|
1139
1161
|
}
|
|
1140
1162
|
]
|
|
1141
1163
|
},
|
|
@@ -1147,7 +1169,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1147
1169
|
hooks: [
|
|
1148
1170
|
{
|
|
1149
1171
|
type: "command",
|
|
1150
|
-
command: `node "${
|
|
1172
|
+
command: `node "${path7.join(packageRoot, "dist", "hooks", "pre-compact.js")}"${logSuffix}`,
|
|
1151
1173
|
timeout: 1e4
|
|
1152
1174
|
}
|
|
1153
1175
|
]
|
|
@@ -1160,7 +1182,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1160
1182
|
hooks: [
|
|
1161
1183
|
{
|
|
1162
1184
|
type: "command",
|
|
1163
|
-
command: `node "${
|
|
1185
|
+
command: `node "${path7.join(packageRoot, "dist", "hooks", "session-end.js")}"${logSuffix}`
|
|
1164
1186
|
}
|
|
1165
1187
|
]
|
|
1166
1188
|
},
|
|
@@ -1172,7 +1194,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1172
1194
|
hooks: [
|
|
1173
1195
|
{
|
|
1174
1196
|
type: "command",
|
|
1175
|
-
command: `node "${
|
|
1197
|
+
command: `node "${path7.join(packageRoot, "dist", "hooks", "notification.js")}"${logSuffix}`
|
|
1176
1198
|
}
|
|
1177
1199
|
]
|
|
1178
1200
|
},
|
|
@@ -1184,7 +1206,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1184
1206
|
hooks: [
|
|
1185
1207
|
{
|
|
1186
1208
|
type: "command",
|
|
1187
|
-
command: `node "${
|
|
1209
|
+
command: `node "${path7.join(packageRoot, "dist", "hooks", "post-compact.js")}"${logSuffix}`,
|
|
1188
1210
|
timeout: 1e4
|
|
1189
1211
|
}
|
|
1190
1212
|
]
|
|
@@ -1197,7 +1219,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1197
1219
|
hooks: [
|
|
1198
1220
|
{
|
|
1199
1221
|
type: "command",
|
|
1200
|
-
command: `node "${
|
|
1222
|
+
command: `node "${path7.join(packageRoot, "dist", "hooks", "instructions-loaded.js")}"${logSuffix}`
|
|
1201
1223
|
}
|
|
1202
1224
|
]
|
|
1203
1225
|
},
|
|
@@ -1302,13 +1324,13 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
1302
1324
|
allowList.push(fullName);
|
|
1303
1325
|
}
|
|
1304
1326
|
}
|
|
1305
|
-
await mkdir3(
|
|
1327
|
+
await mkdir3(path7.dirname(settingsPath), { recursive: true });
|
|
1306
1328
|
await writeFile3(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
1307
1329
|
return { added, skipped };
|
|
1308
1330
|
}
|
|
1309
|
-
async function cleanOldShellFunctions(homeDir =
|
|
1310
|
-
const rosterPath =
|
|
1311
|
-
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;
|
|
1312
1334
|
let employees;
|
|
1313
1335
|
try {
|
|
1314
1336
|
employees = JSON.parse(await readFile3(rosterPath, "utf-8"));
|
|
@@ -1323,13 +1345,13 @@ async function cleanOldShellFunctions(homeDir = os5.homedir()) {
|
|
|
1323
1345
|
return { name: n, funcDef, forLoop };
|
|
1324
1346
|
});
|
|
1325
1347
|
const rcFiles = [
|
|
1326
|
-
|
|
1327
|
-
|
|
1348
|
+
path7.join(homeDir, ".zshrc"),
|
|
1349
|
+
path7.join(homeDir, ".bashrc")
|
|
1328
1350
|
];
|
|
1329
1351
|
const REMOVED_MARKER = "# Removed by exe-os \u2014 wrappers now at ~/.exe-os/bin/";
|
|
1330
1352
|
let totalRemoved = 0;
|
|
1331
1353
|
for (const rcPath of rcFiles) {
|
|
1332
|
-
if (!
|
|
1354
|
+
if (!existsSync8(rcPath)) continue;
|
|
1333
1355
|
let content;
|
|
1334
1356
|
try {
|
|
1335
1357
|
content = await readFile3(rcPath, "utf-8");
|
|
@@ -1433,8 +1455,8 @@ function escapeRegExp(s) {
|
|
|
1433
1455
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1434
1456
|
}
|
|
1435
1457
|
async function injectOrchestrationRules(homeDir) {
|
|
1436
|
-
const claudeDir =
|
|
1437
|
-
const claudeMdPath =
|
|
1458
|
+
const claudeDir = path7.join(homeDir, ".claude");
|
|
1459
|
+
const claudeMdPath = path7.join(claudeDir, "CLAUDE.md");
|
|
1438
1460
|
await mkdir3(claudeDir, { recursive: true });
|
|
1439
1461
|
let existing = "";
|
|
1440
1462
|
try {
|
|
@@ -1456,19 +1478,19 @@ async function injectOrchestrationRules(homeDir) {
|
|
|
1456
1478
|
await writeFile3(claudeMdPath, existing + separator + ORCHESTRATION_RULES + "\n", "utf-8");
|
|
1457
1479
|
return "injected";
|
|
1458
1480
|
}
|
|
1459
|
-
async function installStatusLine(packageRoot, homeDir =
|
|
1481
|
+
async function installStatusLine(packageRoot, homeDir = os6.homedir()) {
|
|
1460
1482
|
const prefs = loadPreferences(homeDir);
|
|
1461
1483
|
if (prefs.ccStatusLine === false) return "opted-out";
|
|
1462
|
-
const claudeDir =
|
|
1484
|
+
const claudeDir = path7.join(homeDir, ".claude");
|
|
1463
1485
|
await mkdir3(claudeDir, { recursive: true });
|
|
1464
|
-
const assetPath =
|
|
1465
|
-
if (!
|
|
1466
|
-
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");
|
|
1467
1489
|
const assetContent = await readFile3(assetPath, "utf-8");
|
|
1468
1490
|
await writeFile3(destScript, assetContent, { mode: 493 });
|
|
1469
|
-
const settingsPath =
|
|
1491
|
+
const settingsPath = path7.join(claudeDir, "settings.json");
|
|
1470
1492
|
let settings = {};
|
|
1471
|
-
if (
|
|
1493
|
+
if (existsSync8(settingsPath)) {
|
|
1472
1494
|
try {
|
|
1473
1495
|
settings = JSON.parse(await readFile3(settingsPath, "utf-8"));
|
|
1474
1496
|
} catch {
|
|
@@ -1505,13 +1527,13 @@ async function runInstaller(homeDir) {
|
|
|
1505
1527
|
`Hooks: ${hookResult.added} added, ${hookResult.skipped} unchanged
|
|
1506
1528
|
`
|
|
1507
1529
|
);
|
|
1508
|
-
const resolvedHome = homeDir ??
|
|
1509
|
-
const exeWorkspace =
|
|
1510
|
-
if (!
|
|
1530
|
+
const resolvedHome = homeDir ?? os6.homedir();
|
|
1531
|
+
const exeWorkspace = path7.join(resolvedHome, "exe");
|
|
1532
|
+
if (!existsSync8(exeWorkspace)) {
|
|
1511
1533
|
try {
|
|
1512
|
-
await mkdir3(
|
|
1513
|
-
await mkdir3(
|
|
1514
|
-
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 });
|
|
1515
1537
|
process.stderr.write(
|
|
1516
1538
|
`Created ~/exe/ \u2014 your automation workspace for non-code projects
|
|
1517
1539
|
`
|
|
@@ -1546,57 +1568,57 @@ exe-os installed successfully.
|
|
|
1546
1568
|
`);
|
|
1547
1569
|
}
|
|
1548
1570
|
function setupTmux(home) {
|
|
1549
|
-
const homeDir = home ??
|
|
1550
|
-
const exeDir =
|
|
1551
|
-
const exeTmuxConf =
|
|
1552
|
-
const userTmuxConf =
|
|
1553
|
-
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");
|
|
1554
1576
|
const sourceLine = "source-file ~/.exe-os/tmux.conf";
|
|
1555
1577
|
const pkgRoot = resolvePackageRoot();
|
|
1556
|
-
const assetPath =
|
|
1557
|
-
if (!
|
|
1578
|
+
const assetPath = path7.join(pkgRoot, "dist", "assets", "tmux.conf");
|
|
1579
|
+
if (!existsSync8(assetPath)) {
|
|
1558
1580
|
process.stderr.write(`exe-os: tmux.conf asset not found at ${assetPath} \u2014 skipping tmux setup
|
|
1559
1581
|
`);
|
|
1560
1582
|
return;
|
|
1561
1583
|
}
|
|
1562
|
-
|
|
1563
|
-
if (
|
|
1564
|
-
const currentContent =
|
|
1565
|
-
const newContent =
|
|
1584
|
+
mkdirSync4(exeDir, { recursive: true });
|
|
1585
|
+
if (existsSync8(exeTmuxConf)) {
|
|
1586
|
+
const currentContent = readFileSync6(exeTmuxConf, "utf8");
|
|
1587
|
+
const newContent = readFileSync6(assetPath, "utf8");
|
|
1566
1588
|
const currentHash = createHash("sha256").update(currentContent).digest("hex");
|
|
1567
1589
|
const newHash = createHash("sha256").update(newContent).digest("hex");
|
|
1568
1590
|
if (currentHash !== newHash) {
|
|
1569
|
-
const shippedPath =
|
|
1570
|
-
const lastShippedHash =
|
|
1591
|
+
const shippedPath = path7.join(exeDir, ".tmux.conf.shipped-hash");
|
|
1592
|
+
const lastShippedHash = existsSync8(shippedPath) ? readFileSync6(shippedPath, "utf8").trim() : "";
|
|
1571
1593
|
if (lastShippedHash && currentHash !== lastShippedHash) {
|
|
1572
1594
|
process.stderr.write("exe-os: tmux config has user customizations \u2014 skipping overwrite\n");
|
|
1573
1595
|
} else {
|
|
1574
1596
|
copyFileSync(assetPath, exeTmuxConf);
|
|
1575
1597
|
process.stderr.write("exe-os: tmux config updated\n");
|
|
1576
1598
|
}
|
|
1577
|
-
|
|
1599
|
+
writeFileSync5(shippedPath, newHash, "utf8");
|
|
1578
1600
|
} else {
|
|
1579
1601
|
process.stderr.write("exe-os: tmux config already up to date\n");
|
|
1580
1602
|
}
|
|
1581
1603
|
} else {
|
|
1582
1604
|
copyFileSync(assetPath, exeTmuxConf);
|
|
1583
|
-
const newContent =
|
|
1605
|
+
const newContent = readFileSync6(assetPath, "utf8");
|
|
1584
1606
|
const newHash = createHash("sha256").update(newContent).digest("hex");
|
|
1585
|
-
|
|
1607
|
+
writeFileSync5(path7.join(exeDir, ".tmux.conf.shipped-hash"), newHash, "utf8");
|
|
1586
1608
|
}
|
|
1587
|
-
if (
|
|
1588
|
-
const existing =
|
|
1609
|
+
if (existsSync8(userTmuxConf)) {
|
|
1610
|
+
const existing = readFileSync6(userTmuxConf, "utf8");
|
|
1589
1611
|
if (!existing.includes(sourceLine)) {
|
|
1590
|
-
if (!
|
|
1612
|
+
if (!existsSync8(backupPath)) {
|
|
1591
1613
|
copyFileSync(userTmuxConf, backupPath);
|
|
1592
1614
|
process.stderr.write(`exe-os: backed up existing tmux config to ${backupPath}
|
|
1593
1615
|
`);
|
|
1594
1616
|
}
|
|
1595
|
-
|
|
1617
|
+
writeFileSync5(userTmuxConf, `${sourceLine}
|
|
1596
1618
|
${existing}`);
|
|
1597
1619
|
}
|
|
1598
1620
|
} else {
|
|
1599
|
-
|
|
1621
|
+
writeFileSync5(userTmuxConf, `# Exe OS tmux defaults \u2014 remove this line to use your own config
|
|
1600
1622
|
${sourceLine}
|
|
1601
1623
|
`);
|
|
1602
1624
|
}
|
|
@@ -1607,10 +1629,10 @@ ${sourceLine}
|
|
|
1607
1629
|
process.stderr.write("exe-os: tmux config installed\n");
|
|
1608
1630
|
}
|
|
1609
1631
|
function setupGhostty(home) {
|
|
1610
|
-
const homeDir = home ??
|
|
1611
|
-
const xdgConfig =
|
|
1612
|
-
const macConfig =
|
|
1613
|
-
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) || (() => {
|
|
1614
1636
|
try {
|
|
1615
1637
|
execSync2("which ghostty 2>/dev/null");
|
|
1616
1638
|
return true;
|
|
@@ -1622,48 +1644,48 @@ function setupGhostty(home) {
|
|
|
1622
1644
|
return;
|
|
1623
1645
|
}
|
|
1624
1646
|
const pkgRoot = resolvePackageRoot();
|
|
1625
|
-
const assetPath =
|
|
1626
|
-
if (!
|
|
1647
|
+
const assetPath = path7.join(pkgRoot, "dist", "assets", "ghostty.conf");
|
|
1648
|
+
if (!existsSync8(assetPath)) {
|
|
1627
1649
|
process.stderr.write("exe-os: ghostty.conf asset not found \u2014 skipping Ghostty setup\n");
|
|
1628
1650
|
return;
|
|
1629
1651
|
}
|
|
1630
1652
|
const configDir = xdgConfig;
|
|
1631
|
-
const configPath =
|
|
1632
|
-
const backupPath =
|
|
1633
|
-
|
|
1653
|
+
const configPath = path7.join(configDir, "config");
|
|
1654
|
+
const backupPath = path7.join(configDir, "config.backup");
|
|
1655
|
+
mkdirSync4(configDir, { recursive: true });
|
|
1634
1656
|
const START_MARKER = "# \u2500\u2500 exe-os:ghostty-start \u2500\u2500";
|
|
1635
1657
|
const END_MARKER = "# \u2500\u2500 exe-os:ghostty-end \u2500\u2500";
|
|
1636
|
-
const assetContent =
|
|
1658
|
+
const assetContent = readFileSync6(assetPath, "utf8").trim();
|
|
1637
1659
|
const markedSection = `${START_MARKER}
|
|
1638
1660
|
${assetContent}
|
|
1639
1661
|
${END_MARKER}`;
|
|
1640
|
-
if (
|
|
1641
|
-
const existing =
|
|
1662
|
+
if (existsSync8(configPath)) {
|
|
1663
|
+
const existing = readFileSync6(configPath, "utf8");
|
|
1642
1664
|
if (existing.includes(START_MARKER) && existing.includes(END_MARKER)) {
|
|
1643
1665
|
process.stderr.write("exe-os: Ghostty config already installed \u2014 preserving local settings\n");
|
|
1644
1666
|
return;
|
|
1645
1667
|
} else if (existing.includes("Exe OS")) {
|
|
1646
|
-
if (!
|
|
1668
|
+
if (!existsSync8(backupPath)) {
|
|
1647
1669
|
copyFileSync(configPath, backupPath);
|
|
1648
1670
|
process.stderr.write(`exe-os: backed up existing Ghostty config to ${backupPath}
|
|
1649
1671
|
`);
|
|
1650
1672
|
}
|
|
1651
|
-
|
|
1673
|
+
writeFileSync5(configPath, `${START_MARKER}
|
|
1652
1674
|
${existing.trim()}
|
|
1653
1675
|
${END_MARKER}
|
|
1654
1676
|
`);
|
|
1655
1677
|
} else {
|
|
1656
|
-
if (!
|
|
1678
|
+
if (!existsSync8(backupPath)) {
|
|
1657
1679
|
copyFileSync(configPath, backupPath);
|
|
1658
1680
|
process.stderr.write(`exe-os: backed up existing Ghostty config to ${backupPath}
|
|
1659
1681
|
`);
|
|
1660
1682
|
}
|
|
1661
|
-
|
|
1683
|
+
writeFileSync5(configPath, `${markedSection}
|
|
1662
1684
|
|
|
1663
1685
|
${existing}`);
|
|
1664
1686
|
}
|
|
1665
1687
|
} else {
|
|
1666
|
-
|
|
1688
|
+
writeFileSync5(configPath, `${markedSection}
|
|
1667
1689
|
`);
|
|
1668
1690
|
}
|
|
1669
1691
|
process.stderr.write("exe-os: Ghostty config installed\n");
|
|
@@ -1687,6 +1709,7 @@ var init_installer = __esm({
|
|
|
1687
1709
|
init_agent_symlinks();
|
|
1688
1710
|
init_mcp_prefix();
|
|
1689
1711
|
init_preferences();
|
|
1712
|
+
init_mcp_http_config();
|
|
1690
1713
|
init_runtime_hook_manifest();
|
|
1691
1714
|
EXE_SECTION_START = "<!-- exe-os:orchestration-start -->";
|
|
1692
1715
|
EXE_SECTION_END = "<!-- exe-os:orchestration-end -->";
|
|
@@ -1715,19 +1738,19 @@ __export(installer_exports, {
|
|
|
1715
1738
|
verifyCodexHooks: () => verifyCodexHooks
|
|
1716
1739
|
});
|
|
1717
1740
|
import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir4 } from "fs/promises";
|
|
1718
|
-
import { existsSync as
|
|
1719
|
-
import
|
|
1720
|
-
import
|
|
1721
|
-
async function mergeCodexHooks(packageRoot, homeDir =
|
|
1722
|
-
const codexDir =
|
|
1723
|
-
const hooksPath =
|
|
1724
|
-
const logsDir =
|
|
1725
|
-
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");
|
|
1726
1749
|
const logSuffix = ` 2>> "${hookLogPath}"`;
|
|
1727
1750
|
await mkdir4(codexDir, { recursive: true });
|
|
1728
1751
|
await mkdir4(logsDir, { recursive: true });
|
|
1729
1752
|
let hooksJson = {};
|
|
1730
|
-
if (
|
|
1753
|
+
if (existsSync10(hooksPath)) {
|
|
1731
1754
|
try {
|
|
1732
1755
|
hooksJson = JSON.parse(await readFile4(hooksPath, "utf-8"));
|
|
1733
1756
|
} catch {
|
|
@@ -1738,19 +1761,6 @@ async function mergeCodexHooks(packageRoot, homeDir = os6.homedir()) {
|
|
|
1738
1761
|
hooksJson.hooks = {};
|
|
1739
1762
|
}
|
|
1740
1763
|
const hooksToRegister = [
|
|
1741
|
-
{
|
|
1742
|
-
event: "SessionStart",
|
|
1743
|
-
group: {
|
|
1744
|
-
hooks: [
|
|
1745
|
-
{
|
|
1746
|
-
type: "command",
|
|
1747
|
-
command: `node "${path8.join(packageRoot, "dist", "hooks", "session-start.js")}"${logSuffix}`,
|
|
1748
|
-
timeout: 30
|
|
1749
|
-
}
|
|
1750
|
-
]
|
|
1751
|
-
},
|
|
1752
|
-
marker: EXE_HOOKS.sessionStart
|
|
1753
|
-
},
|
|
1754
1764
|
{
|
|
1755
1765
|
event: "PostToolUse",
|
|
1756
1766
|
group: {
|
|
@@ -1760,7 +1770,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os6.homedir()) {
|
|
|
1760
1770
|
// Combined hook: runs ingest + error-recall in one Node process.
|
|
1761
1771
|
// Eliminates a cold-start cycle per tool call (~3-6s savings on Codex).
|
|
1762
1772
|
type: "command",
|
|
1763
|
-
command: `node "${
|
|
1773
|
+
command: `node "${path9.join(packageRoot, "dist", "hooks", "post-tool-combined.js")}"${logSuffix}`
|
|
1764
1774
|
}
|
|
1765
1775
|
]
|
|
1766
1776
|
},
|
|
@@ -1774,7 +1784,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os6.homedir()) {
|
|
|
1774
1784
|
// Single hook: prompt-submit handles memory retrieval + entity boost.
|
|
1775
1785
|
// exe-heartbeat-hook is CC-specific (intercom) — omitted on Codex.
|
|
1776
1786
|
type: "command",
|
|
1777
|
-
command: `node "${
|
|
1787
|
+
command: `node "${path9.join(packageRoot, "dist", "hooks", "prompt-submit.js")}"${logSuffix}`
|
|
1778
1788
|
}
|
|
1779
1789
|
]
|
|
1780
1790
|
},
|
|
@@ -1786,7 +1796,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os6.homedir()) {
|
|
|
1786
1796
|
hooks: [
|
|
1787
1797
|
{
|
|
1788
1798
|
type: "command",
|
|
1789
|
-
command: `node "${
|
|
1799
|
+
command: `node "${path9.join(packageRoot, "dist", "hooks", "stop.js")}"${logSuffix}`
|
|
1790
1800
|
}
|
|
1791
1801
|
]
|
|
1792
1802
|
},
|
|
@@ -1799,7 +1809,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os6.homedir()) {
|
|
|
1799
1809
|
hooks: [
|
|
1800
1810
|
{
|
|
1801
1811
|
type: "command",
|
|
1802
|
-
command: `node "${
|
|
1812
|
+
command: `node "${path9.join(packageRoot, "dist", "hooks", "pre-tool-use.js")}"${logSuffix}`
|
|
1803
1813
|
}
|
|
1804
1814
|
]
|
|
1805
1815
|
},
|
|
@@ -1808,6 +1818,16 @@ async function mergeCodexHooks(packageRoot, homeDir = os6.homedir()) {
|
|
|
1808
1818
|
];
|
|
1809
1819
|
let added = 0;
|
|
1810
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
|
+
}
|
|
1811
1831
|
const postToolGroups = hooksJson.hooks["PostToolUse"];
|
|
1812
1832
|
if (Array.isArray(postToolGroups)) {
|
|
1813
1833
|
hooksJson.hooks["PostToolUse"] = postToolGroups.map((g) => ({
|
|
@@ -1837,15 +1857,13 @@ async function mergeCodexHooks(packageRoot, homeDir = os6.homedir()) {
|
|
|
1837
1857
|
await writeFile4(hooksPath, JSON.stringify(hooksJson, null, 2) + "\n");
|
|
1838
1858
|
return { added, skipped };
|
|
1839
1859
|
}
|
|
1840
|
-
function verifyCodexHooks(homeDir =
|
|
1841
|
-
const hooksPath =
|
|
1842
|
-
if (!
|
|
1860
|
+
function verifyCodexHooks(homeDir = os7.homedir()) {
|
|
1861
|
+
const hooksPath = path9.join(homeDir, ".codex", "hooks.json");
|
|
1862
|
+
if (!existsSync10(hooksPath)) return false;
|
|
1843
1863
|
try {
|
|
1844
|
-
const hooksJson = JSON.parse(
|
|
1845
|
-
__require("fs").readFileSync(hooksPath, "utf-8")
|
|
1846
|
-
);
|
|
1864
|
+
const hooksJson = JSON.parse(readFileSync8(hooksPath, "utf-8"));
|
|
1847
1865
|
if (!hooksJson.hooks) return false;
|
|
1848
|
-
const required = ["
|
|
1866
|
+
const required = ["PostToolUse", "UserPromptSubmit", "Stop", "PreToolUse"];
|
|
1849
1867
|
for (const event of required) {
|
|
1850
1868
|
const groups = hooksJson.hooks[event];
|
|
1851
1869
|
if (!groups || !groups.some(
|
|
@@ -1863,19 +1881,23 @@ function verifyCodexHooks(homeDir = os6.homedir()) {
|
|
|
1863
1881
|
)) {
|
|
1864
1882
|
return false;
|
|
1865
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
|
+
}
|
|
1866
1888
|
return true;
|
|
1867
1889
|
} catch {
|
|
1868
1890
|
return false;
|
|
1869
1891
|
}
|
|
1870
1892
|
}
|
|
1871
|
-
async function installCodexStatusLine(homeDir =
|
|
1893
|
+
async function installCodexStatusLine(homeDir = os7.homedir()) {
|
|
1872
1894
|
const prefs = loadPreferences(homeDir);
|
|
1873
1895
|
if (prefs.codexStatusLine === false) return "opted-out";
|
|
1874
|
-
const codexDir =
|
|
1875
|
-
const configPath =
|
|
1896
|
+
const codexDir = path9.join(homeDir, ".codex");
|
|
1897
|
+
const configPath = path9.join(codexDir, "config.toml");
|
|
1876
1898
|
await mkdir4(codexDir, { recursive: true });
|
|
1877
1899
|
let content = "";
|
|
1878
|
-
if (
|
|
1900
|
+
if (existsSync10(configPath)) {
|
|
1879
1901
|
content = await readFile4(configPath, "utf-8");
|
|
1880
1902
|
if (/\[tui\][\s\S]*?status_line\s*=/.test(content)) {
|
|
1881
1903
|
return "already-configured";
|
|
@@ -1892,53 +1914,47 @@ status_line = [${DEFAULT_CODEX_STATUS_LINE.map((s) => `"${s}"`).join(", ")}]`;
|
|
|
1892
1914
|
await writeFile4(configPath, content);
|
|
1893
1915
|
return "installed";
|
|
1894
1916
|
}
|
|
1895
|
-
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) {
|
|
1896
1925
|
return [
|
|
1897
1926
|
"[mcp_servers.exe-os]",
|
|
1898
|
-
`
|
|
1899
|
-
`
|
|
1927
|
+
`url = ${tomlString(mcpHttpUrl())}`,
|
|
1928
|
+
`http_headers = ${codexHttpHeadersToml(homeDir)}`,
|
|
1900
1929
|
`startup_timeout_sec = ${CODEX_EXE_MCP_STARTUP_TIMEOUT_SEC}`,
|
|
1901
1930
|
`tool_timeout_sec = ${CODEX_EXE_MCP_TOOL_TIMEOUT_SEC}`,
|
|
1902
1931
|
""
|
|
1903
1932
|
].join("\n");
|
|
1904
1933
|
}
|
|
1905
|
-
function reconcileCodexMcpSection(sectionContent,
|
|
1934
|
+
function reconcileCodexMcpSection(sectionContent, homeDir) {
|
|
1906
1935
|
const desiredLines = {
|
|
1907
|
-
|
|
1908
|
-
|
|
1936
|
+
url: `url = ${tomlString(mcpHttpUrl())}`,
|
|
1937
|
+
headers: `http_headers = ${codexHttpHeadersToml(homeDir)}`,
|
|
1909
1938
|
startup: `startup_timeout_sec = ${CODEX_EXE_MCP_STARTUP_TIMEOUT_SEC}`,
|
|
1910
1939
|
tool: `tool_timeout_sec = ${CODEX_EXE_MCP_TOOL_TIMEOUT_SEC}`
|
|
1911
1940
|
};
|
|
1912
1941
|
let next = sectionContent;
|
|
1913
|
-
|
|
1914
|
-
const
|
|
1915
|
-
|
|
1916
|
-
const updated = next.replace(regex, desired);
|
|
1917
|
-
if (updated !== next) {
|
|
1918
|
-
next = updated;
|
|
1919
|
-
changed = true;
|
|
1920
|
-
}
|
|
1921
|
-
return;
|
|
1922
|
-
}
|
|
1923
|
-
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}
|
|
1924
1945
|
` : `${next}
|
|
1925
|
-
${
|
|
1946
|
+
${line}
|
|
1926
1947
|
`;
|
|
1927
|
-
|
|
1928
|
-
};
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
}
|
|
1935
|
-
async function registerCodexMcpServer(packageRoot, homeDir = os6.homedir()) {
|
|
1936
|
-
const codexDir = path8.join(homeDir, ".codex");
|
|
1937
|
-
const configPath = path8.join(codexDir, "config.toml");
|
|
1938
|
-
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;
|
|
1939
1955
|
await mkdir4(codexDir, { recursive: true });
|
|
1940
1956
|
let content = "";
|
|
1941
|
-
if (
|
|
1957
|
+
if (existsSync10(configPath)) {
|
|
1942
1958
|
content = await readFile4(configPath, "utf-8");
|
|
1943
1959
|
}
|
|
1944
1960
|
const sectionHeader = "[mcp_servers.exe-os]";
|
|
@@ -1948,7 +1964,7 @@ async function registerCodexMcpServer(packageRoot, homeDir = os6.homedir()) {
|
|
|
1948
1964
|
const nextSectionMatch = afterHeader.match(/\n\[(?!mcp_servers\.exe-os)/);
|
|
1949
1965
|
const sectionEnd = nextSectionMatch ? headerIndex + sectionHeader.length + nextSectionMatch.index : content.length;
|
|
1950
1966
|
const sectionContent = content.slice(headerIndex, sectionEnd);
|
|
1951
|
-
const reconciled = reconcileCodexMcpSection(sectionContent,
|
|
1967
|
+
const reconciled = reconcileCodexMcpSection(sectionContent, homeDir);
|
|
1952
1968
|
if (!reconciled.changed) {
|
|
1953
1969
|
return "already-registered";
|
|
1954
1970
|
}
|
|
@@ -1956,17 +1972,17 @@ async function registerCodexMcpServer(packageRoot, homeDir = os6.homedir()) {
|
|
|
1956
1972
|
await writeFile4(configPath, content);
|
|
1957
1973
|
return "updated";
|
|
1958
1974
|
}
|
|
1959
|
-
const newSection = desiredCodexMcpSection(
|
|
1975
|
+
const newSection = desiredCodexMcpSection(homeDir);
|
|
1960
1976
|
const separator = content.length > 0 && !content.endsWith("\n") ? "\n\n" : content.length > 0 ? "\n" : "";
|
|
1961
1977
|
content = content + separator + newSection;
|
|
1962
1978
|
await writeFile4(configPath, content);
|
|
1963
1979
|
return "registered";
|
|
1964
1980
|
}
|
|
1965
|
-
async function ensureCodexHooksFeature(homeDir =
|
|
1966
|
-
const configPath =
|
|
1967
|
-
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 });
|
|
1968
1984
|
let content = "";
|
|
1969
|
-
if (
|
|
1985
|
+
if (existsSync10(configPath)) {
|
|
1970
1986
|
content = await readFile4(configPath, "utf-8");
|
|
1971
1987
|
}
|
|
1972
1988
|
if (/\[features\][\s\S]*?codex_hooks\s*=\s*true/.test(content)) {
|
|
@@ -2021,6 +2037,7 @@ var init_installer2 = __esm({
|
|
|
2021
2037
|
init_installer();
|
|
2022
2038
|
init_preferences();
|
|
2023
2039
|
init_runtime_hook_manifest();
|
|
2040
|
+
init_mcp_http_config();
|
|
2024
2041
|
DEFAULT_CODEX_STATUS_LINE = [
|
|
2025
2042
|
"model-with-reasoning",
|
|
2026
2043
|
"current-dir",
|
|
@@ -2036,44 +2053,44 @@ var init_installer2 = __esm({
|
|
|
2036
2053
|
|
|
2037
2054
|
// src/bin/install.ts
|
|
2038
2055
|
init_installer();
|
|
2039
|
-
import { existsSync as
|
|
2056
|
+
import { existsSync as existsSync11, readFileSync as readFileSync9, writeFileSync as writeFileSync7, unlinkSync as unlinkSync3, readdirSync as readdirSync2, openSync, closeSync } from "fs";
|
|
2040
2057
|
import { spawn, execSync as execSync3 } from "child_process";
|
|
2041
|
-
import
|
|
2042
|
-
import
|
|
2058
|
+
import path10 from "path";
|
|
2059
|
+
import os8 from "os";
|
|
2043
2060
|
|
|
2044
2061
|
// src/lib/session-wrappers.ts
|
|
2045
2062
|
import {
|
|
2046
|
-
existsSync as
|
|
2047
|
-
readFileSync as
|
|
2048
|
-
writeFileSync as
|
|
2049
|
-
mkdirSync as
|
|
2063
|
+
existsSync as existsSync9,
|
|
2064
|
+
readFileSync as readFileSync7,
|
|
2065
|
+
writeFileSync as writeFileSync6,
|
|
2066
|
+
mkdirSync as mkdirSync5,
|
|
2050
2067
|
chmodSync as chmodSync3,
|
|
2051
2068
|
readdirSync,
|
|
2052
2069
|
unlinkSync as unlinkSync2
|
|
2053
2070
|
} from "fs";
|
|
2054
|
-
import
|
|
2071
|
+
import path8 from "path";
|
|
2055
2072
|
import { homedir } from "os";
|
|
2056
2073
|
var MAX_N = 9;
|
|
2057
2074
|
function generateSessionWrappers(packageRoot, homeDir) {
|
|
2058
2075
|
const home = homeDir ?? homedir();
|
|
2059
|
-
const binDir =
|
|
2060
|
-
const rosterPath =
|
|
2061
|
-
|
|
2062
|
-
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");
|
|
2063
2080
|
const candidates = [
|
|
2064
|
-
|
|
2065
|
-
|
|
2081
|
+
path8.join(packageRoot, "dist", "bin", "exe-start.sh"),
|
|
2082
|
+
path8.join(packageRoot, "src", "bin", "exe-start.sh")
|
|
2066
2083
|
];
|
|
2067
2084
|
for (const src of candidates) {
|
|
2068
|
-
if (
|
|
2069
|
-
|
|
2085
|
+
if (existsSync9(src)) {
|
|
2086
|
+
writeFileSync6(exeStartDst, readFileSync7(src));
|
|
2070
2087
|
chmodSync3(exeStartDst, 493);
|
|
2071
2088
|
break;
|
|
2072
2089
|
}
|
|
2073
2090
|
}
|
|
2074
2091
|
let employees = [];
|
|
2075
2092
|
try {
|
|
2076
|
-
employees = JSON.parse(
|
|
2093
|
+
employees = JSON.parse(readFileSync7(rosterPath, "utf8"));
|
|
2077
2094
|
} catch {
|
|
2078
2095
|
return { created: 0, pathConfigured: false };
|
|
2079
2096
|
}
|
|
@@ -2083,9 +2100,9 @@ function generateSessionWrappers(packageRoot, homeDir) {
|
|
|
2083
2100
|
try {
|
|
2084
2101
|
for (const f of readdirSync(binDir)) {
|
|
2085
2102
|
if (f === "exe-start") continue;
|
|
2086
|
-
const fPath =
|
|
2103
|
+
const fPath = path8.join(binDir, f);
|
|
2087
2104
|
try {
|
|
2088
|
-
const content =
|
|
2105
|
+
const content = readFileSync7(fPath, "utf8");
|
|
2089
2106
|
if (content.includes("exe-start")) {
|
|
2090
2107
|
unlinkSync2(fPath);
|
|
2091
2108
|
}
|
|
@@ -2100,34 +2117,34 @@ exec "${exeStartDst}" "$0" "$@"
|
|
|
2100
2117
|
`;
|
|
2101
2118
|
for (const emp of employees) {
|
|
2102
2119
|
for (let n = 1; n <= MAX_N; n++) {
|
|
2103
|
-
const wrapperPath =
|
|
2104
|
-
|
|
2120
|
+
const wrapperPath = path8.join(binDir, `${emp.name}${n}`);
|
|
2121
|
+
writeFileSync6(wrapperPath, wrapperContent);
|
|
2105
2122
|
chmodSync3(wrapperPath, 493);
|
|
2106
2123
|
created++;
|
|
2107
|
-
const codexPath =
|
|
2108
|
-
|
|
2124
|
+
const codexPath = path8.join(binDir, `${emp.name}${n}-codex`);
|
|
2125
|
+
writeFileSync6(codexPath, wrapperContent);
|
|
2109
2126
|
chmodSync3(codexPath, 493);
|
|
2110
2127
|
created++;
|
|
2111
2128
|
}
|
|
2112
2129
|
}
|
|
2113
2130
|
const codexLauncherCandidates = [
|
|
2114
|
-
|
|
2115
|
-
|
|
2131
|
+
path8.join(packageRoot, "dist", "bin", "exe-start-codex.js"),
|
|
2132
|
+
path8.join(packageRoot, "src", "bin", "exe-start-codex.ts")
|
|
2116
2133
|
];
|
|
2117
2134
|
let codexLauncher = null;
|
|
2118
2135
|
for (const c of codexLauncherCandidates) {
|
|
2119
|
-
if (
|
|
2136
|
+
if (existsSync9(c)) {
|
|
2120
2137
|
codexLauncher = c;
|
|
2121
2138
|
break;
|
|
2122
2139
|
}
|
|
2123
2140
|
}
|
|
2124
2141
|
if (codexLauncher) {
|
|
2125
2142
|
for (const emp of employees) {
|
|
2126
|
-
const wrapperPath =
|
|
2143
|
+
const wrapperPath = path8.join(binDir, `${emp.name}-codex`);
|
|
2127
2144
|
const content = `#!/bin/bash
|
|
2128
2145
|
exec node "${codexLauncher}" --agent ${emp.name} "$@"
|
|
2129
2146
|
`;
|
|
2130
|
-
|
|
2147
|
+
writeFileSync6(wrapperPath, content);
|
|
2131
2148
|
chmodSync3(wrapperPath, 493);
|
|
2132
2149
|
created++;
|
|
2133
2150
|
}
|
|
@@ -2146,24 +2163,24 @@ export PATH="${binDir}:$PATH"
|
|
|
2146
2163
|
const shell = process.env.SHELL ?? "/bin/bash";
|
|
2147
2164
|
const profilePaths = [];
|
|
2148
2165
|
if (shell.includes("zsh")) {
|
|
2149
|
-
profilePaths.push(
|
|
2166
|
+
profilePaths.push(path8.join(home, ".zshrc"));
|
|
2150
2167
|
} else if (shell.includes("bash")) {
|
|
2151
|
-
profilePaths.push(
|
|
2152
|
-
profilePaths.push(
|
|
2168
|
+
profilePaths.push(path8.join(home, ".bashrc"));
|
|
2169
|
+
profilePaths.push(path8.join(home, ".bash_profile"));
|
|
2153
2170
|
} else {
|
|
2154
|
-
profilePaths.push(
|
|
2171
|
+
profilePaths.push(path8.join(home, ".profile"));
|
|
2155
2172
|
}
|
|
2156
2173
|
for (const profilePath of profilePaths) {
|
|
2157
2174
|
try {
|
|
2158
2175
|
let content = "";
|
|
2159
2176
|
try {
|
|
2160
|
-
content =
|
|
2177
|
+
content = readFileSync7(profilePath, "utf8");
|
|
2161
2178
|
} catch {
|
|
2162
2179
|
}
|
|
2163
2180
|
if (content.includes(".exe-os/bin")) {
|
|
2164
2181
|
return false;
|
|
2165
2182
|
}
|
|
2166
|
-
|
|
2183
|
+
writeFileSync6(profilePath, content + exportLine);
|
|
2167
2184
|
return true;
|
|
2168
2185
|
} catch {
|
|
2169
2186
|
continue;
|
|
@@ -2173,14 +2190,14 @@ export PATH="${binDir}:$PATH"
|
|
|
2173
2190
|
}
|
|
2174
2191
|
|
|
2175
2192
|
// src/bin/install.ts
|
|
2176
|
-
var homedir2 =
|
|
2177
|
-
var EXE_DIR =
|
|
2193
|
+
var homedir2 = os8.homedir;
|
|
2194
|
+
var EXE_DIR = path10.join(homedir2(), ".exe-os");
|
|
2178
2195
|
function restartDaemon() {
|
|
2179
|
-
const pidPath =
|
|
2180
|
-
const sockPath =
|
|
2196
|
+
const pidPath = path10.join(EXE_DIR, "exed.pid");
|
|
2197
|
+
const sockPath = path10.join(EXE_DIR, "exed.sock");
|
|
2181
2198
|
try {
|
|
2182
|
-
if (
|
|
2183
|
-
const pid = parseInt(
|
|
2199
|
+
if (existsSync11(pidPath)) {
|
|
2200
|
+
const pid = parseInt(readFileSync9(pidPath, "utf8").trim(), 10);
|
|
2184
2201
|
if (!isNaN(pid) && pid > 0) {
|
|
2185
2202
|
try {
|
|
2186
2203
|
process.kill(pid, "SIGKILL");
|
|
@@ -2221,18 +2238,18 @@ function restartDaemon() {
|
|
|
2221
2238
|
} catch {
|
|
2222
2239
|
}
|
|
2223
2240
|
try {
|
|
2224
|
-
const versionPath =
|
|
2225
|
-
|
|
2241
|
+
const versionPath = path10.join(EXE_DIR, "mcp-version");
|
|
2242
|
+
writeFileSync7(versionPath, `deploy-${Date.now()}`);
|
|
2226
2243
|
process.stderr.write(`exe-os: MCP version marker updated \u2014 servers will hot-reload within 10s
|
|
2227
2244
|
`);
|
|
2228
2245
|
} catch {
|
|
2229
2246
|
}
|
|
2230
2247
|
try {
|
|
2231
|
-
const wpDir =
|
|
2232
|
-
if (
|
|
2248
|
+
const wpDir = path10.join(EXE_DIR, "worker-pids");
|
|
2249
|
+
if (existsSync11(wpDir)) {
|
|
2233
2250
|
for (const f of readdirSync2(wpDir)) {
|
|
2234
2251
|
try {
|
|
2235
|
-
unlinkSync3(
|
|
2252
|
+
unlinkSync3(path10.join(wpDir, f));
|
|
2236
2253
|
} catch {
|
|
2237
2254
|
}
|
|
2238
2255
|
}
|
|
@@ -2249,7 +2266,7 @@ function restartDaemon() {
|
|
|
2249
2266
|
}
|
|
2250
2267
|
} catch {
|
|
2251
2268
|
}
|
|
2252
|
-
const totalGB =
|
|
2269
|
+
const totalGB = os8.totalmem() / (1024 * 1024 * 1024);
|
|
2253
2270
|
if (totalGB <= 8) {
|
|
2254
2271
|
process.stderr.write(
|
|
2255
2272
|
`exe-os: ${totalGB.toFixed(0)}GB system \u2014 skipping daemon spawn (keyword search mode)
|
|
@@ -2259,13 +2276,13 @@ function restartDaemon() {
|
|
|
2259
2276
|
}
|
|
2260
2277
|
try {
|
|
2261
2278
|
const pkgRoot = resolvePackageRoot();
|
|
2262
|
-
const daemonPath =
|
|
2263
|
-
if (!
|
|
2279
|
+
const daemonPath = path10.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
2280
|
+
if (!existsSync11(daemonPath)) {
|
|
2264
2281
|
process.stderr.write(`exe-os: daemon not found at ${daemonPath} \u2014 skipping respawn
|
|
2265
2282
|
`);
|
|
2266
2283
|
return;
|
|
2267
2284
|
}
|
|
2268
|
-
const logPath =
|
|
2285
|
+
const logPath = path10.join(EXE_DIR, "exed.log");
|
|
2269
2286
|
let stderrFd = "ignore";
|
|
2270
2287
|
try {
|
|
2271
2288
|
stderrFd = openSync(logPath, "a");
|