@askexenow/exe-os 0.8.32 → 0.8.36
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 +332 -348
- package/dist/bin/backfill-responses.js +72 -12
- package/dist/bin/backfill-vectors.js +72 -12
- package/dist/bin/cleanup-stale-review-tasks.js +63 -3
- package/dist/bin/cli.js +1518 -1122
- package/dist/bin/exe-agent.js +4 -4
- package/dist/bin/exe-assign.js +80 -18
- package/dist/bin/exe-boot.js +408 -89
- package/dist/bin/exe-call.js +83 -24
- package/dist/bin/exe-dispatch.js +18 -10
- package/dist/bin/exe-doctor.js +63 -3
- package/dist/bin/exe-export-behaviors.js +64 -3
- package/dist/bin/exe-forget.js +69 -4
- package/dist/bin/exe-gateway.js +121 -36
- package/dist/bin/exe-heartbeat.js +77 -13
- package/dist/bin/exe-kill.js +64 -3
- package/dist/bin/exe-launch-agent.js +162 -35
- package/dist/bin/exe-link.js +946 -0
- package/dist/bin/exe-new-employee.js +121 -36
- package/dist/bin/exe-pending-messages.js +72 -7
- package/dist/bin/exe-pending-notifications.js +63 -3
- package/dist/bin/exe-pending-reviews.js +75 -10
- package/dist/bin/exe-rename.js +1287 -0
- package/dist/bin/exe-review.js +64 -4
- package/dist/bin/exe-search.js +79 -13
- package/dist/bin/exe-session-cleanup.js +91 -26
- package/dist/bin/exe-status.js +64 -4
- package/dist/bin/exe-team.js +64 -4
- package/dist/bin/git-sweep.js +71 -4
- package/dist/bin/graph-backfill.js +64 -3
- package/dist/bin/graph-export.js +64 -3
- package/dist/bin/install.js +3 -3
- package/dist/bin/scan-tasks.js +71 -4
- package/dist/bin/setup.js +156 -38
- package/dist/bin/shard-migrate.js +64 -3
- package/dist/bin/wiki-sync.js +64 -3
- package/dist/gateway/index.js +122 -37
- package/dist/hooks/bug-report-worker.js +209 -23
- package/dist/hooks/commit-complete.js +71 -4
- package/dist/hooks/error-recall.js +79 -13
- package/dist/hooks/ingest-worker.js +129 -43
- package/dist/hooks/instructions-loaded.js +71 -4
- package/dist/hooks/notification.js +71 -4
- package/dist/hooks/post-compact.js +71 -4
- package/dist/hooks/pre-compact.js +71 -4
- package/dist/hooks/pre-tool-use.js +413 -194
- package/dist/hooks/prompt-ingest-worker.js +82 -22
- package/dist/hooks/prompt-submit.js +103 -37
- package/dist/hooks/response-ingest-worker.js +87 -22
- package/dist/hooks/session-end.js +71 -4
- package/dist/hooks/session-start.js +79 -13
- package/dist/hooks/stop.js +71 -4
- package/dist/hooks/subagent-stop.js +71 -4
- package/dist/hooks/summary-worker.js +303 -50
- package/dist/index.js +134 -46
- package/dist/lib/cloud-sync.js +209 -15
- package/dist/lib/consolidation.js +4 -4
- package/dist/lib/database.js +64 -2
- package/dist/lib/device-registry.js +70 -3
- package/dist/lib/employee-templates.js +48 -22
- package/dist/lib/employees.js +34 -1
- package/dist/lib/exe-daemon.js +136 -53
- package/dist/lib/hybrid-search.js +79 -13
- package/dist/lib/identity-templates.js +57 -6
- package/dist/lib/identity.js +3 -3
- package/dist/lib/messaging.js +22 -14
- package/dist/lib/reminders.js +3 -3
- package/dist/lib/schedules.js +63 -3
- package/dist/lib/skill-learning.js +3 -3
- package/dist/lib/status-brief.js +63 -5
- package/dist/lib/store.js +64 -3
- package/dist/lib/task-router.js +4 -2
- package/dist/lib/tasks.js +48 -21
- package/dist/lib/tmux-routing.js +47 -20
- package/dist/mcp/server.js +727 -58
- package/dist/mcp/tools/complete-reminder.js +3 -3
- package/dist/mcp/tools/create-reminder.js +3 -3
- package/dist/mcp/tools/create-task.js +151 -24
- package/dist/mcp/tools/deactivate-behavior.js +3 -3
- package/dist/mcp/tools/list-reminders.js +3 -3
- package/dist/mcp/tools/list-tasks.js +17 -8
- package/dist/mcp/tools/send-message.js +24 -16
- package/dist/mcp/tools/update-task.js +25 -16
- package/dist/runtime/index.js +112 -24
- package/dist/tui/App.js +139 -36
- package/package.json +6 -2
- package/src/commands/exe/rename.md +12 -0
|
@@ -23,12 +23,68 @@ var init_memory = __esm({
|
|
|
23
23
|
}
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
+
// src/lib/db-retry.ts
|
|
27
|
+
function isBusyError(err) {
|
|
28
|
+
if (err instanceof Error) {
|
|
29
|
+
const msg = err.message.toLowerCase();
|
|
30
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
31
|
+
}
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
function delay(ms) {
|
|
35
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
36
|
+
}
|
|
37
|
+
async function retryOnBusy(fn, label) {
|
|
38
|
+
let lastError;
|
|
39
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
40
|
+
try {
|
|
41
|
+
return await fn();
|
|
42
|
+
} catch (err) {
|
|
43
|
+
lastError = err;
|
|
44
|
+
if (!isBusyError(err) || attempt === MAX_RETRIES) {
|
|
45
|
+
throw err;
|
|
46
|
+
}
|
|
47
|
+
const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
48
|
+
const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
|
|
49
|
+
process.stderr.write(
|
|
50
|
+
`[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
|
|
51
|
+
`
|
|
52
|
+
);
|
|
53
|
+
await delay(backoff + jitter);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
throw lastError;
|
|
57
|
+
}
|
|
58
|
+
function wrapWithRetry(client) {
|
|
59
|
+
return new Proxy(client, {
|
|
60
|
+
get(target, prop, receiver) {
|
|
61
|
+
if (prop === "execute") {
|
|
62
|
+
return (sql) => retryOnBusy(() => target.execute(sql), "execute");
|
|
63
|
+
}
|
|
64
|
+
if (prop === "batch") {
|
|
65
|
+
return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
|
|
66
|
+
}
|
|
67
|
+
return Reflect.get(target, prop, receiver);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
|
|
72
|
+
var init_db_retry = __esm({
|
|
73
|
+
"src/lib/db-retry.ts"() {
|
|
74
|
+
"use strict";
|
|
75
|
+
MAX_RETRIES = 3;
|
|
76
|
+
BASE_DELAY_MS = 200;
|
|
77
|
+
MAX_JITTER_MS = 300;
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
26
81
|
// src/lib/database.ts
|
|
27
82
|
import { createClient } from "@libsql/client";
|
|
28
83
|
async function initDatabase(config) {
|
|
29
84
|
if (_client) {
|
|
30
85
|
_client.close();
|
|
31
86
|
_client = null;
|
|
87
|
+
_resilientClient = null;
|
|
32
88
|
}
|
|
33
89
|
const opts = {
|
|
34
90
|
url: `file:${config.dbPath}`
|
|
@@ -37,20 +93,27 @@ async function initDatabase(config) {
|
|
|
37
93
|
opts.encryptionKey = config.encryptionKey;
|
|
38
94
|
}
|
|
39
95
|
_client = createClient(opts);
|
|
96
|
+
_resilientClient = wrapWithRetry(_client);
|
|
40
97
|
}
|
|
41
98
|
function isInitialized() {
|
|
42
99
|
return _client !== null;
|
|
43
100
|
}
|
|
44
101
|
function getClient() {
|
|
102
|
+
if (!_resilientClient) {
|
|
103
|
+
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
104
|
+
}
|
|
105
|
+
return _resilientClient;
|
|
106
|
+
}
|
|
107
|
+
function getRawClient() {
|
|
45
108
|
if (!_client) {
|
|
46
109
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
47
110
|
}
|
|
48
111
|
return _client;
|
|
49
112
|
}
|
|
50
113
|
async function ensureSchema() {
|
|
51
|
-
const client =
|
|
114
|
+
const client = getRawClient();
|
|
52
115
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
53
|
-
await client.execute("PRAGMA busy_timeout =
|
|
116
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
54
117
|
try {
|
|
55
118
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
56
119
|
} catch {
|
|
@@ -839,11 +902,13 @@ async function ensureSchema() {
|
|
|
839
902
|
}
|
|
840
903
|
}
|
|
841
904
|
}
|
|
842
|
-
var _client, initTurso;
|
|
905
|
+
var _client, _resilientClient, initTurso;
|
|
843
906
|
var init_database = __esm({
|
|
844
907
|
"src/lib/database.ts"() {
|
|
845
908
|
"use strict";
|
|
909
|
+
init_db_retry();
|
|
846
910
|
_client = null;
|
|
911
|
+
_resilientClient = null;
|
|
847
912
|
initTurso = initDatabase;
|
|
848
913
|
}
|
|
849
914
|
});
|
|
@@ -1290,12 +1355,12 @@ function shardExists(projectName) {
|
|
|
1290
1355
|
}
|
|
1291
1356
|
function listShards() {
|
|
1292
1357
|
if (!existsSync3(SHARDS_DIR)) return [];
|
|
1293
|
-
const { readdirSync:
|
|
1294
|
-
return
|
|
1358
|
+
const { readdirSync: readdirSync3 } = __require("fs");
|
|
1359
|
+
return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
1295
1360
|
}
|
|
1296
1361
|
async function ensureShardSchema(client) {
|
|
1297
1362
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
1298
|
-
await client.execute("PRAGMA busy_timeout =
|
|
1363
|
+
await client.execute("PRAGMA busy_timeout = 30000");
|
|
1299
1364
|
try {
|
|
1300
1365
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
1301
1366
|
} catch {
|
|
@@ -1481,7 +1546,7 @@ var init_shard_manager = __esm({
|
|
|
1481
1546
|
|
|
1482
1547
|
// src/lib/employees.ts
|
|
1483
1548
|
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
|
|
1484
|
-
import { existsSync as existsSync5, symlinkSync, readlinkSync } from "fs";
|
|
1549
|
+
import { existsSync as existsSync5, symlinkSync, readlinkSync, readFileSync as readFileSync3 } from "fs";
|
|
1485
1550
|
import { execSync } from "child_process";
|
|
1486
1551
|
import path5 from "path";
|
|
1487
1552
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
@@ -1495,6 +1560,45 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
|
1495
1560
|
return [];
|
|
1496
1561
|
}
|
|
1497
1562
|
}
|
|
1563
|
+
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
1564
|
+
await mkdir3(path5.dirname(employeesPath), { recursive: true });
|
|
1565
|
+
await writeFile3(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
1566
|
+
}
|
|
1567
|
+
function registerBinSymlinks(name) {
|
|
1568
|
+
const created = [];
|
|
1569
|
+
const skipped = [];
|
|
1570
|
+
const errors = [];
|
|
1571
|
+
let exeBinPath;
|
|
1572
|
+
try {
|
|
1573
|
+
exeBinPath = execSync("which exe", { encoding: "utf-8" }).trim();
|
|
1574
|
+
} catch {
|
|
1575
|
+
errors.push("Could not find 'exe' in PATH");
|
|
1576
|
+
return { created, skipped, errors };
|
|
1577
|
+
}
|
|
1578
|
+
const binDir = path5.dirname(exeBinPath);
|
|
1579
|
+
let target;
|
|
1580
|
+
try {
|
|
1581
|
+
target = readlinkSync(exeBinPath);
|
|
1582
|
+
} catch {
|
|
1583
|
+
errors.push("Could not read 'exe' symlink");
|
|
1584
|
+
return { created, skipped, errors };
|
|
1585
|
+
}
|
|
1586
|
+
for (const suffix of ["", "-opencode"]) {
|
|
1587
|
+
const linkName = `${name}${suffix}`;
|
|
1588
|
+
const linkPath = path5.join(binDir, linkName);
|
|
1589
|
+
if (existsSync5(linkPath)) {
|
|
1590
|
+
skipped.push(linkName);
|
|
1591
|
+
continue;
|
|
1592
|
+
}
|
|
1593
|
+
try {
|
|
1594
|
+
symlinkSync(target, linkPath);
|
|
1595
|
+
created.push(linkName);
|
|
1596
|
+
} catch (err) {
|
|
1597
|
+
errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
return { created, skipped, errors };
|
|
1601
|
+
}
|
|
1498
1602
|
var EMPLOYEES_PATH;
|
|
1499
1603
|
var init_employees = __esm({
|
|
1500
1604
|
"src/lib/employees.ts"() {
|
|
@@ -1505,7 +1609,7 @@ var init_employees = __esm({
|
|
|
1505
1609
|
});
|
|
1506
1610
|
|
|
1507
1611
|
// src/lib/license.ts
|
|
1508
|
-
import { readFileSync as
|
|
1612
|
+
import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync6, mkdirSync as mkdirSync2 } from "fs";
|
|
1509
1613
|
import { randomUUID } from "crypto";
|
|
1510
1614
|
import path6 from "path";
|
|
1511
1615
|
import { jwtVerify, importSPKI } from "jose";
|
|
@@ -1513,14 +1617,14 @@ function loadDeviceId() {
|
|
|
1513
1617
|
const deviceJsonPath = path6.join(EXE_AI_DIR, "device.json");
|
|
1514
1618
|
try {
|
|
1515
1619
|
if (existsSync6(deviceJsonPath)) {
|
|
1516
|
-
const data = JSON.parse(
|
|
1620
|
+
const data = JSON.parse(readFileSync4(deviceJsonPath, "utf8"));
|
|
1517
1621
|
if (data.deviceId) return data.deviceId;
|
|
1518
1622
|
}
|
|
1519
1623
|
} catch {
|
|
1520
1624
|
}
|
|
1521
1625
|
try {
|
|
1522
1626
|
if (existsSync6(DEVICE_ID_PATH)) {
|
|
1523
|
-
const id2 =
|
|
1627
|
+
const id2 = readFileSync4(DEVICE_ID_PATH, "utf8").trim();
|
|
1524
1628
|
if (id2) return id2;
|
|
1525
1629
|
}
|
|
1526
1630
|
} catch {
|
|
@@ -1533,7 +1637,7 @@ function loadDeviceId() {
|
|
|
1533
1637
|
function loadLicense() {
|
|
1534
1638
|
try {
|
|
1535
1639
|
if (!existsSync6(LICENSE_PATH)) return null;
|
|
1536
|
-
return
|
|
1640
|
+
return readFileSync4(LICENSE_PATH, "utf8").trim();
|
|
1537
1641
|
} catch {
|
|
1538
1642
|
return null;
|
|
1539
1643
|
}
|
|
@@ -1563,7 +1667,7 @@ async function verifyLicenseJwt(token) {
|
|
|
1563
1667
|
async function getCachedLicense() {
|
|
1564
1668
|
try {
|
|
1565
1669
|
if (!existsSync6(CACHE_PATH)) return null;
|
|
1566
|
-
const raw = JSON.parse(
|
|
1670
|
+
const raw = JSON.parse(readFileSync4(CACHE_PATH, "utf8"));
|
|
1567
1671
|
if (!raw.token || typeof raw.token !== "string") return null;
|
|
1568
1672
|
return await verifyLicenseJwt(raw.token);
|
|
1569
1673
|
} catch {
|
|
@@ -1679,12 +1783,12 @@ __export(plan_limits_exports, {
|
|
|
1679
1783
|
countActiveMemories: () => countActiveMemories,
|
|
1680
1784
|
getLicenseSync: () => getLicenseSync
|
|
1681
1785
|
});
|
|
1682
|
-
import { readFileSync as
|
|
1786
|
+
import { readFileSync as readFileSync5, existsSync as existsSync7 } from "fs";
|
|
1683
1787
|
import path7 from "path";
|
|
1684
1788
|
function getLicenseSync() {
|
|
1685
1789
|
try {
|
|
1686
1790
|
if (!existsSync7(CACHE_PATH2)) return freeLicense();
|
|
1687
|
-
const raw = JSON.parse(
|
|
1791
|
+
const raw = JSON.parse(readFileSync5(CACHE_PATH2, "utf8"));
|
|
1688
1792
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
1689
1793
|
const parts = raw.token.split(".");
|
|
1690
1794
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -1752,7 +1856,7 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
1752
1856
|
let count = 0;
|
|
1753
1857
|
try {
|
|
1754
1858
|
if (existsSync7(filePath)) {
|
|
1755
|
-
const raw =
|
|
1859
|
+
const raw = readFileSync5(filePath, "utf8");
|
|
1756
1860
|
const employees = JSON.parse(raw);
|
|
1757
1861
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
1758
1862
|
}
|
|
@@ -1797,7 +1901,7 @@ var init_plan_limits = __esm({
|
|
|
1797
1901
|
import net from "net";
|
|
1798
1902
|
import { spawn } from "child_process";
|
|
1799
1903
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
1800
|
-
import { existsSync as existsSync8, unlinkSync as unlinkSync2, readFileSync as
|
|
1904
|
+
import { existsSync as existsSync8, unlinkSync as unlinkSync2, readFileSync as readFileSync6, openSync, closeSync, statSync } from "fs";
|
|
1801
1905
|
import path8 from "path";
|
|
1802
1906
|
import { fileURLToPath } from "url";
|
|
1803
1907
|
function handleData(chunk) {
|
|
@@ -1822,7 +1926,7 @@ function handleData(chunk) {
|
|
|
1822
1926
|
function cleanupStaleFiles() {
|
|
1823
1927
|
if (existsSync8(PID_PATH)) {
|
|
1824
1928
|
try {
|
|
1825
|
-
const pid = parseInt(
|
|
1929
|
+
const pid = parseInt(readFileSync6(PID_PATH, "utf8").trim(), 10);
|
|
1826
1930
|
if (pid > 0) {
|
|
1827
1931
|
try {
|
|
1828
1932
|
process.kill(pid, 0);
|
|
@@ -1970,11 +2074,11 @@ async function connectEmbedDaemon() {
|
|
|
1970
2074
|
}
|
|
1971
2075
|
}
|
|
1972
2076
|
const start = Date.now();
|
|
1973
|
-
let
|
|
2077
|
+
let delay2 = 100;
|
|
1974
2078
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
1975
|
-
await new Promise((r) => setTimeout(r,
|
|
2079
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
1976
2080
|
if (await connectToSocket()) return true;
|
|
1977
|
-
|
|
2081
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
1978
2082
|
}
|
|
1979
2083
|
return false;
|
|
1980
2084
|
}
|
|
@@ -2030,7 +2134,7 @@ function killAndRespawnDaemon() {
|
|
|
2030
2134
|
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
2031
2135
|
if (existsSync8(PID_PATH)) {
|
|
2032
2136
|
try {
|
|
2033
|
-
const pid = parseInt(
|
|
2137
|
+
const pid = parseInt(readFileSync6(PID_PATH, "utf8").trim(), 10);
|
|
2034
2138
|
if (pid > 0) {
|
|
2035
2139
|
try {
|
|
2036
2140
|
process.kill(pid, "SIGKILL");
|
|
@@ -2066,11 +2170,11 @@ async function embedViaClient(text, priority = "high") {
|
|
|
2066
2170
|
`);
|
|
2067
2171
|
killAndRespawnDaemon();
|
|
2068
2172
|
const start = Date.now();
|
|
2069
|
-
let
|
|
2173
|
+
let delay2 = 200;
|
|
2070
2174
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
2071
|
-
await new Promise((r) => setTimeout(r,
|
|
2175
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
2072
2176
|
if (await connectToSocket()) break;
|
|
2073
|
-
|
|
2177
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
2074
2178
|
}
|
|
2075
2179
|
if (!_connected) return null;
|
|
2076
2180
|
}
|
|
@@ -2082,11 +2186,11 @@ async function embedViaClient(text, priority = "high") {
|
|
|
2082
2186
|
`);
|
|
2083
2187
|
killAndRespawnDaemon();
|
|
2084
2188
|
const start = Date.now();
|
|
2085
|
-
let
|
|
2189
|
+
let delay2 = 200;
|
|
2086
2190
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
2087
|
-
await new Promise((r) => setTimeout(r,
|
|
2191
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
2088
2192
|
if (await connectToSocket()) break;
|
|
2089
|
-
|
|
2193
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
2090
2194
|
}
|
|
2091
2195
|
if (!_connected) return null;
|
|
2092
2196
|
const retry = await sendRequest([text], priority);
|
|
@@ -2166,10 +2270,10 @@ async function disposeEmbedder() {
|
|
|
2166
2270
|
async function embedDirect(text) {
|
|
2167
2271
|
const llamaCpp = await import("node-llama-cpp");
|
|
2168
2272
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
2169
|
-
const { existsSync:
|
|
2170
|
-
const
|
|
2171
|
-
const modelPath =
|
|
2172
|
-
if (!
|
|
2273
|
+
const { existsSync: existsSync11 } = await import("fs");
|
|
2274
|
+
const path11 = await import("path");
|
|
2275
|
+
const modelPath = path11.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
2276
|
+
if (!existsSync11(modelPath)) {
|
|
2173
2277
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
2174
2278
|
}
|
|
2175
2279
|
const llama = await llamaCpp.getLlama();
|
|
@@ -2279,10 +2383,25 @@ var init_compress = __esm({
|
|
|
2279
2383
|
// src/lib/cloud-sync.ts
|
|
2280
2384
|
var cloud_sync_exports = {};
|
|
2281
2385
|
__export(cloud_sync_exports, {
|
|
2386
|
+
buildRosterBlob: () => buildRosterBlob,
|
|
2282
2387
|
cloudPull: () => cloudPull,
|
|
2388
|
+
cloudPullRoster: () => cloudPullRoster,
|
|
2283
2389
|
cloudPush: () => cloudPush,
|
|
2284
|
-
|
|
2390
|
+
cloudPushRoster: () => cloudPushRoster,
|
|
2391
|
+
cloudSync: () => cloudSync,
|
|
2392
|
+
mergeRosterFromRemote: () => mergeRosterFromRemote
|
|
2285
2393
|
});
|
|
2394
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync2, existsSync as existsSync9, readdirSync as readdirSync2, mkdirSync as mkdirSync3, appendFileSync } from "fs";
|
|
2395
|
+
import path9 from "path";
|
|
2396
|
+
import { homedir } from "os";
|
|
2397
|
+
function logError(msg) {
|
|
2398
|
+
try {
|
|
2399
|
+
const logPath = path9.join(homedir(), ".exe-os", "workers.log");
|
|
2400
|
+
appendFileSync(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
|
|
2401
|
+
`);
|
|
2402
|
+
} catch {
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2286
2405
|
function assertSecureEndpoint(endpoint) {
|
|
2287
2406
|
if (endpoint.startsWith("https://")) return;
|
|
2288
2407
|
if (endpoint.startsWith("http://")) {
|
|
@@ -2315,8 +2434,7 @@ async function cloudPush(records, maxVersion, config) {
|
|
|
2315
2434
|
});
|
|
2316
2435
|
return resp.ok;
|
|
2317
2436
|
} catch (err) {
|
|
2318
|
-
|
|
2319
|
-
`);
|
|
2437
|
+
logError(`[cloud-sync] PUSH FAILED: ${err instanceof Error ? err.message : String(err)}`);
|
|
2320
2438
|
return false;
|
|
2321
2439
|
}
|
|
2322
2440
|
}
|
|
@@ -2347,8 +2465,7 @@ async function cloudPull(sinceVersion, config) {
|
|
|
2347
2465
|
}
|
|
2348
2466
|
return { records: allRecords, maxVersion: data.max_version ?? sinceVersion };
|
|
2349
2467
|
} catch (err) {
|
|
2350
|
-
|
|
2351
|
-
`);
|
|
2468
|
+
logError(`[cloud-sync] PULL FAILED: ${err instanceof Error ? err.message : String(err)}`);
|
|
2352
2469
|
return { records: [], maxVersion: sinceVersion };
|
|
2353
2470
|
}
|
|
2354
2471
|
}
|
|
@@ -2437,8 +2554,137 @@ async function cloudSync(config) {
|
|
|
2437
2554
|
pushed = records.length;
|
|
2438
2555
|
}
|
|
2439
2556
|
}
|
|
2557
|
+
try {
|
|
2558
|
+
await cloudPushRoster(config);
|
|
2559
|
+
} catch (err) {
|
|
2560
|
+
process.stderr.write(`[cloud-sync] Roster push warning: ${err instanceof Error ? err.message : String(err)}
|
|
2561
|
+
`);
|
|
2562
|
+
}
|
|
2563
|
+
try {
|
|
2564
|
+
await cloudPullRoster(config);
|
|
2565
|
+
} catch (err) {
|
|
2566
|
+
process.stderr.write(`[cloud-sync] Roster pull warning: ${err instanceof Error ? err.message : String(err)}
|
|
2567
|
+
`);
|
|
2568
|
+
}
|
|
2440
2569
|
return { pushed, pulled };
|
|
2441
2570
|
}
|
|
2571
|
+
function buildRosterBlob(paths) {
|
|
2572
|
+
const rosterPath = paths?.rosterPath ?? path9.join(EXE_AI_DIR, "exe-employees.json");
|
|
2573
|
+
const identityDir = paths?.identityDir ?? path9.join(EXE_AI_DIR, "identity");
|
|
2574
|
+
let roster = [];
|
|
2575
|
+
if (existsSync9(rosterPath)) {
|
|
2576
|
+
try {
|
|
2577
|
+
roster = JSON.parse(readFileSync7(rosterPath, "utf-8"));
|
|
2578
|
+
} catch {
|
|
2579
|
+
}
|
|
2580
|
+
}
|
|
2581
|
+
const identities = {};
|
|
2582
|
+
if (existsSync9(identityDir)) {
|
|
2583
|
+
for (const file of readdirSync2(identityDir).filter((f) => f.endsWith(".md"))) {
|
|
2584
|
+
try {
|
|
2585
|
+
identities[file] = readFileSync7(path9.join(identityDir, file), "utf-8");
|
|
2586
|
+
} catch {
|
|
2587
|
+
}
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
const content = JSON.stringify({ roster, identities });
|
|
2591
|
+
const hash = Buffer.from(content).length;
|
|
2592
|
+
return { roster, identities, version: hash };
|
|
2593
|
+
}
|
|
2594
|
+
async function cloudPushRoster(config) {
|
|
2595
|
+
assertSecureEndpoint(config.endpoint);
|
|
2596
|
+
const blob = buildRosterBlob();
|
|
2597
|
+
if (blob.roster.length === 0) return true;
|
|
2598
|
+
try {
|
|
2599
|
+
const client = getClient();
|
|
2600
|
+
const meta = await client.execute(
|
|
2601
|
+
"SELECT value FROM sync_meta WHERE key = 'last_roster_push_version'"
|
|
2602
|
+
);
|
|
2603
|
+
const lastVersion = meta.rows.length > 0 ? Number(meta.rows[0].value) : 0;
|
|
2604
|
+
if (blob.version === lastVersion) return true;
|
|
2605
|
+
} catch {
|
|
2606
|
+
}
|
|
2607
|
+
try {
|
|
2608
|
+
const json = JSON.stringify(blob);
|
|
2609
|
+
const compressed = compress(Buffer.from(json, "utf8"));
|
|
2610
|
+
const encrypted = encryptSyncBlob(compressed);
|
|
2611
|
+
const resp = await fetch(`${config.endpoint}/sync/push-roster`, {
|
|
2612
|
+
method: "POST",
|
|
2613
|
+
headers: {
|
|
2614
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
2615
|
+
"Content-Type": "application/json",
|
|
2616
|
+
"X-Device-Id": loadDeviceId()
|
|
2617
|
+
},
|
|
2618
|
+
body: JSON.stringify({ blob: encrypted })
|
|
2619
|
+
});
|
|
2620
|
+
if (resp.ok) {
|
|
2621
|
+
try {
|
|
2622
|
+
const client = getClient();
|
|
2623
|
+
await client.execute({
|
|
2624
|
+
sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('last_roster_push_version', ?)",
|
|
2625
|
+
args: [String(blob.version)]
|
|
2626
|
+
});
|
|
2627
|
+
} catch {
|
|
2628
|
+
}
|
|
2629
|
+
}
|
|
2630
|
+
return resp.ok;
|
|
2631
|
+
} catch (err) {
|
|
2632
|
+
process.stderr.write(`[cloud-sync] ROSTER PUSH FAILED: ${err instanceof Error ? err.message : String(err)}
|
|
2633
|
+
`);
|
|
2634
|
+
return false;
|
|
2635
|
+
}
|
|
2636
|
+
}
|
|
2637
|
+
async function cloudPullRoster(config) {
|
|
2638
|
+
assertSecureEndpoint(config.endpoint);
|
|
2639
|
+
try {
|
|
2640
|
+
const resp = await fetch(`${config.endpoint}/sync/pull-roster`, {
|
|
2641
|
+
method: "GET",
|
|
2642
|
+
headers: {
|
|
2643
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
2644
|
+
"X-Device-Id": loadDeviceId()
|
|
2645
|
+
}
|
|
2646
|
+
});
|
|
2647
|
+
if (!resp.ok) return { added: 0 };
|
|
2648
|
+
const data = await resp.json();
|
|
2649
|
+
if (!data.blob) return { added: 0 };
|
|
2650
|
+
const compressed = decryptSyncBlob(data.blob);
|
|
2651
|
+
const json = decompress(compressed).toString("utf8");
|
|
2652
|
+
const remote = JSON.parse(json);
|
|
2653
|
+
return mergeRosterFromRemote(remote);
|
|
2654
|
+
} catch (err) {
|
|
2655
|
+
process.stderr.write(`[cloud-sync] ROSTER PULL FAILED: ${err instanceof Error ? err.message : String(err)}
|
|
2656
|
+
`);
|
|
2657
|
+
return { added: 0 };
|
|
2658
|
+
}
|
|
2659
|
+
}
|
|
2660
|
+
async function mergeRosterFromRemote(remote, paths) {
|
|
2661
|
+
const rosterPath = paths?.rosterPath ?? void 0;
|
|
2662
|
+
const identityDir = paths?.identityDir ?? path9.join(EXE_AI_DIR, "identity");
|
|
2663
|
+
const localEmployees = await loadEmployees(rosterPath);
|
|
2664
|
+
const localNames = new Set(localEmployees.map((e) => e.name));
|
|
2665
|
+
let added = 0;
|
|
2666
|
+
for (const remoteEmp of remote.roster) {
|
|
2667
|
+
if (localNames.has(remoteEmp.name)) continue;
|
|
2668
|
+
localEmployees.push(remoteEmp);
|
|
2669
|
+
localNames.add(remoteEmp.name);
|
|
2670
|
+
added++;
|
|
2671
|
+
if (remote.identities[`${remoteEmp.name}.md`]) {
|
|
2672
|
+
if (!existsSync9(identityDir)) mkdirSync3(identityDir, { recursive: true });
|
|
2673
|
+
const idPath = path9.join(identityDir, `${remoteEmp.name}.md`);
|
|
2674
|
+
if (!existsSync9(idPath)) {
|
|
2675
|
+
writeFileSync2(idPath, remote.identities[`${remoteEmp.name}.md`], "utf-8");
|
|
2676
|
+
}
|
|
2677
|
+
}
|
|
2678
|
+
try {
|
|
2679
|
+
registerBinSymlinks(remoteEmp.name);
|
|
2680
|
+
} catch {
|
|
2681
|
+
}
|
|
2682
|
+
}
|
|
2683
|
+
if (added > 0) {
|
|
2684
|
+
await saveEmployees(localEmployees, rosterPath);
|
|
2685
|
+
}
|
|
2686
|
+
return { added };
|
|
2687
|
+
}
|
|
2442
2688
|
var LOCALHOST_PATTERNS;
|
|
2443
2689
|
var init_cloud_sync = __esm({
|
|
2444
2690
|
"src/lib/cloud-sync.ts"() {
|
|
@@ -2448,6 +2694,8 @@ var init_cloud_sync = __esm({
|
|
|
2448
2694
|
init_compress();
|
|
2449
2695
|
init_plan_limits();
|
|
2450
2696
|
init_license();
|
|
2697
|
+
init_config();
|
|
2698
|
+
init_employees();
|
|
2451
2699
|
LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
|
|
2452
2700
|
}
|
|
2453
2701
|
});
|
|
@@ -2724,8 +2972,8 @@ async function writeNotification(notification) {
|
|
|
2724
2972
|
|
|
2725
2973
|
// src/adapters/claude/hooks/summary-worker.ts
|
|
2726
2974
|
import { execSync as execSync2 } from "child_process";
|
|
2727
|
-
import { existsSync as
|
|
2728
|
-
import
|
|
2975
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync4, openSync as openSync2, closeSync as closeSync2 } from "fs";
|
|
2976
|
+
import path10 from "path";
|
|
2729
2977
|
async function main() {
|
|
2730
2978
|
const agentId = process.env.AGENT_ID ?? "default";
|
|
2731
2979
|
const agentRole = process.env.AGENT_ROLE ?? "employee";
|
|
@@ -2832,28 +3080,33 @@ async function main() {
|
|
|
2832
3080
|
try {
|
|
2833
3081
|
const { embed: embed2 } = await Promise.resolve().then(() => (init_embedder(), embedder_exports));
|
|
2834
3082
|
const vector = await embed2(summaryText);
|
|
2835
|
-
await client.execute({
|
|
2836
|
-
sql: `
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
)`,
|
|
2840
|
-
args: [vectorToBlob(vector), agentId]
|
|
3083
|
+
const rowResult = await client.execute({
|
|
3084
|
+
sql: `SELECT rowid FROM memories WHERE agent_id = ? AND tool_name = 'auto-summary'
|
|
3085
|
+
ORDER BY timestamp DESC LIMIT 1`,
|
|
3086
|
+
args: [agentId]
|
|
2841
3087
|
});
|
|
3088
|
+
const targetRowid = rowResult.rows[0]?.rowid;
|
|
3089
|
+
if (targetRowid != null) {
|
|
3090
|
+
await client.execute({
|
|
3091
|
+
sql: `UPDATE memories SET vector = vector32(?) WHERE rowid = ?`,
|
|
3092
|
+
args: [vectorToBlob(vector), targetRowid]
|
|
3093
|
+
});
|
|
3094
|
+
}
|
|
2842
3095
|
} catch (err) {
|
|
2843
3096
|
process.stderr.write("[summary-worker] embed failed: " + (err instanceof Error ? err.message : String(err)) + "\n");
|
|
2844
3097
|
}
|
|
2845
3098
|
try {
|
|
2846
3099
|
const { EXE_AI_DIR: EXE_AI_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
2847
|
-
const flagPath =
|
|
2848
|
-
if (
|
|
3100
|
+
const flagPath = path10.join(EXE_AI_DIR2, "session-cache", "needs-backfill");
|
|
3101
|
+
if (existsSync10(flagPath)) {
|
|
2849
3102
|
const { spawn: spawn2 } = await import("child_process");
|
|
2850
3103
|
const { fileURLToPath: fileURLToPath2 } = await import("url");
|
|
2851
3104
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
2852
|
-
const backfillPath =
|
|
2853
|
-
if (
|
|
3105
|
+
const backfillPath = path10.resolve(path10.dirname(thisFile), "backfill-vectors.js");
|
|
3106
|
+
if (existsSync10(backfillPath)) {
|
|
2854
3107
|
const { EXE_AI_DIR: exeDir2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
2855
|
-
const bLogPath =
|
|
2856
|
-
|
|
3108
|
+
const bLogPath = path10.join(exeDir2, "workers.log");
|
|
3109
|
+
mkdirSync4(path10.dirname(bLogPath), { recursive: true });
|
|
2857
3110
|
const bLogFd = openSync2(bLogPath, "a");
|
|
2858
3111
|
const child = spawn2(process.execPath, [backfillPath], {
|
|
2859
3112
|
detached: true,
|