@askexenow/exe-os 0.9.7 → 0.9.8
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/dist/bin/backfill-conversations.js +754 -79
- package/dist/bin/backfill-responses.js +752 -77
- package/dist/bin/backfill-vectors.js +752 -77
- package/dist/bin/cleanup-stale-review-tasks.js +657 -35
- package/dist/bin/cli.js +1388 -605
- package/dist/bin/exe-agent-config.js +123 -95
- package/dist/bin/exe-agent.js +41 -25
- package/dist/bin/exe-assign.js +732 -57
- package/dist/bin/exe-boot.js +784 -153
- package/dist/bin/exe-call.js +209 -138
- package/dist/bin/exe-cloud.js +35 -12
- package/dist/bin/exe-dispatch.js +692 -70
- package/dist/bin/exe-doctor.js +648 -26
- package/dist/bin/exe-export-behaviors.js +650 -20
- package/dist/bin/exe-forget.js +635 -13
- package/dist/bin/exe-gateway.js +1053 -271
- package/dist/bin/exe-heartbeat.js +665 -43
- package/dist/bin/exe-kill.js +646 -16
- package/dist/bin/exe-launch-agent.js +887 -97
- package/dist/bin/exe-link.js +658 -43
- package/dist/bin/exe-new-employee.js +378 -177
- package/dist/bin/exe-pending-messages.js +656 -34
- package/dist/bin/exe-pending-notifications.js +635 -13
- package/dist/bin/exe-pending-reviews.js +659 -37
- package/dist/bin/exe-rename.js +645 -30
- package/dist/bin/exe-review.js +635 -13
- package/dist/bin/exe-search.js +771 -88
- package/dist/bin/exe-session-cleanup.js +834 -150
- package/dist/bin/exe-settings.js +127 -91
- package/dist/bin/exe-start-codex.js +729 -94
- package/dist/bin/exe-start-opencode.js +717 -82
- package/dist/bin/exe-status.js +657 -35
- package/dist/bin/exe-team.js +635 -13
- package/dist/bin/git-sweep.js +720 -89
- package/dist/bin/graph-backfill.js +643 -13
- package/dist/bin/graph-export.js +646 -16
- package/dist/bin/install.js +596 -193
- package/dist/bin/scan-tasks.js +724 -93
- package/dist/bin/setup.js +1038 -210
- package/dist/bin/shard-migrate.js +645 -15
- package/dist/bin/wiki-sync.js +646 -16
- package/dist/gateway/index.js +1027 -245
- package/dist/hooks/bug-report-worker.js +891 -170
- package/dist/hooks/commit-complete.js +718 -87
- package/dist/hooks/error-recall.js +776 -93
- package/dist/hooks/exe-heartbeat-hook.js +85 -71
- package/dist/hooks/ingest-worker.js +840 -156
- package/dist/hooks/ingest.js +90 -73
- package/dist/hooks/instructions-loaded.js +669 -38
- package/dist/hooks/notification.js +661 -30
- package/dist/hooks/post-compact.js +674 -43
- package/dist/hooks/pre-compact.js +718 -87
- package/dist/hooks/pre-tool-use.js +872 -125
- package/dist/hooks/prompt-ingest-worker.js +758 -83
- package/dist/hooks/prompt-submit.js +1060 -319
- package/dist/hooks/response-ingest-worker.js +758 -83
- package/dist/hooks/session-end.js +721 -90
- package/dist/hooks/session-start.js +1031 -207
- package/dist/hooks/stop.js +680 -49
- package/dist/hooks/subagent-stop.js +674 -43
- package/dist/hooks/summary-worker.js +816 -132
- package/dist/index.js +1015 -232
- package/dist/lib/cloud-sync.js +663 -48
- package/dist/lib/consolidation.js +26 -3
- package/dist/lib/database.js +626 -18
- package/dist/lib/db.js +2261 -0
- package/dist/lib/device-registry.js +640 -25
- package/dist/lib/embedder.js +96 -43
- package/dist/lib/employee-templates.js +16 -0
- package/dist/lib/employees.js +259 -83
- package/dist/lib/exe-daemon-client.js +101 -63
- package/dist/lib/exe-daemon.js +894 -162
- package/dist/lib/hybrid-search.js +771 -88
- package/dist/lib/identity.js +27 -7
- package/dist/lib/messaging.js +55 -28
- package/dist/lib/reminders.js +21 -1
- package/dist/lib/schedules.js +636 -14
- package/dist/lib/skill-learning.js +21 -1
- package/dist/lib/store.js +643 -13
- package/dist/lib/task-router.js +82 -71
- package/dist/lib/tasks.js +98 -71
- package/dist/lib/tmux-routing.js +87 -60
- package/dist/lib/token-spend.js +26 -6
- package/dist/mcp/server.js +1784 -458
- package/dist/mcp/tools/complete-reminder.js +21 -1
- package/dist/mcp/tools/create-reminder.js +21 -1
- package/dist/mcp/tools/create-task.js +290 -164
- package/dist/mcp/tools/deactivate-behavior.js +24 -4
- package/dist/mcp/tools/list-reminders.js +21 -1
- package/dist/mcp/tools/list-tasks.js +195 -38
- package/dist/mcp/tools/send-message.js +58 -31
- package/dist/mcp/tools/update-task.js +75 -48
- package/dist/runtime/index.js +720 -89
- package/dist/tui/App.js +853 -123
- package/package.json +3 -2
package/dist/bin/setup.js
CHANGED
|
@@ -634,74 +634,123 @@ async function pingDaemon() {
|
|
|
634
634
|
return null;
|
|
635
635
|
}
|
|
636
636
|
function killAndRespawnDaemon() {
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
637
|
+
if (!acquireSpawnLock()) {
|
|
638
|
+
process.stderr.write("[exed-client] Another process is already restarting daemon \u2014 skipping\n");
|
|
639
|
+
if (_socket) {
|
|
640
|
+
_socket.destroy();
|
|
641
|
+
_socket = null;
|
|
642
|
+
}
|
|
643
|
+
_connected = false;
|
|
644
|
+
_buffer = "";
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
try {
|
|
648
|
+
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
649
|
+
if (existsSync4(PID_PATH)) {
|
|
650
|
+
try {
|
|
651
|
+
const pid = parseInt(readFileSync2(PID_PATH, "utf8").trim(), 10);
|
|
652
|
+
if (pid > 0) {
|
|
653
|
+
try {
|
|
654
|
+
process.kill(pid, "SIGKILL");
|
|
655
|
+
} catch {
|
|
656
|
+
}
|
|
645
657
|
}
|
|
658
|
+
} catch {
|
|
646
659
|
}
|
|
660
|
+
}
|
|
661
|
+
if (_socket) {
|
|
662
|
+
_socket.destroy();
|
|
663
|
+
_socket = null;
|
|
664
|
+
}
|
|
665
|
+
_connected = false;
|
|
666
|
+
_buffer = "";
|
|
667
|
+
try {
|
|
668
|
+
unlinkSync2(PID_PATH);
|
|
647
669
|
} catch {
|
|
648
670
|
}
|
|
671
|
+
try {
|
|
672
|
+
unlinkSync2(SOCKET_PATH);
|
|
673
|
+
} catch {
|
|
674
|
+
}
|
|
675
|
+
spawnDaemon();
|
|
676
|
+
} finally {
|
|
677
|
+
releaseSpawnLock();
|
|
649
678
|
}
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
_socket = null;
|
|
653
|
-
}
|
|
654
|
-
_connected = false;
|
|
655
|
-
_buffer = "";
|
|
679
|
+
}
|
|
680
|
+
function isDaemonTooYoung() {
|
|
656
681
|
try {
|
|
657
|
-
|
|
682
|
+
const stat = statSync(PID_PATH);
|
|
683
|
+
return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
|
|
658
684
|
} catch {
|
|
685
|
+
return false;
|
|
659
686
|
}
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
687
|
+
}
|
|
688
|
+
async function retryThenRestart(doRequest, label) {
|
|
689
|
+
const result = await doRequest();
|
|
690
|
+
if (!result.error) {
|
|
691
|
+
_consecutiveFailures = 0;
|
|
692
|
+
return result;
|
|
693
|
+
}
|
|
694
|
+
_consecutiveFailures++;
|
|
695
|
+
for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
|
|
696
|
+
const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
|
|
697
|
+
process.stderr.write(`[exed-client] ${label} failed (${result.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
|
|
698
|
+
`);
|
|
699
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
700
|
+
if (!_connected) {
|
|
701
|
+
if (!await connectToSocket()) continue;
|
|
702
|
+
}
|
|
703
|
+
const retry = await doRequest();
|
|
704
|
+
if (!retry.error) {
|
|
705
|
+
_consecutiveFailures = 0;
|
|
706
|
+
return retry;
|
|
707
|
+
}
|
|
708
|
+
_consecutiveFailures++;
|
|
709
|
+
}
|
|
710
|
+
if (isDaemonTooYoung()) {
|
|
711
|
+
process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
|
|
712
|
+
`);
|
|
713
|
+
return { error: result.error };
|
|
663
714
|
}
|
|
664
|
-
|
|
715
|
+
process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
|
|
716
|
+
`);
|
|
717
|
+
killAndRespawnDaemon();
|
|
718
|
+
const start = Date.now();
|
|
719
|
+
let delay2 = 200;
|
|
720
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
721
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
722
|
+
if (await connectToSocket()) break;
|
|
723
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
724
|
+
}
|
|
725
|
+
if (!_connected) return { error: "Daemon restart failed" };
|
|
726
|
+
const final = await doRequest();
|
|
727
|
+
if (!final.error) _consecutiveFailures = 0;
|
|
728
|
+
return final;
|
|
665
729
|
}
|
|
666
730
|
async function embedViaClient(text, priority = "high") {
|
|
667
731
|
if (!_connected && !await connectEmbedDaemon()) return null;
|
|
668
732
|
_requestCount++;
|
|
669
733
|
if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
|
|
670
734
|
const health = await pingDaemon();
|
|
671
|
-
if (!health) {
|
|
735
|
+
if (!health && !isDaemonTooYoung()) {
|
|
672
736
|
process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
|
|
673
737
|
`);
|
|
674
738
|
killAndRespawnDaemon();
|
|
675
739
|
const start = Date.now();
|
|
676
|
-
let
|
|
740
|
+
let d = 200;
|
|
677
741
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
678
|
-
await new Promise((r) => setTimeout(r,
|
|
742
|
+
await new Promise((r) => setTimeout(r, d));
|
|
679
743
|
if (await connectToSocket()) break;
|
|
680
|
-
|
|
744
|
+
d = Math.min(d * 2, 3e3);
|
|
681
745
|
}
|
|
682
746
|
if (!_connected) return null;
|
|
683
747
|
}
|
|
684
748
|
}
|
|
685
|
-
const result = await
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
killAndRespawnDaemon();
|
|
691
|
-
const start = Date.now();
|
|
692
|
-
let delay2 = 200;
|
|
693
|
-
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
694
|
-
await new Promise((r) => setTimeout(r, delay2));
|
|
695
|
-
if (await connectToSocket()) break;
|
|
696
|
-
delay2 = Math.min(delay2 * 2, 3e3);
|
|
697
|
-
}
|
|
698
|
-
if (!_connected) return null;
|
|
699
|
-
const retry = await sendRequest([text], priority);
|
|
700
|
-
if (!retry.error && retry.vectors?.[0]) return retry.vectors[0];
|
|
701
|
-
process.stderr.write(`[exed-client] Embed retry also failed: ${retry.error ?? "no vector"}
|
|
702
|
-
`);
|
|
703
|
-
}
|
|
704
|
-
return null;
|
|
749
|
+
const result = await retryThenRestart(
|
|
750
|
+
() => sendRequest([text], priority),
|
|
751
|
+
"Embed"
|
|
752
|
+
);
|
|
753
|
+
return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
|
|
705
754
|
}
|
|
706
755
|
function disconnectClient() {
|
|
707
756
|
if (_socket) {
|
|
@@ -719,7 +768,7 @@ function disconnectClient() {
|
|
|
719
768
|
function isClientConnected() {
|
|
720
769
|
return _connected;
|
|
721
770
|
}
|
|
722
|
-
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, HEALTH_CHECK_INTERVAL, _pending, MAX_BUFFER;
|
|
771
|
+
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, _consecutiveFailures, HEALTH_CHECK_INTERVAL, MAX_RETRIES_BEFORE_RESTART, RETRY_DELAYS_MS, MIN_DAEMON_AGE_MS, _pending, MAX_BUFFER;
|
|
723
772
|
var init_exe_daemon_client = __esm({
|
|
724
773
|
"src/lib/exe-daemon-client.ts"() {
|
|
725
774
|
"use strict";
|
|
@@ -734,7 +783,11 @@ var init_exe_daemon_client = __esm({
|
|
|
734
783
|
_connected = false;
|
|
735
784
|
_buffer = "";
|
|
736
785
|
_requestCount = 0;
|
|
786
|
+
_consecutiveFailures = 0;
|
|
737
787
|
HEALTH_CHECK_INTERVAL = 100;
|
|
788
|
+
MAX_RETRIES_BEFORE_RESTART = 3;
|
|
789
|
+
RETRY_DELAYS_MS = [1e3, 3e3, 5e3];
|
|
790
|
+
MIN_DAEMON_AGE_MS = 3e4;
|
|
738
791
|
_pending = /* @__PURE__ */ new Map();
|
|
739
792
|
MAX_BUFFER = 1e7;
|
|
740
793
|
}
|
|
@@ -777,10 +830,10 @@ async function disposeEmbedder() {
|
|
|
777
830
|
async function embedDirect(text) {
|
|
778
831
|
const llamaCpp = await import("node-llama-cpp");
|
|
779
832
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
780
|
-
const { existsSync:
|
|
781
|
-
const
|
|
782
|
-
const modelPath =
|
|
783
|
-
if (!
|
|
833
|
+
const { existsSync: existsSync14 } = await import("fs");
|
|
834
|
+
const path15 = await import("path");
|
|
835
|
+
const modelPath = path15.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
836
|
+
if (!existsSync14(modelPath)) {
|
|
784
837
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
785
838
|
}
|
|
786
839
|
const llama = await llamaCpp.getLlama();
|
|
@@ -1309,6 +1362,118 @@ var init_db_retry = __esm({
|
|
|
1309
1362
|
}
|
|
1310
1363
|
});
|
|
1311
1364
|
|
|
1365
|
+
// src/lib/runtime-table.ts
|
|
1366
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
1367
|
+
var init_runtime_table = __esm({
|
|
1368
|
+
"src/lib/runtime-table.ts"() {
|
|
1369
|
+
"use strict";
|
|
1370
|
+
RUNTIME_TABLE = {
|
|
1371
|
+
codex: {
|
|
1372
|
+
binary: "codex",
|
|
1373
|
+
launchMode: "interactive",
|
|
1374
|
+
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
1375
|
+
inlineFlag: "--no-alt-screen",
|
|
1376
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
1377
|
+
defaultModel: "gpt-5.4"
|
|
1378
|
+
},
|
|
1379
|
+
opencode: {
|
|
1380
|
+
binary: "opencode",
|
|
1381
|
+
launchMode: "exec",
|
|
1382
|
+
autoApproveFlag: "--dangerously-skip-permissions",
|
|
1383
|
+
inlineFlag: "",
|
|
1384
|
+
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
1385
|
+
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
1386
|
+
}
|
|
1387
|
+
};
|
|
1388
|
+
DEFAULT_RUNTIME = "claude";
|
|
1389
|
+
}
|
|
1390
|
+
});
|
|
1391
|
+
|
|
1392
|
+
// src/lib/agent-config.ts
|
|
1393
|
+
var agent_config_exports = {};
|
|
1394
|
+
__export(agent_config_exports, {
|
|
1395
|
+
AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
|
|
1396
|
+
DEFAULT_MODELS: () => DEFAULT_MODELS,
|
|
1397
|
+
KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
|
|
1398
|
+
RUNTIME_LABELS: () => RUNTIME_LABELS,
|
|
1399
|
+
clearAgentRuntime: () => clearAgentRuntime,
|
|
1400
|
+
getAgentRuntime: () => getAgentRuntime,
|
|
1401
|
+
loadAgentConfig: () => loadAgentConfig,
|
|
1402
|
+
saveAgentConfig: () => saveAgentConfig,
|
|
1403
|
+
setAgentRuntime: () => setAgentRuntime
|
|
1404
|
+
});
|
|
1405
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync6, mkdirSync as mkdirSync2 } from "fs";
|
|
1406
|
+
import path6 from "path";
|
|
1407
|
+
function loadAgentConfig() {
|
|
1408
|
+
if (!existsSync6(AGENT_CONFIG_PATH)) return {};
|
|
1409
|
+
try {
|
|
1410
|
+
return JSON.parse(readFileSync4(AGENT_CONFIG_PATH, "utf-8"));
|
|
1411
|
+
} catch {
|
|
1412
|
+
return {};
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
function saveAgentConfig(config) {
|
|
1416
|
+
const dir = path6.dirname(AGENT_CONFIG_PATH);
|
|
1417
|
+
if (!existsSync6(dir)) mkdirSync2(dir, { recursive: true });
|
|
1418
|
+
writeFileSync2(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
1419
|
+
}
|
|
1420
|
+
function getAgentRuntime(agentId) {
|
|
1421
|
+
const config = loadAgentConfig();
|
|
1422
|
+
const entry = config[agentId];
|
|
1423
|
+
if (entry) return entry;
|
|
1424
|
+
const orgDefault = config["default"];
|
|
1425
|
+
if (orgDefault) return orgDefault;
|
|
1426
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
1427
|
+
}
|
|
1428
|
+
function setAgentRuntime(agentId, runtime, model) {
|
|
1429
|
+
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
1430
|
+
if (!knownModels) {
|
|
1431
|
+
return {
|
|
1432
|
+
ok: false,
|
|
1433
|
+
error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
|
|
1434
|
+
};
|
|
1435
|
+
}
|
|
1436
|
+
if (!knownModels.includes(model)) {
|
|
1437
|
+
return {
|
|
1438
|
+
ok: false,
|
|
1439
|
+
error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
|
|
1440
|
+
};
|
|
1441
|
+
}
|
|
1442
|
+
const config = loadAgentConfig();
|
|
1443
|
+
config[agentId] = { runtime, model };
|
|
1444
|
+
saveAgentConfig(config);
|
|
1445
|
+
return { ok: true };
|
|
1446
|
+
}
|
|
1447
|
+
function clearAgentRuntime(agentId) {
|
|
1448
|
+
const config = loadAgentConfig();
|
|
1449
|
+
delete config[agentId];
|
|
1450
|
+
saveAgentConfig(config);
|
|
1451
|
+
}
|
|
1452
|
+
var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
|
|
1453
|
+
var init_agent_config = __esm({
|
|
1454
|
+
"src/lib/agent-config.ts"() {
|
|
1455
|
+
"use strict";
|
|
1456
|
+
init_config();
|
|
1457
|
+
init_runtime_table();
|
|
1458
|
+
AGENT_CONFIG_PATH = path6.join(EXE_AI_DIR, "agent-config.json");
|
|
1459
|
+
KNOWN_RUNTIMES = {
|
|
1460
|
+
claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
1461
|
+
codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
|
|
1462
|
+
opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
|
|
1463
|
+
};
|
|
1464
|
+
RUNTIME_LABELS = {
|
|
1465
|
+
claude: "Claude Code (Anthropic)",
|
|
1466
|
+
codex: "Codex (OpenAI)",
|
|
1467
|
+
opencode: "OpenCode (open source)"
|
|
1468
|
+
};
|
|
1469
|
+
DEFAULT_MODELS = {
|
|
1470
|
+
claude: "claude-opus-4",
|
|
1471
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
1472
|
+
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
});
|
|
1476
|
+
|
|
1312
1477
|
// src/lib/employees.ts
|
|
1313
1478
|
var employees_exports = {};
|
|
1314
1479
|
__export(employees_exports, {
|
|
@@ -1324,6 +1489,7 @@ __export(employees_exports, {
|
|
|
1324
1489
|
getEmployeeByRole: () => getEmployeeByRole,
|
|
1325
1490
|
getEmployeeNamesByRole: () => getEmployeeNamesByRole,
|
|
1326
1491
|
hasRole: () => hasRole,
|
|
1492
|
+
hireEmployee: () => hireEmployee,
|
|
1327
1493
|
isCoordinatorName: () => isCoordinatorName,
|
|
1328
1494
|
isCoordinatorRole: () => isCoordinatorRole,
|
|
1329
1495
|
isMultiInstance: () => isMultiInstance,
|
|
@@ -1336,9 +1502,9 @@ __export(employees_exports, {
|
|
|
1336
1502
|
validateEmployeeName: () => validateEmployeeName
|
|
1337
1503
|
});
|
|
1338
1504
|
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir4 } from "fs/promises";
|
|
1339
|
-
import { existsSync as
|
|
1505
|
+
import { existsSync as existsSync7, symlinkSync, readlinkSync, readFileSync as readFileSync5, renameSync as renameSync3, unlinkSync as unlinkSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
1340
1506
|
import { execSync } from "child_process";
|
|
1341
|
-
import
|
|
1507
|
+
import path7 from "path";
|
|
1342
1508
|
import os4 from "os";
|
|
1343
1509
|
function normalizeRole(role) {
|
|
1344
1510
|
return (role ?? "").trim().toLowerCase();
|
|
@@ -1375,7 +1541,7 @@ function validateEmployeeName(name) {
|
|
|
1375
1541
|
return { valid: true };
|
|
1376
1542
|
}
|
|
1377
1543
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
1378
|
-
if (!
|
|
1544
|
+
if (!existsSync7(employeesPath)) {
|
|
1379
1545
|
return [];
|
|
1380
1546
|
}
|
|
1381
1547
|
const raw = await readFile3(employeesPath, "utf-8");
|
|
@@ -1386,13 +1552,13 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
|
1386
1552
|
}
|
|
1387
1553
|
}
|
|
1388
1554
|
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
1389
|
-
await mkdir4(
|
|
1555
|
+
await mkdir4(path7.dirname(employeesPath), { recursive: true });
|
|
1390
1556
|
await writeFile3(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
1391
1557
|
}
|
|
1392
1558
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
1393
|
-
if (!
|
|
1559
|
+
if (!existsSync7(employeesPath)) return [];
|
|
1394
1560
|
try {
|
|
1395
|
-
return JSON.parse(
|
|
1561
|
+
return JSON.parse(readFileSync5(employeesPath, "utf-8"));
|
|
1396
1562
|
} catch {
|
|
1397
1563
|
return [];
|
|
1398
1564
|
}
|
|
@@ -1434,6 +1600,52 @@ function addEmployee(employees, employee) {
|
|
|
1434
1600
|
}
|
|
1435
1601
|
return [...employees, normalized];
|
|
1436
1602
|
}
|
|
1603
|
+
function appendToCoordinatorTeam(employee) {
|
|
1604
|
+
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
1605
|
+
if (!coordinator) return;
|
|
1606
|
+
const idPath = path7.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
1607
|
+
if (!existsSync7(idPath)) return;
|
|
1608
|
+
const content = readFileSync5(idPath, "utf-8");
|
|
1609
|
+
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
1610
|
+
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
1611
|
+
if (!teamMatch || teamMatch.index === void 0) return;
|
|
1612
|
+
const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
|
|
1613
|
+
const nextHeading = afterTeam.match(/\n## /);
|
|
1614
|
+
const entry = `
|
|
1615
|
+
**${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
|
|
1616
|
+
`;
|
|
1617
|
+
let updated;
|
|
1618
|
+
if (nextHeading && nextHeading.index !== void 0) {
|
|
1619
|
+
const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
|
|
1620
|
+
updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
|
|
1621
|
+
} else {
|
|
1622
|
+
updated = content.trimEnd() + "\n" + entry;
|
|
1623
|
+
}
|
|
1624
|
+
writeFileSync3(idPath, updated, "utf-8");
|
|
1625
|
+
}
|
|
1626
|
+
function capitalize(s) {
|
|
1627
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
1628
|
+
}
|
|
1629
|
+
async function hireEmployee(employee) {
|
|
1630
|
+
const employees = await loadEmployees();
|
|
1631
|
+
const updated = addEmployee(employees, employee);
|
|
1632
|
+
await saveEmployees(updated);
|
|
1633
|
+
try {
|
|
1634
|
+
appendToCoordinatorTeam(employee);
|
|
1635
|
+
} catch {
|
|
1636
|
+
}
|
|
1637
|
+
try {
|
|
1638
|
+
const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
|
|
1639
|
+
const config = loadAgentConfig2();
|
|
1640
|
+
const name = employee.name.toLowerCase();
|
|
1641
|
+
if (!config[name] && config["default"]) {
|
|
1642
|
+
config[name] = { ...config["default"] };
|
|
1643
|
+
saveAgentConfig2(config);
|
|
1644
|
+
}
|
|
1645
|
+
} catch {
|
|
1646
|
+
}
|
|
1647
|
+
return updated;
|
|
1648
|
+
}
|
|
1437
1649
|
async function normalizeRosterCase(rosterPath) {
|
|
1438
1650
|
const employees = await loadEmployees(rosterPath);
|
|
1439
1651
|
let changed = false;
|
|
@@ -1443,14 +1655,14 @@ async function normalizeRosterCase(rosterPath) {
|
|
|
1443
1655
|
emp.name = emp.name.toLowerCase();
|
|
1444
1656
|
changed = true;
|
|
1445
1657
|
try {
|
|
1446
|
-
const identityDir =
|
|
1447
|
-
const oldPath =
|
|
1448
|
-
const newPath =
|
|
1449
|
-
if (
|
|
1658
|
+
const identityDir = path7.join(os4.homedir(), ".exe-os", "identity");
|
|
1659
|
+
const oldPath = path7.join(identityDir, `${oldName}.md`);
|
|
1660
|
+
const newPath = path7.join(identityDir, `${emp.name}.md`);
|
|
1661
|
+
if (existsSync7(oldPath) && !existsSync7(newPath)) {
|
|
1450
1662
|
renameSync3(oldPath, newPath);
|
|
1451
|
-
} else if (
|
|
1452
|
-
const content =
|
|
1453
|
-
|
|
1663
|
+
} else if (existsSync7(oldPath) && oldPath !== newPath) {
|
|
1664
|
+
const content = readFileSync5(oldPath, "utf-8");
|
|
1665
|
+
writeFileSync3(newPath, content, "utf-8");
|
|
1454
1666
|
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
1455
1667
|
unlinkSync3(oldPath);
|
|
1456
1668
|
}
|
|
@@ -1480,7 +1692,7 @@ function registerBinSymlinks(name) {
|
|
|
1480
1692
|
errors.push("Could not find 'exe-os' in PATH");
|
|
1481
1693
|
return { created, skipped, errors };
|
|
1482
1694
|
}
|
|
1483
|
-
const binDir =
|
|
1695
|
+
const binDir = path7.dirname(exeBinPath);
|
|
1484
1696
|
let target;
|
|
1485
1697
|
try {
|
|
1486
1698
|
target = readlinkSync(exeBinPath);
|
|
@@ -1490,8 +1702,8 @@ function registerBinSymlinks(name) {
|
|
|
1490
1702
|
}
|
|
1491
1703
|
for (const suffix of ["", "-opencode"]) {
|
|
1492
1704
|
const linkName = `${name}${suffix}`;
|
|
1493
|
-
const linkPath =
|
|
1494
|
-
if (
|
|
1705
|
+
const linkPath = path7.join(binDir, linkName);
|
|
1706
|
+
if (existsSync7(linkPath)) {
|
|
1495
1707
|
skipped.push(linkName);
|
|
1496
1708
|
continue;
|
|
1497
1709
|
}
|
|
@@ -1504,15 +1716,601 @@ function registerBinSymlinks(name) {
|
|
|
1504
1716
|
}
|
|
1505
1717
|
return { created, skipped, errors };
|
|
1506
1718
|
}
|
|
1507
|
-
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
|
|
1719
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
|
|
1508
1720
|
var init_employees = __esm({
|
|
1509
1721
|
"src/lib/employees.ts"() {
|
|
1510
1722
|
"use strict";
|
|
1511
1723
|
init_config();
|
|
1512
|
-
EMPLOYEES_PATH =
|
|
1724
|
+
EMPLOYEES_PATH = path7.join(EXE_AI_DIR, "exe-employees.json");
|
|
1513
1725
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
1514
1726
|
COORDINATOR_ROLE = "COO";
|
|
1515
1727
|
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
1728
|
+
IDENTITY_DIR = path7.join(EXE_AI_DIR, "identity");
|
|
1729
|
+
TEAM_SECTION_RE = /^## Team\b.*$/m;
|
|
1730
|
+
}
|
|
1731
|
+
});
|
|
1732
|
+
|
|
1733
|
+
// src/lib/database-adapter.ts
|
|
1734
|
+
import os5 from "os";
|
|
1735
|
+
import path8 from "path";
|
|
1736
|
+
import { createRequire } from "module";
|
|
1737
|
+
import { pathToFileURL } from "url";
|
|
1738
|
+
function quotedIdentifier(identifier) {
|
|
1739
|
+
return `"${identifier.replace(/"/g, '""')}"`;
|
|
1740
|
+
}
|
|
1741
|
+
function unqualifiedTableName(name) {
|
|
1742
|
+
const raw = name.trim().replace(/^"|"$/g, "");
|
|
1743
|
+
const parts = raw.split(".");
|
|
1744
|
+
return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
|
|
1745
|
+
}
|
|
1746
|
+
function stripTrailingSemicolon(sql) {
|
|
1747
|
+
return sql.trim().replace(/;+\s*$/u, "");
|
|
1748
|
+
}
|
|
1749
|
+
function appendClause(sql, clause) {
|
|
1750
|
+
const trimmed = stripTrailingSemicolon(sql);
|
|
1751
|
+
const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
|
|
1752
|
+
if (!returningMatch) {
|
|
1753
|
+
return `${trimmed}${clause}`;
|
|
1754
|
+
}
|
|
1755
|
+
const idx = returningMatch.index;
|
|
1756
|
+
return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
|
|
1757
|
+
}
|
|
1758
|
+
function normalizeStatement(stmt) {
|
|
1759
|
+
if (typeof stmt === "string") {
|
|
1760
|
+
return { kind: "positional", sql: stmt, args: [] };
|
|
1761
|
+
}
|
|
1762
|
+
const sql = stmt.sql;
|
|
1763
|
+
if (Array.isArray(stmt.args) || stmt.args === void 0) {
|
|
1764
|
+
return { kind: "positional", sql, args: stmt.args ?? [] };
|
|
1765
|
+
}
|
|
1766
|
+
return { kind: "named", sql, args: stmt.args };
|
|
1767
|
+
}
|
|
1768
|
+
function rewriteBooleanLiterals(sql) {
|
|
1769
|
+
let out = sql;
|
|
1770
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
1771
|
+
const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
|
|
1772
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
|
|
1773
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
|
|
1774
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
|
|
1775
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
|
|
1776
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
|
|
1777
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
|
|
1778
|
+
}
|
|
1779
|
+
return out;
|
|
1780
|
+
}
|
|
1781
|
+
function rewriteInsertOrIgnore(sql) {
|
|
1782
|
+
if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
|
|
1783
|
+
return sql;
|
|
1784
|
+
}
|
|
1785
|
+
const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
|
|
1786
|
+
return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
|
|
1787
|
+
}
|
|
1788
|
+
function rewriteInsertOrReplace(sql) {
|
|
1789
|
+
const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
|
|
1790
|
+
if (!match) {
|
|
1791
|
+
return sql;
|
|
1792
|
+
}
|
|
1793
|
+
const rawTable = match[1];
|
|
1794
|
+
const rawColumns = match[2];
|
|
1795
|
+
const remainder = match[3];
|
|
1796
|
+
const tableName = unqualifiedTableName(rawTable);
|
|
1797
|
+
const conflictKeys = UPSERT_KEYS[tableName];
|
|
1798
|
+
if (!conflictKeys?.length) {
|
|
1799
|
+
return sql;
|
|
1800
|
+
}
|
|
1801
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
1802
|
+
const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
|
|
1803
|
+
const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
|
|
1804
|
+
const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
|
|
1805
|
+
return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
|
|
1806
|
+
}
|
|
1807
|
+
function rewriteSql(sql) {
|
|
1808
|
+
let out = sql;
|
|
1809
|
+
out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
|
|
1810
|
+
out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
|
|
1811
|
+
out = rewriteBooleanLiterals(out);
|
|
1812
|
+
out = rewriteInsertOrReplace(out);
|
|
1813
|
+
out = rewriteInsertOrIgnore(out);
|
|
1814
|
+
return stripTrailingSemicolon(out);
|
|
1815
|
+
}
|
|
1816
|
+
function toBoolean(value) {
|
|
1817
|
+
if (value === null || value === void 0) return value;
|
|
1818
|
+
if (typeof value === "boolean") return value;
|
|
1819
|
+
if (typeof value === "number") return value !== 0;
|
|
1820
|
+
if (typeof value === "bigint") return value !== 0n;
|
|
1821
|
+
if (typeof value === "string") {
|
|
1822
|
+
const normalized = value.trim().toLowerCase();
|
|
1823
|
+
if (normalized === "0" || normalized === "false") return false;
|
|
1824
|
+
if (normalized === "1" || normalized === "true") return true;
|
|
1825
|
+
}
|
|
1826
|
+
return Boolean(value);
|
|
1827
|
+
}
|
|
1828
|
+
function countQuestionMarks(sql, end) {
|
|
1829
|
+
let count = 0;
|
|
1830
|
+
let inSingle = false;
|
|
1831
|
+
let inDouble = false;
|
|
1832
|
+
let inLineComment = false;
|
|
1833
|
+
let inBlockComment = false;
|
|
1834
|
+
for (let i = 0; i < end; i++) {
|
|
1835
|
+
const ch = sql[i];
|
|
1836
|
+
const next = sql[i + 1];
|
|
1837
|
+
if (inLineComment) {
|
|
1838
|
+
if (ch === "\n") inLineComment = false;
|
|
1839
|
+
continue;
|
|
1840
|
+
}
|
|
1841
|
+
if (inBlockComment) {
|
|
1842
|
+
if (ch === "*" && next === "/") {
|
|
1843
|
+
inBlockComment = false;
|
|
1844
|
+
i += 1;
|
|
1845
|
+
}
|
|
1846
|
+
continue;
|
|
1847
|
+
}
|
|
1848
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
1849
|
+
inLineComment = true;
|
|
1850
|
+
i += 1;
|
|
1851
|
+
continue;
|
|
1852
|
+
}
|
|
1853
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
1854
|
+
inBlockComment = true;
|
|
1855
|
+
i += 1;
|
|
1856
|
+
continue;
|
|
1857
|
+
}
|
|
1858
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
1859
|
+
inSingle = !inSingle;
|
|
1860
|
+
continue;
|
|
1861
|
+
}
|
|
1862
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
1863
|
+
inDouble = !inDouble;
|
|
1864
|
+
continue;
|
|
1865
|
+
}
|
|
1866
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
1867
|
+
count += 1;
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
return count;
|
|
1871
|
+
}
|
|
1872
|
+
function findBooleanPlaceholderIndexes(sql) {
|
|
1873
|
+
const indexes = /* @__PURE__ */ new Set();
|
|
1874
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
1875
|
+
const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
|
|
1876
|
+
for (const match of sql.matchAll(pattern)) {
|
|
1877
|
+
const matchText = match[0];
|
|
1878
|
+
const qIndex = match.index + matchText.lastIndexOf("?");
|
|
1879
|
+
indexes.add(countQuestionMarks(sql, qIndex + 1));
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
return indexes;
|
|
1883
|
+
}
|
|
1884
|
+
function coerceInsertBooleanArgs(sql, args2) {
|
|
1885
|
+
const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
|
|
1886
|
+
if (!match) return;
|
|
1887
|
+
const rawTable = match[1];
|
|
1888
|
+
const rawColumns = match[2];
|
|
1889
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
1890
|
+
if (!boolColumns?.size) return;
|
|
1891
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
1892
|
+
for (const [index, column] of columns.entries()) {
|
|
1893
|
+
if (boolColumns.has(column) && index < args2.length) {
|
|
1894
|
+
args2[index] = toBoolean(args2[index]);
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
function coerceUpdateBooleanArgs(sql, args2) {
|
|
1899
|
+
const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
|
|
1900
|
+
if (!match) return;
|
|
1901
|
+
const rawTable = match[1];
|
|
1902
|
+
const setClause = match[2];
|
|
1903
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
1904
|
+
if (!boolColumns?.size) return;
|
|
1905
|
+
const assignments = setClause.split(",");
|
|
1906
|
+
let placeholderIndex = 0;
|
|
1907
|
+
for (const assignment of assignments) {
|
|
1908
|
+
if (!assignment.includes("?")) continue;
|
|
1909
|
+
placeholderIndex += 1;
|
|
1910
|
+
const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
|
|
1911
|
+
if (colMatch && boolColumns.has(colMatch[1])) {
|
|
1912
|
+
args2[placeholderIndex - 1] = toBoolean(args2[placeholderIndex - 1]);
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
function coerceBooleanArgs(sql, args2) {
|
|
1917
|
+
const nextArgs = [...args2];
|
|
1918
|
+
coerceInsertBooleanArgs(sql, nextArgs);
|
|
1919
|
+
coerceUpdateBooleanArgs(sql, nextArgs);
|
|
1920
|
+
const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
|
|
1921
|
+
for (const index of placeholderIndexes) {
|
|
1922
|
+
if (index > 0 && index <= nextArgs.length) {
|
|
1923
|
+
nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
return nextArgs;
|
|
1927
|
+
}
|
|
1928
|
+
function convertQuestionMarksToDollarParams(sql) {
|
|
1929
|
+
let out = "";
|
|
1930
|
+
let placeholder = 0;
|
|
1931
|
+
let inSingle = false;
|
|
1932
|
+
let inDouble = false;
|
|
1933
|
+
let inLineComment = false;
|
|
1934
|
+
let inBlockComment = false;
|
|
1935
|
+
for (let i = 0; i < sql.length; i++) {
|
|
1936
|
+
const ch = sql[i];
|
|
1937
|
+
const next = sql[i + 1];
|
|
1938
|
+
if (inLineComment) {
|
|
1939
|
+
out += ch;
|
|
1940
|
+
if (ch === "\n") inLineComment = false;
|
|
1941
|
+
continue;
|
|
1942
|
+
}
|
|
1943
|
+
if (inBlockComment) {
|
|
1944
|
+
out += ch;
|
|
1945
|
+
if (ch === "*" && next === "/") {
|
|
1946
|
+
out += next;
|
|
1947
|
+
inBlockComment = false;
|
|
1948
|
+
i += 1;
|
|
1949
|
+
}
|
|
1950
|
+
continue;
|
|
1951
|
+
}
|
|
1952
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
1953
|
+
out += ch + next;
|
|
1954
|
+
inLineComment = true;
|
|
1955
|
+
i += 1;
|
|
1956
|
+
continue;
|
|
1957
|
+
}
|
|
1958
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
1959
|
+
out += ch + next;
|
|
1960
|
+
inBlockComment = true;
|
|
1961
|
+
i += 1;
|
|
1962
|
+
continue;
|
|
1963
|
+
}
|
|
1964
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
1965
|
+
inSingle = !inSingle;
|
|
1966
|
+
out += ch;
|
|
1967
|
+
continue;
|
|
1968
|
+
}
|
|
1969
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
1970
|
+
inDouble = !inDouble;
|
|
1971
|
+
out += ch;
|
|
1972
|
+
continue;
|
|
1973
|
+
}
|
|
1974
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
1975
|
+
placeholder += 1;
|
|
1976
|
+
out += `$${placeholder}`;
|
|
1977
|
+
continue;
|
|
1978
|
+
}
|
|
1979
|
+
out += ch;
|
|
1980
|
+
}
|
|
1981
|
+
return out;
|
|
1982
|
+
}
|
|
1983
|
+
function translateStatementForPostgres(stmt) {
|
|
1984
|
+
const normalized = normalizeStatement(stmt);
|
|
1985
|
+
if (normalized.kind === "named") {
|
|
1986
|
+
throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
|
|
1987
|
+
}
|
|
1988
|
+
const rewrittenSql = rewriteSql(normalized.sql);
|
|
1989
|
+
const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
|
|
1990
|
+
return {
|
|
1991
|
+
sql: convertQuestionMarksToDollarParams(rewrittenSql),
|
|
1992
|
+
args: coercedArgs
|
|
1993
|
+
};
|
|
1994
|
+
}
|
|
1995
|
+
function shouldBypassPostgres(stmt) {
|
|
1996
|
+
const normalized = normalizeStatement(stmt);
|
|
1997
|
+
if (normalized.kind === "named") {
|
|
1998
|
+
return true;
|
|
1999
|
+
}
|
|
2000
|
+
return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
|
|
2001
|
+
}
|
|
2002
|
+
function shouldFallbackOnError(error) {
|
|
2003
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2004
|
+
return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
|
|
2005
|
+
}
|
|
2006
|
+
function isReadQuery(sql) {
|
|
2007
|
+
const trimmed = sql.trimStart();
|
|
2008
|
+
return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
|
|
2009
|
+
}
|
|
2010
|
+
function buildRow(row, columns) {
|
|
2011
|
+
const values = columns.map((column) => row[column]);
|
|
2012
|
+
return Object.assign(values, row);
|
|
2013
|
+
}
|
|
2014
|
+
function buildResultSet(rows, rowsAffected = 0) {
|
|
2015
|
+
const columns = rows[0] ? Object.keys(rows[0]) : [];
|
|
2016
|
+
const resultRows = rows.map((row) => buildRow(row, columns));
|
|
2017
|
+
return {
|
|
2018
|
+
columns,
|
|
2019
|
+
columnTypes: columns.map(() => ""),
|
|
2020
|
+
rows: resultRows,
|
|
2021
|
+
rowsAffected,
|
|
2022
|
+
lastInsertRowid: void 0,
|
|
2023
|
+
toJSON() {
|
|
2024
|
+
return {
|
|
2025
|
+
columns,
|
|
2026
|
+
columnTypes: columns.map(() => ""),
|
|
2027
|
+
rows,
|
|
2028
|
+
rowsAffected,
|
|
2029
|
+
lastInsertRowid: void 0
|
|
2030
|
+
};
|
|
2031
|
+
}
|
|
2032
|
+
};
|
|
2033
|
+
}
|
|
2034
|
+
async function loadPrismaClient() {
|
|
2035
|
+
if (!prismaClientPromise) {
|
|
2036
|
+
prismaClientPromise = (async () => {
|
|
2037
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
2038
|
+
if (explicitPath) {
|
|
2039
|
+
const module2 = await import(pathToFileURL(explicitPath).href);
|
|
2040
|
+
const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
|
|
2041
|
+
if (!PrismaClient2) {
|
|
2042
|
+
throw new Error(`No PrismaClient export found at ${explicitPath}`);
|
|
2043
|
+
}
|
|
2044
|
+
return new PrismaClient2();
|
|
2045
|
+
}
|
|
2046
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path8.join(os5.homedir(), "exe-db");
|
|
2047
|
+
const requireFromExeDb = createRequire(path8.join(exeDbRoot, "package.json"));
|
|
2048
|
+
const prismaEntry = requireFromExeDb.resolve("@prisma/client");
|
|
2049
|
+
const module = await import(pathToFileURL(prismaEntry).href);
|
|
2050
|
+
const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
|
|
2051
|
+
if (!PrismaClient) {
|
|
2052
|
+
throw new Error(`No PrismaClient export found in ${prismaEntry}`);
|
|
2053
|
+
}
|
|
2054
|
+
return new PrismaClient();
|
|
2055
|
+
})();
|
|
2056
|
+
}
|
|
2057
|
+
return prismaClientPromise;
|
|
2058
|
+
}
|
|
2059
|
+
async function ensureCompatibilityViews(prisma) {
|
|
2060
|
+
if (!compatibilityBootstrapPromise) {
|
|
2061
|
+
compatibilityBootstrapPromise = (async () => {
|
|
2062
|
+
for (const mapping of VIEW_MAPPINGS) {
|
|
2063
|
+
const relation = mapping.source.replace(/"/g, "");
|
|
2064
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
2065
|
+
"SELECT to_regclass($1) AS regclass",
|
|
2066
|
+
relation
|
|
2067
|
+
);
|
|
2068
|
+
if (!rows[0]?.regclass) {
|
|
2069
|
+
continue;
|
|
2070
|
+
}
|
|
2071
|
+
await prisma.$executeRawUnsafe(
|
|
2072
|
+
`CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
|
|
2073
|
+
);
|
|
2074
|
+
}
|
|
2075
|
+
})();
|
|
2076
|
+
}
|
|
2077
|
+
return compatibilityBootstrapPromise;
|
|
2078
|
+
}
|
|
2079
|
+
async function executeOnPrisma(executor, stmt) {
|
|
2080
|
+
const translated = translateStatementForPostgres(stmt);
|
|
2081
|
+
if (isReadQuery(translated.sql)) {
|
|
2082
|
+
const rows = await executor.$queryRawUnsafe(
|
|
2083
|
+
translated.sql,
|
|
2084
|
+
...translated.args
|
|
2085
|
+
);
|
|
2086
|
+
return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
|
|
2087
|
+
}
|
|
2088
|
+
const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
|
|
2089
|
+
return buildResultSet([], rowsAffected);
|
|
2090
|
+
}
|
|
2091
|
+
function splitSqlStatements(sql) {
|
|
2092
|
+
const parts = [];
|
|
2093
|
+
let current = "";
|
|
2094
|
+
let inSingle = false;
|
|
2095
|
+
let inDouble = false;
|
|
2096
|
+
let inLineComment = false;
|
|
2097
|
+
let inBlockComment = false;
|
|
2098
|
+
for (let i = 0; i < sql.length; i++) {
|
|
2099
|
+
const ch = sql[i];
|
|
2100
|
+
const next = sql[i + 1];
|
|
2101
|
+
if (inLineComment) {
|
|
2102
|
+
current += ch;
|
|
2103
|
+
if (ch === "\n") inLineComment = false;
|
|
2104
|
+
continue;
|
|
2105
|
+
}
|
|
2106
|
+
if (inBlockComment) {
|
|
2107
|
+
current += ch;
|
|
2108
|
+
if (ch === "*" && next === "/") {
|
|
2109
|
+
current += next;
|
|
2110
|
+
inBlockComment = false;
|
|
2111
|
+
i += 1;
|
|
2112
|
+
}
|
|
2113
|
+
continue;
|
|
2114
|
+
}
|
|
2115
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
2116
|
+
current += ch + next;
|
|
2117
|
+
inLineComment = true;
|
|
2118
|
+
i += 1;
|
|
2119
|
+
continue;
|
|
2120
|
+
}
|
|
2121
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
2122
|
+
current += ch + next;
|
|
2123
|
+
inBlockComment = true;
|
|
2124
|
+
i += 1;
|
|
2125
|
+
continue;
|
|
2126
|
+
}
|
|
2127
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
2128
|
+
inSingle = !inSingle;
|
|
2129
|
+
current += ch;
|
|
2130
|
+
continue;
|
|
2131
|
+
}
|
|
2132
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
2133
|
+
inDouble = !inDouble;
|
|
2134
|
+
current += ch;
|
|
2135
|
+
continue;
|
|
2136
|
+
}
|
|
2137
|
+
if (!inSingle && !inDouble && ch === ";") {
|
|
2138
|
+
if (current.trim()) {
|
|
2139
|
+
parts.push(current.trim());
|
|
2140
|
+
}
|
|
2141
|
+
current = "";
|
|
2142
|
+
continue;
|
|
2143
|
+
}
|
|
2144
|
+
current += ch;
|
|
2145
|
+
}
|
|
2146
|
+
if (current.trim()) {
|
|
2147
|
+
parts.push(current.trim());
|
|
2148
|
+
}
|
|
2149
|
+
return parts;
|
|
2150
|
+
}
|
|
2151
|
+
async function createPrismaDbAdapter(fallbackClient) {
|
|
2152
|
+
const prisma = await loadPrismaClient();
|
|
2153
|
+
await ensureCompatibilityViews(prisma);
|
|
2154
|
+
let closed = false;
|
|
2155
|
+
let adapter;
|
|
2156
|
+
const fallbackExecute = async (stmt, error) => {
|
|
2157
|
+
if (!fallbackClient) {
|
|
2158
|
+
if (error) throw error;
|
|
2159
|
+
throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
|
|
2160
|
+
}
|
|
2161
|
+
if (error) {
|
|
2162
|
+
process.stderr.write(
|
|
2163
|
+
`[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
2164
|
+
`
|
|
2165
|
+
);
|
|
2166
|
+
}
|
|
2167
|
+
return fallbackClient.execute(stmt);
|
|
2168
|
+
};
|
|
2169
|
+
adapter = {
|
|
2170
|
+
async execute(stmt) {
|
|
2171
|
+
if (shouldBypassPostgres(stmt)) {
|
|
2172
|
+
return fallbackExecute(stmt);
|
|
2173
|
+
}
|
|
2174
|
+
try {
|
|
2175
|
+
return await executeOnPrisma(prisma, stmt);
|
|
2176
|
+
} catch (error) {
|
|
2177
|
+
if (shouldFallbackOnError(error)) {
|
|
2178
|
+
return fallbackExecute(stmt, error);
|
|
2179
|
+
}
|
|
2180
|
+
throw error;
|
|
2181
|
+
}
|
|
2182
|
+
},
|
|
2183
|
+
async batch(stmts, mode) {
|
|
2184
|
+
if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
|
|
2185
|
+
if (!fallbackClient) {
|
|
2186
|
+
throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
|
|
2187
|
+
}
|
|
2188
|
+
return fallbackClient.batch(stmts, mode);
|
|
2189
|
+
}
|
|
2190
|
+
try {
|
|
2191
|
+
if (prisma.$transaction) {
|
|
2192
|
+
return await prisma.$transaction(async (tx) => {
|
|
2193
|
+
const results2 = [];
|
|
2194
|
+
for (const stmt of stmts) {
|
|
2195
|
+
results2.push(await executeOnPrisma(tx, stmt));
|
|
2196
|
+
}
|
|
2197
|
+
return results2;
|
|
2198
|
+
});
|
|
2199
|
+
}
|
|
2200
|
+
const results = [];
|
|
2201
|
+
for (const stmt of stmts) {
|
|
2202
|
+
results.push(await executeOnPrisma(prisma, stmt));
|
|
2203
|
+
}
|
|
2204
|
+
return results;
|
|
2205
|
+
} catch (error) {
|
|
2206
|
+
if (fallbackClient && shouldFallbackOnError(error)) {
|
|
2207
|
+
process.stderr.write(
|
|
2208
|
+
`[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
2209
|
+
`
|
|
2210
|
+
);
|
|
2211
|
+
return fallbackClient.batch(stmts, mode);
|
|
2212
|
+
}
|
|
2213
|
+
throw error;
|
|
2214
|
+
}
|
|
2215
|
+
},
|
|
2216
|
+
async migrate(stmts) {
|
|
2217
|
+
if (fallbackClient) {
|
|
2218
|
+
return fallbackClient.migrate(stmts);
|
|
2219
|
+
}
|
|
2220
|
+
return adapter.batch(stmts, "deferred");
|
|
2221
|
+
},
|
|
2222
|
+
async transaction(mode) {
|
|
2223
|
+
if (!fallbackClient) {
|
|
2224
|
+
throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
|
|
2225
|
+
}
|
|
2226
|
+
return fallbackClient.transaction(mode);
|
|
2227
|
+
},
|
|
2228
|
+
async executeMultiple(sql) {
|
|
2229
|
+
if (fallbackClient && shouldBypassPostgres(sql)) {
|
|
2230
|
+
return fallbackClient.executeMultiple(sql);
|
|
2231
|
+
}
|
|
2232
|
+
for (const statement of splitSqlStatements(sql)) {
|
|
2233
|
+
await adapter.execute(statement);
|
|
2234
|
+
}
|
|
2235
|
+
},
|
|
2236
|
+
async sync() {
|
|
2237
|
+
if (fallbackClient) {
|
|
2238
|
+
return fallbackClient.sync();
|
|
2239
|
+
}
|
|
2240
|
+
return { frame_no: 0, frames_synced: 0 };
|
|
2241
|
+
},
|
|
2242
|
+
close() {
|
|
2243
|
+
closed = true;
|
|
2244
|
+
prismaClientPromise = null;
|
|
2245
|
+
compatibilityBootstrapPromise = null;
|
|
2246
|
+
void prisma.$disconnect?.();
|
|
2247
|
+
},
|
|
2248
|
+
get closed() {
|
|
2249
|
+
return closed;
|
|
2250
|
+
},
|
|
2251
|
+
get protocol() {
|
|
2252
|
+
return "prisma-postgres";
|
|
2253
|
+
}
|
|
2254
|
+
};
|
|
2255
|
+
return adapter;
|
|
2256
|
+
}
|
|
2257
|
+
var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
|
|
2258
|
+
var init_database_adapter = __esm({
|
|
2259
|
+
"src/lib/database-adapter.ts"() {
|
|
2260
|
+
"use strict";
|
|
2261
|
+
VIEW_MAPPINGS = [
|
|
2262
|
+
{ view: "memories", source: "memory.memory_records" },
|
|
2263
|
+
{ view: "tasks", source: "memory.tasks" },
|
|
2264
|
+
{ view: "behaviors", source: "memory.behaviors" },
|
|
2265
|
+
{ view: "entities", source: "memory.entities" },
|
|
2266
|
+
{ view: "relationships", source: "memory.relationships" },
|
|
2267
|
+
{ view: "entity_memories", source: "memory.entity_memories" },
|
|
2268
|
+
{ view: "entity_aliases", source: "memory.entity_aliases" },
|
|
2269
|
+
{ view: "notifications", source: "memory.notifications" },
|
|
2270
|
+
{ view: "messages", source: "memory.messages" },
|
|
2271
|
+
{ view: "users", source: "wiki.users" },
|
|
2272
|
+
{ view: "workspaces", source: "wiki.workspaces" },
|
|
2273
|
+
{ view: "workspace_users", source: "wiki.workspace_users" },
|
|
2274
|
+
{ view: "documents", source: "wiki.workspace_documents" },
|
|
2275
|
+
{ view: "chats", source: "wiki.workspace_chats" }
|
|
2276
|
+
];
|
|
2277
|
+
UPSERT_KEYS = {
|
|
2278
|
+
memories: ["id"],
|
|
2279
|
+
tasks: ["id"],
|
|
2280
|
+
behaviors: ["id"],
|
|
2281
|
+
entities: ["id"],
|
|
2282
|
+
relationships: ["id"],
|
|
2283
|
+
entity_aliases: ["alias"],
|
|
2284
|
+
notifications: ["id"],
|
|
2285
|
+
messages: ["id"],
|
|
2286
|
+
users: ["id"],
|
|
2287
|
+
workspaces: ["id"],
|
|
2288
|
+
workspace_users: ["id"],
|
|
2289
|
+
documents: ["id"],
|
|
2290
|
+
chats: ["id"]
|
|
2291
|
+
};
|
|
2292
|
+
BOOLEAN_COLUMNS_BY_TABLE = {
|
|
2293
|
+
memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
|
|
2294
|
+
behaviors: /* @__PURE__ */ new Set(["active"]),
|
|
2295
|
+
notifications: /* @__PURE__ */ new Set(["read"]),
|
|
2296
|
+
users: /* @__PURE__ */ new Set(["has_personal_memory"])
|
|
2297
|
+
};
|
|
2298
|
+
BOOLEAN_COLUMN_NAMES = new Set(
|
|
2299
|
+
Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
|
|
2300
|
+
);
|
|
2301
|
+
IMMEDIATE_FALLBACK_PATTERNS = [
|
|
2302
|
+
/\bPRAGMA\b/i,
|
|
2303
|
+
/\bsqlite_master\b/i,
|
|
2304
|
+
/(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
|
|
2305
|
+
/\bMATCH\b/i,
|
|
2306
|
+
/\bvector_distance_cos\s*\(/i,
|
|
2307
|
+
/\bjson_extract\s*\(/i,
|
|
2308
|
+
/\bjulianday\s*\(/i,
|
|
2309
|
+
/\bstrftime\s*\(/i,
|
|
2310
|
+
/\blast_insert_rowid\s*\(/i
|
|
2311
|
+
];
|
|
2312
|
+
prismaClientPromise = null;
|
|
2313
|
+
compatibilityBootstrapPromise = null;
|
|
1516
2314
|
}
|
|
1517
2315
|
});
|
|
1518
2316
|
|
|
@@ -1586,7 +2384,7 @@ __export(db_daemon_client_exports, {
|
|
|
1586
2384
|
createDaemonDbClient: () => createDaemonDbClient,
|
|
1587
2385
|
initDaemonDbClient: () => initDaemonDbClient
|
|
1588
2386
|
});
|
|
1589
|
-
function
|
|
2387
|
+
function normalizeStatement2(stmt) {
|
|
1590
2388
|
if (typeof stmt === "string") {
|
|
1591
2389
|
return { sql: stmt, args: [] };
|
|
1592
2390
|
}
|
|
@@ -1610,7 +2408,7 @@ function createDaemonDbClient(fallbackClient) {
|
|
|
1610
2408
|
if (!_useDaemon || !isClientConnected()) {
|
|
1611
2409
|
return fallbackClient.execute(stmt);
|
|
1612
2410
|
}
|
|
1613
|
-
const { sql, args: args2 } =
|
|
2411
|
+
const { sql, args: args2 } = normalizeStatement2(stmt);
|
|
1614
2412
|
const response = await sendDaemonRequest({
|
|
1615
2413
|
type: "db-execute",
|
|
1616
2414
|
sql,
|
|
@@ -1635,7 +2433,7 @@ function createDaemonDbClient(fallbackClient) {
|
|
|
1635
2433
|
if (!_useDaemon || !isClientConnected()) {
|
|
1636
2434
|
return fallbackClient.batch(stmts, mode);
|
|
1637
2435
|
}
|
|
1638
|
-
const statements = stmts.map(
|
|
2436
|
+
const statements = stmts.map(normalizeStatement2);
|
|
1639
2437
|
const response = await sendDaemonRequest({
|
|
1640
2438
|
type: "db-batch",
|
|
1641
2439
|
statements,
|
|
@@ -1730,6 +2528,18 @@ __export(database_exports, {
|
|
|
1730
2528
|
});
|
|
1731
2529
|
import { createClient } from "@libsql/client";
|
|
1732
2530
|
async function initDatabase(config) {
|
|
2531
|
+
if (_walCheckpointTimer) {
|
|
2532
|
+
clearInterval(_walCheckpointTimer);
|
|
2533
|
+
_walCheckpointTimer = null;
|
|
2534
|
+
}
|
|
2535
|
+
if (_daemonClient) {
|
|
2536
|
+
_daemonClient.close();
|
|
2537
|
+
_daemonClient = null;
|
|
2538
|
+
}
|
|
2539
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
2540
|
+
_adapterClient.close();
|
|
2541
|
+
}
|
|
2542
|
+
_adapterClient = null;
|
|
1733
2543
|
if (_client) {
|
|
1734
2544
|
_client.close();
|
|
1735
2545
|
_client = null;
|
|
@@ -1743,6 +2553,7 @@ async function initDatabase(config) {
|
|
|
1743
2553
|
}
|
|
1744
2554
|
_client = createClient(opts);
|
|
1745
2555
|
_resilientClient = wrapWithRetry(_client);
|
|
2556
|
+
_adapterClient = _resilientClient;
|
|
1746
2557
|
_client.execute("PRAGMA busy_timeout = 30000").catch(() => {
|
|
1747
2558
|
});
|
|
1748
2559
|
_client.execute("PRAGMA journal_mode = WAL").catch(() => {
|
|
@@ -1753,14 +2564,20 @@ async function initDatabase(config) {
|
|
|
1753
2564
|
});
|
|
1754
2565
|
}, 3e4);
|
|
1755
2566
|
_walCheckpointTimer.unref();
|
|
2567
|
+
if (process.env.DATABASE_URL) {
|
|
2568
|
+
_adapterClient = await createPrismaDbAdapter(_resilientClient);
|
|
2569
|
+
}
|
|
1756
2570
|
}
|
|
1757
2571
|
function isInitialized() {
|
|
1758
|
-
return _client !== null;
|
|
2572
|
+
return _adapterClient !== null || _client !== null;
|
|
1759
2573
|
}
|
|
1760
2574
|
function getClient() {
|
|
1761
|
-
if (!
|
|
2575
|
+
if (!_adapterClient) {
|
|
1762
2576
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
1763
2577
|
}
|
|
2578
|
+
if (process.env.DATABASE_URL) {
|
|
2579
|
+
return _adapterClient;
|
|
2580
|
+
}
|
|
1764
2581
|
if (process.env.EXE_IS_DAEMON === "1") {
|
|
1765
2582
|
return _resilientClient;
|
|
1766
2583
|
}
|
|
@@ -1770,6 +2587,7 @@ function getClient() {
|
|
|
1770
2587
|
return _resilientClient;
|
|
1771
2588
|
}
|
|
1772
2589
|
async function initDaemonClient() {
|
|
2590
|
+
if (process.env.DATABASE_URL) return;
|
|
1773
2591
|
if (process.env.EXE_IS_DAEMON === "1") return;
|
|
1774
2592
|
if (!_resilientClient) return;
|
|
1775
2593
|
try {
|
|
@@ -2714,26 +3532,36 @@ async function ensureSchema() {
|
|
|
2714
3532
|
}
|
|
2715
3533
|
}
|
|
2716
3534
|
async function disposeDatabase() {
|
|
3535
|
+
if (_walCheckpointTimer) {
|
|
3536
|
+
clearInterval(_walCheckpointTimer);
|
|
3537
|
+
_walCheckpointTimer = null;
|
|
3538
|
+
}
|
|
2717
3539
|
if (_daemonClient) {
|
|
2718
3540
|
_daemonClient.close();
|
|
2719
3541
|
_daemonClient = null;
|
|
2720
3542
|
}
|
|
3543
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
3544
|
+
_adapterClient.close();
|
|
3545
|
+
}
|
|
3546
|
+
_adapterClient = null;
|
|
2721
3547
|
if (_client) {
|
|
2722
3548
|
_client.close();
|
|
2723
3549
|
_client = null;
|
|
2724
3550
|
_resilientClient = null;
|
|
2725
3551
|
}
|
|
2726
3552
|
}
|
|
2727
|
-
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
|
|
3553
|
+
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
|
|
2728
3554
|
var init_database = __esm({
|
|
2729
3555
|
"src/lib/database.ts"() {
|
|
2730
3556
|
"use strict";
|
|
2731
3557
|
init_db_retry();
|
|
2732
3558
|
init_employees();
|
|
3559
|
+
init_database_adapter();
|
|
2733
3560
|
_client = null;
|
|
2734
3561
|
_resilientClient = null;
|
|
2735
3562
|
_walCheckpointTimer = null;
|
|
2736
3563
|
_daemonClient = null;
|
|
3564
|
+
_adapterClient = null;
|
|
2737
3565
|
initTurso = initDatabase;
|
|
2738
3566
|
disposeTurso = disposeDatabase;
|
|
2739
3567
|
}
|
|
@@ -2778,8 +3606,8 @@ __export(crdt_sync_exports, {
|
|
|
2778
3606
|
rebuildFromDb: () => rebuildFromDb
|
|
2779
3607
|
});
|
|
2780
3608
|
import * as Y from "yjs";
|
|
2781
|
-
import { readFileSync as
|
|
2782
|
-
import
|
|
3609
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync8, mkdirSync as mkdirSync3, unlinkSync as unlinkSync4 } from "fs";
|
|
3610
|
+
import path9 from "path";
|
|
2783
3611
|
import { homedir } from "os";
|
|
2784
3612
|
function getStatePath() {
|
|
2785
3613
|
return _statePathOverride ?? DEFAULT_STATE_PATH;
|
|
@@ -2791,9 +3619,9 @@ function initCrdtDoc() {
|
|
|
2791
3619
|
if (doc) return doc;
|
|
2792
3620
|
doc = new Y.Doc();
|
|
2793
3621
|
const sp = getStatePath();
|
|
2794
|
-
if (
|
|
3622
|
+
if (existsSync8(sp)) {
|
|
2795
3623
|
try {
|
|
2796
|
-
const state =
|
|
3624
|
+
const state = readFileSync6(sp);
|
|
2797
3625
|
Y.applyUpdate(doc, new Uint8Array(state));
|
|
2798
3626
|
} catch {
|
|
2799
3627
|
console.warn("[crdt-sync] WARN: corrupted state file, rebuilding from DB");
|
|
@@ -2935,10 +3763,10 @@ function persistState() {
|
|
|
2935
3763
|
if (!doc) return;
|
|
2936
3764
|
try {
|
|
2937
3765
|
const sp = getStatePath();
|
|
2938
|
-
const dir =
|
|
2939
|
-
if (!
|
|
3766
|
+
const dir = path9.dirname(sp);
|
|
3767
|
+
if (!existsSync8(dir)) mkdirSync3(dir, { recursive: true });
|
|
2940
3768
|
const state = Y.encodeStateAsUpdate(doc);
|
|
2941
|
-
|
|
3769
|
+
writeFileSync4(sp, Buffer.from(state));
|
|
2942
3770
|
} catch {
|
|
2943
3771
|
}
|
|
2944
3772
|
}
|
|
@@ -2979,7 +3807,7 @@ var DEFAULT_STATE_PATH, _statePathOverride, doc;
|
|
|
2979
3807
|
var init_crdt_sync = __esm({
|
|
2980
3808
|
"src/lib/crdt-sync.ts"() {
|
|
2981
3809
|
"use strict";
|
|
2982
|
-
DEFAULT_STATE_PATH =
|
|
3810
|
+
DEFAULT_STATE_PATH = path9.join(homedir(), ".exe-os", "crdt-state.bin");
|
|
2983
3811
|
_statePathOverride = null;
|
|
2984
3812
|
doc = null;
|
|
2985
3813
|
}
|
|
@@ -3013,16 +3841,16 @@ __export(cloud_sync_exports, {
|
|
|
3013
3841
|
mergeRosterFromRemote: () => mergeRosterFromRemote,
|
|
3014
3842
|
recordRosterDeletion: () => recordRosterDeletion
|
|
3015
3843
|
});
|
|
3016
|
-
import { readFileSync as
|
|
3844
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync9, readdirSync, mkdirSync as mkdirSync4, appendFileSync, unlinkSync as unlinkSync5, openSync as openSync2, closeSync as closeSync2 } from "fs";
|
|
3017
3845
|
import crypto2 from "crypto";
|
|
3018
|
-
import
|
|
3846
|
+
import path10 from "path";
|
|
3019
3847
|
import { homedir as homedir2 } from "os";
|
|
3020
3848
|
function sqlSafe(v) {
|
|
3021
3849
|
return v === void 0 ? null : v;
|
|
3022
3850
|
}
|
|
3023
3851
|
function logError(msg) {
|
|
3024
3852
|
try {
|
|
3025
|
-
const logPath =
|
|
3853
|
+
const logPath = path10.join(homedir2(), ".exe-os", "workers.log");
|
|
3026
3854
|
appendFileSync(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
|
|
3027
3855
|
`);
|
|
3028
3856
|
} catch {
|
|
@@ -3032,18 +3860,18 @@ async function withRosterLock(fn) {
|
|
|
3032
3860
|
try {
|
|
3033
3861
|
const fd = openSync2(ROSTER_LOCK_PATH, "wx");
|
|
3034
3862
|
closeSync2(fd);
|
|
3035
|
-
|
|
3863
|
+
writeFileSync5(ROSTER_LOCK_PATH, String(Date.now()));
|
|
3036
3864
|
} catch (err) {
|
|
3037
3865
|
if (err.code === "EEXIST") {
|
|
3038
3866
|
try {
|
|
3039
|
-
const ts = parseInt(
|
|
3867
|
+
const ts = parseInt(readFileSync7(ROSTER_LOCK_PATH, "utf-8"), 10);
|
|
3040
3868
|
if (Date.now() - ts < LOCK_STALE_MS) {
|
|
3041
3869
|
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
3042
3870
|
}
|
|
3043
3871
|
unlinkSync5(ROSTER_LOCK_PATH);
|
|
3044
3872
|
const fd = openSync2(ROSTER_LOCK_PATH, "wx");
|
|
3045
3873
|
closeSync2(fd);
|
|
3046
|
-
|
|
3874
|
+
writeFileSync5(ROSTER_LOCK_PATH, String(Date.now()));
|
|
3047
3875
|
} catch (retryErr) {
|
|
3048
3876
|
if (retryErr instanceof Error && retryErr.message.includes("already in progress")) throw retryErr;
|
|
3049
3877
|
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
@@ -3417,8 +4245,8 @@ async function cloudSync(config) {
|
|
|
3417
4245
|
try {
|
|
3418
4246
|
const employees = await loadEmployees();
|
|
3419
4247
|
rosterResult.employees = employees.length;
|
|
3420
|
-
const idDir =
|
|
3421
|
-
if (
|
|
4248
|
+
const idDir = path10.join(EXE_AI_DIR, "identity");
|
|
4249
|
+
if (existsSync9(idDir)) {
|
|
3422
4250
|
rosterResult.identities = readdirSync(idDir).filter((f) => f.endsWith(".md")).length;
|
|
3423
4251
|
}
|
|
3424
4252
|
} catch {
|
|
@@ -3439,56 +4267,56 @@ async function cloudSync(config) {
|
|
|
3439
4267
|
function recordRosterDeletion(name) {
|
|
3440
4268
|
let deletions = [];
|
|
3441
4269
|
try {
|
|
3442
|
-
if (
|
|
3443
|
-
deletions = JSON.parse(
|
|
4270
|
+
if (existsSync9(ROSTER_DELETIONS_PATH)) {
|
|
4271
|
+
deletions = JSON.parse(readFileSync7(ROSTER_DELETIONS_PATH, "utf-8"));
|
|
3444
4272
|
}
|
|
3445
4273
|
} catch {
|
|
3446
4274
|
}
|
|
3447
4275
|
if (!deletions.includes(name)) deletions.push(name);
|
|
3448
|
-
|
|
4276
|
+
writeFileSync5(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
|
|
3449
4277
|
}
|
|
3450
4278
|
function consumeRosterDeletions() {
|
|
3451
4279
|
try {
|
|
3452
|
-
if (!
|
|
3453
|
-
const deletions = JSON.parse(
|
|
3454
|
-
|
|
4280
|
+
if (!existsSync9(ROSTER_DELETIONS_PATH)) return [];
|
|
4281
|
+
const deletions = JSON.parse(readFileSync7(ROSTER_DELETIONS_PATH, "utf-8"));
|
|
4282
|
+
writeFileSync5(ROSTER_DELETIONS_PATH, "[]");
|
|
3455
4283
|
return deletions;
|
|
3456
4284
|
} catch {
|
|
3457
4285
|
return [];
|
|
3458
4286
|
}
|
|
3459
4287
|
}
|
|
3460
4288
|
function buildRosterBlob(paths) {
|
|
3461
|
-
const rosterPath = paths?.rosterPath ??
|
|
3462
|
-
const identityDir = paths?.identityDir ??
|
|
3463
|
-
const configPath = paths?.configPath ??
|
|
4289
|
+
const rosterPath = paths?.rosterPath ?? path10.join(EXE_AI_DIR, "exe-employees.json");
|
|
4290
|
+
const identityDir = paths?.identityDir ?? path10.join(EXE_AI_DIR, "identity");
|
|
4291
|
+
const configPath = paths?.configPath ?? path10.join(EXE_AI_DIR, "config.json");
|
|
3464
4292
|
let roster = [];
|
|
3465
|
-
if (
|
|
4293
|
+
if (existsSync9(rosterPath)) {
|
|
3466
4294
|
try {
|
|
3467
|
-
roster = JSON.parse(
|
|
4295
|
+
roster = JSON.parse(readFileSync7(rosterPath, "utf-8"));
|
|
3468
4296
|
} catch {
|
|
3469
4297
|
}
|
|
3470
4298
|
}
|
|
3471
4299
|
const identities = {};
|
|
3472
|
-
if (
|
|
4300
|
+
if (existsSync9(identityDir)) {
|
|
3473
4301
|
for (const file of readdirSync(identityDir).filter((f) => f.endsWith(".md"))) {
|
|
3474
4302
|
try {
|
|
3475
|
-
identities[file] =
|
|
4303
|
+
identities[file] = readFileSync7(path10.join(identityDir, file), "utf-8");
|
|
3476
4304
|
} catch {
|
|
3477
4305
|
}
|
|
3478
4306
|
}
|
|
3479
4307
|
}
|
|
3480
4308
|
let config;
|
|
3481
|
-
if (
|
|
4309
|
+
if (existsSync9(configPath)) {
|
|
3482
4310
|
try {
|
|
3483
|
-
config = JSON.parse(
|
|
4311
|
+
config = JSON.parse(readFileSync7(configPath, "utf-8"));
|
|
3484
4312
|
} catch {
|
|
3485
4313
|
}
|
|
3486
4314
|
}
|
|
3487
4315
|
let agentConfig;
|
|
3488
|
-
const agentConfigPath =
|
|
3489
|
-
if (
|
|
4316
|
+
const agentConfigPath = path10.join(EXE_AI_DIR, "agent-config.json");
|
|
4317
|
+
if (existsSync9(agentConfigPath)) {
|
|
3490
4318
|
try {
|
|
3491
|
-
agentConfig = JSON.parse(
|
|
4319
|
+
agentConfig = JSON.parse(readFileSync7(agentConfigPath, "utf-8"));
|
|
3492
4320
|
} catch {
|
|
3493
4321
|
}
|
|
3494
4322
|
}
|
|
@@ -3564,23 +4392,23 @@ async function cloudPullRoster(config) {
|
|
|
3564
4392
|
}
|
|
3565
4393
|
}
|
|
3566
4394
|
function mergeConfig(remoteConfig, configPath) {
|
|
3567
|
-
const cfgPath = configPath ??
|
|
4395
|
+
const cfgPath = configPath ?? path10.join(EXE_AI_DIR, "config.json");
|
|
3568
4396
|
let local = {};
|
|
3569
|
-
if (
|
|
4397
|
+
if (existsSync9(cfgPath)) {
|
|
3570
4398
|
try {
|
|
3571
|
-
local = JSON.parse(
|
|
4399
|
+
local = JSON.parse(readFileSync7(cfgPath, "utf-8"));
|
|
3572
4400
|
} catch {
|
|
3573
4401
|
}
|
|
3574
4402
|
}
|
|
3575
4403
|
const merged = { ...remoteConfig, ...local };
|
|
3576
|
-
const dir =
|
|
3577
|
-
if (!
|
|
3578
|
-
|
|
4404
|
+
const dir = path10.dirname(cfgPath);
|
|
4405
|
+
if (!existsSync9(dir)) mkdirSync4(dir, { recursive: true });
|
|
4406
|
+
writeFileSync5(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
3579
4407
|
}
|
|
3580
4408
|
async function mergeRosterFromRemote(remote, paths) {
|
|
3581
4409
|
return withRosterLock(async () => {
|
|
3582
4410
|
const rosterPath = paths?.rosterPath ?? void 0;
|
|
3583
|
-
const identityDir = paths?.identityDir ??
|
|
4411
|
+
const identityDir = paths?.identityDir ?? path10.join(EXE_AI_DIR, "identity");
|
|
3584
4412
|
const localEmployees = await loadEmployees(rosterPath);
|
|
3585
4413
|
const localNames = new Set(localEmployees.map((e) => e.name));
|
|
3586
4414
|
let added = 0;
|
|
@@ -3601,15 +4429,15 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
3601
4429
|
) ?? lookupKey;
|
|
3602
4430
|
const remoteIdentity = remote.identities[matchedKey];
|
|
3603
4431
|
if (remoteIdentity) {
|
|
3604
|
-
if (!
|
|
3605
|
-
const idPath =
|
|
4432
|
+
if (!existsSync9(identityDir)) mkdirSync4(identityDir, { recursive: true });
|
|
4433
|
+
const idPath = path10.join(identityDir, `${remoteEmp.name}.md`);
|
|
3606
4434
|
let localIdentity = null;
|
|
3607
4435
|
try {
|
|
3608
|
-
localIdentity =
|
|
4436
|
+
localIdentity = existsSync9(idPath) ? readFileSync7(idPath, "utf-8") : null;
|
|
3609
4437
|
} catch {
|
|
3610
4438
|
}
|
|
3611
4439
|
if (localIdentity !== remoteIdentity) {
|
|
3612
|
-
|
|
4440
|
+
writeFileSync5(idPath, remoteIdentity, "utf-8");
|
|
3613
4441
|
identitiesUpdated++;
|
|
3614
4442
|
}
|
|
3615
4443
|
}
|
|
@@ -3635,16 +4463,16 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
3635
4463
|
}
|
|
3636
4464
|
if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
|
|
3637
4465
|
try {
|
|
3638
|
-
const agentConfigPath =
|
|
4466
|
+
const agentConfigPath = path10.join(EXE_AI_DIR, "agent-config.json");
|
|
3639
4467
|
let local = {};
|
|
3640
|
-
if (
|
|
4468
|
+
if (existsSync9(agentConfigPath)) {
|
|
3641
4469
|
try {
|
|
3642
|
-
local = JSON.parse(
|
|
4470
|
+
local = JSON.parse(readFileSync7(agentConfigPath, "utf-8"));
|
|
3643
4471
|
} catch {
|
|
3644
4472
|
}
|
|
3645
4473
|
}
|
|
3646
4474
|
const merged = { ...remote.agentConfig, ...local };
|
|
3647
|
-
|
|
4475
|
+
writeFileSync5(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
3648
4476
|
} catch {
|
|
3649
4477
|
}
|
|
3650
4478
|
}
|
|
@@ -4082,9 +4910,9 @@ var init_cloud_sync = __esm({
|
|
|
4082
4910
|
LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
|
|
4083
4911
|
FETCH_TIMEOUT_MS = 3e4;
|
|
4084
4912
|
PUSH_BATCH_SIZE = 5e3;
|
|
4085
|
-
ROSTER_LOCK_PATH =
|
|
4913
|
+
ROSTER_LOCK_PATH = path10.join(EXE_AI_DIR, "roster-merge.lock");
|
|
4086
4914
|
LOCK_STALE_MS = 3e4;
|
|
4087
|
-
ROSTER_DELETIONS_PATH =
|
|
4915
|
+
ROSTER_DELETIONS_PATH = path10.join(EXE_AI_DIR, "roster-deletions.json");
|
|
4088
4916
|
}
|
|
4089
4917
|
});
|
|
4090
4918
|
|
|
@@ -4094,33 +4922,33 @@ __export(preferences_exports, {
|
|
|
4094
4922
|
loadPreferences: () => loadPreferences,
|
|
4095
4923
|
savePreferences: () => savePreferences
|
|
4096
4924
|
});
|
|
4097
|
-
import { existsSync as
|
|
4098
|
-
import
|
|
4099
|
-
import
|
|
4100
|
-
function loadPreferences(homeDir =
|
|
4101
|
-
const configPath =
|
|
4102
|
-
if (!
|
|
4925
|
+
import { existsSync as existsSync10, readFileSync as readFileSync8, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5 } from "fs";
|
|
4926
|
+
import path11 from "path";
|
|
4927
|
+
import os6 from "os";
|
|
4928
|
+
function loadPreferences(homeDir = os6.homedir()) {
|
|
4929
|
+
const configPath = path11.join(homeDir, ".exe-os", "config.json");
|
|
4930
|
+
if (!existsSync10(configPath)) return {};
|
|
4103
4931
|
try {
|
|
4104
|
-
const config = JSON.parse(
|
|
4932
|
+
const config = JSON.parse(readFileSync8(configPath, "utf-8"));
|
|
4105
4933
|
return config.preferences ?? {};
|
|
4106
4934
|
} catch {
|
|
4107
4935
|
return {};
|
|
4108
4936
|
}
|
|
4109
4937
|
}
|
|
4110
|
-
function savePreferences(prefs, homeDir =
|
|
4111
|
-
const configDir =
|
|
4112
|
-
const configPath =
|
|
4113
|
-
|
|
4938
|
+
function savePreferences(prefs, homeDir = os6.homedir()) {
|
|
4939
|
+
const configDir = path11.join(homeDir, ".exe-os");
|
|
4940
|
+
const configPath = path11.join(configDir, "config.json");
|
|
4941
|
+
mkdirSync5(configDir, { recursive: true });
|
|
4114
4942
|
let config = {};
|
|
4115
|
-
if (
|
|
4943
|
+
if (existsSync10(configPath)) {
|
|
4116
4944
|
try {
|
|
4117
|
-
config = JSON.parse(
|
|
4945
|
+
config = JSON.parse(readFileSync8(configPath, "utf-8"));
|
|
4118
4946
|
} catch {
|
|
4119
4947
|
config = {};
|
|
4120
4948
|
}
|
|
4121
4949
|
}
|
|
4122
4950
|
config.preferences = prefs;
|
|
4123
|
-
|
|
4951
|
+
writeFileSync6(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
4124
4952
|
}
|
|
4125
4953
|
var init_preferences = __esm({
|
|
4126
4954
|
"src/lib/preferences.ts"() {
|
|
@@ -4895,17 +5723,17 @@ __export(identity_exports, {
|
|
|
4895
5723
|
listIdentities: () => listIdentities,
|
|
4896
5724
|
updateIdentity: () => updateIdentity
|
|
4897
5725
|
});
|
|
4898
|
-
import { existsSync as
|
|
5726
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
|
|
4899
5727
|
import { readdirSync as readdirSync2 } from "fs";
|
|
4900
|
-
import
|
|
5728
|
+
import path12 from "path";
|
|
4901
5729
|
import { createHash as createHash2 } from "crypto";
|
|
4902
5730
|
function ensureDir() {
|
|
4903
|
-
if (!
|
|
4904
|
-
|
|
5731
|
+
if (!existsSync11(IDENTITY_DIR2)) {
|
|
5732
|
+
mkdirSync6(IDENTITY_DIR2, { recursive: true });
|
|
4905
5733
|
}
|
|
4906
5734
|
}
|
|
4907
5735
|
function identityPath(agentId) {
|
|
4908
|
-
return
|
|
5736
|
+
return path12.join(IDENTITY_DIR2, `${agentId}.md`);
|
|
4909
5737
|
}
|
|
4910
5738
|
function parseFrontmatter(raw) {
|
|
4911
5739
|
const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
@@ -4946,8 +5774,8 @@ function contentHash(content) {
|
|
|
4946
5774
|
}
|
|
4947
5775
|
function getIdentity(agentId) {
|
|
4948
5776
|
const filePath = identityPath(agentId);
|
|
4949
|
-
if (!
|
|
4950
|
-
const raw =
|
|
5777
|
+
if (!existsSync11(filePath)) return null;
|
|
5778
|
+
const raw = readFileSync9(filePath, "utf-8");
|
|
4951
5779
|
const { frontmatter, body } = parseFrontmatter(raw);
|
|
4952
5780
|
return {
|
|
4953
5781
|
agentId,
|
|
@@ -4961,7 +5789,7 @@ async function updateIdentity(agentId, content, updatedBy) {
|
|
|
4961
5789
|
ensureDir();
|
|
4962
5790
|
const filePath = identityPath(agentId);
|
|
4963
5791
|
const hash = contentHash(content);
|
|
4964
|
-
|
|
5792
|
+
writeFileSync7(filePath, content, "utf-8");
|
|
4965
5793
|
try {
|
|
4966
5794
|
const client = getClient();
|
|
4967
5795
|
await client.execute({
|
|
@@ -4978,7 +5806,7 @@ async function updateIdentity(agentId, content, updatedBy) {
|
|
|
4978
5806
|
}
|
|
4979
5807
|
function listIdentities() {
|
|
4980
5808
|
ensureDir();
|
|
4981
|
-
const files = readdirSync2(
|
|
5809
|
+
const files = readdirSync2(IDENTITY_DIR2).filter((f) => f.endsWith(".md"));
|
|
4982
5810
|
const results = [];
|
|
4983
5811
|
for (const file of files) {
|
|
4984
5812
|
const agentId = file.replace(".md", "");
|
|
@@ -5011,13 +5839,13 @@ ${teamLines.join("\n")}`);
|
|
|
5011
5839
|
}
|
|
5012
5840
|
return parts.join("\n\n");
|
|
5013
5841
|
}
|
|
5014
|
-
var
|
|
5842
|
+
var IDENTITY_DIR2;
|
|
5015
5843
|
var init_identity = __esm({
|
|
5016
5844
|
"src/lib/identity.ts"() {
|
|
5017
5845
|
"use strict";
|
|
5018
5846
|
init_config();
|
|
5019
5847
|
init_database();
|
|
5020
|
-
|
|
5848
|
+
IDENTITY_DIR2 = path12.join(EXE_AI_DIR, "identity");
|
|
5021
5849
|
}
|
|
5022
5850
|
});
|
|
5023
5851
|
|
|
@@ -5568,36 +6396,36 @@ __export(session_wrappers_exports, {
|
|
|
5568
6396
|
generateSessionWrappers: () => generateSessionWrappers
|
|
5569
6397
|
});
|
|
5570
6398
|
import {
|
|
5571
|
-
existsSync as
|
|
5572
|
-
readFileSync as
|
|
5573
|
-
writeFileSync as
|
|
5574
|
-
mkdirSync as
|
|
6399
|
+
existsSync as existsSync12,
|
|
6400
|
+
readFileSync as readFileSync10,
|
|
6401
|
+
writeFileSync as writeFileSync8,
|
|
6402
|
+
mkdirSync as mkdirSync7,
|
|
5575
6403
|
chmodSync,
|
|
5576
6404
|
readdirSync as readdirSync3,
|
|
5577
6405
|
unlinkSync as unlinkSync6
|
|
5578
6406
|
} from "fs";
|
|
5579
|
-
import
|
|
6407
|
+
import path13 from "path";
|
|
5580
6408
|
import { homedir as homedir3 } from "os";
|
|
5581
6409
|
function generateSessionWrappers(packageRoot, homeDir) {
|
|
5582
6410
|
const home = homeDir ?? homedir3();
|
|
5583
|
-
const binDir =
|
|
5584
|
-
const rosterPath =
|
|
5585
|
-
|
|
5586
|
-
const exeStartDst =
|
|
6411
|
+
const binDir = path13.join(home, ".exe-os", "bin");
|
|
6412
|
+
const rosterPath = path13.join(home, ".exe-os", "exe-employees.json");
|
|
6413
|
+
mkdirSync7(binDir, { recursive: true });
|
|
6414
|
+
const exeStartDst = path13.join(binDir, "exe-start");
|
|
5587
6415
|
const candidates = [
|
|
5588
|
-
|
|
5589
|
-
|
|
6416
|
+
path13.join(packageRoot, "dist", "bin", "exe-start.sh"),
|
|
6417
|
+
path13.join(packageRoot, "src", "bin", "exe-start.sh")
|
|
5590
6418
|
];
|
|
5591
6419
|
for (const src of candidates) {
|
|
5592
|
-
if (
|
|
5593
|
-
|
|
6420
|
+
if (existsSync12(src)) {
|
|
6421
|
+
writeFileSync8(exeStartDst, readFileSync10(src));
|
|
5594
6422
|
chmodSync(exeStartDst, 493);
|
|
5595
6423
|
break;
|
|
5596
6424
|
}
|
|
5597
6425
|
}
|
|
5598
6426
|
let employees = [];
|
|
5599
6427
|
try {
|
|
5600
|
-
employees = JSON.parse(
|
|
6428
|
+
employees = JSON.parse(readFileSync10(rosterPath, "utf8"));
|
|
5601
6429
|
} catch {
|
|
5602
6430
|
return { created: 0, pathConfigured: false };
|
|
5603
6431
|
}
|
|
@@ -5607,9 +6435,9 @@ function generateSessionWrappers(packageRoot, homeDir) {
|
|
|
5607
6435
|
try {
|
|
5608
6436
|
for (const f of readdirSync3(binDir)) {
|
|
5609
6437
|
if (f === "exe-start") continue;
|
|
5610
|
-
const fPath =
|
|
6438
|
+
const fPath = path13.join(binDir, f);
|
|
5611
6439
|
try {
|
|
5612
|
-
const content =
|
|
6440
|
+
const content = readFileSync10(fPath, "utf8");
|
|
5613
6441
|
if (content.includes("exe-start")) {
|
|
5614
6442
|
unlinkSync6(fPath);
|
|
5615
6443
|
}
|
|
@@ -5624,30 +6452,30 @@ exec "${exeStartDst}" "$0" "$@"
|
|
|
5624
6452
|
`;
|
|
5625
6453
|
for (const emp of employees) {
|
|
5626
6454
|
for (let n = 1; n <= MAX_N; n++) {
|
|
5627
|
-
const wrapperPath =
|
|
5628
|
-
|
|
6455
|
+
const wrapperPath = path13.join(binDir, `${emp.name}${n}`);
|
|
6456
|
+
writeFileSync8(wrapperPath, wrapperContent);
|
|
5629
6457
|
chmodSync(wrapperPath, 493);
|
|
5630
6458
|
created++;
|
|
5631
6459
|
}
|
|
5632
6460
|
}
|
|
5633
6461
|
const codexLauncherCandidates = [
|
|
5634
|
-
|
|
5635
|
-
|
|
6462
|
+
path13.join(packageRoot, "dist", "bin", "exe-start-codex.js"),
|
|
6463
|
+
path13.join(packageRoot, "src", "bin", "exe-start-codex.ts")
|
|
5636
6464
|
];
|
|
5637
6465
|
let codexLauncher = null;
|
|
5638
6466
|
for (const c of codexLauncherCandidates) {
|
|
5639
|
-
if (
|
|
6467
|
+
if (existsSync12(c)) {
|
|
5640
6468
|
codexLauncher = c;
|
|
5641
6469
|
break;
|
|
5642
6470
|
}
|
|
5643
6471
|
}
|
|
5644
6472
|
if (codexLauncher) {
|
|
5645
6473
|
for (const emp of employees) {
|
|
5646
|
-
const wrapperPath =
|
|
6474
|
+
const wrapperPath = path13.join(binDir, `${emp.name}-codex`);
|
|
5647
6475
|
const content = `#!/bin/bash
|
|
5648
6476
|
exec node "${codexLauncher}" --agent ${emp.name} "$@"
|
|
5649
6477
|
`;
|
|
5650
|
-
|
|
6478
|
+
writeFileSync8(wrapperPath, content);
|
|
5651
6479
|
chmodSync(wrapperPath, 493);
|
|
5652
6480
|
created++;
|
|
5653
6481
|
}
|
|
@@ -5666,24 +6494,24 @@ export PATH="${binDir}:$PATH"
|
|
|
5666
6494
|
const shell = process.env.SHELL ?? "/bin/bash";
|
|
5667
6495
|
const profilePaths = [];
|
|
5668
6496
|
if (shell.includes("zsh")) {
|
|
5669
|
-
profilePaths.push(
|
|
6497
|
+
profilePaths.push(path13.join(home, ".zshrc"));
|
|
5670
6498
|
} else if (shell.includes("bash")) {
|
|
5671
|
-
profilePaths.push(
|
|
5672
|
-
profilePaths.push(
|
|
6499
|
+
profilePaths.push(path13.join(home, ".bashrc"));
|
|
6500
|
+
profilePaths.push(path13.join(home, ".bash_profile"));
|
|
5673
6501
|
} else {
|
|
5674
|
-
profilePaths.push(
|
|
6502
|
+
profilePaths.push(path13.join(home, ".profile"));
|
|
5675
6503
|
}
|
|
5676
6504
|
for (const profilePath of profilePaths) {
|
|
5677
6505
|
try {
|
|
5678
6506
|
let content = "";
|
|
5679
6507
|
try {
|
|
5680
|
-
content =
|
|
6508
|
+
content = readFileSync10(profilePath, "utf8");
|
|
5681
6509
|
} catch {
|
|
5682
6510
|
}
|
|
5683
6511
|
if (content.includes(".exe-os/bin")) {
|
|
5684
6512
|
return false;
|
|
5685
6513
|
}
|
|
5686
|
-
|
|
6514
|
+
writeFileSync8(profilePath, content + exportLine);
|
|
5687
6515
|
return true;
|
|
5688
6516
|
} catch {
|
|
5689
6517
|
continue;
|
|
@@ -5703,9 +6531,9 @@ var init_session_wrappers = __esm({
|
|
|
5703
6531
|
init_config();
|
|
5704
6532
|
init_keychain();
|
|
5705
6533
|
import crypto3 from "crypto";
|
|
5706
|
-
import { existsSync as
|
|
5707
|
-
import
|
|
5708
|
-
import
|
|
6534
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as readFileSync11, writeFileSync as writeFileSync9, unlinkSync as unlinkSync7 } from "fs";
|
|
6535
|
+
import os7 from "os";
|
|
6536
|
+
import path14 from "path";
|
|
5709
6537
|
import { createInterface } from "readline";
|
|
5710
6538
|
|
|
5711
6539
|
// src/lib/model-downloader.ts
|
|
@@ -5797,32 +6625,32 @@ async function fileHash(filePath) {
|
|
|
5797
6625
|
|
|
5798
6626
|
// src/lib/setup-wizard.ts
|
|
5799
6627
|
function findPackageRoot2() {
|
|
5800
|
-
let dir =
|
|
5801
|
-
const root =
|
|
6628
|
+
let dir = path14.dirname(new URL(import.meta.url).pathname);
|
|
6629
|
+
const root = path14.parse(dir).root;
|
|
5802
6630
|
while (dir !== root) {
|
|
5803
|
-
const pkgPath =
|
|
5804
|
-
if (
|
|
6631
|
+
const pkgPath = path14.join(dir, "package.json");
|
|
6632
|
+
if (existsSync13(pkgPath)) {
|
|
5805
6633
|
try {
|
|
5806
|
-
const pkg = JSON.parse(
|
|
6634
|
+
const pkg = JSON.parse(readFileSync11(pkgPath, "utf-8"));
|
|
5807
6635
|
if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
|
|
5808
6636
|
} catch {
|
|
5809
6637
|
}
|
|
5810
6638
|
}
|
|
5811
|
-
dir =
|
|
6639
|
+
dir = path14.dirname(dir);
|
|
5812
6640
|
}
|
|
5813
6641
|
return null;
|
|
5814
6642
|
}
|
|
5815
|
-
var SETUP_STATE_PATH =
|
|
6643
|
+
var SETUP_STATE_PATH = path14.join(os7.homedir(), ".exe-os", "setup-state.json");
|
|
5816
6644
|
function loadSetupState() {
|
|
5817
6645
|
try {
|
|
5818
|
-
return JSON.parse(
|
|
6646
|
+
return JSON.parse(readFileSync11(SETUP_STATE_PATH, "utf8"));
|
|
5819
6647
|
} catch {
|
|
5820
6648
|
return { completedSteps: [], startedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
5821
6649
|
}
|
|
5822
6650
|
}
|
|
5823
6651
|
function saveSetupState(state) {
|
|
5824
|
-
|
|
5825
|
-
|
|
6652
|
+
mkdirSync8(path14.dirname(SETUP_STATE_PATH), { recursive: true });
|
|
6653
|
+
writeFileSync9(SETUP_STATE_PATH, JSON.stringify(state, null, 2));
|
|
5826
6654
|
}
|
|
5827
6655
|
function clearSetupState() {
|
|
5828
6656
|
try {
|
|
@@ -5841,10 +6669,10 @@ function ask(rl, prompt) {
|
|
|
5841
6669
|
});
|
|
5842
6670
|
}
|
|
5843
6671
|
function getAvailableMemoryGB() {
|
|
5844
|
-
return
|
|
6672
|
+
return os7.freemem() / (1024 * 1024 * 1024);
|
|
5845
6673
|
}
|
|
5846
6674
|
function getTotalMemoryGB() {
|
|
5847
|
-
return
|
|
6675
|
+
return os7.totalmem() / (1024 * 1024 * 1024);
|
|
5848
6676
|
}
|
|
5849
6677
|
function isLowMemory() {
|
|
5850
6678
|
return getAvailableMemoryGB() < 2;
|
|
@@ -5855,8 +6683,8 @@ async function validateModel(log) {
|
|
|
5855
6683
|
if (totalGB <= 8 || isLowMemory()) {
|
|
5856
6684
|
log(`System memory: ${totalGB.toFixed(0)}GB total, ${freeGB.toFixed(1)}GB free`);
|
|
5857
6685
|
log("Skipping in-memory model validation (low memory \u2014 will validate on first use).");
|
|
5858
|
-
const modelPath =
|
|
5859
|
-
if (
|
|
6686
|
+
const modelPath = path14.join(MODELS_DIR, LOCAL_FILENAME);
|
|
6687
|
+
if (existsSync13(modelPath)) {
|
|
5860
6688
|
const { statSync: statSync2 } = await import("fs");
|
|
5861
6689
|
const size = statSync2(modelPath).size;
|
|
5862
6690
|
if (size > 300 * 1e6) {
|
|
@@ -5947,7 +6775,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
5947
6775
|
if (state.completedSteps.length > 0) {
|
|
5948
6776
|
log(`Resuming setup from step ${Math.max(...state.completedSteps) + 1}...`);
|
|
5949
6777
|
}
|
|
5950
|
-
if (
|
|
6778
|
+
if (existsSync13(LEGACY_LANCE_PATH)) {
|
|
5951
6779
|
log("\u26A0 Found v1.0 LanceDB at ~/.exe-os/local.lance");
|
|
5952
6780
|
log(" v1.1 uses libSQL (SQLite). Your existing memories are not automatically migrated.");
|
|
5953
6781
|
log(" The old directory will not be modified or deleted.");
|
|
@@ -6111,19 +6939,19 @@ async function runSetupWizard(opts = {}) {
|
|
|
6111
6939
|
await saveConfig(config);
|
|
6112
6940
|
log("");
|
|
6113
6941
|
try {
|
|
6114
|
-
const claudeJsonPath =
|
|
6942
|
+
const claudeJsonPath = path14.join(os7.homedir(), ".claude.json");
|
|
6115
6943
|
let claudeJson = {};
|
|
6116
6944
|
try {
|
|
6117
|
-
claudeJson = JSON.parse(
|
|
6945
|
+
claudeJson = JSON.parse(readFileSync11(claudeJsonPath, "utf8"));
|
|
6118
6946
|
} catch {
|
|
6119
6947
|
}
|
|
6120
6948
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
6121
6949
|
const projects = claudeJson.projects;
|
|
6122
|
-
for (const dir of [process.cwd(),
|
|
6950
|
+
for (const dir of [process.cwd(), os7.homedir()]) {
|
|
6123
6951
|
if (!projects[dir]) projects[dir] = {};
|
|
6124
6952
|
projects[dir].hasTrustDialogAccepted = true;
|
|
6125
6953
|
}
|
|
6126
|
-
|
|
6954
|
+
writeFileSync9(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
6127
6955
|
} catch {
|
|
6128
6956
|
}
|
|
6129
6957
|
state.completedSteps.push(5);
|
|
@@ -6137,7 +6965,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
6137
6965
|
const prefs = { ...existingPrefs };
|
|
6138
6966
|
log("=== Config Defaults ===");
|
|
6139
6967
|
log("");
|
|
6140
|
-
const ghosttyDetected =
|
|
6968
|
+
const ghosttyDetected = existsSync13(path14.join(os7.homedir(), ".config", "ghostty")) || existsSync13(path14.join(os7.homedir(), "Library", "Application Support", "com.mitchellh.ghostty"));
|
|
6141
6969
|
if (ghosttyDetected) {
|
|
6142
6970
|
const ghosttyAnswer = await ask(rl, "Detected Ghostty terminal. Use exe-os Ghostty defaults? (Y/n) ");
|
|
6143
6971
|
prefs.ghostty = ghosttyAnswer.toLowerCase() !== "n";
|
|
@@ -6184,7 +7012,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
6184
7012
|
let missingIdentities = [];
|
|
6185
7013
|
for (const emp of roster) {
|
|
6186
7014
|
const idPath = identityPath2(emp.name);
|
|
6187
|
-
if (!
|
|
7015
|
+
if (!existsSync13(idPath)) {
|
|
6188
7016
|
missingIdentities.push(emp.name);
|
|
6189
7017
|
}
|
|
6190
7018
|
}
|
|
@@ -6216,7 +7044,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
6216
7044
|
}
|
|
6217
7045
|
missingIdentities = [];
|
|
6218
7046
|
for (const emp of roster) {
|
|
6219
|
-
if (!
|
|
7047
|
+
if (!existsSync13(identityPath2(emp.name))) {
|
|
6220
7048
|
missingIdentities.push(emp.name);
|
|
6221
7049
|
}
|
|
6222
7050
|
}
|
|
@@ -6281,9 +7109,9 @@ async function runSetupWizard(opts = {}) {
|
|
|
6281
7109
|
const cooIdentityContent = getIdentityTemplate("coo");
|
|
6282
7110
|
if (cooIdentityContent) {
|
|
6283
7111
|
const cooIdPath = identityPath2(cooName);
|
|
6284
|
-
|
|
7112
|
+
mkdirSync8(path14.dirname(cooIdPath), { recursive: true });
|
|
6285
7113
|
const replaced = cooIdentityContent.replace(/agent_id:\s*exe/g, `agent_id: ${cooName}`).replace(/\$\{agent_id\}/g, cooName);
|
|
6286
|
-
|
|
7114
|
+
writeFileSync9(cooIdPath, replaced, "utf-8");
|
|
6287
7115
|
}
|
|
6288
7116
|
registerBinSymlinks2(cooName);
|
|
6289
7117
|
createdEmployees.push({ name: cooName, role: "COO" });
|
|
@@ -6377,9 +7205,9 @@ async function runSetupWizard(opts = {}) {
|
|
|
6377
7205
|
const ctoIdentityContent = getIdentityTemplate("cto");
|
|
6378
7206
|
if (ctoIdentityContent) {
|
|
6379
7207
|
const ctoIdPath = identityPath2(ctoName);
|
|
6380
|
-
|
|
7208
|
+
mkdirSync8(path14.dirname(ctoIdPath), { recursive: true });
|
|
6381
7209
|
const replaced = ctoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${ctoName}`).replace(/\$\{agent_id\}/g, ctoName);
|
|
6382
|
-
|
|
7210
|
+
writeFileSync9(ctoIdPath, replaced, "utf-8");
|
|
6383
7211
|
}
|
|
6384
7212
|
registerBinSymlinks2(ctoName);
|
|
6385
7213
|
createdEmployees.push({ name: ctoName, role: "CTO" });
|
|
@@ -6400,9 +7228,9 @@ async function runSetupWizard(opts = {}) {
|
|
|
6400
7228
|
const cmoIdentityContent = getIdentityTemplate("cmo");
|
|
6401
7229
|
if (cmoIdentityContent) {
|
|
6402
7230
|
const cmoIdPath = identityPath2(cmoName);
|
|
6403
|
-
|
|
7231
|
+
mkdirSync8(path14.dirname(cmoIdPath), { recursive: true });
|
|
6404
7232
|
const replaced = cmoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${cmoName}`).replace(/\$\{agent_id\}/g, cmoName);
|
|
6405
|
-
|
|
7233
|
+
writeFileSync9(cmoIdPath, replaced, "utf-8");
|
|
6406
7234
|
}
|
|
6407
7235
|
registerBinSymlinks2(cmoName);
|
|
6408
7236
|
createdEmployees.push({ name: cmoName, role: "CMO" });
|
|
@@ -6424,7 +7252,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
6424
7252
|
log(`Session shortcuts generated (${cooName}1, ${cooName}2, ...)`);
|
|
6425
7253
|
}
|
|
6426
7254
|
if (wrapResult.pathConfigured) {
|
|
6427
|
-
const binDir =
|
|
7255
|
+
const binDir = path14.join(os7.homedir(), ".exe-os", "bin");
|
|
6428
7256
|
process.env.PATH = `${binDir}:${process.env.PATH ?? ""}`;
|
|
6429
7257
|
pathJustConfigured = true;
|
|
6430
7258
|
}
|
|
@@ -6467,7 +7295,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
6467
7295
|
const pkgRoot2 = findPackageRoot2();
|
|
6468
7296
|
if (pkgRoot2) {
|
|
6469
7297
|
try {
|
|
6470
|
-
version = JSON.parse(
|
|
7298
|
+
version = JSON.parse(readFileSync11(path14.join(pkgRoot2, "package.json"), "utf-8")).version;
|
|
6471
7299
|
} catch {
|
|
6472
7300
|
}
|
|
6473
7301
|
}
|