@askexenow/exe-os 0.8.83 → 0.8.86
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 +746 -595
- package/dist/bin/backfill-responses.js +745 -594
- package/dist/bin/backfill-vectors.js +312 -226
- package/dist/bin/cleanup-stale-review-tasks.js +154 -21
- package/dist/bin/cli.js +14678 -12676
- package/dist/bin/exe-agent-config.js +242 -0
- package/dist/bin/exe-agent.js +100 -91
- package/dist/bin/exe-assign.js +1003 -854
- package/dist/bin/exe-boot.js +1420 -485
- package/dist/bin/exe-call.js +10 -0
- package/dist/bin/exe-cloud.js +29 -6
- package/dist/bin/exe-dispatch.js +572 -271
- package/dist/bin/exe-doctor.js +403 -6
- package/dist/bin/exe-export-behaviors.js +175 -72
- package/dist/bin/exe-forget.js +102 -3
- package/dist/bin/exe-gateway.js +796 -292
- package/dist/bin/exe-healthcheck.js +134 -1
- package/dist/bin/exe-heartbeat.js +172 -36
- package/dist/bin/exe-kill.js +175 -72
- package/dist/bin/exe-launch-agent.js +189 -76
- package/dist/bin/exe-link.js +927 -82
- package/dist/bin/exe-new-employee.js +60 -8
- package/dist/bin/exe-pending-messages.js +151 -19
- package/dist/bin/exe-pending-notifications.js +97 -2
- package/dist/bin/exe-pending-reviews.js +155 -22
- package/dist/bin/exe-rename.js +564 -23
- package/dist/bin/exe-review.js +231 -73
- package/dist/bin/exe-search.js +995 -228
- package/dist/bin/exe-session-cleanup.js +4930 -1664
- package/dist/bin/exe-settings.js +20 -5
- package/dist/bin/exe-start-codex.js +2598 -0
- package/dist/bin/exe-start.sh +15 -3
- package/dist/bin/exe-status.js +154 -21
- package/dist/bin/exe-team.js +97 -2
- package/dist/bin/git-sweep.js +1180 -363
- package/dist/bin/graph-backfill.js +175 -72
- package/dist/bin/graph-export.js +175 -72
- package/dist/bin/install.js +60 -7
- package/dist/bin/list-providers.js +1 -0
- package/dist/bin/scan-tasks.js +1185 -367
- package/dist/bin/setup.js +914 -270
- package/dist/bin/shard-migrate.js +175 -72
- package/dist/bin/update.js +1 -0
- package/dist/bin/wiki-sync.js +175 -72
- package/dist/gateway/index.js +792 -285
- package/dist/hooks/bug-report-worker.js +445 -135
- package/dist/hooks/commit-complete.js +1178 -361
- package/dist/hooks/error-recall.js +994 -228
- package/dist/hooks/ingest-worker.js +1799 -1234
- package/dist/hooks/ingest.js +3 -0
- package/dist/hooks/instructions-loaded.js +707 -97
- package/dist/hooks/notification.js +699 -89
- package/dist/hooks/post-compact.js +757 -109
- package/dist/hooks/pre-compact.js +1061 -244
- package/dist/hooks/pre-tool-use.js +787 -130
- package/dist/hooks/prompt-ingest-worker.js +242 -101
- package/dist/hooks/prompt-submit.js +1121 -299
- package/dist/hooks/response-ingest-worker.js +242 -101
- package/dist/hooks/session-end.js +4063 -397
- package/dist/hooks/session-start.js +1071 -254
- package/dist/hooks/stop.js +768 -120
- package/dist/hooks/subagent-stop.js +757 -109
- package/dist/hooks/summary-worker.js +1706 -1011
- package/dist/index.js +1821 -1098
- package/dist/lib/agent-config.js +167 -0
- package/dist/lib/cloud-sync.js +932 -88
- package/dist/lib/consolidation.js +2 -1
- package/dist/lib/database.js +642 -87
- package/dist/lib/db-daemon-client.js +503 -0
- package/dist/lib/device-registry.js +547 -7
- package/dist/lib/embedder.js +14 -28
- package/dist/lib/employee-templates.js +84 -74
- package/dist/lib/employees.js +9 -0
- package/dist/lib/exe-daemon-client.js +16 -29
- package/dist/lib/exe-daemon.js +2733 -1575
- package/dist/lib/hybrid-search.js +995 -228
- package/dist/lib/identity.js +87 -67
- package/dist/lib/keychain.js +9 -1
- package/dist/lib/messaging.js +103 -40
- package/dist/lib/reminders.js +91 -74
- package/dist/lib/runtime-table.js +16 -0
- package/dist/lib/schedules.js +96 -2
- package/dist/lib/session-wrappers.js +22 -0
- package/dist/lib/skill-learning.js +103 -85
- package/dist/lib/store.js +234 -73
- package/dist/lib/tasks.js +348 -134
- package/dist/lib/tmux-routing.js +422 -208
- package/dist/lib/token-spend.js +273 -0
- package/dist/lib/ws-client.js +11 -0
- package/dist/mcp/server.js +5742 -696
- package/dist/mcp/tools/complete-reminder.js +94 -77
- package/dist/mcp/tools/create-reminder.js +94 -77
- package/dist/mcp/tools/create-task.js +375 -152
- package/dist/mcp/tools/deactivate-behavior.js +95 -77
- package/dist/mcp/tools/list-reminders.js +94 -77
- package/dist/mcp/tools/list-tasks.js +99 -31
- package/dist/mcp/tools/send-message.js +108 -45
- package/dist/mcp/tools/update-task.js +162 -77
- package/dist/runtime/index.js +1075 -258
- package/dist/tui/App.js +1333 -506
- package/package.json +6 -1
- package/src/commands/exe/agent-config.md +27 -0
- package/src/commands/exe/cc-doctor.md +10 -0
package/dist/bin/exe-link.js
CHANGED
|
@@ -48,12 +48,20 @@ async function getMasterKey() {
|
|
|
48
48
|
}
|
|
49
49
|
const keyPath = getKeyPath();
|
|
50
50
|
if (!existsSync(keyPath)) {
|
|
51
|
+
process.stderr.write(
|
|
52
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
53
|
+
`
|
|
54
|
+
);
|
|
51
55
|
return null;
|
|
52
56
|
}
|
|
53
57
|
try {
|
|
54
58
|
const content = await readFile(keyPath, "utf-8");
|
|
55
59
|
return Buffer.from(content.trim(), "base64");
|
|
56
|
-
} catch {
|
|
60
|
+
} catch (err) {
|
|
61
|
+
process.stderr.write(
|
|
62
|
+
`[keychain] Key read failed at ${keyPath}: ${err instanceof Error ? err.message : String(err)}
|
|
63
|
+
`
|
|
64
|
+
);
|
|
57
65
|
return null;
|
|
58
66
|
}
|
|
59
67
|
}
|
|
@@ -570,6 +578,443 @@ var init_employees = __esm({
|
|
|
570
578
|
}
|
|
571
579
|
});
|
|
572
580
|
|
|
581
|
+
// src/lib/exe-daemon-client.ts
|
|
582
|
+
import net from "net";
|
|
583
|
+
import { spawn } from "child_process";
|
|
584
|
+
import { randomUUID } from "crypto";
|
|
585
|
+
import { existsSync as existsSync4, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
|
|
586
|
+
import path4 from "path";
|
|
587
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
588
|
+
function handleData(chunk) {
|
|
589
|
+
_buffer += chunk.toString();
|
|
590
|
+
if (_buffer.length > MAX_BUFFER) {
|
|
591
|
+
_buffer = "";
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
let newlineIdx;
|
|
595
|
+
while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
|
|
596
|
+
const line = _buffer.slice(0, newlineIdx).trim();
|
|
597
|
+
_buffer = _buffer.slice(newlineIdx + 1);
|
|
598
|
+
if (!line) continue;
|
|
599
|
+
try {
|
|
600
|
+
const response = JSON.parse(line);
|
|
601
|
+
const id = response.id;
|
|
602
|
+
if (!id) continue;
|
|
603
|
+
const entry = _pending.get(id);
|
|
604
|
+
if (entry) {
|
|
605
|
+
clearTimeout(entry.timer);
|
|
606
|
+
_pending.delete(id);
|
|
607
|
+
entry.resolve(response);
|
|
608
|
+
}
|
|
609
|
+
} catch {
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
function cleanupStaleFiles() {
|
|
614
|
+
if (existsSync4(PID_PATH)) {
|
|
615
|
+
try {
|
|
616
|
+
const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
|
|
617
|
+
if (pid > 0) {
|
|
618
|
+
try {
|
|
619
|
+
process.kill(pid, 0);
|
|
620
|
+
return;
|
|
621
|
+
} catch {
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
} catch {
|
|
625
|
+
}
|
|
626
|
+
try {
|
|
627
|
+
unlinkSync2(PID_PATH);
|
|
628
|
+
} catch {
|
|
629
|
+
}
|
|
630
|
+
try {
|
|
631
|
+
unlinkSync2(SOCKET_PATH);
|
|
632
|
+
} catch {
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
function findPackageRoot() {
|
|
637
|
+
let dir = path4.dirname(fileURLToPath2(import.meta.url));
|
|
638
|
+
const { root } = path4.parse(dir);
|
|
639
|
+
while (dir !== root) {
|
|
640
|
+
if (existsSync4(path4.join(dir, "package.json"))) return dir;
|
|
641
|
+
dir = path4.dirname(dir);
|
|
642
|
+
}
|
|
643
|
+
return null;
|
|
644
|
+
}
|
|
645
|
+
function spawnDaemon() {
|
|
646
|
+
const pkgRoot = findPackageRoot();
|
|
647
|
+
if (!pkgRoot) {
|
|
648
|
+
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
const daemonPath = path4.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
652
|
+
if (!existsSync4(daemonPath)) {
|
|
653
|
+
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
654
|
+
`);
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
const resolvedPath = daemonPath;
|
|
658
|
+
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
659
|
+
`);
|
|
660
|
+
const logPath = path4.join(path4.dirname(SOCKET_PATH), "exed.log");
|
|
661
|
+
let stderrFd = "ignore";
|
|
662
|
+
try {
|
|
663
|
+
stderrFd = openSync(logPath, "a");
|
|
664
|
+
} catch {
|
|
665
|
+
}
|
|
666
|
+
const child = spawn(process.execPath, [resolvedPath], {
|
|
667
|
+
detached: true,
|
|
668
|
+
stdio: ["ignore", "ignore", stderrFd],
|
|
669
|
+
env: {
|
|
670
|
+
...process.env,
|
|
671
|
+
TMUX: void 0,
|
|
672
|
+
// Daemon is global — must not inherit session scope
|
|
673
|
+
TMUX_PANE: void 0,
|
|
674
|
+
// Prevents resolveExeSession() from scoping to one session
|
|
675
|
+
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
676
|
+
EXE_DAEMON_PID: PID_PATH
|
|
677
|
+
}
|
|
678
|
+
});
|
|
679
|
+
child.unref();
|
|
680
|
+
if (typeof stderrFd === "number") {
|
|
681
|
+
try {
|
|
682
|
+
closeSync(stderrFd);
|
|
683
|
+
} catch {
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
function acquireSpawnLock() {
|
|
688
|
+
try {
|
|
689
|
+
const fd = openSync(SPAWN_LOCK_PATH, "wx");
|
|
690
|
+
closeSync(fd);
|
|
691
|
+
return true;
|
|
692
|
+
} catch {
|
|
693
|
+
try {
|
|
694
|
+
const stat = statSync(SPAWN_LOCK_PATH);
|
|
695
|
+
if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
|
|
696
|
+
try {
|
|
697
|
+
unlinkSync2(SPAWN_LOCK_PATH);
|
|
698
|
+
} catch {
|
|
699
|
+
}
|
|
700
|
+
try {
|
|
701
|
+
const fd = openSync(SPAWN_LOCK_PATH, "wx");
|
|
702
|
+
closeSync(fd);
|
|
703
|
+
return true;
|
|
704
|
+
} catch {
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
} catch {
|
|
708
|
+
}
|
|
709
|
+
return false;
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
function releaseSpawnLock() {
|
|
713
|
+
try {
|
|
714
|
+
unlinkSync2(SPAWN_LOCK_PATH);
|
|
715
|
+
} catch {
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
function connectToSocket() {
|
|
719
|
+
return new Promise((resolve) => {
|
|
720
|
+
if (_socket && _connected) {
|
|
721
|
+
resolve(true);
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
const socket = net.createConnection({ path: SOCKET_PATH });
|
|
725
|
+
const connectTimeout = setTimeout(() => {
|
|
726
|
+
socket.destroy();
|
|
727
|
+
resolve(false);
|
|
728
|
+
}, 2e3);
|
|
729
|
+
socket.on("connect", () => {
|
|
730
|
+
clearTimeout(connectTimeout);
|
|
731
|
+
_socket = socket;
|
|
732
|
+
_connected = true;
|
|
733
|
+
_buffer = "";
|
|
734
|
+
socket.on("data", handleData);
|
|
735
|
+
socket.on("close", () => {
|
|
736
|
+
_connected = false;
|
|
737
|
+
_socket = null;
|
|
738
|
+
for (const [id, entry] of _pending) {
|
|
739
|
+
clearTimeout(entry.timer);
|
|
740
|
+
_pending.delete(id);
|
|
741
|
+
entry.resolve({ error: "Connection closed" });
|
|
742
|
+
}
|
|
743
|
+
});
|
|
744
|
+
socket.on("error", () => {
|
|
745
|
+
_connected = false;
|
|
746
|
+
_socket = null;
|
|
747
|
+
});
|
|
748
|
+
resolve(true);
|
|
749
|
+
});
|
|
750
|
+
socket.on("error", () => {
|
|
751
|
+
clearTimeout(connectTimeout);
|
|
752
|
+
resolve(false);
|
|
753
|
+
});
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
async function connectEmbedDaemon() {
|
|
757
|
+
if (_socket && _connected) return true;
|
|
758
|
+
if (await connectToSocket()) return true;
|
|
759
|
+
if (acquireSpawnLock()) {
|
|
760
|
+
try {
|
|
761
|
+
cleanupStaleFiles();
|
|
762
|
+
spawnDaemon();
|
|
763
|
+
} finally {
|
|
764
|
+
releaseSpawnLock();
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
const start = Date.now();
|
|
768
|
+
let delay2 = 100;
|
|
769
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
770
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
771
|
+
if (await connectToSocket()) return true;
|
|
772
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
773
|
+
}
|
|
774
|
+
return false;
|
|
775
|
+
}
|
|
776
|
+
function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
777
|
+
return new Promise((resolve) => {
|
|
778
|
+
if (!_socket || !_connected) {
|
|
779
|
+
resolve({ error: "Not connected" });
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
const id = randomUUID();
|
|
783
|
+
const timer = setTimeout(() => {
|
|
784
|
+
_pending.delete(id);
|
|
785
|
+
resolve({ error: "Request timeout" });
|
|
786
|
+
}, timeoutMs);
|
|
787
|
+
_pending.set(id, { resolve, timer });
|
|
788
|
+
try {
|
|
789
|
+
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
790
|
+
} catch {
|
|
791
|
+
clearTimeout(timer);
|
|
792
|
+
_pending.delete(id);
|
|
793
|
+
resolve({ error: "Write failed" });
|
|
794
|
+
}
|
|
795
|
+
});
|
|
796
|
+
}
|
|
797
|
+
function isClientConnected() {
|
|
798
|
+
return _connected;
|
|
799
|
+
}
|
|
800
|
+
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _pending, MAX_BUFFER;
|
|
801
|
+
var init_exe_daemon_client = __esm({
|
|
802
|
+
"src/lib/exe-daemon-client.ts"() {
|
|
803
|
+
"use strict";
|
|
804
|
+
init_config();
|
|
805
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path4.join(EXE_AI_DIR, "exed.sock");
|
|
806
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path4.join(EXE_AI_DIR, "exed.pid");
|
|
807
|
+
SPAWN_LOCK_PATH = path4.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
808
|
+
SPAWN_LOCK_STALE_MS = 3e4;
|
|
809
|
+
CONNECT_TIMEOUT_MS = 15e3;
|
|
810
|
+
REQUEST_TIMEOUT_MS = 3e4;
|
|
811
|
+
_socket = null;
|
|
812
|
+
_connected = false;
|
|
813
|
+
_buffer = "";
|
|
814
|
+
_pending = /* @__PURE__ */ new Map();
|
|
815
|
+
MAX_BUFFER = 1e7;
|
|
816
|
+
}
|
|
817
|
+
});
|
|
818
|
+
|
|
819
|
+
// src/lib/daemon-protocol.ts
|
|
820
|
+
function serializeValue(v) {
|
|
821
|
+
if (v === null || v === void 0) return null;
|
|
822
|
+
if (typeof v === "bigint") return Number(v);
|
|
823
|
+
if (typeof v === "boolean") return v ? 1 : 0;
|
|
824
|
+
if (v instanceof Uint8Array) {
|
|
825
|
+
return { __blob: Buffer.from(v).toString("base64") };
|
|
826
|
+
}
|
|
827
|
+
if (ArrayBuffer.isView(v)) {
|
|
828
|
+
return { __blob: Buffer.from(v.buffer, v.byteOffset, v.byteLength).toString("base64") };
|
|
829
|
+
}
|
|
830
|
+
if (v instanceof ArrayBuffer) {
|
|
831
|
+
return { __blob: Buffer.from(v).toString("base64") };
|
|
832
|
+
}
|
|
833
|
+
if (typeof v === "string" || typeof v === "number") return v;
|
|
834
|
+
return String(v);
|
|
835
|
+
}
|
|
836
|
+
function deserializeValue(v) {
|
|
837
|
+
if (v === null) return null;
|
|
838
|
+
if (typeof v === "object" && v !== null && "__blob" in v) {
|
|
839
|
+
const buf = Buffer.from(v.__blob, "base64");
|
|
840
|
+
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
|
841
|
+
}
|
|
842
|
+
return v;
|
|
843
|
+
}
|
|
844
|
+
function deserializeResultSet(srs) {
|
|
845
|
+
const rows = srs.rows.map((obj) => {
|
|
846
|
+
const values = srs.columns.map(
|
|
847
|
+
(col) => deserializeValue(obj[col] ?? null)
|
|
848
|
+
);
|
|
849
|
+
const row = values;
|
|
850
|
+
for (let i = 0; i < srs.columns.length; i++) {
|
|
851
|
+
const col = srs.columns[i];
|
|
852
|
+
if (col !== void 0) {
|
|
853
|
+
row[col] = values[i] ?? null;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
Object.defineProperty(row, "length", {
|
|
857
|
+
value: values.length,
|
|
858
|
+
enumerable: false
|
|
859
|
+
});
|
|
860
|
+
return row;
|
|
861
|
+
});
|
|
862
|
+
return {
|
|
863
|
+
columns: srs.columns,
|
|
864
|
+
columnTypes: srs.columnTypes ?? [],
|
|
865
|
+
rows,
|
|
866
|
+
rowsAffected: srs.rowsAffected,
|
|
867
|
+
lastInsertRowid: srs.lastInsertRowid != null ? BigInt(srs.lastInsertRowid) : void 0,
|
|
868
|
+
toJSON: () => ({
|
|
869
|
+
columns: srs.columns,
|
|
870
|
+
columnTypes: srs.columnTypes ?? [],
|
|
871
|
+
rows: srs.rows,
|
|
872
|
+
rowsAffected: srs.rowsAffected,
|
|
873
|
+
lastInsertRowid: srs.lastInsertRowid
|
|
874
|
+
})
|
|
875
|
+
};
|
|
876
|
+
}
|
|
877
|
+
var init_daemon_protocol = __esm({
|
|
878
|
+
"src/lib/daemon-protocol.ts"() {
|
|
879
|
+
"use strict";
|
|
880
|
+
}
|
|
881
|
+
});
|
|
882
|
+
|
|
883
|
+
// src/lib/db-daemon-client.ts
|
|
884
|
+
var db_daemon_client_exports = {};
|
|
885
|
+
__export(db_daemon_client_exports, {
|
|
886
|
+
createDaemonDbClient: () => createDaemonDbClient,
|
|
887
|
+
initDaemonDbClient: () => initDaemonDbClient
|
|
888
|
+
});
|
|
889
|
+
function normalizeStatement(stmt) {
|
|
890
|
+
if (typeof stmt === "string") {
|
|
891
|
+
return { sql: stmt, args: [] };
|
|
892
|
+
}
|
|
893
|
+
const sql = stmt.sql;
|
|
894
|
+
let args = [];
|
|
895
|
+
if (Array.isArray(stmt.args)) {
|
|
896
|
+
args = stmt.args.map((v) => serializeValue(v));
|
|
897
|
+
} else if (stmt.args && typeof stmt.args === "object") {
|
|
898
|
+
const named = {};
|
|
899
|
+
for (const [key, val] of Object.entries(stmt.args)) {
|
|
900
|
+
named[key] = serializeValue(val);
|
|
901
|
+
}
|
|
902
|
+
return { sql, args: named };
|
|
903
|
+
}
|
|
904
|
+
return { sql, args };
|
|
905
|
+
}
|
|
906
|
+
function createDaemonDbClient(fallbackClient) {
|
|
907
|
+
let _useDaemon = false;
|
|
908
|
+
const client = {
|
|
909
|
+
async execute(stmt) {
|
|
910
|
+
if (!_useDaemon || !isClientConnected()) {
|
|
911
|
+
return fallbackClient.execute(stmt);
|
|
912
|
+
}
|
|
913
|
+
const { sql, args } = normalizeStatement(stmt);
|
|
914
|
+
const response = await sendDaemonRequest({
|
|
915
|
+
type: "db-execute",
|
|
916
|
+
sql,
|
|
917
|
+
args
|
|
918
|
+
});
|
|
919
|
+
if (response.error) {
|
|
920
|
+
const errMsg = String(response.error);
|
|
921
|
+
if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
|
|
922
|
+
process.stderr.write(`[db-daemon] Transport error (${errMsg}), falling back to direct
|
|
923
|
+
`);
|
|
924
|
+
return fallbackClient.execute(stmt);
|
|
925
|
+
}
|
|
926
|
+
throw new Error(errMsg);
|
|
927
|
+
}
|
|
928
|
+
if (response.db) {
|
|
929
|
+
return deserializeResultSet(response.db);
|
|
930
|
+
}
|
|
931
|
+
process.stderr.write("[db-daemon] Unexpected response shape, falling back to direct\n");
|
|
932
|
+
return fallbackClient.execute(stmt);
|
|
933
|
+
},
|
|
934
|
+
async batch(stmts, mode) {
|
|
935
|
+
if (!_useDaemon || !isClientConnected()) {
|
|
936
|
+
return fallbackClient.batch(stmts, mode);
|
|
937
|
+
}
|
|
938
|
+
const statements = stmts.map(normalizeStatement);
|
|
939
|
+
const response = await sendDaemonRequest({
|
|
940
|
+
type: "db-batch",
|
|
941
|
+
statements,
|
|
942
|
+
mode: mode ?? "deferred"
|
|
943
|
+
});
|
|
944
|
+
if (response.error) {
|
|
945
|
+
const errMsg = String(response.error);
|
|
946
|
+
if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
|
|
947
|
+
process.stderr.write(`[db-daemon] Batch transport error (${errMsg}), falling back to direct
|
|
948
|
+
`);
|
|
949
|
+
return fallbackClient.batch(stmts, mode);
|
|
950
|
+
}
|
|
951
|
+
throw new Error(errMsg);
|
|
952
|
+
}
|
|
953
|
+
const batchResults = response["db-batch"];
|
|
954
|
+
if (batchResults) {
|
|
955
|
+
return batchResults.map(deserializeResultSet);
|
|
956
|
+
}
|
|
957
|
+
process.stderr.write("[db-daemon] Unexpected batch response shape, falling back to direct\n");
|
|
958
|
+
return fallbackClient.batch(stmts, mode);
|
|
959
|
+
},
|
|
960
|
+
// Transaction support — delegate to fallback (transactions need direct connection)
|
|
961
|
+
async transaction(mode) {
|
|
962
|
+
return fallbackClient.transaction(mode);
|
|
963
|
+
},
|
|
964
|
+
// executeMultiple — delegate to fallback (used only for schema migrations)
|
|
965
|
+
async executeMultiple(sql) {
|
|
966
|
+
return fallbackClient.executeMultiple(sql);
|
|
967
|
+
},
|
|
968
|
+
// migrate — delegate to fallback
|
|
969
|
+
async migrate(stmts) {
|
|
970
|
+
return fallbackClient.migrate(stmts);
|
|
971
|
+
},
|
|
972
|
+
// Sync mode — delegate to fallback
|
|
973
|
+
sync() {
|
|
974
|
+
return fallbackClient.sync();
|
|
975
|
+
},
|
|
976
|
+
close() {
|
|
977
|
+
_useDaemon = false;
|
|
978
|
+
},
|
|
979
|
+
get closed() {
|
|
980
|
+
return fallbackClient.closed;
|
|
981
|
+
},
|
|
982
|
+
get protocol() {
|
|
983
|
+
return fallbackClient.protocol;
|
|
984
|
+
}
|
|
985
|
+
};
|
|
986
|
+
return {
|
|
987
|
+
...client,
|
|
988
|
+
/** Enable daemon routing (call after confirming daemon is connected) */
|
|
989
|
+
_enableDaemon() {
|
|
990
|
+
_useDaemon = true;
|
|
991
|
+
},
|
|
992
|
+
/** Check if daemon routing is active */
|
|
993
|
+
_isDaemonActive() {
|
|
994
|
+
return _useDaemon && isClientConnected();
|
|
995
|
+
}
|
|
996
|
+
};
|
|
997
|
+
}
|
|
998
|
+
async function initDaemonDbClient(fallbackClient) {
|
|
999
|
+
if (process.env.EXE_IS_DAEMON === "1") return null;
|
|
1000
|
+
const connected = await connectEmbedDaemon();
|
|
1001
|
+
if (!connected) {
|
|
1002
|
+
process.stderr.write("[db-daemon] Daemon unavailable \u2014 using direct SQLite\n");
|
|
1003
|
+
return null;
|
|
1004
|
+
}
|
|
1005
|
+
const client = createDaemonDbClient(fallbackClient);
|
|
1006
|
+
client._enableDaemon();
|
|
1007
|
+
process.stderr.write("[db-daemon] DB routing through daemon (single-writer)\n");
|
|
1008
|
+
return client;
|
|
1009
|
+
}
|
|
1010
|
+
var init_db_daemon_client = __esm({
|
|
1011
|
+
"src/lib/db-daemon-client.ts"() {
|
|
1012
|
+
"use strict";
|
|
1013
|
+
init_exe_daemon_client();
|
|
1014
|
+
init_daemon_protocol();
|
|
1015
|
+
}
|
|
1016
|
+
});
|
|
1017
|
+
|
|
573
1018
|
// src/lib/database.ts
|
|
574
1019
|
var database_exports = {};
|
|
575
1020
|
__export(database_exports, {
|
|
@@ -578,6 +1023,7 @@ __export(database_exports, {
|
|
|
578
1023
|
ensureSchema: () => ensureSchema,
|
|
579
1024
|
getClient: () => getClient,
|
|
580
1025
|
getRawClient: () => getRawClient,
|
|
1026
|
+
initDaemonClient: () => initDaemonClient,
|
|
581
1027
|
initDatabase: () => initDatabase,
|
|
582
1028
|
initTurso: () => initTurso,
|
|
583
1029
|
isInitialized: () => isInitialized
|
|
@@ -605,8 +1051,27 @@ function getClient() {
|
|
|
605
1051
|
if (!_resilientClient) {
|
|
606
1052
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
607
1053
|
}
|
|
1054
|
+
if (process.env.EXE_IS_DAEMON === "1") {
|
|
1055
|
+
return _resilientClient;
|
|
1056
|
+
}
|
|
1057
|
+
if (_daemonClient && _daemonClient._isDaemonActive()) {
|
|
1058
|
+
return _daemonClient;
|
|
1059
|
+
}
|
|
608
1060
|
return _resilientClient;
|
|
609
1061
|
}
|
|
1062
|
+
async function initDaemonClient() {
|
|
1063
|
+
if (process.env.EXE_IS_DAEMON === "1") return;
|
|
1064
|
+
if (!_resilientClient) return;
|
|
1065
|
+
try {
|
|
1066
|
+
const { initDaemonDbClient: initDaemonDbClient2 } = await Promise.resolve().then(() => (init_db_daemon_client(), db_daemon_client_exports));
|
|
1067
|
+
_daemonClient = await initDaemonDbClient2(_resilientClient);
|
|
1068
|
+
} catch (err) {
|
|
1069
|
+
process.stderr.write(
|
|
1070
|
+
`[database] Daemon client init failed (non-fatal): ${err instanceof Error ? err.message : String(err)}
|
|
1071
|
+
`
|
|
1072
|
+
);
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
610
1075
|
function getRawClient() {
|
|
611
1076
|
if (!_client) {
|
|
612
1077
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
@@ -1093,6 +1558,12 @@ async function ensureSchema() {
|
|
|
1093
1558
|
} catch {
|
|
1094
1559
|
}
|
|
1095
1560
|
}
|
|
1561
|
+
try {
|
|
1562
|
+
await client.execute(
|
|
1563
|
+
`CREATE INDEX IF NOT EXISTS idx_memories_content_hash ON memories(content_hash, agent_id)`
|
|
1564
|
+
);
|
|
1565
|
+
} catch {
|
|
1566
|
+
}
|
|
1096
1567
|
await client.executeMultiple(`
|
|
1097
1568
|
CREATE TABLE IF NOT EXISTS entities (
|
|
1098
1569
|
id TEXT PRIMARY KEY,
|
|
@@ -1145,7 +1616,30 @@ async function ensureSchema() {
|
|
|
1145
1616
|
entity_id TEXT NOT NULL,
|
|
1146
1617
|
PRIMARY KEY (hyperedge_id, entity_id)
|
|
1147
1618
|
);
|
|
1619
|
+
|
|
1620
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS entities_fts USING fts5(
|
|
1621
|
+
name,
|
|
1622
|
+
content=entities,
|
|
1623
|
+
content_rowid=rowid
|
|
1624
|
+
);
|
|
1625
|
+
|
|
1626
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_ai AFTER INSERT ON entities BEGIN
|
|
1627
|
+
INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
|
|
1628
|
+
END;
|
|
1629
|
+
|
|
1630
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_ad AFTER DELETE ON entities BEGIN
|
|
1631
|
+
INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
|
|
1632
|
+
END;
|
|
1633
|
+
|
|
1634
|
+
CREATE TRIGGER IF NOT EXISTS entities_fts_au AFTER UPDATE ON entities BEGIN
|
|
1635
|
+
INSERT INTO entities_fts(entities_fts, rowid, name) VALUES('delete', old.rowid, old.name);
|
|
1636
|
+
INSERT INTO entities_fts(rowid, name) VALUES (new.rowid, new.name);
|
|
1637
|
+
END;
|
|
1148
1638
|
`);
|
|
1639
|
+
try {
|
|
1640
|
+
await client.execute("INSERT INTO entities_fts(entities_fts) VALUES('rebuild')");
|
|
1641
|
+
} catch {
|
|
1642
|
+
}
|
|
1149
1643
|
await client.executeMultiple(`
|
|
1150
1644
|
CREATE TABLE IF NOT EXISTS entity_aliases (
|
|
1151
1645
|
alias TEXT NOT NULL PRIMARY KEY,
|
|
@@ -1326,6 +1820,33 @@ async function ensureSchema() {
|
|
|
1326
1820
|
CREATE INDEX IF NOT EXISTS idx_conversations_channel
|
|
1327
1821
|
ON conversations(channel_id);
|
|
1328
1822
|
`);
|
|
1823
|
+
await client.executeMultiple(`
|
|
1824
|
+
CREATE TABLE IF NOT EXISTS session_agent_map (
|
|
1825
|
+
session_uuid TEXT PRIMARY KEY,
|
|
1826
|
+
agent_id TEXT NOT NULL,
|
|
1827
|
+
session_name TEXT,
|
|
1828
|
+
task_id TEXT,
|
|
1829
|
+
project_name TEXT,
|
|
1830
|
+
started_at TEXT NOT NULL
|
|
1831
|
+
);
|
|
1832
|
+
|
|
1833
|
+
CREATE INDEX IF NOT EXISTS idx_session_agent_map_agent
|
|
1834
|
+
ON session_agent_map(agent_id);
|
|
1835
|
+
`);
|
|
1836
|
+
try {
|
|
1837
|
+
const mapCount = await client.execute({ sql: `SELECT COUNT(*) as cnt FROM session_agent_map`, args: [] });
|
|
1838
|
+
if (Number(mapCount.rows[0]?.cnt ?? 0) === 0) {
|
|
1839
|
+
await client.execute({
|
|
1840
|
+
sql: `INSERT OR IGNORE INTO session_agent_map (session_uuid, agent_id, session_name, started_at)
|
|
1841
|
+
SELECT session_id, agent_id, '', MIN(timestamp)
|
|
1842
|
+
FROM memories
|
|
1843
|
+
WHERE session_id IS NOT NULL AND session_id != '' AND agent_id IS NOT NULL AND agent_id != ''
|
|
1844
|
+
GROUP BY session_id, agent_id`,
|
|
1845
|
+
args: []
|
|
1846
|
+
});
|
|
1847
|
+
}
|
|
1848
|
+
} catch {
|
|
1849
|
+
}
|
|
1329
1850
|
try {
|
|
1330
1851
|
await client.execute({
|
|
1331
1852
|
sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
|
|
@@ -1459,15 +1980,41 @@ async function ensureSchema() {
|
|
|
1459
1980
|
});
|
|
1460
1981
|
} catch {
|
|
1461
1982
|
}
|
|
1983
|
+
for (const col of [
|
|
1984
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
1985
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
1986
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
1987
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
1988
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
1989
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
1990
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
1991
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
1992
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
1993
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
1994
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
1995
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
1996
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
1997
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
1998
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
1999
|
+
]) {
|
|
2000
|
+
try {
|
|
2001
|
+
await client.execute(col);
|
|
2002
|
+
} catch {
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
1462
2005
|
}
|
|
1463
2006
|
async function disposeDatabase() {
|
|
2007
|
+
if (_daemonClient) {
|
|
2008
|
+
_daemonClient.close();
|
|
2009
|
+
_daemonClient = null;
|
|
2010
|
+
}
|
|
1464
2011
|
if (_client) {
|
|
1465
2012
|
_client.close();
|
|
1466
2013
|
_client = null;
|
|
1467
2014
|
_resilientClient = null;
|
|
1468
2015
|
}
|
|
1469
2016
|
}
|
|
1470
|
-
var _client, _resilientClient, initTurso, disposeTurso;
|
|
2017
|
+
var _client, _resilientClient, _daemonClient, initTurso, disposeTurso;
|
|
1471
2018
|
var init_database = __esm({
|
|
1472
2019
|
"src/lib/database.ts"() {
|
|
1473
2020
|
"use strict";
|
|
@@ -1475,6 +2022,7 @@ var init_database = __esm({
|
|
|
1475
2022
|
init_employees();
|
|
1476
2023
|
_client = null;
|
|
1477
2024
|
_resilientClient = null;
|
|
2025
|
+
_daemonClient = null;
|
|
1478
2026
|
initTurso = initDatabase;
|
|
1479
2027
|
disposeTurso = disposeDatabase;
|
|
1480
2028
|
}
|
|
@@ -1501,27 +2049,27 @@ var init_compress = __esm({
|
|
|
1501
2049
|
});
|
|
1502
2050
|
|
|
1503
2051
|
// src/lib/license.ts
|
|
1504
|
-
import { readFileSync as
|
|
1505
|
-
import { randomUUID } from "crypto";
|
|
1506
|
-
import
|
|
2052
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync5, mkdirSync } from "fs";
|
|
2053
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
2054
|
+
import path5 from "path";
|
|
1507
2055
|
import { jwtVerify, importSPKI } from "jose";
|
|
1508
2056
|
function loadDeviceId() {
|
|
1509
|
-
const deviceJsonPath =
|
|
2057
|
+
const deviceJsonPath = path5.join(EXE_AI_DIR, "device.json");
|
|
1510
2058
|
try {
|
|
1511
|
-
if (
|
|
1512
|
-
const data = JSON.parse(
|
|
2059
|
+
if (existsSync5(deviceJsonPath)) {
|
|
2060
|
+
const data = JSON.parse(readFileSync4(deviceJsonPath, "utf8"));
|
|
1513
2061
|
if (data.deviceId) return data.deviceId;
|
|
1514
2062
|
}
|
|
1515
2063
|
} catch {
|
|
1516
2064
|
}
|
|
1517
2065
|
try {
|
|
1518
|
-
if (
|
|
1519
|
-
const id2 =
|
|
2066
|
+
if (existsSync5(DEVICE_ID_PATH)) {
|
|
2067
|
+
const id2 = readFileSync4(DEVICE_ID_PATH, "utf8").trim();
|
|
1520
2068
|
if (id2) return id2;
|
|
1521
2069
|
}
|
|
1522
2070
|
} catch {
|
|
1523
2071
|
}
|
|
1524
|
-
const id =
|
|
2072
|
+
const id = randomUUID2();
|
|
1525
2073
|
mkdirSync(EXE_AI_DIR, { recursive: true });
|
|
1526
2074
|
writeFileSync2(DEVICE_ID_PATH, id, "utf8");
|
|
1527
2075
|
return id;
|
|
@@ -1531,9 +2079,235 @@ var init_license = __esm({
|
|
|
1531
2079
|
"src/lib/license.ts"() {
|
|
1532
2080
|
"use strict";
|
|
1533
2081
|
init_config();
|
|
1534
|
-
LICENSE_PATH =
|
|
1535
|
-
CACHE_PATH =
|
|
1536
|
-
DEVICE_ID_PATH =
|
|
2082
|
+
LICENSE_PATH = path5.join(EXE_AI_DIR, "license.key");
|
|
2083
|
+
CACHE_PATH = path5.join(EXE_AI_DIR, "license-cache.json");
|
|
2084
|
+
DEVICE_ID_PATH = path5.join(EXE_AI_DIR, "device-id");
|
|
2085
|
+
}
|
|
2086
|
+
});
|
|
2087
|
+
|
|
2088
|
+
// src/lib/crdt-sync.ts
|
|
2089
|
+
var crdt_sync_exports = {};
|
|
2090
|
+
__export(crdt_sync_exports, {
|
|
2091
|
+
_setStatePath: () => _setStatePath,
|
|
2092
|
+
applyRemoteUpdate: () => applyRemoteUpdate,
|
|
2093
|
+
destroyCrdtDoc: () => destroyCrdtDoc,
|
|
2094
|
+
getDiffUpdate: () => getDiffUpdate,
|
|
2095
|
+
getFullState: () => getFullState,
|
|
2096
|
+
getStateVector: () => getStateVector,
|
|
2097
|
+
importExistingBehaviors: () => importExistingBehaviors,
|
|
2098
|
+
importExistingMemories: () => importExistingMemories,
|
|
2099
|
+
initCrdtDoc: () => initCrdtDoc,
|
|
2100
|
+
isCrdtSyncEnabled: () => isCrdtSyncEnabled,
|
|
2101
|
+
onUpdate: () => onUpdate,
|
|
2102
|
+
readAllBehaviors: () => readAllBehaviors,
|
|
2103
|
+
readAllMemories: () => readAllMemories,
|
|
2104
|
+
rebuildFromDb: () => rebuildFromDb
|
|
2105
|
+
});
|
|
2106
|
+
import * as Y from "yjs";
|
|
2107
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync6, mkdirSync as mkdirSync2, unlinkSync as unlinkSync3 } from "fs";
|
|
2108
|
+
import path6 from "path";
|
|
2109
|
+
import { homedir } from "os";
|
|
2110
|
+
function getStatePath() {
|
|
2111
|
+
return _statePathOverride ?? DEFAULT_STATE_PATH;
|
|
2112
|
+
}
|
|
2113
|
+
function _setStatePath(p) {
|
|
2114
|
+
_statePathOverride = p;
|
|
2115
|
+
}
|
|
2116
|
+
function initCrdtDoc() {
|
|
2117
|
+
if (doc) return doc;
|
|
2118
|
+
doc = new Y.Doc();
|
|
2119
|
+
const sp = getStatePath();
|
|
2120
|
+
if (existsSync6(sp)) {
|
|
2121
|
+
try {
|
|
2122
|
+
const state = readFileSync5(sp);
|
|
2123
|
+
Y.applyUpdate(doc, new Uint8Array(state));
|
|
2124
|
+
} catch {
|
|
2125
|
+
console.warn("[crdt-sync] WARN: corrupted state file, rebuilding from DB");
|
|
2126
|
+
try {
|
|
2127
|
+
unlinkSync3(sp);
|
|
2128
|
+
} catch {
|
|
2129
|
+
}
|
|
2130
|
+
rebuildFromDb().catch((err) => {
|
|
2131
|
+
console.warn("[crdt-sync] rebuild from DB failed:", err);
|
|
2132
|
+
});
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
doc.on("update", () => {
|
|
2136
|
+
persistState();
|
|
2137
|
+
});
|
|
2138
|
+
return doc;
|
|
2139
|
+
}
|
|
2140
|
+
function getMemoriesMap() {
|
|
2141
|
+
const d = initCrdtDoc();
|
|
2142
|
+
return d.getMap("memories");
|
|
2143
|
+
}
|
|
2144
|
+
function getBehaviorsMap() {
|
|
2145
|
+
const d = initCrdtDoc();
|
|
2146
|
+
return d.getMap("behaviors");
|
|
2147
|
+
}
|
|
2148
|
+
function applyRemoteUpdate(update) {
|
|
2149
|
+
const d = initCrdtDoc();
|
|
2150
|
+
Y.applyUpdate(d, update);
|
|
2151
|
+
}
|
|
2152
|
+
function getFullState() {
|
|
2153
|
+
const d = initCrdtDoc();
|
|
2154
|
+
return Y.encodeStateAsUpdate(d);
|
|
2155
|
+
}
|
|
2156
|
+
function getDiffUpdate(remoteStateVector) {
|
|
2157
|
+
const d = initCrdtDoc();
|
|
2158
|
+
return Y.encodeStateAsUpdate(d, remoteStateVector);
|
|
2159
|
+
}
|
|
2160
|
+
function getStateVector() {
|
|
2161
|
+
const d = initCrdtDoc();
|
|
2162
|
+
return Y.encodeStateVector(d);
|
|
2163
|
+
}
|
|
2164
|
+
function importExistingMemories(memories) {
|
|
2165
|
+
const map = getMemoriesMap();
|
|
2166
|
+
const d = initCrdtDoc();
|
|
2167
|
+
let imported = 0;
|
|
2168
|
+
d.transact(() => {
|
|
2169
|
+
for (const mem of memories) {
|
|
2170
|
+
if (!mem.id) continue;
|
|
2171
|
+
if (map.has(mem.id)) continue;
|
|
2172
|
+
const entry = new Y.Map();
|
|
2173
|
+
entry.set("id", mem.id);
|
|
2174
|
+
entry.set("agent_id", mem.agent_id ?? null);
|
|
2175
|
+
entry.set("agent_role", mem.agent_role ?? null);
|
|
2176
|
+
entry.set("session_id", mem.session_id ?? null);
|
|
2177
|
+
entry.set("timestamp", mem.timestamp ?? null);
|
|
2178
|
+
entry.set("tool_name", mem.tool_name ?? null);
|
|
2179
|
+
entry.set("project_name", mem.project_name ?? null);
|
|
2180
|
+
entry.set("has_error", mem.has_error ?? 0);
|
|
2181
|
+
entry.set("raw_text", mem.raw_text ?? "");
|
|
2182
|
+
entry.set("version", mem.version ?? 0);
|
|
2183
|
+
entry.set("author_device_id", mem.author_device_id ?? null);
|
|
2184
|
+
entry.set("scope", mem.scope ?? "business");
|
|
2185
|
+
map.set(mem.id, entry);
|
|
2186
|
+
imported++;
|
|
2187
|
+
}
|
|
2188
|
+
});
|
|
2189
|
+
return imported;
|
|
2190
|
+
}
|
|
2191
|
+
function importExistingBehaviors(behaviors) {
|
|
2192
|
+
const map = getBehaviorsMap();
|
|
2193
|
+
const d = initCrdtDoc();
|
|
2194
|
+
let imported = 0;
|
|
2195
|
+
d.transact(() => {
|
|
2196
|
+
for (const beh of behaviors) {
|
|
2197
|
+
if (!beh.id) continue;
|
|
2198
|
+
if (map.has(beh.id)) continue;
|
|
2199
|
+
const entry = new Y.Map();
|
|
2200
|
+
entry.set("id", beh.id);
|
|
2201
|
+
entry.set("agent_id", beh.agent_id ?? null);
|
|
2202
|
+
entry.set("project_name", beh.project_name ?? null);
|
|
2203
|
+
entry.set("domain", beh.domain ?? null);
|
|
2204
|
+
entry.set("content", beh.content ?? null);
|
|
2205
|
+
entry.set("active", beh.active ?? 1);
|
|
2206
|
+
entry.set("priority", beh.priority ?? "p1");
|
|
2207
|
+
entry.set("created_at", beh.created_at ?? null);
|
|
2208
|
+
entry.set("updated_at", beh.updated_at ?? null);
|
|
2209
|
+
map.set(beh.id, entry);
|
|
2210
|
+
imported++;
|
|
2211
|
+
}
|
|
2212
|
+
});
|
|
2213
|
+
return imported;
|
|
2214
|
+
}
|
|
2215
|
+
function readAllMemories() {
|
|
2216
|
+
const map = getMemoriesMap();
|
|
2217
|
+
const records = [];
|
|
2218
|
+
map.forEach((entry, id) => {
|
|
2219
|
+
records.push({
|
|
2220
|
+
id,
|
|
2221
|
+
agent_id: entry.get("agent_id"),
|
|
2222
|
+
agent_role: entry.get("agent_role"),
|
|
2223
|
+
session_id: entry.get("session_id"),
|
|
2224
|
+
timestamp: entry.get("timestamp"),
|
|
2225
|
+
tool_name: entry.get("tool_name"),
|
|
2226
|
+
project_name: entry.get("project_name"),
|
|
2227
|
+
has_error: entry.get("has_error"),
|
|
2228
|
+
raw_text: entry.get("raw_text"),
|
|
2229
|
+
version: entry.get("version"),
|
|
2230
|
+
author_device_id: entry.get("author_device_id"),
|
|
2231
|
+
scope: entry.get("scope")
|
|
2232
|
+
});
|
|
2233
|
+
});
|
|
2234
|
+
return records;
|
|
2235
|
+
}
|
|
2236
|
+
function readAllBehaviors() {
|
|
2237
|
+
const map = getBehaviorsMap();
|
|
2238
|
+
const records = [];
|
|
2239
|
+
map.forEach((entry, id) => {
|
|
2240
|
+
records.push({
|
|
2241
|
+
id,
|
|
2242
|
+
agent_id: entry.get("agent_id"),
|
|
2243
|
+
project_name: entry.get("project_name"),
|
|
2244
|
+
domain: entry.get("domain"),
|
|
2245
|
+
content: entry.get("content"),
|
|
2246
|
+
active: entry.get("active"),
|
|
2247
|
+
priority: entry.get("priority"),
|
|
2248
|
+
created_at: entry.get("created_at"),
|
|
2249
|
+
updated_at: entry.get("updated_at")
|
|
2250
|
+
});
|
|
2251
|
+
});
|
|
2252
|
+
return records;
|
|
2253
|
+
}
|
|
2254
|
+
function onUpdate(callback) {
|
|
2255
|
+
const d = initCrdtDoc();
|
|
2256
|
+
const handler = (update) => callback(update);
|
|
2257
|
+
d.on("update", handler);
|
|
2258
|
+
return () => d.off("update", handler);
|
|
2259
|
+
}
|
|
2260
|
+
function persistState() {
|
|
2261
|
+
if (!doc) return;
|
|
2262
|
+
try {
|
|
2263
|
+
const sp = getStatePath();
|
|
2264
|
+
const dir = path6.dirname(sp);
|
|
2265
|
+
if (!existsSync6(dir)) mkdirSync2(dir, { recursive: true });
|
|
2266
|
+
const state = Y.encodeStateAsUpdate(doc);
|
|
2267
|
+
writeFileSync3(sp, Buffer.from(state));
|
|
2268
|
+
} catch {
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2271
|
+
function isCrdtSyncEnabled() {
|
|
2272
|
+
return process.env.EXE_CRDT_SYNC !== "0";
|
|
2273
|
+
}
|
|
2274
|
+
async function rebuildFromDb() {
|
|
2275
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
2276
|
+
const client = getClient2();
|
|
2277
|
+
const result = await client.execute(
|
|
2278
|
+
"SELECT id, agent_id, agent_role, session_id, timestamp, tool_name, project_name, has_error, raw_text, version, author_device_id, scope FROM memories"
|
|
2279
|
+
);
|
|
2280
|
+
const memories = result.rows.map((row) => ({
|
|
2281
|
+
id: String(row.id),
|
|
2282
|
+
agent_id: row.agent_id,
|
|
2283
|
+
agent_role: row.agent_role,
|
|
2284
|
+
session_id: row.session_id,
|
|
2285
|
+
timestamp: row.timestamp,
|
|
2286
|
+
tool_name: row.tool_name,
|
|
2287
|
+
project_name: row.project_name,
|
|
2288
|
+
has_error: row.has_error,
|
|
2289
|
+
raw_text: row.raw_text,
|
|
2290
|
+
version: row.version,
|
|
2291
|
+
author_device_id: row.author_device_id,
|
|
2292
|
+
scope: row.scope
|
|
2293
|
+
}));
|
|
2294
|
+
const count = importExistingMemories(memories);
|
|
2295
|
+
persistState();
|
|
2296
|
+
return count;
|
|
2297
|
+
}
|
|
2298
|
+
function destroyCrdtDoc() {
|
|
2299
|
+
if (doc) {
|
|
2300
|
+
doc.destroy();
|
|
2301
|
+
doc = null;
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
var DEFAULT_STATE_PATH, _statePathOverride, doc;
|
|
2305
|
+
var init_crdt_sync = __esm({
|
|
2306
|
+
"src/lib/crdt-sync.ts"() {
|
|
2307
|
+
"use strict";
|
|
2308
|
+
DEFAULT_STATE_PATH = path6.join(homedir(), ".exe-os", "crdt-state.bin");
|
|
2309
|
+
_statePathOverride = null;
|
|
2310
|
+
doc = null;
|
|
1537
2311
|
}
|
|
1538
2312
|
});
|
|
1539
2313
|
|
|
@@ -1565,16 +2339,16 @@ __export(cloud_sync_exports, {
|
|
|
1565
2339
|
mergeRosterFromRemote: () => mergeRosterFromRemote,
|
|
1566
2340
|
recordRosterDeletion: () => recordRosterDeletion
|
|
1567
2341
|
});
|
|
1568
|
-
import { readFileSync as
|
|
2342
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync7, readdirSync, mkdirSync as mkdirSync3, appendFileSync, unlinkSync as unlinkSync4, openSync as openSync2, closeSync as closeSync2 } from "fs";
|
|
1569
2343
|
import crypto2 from "crypto";
|
|
1570
|
-
import
|
|
1571
|
-
import { homedir } from "os";
|
|
2344
|
+
import path7 from "path";
|
|
2345
|
+
import { homedir as homedir2 } from "os";
|
|
1572
2346
|
function sqlSafe(v) {
|
|
1573
2347
|
return v === void 0 ? null : v;
|
|
1574
2348
|
}
|
|
1575
2349
|
function logError(msg) {
|
|
1576
2350
|
try {
|
|
1577
|
-
const logPath =
|
|
2351
|
+
const logPath = path7.join(homedir2(), ".exe-os", "workers.log");
|
|
1578
2352
|
appendFileSync(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
|
|
1579
2353
|
`);
|
|
1580
2354
|
} catch {
|
|
@@ -1582,20 +2356,20 @@ function logError(msg) {
|
|
|
1582
2356
|
}
|
|
1583
2357
|
async function withRosterLock(fn) {
|
|
1584
2358
|
try {
|
|
1585
|
-
const fd =
|
|
1586
|
-
|
|
1587
|
-
|
|
2359
|
+
const fd = openSync2(ROSTER_LOCK_PATH, "wx");
|
|
2360
|
+
closeSync2(fd);
|
|
2361
|
+
writeFileSync4(ROSTER_LOCK_PATH, String(Date.now()));
|
|
1588
2362
|
} catch (err) {
|
|
1589
2363
|
if (err.code === "EEXIST") {
|
|
1590
2364
|
try {
|
|
1591
|
-
const ts = parseInt(
|
|
2365
|
+
const ts = parseInt(readFileSync6(ROSTER_LOCK_PATH, "utf-8"), 10);
|
|
1592
2366
|
if (Date.now() - ts < LOCK_STALE_MS) {
|
|
1593
2367
|
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
1594
2368
|
}
|
|
1595
|
-
|
|
1596
|
-
const fd =
|
|
1597
|
-
|
|
1598
|
-
|
|
2369
|
+
unlinkSync4(ROSTER_LOCK_PATH);
|
|
2370
|
+
const fd = openSync2(ROSTER_LOCK_PATH, "wx");
|
|
2371
|
+
closeSync2(fd);
|
|
2372
|
+
writeFileSync4(ROSTER_LOCK_PATH, String(Date.now()));
|
|
1599
2373
|
} catch (retryErr) {
|
|
1600
2374
|
if (retryErr instanceof Error && retryErr.message.includes("already in progress")) throw retryErr;
|
|
1601
2375
|
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
@@ -1608,7 +2382,7 @@ async function withRosterLock(fn) {
|
|
|
1608
2382
|
return await fn();
|
|
1609
2383
|
} finally {
|
|
1610
2384
|
try {
|
|
1611
|
-
|
|
2385
|
+
unlinkSync4(ROSTER_LOCK_PATH);
|
|
1612
2386
|
} catch {
|
|
1613
2387
|
}
|
|
1614
2388
|
}
|
|
@@ -1753,29 +2527,75 @@ async function cloudSync(config) {
|
|
|
1753
2527
|
const pullResult = await cloudPull(lastPullVersion, config);
|
|
1754
2528
|
let pulled = 0;
|
|
1755
2529
|
if (pullResult.records.length > 0) {
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
2530
|
+
if (isCrdtSyncEnabled()) {
|
|
2531
|
+
const { initCrdtDoc: initCrdtDoc2, importExistingMemories: importExistingMemories2, readAllMemories: readAllMemories2 } = await Promise.resolve().then(() => (init_crdt_sync(), crdt_sync_exports));
|
|
2532
|
+
initCrdtDoc2();
|
|
2533
|
+
importExistingMemories2(
|
|
2534
|
+
pullResult.records.map((rec) => ({
|
|
2535
|
+
id: String(rec.id ?? ""),
|
|
2536
|
+
agent_id: rec.agent_id,
|
|
2537
|
+
agent_role: rec.agent_role,
|
|
2538
|
+
session_id: rec.session_id,
|
|
2539
|
+
timestamp: rec.timestamp,
|
|
2540
|
+
tool_name: rec.tool_name,
|
|
2541
|
+
project_name: rec.project_name,
|
|
2542
|
+
has_error: rec.has_error ?? 0,
|
|
2543
|
+
raw_text: rec.raw_text ?? "",
|
|
2544
|
+
version: rec.version ?? 0,
|
|
2545
|
+
author_device_id: rec.author_device_id,
|
|
2546
|
+
scope: rec.scope ?? "business"
|
|
2547
|
+
}))
|
|
2548
|
+
);
|
|
2549
|
+
const pulledIds = new Set(pullResult.records.map((r) => String(r.id ?? "")));
|
|
2550
|
+
const merged = readAllMemories2().filter((rec) => pulledIds.has(rec.id));
|
|
2551
|
+
const stmts = merged.map((rec) => ({
|
|
2552
|
+
sql: `INSERT OR REPLACE INTO memories
|
|
2553
|
+
(id, agent_id, agent_role, session_id, timestamp,
|
|
2554
|
+
tool_name, project_name, has_error, raw_text, version,
|
|
2555
|
+
author_device_id, scope)
|
|
2556
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
2557
|
+
args: [
|
|
2558
|
+
sqlSafe(rec.id),
|
|
2559
|
+
sqlSafe(rec.agent_id),
|
|
2560
|
+
sqlSafe(rec.agent_role),
|
|
2561
|
+
sqlSafe(rec.session_id),
|
|
2562
|
+
sqlSafe(rec.timestamp),
|
|
2563
|
+
sqlSafe(rec.tool_name),
|
|
2564
|
+
sqlSafe(rec.project_name),
|
|
2565
|
+
sqlSafe(rec.has_error ?? 0),
|
|
2566
|
+
sqlSafe(rec.raw_text ?? ""),
|
|
2567
|
+
sqlSafe(rec.version ?? 0),
|
|
2568
|
+
sqlSafe(rec.author_device_id),
|
|
2569
|
+
sqlSafe(rec.scope ?? "business")
|
|
2570
|
+
]
|
|
2571
|
+
}));
|
|
2572
|
+
if (stmts.length > 0) await client.batch(stmts, "write");
|
|
2573
|
+
pulled = pullResult.records.length;
|
|
2574
|
+
} else {
|
|
2575
|
+
const stmts = pullResult.records.map((rec) => ({
|
|
2576
|
+
sql: `INSERT OR REPLACE INTO memories
|
|
2577
|
+
(id, agent_id, agent_role, session_id, timestamp,
|
|
2578
|
+
tool_name, project_name, has_error, raw_text, version,
|
|
2579
|
+
author_device_id, scope)
|
|
2580
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
2581
|
+
args: [
|
|
2582
|
+
sqlSafe(rec.id),
|
|
2583
|
+
sqlSafe(rec.agent_id),
|
|
2584
|
+
sqlSafe(rec.agent_role),
|
|
2585
|
+
sqlSafe(rec.session_id),
|
|
2586
|
+
sqlSafe(rec.timestamp),
|
|
2587
|
+
sqlSafe(rec.tool_name),
|
|
2588
|
+
sqlSafe(rec.project_name),
|
|
2589
|
+
sqlSafe(rec.has_error ?? 0),
|
|
2590
|
+
sqlSafe(rec.raw_text ?? ""),
|
|
2591
|
+
sqlSafe(rec.version ?? 0),
|
|
2592
|
+
sqlSafe(rec.author_device_id),
|
|
2593
|
+
sqlSafe(rec.scope ?? "business")
|
|
2594
|
+
]
|
|
2595
|
+
}));
|
|
2596
|
+
await client.batch(stmts, "write");
|
|
2597
|
+
pulled = pullResult.records.length;
|
|
2598
|
+
}
|
|
1779
2599
|
}
|
|
1780
2600
|
if (pullResult.maxVersion > lastPullVersion) {
|
|
1781
2601
|
await client.execute({
|
|
@@ -1923,8 +2743,8 @@ async function cloudSync(config) {
|
|
|
1923
2743
|
try {
|
|
1924
2744
|
const employees = await loadEmployees();
|
|
1925
2745
|
rosterResult.employees = employees.length;
|
|
1926
|
-
const idDir =
|
|
1927
|
-
if (
|
|
2746
|
+
const idDir = path7.join(EXE_AI_DIR, "identity");
|
|
2747
|
+
if (existsSync7(idDir)) {
|
|
1928
2748
|
rosterResult.identities = readdirSync(idDir).filter((f) => f.endsWith(".md")).length;
|
|
1929
2749
|
}
|
|
1930
2750
|
} catch {
|
|
@@ -1945,55 +2765,63 @@ async function cloudSync(config) {
|
|
|
1945
2765
|
function recordRosterDeletion(name) {
|
|
1946
2766
|
let deletions = [];
|
|
1947
2767
|
try {
|
|
1948
|
-
if (
|
|
1949
|
-
deletions = JSON.parse(
|
|
2768
|
+
if (existsSync7(ROSTER_DELETIONS_PATH)) {
|
|
2769
|
+
deletions = JSON.parse(readFileSync6(ROSTER_DELETIONS_PATH, "utf-8"));
|
|
1950
2770
|
}
|
|
1951
2771
|
} catch {
|
|
1952
2772
|
}
|
|
1953
2773
|
if (!deletions.includes(name)) deletions.push(name);
|
|
1954
|
-
|
|
2774
|
+
writeFileSync4(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
|
|
1955
2775
|
}
|
|
1956
2776
|
function consumeRosterDeletions() {
|
|
1957
2777
|
try {
|
|
1958
|
-
if (!
|
|
1959
|
-
const deletions = JSON.parse(
|
|
1960
|
-
|
|
2778
|
+
if (!existsSync7(ROSTER_DELETIONS_PATH)) return [];
|
|
2779
|
+
const deletions = JSON.parse(readFileSync6(ROSTER_DELETIONS_PATH, "utf-8"));
|
|
2780
|
+
writeFileSync4(ROSTER_DELETIONS_PATH, "[]");
|
|
1961
2781
|
return deletions;
|
|
1962
2782
|
} catch {
|
|
1963
2783
|
return [];
|
|
1964
2784
|
}
|
|
1965
2785
|
}
|
|
1966
2786
|
function buildRosterBlob(paths) {
|
|
1967
|
-
const rosterPath = paths?.rosterPath ??
|
|
1968
|
-
const identityDir = paths?.identityDir ??
|
|
1969
|
-
const configPath = paths?.configPath ??
|
|
2787
|
+
const rosterPath = paths?.rosterPath ?? path7.join(EXE_AI_DIR, "exe-employees.json");
|
|
2788
|
+
const identityDir = paths?.identityDir ?? path7.join(EXE_AI_DIR, "identity");
|
|
2789
|
+
const configPath = paths?.configPath ?? path7.join(EXE_AI_DIR, "config.json");
|
|
1970
2790
|
let roster = [];
|
|
1971
|
-
if (
|
|
2791
|
+
if (existsSync7(rosterPath)) {
|
|
1972
2792
|
try {
|
|
1973
|
-
roster = JSON.parse(
|
|
2793
|
+
roster = JSON.parse(readFileSync6(rosterPath, "utf-8"));
|
|
1974
2794
|
} catch {
|
|
1975
2795
|
}
|
|
1976
2796
|
}
|
|
1977
2797
|
const identities = {};
|
|
1978
|
-
if (
|
|
2798
|
+
if (existsSync7(identityDir)) {
|
|
1979
2799
|
for (const file of readdirSync(identityDir).filter((f) => f.endsWith(".md"))) {
|
|
1980
2800
|
try {
|
|
1981
|
-
identities[file] =
|
|
2801
|
+
identities[file] = readFileSync6(path7.join(identityDir, file), "utf-8");
|
|
1982
2802
|
} catch {
|
|
1983
2803
|
}
|
|
1984
2804
|
}
|
|
1985
2805
|
}
|
|
1986
2806
|
let config;
|
|
1987
|
-
if (
|
|
2807
|
+
if (existsSync7(configPath)) {
|
|
2808
|
+
try {
|
|
2809
|
+
config = JSON.parse(readFileSync6(configPath, "utf-8"));
|
|
2810
|
+
} catch {
|
|
2811
|
+
}
|
|
2812
|
+
}
|
|
2813
|
+
let agentConfig;
|
|
2814
|
+
const agentConfigPath = path7.join(EXE_AI_DIR, "agent-config.json");
|
|
2815
|
+
if (existsSync7(agentConfigPath)) {
|
|
1988
2816
|
try {
|
|
1989
|
-
|
|
2817
|
+
agentConfig = JSON.parse(readFileSync6(agentConfigPath, "utf-8"));
|
|
1990
2818
|
} catch {
|
|
1991
2819
|
}
|
|
1992
2820
|
}
|
|
1993
2821
|
const deletedNames = consumeRosterDeletions();
|
|
1994
|
-
const content = JSON.stringify({ roster, identities, config, deletedNames });
|
|
2822
|
+
const content = JSON.stringify({ roster, identities, config, agentConfig, deletedNames });
|
|
1995
2823
|
const hash = crypto2.createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
1996
|
-
return { roster, identities, config, deletedNames, version: hash };
|
|
2824
|
+
return { roster, identities, config, agentConfig, deletedNames, version: hash };
|
|
1997
2825
|
}
|
|
1998
2826
|
async function cloudPushRoster(config) {
|
|
1999
2827
|
assertSecureEndpoint(config.endpoint);
|
|
@@ -2062,23 +2890,23 @@ async function cloudPullRoster(config) {
|
|
|
2062
2890
|
}
|
|
2063
2891
|
}
|
|
2064
2892
|
function mergeConfig(remoteConfig, configPath) {
|
|
2065
|
-
const cfgPath = configPath ??
|
|
2893
|
+
const cfgPath = configPath ?? path7.join(EXE_AI_DIR, "config.json");
|
|
2066
2894
|
let local = {};
|
|
2067
|
-
if (
|
|
2895
|
+
if (existsSync7(cfgPath)) {
|
|
2068
2896
|
try {
|
|
2069
|
-
local = JSON.parse(
|
|
2897
|
+
local = JSON.parse(readFileSync6(cfgPath, "utf-8"));
|
|
2070
2898
|
} catch {
|
|
2071
2899
|
}
|
|
2072
2900
|
}
|
|
2073
2901
|
const merged = { ...remoteConfig, ...local };
|
|
2074
|
-
const dir =
|
|
2075
|
-
if (!
|
|
2076
|
-
|
|
2902
|
+
const dir = path7.dirname(cfgPath);
|
|
2903
|
+
if (!existsSync7(dir)) mkdirSync3(dir, { recursive: true });
|
|
2904
|
+
writeFileSync4(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
2077
2905
|
}
|
|
2078
2906
|
async function mergeRosterFromRemote(remote, paths) {
|
|
2079
2907
|
return withRosterLock(async () => {
|
|
2080
2908
|
const rosterPath = paths?.rosterPath ?? void 0;
|
|
2081
|
-
const identityDir = paths?.identityDir ??
|
|
2909
|
+
const identityDir = paths?.identityDir ?? path7.join(EXE_AI_DIR, "identity");
|
|
2082
2910
|
const localEmployees = await loadEmployees(rosterPath);
|
|
2083
2911
|
const localNames = new Set(localEmployees.map((e) => e.name));
|
|
2084
2912
|
let added = 0;
|
|
@@ -2099,15 +2927,15 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
2099
2927
|
) ?? lookupKey;
|
|
2100
2928
|
const remoteIdentity = remote.identities[matchedKey];
|
|
2101
2929
|
if (remoteIdentity) {
|
|
2102
|
-
if (!
|
|
2103
|
-
const idPath =
|
|
2930
|
+
if (!existsSync7(identityDir)) mkdirSync3(identityDir, { recursive: true });
|
|
2931
|
+
const idPath = path7.join(identityDir, `${remoteEmp.name}.md`);
|
|
2104
2932
|
let localIdentity = null;
|
|
2105
2933
|
try {
|
|
2106
|
-
localIdentity =
|
|
2934
|
+
localIdentity = existsSync7(idPath) ? readFileSync6(idPath, "utf-8") : null;
|
|
2107
2935
|
} catch {
|
|
2108
2936
|
}
|
|
2109
2937
|
if (localIdentity !== remoteIdentity) {
|
|
2110
|
-
|
|
2938
|
+
writeFileSync4(idPath, remoteIdentity, "utf-8");
|
|
2111
2939
|
identitiesUpdated++;
|
|
2112
2940
|
}
|
|
2113
2941
|
}
|
|
@@ -2131,6 +2959,21 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
2131
2959
|
} catch {
|
|
2132
2960
|
}
|
|
2133
2961
|
}
|
|
2962
|
+
if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
|
|
2963
|
+
try {
|
|
2964
|
+
const agentConfigPath = path7.join(EXE_AI_DIR, "agent-config.json");
|
|
2965
|
+
let local = {};
|
|
2966
|
+
if (existsSync7(agentConfigPath)) {
|
|
2967
|
+
try {
|
|
2968
|
+
local = JSON.parse(readFileSync6(agentConfigPath, "utf-8"));
|
|
2969
|
+
} catch {
|
|
2970
|
+
}
|
|
2971
|
+
}
|
|
2972
|
+
const merged = { ...remote.agentConfig, ...local };
|
|
2973
|
+
writeFileSync4(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
2974
|
+
} catch {
|
|
2975
|
+
}
|
|
2976
|
+
}
|
|
2134
2977
|
return { added, identitiesUpdated };
|
|
2135
2978
|
});
|
|
2136
2979
|
}
|
|
@@ -2560,13 +3403,14 @@ var init_cloud_sync = __esm({
|
|
|
2560
3403
|
init_compress();
|
|
2561
3404
|
init_license();
|
|
2562
3405
|
init_config();
|
|
3406
|
+
init_crdt_sync();
|
|
2563
3407
|
init_employees();
|
|
2564
3408
|
LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
|
|
2565
3409
|
FETCH_TIMEOUT_MS = 3e4;
|
|
2566
3410
|
PUSH_BATCH_SIZE = 5e3;
|
|
2567
|
-
ROSTER_LOCK_PATH =
|
|
3411
|
+
ROSTER_LOCK_PATH = path7.join(EXE_AI_DIR, "roster-merge.lock");
|
|
2568
3412
|
LOCK_STALE_MS = 3e4;
|
|
2569
|
-
ROSTER_DELETIONS_PATH =
|
|
3413
|
+
ROSTER_DELETIONS_PATH = path7.join(EXE_AI_DIR, "roster-deletions.json");
|
|
2570
3414
|
}
|
|
2571
3415
|
});
|
|
2572
3416
|
|
|
@@ -2579,6 +3423,7 @@ import { realpathSync } from "fs";
|
|
|
2579
3423
|
import { fileURLToPath } from "url";
|
|
2580
3424
|
function isMainModule(importMetaUrl) {
|
|
2581
3425
|
if (process.argv[1] == null) return false;
|
|
3426
|
+
if (process.argv[1].includes("mcp/server")) return false;
|
|
2582
3427
|
try {
|
|
2583
3428
|
const scriptPath = realpathSync(process.argv[1]);
|
|
2584
3429
|
const modulePath = realpathSync(fileURLToPath(importMetaUrl));
|