@askexenow/exe-os 0.8.37 → 0.8.39
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/README.md +17 -8
- package/dist/bin/backfill-conversations.js +112 -70
- package/dist/bin/backfill-responses.js +53 -18
- package/dist/bin/backfill-vectors.js +43 -16
- package/dist/bin/cleanup-stale-review-tasks.js +38 -16
- package/dist/bin/cli.js +790 -468
- package/dist/bin/exe-agent.js +19 -4
- package/dist/bin/exe-assign.js +46 -13
- package/dist/bin/exe-boot.js +288 -129
- package/dist/bin/exe-call.js +20 -10
- package/dist/bin/exe-cloud.js +135 -30
- package/dist/bin/exe-dispatch.js +1 -1
- package/dist/bin/exe-doctor.js +38 -16
- package/dist/bin/exe-export-behaviors.js +43 -21
- package/dist/bin/exe-forget.js +39 -17
- package/dist/bin/exe-gateway.js +159 -50
- package/dist/bin/exe-heartbeat.js +53 -31
- package/dist/bin/exe-kill.js +40 -18
- package/dist/bin/exe-launch-agent.js +109 -36
- package/dist/bin/exe-link.js +196 -87
- package/dist/bin/exe-new-employee.js +56 -17
- package/dist/bin/exe-pending-messages.js +47 -25
- package/dist/bin/exe-pending-notifications.js +38 -16
- package/dist/bin/exe-pending-reviews.js +51 -29
- package/dist/bin/exe-rename.js +21 -7
- package/dist/bin/exe-review.js +41 -13
- package/dist/bin/exe-search.js +57 -21
- package/dist/bin/exe-session-cleanup.js +67 -31
- package/dist/bin/exe-settings.js +63 -2
- package/dist/bin/exe-status.js +35 -13
- package/dist/bin/exe-team.js +35 -13
- package/dist/bin/git-sweep.js +45 -17
- package/dist/bin/graph-backfill.js +38 -16
- package/dist/bin/graph-export.js +38 -16
- package/dist/bin/install.js +10 -1
- package/dist/bin/scan-tasks.js +47 -19
- package/dist/bin/setup.js +444 -259
- package/dist/bin/shard-migrate.js +38 -16
- package/dist/bin/wiki-sync.js +40 -17
- package/dist/gateway/index.js +113 -48
- package/dist/hooks/bug-report-worker.js +66 -39
- package/dist/hooks/commit-complete.js +45 -17
- package/dist/hooks/error-recall.js +60 -20
- package/dist/hooks/exe-heartbeat-hook.js +3 -2
- package/dist/hooks/ingest-worker.js +174 -45
- package/dist/hooks/ingest.js +74 -28
- package/dist/hooks/instructions-loaded.js +46 -17
- package/dist/hooks/notification.js +44 -15
- package/dist/hooks/post-compact.js +44 -15
- package/dist/hooks/pre-compact.js +42 -14
- package/dist/hooks/pre-tool-use.js +59 -22
- package/dist/hooks/prompt-ingest-worker.js +75 -14
- package/dist/hooks/prompt-submit.js +75 -32
- package/dist/hooks/response-ingest-worker.js +76 -15
- package/dist/hooks/session-end.js +54 -22
- package/dist/hooks/session-start.js +57 -20
- package/dist/hooks/stop.js +44 -15
- package/dist/hooks/subagent-stop.js +44 -15
- package/dist/hooks/summary-worker.js +339 -106
- package/dist/index.js +94 -23
- package/dist/lib/cloud-sync.js +191 -80
- package/dist/lib/config.js +4 -1
- package/dist/lib/consolidation.js +5 -4
- package/dist/lib/database.js +1 -0
- package/dist/lib/device-registry.js +2 -1
- package/dist/lib/embedder.js +9 -1
- package/dist/lib/employee-templates.js +5 -0
- package/dist/lib/employees.js +11 -6
- package/dist/lib/exe-daemon-client.js +6 -1
- package/dist/lib/exe-daemon.js +95 -36
- package/dist/lib/hybrid-search.js +57 -21
- package/dist/lib/identity-templates.js +16 -7
- package/dist/lib/identity.js +1 -1
- package/dist/lib/keychain.js +2 -1
- package/dist/lib/license.js +56 -6
- package/dist/lib/messaging.js +1 -1
- package/dist/lib/reminders.js +2 -2
- package/dist/lib/schedules.js +38 -16
- package/dist/lib/skill-learning.js +1 -1
- package/dist/lib/store.js +44 -16
- package/dist/lib/tasks.js +1 -1
- package/dist/lib/tmux-routing.js +1 -1
- package/dist/mcp/server.js +280 -155
- package/dist/mcp/tools/complete-reminder.js +1 -1
- package/dist/mcp/tools/create-task.js +14 -6
- package/dist/mcp/tools/deactivate-behavior.js +2 -2
- package/dist/mcp/tools/list-reminders.js +1 -1
- package/dist/mcp/tools/list-tasks.js +36 -28
- package/dist/mcp/tools/send-message.js +1 -1
- package/dist/mcp/tools/update-task.js +1 -1
- package/dist/runtime/index.js +42 -8
- package/dist/tui/App.js +220 -99
- package/package.json +5 -3
package/dist/bin/exe-boot.js
CHANGED
|
@@ -42,7 +42,7 @@ __export(config_exports, {
|
|
|
42
42
|
migrateConfig: () => migrateConfig,
|
|
43
43
|
saveConfig: () => saveConfig
|
|
44
44
|
});
|
|
45
|
-
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
45
|
+
import { readFile, writeFile, mkdir, chmod } from "fs/promises";
|
|
46
46
|
import { readFileSync, existsSync, renameSync } from "fs";
|
|
47
47
|
import path from "path";
|
|
48
48
|
import os from "os";
|
|
@@ -168,6 +168,9 @@ async function saveConfig(config) {
|
|
|
168
168
|
await mkdir(dir, { recursive: true });
|
|
169
169
|
const configPath = path.join(dir, "config.json");
|
|
170
170
|
await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
171
|
+
if (config.cloud?.apiKey) {
|
|
172
|
+
await chmod(configPath, 384);
|
|
173
|
+
}
|
|
171
174
|
}
|
|
172
175
|
async function loadConfigFrom(configPath) {
|
|
173
176
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -306,15 +309,20 @@ function isMultiInstance(agentName, employees) {
|
|
|
306
309
|
if (!emp) return false;
|
|
307
310
|
return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
|
|
308
311
|
}
|
|
312
|
+
function findExeBin() {
|
|
313
|
+
try {
|
|
314
|
+
return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
315
|
+
} catch {
|
|
316
|
+
return null;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
309
319
|
function registerBinSymlinks(name) {
|
|
310
320
|
const created = [];
|
|
311
321
|
const skipped = [];
|
|
312
322
|
const errors = [];
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
} catch {
|
|
317
|
-
errors.push("Could not find 'exe' in PATH");
|
|
323
|
+
const exeBinPath = findExeBin();
|
|
324
|
+
if (!exeBinPath) {
|
|
325
|
+
errors.push("Could not find 'exe-os' in PATH");
|
|
318
326
|
return { created, skipped, errors };
|
|
319
327
|
}
|
|
320
328
|
const binDir = path2.dirname(exeBinPath);
|
|
@@ -449,6 +457,7 @@ async function ensureSchema() {
|
|
|
449
457
|
const client = getRawClient();
|
|
450
458
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
451
459
|
await client.execute("PRAGMA busy_timeout = 30000");
|
|
460
|
+
await client.execute("PRAGMA wal_autocheckpoint = 1000");
|
|
452
461
|
try {
|
|
453
462
|
await client.execute("PRAGMA libsql_vector_search_ef = 128");
|
|
454
463
|
} catch {
|
|
@@ -1257,12 +1266,13 @@ __export(keychain_exports, {
|
|
|
1257
1266
|
importMnemonic: () => importMnemonic,
|
|
1258
1267
|
setMasterKey: () => setMasterKey
|
|
1259
1268
|
});
|
|
1260
|
-
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod } from "fs/promises";
|
|
1269
|
+
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
1261
1270
|
import { existsSync as existsSync3 } from "fs";
|
|
1262
1271
|
import path3 from "path";
|
|
1272
|
+
import os2 from "os";
|
|
1263
1273
|
import crypto from "crypto";
|
|
1264
1274
|
function getKeyDir() {
|
|
1265
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(
|
|
1275
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(os2.homedir(), ".exe-os");
|
|
1266
1276
|
}
|
|
1267
1277
|
function getKeyPath() {
|
|
1268
1278
|
return path3.join(getKeyDir(), "master.key");
|
|
@@ -1310,7 +1320,7 @@ async function setMasterKey(key) {
|
|
|
1310
1320
|
await mkdir3(dir, { recursive: true });
|
|
1311
1321
|
const keyPath = getKeyPath();
|
|
1312
1322
|
await writeFile3(keyPath, b64 + "\n", "utf-8");
|
|
1313
|
-
await
|
|
1323
|
+
await chmod2(keyPath, 384);
|
|
1314
1324
|
}
|
|
1315
1325
|
async function deleteMasterKey() {
|
|
1316
1326
|
const keytar = await tryKeytar();
|
|
@@ -1408,7 +1418,7 @@ __export(shard_manager_exports, {
|
|
|
1408
1418
|
shardExists: () => shardExists
|
|
1409
1419
|
});
|
|
1410
1420
|
import path4 from "path";
|
|
1411
|
-
import { existsSync as existsSync4, mkdirSync } from "fs";
|
|
1421
|
+
import { existsSync as existsSync4, mkdirSync, readdirSync } from "fs";
|
|
1412
1422
|
import { createClient as createClient2 } from "@libsql/client";
|
|
1413
1423
|
function initShardManager(encryptionKey) {
|
|
1414
1424
|
_encryptionKey = encryptionKey;
|
|
@@ -1447,8 +1457,7 @@ function shardExists(projectName) {
|
|
|
1447
1457
|
}
|
|
1448
1458
|
function listShards() {
|
|
1449
1459
|
if (!existsSync4(SHARDS_DIR)) return [];
|
|
1450
|
-
|
|
1451
|
-
return readdirSync7(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
1460
|
+
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
1452
1461
|
}
|
|
1453
1462
|
async function ensureShardSchema(client) {
|
|
1454
1463
|
await client.execute("PRAGMA journal_mode = WAL");
|
|
@@ -1637,6 +1646,28 @@ var init_shard_manager = __esm({
|
|
|
1637
1646
|
});
|
|
1638
1647
|
|
|
1639
1648
|
// src/lib/store.ts
|
|
1649
|
+
function isBusyError2(err) {
|
|
1650
|
+
if (err instanceof Error) {
|
|
1651
|
+
const msg = err.message.toLowerCase();
|
|
1652
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
1653
|
+
}
|
|
1654
|
+
return false;
|
|
1655
|
+
}
|
|
1656
|
+
async function retryOnBusy2(fn, label) {
|
|
1657
|
+
for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
|
|
1658
|
+
try {
|
|
1659
|
+
return await fn();
|
|
1660
|
+
} catch (err) {
|
|
1661
|
+
if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
|
|
1662
|
+
process.stderr.write(
|
|
1663
|
+
`[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
|
|
1664
|
+
`
|
|
1665
|
+
);
|
|
1666
|
+
await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
throw new Error("unreachable");
|
|
1670
|
+
}
|
|
1640
1671
|
async function initStore(options) {
|
|
1641
1672
|
if (_flushTimer !== null) {
|
|
1642
1673
|
clearInterval(_flushTimer);
|
|
@@ -1665,17 +1696,20 @@ async function initStore(options) {
|
|
|
1665
1696
|
dbPath,
|
|
1666
1697
|
encryptionKey: hexKey
|
|
1667
1698
|
});
|
|
1668
|
-
await ensureSchema();
|
|
1699
|
+
await retryOnBusy2(() => ensureSchema(), "ensureSchema");
|
|
1669
1700
|
try {
|
|
1670
1701
|
const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
1671
1702
|
initShardManager2(hexKey);
|
|
1672
1703
|
} catch {
|
|
1673
1704
|
}
|
|
1674
1705
|
const client = getClient();
|
|
1675
|
-
const vResult = await
|
|
1706
|
+
const vResult = await retryOnBusy2(
|
|
1707
|
+
() => client.execute("SELECT MAX(version) as max_v FROM memories"),
|
|
1708
|
+
"version-query"
|
|
1709
|
+
);
|
|
1676
1710
|
_nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
1677
1711
|
}
|
|
1678
|
-
var _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
|
|
1712
|
+
var INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
|
|
1679
1713
|
var init_store = __esm({
|
|
1680
1714
|
"src/lib/store.ts"() {
|
|
1681
1715
|
"use strict";
|
|
@@ -1683,6 +1717,8 @@ var init_store = __esm({
|
|
|
1683
1717
|
init_database();
|
|
1684
1718
|
init_keychain();
|
|
1685
1719
|
init_config();
|
|
1720
|
+
INIT_MAX_RETRIES = 3;
|
|
1721
|
+
INIT_RETRY_DELAY_MS = 1e3;
|
|
1686
1722
|
_pendingRecords = [];
|
|
1687
1723
|
_batchSize = 20;
|
|
1688
1724
|
_flushIntervalMs = 1e4;
|
|
@@ -1695,10 +1731,10 @@ var init_store = __esm({
|
|
|
1695
1731
|
// src/lib/notifications.ts
|
|
1696
1732
|
import crypto2 from "crypto";
|
|
1697
1733
|
import path5 from "path";
|
|
1698
|
-
import
|
|
1734
|
+
import os3 from "os";
|
|
1699
1735
|
import {
|
|
1700
1736
|
readFileSync as readFileSync3,
|
|
1701
|
-
readdirSync,
|
|
1737
|
+
readdirSync as readdirSync2,
|
|
1702
1738
|
unlinkSync,
|
|
1703
1739
|
existsSync as existsSync5,
|
|
1704
1740
|
rmdirSync
|
|
@@ -1813,12 +1849,12 @@ async function markDoneTaskNotificationsAsRead() {
|
|
|
1813
1849
|
}
|
|
1814
1850
|
}
|
|
1815
1851
|
async function migrateJsonNotifications() {
|
|
1816
|
-
const base = process.env.EXE_OS_DIR || process.env.EXE_MEM_DIR || path5.join(
|
|
1852
|
+
const base = process.env.EXE_OS_DIR || process.env.EXE_MEM_DIR || path5.join(os3.homedir(), ".exe-os");
|
|
1817
1853
|
const notifDir = path5.join(base, "notifications");
|
|
1818
1854
|
if (!existsSync5(notifDir)) return 0;
|
|
1819
1855
|
let migrated = 0;
|
|
1820
1856
|
try {
|
|
1821
|
-
const files =
|
|
1857
|
+
const files = readdirSync2(notifDir).filter((f) => f.endsWith(".json"));
|
|
1822
1858
|
if (files.length === 0) return 0;
|
|
1823
1859
|
const client = getClient();
|
|
1824
1860
|
for (const file of files) {
|
|
@@ -1846,7 +1882,7 @@ async function migrateJsonNotifications() {
|
|
|
1846
1882
|
}
|
|
1847
1883
|
}
|
|
1848
1884
|
try {
|
|
1849
|
-
const remaining =
|
|
1885
|
+
const remaining = readdirSync2(notifDir);
|
|
1850
1886
|
if (remaining.length === 0) {
|
|
1851
1887
|
rmdirSync(notifDir);
|
|
1852
1888
|
}
|
|
@@ -1910,7 +1946,7 @@ __export(session_registry_exports, {
|
|
|
1910
1946
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, existsSync as existsSync6 } from "fs";
|
|
1911
1947
|
import { execSync as execSync4 } from "child_process";
|
|
1912
1948
|
import path7 from "path";
|
|
1913
|
-
import
|
|
1949
|
+
import os4 from "os";
|
|
1914
1950
|
function registerSession(entry) {
|
|
1915
1951
|
const dir = path7.dirname(REGISTRY_PATH);
|
|
1916
1952
|
if (!existsSync6(dir)) {
|
|
@@ -1956,7 +1992,7 @@ var REGISTRY_PATH;
|
|
|
1956
1992
|
var init_session_registry = __esm({
|
|
1957
1993
|
"src/lib/session-registry.ts"() {
|
|
1958
1994
|
"use strict";
|
|
1959
|
-
REGISTRY_PATH = path7.join(
|
|
1995
|
+
REGISTRY_PATH = path7.join(os4.homedir(), ".exe-os", "session-registry.json");
|
|
1960
1996
|
}
|
|
1961
1997
|
});
|
|
1962
1998
|
|
|
@@ -2143,7 +2179,7 @@ var init_provider_table = __esm({
|
|
|
2143
2179
|
// src/lib/intercom-queue.ts
|
|
2144
2180
|
import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, renameSync as renameSync2, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
|
|
2145
2181
|
import path8 from "path";
|
|
2146
|
-
import
|
|
2182
|
+
import os5 from "os";
|
|
2147
2183
|
function ensureDir() {
|
|
2148
2184
|
const dir = path8.dirname(QUEUE_PATH);
|
|
2149
2185
|
if (!existsSync7(dir)) mkdirSync4(dir, { recursive: true });
|
|
@@ -2183,9 +2219,9 @@ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
|
2183
2219
|
var init_intercom_queue = __esm({
|
|
2184
2220
|
"src/lib/intercom-queue.ts"() {
|
|
2185
2221
|
"use strict";
|
|
2186
|
-
QUEUE_PATH = path8.join(
|
|
2222
|
+
QUEUE_PATH = path8.join(os5.homedir(), ".exe-os", "intercom-queue.json");
|
|
2187
2223
|
TTL_MS = 60 * 60 * 1e3;
|
|
2188
|
-
INTERCOM_LOG = path8.join(
|
|
2224
|
+
INTERCOM_LOG = path8.join(os5.homedir(), ".exe-os", "intercom.log");
|
|
2189
2225
|
}
|
|
2190
2226
|
});
|
|
2191
2227
|
|
|
@@ -2202,12 +2238,22 @@ __export(license_exports, {
|
|
|
2202
2238
|
loadLicense: () => loadLicense,
|
|
2203
2239
|
mirrorLicenseKey: () => mirrorLicenseKey,
|
|
2204
2240
|
saveLicense: () => saveLicense,
|
|
2241
|
+
startLicenseRevalidation: () => startLicenseRevalidation,
|
|
2242
|
+
stopLicenseRevalidation: () => stopLicenseRevalidation,
|
|
2205
2243
|
validateLicense: () => validateLicense
|
|
2206
2244
|
});
|
|
2207
2245
|
import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync8, mkdirSync as mkdirSync5 } from "fs";
|
|
2208
2246
|
import { randomUUID } from "crypto";
|
|
2209
2247
|
import path9 from "path";
|
|
2210
2248
|
import { jwtVerify, importSPKI } from "jose";
|
|
2249
|
+
async function fetchRetry(url, init) {
|
|
2250
|
+
try {
|
|
2251
|
+
return await fetch(url, init);
|
|
2252
|
+
} catch {
|
|
2253
|
+
await new Promise((r) => setTimeout(r, RETRY_DELAY_MS));
|
|
2254
|
+
return fetch(url, { ...init, signal: AbortSignal.timeout(1e4) });
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2211
2257
|
function loadDeviceId() {
|
|
2212
2258
|
const deviceJsonPath = path9.join(EXE_AI_DIR, "device.json");
|
|
2213
2259
|
try {
|
|
@@ -2239,7 +2285,7 @@ function loadLicense() {
|
|
|
2239
2285
|
}
|
|
2240
2286
|
function saveLicense(apiKey) {
|
|
2241
2287
|
mkdirSync5(EXE_AI_DIR, { recursive: true });
|
|
2242
|
-
writeFileSync4(LICENSE_PATH, apiKey.trim(), "utf8");
|
|
2288
|
+
writeFileSync4(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
|
|
2243
2289
|
}
|
|
2244
2290
|
async function verifyLicenseJwt(token) {
|
|
2245
2291
|
try {
|
|
@@ -2291,7 +2337,7 @@ function cacheResponse(token) {
|
|
|
2291
2337
|
async function validateLicense(apiKey, deviceId) {
|
|
2292
2338
|
const did = deviceId ?? loadDeviceId();
|
|
2293
2339
|
try {
|
|
2294
|
-
const res = await
|
|
2340
|
+
const res = await fetchRetry(`${API_BASE}/auth/activate`, {
|
|
2295
2341
|
method: "POST",
|
|
2296
2342
|
headers: { "Content-Type": "application/json" },
|
|
2297
2343
|
body: JSON.stringify({ apiKey, deviceId: did }),
|
|
@@ -2326,14 +2372,23 @@ async function validateLicense(apiKey, deviceId) {
|
|
|
2326
2372
|
} catch {
|
|
2327
2373
|
const cached = await getCachedLicense();
|
|
2328
2374
|
if (cached) return cached;
|
|
2329
|
-
return FREE_LICENSE;
|
|
2375
|
+
return { ...FREE_LICENSE, valid: false, error: "offline" };
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
function getCacheAgeMs() {
|
|
2379
|
+
try {
|
|
2380
|
+
const { statSync: statSync2 } = __require("fs");
|
|
2381
|
+
const s = statSync2(CACHE_PATH);
|
|
2382
|
+
return Date.now() - s.mtimeMs;
|
|
2383
|
+
} catch {
|
|
2384
|
+
return Infinity;
|
|
2330
2385
|
}
|
|
2331
2386
|
}
|
|
2332
2387
|
async function checkLicense() {
|
|
2333
2388
|
const key = loadLicense();
|
|
2334
2389
|
if (!key) return FREE_LICENSE;
|
|
2335
2390
|
const cached = await getCachedLicense();
|
|
2336
|
-
if (cached) return cached;
|
|
2391
|
+
if (cached && getCacheAgeMs() < CACHE_MAX_AGE_MS) return cached;
|
|
2337
2392
|
const deviceId = loadDeviceId();
|
|
2338
2393
|
return validateLicense(key, deviceId);
|
|
2339
2394
|
}
|
|
@@ -2373,7 +2428,7 @@ async function assertVpsLicense(opts) {
|
|
|
2373
2428
|
let explicitRejection = false;
|
|
2374
2429
|
let transientFailure = false;
|
|
2375
2430
|
try {
|
|
2376
|
-
const res = await
|
|
2431
|
+
const res = await fetchRetry(`${API_BASE}/auth/activate`, {
|
|
2377
2432
|
method: "POST",
|
|
2378
2433
|
headers: { "Content-Type": "application/json" },
|
|
2379
2434
|
body: JSON.stringify({ apiKey, deviceId }),
|
|
@@ -2454,7 +2509,28 @@ async function assertVpsLicense(opts) {
|
|
|
2454
2509
|
`License validation unreachable for more than ${graceDays} days. Restore network connectivity to https://askexe.com/cloud and retry. This VPS image refuses to boot after the offline grace window.`
|
|
2455
2510
|
);
|
|
2456
2511
|
}
|
|
2457
|
-
|
|
2512
|
+
function startLicenseRevalidation(intervalMs = 36e5) {
|
|
2513
|
+
if (_revalTimer) return;
|
|
2514
|
+
_revalTimer = setInterval(async () => {
|
|
2515
|
+
try {
|
|
2516
|
+
const license = await checkLicense();
|
|
2517
|
+
if (!license.valid) {
|
|
2518
|
+
process.stderr.write("[exe-os] License expired or invalid \u2014 features may be restricted\n");
|
|
2519
|
+
}
|
|
2520
|
+
} catch {
|
|
2521
|
+
}
|
|
2522
|
+
}, intervalMs);
|
|
2523
|
+
if (_revalTimer && typeof _revalTimer === "object" && "unref" in _revalTimer) {
|
|
2524
|
+
_revalTimer.unref();
|
|
2525
|
+
}
|
|
2526
|
+
}
|
|
2527
|
+
function stopLicenseRevalidation() {
|
|
2528
|
+
if (_revalTimer) {
|
|
2529
|
+
clearInterval(_revalTimer);
|
|
2530
|
+
_revalTimer = null;
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2533
|
+
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, RETRY_DELAY_MS, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE, CACHE_MAX_AGE_MS, _revalTimer;
|
|
2458
2534
|
var init_license = __esm({
|
|
2459
2535
|
"src/lib/license.ts"() {
|
|
2460
2536
|
"use strict";
|
|
@@ -2463,6 +2539,7 @@ var init_license = __esm({
|
|
|
2463
2539
|
CACHE_PATH = path9.join(EXE_AI_DIR, "license-cache.json");
|
|
2464
2540
|
DEVICE_ID_PATH = path9.join(EXE_AI_DIR, "device-id");
|
|
2465
2541
|
API_BASE = "https://askexe.com/cloud";
|
|
2542
|
+
RETRY_DELAY_MS = 500;
|
|
2466
2543
|
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
2467
2544
|
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
2468
2545
|
4uj+UqeKCcvtgNHKmOK278HJaJcANe9xAeji8AFYu27q3WtzCi04pHudow==
|
|
@@ -2484,6 +2561,8 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
|
2484
2561
|
employeeLimit: 1,
|
|
2485
2562
|
memoryLimit: 5e3
|
|
2486
2563
|
};
|
|
2564
|
+
CACHE_MAX_AGE_MS = 36e5;
|
|
2565
|
+
_revalTimer = null;
|
|
2487
2566
|
}
|
|
2488
2567
|
});
|
|
2489
2568
|
|
|
@@ -2577,7 +2656,7 @@ var init_plan_limits = __esm({
|
|
|
2577
2656
|
import { execFileSync as execFileSync2, execSync as execSync6 } from "child_process";
|
|
2578
2657
|
import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, existsSync as existsSync10, appendFileSync } from "fs";
|
|
2579
2658
|
import path11 from "path";
|
|
2580
|
-
import
|
|
2659
|
+
import os6 from "os";
|
|
2581
2660
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2582
2661
|
import { unlinkSync as unlinkSync3 } from "fs";
|
|
2583
2662
|
function spawnLockPath(sessionName) {
|
|
@@ -2882,7 +2961,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
2882
2961
|
const transport = getTransport();
|
|
2883
2962
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
2884
2963
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
2885
|
-
const logDir = path11.join(
|
|
2964
|
+
const logDir = path11.join(os6.homedir(), ".exe-os", "session-logs");
|
|
2886
2965
|
const logFile = path11.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
2887
2966
|
if (!existsSync10(logDir)) {
|
|
2888
2967
|
mkdirSync6(logDir, { recursive: true });
|
|
@@ -2898,7 +2977,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
2898
2977
|
} catch {
|
|
2899
2978
|
}
|
|
2900
2979
|
try {
|
|
2901
|
-
const claudeJsonPath = path11.join(
|
|
2980
|
+
const claudeJsonPath = path11.join(os6.homedir(), ".claude.json");
|
|
2902
2981
|
let claudeJson = {};
|
|
2903
2982
|
try {
|
|
2904
2983
|
claudeJson = JSON.parse(readFileSync9(claudeJsonPath, "utf8"));
|
|
@@ -2913,7 +2992,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
2913
2992
|
} catch {
|
|
2914
2993
|
}
|
|
2915
2994
|
try {
|
|
2916
|
-
const settingsDir = path11.join(
|
|
2995
|
+
const settingsDir = path11.join(os6.homedir(), ".claude", "projects");
|
|
2917
2996
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
2918
2997
|
const projSettingsDir = path11.join(settingsDir, normalizedKey);
|
|
2919
2998
|
const settingsPath = path11.join(projSettingsDir, "settings.json");
|
|
@@ -2961,7 +3040,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
2961
3040
|
let legacyFallbackWarned = false;
|
|
2962
3041
|
if (!useExeAgent && !useBinSymlink) {
|
|
2963
3042
|
const identityPath = path11.join(
|
|
2964
|
-
|
|
3043
|
+
os6.homedir(),
|
|
2965
3044
|
".exe-os",
|
|
2966
3045
|
"identity",
|
|
2967
3046
|
`${employeeName}.md`
|
|
@@ -2991,7 +3070,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
2991
3070
|
}
|
|
2992
3071
|
let sessionContextFlag = "";
|
|
2993
3072
|
try {
|
|
2994
|
-
const ctxDir = path11.join(
|
|
3073
|
+
const ctxDir = path11.join(os6.homedir(), ".exe-os", "session-cache");
|
|
2995
3074
|
mkdirSync6(ctxDir, { recursive: true });
|
|
2996
3075
|
const ctxFile = path11.join(ctxDir, `session-context-${sessionName}.md`);
|
|
2997
3076
|
const ctxContent = [
|
|
@@ -3102,11 +3181,11 @@ var init_tmux_routing = __esm({
|
|
|
3102
3181
|
init_provider_table();
|
|
3103
3182
|
init_intercom_queue();
|
|
3104
3183
|
init_plan_limits();
|
|
3105
|
-
SPAWN_LOCK_DIR = path11.join(
|
|
3106
|
-
SESSION_CACHE = path11.join(
|
|
3184
|
+
SPAWN_LOCK_DIR = path11.join(os6.homedir(), ".exe-os", "spawn-locks");
|
|
3185
|
+
SESSION_CACHE = path11.join(os6.homedir(), ".exe-os", "session-cache");
|
|
3107
3186
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
3108
3187
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
3109
|
-
INTERCOM_LOG2 = path11.join(
|
|
3188
|
+
INTERCOM_LOG2 = path11.join(os6.homedir(), ".exe-os", "intercom.log");
|
|
3110
3189
|
DEBOUNCE_FILE = path11.join(SESSION_CACHE, "intercom-debounce.json");
|
|
3111
3190
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
3112
3191
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
|
|
@@ -3124,7 +3203,7 @@ __export(task_scanner_exports, {
|
|
|
3124
3203
|
formatText: () => formatText,
|
|
3125
3204
|
scanAgentTasks: () => scanAgentTasks
|
|
3126
3205
|
});
|
|
3127
|
-
import { readdirSync as
|
|
3206
|
+
import { readdirSync as readdirSync4, readFileSync as readFileSync10, existsSync as existsSync11, statSync } from "fs";
|
|
3128
3207
|
import { execSync as execSync7 } from "child_process";
|
|
3129
3208
|
import path12 from "path";
|
|
3130
3209
|
function getProjectRoot() {
|
|
@@ -3146,7 +3225,7 @@ function scanAgentTasks(agentId) {
|
|
|
3146
3225
|
let total = 0;
|
|
3147
3226
|
if (!existsSync11(taskDir)) return { open, inProgress, done, total };
|
|
3148
3227
|
try {
|
|
3149
|
-
const files =
|
|
3228
|
+
const files = readdirSync4(taskDir).filter((f) => f.endsWith(".md"));
|
|
3150
3229
|
total = files.length;
|
|
3151
3230
|
for (const f of files) {
|
|
3152
3231
|
try {
|
|
@@ -3702,7 +3781,7 @@ var init_tasks_crud = __esm({
|
|
|
3702
3781
|
|
|
3703
3782
|
// src/lib/tasks-review.ts
|
|
3704
3783
|
import path15 from "path";
|
|
3705
|
-
import { existsSync as existsSync13, readdirSync as
|
|
3784
|
+
import { existsSync as existsSync13, readdirSync as readdirSync5, unlinkSync as unlinkSync4 } from "fs";
|
|
3706
3785
|
async function countPendingReviews() {
|
|
3707
3786
|
const client = getClient();
|
|
3708
3787
|
const result = await client.execute({
|
|
@@ -3824,7 +3903,7 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
3824
3903
|
try {
|
|
3825
3904
|
const cacheDir = path15.join(EXE_AI_DIR, "session-cache");
|
|
3826
3905
|
if (existsSync13(cacheDir)) {
|
|
3827
|
-
for (const f of
|
|
3906
|
+
for (const f of readdirSync5(cacheDir)) {
|
|
3828
3907
|
if (f.startsWith("review-notified-")) {
|
|
3829
3908
|
unlinkSync4(path15.join(cacheDir, f));
|
|
3830
3909
|
}
|
|
@@ -4707,6 +4786,7 @@ var init_compress = __esm({
|
|
|
4707
4786
|
// src/lib/cloud-sync.ts
|
|
4708
4787
|
var cloud_sync_exports = {};
|
|
4709
4788
|
__export(cloud_sync_exports, {
|
|
4789
|
+
assertSecureEndpoint: () => assertSecureEndpoint,
|
|
4710
4790
|
buildRosterBlob: () => buildRosterBlob,
|
|
4711
4791
|
cloudPull: () => cloudPull,
|
|
4712
4792
|
cloudPullBehaviors: () => cloudPullBehaviors,
|
|
@@ -4726,9 +4806,10 @@ __export(cloud_sync_exports, {
|
|
|
4726
4806
|
cloudPushTasks: () => cloudPushTasks,
|
|
4727
4807
|
cloudSync: () => cloudSync,
|
|
4728
4808
|
mergeConfig: () => mergeConfig,
|
|
4729
|
-
mergeRosterFromRemote: () => mergeRosterFromRemote
|
|
4809
|
+
mergeRosterFromRemote: () => mergeRosterFromRemote,
|
|
4810
|
+
recordRosterDeletion: () => recordRosterDeletion
|
|
4730
4811
|
});
|
|
4731
|
-
import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, existsSync as existsSync14, readdirSync as
|
|
4812
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, existsSync as existsSync14, readdirSync as readdirSync6, mkdirSync as mkdirSync8, appendFileSync as appendFileSync2, unlinkSync as unlinkSync6 } from "fs";
|
|
4732
4813
|
import path18 from "path";
|
|
4733
4814
|
import { homedir } from "os";
|
|
4734
4815
|
function logError(msg) {
|
|
@@ -4739,16 +4820,47 @@ function logError(msg) {
|
|
|
4739
4820
|
} catch {
|
|
4740
4821
|
}
|
|
4741
4822
|
}
|
|
4823
|
+
async function withRosterLock(fn) {
|
|
4824
|
+
if (existsSync14(ROSTER_LOCK_PATH)) {
|
|
4825
|
+
try {
|
|
4826
|
+
const ts = parseInt(readFileSync12(ROSTER_LOCK_PATH, "utf-8"), 10);
|
|
4827
|
+
if (Date.now() - ts < LOCK_STALE_MS) {
|
|
4828
|
+
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
4829
|
+
}
|
|
4830
|
+
} catch (err) {
|
|
4831
|
+
if (err instanceof Error && err.message.includes("already in progress")) throw err;
|
|
4832
|
+
}
|
|
4833
|
+
}
|
|
4834
|
+
writeFileSync7(ROSTER_LOCK_PATH, String(Date.now()));
|
|
4835
|
+
try {
|
|
4836
|
+
return await fn();
|
|
4837
|
+
} finally {
|
|
4838
|
+
try {
|
|
4839
|
+
unlinkSync6(ROSTER_LOCK_PATH);
|
|
4840
|
+
} catch {
|
|
4841
|
+
}
|
|
4842
|
+
}
|
|
4843
|
+
}
|
|
4742
4844
|
async function fetchWithRetry(url, init) {
|
|
4743
|
-
const
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
|
|
4749
|
-
|
|
4845
|
+
const MAX_RETRIES2 = 3;
|
|
4846
|
+
const BASE_DELAY_MS2 = 200;
|
|
4847
|
+
let lastError;
|
|
4848
|
+
for (let attempt = 0; attempt <= MAX_RETRIES2; attempt++) {
|
|
4849
|
+
try {
|
|
4850
|
+
const signal = AbortSignal.timeout(FETCH_TIMEOUT_MS);
|
|
4851
|
+
const resp = await fetch(url, { ...init, signal });
|
|
4852
|
+
if (resp.status >= 500 && attempt < MAX_RETRIES2) {
|
|
4853
|
+
await new Promise((r) => setTimeout(r, BASE_DELAY_MS2 * Math.pow(2, attempt)));
|
|
4854
|
+
continue;
|
|
4855
|
+
}
|
|
4856
|
+
return resp;
|
|
4857
|
+
} catch (err) {
|
|
4858
|
+
lastError = err;
|
|
4859
|
+
if (attempt === MAX_RETRIES2) throw err;
|
|
4860
|
+
await new Promise((r) => setTimeout(r, BASE_DELAY_MS2 * Math.pow(2, attempt)));
|
|
4861
|
+
}
|
|
4750
4862
|
}
|
|
4751
|
-
|
|
4863
|
+
throw lastError;
|
|
4752
4864
|
}
|
|
4753
4865
|
function assertSecureEndpoint(endpoint) {
|
|
4754
4866
|
if (endpoint.startsWith("https://")) return;
|
|
@@ -4776,10 +4888,15 @@ async function cloudPush(records, maxVersion, config) {
|
|
|
4776
4888
|
headers: {
|
|
4777
4889
|
Authorization: `Bearer ${config.apiKey}`,
|
|
4778
4890
|
"Content-Type": "application/json",
|
|
4779
|
-
"X-Device-Id": loadDeviceId()
|
|
4891
|
+
"X-Device-Id": loadDeviceId(),
|
|
4892
|
+
"X-Expected-Version": String(maxVersion)
|
|
4780
4893
|
},
|
|
4781
4894
|
body: JSON.stringify({ version: maxVersion, blob })
|
|
4782
4895
|
});
|
|
4896
|
+
if (resp.status === 409) {
|
|
4897
|
+
logError("[cloud-sync] PUSH VERSION CONFLICT \u2014 re-pull required before next push");
|
|
4898
|
+
return false;
|
|
4899
|
+
}
|
|
4783
4900
|
return resp.ok;
|
|
4784
4901
|
} catch (err) {
|
|
4785
4902
|
logError(`[cloud-sync] PUSH FAILED: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -4866,18 +4983,21 @@ async function cloudSync(config) {
|
|
|
4866
4983
|
"SELECT value FROM sync_meta WHERE key = 'last_cloud_push_version'"
|
|
4867
4984
|
);
|
|
4868
4985
|
const lastPushVersion = pushMeta.rows.length > 0 ? Number(pushMeta.rows[0].value) : 0;
|
|
4869
|
-
const recordsResult = await client.execute({
|
|
4870
|
-
sql: `SELECT id, agent_id, agent_role, session_id, timestamp,
|
|
4871
|
-
tool_name, project_name, has_error, raw_text, version,
|
|
4872
|
-
author_device_id, scope
|
|
4873
|
-
FROM memories
|
|
4874
|
-
WHERE version > ?
|
|
4875
|
-
AND (scope IS NULL OR scope != 'personal')
|
|
4876
|
-
ORDER BY version ASC`,
|
|
4877
|
-
args: [lastPushVersion]
|
|
4878
|
-
});
|
|
4879
4986
|
let pushed = 0;
|
|
4880
|
-
|
|
4987
|
+
let batchCursor = lastPushVersion;
|
|
4988
|
+
while (true) {
|
|
4989
|
+
const recordsResult = await client.execute({
|
|
4990
|
+
sql: `SELECT id, agent_id, agent_role, session_id, timestamp,
|
|
4991
|
+
tool_name, project_name, has_error, raw_text, version,
|
|
4992
|
+
author_device_id, scope
|
|
4993
|
+
FROM memories
|
|
4994
|
+
WHERE version > ?
|
|
4995
|
+
AND (scope IS NULL OR scope != 'personal')
|
|
4996
|
+
ORDER BY version ASC
|
|
4997
|
+
LIMIT ?`,
|
|
4998
|
+
args: [batchCursor, PUSH_BATCH_SIZE]
|
|
4999
|
+
});
|
|
5000
|
+
if (recordsResult.rows.length === 0) break;
|
|
4881
5001
|
const records = recordsResult.rows.map((row) => ({
|
|
4882
5002
|
id: row.id,
|
|
4883
5003
|
agent_id: row.agent_id,
|
|
@@ -4894,13 +5014,14 @@ async function cloudSync(config) {
|
|
|
4894
5014
|
}));
|
|
4895
5015
|
const maxVersion = Number(records[records.length - 1].version);
|
|
4896
5016
|
const pushOk = await cloudPush(records, maxVersion, config);
|
|
4897
|
-
if (pushOk)
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
5017
|
+
if (!pushOk) break;
|
|
5018
|
+
await client.execute({
|
|
5019
|
+
sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('last_cloud_push_version', ?)",
|
|
5020
|
+
args: [String(maxVersion)]
|
|
5021
|
+
});
|
|
5022
|
+
pushed += records.length;
|
|
5023
|
+
batchCursor = maxVersion;
|
|
5024
|
+
if (recordsResult.rows.length < PUSH_BATCH_SIZE) break;
|
|
4904
5025
|
}
|
|
4905
5026
|
try {
|
|
4906
5027
|
await cloudPushRoster(config);
|
|
@@ -4982,6 +5103,27 @@ async function cloudSync(config) {
|
|
|
4982
5103
|
documents: documentsResult
|
|
4983
5104
|
};
|
|
4984
5105
|
}
|
|
5106
|
+
function recordRosterDeletion(name) {
|
|
5107
|
+
let deletions = [];
|
|
5108
|
+
try {
|
|
5109
|
+
if (existsSync14(ROSTER_DELETIONS_PATH)) {
|
|
5110
|
+
deletions = JSON.parse(readFileSync12(ROSTER_DELETIONS_PATH, "utf-8"));
|
|
5111
|
+
}
|
|
5112
|
+
} catch {
|
|
5113
|
+
}
|
|
5114
|
+
if (!deletions.includes(name)) deletions.push(name);
|
|
5115
|
+
writeFileSync7(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
|
|
5116
|
+
}
|
|
5117
|
+
function consumeRosterDeletions() {
|
|
5118
|
+
try {
|
|
5119
|
+
if (!existsSync14(ROSTER_DELETIONS_PATH)) return [];
|
|
5120
|
+
const deletions = JSON.parse(readFileSync12(ROSTER_DELETIONS_PATH, "utf-8"));
|
|
5121
|
+
writeFileSync7(ROSTER_DELETIONS_PATH, "[]");
|
|
5122
|
+
return deletions;
|
|
5123
|
+
} catch {
|
|
5124
|
+
return [];
|
|
5125
|
+
}
|
|
5126
|
+
}
|
|
4985
5127
|
function buildRosterBlob(paths) {
|
|
4986
5128
|
const rosterPath = paths?.rosterPath ?? path18.join(EXE_AI_DIR, "exe-employees.json");
|
|
4987
5129
|
const identityDir = paths?.identityDir ?? path18.join(EXE_AI_DIR, "identity");
|
|
@@ -4995,7 +5137,7 @@ function buildRosterBlob(paths) {
|
|
|
4995
5137
|
}
|
|
4996
5138
|
const identities = {};
|
|
4997
5139
|
if (existsSync14(identityDir)) {
|
|
4998
|
-
for (const file of
|
|
5140
|
+
for (const file of readdirSync6(identityDir).filter((f) => f.endsWith(".md"))) {
|
|
4999
5141
|
try {
|
|
5000
5142
|
identities[file] = readFileSync12(path18.join(identityDir, file), "utf-8");
|
|
5001
5143
|
} catch {
|
|
@@ -5009,9 +5151,10 @@ function buildRosterBlob(paths) {
|
|
|
5009
5151
|
} catch {
|
|
5010
5152
|
}
|
|
5011
5153
|
}
|
|
5012
|
-
const
|
|
5154
|
+
const deletedNames = consumeRosterDeletions();
|
|
5155
|
+
const content = JSON.stringify({ roster, identities, config, deletedNames });
|
|
5013
5156
|
const hash = Buffer.from(content).length;
|
|
5014
|
-
return { roster, identities, config, version: hash };
|
|
5157
|
+
return { roster, identities, config, deletedNames, version: hash };
|
|
5015
5158
|
}
|
|
5016
5159
|
async function cloudPushRoster(config) {
|
|
5017
5160
|
assertSecureEndpoint(config.endpoint);
|
|
@@ -5094,38 +5237,50 @@ function mergeConfig(remoteConfig, configPath) {
|
|
|
5094
5237
|
writeFileSync7(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
5095
5238
|
}
|
|
5096
5239
|
async function mergeRosterFromRemote(remote, paths) {
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
if (
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5240
|
+
return withRosterLock(async () => {
|
|
5241
|
+
const rosterPath = paths?.rosterPath ?? void 0;
|
|
5242
|
+
const identityDir = paths?.identityDir ?? path18.join(EXE_AI_DIR, "identity");
|
|
5243
|
+
const localEmployees = await loadEmployees(rosterPath);
|
|
5244
|
+
const localNames = new Set(localEmployees.map((e) => e.name));
|
|
5245
|
+
let added = 0;
|
|
5246
|
+
for (const remoteEmp of remote.roster) {
|
|
5247
|
+
if (localNames.has(remoteEmp.name)) continue;
|
|
5248
|
+
localEmployees.push(remoteEmp);
|
|
5249
|
+
localNames.add(remoteEmp.name);
|
|
5250
|
+
added++;
|
|
5251
|
+
if (remote.identities[`${remoteEmp.name}.md`]) {
|
|
5252
|
+
if (!existsSync14(identityDir)) mkdirSync8(identityDir, { recursive: true });
|
|
5253
|
+
const idPath = path18.join(identityDir, `${remoteEmp.name}.md`);
|
|
5254
|
+
if (!existsSync14(idPath)) {
|
|
5255
|
+
writeFileSync7(idPath, remote.identities[`${remoteEmp.name}.md`], "utf-8");
|
|
5256
|
+
}
|
|
5257
|
+
}
|
|
5258
|
+
try {
|
|
5259
|
+
registerBinSymlinks(remoteEmp.name);
|
|
5260
|
+
} catch {
|
|
5112
5261
|
}
|
|
5113
5262
|
}
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
5263
|
+
let removed = 0;
|
|
5264
|
+
if (remote.deletedNames && remote.deletedNames.length > 0) {
|
|
5265
|
+
const toRemove = new Set(remote.deletedNames);
|
|
5266
|
+
const filtered = localEmployees.filter((e) => !toRemove.has(e.name));
|
|
5267
|
+
removed = localEmployees.length - filtered.length;
|
|
5268
|
+
if (removed > 0) {
|
|
5269
|
+
localEmployees.length = 0;
|
|
5270
|
+
localEmployees.push(...filtered);
|
|
5271
|
+
}
|
|
5117
5272
|
}
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
await saveEmployees(localEmployees, rosterPath);
|
|
5121
|
-
}
|
|
5122
|
-
if (remote.config && Object.keys(remote.config).length > 0) {
|
|
5123
|
-
try {
|
|
5124
|
-
mergeConfig(remote.config, paths?.configPath);
|
|
5125
|
-
} catch {
|
|
5273
|
+
if (added > 0 || removed > 0) {
|
|
5274
|
+
await saveEmployees(localEmployees, rosterPath);
|
|
5126
5275
|
}
|
|
5127
|
-
|
|
5128
|
-
|
|
5276
|
+
if (remote.config && Object.keys(remote.config).length > 0) {
|
|
5277
|
+
try {
|
|
5278
|
+
mergeConfig(remote.config, paths?.configPath);
|
|
5279
|
+
} catch {
|
|
5280
|
+
}
|
|
5281
|
+
}
|
|
5282
|
+
return { added };
|
|
5283
|
+
});
|
|
5129
5284
|
}
|
|
5130
5285
|
async function cloudPushBlob(route, data, metaKey, config) {
|
|
5131
5286
|
if (data.length === 0) return { ok: true };
|
|
@@ -5193,7 +5348,7 @@ async function cloudPullBlob(route, config) {
|
|
|
5193
5348
|
}
|
|
5194
5349
|
async function cloudPushBehaviors(config) {
|
|
5195
5350
|
const client = getClient();
|
|
5196
|
-
const result = await client.execute("SELECT * FROM behaviors");
|
|
5351
|
+
const result = await client.execute("SELECT * FROM behaviors LIMIT 10000");
|
|
5197
5352
|
const rows = result.rows;
|
|
5198
5353
|
const { ok } = await cloudPushBlob(
|
|
5199
5354
|
"/sync/push-behaviors",
|
|
@@ -5241,13 +5396,13 @@ async function cloudPullBehaviors(config) {
|
|
|
5241
5396
|
async function cloudPushGraphRAG(config) {
|
|
5242
5397
|
const client = getClient();
|
|
5243
5398
|
const [entities, relationships, aliases, entityMems, relMems, hyperedges, hyperedgeNodes] = await Promise.all([
|
|
5244
|
-
client.execute("SELECT * FROM entities"),
|
|
5245
|
-
client.execute("SELECT * FROM relationships"),
|
|
5246
|
-
client.execute("SELECT * FROM entity_aliases"),
|
|
5247
|
-
client.execute("SELECT * FROM entity_memories"),
|
|
5248
|
-
client.execute("SELECT * FROM relationship_memories"),
|
|
5249
|
-
client.execute("SELECT * FROM hyperedges"),
|
|
5250
|
-
client.execute("SELECT * FROM hyperedge_nodes")
|
|
5399
|
+
client.execute("SELECT * FROM entities LIMIT 50000"),
|
|
5400
|
+
client.execute("SELECT * FROM relationships LIMIT 50000"),
|
|
5401
|
+
client.execute("SELECT * FROM entity_aliases LIMIT 50000"),
|
|
5402
|
+
client.execute("SELECT * FROM entity_memories LIMIT 50000"),
|
|
5403
|
+
client.execute("SELECT * FROM relationship_memories LIMIT 50000"),
|
|
5404
|
+
client.execute("SELECT * FROM hyperedges LIMIT 50000"),
|
|
5405
|
+
client.execute("SELECT * FROM hyperedge_nodes LIMIT 50000")
|
|
5251
5406
|
]);
|
|
5252
5407
|
const blob = {
|
|
5253
5408
|
entities: entities.rows,
|
|
@@ -5349,7 +5504,7 @@ async function cloudPullGraphRAG(config) {
|
|
|
5349
5504
|
}
|
|
5350
5505
|
async function cloudPushTasks(config) {
|
|
5351
5506
|
const client = getClient();
|
|
5352
|
-
const result = await client.execute("SELECT * FROM tasks");
|
|
5507
|
+
const result = await client.execute("SELECT * FROM tasks LIMIT 10000");
|
|
5353
5508
|
const rows = result.rows;
|
|
5354
5509
|
const { ok } = await cloudPushBlob(
|
|
5355
5510
|
"/sync/push-tasks",
|
|
@@ -5395,7 +5550,7 @@ async function cloudPullTasks(config) {
|
|
|
5395
5550
|
}
|
|
5396
5551
|
async function cloudPushConversations(config) {
|
|
5397
5552
|
const client = getClient();
|
|
5398
|
-
const result = await client.execute("SELECT * FROM conversations");
|
|
5553
|
+
const result = await client.execute("SELECT * FROM conversations LIMIT 50000");
|
|
5399
5554
|
const rows = result.rows;
|
|
5400
5555
|
const { ok } = await cloudPushBlob(
|
|
5401
5556
|
"/sync/push-conversations",
|
|
@@ -5445,8 +5600,8 @@ async function cloudPullConversations(config) {
|
|
|
5445
5600
|
async function cloudPushDocuments(config) {
|
|
5446
5601
|
const client = getClient();
|
|
5447
5602
|
const [workspaces, documents] = await Promise.all([
|
|
5448
|
-
client.execute("SELECT * FROM workspaces"),
|
|
5449
|
-
client.execute("SELECT * FROM documents")
|
|
5603
|
+
client.execute("SELECT * FROM workspaces LIMIT 1000"),
|
|
5604
|
+
client.execute("SELECT * FROM documents LIMIT 10000")
|
|
5450
5605
|
]);
|
|
5451
5606
|
const blob = {
|
|
5452
5607
|
workspaces: workspaces.rows,
|
|
@@ -5499,7 +5654,7 @@ async function cloudPullDocuments(config) {
|
|
|
5499
5654
|
}
|
|
5500
5655
|
return { pulled };
|
|
5501
5656
|
}
|
|
5502
|
-
var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS;
|
|
5657
|
+
var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, ROSTER_DELETIONS_PATH;
|
|
5503
5658
|
var init_cloud_sync = __esm({
|
|
5504
5659
|
"src/lib/cloud-sync.ts"() {
|
|
5505
5660
|
"use strict";
|
|
@@ -5512,6 +5667,10 @@ var init_cloud_sync = __esm({
|
|
|
5512
5667
|
init_employees();
|
|
5513
5668
|
LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
|
|
5514
5669
|
FETCH_TIMEOUT_MS = 3e4;
|
|
5670
|
+
PUSH_BATCH_SIZE = 5e3;
|
|
5671
|
+
ROSTER_LOCK_PATH = path18.join(EXE_AI_DIR, "roster-merge.lock");
|
|
5672
|
+
LOCK_STALE_MS = 3e4;
|
|
5673
|
+
ROSTER_DELETIONS_PATH = path18.join(EXE_AI_DIR, "roster-deletions.json");
|
|
5515
5674
|
}
|
|
5516
5675
|
});
|
|
5517
5676
|
|
|
@@ -5695,8 +5854,8 @@ var init_schedules = __esm({
|
|
|
5695
5854
|
init_employees();
|
|
5696
5855
|
import path19 from "path";
|
|
5697
5856
|
import { mkdir as mkdir5, writeFile as writeFile6 } from "fs/promises";
|
|
5698
|
-
import { existsSync as existsSync15, readFileSync as readFileSync13, readdirSync as
|
|
5699
|
-
import
|
|
5857
|
+
import { existsSync as existsSync15, readFileSync as readFileSync13, readdirSync as readdirSync7, unlinkSync as unlinkSync7 } from "fs";
|
|
5858
|
+
import os7 from "os";
|
|
5700
5859
|
|
|
5701
5860
|
// src/lib/employee-templates.ts
|
|
5702
5861
|
var DEFAULT_EXE = {
|
|
@@ -6034,7 +6193,7 @@ init_notifications();
|
|
|
6034
6193
|
|
|
6035
6194
|
// src/adapters/claude/active-agent.ts
|
|
6036
6195
|
init_config();
|
|
6037
|
-
import { readFileSync as readFileSync4, writeFileSync, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2, readdirSync as
|
|
6196
|
+
import { readFileSync as readFileSync4, writeFileSync, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2, readdirSync as readdirSync3 } from "fs";
|
|
6038
6197
|
import { execSync as execSync3 } from "child_process";
|
|
6039
6198
|
import path6 from "path";
|
|
6040
6199
|
|
|
@@ -6144,18 +6303,18 @@ async function boot(options) {
|
|
|
6144
6303
|
} catch {
|
|
6145
6304
|
}
|
|
6146
6305
|
try {
|
|
6147
|
-
const { readdirSync:
|
|
6306
|
+
const { readdirSync: readdirSync8, readFileSync: readFs } = await import("fs");
|
|
6148
6307
|
const { STATUS_RE: STATUS_RE2, PRIORITY_RE: PRIORITY_RE2, TITLE_RE: TITLE_RE2 } = await Promise.resolve().then(() => (init_task_scanner(), task_scanner_exports));
|
|
6149
6308
|
const { getProjectName: getProjectName2 } = await Promise.resolve().then(() => (init_project_name(), project_name_exports));
|
|
6150
6309
|
const exeDir = "exe";
|
|
6151
|
-
const entries =
|
|
6310
|
+
const entries = readdirSync8(exeDir, { withFileTypes: true });
|
|
6152
6311
|
const employeeDirs = entries.filter((e) => e.isDirectory() && !["output", "research"].includes(e.name));
|
|
6153
6312
|
for (const dir of employeeDirs) {
|
|
6154
6313
|
const employee = dir.name;
|
|
6155
6314
|
const taskDir = path19.join(exeDir, employee);
|
|
6156
6315
|
let files;
|
|
6157
6316
|
try {
|
|
6158
|
-
files =
|
|
6317
|
+
files = readdirSync8(taskDir).filter((f) => f.endsWith(".md"));
|
|
6159
6318
|
} catch {
|
|
6160
6319
|
continue;
|
|
6161
6320
|
}
|
|
@@ -6249,10 +6408,10 @@ async function boot(options) {
|
|
|
6249
6408
|
try {
|
|
6250
6409
|
const exeExeDir = path19.join(process.cwd(), "exe", "exe");
|
|
6251
6410
|
if (existsSync15(exeExeDir)) {
|
|
6252
|
-
for (const f of
|
|
6411
|
+
for (const f of readdirSync7(exeExeDir)) {
|
|
6253
6412
|
if (f.startsWith("review-") && f.endsWith(".md")) {
|
|
6254
6413
|
try {
|
|
6255
|
-
|
|
6414
|
+
unlinkSync7(path19.join(exeExeDir, f));
|
|
6256
6415
|
} catch {
|
|
6257
6416
|
}
|
|
6258
6417
|
}
|
|
@@ -6762,7 +6921,7 @@ async function boot(options) {
|
|
|
6762
6921
|
]);
|
|
6763
6922
|
try {
|
|
6764
6923
|
const configPath = path19.join(
|
|
6765
|
-
process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path19.join(
|
|
6924
|
+
process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path19.join(os7.homedir(), ".exe-os"),
|
|
6766
6925
|
"config.json"
|
|
6767
6926
|
);
|
|
6768
6927
|
if (existsSync15(configPath)) {
|
|
@@ -6854,8 +7013,8 @@ async function boot(options) {
|
|
|
6854
7013
|
try {
|
|
6855
7014
|
const flagPath = path19.join(EXE_AI_DIR, "session-cache", "needs-backfill");
|
|
6856
7015
|
if (existsSync15(flagPath)) {
|
|
6857
|
-
const { unlinkSync:
|
|
6858
|
-
|
|
7016
|
+
const { unlinkSync: unlinkSync8 } = await import("fs");
|
|
7017
|
+
unlinkSync8(flagPath);
|
|
6859
7018
|
}
|
|
6860
7019
|
} catch {
|
|
6861
7020
|
}
|
|
@@ -6956,7 +7115,7 @@ ${brief}`;
|
|
|
6956
7115
|
console.log(brief);
|
|
6957
7116
|
try {
|
|
6958
7117
|
const configPath2 = path19.join(
|
|
6959
|
-
process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path19.join(
|
|
7118
|
+
process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path19.join(os7.homedir(), ".exe-os"),
|
|
6960
7119
|
"config.json"
|
|
6961
7120
|
);
|
|
6962
7121
|
if (existsSync15(configPath2)) {
|