@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-link.js
CHANGED
|
@@ -31,15 +31,15 @@ __export(config_exports, {
|
|
|
31
31
|
migrateConfig: () => migrateConfig,
|
|
32
32
|
saveConfig: () => saveConfig
|
|
33
33
|
});
|
|
34
|
-
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
34
|
+
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
|
|
35
35
|
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
36
36
|
import path2 from "path";
|
|
37
|
-
import
|
|
37
|
+
import os2 from "os";
|
|
38
38
|
function resolveDataDir() {
|
|
39
39
|
if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
|
|
40
40
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
41
|
-
const newDir = path2.join(
|
|
42
|
-
const legacyDir = path2.join(
|
|
41
|
+
const newDir = path2.join(os2.homedir(), ".exe-os");
|
|
42
|
+
const legacyDir = path2.join(os2.homedir(), ".exe-mem");
|
|
43
43
|
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
44
44
|
try {
|
|
45
45
|
renameSync(legacyDir, newDir);
|
|
@@ -126,7 +126,7 @@ async function loadConfig() {
|
|
|
126
126
|
normalizeAutoUpdate(migratedCfg);
|
|
127
127
|
const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
|
|
128
128
|
if (config.dbPath.startsWith("~")) {
|
|
129
|
-
config.dbPath = config.dbPath.replace(/^~/,
|
|
129
|
+
config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
|
|
130
130
|
}
|
|
131
131
|
return config;
|
|
132
132
|
} catch {
|
|
@@ -157,6 +157,9 @@ async function saveConfig(config) {
|
|
|
157
157
|
await mkdir2(dir, { recursive: true });
|
|
158
158
|
const configPath = path2.join(dir, "config.json");
|
|
159
159
|
await writeFile2(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
160
|
+
if (config.cloud?.apiKey) {
|
|
161
|
+
await chmod2(configPath, 384);
|
|
162
|
+
}
|
|
160
163
|
}
|
|
161
164
|
async function loadConfigFrom(configPath) {
|
|
162
165
|
const raw = await readFile2(configPath, "utf-8");
|
|
@@ -381,15 +384,20 @@ async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
|
381
384
|
await mkdir3(path3.dirname(employeesPath), { recursive: true });
|
|
382
385
|
await writeFile3(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
383
386
|
}
|
|
387
|
+
function findExeBin() {
|
|
388
|
+
try {
|
|
389
|
+
return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
390
|
+
} catch {
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
384
394
|
function registerBinSymlinks(name) {
|
|
385
395
|
const created = [];
|
|
386
396
|
const skipped = [];
|
|
387
397
|
const errors = [];
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
} catch {
|
|
392
|
-
errors.push("Could not find 'exe' in PATH");
|
|
398
|
+
const exeBinPath = findExeBin();
|
|
399
|
+
if (!exeBinPath) {
|
|
400
|
+
errors.push("Could not find 'exe-os' in PATH");
|
|
393
401
|
return { created, skipped, errors };
|
|
394
402
|
}
|
|
395
403
|
const binDir = path3.dirname(exeBinPath);
|
|
@@ -430,6 +438,14 @@ import { readFileSync as readFileSync3, writeFileSync, existsSync as existsSync4
|
|
|
430
438
|
import { randomUUID } from "crypto";
|
|
431
439
|
import path4 from "path";
|
|
432
440
|
import { jwtVerify, importSPKI } from "jose";
|
|
441
|
+
async function fetchRetry(url, init) {
|
|
442
|
+
try {
|
|
443
|
+
return await fetch(url, init);
|
|
444
|
+
} catch {
|
|
445
|
+
await new Promise((r) => setTimeout(r, RETRY_DELAY_MS));
|
|
446
|
+
return fetch(url, { ...init, signal: AbortSignal.timeout(1e4) });
|
|
447
|
+
}
|
|
448
|
+
}
|
|
433
449
|
function loadDeviceId() {
|
|
434
450
|
const deviceJsonPath = path4.join(EXE_AI_DIR, "device.json");
|
|
435
451
|
try {
|
|
@@ -500,7 +516,7 @@ function cacheResponse(token) {
|
|
|
500
516
|
async function validateLicense(apiKey, deviceId) {
|
|
501
517
|
const did = deviceId ?? loadDeviceId();
|
|
502
518
|
try {
|
|
503
|
-
const res = await
|
|
519
|
+
const res = await fetchRetry(`${API_BASE}/auth/activate`, {
|
|
504
520
|
method: "POST",
|
|
505
521
|
headers: { "Content-Type": "application/json" },
|
|
506
522
|
body: JSON.stringify({ apiKey, deviceId: did }),
|
|
@@ -535,14 +551,23 @@ async function validateLicense(apiKey, deviceId) {
|
|
|
535
551
|
} catch {
|
|
536
552
|
const cached = await getCachedLicense();
|
|
537
553
|
if (cached) return cached;
|
|
538
|
-
return FREE_LICENSE;
|
|
554
|
+
return { ...FREE_LICENSE, valid: false, error: "offline" };
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
function getCacheAgeMs() {
|
|
558
|
+
try {
|
|
559
|
+
const { statSync } = __require("fs");
|
|
560
|
+
const s = statSync(CACHE_PATH);
|
|
561
|
+
return Date.now() - s.mtimeMs;
|
|
562
|
+
} catch {
|
|
563
|
+
return Infinity;
|
|
539
564
|
}
|
|
540
565
|
}
|
|
541
566
|
async function checkLicense() {
|
|
542
567
|
const key = loadLicense();
|
|
543
568
|
if (!key) return FREE_LICENSE;
|
|
544
569
|
const cached = await getCachedLicense();
|
|
545
|
-
if (cached) return cached;
|
|
570
|
+
if (cached && getCacheAgeMs() < CACHE_MAX_AGE_MS) return cached;
|
|
546
571
|
const deviceId = loadDeviceId();
|
|
547
572
|
return validateLicense(key, deviceId);
|
|
548
573
|
}
|
|
@@ -556,7 +581,7 @@ function isFeatureAllowed(license, feature) {
|
|
|
556
581
|
return license.plan === "team" || license.plan === "agency" || license.plan === "enterprise";
|
|
557
582
|
}
|
|
558
583
|
}
|
|
559
|
-
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE;
|
|
584
|
+
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;
|
|
560
585
|
var init_license = __esm({
|
|
561
586
|
"src/lib/license.ts"() {
|
|
562
587
|
"use strict";
|
|
@@ -565,6 +590,7 @@ var init_license = __esm({
|
|
|
565
590
|
CACHE_PATH = path4.join(EXE_AI_DIR, "license-cache.json");
|
|
566
591
|
DEVICE_ID_PATH = path4.join(EXE_AI_DIR, "device-id");
|
|
567
592
|
API_BASE = "https://askexe.com/cloud";
|
|
593
|
+
RETRY_DELAY_MS = 500;
|
|
568
594
|
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
569
595
|
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
570
596
|
4uj+UqeKCcvtgNHKmOK278HJaJcANe9xAeji8AFYu27q3WtzCi04pHudow==
|
|
@@ -586,6 +612,7 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
|
586
612
|
employeeLimit: 1,
|
|
587
613
|
memoryLimit: 5e3
|
|
588
614
|
};
|
|
615
|
+
CACHE_MAX_AGE_MS = 36e5;
|
|
589
616
|
}
|
|
590
617
|
});
|
|
591
618
|
|
|
@@ -621,6 +648,7 @@ var init_plan_limits = __esm({
|
|
|
621
648
|
// src/lib/cloud-sync.ts
|
|
622
649
|
var cloud_sync_exports = {};
|
|
623
650
|
__export(cloud_sync_exports, {
|
|
651
|
+
assertSecureEndpoint: () => assertSecureEndpoint,
|
|
624
652
|
buildRosterBlob: () => buildRosterBlob,
|
|
625
653
|
cloudPull: () => cloudPull,
|
|
626
654
|
cloudPullBehaviors: () => cloudPullBehaviors,
|
|
@@ -640,9 +668,10 @@ __export(cloud_sync_exports, {
|
|
|
640
668
|
cloudPushTasks: () => cloudPushTasks,
|
|
641
669
|
cloudSync: () => cloudSync,
|
|
642
670
|
mergeConfig: () => mergeConfig,
|
|
643
|
-
mergeRosterFromRemote: () => mergeRosterFromRemote
|
|
671
|
+
mergeRosterFromRemote: () => mergeRosterFromRemote,
|
|
672
|
+
recordRosterDeletion: () => recordRosterDeletion
|
|
644
673
|
});
|
|
645
|
-
import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync6, readdirSync, mkdirSync as mkdirSync2, appendFileSync } from "fs";
|
|
674
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync6, readdirSync, mkdirSync as mkdirSync2, appendFileSync, unlinkSync } from "fs";
|
|
646
675
|
import path6 from "path";
|
|
647
676
|
import { homedir } from "os";
|
|
648
677
|
function logError(msg) {
|
|
@@ -653,16 +682,47 @@ function logError(msg) {
|
|
|
653
682
|
} catch {
|
|
654
683
|
}
|
|
655
684
|
}
|
|
685
|
+
async function withRosterLock(fn) {
|
|
686
|
+
if (existsSync6(ROSTER_LOCK_PATH)) {
|
|
687
|
+
try {
|
|
688
|
+
const ts = parseInt(readFileSync5(ROSTER_LOCK_PATH, "utf-8"), 10);
|
|
689
|
+
if (Date.now() - ts < LOCK_STALE_MS) {
|
|
690
|
+
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
691
|
+
}
|
|
692
|
+
} catch (err) {
|
|
693
|
+
if (err instanceof Error && err.message.includes("already in progress")) throw err;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
writeFileSync2(ROSTER_LOCK_PATH, String(Date.now()));
|
|
697
|
+
try {
|
|
698
|
+
return await fn();
|
|
699
|
+
} finally {
|
|
700
|
+
try {
|
|
701
|
+
unlinkSync(ROSTER_LOCK_PATH);
|
|
702
|
+
} catch {
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
}
|
|
656
706
|
async function fetchWithRetry(url, init) {
|
|
657
|
-
const
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
707
|
+
const MAX_RETRIES = 3;
|
|
708
|
+
const BASE_DELAY_MS = 200;
|
|
709
|
+
let lastError;
|
|
710
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
711
|
+
try {
|
|
712
|
+
const signal = AbortSignal.timeout(FETCH_TIMEOUT_MS);
|
|
713
|
+
const resp = await fetch(url, { ...init, signal });
|
|
714
|
+
if (resp.status >= 500 && attempt < MAX_RETRIES) {
|
|
715
|
+
await new Promise((r) => setTimeout(r, BASE_DELAY_MS * Math.pow(2, attempt)));
|
|
716
|
+
continue;
|
|
717
|
+
}
|
|
718
|
+
return resp;
|
|
719
|
+
} catch (err) {
|
|
720
|
+
lastError = err;
|
|
721
|
+
if (attempt === MAX_RETRIES) throw err;
|
|
722
|
+
await new Promise((r) => setTimeout(r, BASE_DELAY_MS * Math.pow(2, attempt)));
|
|
723
|
+
}
|
|
664
724
|
}
|
|
665
|
-
|
|
725
|
+
throw lastError;
|
|
666
726
|
}
|
|
667
727
|
function assertSecureEndpoint(endpoint) {
|
|
668
728
|
if (endpoint.startsWith("https://")) return;
|
|
@@ -690,10 +750,15 @@ async function cloudPush(records, maxVersion, config) {
|
|
|
690
750
|
headers: {
|
|
691
751
|
Authorization: `Bearer ${config.apiKey}`,
|
|
692
752
|
"Content-Type": "application/json",
|
|
693
|
-
"X-Device-Id": loadDeviceId()
|
|
753
|
+
"X-Device-Id": loadDeviceId(),
|
|
754
|
+
"X-Expected-Version": String(maxVersion)
|
|
694
755
|
},
|
|
695
756
|
body: JSON.stringify({ version: maxVersion, blob })
|
|
696
757
|
});
|
|
758
|
+
if (resp.status === 409) {
|
|
759
|
+
logError("[cloud-sync] PUSH VERSION CONFLICT \u2014 re-pull required before next push");
|
|
760
|
+
return false;
|
|
761
|
+
}
|
|
697
762
|
return resp.ok;
|
|
698
763
|
} catch (err) {
|
|
699
764
|
logError(`[cloud-sync] PUSH FAILED: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -780,18 +845,21 @@ async function cloudSync(config) {
|
|
|
780
845
|
"SELECT value FROM sync_meta WHERE key = 'last_cloud_push_version'"
|
|
781
846
|
);
|
|
782
847
|
const lastPushVersion = pushMeta.rows.length > 0 ? Number(pushMeta.rows[0].value) : 0;
|
|
783
|
-
const recordsResult = await client.execute({
|
|
784
|
-
sql: `SELECT id, agent_id, agent_role, session_id, timestamp,
|
|
785
|
-
tool_name, project_name, has_error, raw_text, version,
|
|
786
|
-
author_device_id, scope
|
|
787
|
-
FROM memories
|
|
788
|
-
WHERE version > ?
|
|
789
|
-
AND (scope IS NULL OR scope != 'personal')
|
|
790
|
-
ORDER BY version ASC`,
|
|
791
|
-
args: [lastPushVersion]
|
|
792
|
-
});
|
|
793
848
|
let pushed = 0;
|
|
794
|
-
|
|
849
|
+
let batchCursor = lastPushVersion;
|
|
850
|
+
while (true) {
|
|
851
|
+
const recordsResult = await client.execute({
|
|
852
|
+
sql: `SELECT id, agent_id, agent_role, session_id, timestamp,
|
|
853
|
+
tool_name, project_name, has_error, raw_text, version,
|
|
854
|
+
author_device_id, scope
|
|
855
|
+
FROM memories
|
|
856
|
+
WHERE version > ?
|
|
857
|
+
AND (scope IS NULL OR scope != 'personal')
|
|
858
|
+
ORDER BY version ASC
|
|
859
|
+
LIMIT ?`,
|
|
860
|
+
args: [batchCursor, PUSH_BATCH_SIZE]
|
|
861
|
+
});
|
|
862
|
+
if (recordsResult.rows.length === 0) break;
|
|
795
863
|
const records = recordsResult.rows.map((row) => ({
|
|
796
864
|
id: row.id,
|
|
797
865
|
agent_id: row.agent_id,
|
|
@@ -808,13 +876,14 @@ async function cloudSync(config) {
|
|
|
808
876
|
}));
|
|
809
877
|
const maxVersion = Number(records[records.length - 1].version);
|
|
810
878
|
const pushOk = await cloudPush(records, maxVersion, config);
|
|
811
|
-
if (pushOk)
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
879
|
+
if (!pushOk) break;
|
|
880
|
+
await client.execute({
|
|
881
|
+
sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('last_cloud_push_version', ?)",
|
|
882
|
+
args: [String(maxVersion)]
|
|
883
|
+
});
|
|
884
|
+
pushed += records.length;
|
|
885
|
+
batchCursor = maxVersion;
|
|
886
|
+
if (recordsResult.rows.length < PUSH_BATCH_SIZE) break;
|
|
818
887
|
}
|
|
819
888
|
try {
|
|
820
889
|
await cloudPushRoster(config);
|
|
@@ -896,6 +965,27 @@ async function cloudSync(config) {
|
|
|
896
965
|
documents: documentsResult
|
|
897
966
|
};
|
|
898
967
|
}
|
|
968
|
+
function recordRosterDeletion(name) {
|
|
969
|
+
let deletions = [];
|
|
970
|
+
try {
|
|
971
|
+
if (existsSync6(ROSTER_DELETIONS_PATH)) {
|
|
972
|
+
deletions = JSON.parse(readFileSync5(ROSTER_DELETIONS_PATH, "utf-8"));
|
|
973
|
+
}
|
|
974
|
+
} catch {
|
|
975
|
+
}
|
|
976
|
+
if (!deletions.includes(name)) deletions.push(name);
|
|
977
|
+
writeFileSync2(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
|
|
978
|
+
}
|
|
979
|
+
function consumeRosterDeletions() {
|
|
980
|
+
try {
|
|
981
|
+
if (!existsSync6(ROSTER_DELETIONS_PATH)) return [];
|
|
982
|
+
const deletions = JSON.parse(readFileSync5(ROSTER_DELETIONS_PATH, "utf-8"));
|
|
983
|
+
writeFileSync2(ROSTER_DELETIONS_PATH, "[]");
|
|
984
|
+
return deletions;
|
|
985
|
+
} catch {
|
|
986
|
+
return [];
|
|
987
|
+
}
|
|
988
|
+
}
|
|
899
989
|
function buildRosterBlob(paths) {
|
|
900
990
|
const rosterPath = paths?.rosterPath ?? path6.join(EXE_AI_DIR, "exe-employees.json");
|
|
901
991
|
const identityDir = paths?.identityDir ?? path6.join(EXE_AI_DIR, "identity");
|
|
@@ -923,9 +1013,10 @@ function buildRosterBlob(paths) {
|
|
|
923
1013
|
} catch {
|
|
924
1014
|
}
|
|
925
1015
|
}
|
|
926
|
-
const
|
|
1016
|
+
const deletedNames = consumeRosterDeletions();
|
|
1017
|
+
const content = JSON.stringify({ roster, identities, config, deletedNames });
|
|
927
1018
|
const hash = Buffer.from(content).length;
|
|
928
|
-
return { roster, identities, config, version: hash };
|
|
1019
|
+
return { roster, identities, config, deletedNames, version: hash };
|
|
929
1020
|
}
|
|
930
1021
|
async function cloudPushRoster(config) {
|
|
931
1022
|
assertSecureEndpoint(config.endpoint);
|
|
@@ -1008,38 +1099,50 @@ function mergeConfig(remoteConfig, configPath) {
|
|
|
1008
1099
|
writeFileSync2(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
1009
1100
|
}
|
|
1010
1101
|
async function mergeRosterFromRemote(remote, paths) {
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
if (
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1102
|
+
return withRosterLock(async () => {
|
|
1103
|
+
const rosterPath = paths?.rosterPath ?? void 0;
|
|
1104
|
+
const identityDir = paths?.identityDir ?? path6.join(EXE_AI_DIR, "identity");
|
|
1105
|
+
const localEmployees = await loadEmployees(rosterPath);
|
|
1106
|
+
const localNames = new Set(localEmployees.map((e) => e.name));
|
|
1107
|
+
let added = 0;
|
|
1108
|
+
for (const remoteEmp of remote.roster) {
|
|
1109
|
+
if (localNames.has(remoteEmp.name)) continue;
|
|
1110
|
+
localEmployees.push(remoteEmp);
|
|
1111
|
+
localNames.add(remoteEmp.name);
|
|
1112
|
+
added++;
|
|
1113
|
+
if (remote.identities[`${remoteEmp.name}.md`]) {
|
|
1114
|
+
if (!existsSync6(identityDir)) mkdirSync2(identityDir, { recursive: true });
|
|
1115
|
+
const idPath = path6.join(identityDir, `${remoteEmp.name}.md`);
|
|
1116
|
+
if (!existsSync6(idPath)) {
|
|
1117
|
+
writeFileSync2(idPath, remote.identities[`${remoteEmp.name}.md`], "utf-8");
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
try {
|
|
1121
|
+
registerBinSymlinks(remoteEmp.name);
|
|
1122
|
+
} catch {
|
|
1026
1123
|
}
|
|
1027
1124
|
}
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1125
|
+
let removed = 0;
|
|
1126
|
+
if (remote.deletedNames && remote.deletedNames.length > 0) {
|
|
1127
|
+
const toRemove = new Set(remote.deletedNames);
|
|
1128
|
+
const filtered = localEmployees.filter((e) => !toRemove.has(e.name));
|
|
1129
|
+
removed = localEmployees.length - filtered.length;
|
|
1130
|
+
if (removed > 0) {
|
|
1131
|
+
localEmployees.length = 0;
|
|
1132
|
+
localEmployees.push(...filtered);
|
|
1133
|
+
}
|
|
1031
1134
|
}
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
await saveEmployees(localEmployees, rosterPath);
|
|
1035
|
-
}
|
|
1036
|
-
if (remote.config && Object.keys(remote.config).length > 0) {
|
|
1037
|
-
try {
|
|
1038
|
-
mergeConfig(remote.config, paths?.configPath);
|
|
1039
|
-
} catch {
|
|
1135
|
+
if (added > 0 || removed > 0) {
|
|
1136
|
+
await saveEmployees(localEmployees, rosterPath);
|
|
1040
1137
|
}
|
|
1041
|
-
|
|
1042
|
-
|
|
1138
|
+
if (remote.config && Object.keys(remote.config).length > 0) {
|
|
1139
|
+
try {
|
|
1140
|
+
mergeConfig(remote.config, paths?.configPath);
|
|
1141
|
+
} catch {
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
return { added };
|
|
1145
|
+
});
|
|
1043
1146
|
}
|
|
1044
1147
|
async function cloudPushBlob(route, data, metaKey, config) {
|
|
1045
1148
|
if (data.length === 0) return { ok: true };
|
|
@@ -1107,7 +1210,7 @@ async function cloudPullBlob(route, config) {
|
|
|
1107
1210
|
}
|
|
1108
1211
|
async function cloudPushBehaviors(config) {
|
|
1109
1212
|
const client = getClient();
|
|
1110
|
-
const result = await client.execute("SELECT * FROM behaviors");
|
|
1213
|
+
const result = await client.execute("SELECT * FROM behaviors LIMIT 10000");
|
|
1111
1214
|
const rows = result.rows;
|
|
1112
1215
|
const { ok } = await cloudPushBlob(
|
|
1113
1216
|
"/sync/push-behaviors",
|
|
@@ -1155,13 +1258,13 @@ async function cloudPullBehaviors(config) {
|
|
|
1155
1258
|
async function cloudPushGraphRAG(config) {
|
|
1156
1259
|
const client = getClient();
|
|
1157
1260
|
const [entities, relationships, aliases, entityMems, relMems, hyperedges, hyperedgeNodes] = await Promise.all([
|
|
1158
|
-
client.execute("SELECT * FROM entities"),
|
|
1159
|
-
client.execute("SELECT * FROM relationships"),
|
|
1160
|
-
client.execute("SELECT * FROM entity_aliases"),
|
|
1161
|
-
client.execute("SELECT * FROM entity_memories"),
|
|
1162
|
-
client.execute("SELECT * FROM relationship_memories"),
|
|
1163
|
-
client.execute("SELECT * FROM hyperedges"),
|
|
1164
|
-
client.execute("SELECT * FROM hyperedge_nodes")
|
|
1261
|
+
client.execute("SELECT * FROM entities LIMIT 50000"),
|
|
1262
|
+
client.execute("SELECT * FROM relationships LIMIT 50000"),
|
|
1263
|
+
client.execute("SELECT * FROM entity_aliases LIMIT 50000"),
|
|
1264
|
+
client.execute("SELECT * FROM entity_memories LIMIT 50000"),
|
|
1265
|
+
client.execute("SELECT * FROM relationship_memories LIMIT 50000"),
|
|
1266
|
+
client.execute("SELECT * FROM hyperedges LIMIT 50000"),
|
|
1267
|
+
client.execute("SELECT * FROM hyperedge_nodes LIMIT 50000")
|
|
1165
1268
|
]);
|
|
1166
1269
|
const blob = {
|
|
1167
1270
|
entities: entities.rows,
|
|
@@ -1263,7 +1366,7 @@ async function cloudPullGraphRAG(config) {
|
|
|
1263
1366
|
}
|
|
1264
1367
|
async function cloudPushTasks(config) {
|
|
1265
1368
|
const client = getClient();
|
|
1266
|
-
const result = await client.execute("SELECT * FROM tasks");
|
|
1369
|
+
const result = await client.execute("SELECT * FROM tasks LIMIT 10000");
|
|
1267
1370
|
const rows = result.rows;
|
|
1268
1371
|
const { ok } = await cloudPushBlob(
|
|
1269
1372
|
"/sync/push-tasks",
|
|
@@ -1309,7 +1412,7 @@ async function cloudPullTasks(config) {
|
|
|
1309
1412
|
}
|
|
1310
1413
|
async function cloudPushConversations(config) {
|
|
1311
1414
|
const client = getClient();
|
|
1312
|
-
const result = await client.execute("SELECT * FROM conversations");
|
|
1415
|
+
const result = await client.execute("SELECT * FROM conversations LIMIT 50000");
|
|
1313
1416
|
const rows = result.rows;
|
|
1314
1417
|
const { ok } = await cloudPushBlob(
|
|
1315
1418
|
"/sync/push-conversations",
|
|
@@ -1359,8 +1462,8 @@ async function cloudPullConversations(config) {
|
|
|
1359
1462
|
async function cloudPushDocuments(config) {
|
|
1360
1463
|
const client = getClient();
|
|
1361
1464
|
const [workspaces, documents] = await Promise.all([
|
|
1362
|
-
client.execute("SELECT * FROM workspaces"),
|
|
1363
|
-
client.execute("SELECT * FROM documents")
|
|
1465
|
+
client.execute("SELECT * FROM workspaces LIMIT 1000"),
|
|
1466
|
+
client.execute("SELECT * FROM documents LIMIT 10000")
|
|
1364
1467
|
]);
|
|
1365
1468
|
const blob = {
|
|
1366
1469
|
workspaces: workspaces.rows,
|
|
@@ -1413,7 +1516,7 @@ async function cloudPullDocuments(config) {
|
|
|
1413
1516
|
}
|
|
1414
1517
|
return { pulled };
|
|
1415
1518
|
}
|
|
1416
|
-
var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS;
|
|
1519
|
+
var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, ROSTER_DELETIONS_PATH;
|
|
1417
1520
|
var init_cloud_sync = __esm({
|
|
1418
1521
|
"src/lib/cloud-sync.ts"() {
|
|
1419
1522
|
"use strict";
|
|
@@ -1426,6 +1529,10 @@ var init_cloud_sync = __esm({
|
|
|
1426
1529
|
init_employees();
|
|
1427
1530
|
LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
|
|
1428
1531
|
FETCH_TIMEOUT_MS = 3e4;
|
|
1532
|
+
PUSH_BATCH_SIZE = 5e3;
|
|
1533
|
+
ROSTER_LOCK_PATH = path6.join(EXE_AI_DIR, "roster-merge.lock");
|
|
1534
|
+
LOCK_STALE_MS = 3e4;
|
|
1535
|
+
ROSTER_DELETIONS_PATH = path6.join(EXE_AI_DIR, "roster-deletions.json");
|
|
1429
1536
|
}
|
|
1430
1537
|
});
|
|
1431
1538
|
|
|
@@ -1436,11 +1543,12 @@ import { createInterface } from "readline";
|
|
|
1436
1543
|
import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
|
|
1437
1544
|
import { existsSync } from "fs";
|
|
1438
1545
|
import path from "path";
|
|
1546
|
+
import os from "os";
|
|
1439
1547
|
import crypto from "crypto";
|
|
1440
1548
|
var SERVICE = "exe-mem";
|
|
1441
1549
|
var ACCOUNT = "master-key";
|
|
1442
1550
|
function getKeyDir() {
|
|
1443
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(
|
|
1551
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
|
|
1444
1552
|
}
|
|
1445
1553
|
function getKeyPath() {
|
|
1446
1554
|
return path.join(getKeyDir(), "master.key");
|
|
@@ -1580,6 +1688,7 @@ async function main() {
|
|
|
1580
1688
|
`);
|
|
1581
1689
|
console.log("Write this down and enter it on your new device with /exe-link import.");
|
|
1582
1690
|
console.log("Anyone with this phrase can decrypt your memories.");
|
|
1691
|
+
console.log("\u26A0 Clear your terminal history after copying.");
|
|
1583
1692
|
} else if (mode === "import") {
|
|
1584
1693
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
1585
1694
|
const mnemonic = await new Promise((resolve) => {
|