@askexenow/exe-os 0.9.65 → 0.9.67
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/deploy/stack-manifests/v0.9.json +54 -5
- package/dist/bin/age-ontology-load.js +61 -0
- package/dist/bin/agentic-ontology-backfill.js +4708 -0
- package/dist/bin/agentic-reflection-backfill.js +4144 -0
- package/dist/bin/{exe-link.js → agentic-semantic-label.js} +1532 -2173
- package/dist/bin/backfill-conversations.js +528 -20
- package/dist/bin/backfill-responses.js +528 -20
- package/dist/bin/backfill-vectors.js +255 -20
- package/dist/bin/bulk-sync-postgres.js +4876 -0
- package/dist/bin/cleanup-stale-review-tasks.js +529 -21
- package/dist/bin/cli.js +3471 -1491
- package/dist/bin/exe-agent-config.js +4 -0
- package/dist/bin/exe-agent.js +16 -0
- package/dist/bin/exe-assign.js +528 -20
- package/dist/bin/exe-boot.js +492 -54
- package/dist/bin/exe-call.js +16 -0
- package/dist/bin/exe-cloud.js +7415 -518
- package/dist/bin/exe-dispatch.js +540 -22
- package/dist/bin/exe-doctor.js +3404 -1225
- package/dist/bin/exe-export-behaviors.js +542 -24
- package/dist/bin/exe-forget.js +529 -21
- package/dist/bin/exe-gateway.js +595 -25
- package/dist/bin/exe-heartbeat.js +541 -24
- package/dist/bin/exe-kill.js +529 -21
- package/dist/bin/exe-launch-agent.js +2334 -1067
- package/dist/bin/exe-new-employee.js +324 -166
- package/dist/bin/exe-pending-messages.js +529 -21
- package/dist/bin/exe-pending-notifications.js +529 -21
- package/dist/bin/exe-pending-reviews.js +529 -21
- package/dist/bin/exe-rename.js +529 -21
- package/dist/bin/exe-review.js +529 -21
- package/dist/bin/exe-search.js +542 -24
- package/dist/bin/exe-session-cleanup.js +540 -22
- package/dist/bin/exe-settings.js +14 -0
- package/dist/bin/exe-start-codex.js +817 -144
- package/dist/bin/exe-start-opencode.js +776 -80
- package/dist/bin/exe-status.js +529 -21
- package/dist/bin/exe-team.js +529 -21
- package/dist/bin/git-sweep.js +540 -22
- package/dist/bin/graph-backfill.js +580 -21
- package/dist/bin/graph-export.js +529 -21
- package/dist/bin/graph-layer-benchmark.js +109 -0
- package/dist/bin/install.js +420 -289
- package/dist/bin/intercom-check.js +540 -22
- package/dist/bin/postgres-agentic-reflection-backfill.js +187 -0
- package/dist/bin/postgres-agentic-semantic-backfill.js +237 -0
- package/dist/bin/scan-tasks.js +540 -22
- package/dist/bin/setup.js +790 -206
- package/dist/bin/shard-migrate.js +528 -20
- package/dist/bin/update.js +4 -0
- package/dist/gateway/index.js +593 -23
- package/dist/hooks/bug-report-worker.js +651 -64
- package/dist/hooks/codex-stop-task-finalizer.js +540 -22
- package/dist/hooks/commit-complete.js +540 -22
- package/dist/hooks/error-recall.js +542 -24
- package/dist/hooks/exe-heartbeat-hook.js +4 -0
- package/dist/hooks/ingest-worker.js +4 -0
- package/dist/hooks/ingest.js +539 -22
- package/dist/hooks/instructions-loaded.js +529 -21
- package/dist/hooks/notification.js +529 -21
- package/dist/hooks/post-compact.js +529 -21
- package/dist/hooks/post-tool-combined.js +543 -25
- package/dist/hooks/pre-compact.js +772 -127
- package/dist/hooks/pre-tool-use.js +529 -21
- package/dist/hooks/prompt-submit.js +543 -25
- package/dist/hooks/session-end.js +673 -140
- package/dist/hooks/session-start.js +662 -26
- package/dist/hooks/stop.js +540 -23
- package/dist/hooks/subagent-stop.js +529 -21
- package/dist/hooks/summary-worker.js +571 -126
- package/dist/index.js +593 -23
- package/dist/lib/agent-config.js +4 -0
- package/dist/lib/cloud-sync.js +408 -47
- package/dist/lib/config.js +25 -1
- package/dist/lib/consolidation.js +5 -1
- package/dist/lib/database.js +128 -0
- package/dist/lib/db-daemon-client.js +4 -0
- package/dist/lib/db.js +128 -0
- package/dist/lib/device-registry.js +128 -0
- package/dist/lib/embedder.js +25 -1
- package/dist/lib/employee-templates.js +16 -0
- package/dist/lib/employees.js +4 -0
- package/dist/lib/exe-daemon-client.js +4 -0
- package/dist/lib/exe-daemon.js +3158 -930
- package/dist/lib/hybrid-search.js +542 -24
- package/dist/lib/identity.js +7 -0
- package/dist/lib/keychain.js +178 -22
- package/dist/lib/license.js +4 -0
- package/dist/lib/messaging.js +7 -0
- package/dist/lib/reminders.js +7 -0
- package/dist/lib/schedules.js +255 -20
- package/dist/lib/skill-learning.js +28 -1
- package/dist/lib/status-brief.js +39 -0
- package/dist/lib/store.js +528 -20
- package/dist/lib/task-router.js +4 -0
- package/dist/lib/tasks.js +28 -1
- package/dist/lib/tmux-routing.js +28 -1
- package/dist/lib/token-spend.js +7 -0
- package/dist/mcp/server.js +2739 -813
- package/dist/mcp/tools/complete-reminder.js +7 -0
- package/dist/mcp/tools/create-reminder.js +7 -0
- package/dist/mcp/tools/create-task.js +28 -1
- package/dist/mcp/tools/deactivate-behavior.js +7 -0
- package/dist/mcp/tools/list-reminders.js +7 -0
- package/dist/mcp/tools/list-tasks.js +7 -0
- package/dist/mcp/tools/send-message.js +7 -0
- package/dist/mcp/tools/update-task.js +28 -1
- package/dist/runtime/index.js +540 -22
- package/dist/tui/App.js +618 -29
- package/package.json +9 -5
- package/src/commands/exe/cloud.md +11 -8
- package/stack.release.json +3 -3
- package/src/commands/exe/link.md +0 -17
package/dist/lib/cloud-sync.js
CHANGED
|
@@ -181,6 +181,10 @@ var init_config = __esm({
|
|
|
181
181
|
checkOnBoot: true,
|
|
182
182
|
autoInstall: false,
|
|
183
183
|
checkIntervalMs: 24 * 60 * 60 * 1e3
|
|
184
|
+
},
|
|
185
|
+
orchestration: {
|
|
186
|
+
phase: "phase_1_coo",
|
|
187
|
+
phaseSetBy: "default"
|
|
184
188
|
}
|
|
185
189
|
};
|
|
186
190
|
}
|
|
@@ -1467,6 +1471,9 @@ function getClient() {
|
|
|
1467
1471
|
if (_daemonClient && _daemonClient._isDaemonActive()) {
|
|
1468
1472
|
return _daemonClient;
|
|
1469
1473
|
}
|
|
1474
|
+
if (!_resilientClient) {
|
|
1475
|
+
return _adapterClient;
|
|
1476
|
+
}
|
|
1470
1477
|
return _resilientClient;
|
|
1471
1478
|
}
|
|
1472
1479
|
async function initDaemonClient() {
|
|
@@ -2499,6 +2506,127 @@ async function ensureSchema() {
|
|
|
2499
2506
|
VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
|
|
2500
2507
|
END;
|
|
2501
2508
|
`);
|
|
2509
|
+
await client.executeMultiple(`
|
|
2510
|
+
CREATE TABLE IF NOT EXISTS agent_sessions (
|
|
2511
|
+
id TEXT PRIMARY KEY,
|
|
2512
|
+
agent_id TEXT NOT NULL,
|
|
2513
|
+
project_name TEXT,
|
|
2514
|
+
started_at TEXT NOT NULL,
|
|
2515
|
+
last_event_at TEXT NOT NULL,
|
|
2516
|
+
event_count INTEGER NOT NULL DEFAULT 0,
|
|
2517
|
+
properties TEXT DEFAULT '{}'
|
|
2518
|
+
);
|
|
2519
|
+
|
|
2520
|
+
CREATE INDEX IF NOT EXISTS idx_agent_sessions_agent_time
|
|
2521
|
+
ON agent_sessions(agent_id, started_at);
|
|
2522
|
+
|
|
2523
|
+
CREATE TABLE IF NOT EXISTS agent_goals (
|
|
2524
|
+
id TEXT PRIMARY KEY,
|
|
2525
|
+
statement TEXT NOT NULL,
|
|
2526
|
+
owner_agent_id TEXT,
|
|
2527
|
+
project_name TEXT,
|
|
2528
|
+
status TEXT NOT NULL DEFAULT 'open',
|
|
2529
|
+
priority INTEGER NOT NULL DEFAULT 5,
|
|
2530
|
+
success_criteria TEXT,
|
|
2531
|
+
parent_goal_id TEXT,
|
|
2532
|
+
due_at TEXT,
|
|
2533
|
+
achieved_at TEXT,
|
|
2534
|
+
supersedes_id TEXT,
|
|
2535
|
+
created_at TEXT NOT NULL,
|
|
2536
|
+
updated_at TEXT NOT NULL,
|
|
2537
|
+
source_memory_id TEXT
|
|
2538
|
+
);
|
|
2539
|
+
|
|
2540
|
+
CREATE INDEX IF NOT EXISTS idx_agent_goals_project_status
|
|
2541
|
+
ON agent_goals(project_name, status, priority);
|
|
2542
|
+
|
|
2543
|
+
CREATE TABLE IF NOT EXISTS agent_events (
|
|
2544
|
+
id TEXT PRIMARY KEY,
|
|
2545
|
+
event_type TEXT NOT NULL,
|
|
2546
|
+
occurred_at TEXT NOT NULL,
|
|
2547
|
+
sequence_index INTEGER NOT NULL,
|
|
2548
|
+
actor_agent_id TEXT,
|
|
2549
|
+
agent_role TEXT,
|
|
2550
|
+
project_name TEXT,
|
|
2551
|
+
session_id TEXT,
|
|
2552
|
+
task_id TEXT,
|
|
2553
|
+
goal_id TEXT,
|
|
2554
|
+
parent_event_id TEXT,
|
|
2555
|
+
intention TEXT,
|
|
2556
|
+
outcome TEXT,
|
|
2557
|
+
evidence_memory_id TEXT,
|
|
2558
|
+
impact TEXT,
|
|
2559
|
+
payload TEXT DEFAULT '{}',
|
|
2560
|
+
created_at TEXT NOT NULL
|
|
2561
|
+
);
|
|
2562
|
+
|
|
2563
|
+
CREATE INDEX IF NOT EXISTS idx_agent_events_time
|
|
2564
|
+
ON agent_events(occurred_at, sequence_index);
|
|
2565
|
+
|
|
2566
|
+
CREATE INDEX IF NOT EXISTS idx_agent_events_session_seq
|
|
2567
|
+
ON agent_events(session_id, sequence_index);
|
|
2568
|
+
|
|
2569
|
+
CREATE INDEX IF NOT EXISTS idx_agent_events_goal_time
|
|
2570
|
+
ON agent_events(goal_id, occurred_at);
|
|
2571
|
+
|
|
2572
|
+
CREATE INDEX IF NOT EXISTS idx_agent_events_memory
|
|
2573
|
+
ON agent_events(evidence_memory_id);
|
|
2574
|
+
|
|
2575
|
+
CREATE TABLE IF NOT EXISTS agent_goal_links (
|
|
2576
|
+
id TEXT PRIMARY KEY,
|
|
2577
|
+
goal_id TEXT NOT NULL,
|
|
2578
|
+
link_type TEXT NOT NULL,
|
|
2579
|
+
target_id TEXT NOT NULL,
|
|
2580
|
+
target_type TEXT NOT NULL,
|
|
2581
|
+
created_at TEXT NOT NULL
|
|
2582
|
+
);
|
|
2583
|
+
|
|
2584
|
+
CREATE INDEX IF NOT EXISTS idx_agent_goal_links_goal
|
|
2585
|
+
ON agent_goal_links(goal_id, target_type);
|
|
2586
|
+
|
|
2587
|
+
CREATE TABLE IF NOT EXISTS agent_semantic_labels (
|
|
2588
|
+
id TEXT PRIMARY KEY,
|
|
2589
|
+
source_memory_id TEXT NOT NULL,
|
|
2590
|
+
event_id TEXT,
|
|
2591
|
+
labeler TEXT NOT NULL,
|
|
2592
|
+
schema_version INTEGER NOT NULL DEFAULT 1,
|
|
2593
|
+
confidence REAL NOT NULL DEFAULT 0,
|
|
2594
|
+
labels TEXT NOT NULL,
|
|
2595
|
+
created_at TEXT NOT NULL,
|
|
2596
|
+
updated_at TEXT NOT NULL
|
|
2597
|
+
);
|
|
2598
|
+
|
|
2599
|
+
CREATE INDEX IF NOT EXISTS idx_agent_semantic_labels_memory
|
|
2600
|
+
ON agent_semantic_labels(source_memory_id, labeler);
|
|
2601
|
+
|
|
2602
|
+
CREATE INDEX IF NOT EXISTS idx_agent_semantic_labels_event
|
|
2603
|
+
ON agent_semantic_labels(event_id);
|
|
2604
|
+
|
|
2605
|
+
CREATE TABLE IF NOT EXISTS agent_reflection_checkpoints (
|
|
2606
|
+
id TEXT PRIMARY KEY,
|
|
2607
|
+
project_name TEXT,
|
|
2608
|
+
session_id TEXT,
|
|
2609
|
+
window_start_at TEXT NOT NULL,
|
|
2610
|
+
window_end_at TEXT NOT NULL,
|
|
2611
|
+
event_count INTEGER NOT NULL DEFAULT 0,
|
|
2612
|
+
goal_count INTEGER NOT NULL DEFAULT 0,
|
|
2613
|
+
success_count INTEGER NOT NULL DEFAULT 0,
|
|
2614
|
+
failure_count INTEGER NOT NULL DEFAULT 0,
|
|
2615
|
+
risk_count INTEGER NOT NULL DEFAULT 0,
|
|
2616
|
+
summary TEXT NOT NULL,
|
|
2617
|
+
learnings TEXT NOT NULL DEFAULT '[]',
|
|
2618
|
+
next_actions TEXT NOT NULL DEFAULT '[]',
|
|
2619
|
+
evidence_event_ids TEXT NOT NULL DEFAULT '[]',
|
|
2620
|
+
confidence REAL NOT NULL DEFAULT 0,
|
|
2621
|
+
created_at TEXT NOT NULL
|
|
2622
|
+
);
|
|
2623
|
+
|
|
2624
|
+
CREATE INDEX IF NOT EXISTS idx_agent_reflection_project_time
|
|
2625
|
+
ON agent_reflection_checkpoints(project_name, window_end_at);
|
|
2626
|
+
|
|
2627
|
+
CREATE INDEX IF NOT EXISTS idx_agent_reflection_session_time
|
|
2628
|
+
ON agent_reflection_checkpoints(session_id, window_end_at);
|
|
2629
|
+
`);
|
|
2502
2630
|
try {
|
|
2503
2631
|
await client.execute({
|
|
2504
2632
|
sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
|
|
@@ -2877,12 +3005,13 @@ var keychain_exports = {};
|
|
|
2877
3005
|
__export(keychain_exports, {
|
|
2878
3006
|
deleteMasterKey: () => deleteMasterKey,
|
|
2879
3007
|
exportMnemonic: () => exportMnemonic,
|
|
3008
|
+
getKeyStorageInfo: () => getKeyStorageInfo,
|
|
2880
3009
|
getMasterKey: () => getMasterKey,
|
|
2881
3010
|
importMnemonic: () => importMnemonic,
|
|
2882
3011
|
setMasterKey: () => setMasterKey
|
|
2883
3012
|
});
|
|
2884
3013
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
2885
|
-
import { existsSync as existsSync8 } from "fs";
|
|
3014
|
+
import { existsSync as existsSync8, statSync as statSync2 } from "fs";
|
|
2886
3015
|
import { execSync as execSync2 } from "child_process";
|
|
2887
3016
|
import path8 from "path";
|
|
2888
3017
|
import os6 from "os";
|
|
@@ -2892,29 +3021,65 @@ function getKeyDir() {
|
|
|
2892
3021
|
function getKeyPath() {
|
|
2893
3022
|
return path8.join(getKeyDir(), "master.key");
|
|
2894
3023
|
}
|
|
2895
|
-
function
|
|
3024
|
+
function nativeKeychainAllowed() {
|
|
3025
|
+
return process.env.EXE_OS_DISABLE_NATIVE_KEYCHAIN !== "1";
|
|
3026
|
+
}
|
|
3027
|
+
function linuxSecretAvailable() {
|
|
3028
|
+
if (!nativeKeychainAllowed()) return false;
|
|
3029
|
+
if (process.platform !== "linux") return false;
|
|
3030
|
+
if (linuxSecretAvailability !== null) return linuxSecretAvailability;
|
|
3031
|
+
try {
|
|
3032
|
+
execSync2("command -v secret-tool >/dev/null 2>&1", { timeout: 1e3 });
|
|
3033
|
+
} catch {
|
|
3034
|
+
linuxSecretAvailability = false;
|
|
3035
|
+
return false;
|
|
3036
|
+
}
|
|
3037
|
+
try {
|
|
3038
|
+
execSync2("secret-tool search --all exe-os probe >/dev/null 2>&1", { timeout: 1e3 });
|
|
3039
|
+
linuxSecretAvailability = true;
|
|
3040
|
+
} catch {
|
|
3041
|
+
linuxSecretAvailability = false;
|
|
3042
|
+
}
|
|
3043
|
+
return linuxSecretAvailability;
|
|
3044
|
+
}
|
|
3045
|
+
function isRootOnlyTrustedServerKeyFile(keyPath) {
|
|
3046
|
+
if (process.platform !== "linux") return false;
|
|
3047
|
+
try {
|
|
3048
|
+
const uid = typeof os6.userInfo().uid === "number" ? os6.userInfo().uid : -1;
|
|
3049
|
+
const st = statSync2(keyPath);
|
|
3050
|
+
if (!st.isFile() || (st.mode & 63) !== 0) return false;
|
|
3051
|
+
if (uid === 0) return true;
|
|
3052
|
+
const exeOsDir = process.env.EXE_OS_DIR;
|
|
3053
|
+
return Boolean(exeOsDir && path8.resolve(keyPath).startsWith(path8.resolve(exeOsDir) + path8.sep));
|
|
3054
|
+
} catch {
|
|
3055
|
+
return false;
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
3058
|
+
function macKeychainGet(service = SERVICE) {
|
|
3059
|
+
if (!nativeKeychainAllowed()) return null;
|
|
2896
3060
|
if (process.platform !== "darwin") return null;
|
|
2897
3061
|
try {
|
|
2898
3062
|
return execSync2(
|
|
2899
|
-
`security find-generic-password -s "${
|
|
3063
|
+
`security find-generic-password -s "${service}" -a "${ACCOUNT}" -w 2>/dev/null`,
|
|
2900
3064
|
{ encoding: "utf-8", timeout: 5e3 }
|
|
2901
3065
|
).trim();
|
|
2902
3066
|
} catch {
|
|
2903
3067
|
return null;
|
|
2904
3068
|
}
|
|
2905
3069
|
}
|
|
2906
|
-
function macKeychainSet(value) {
|
|
3070
|
+
function macKeychainSet(value, service = SERVICE) {
|
|
3071
|
+
if (!nativeKeychainAllowed()) return false;
|
|
2907
3072
|
if (process.platform !== "darwin") return false;
|
|
2908
3073
|
try {
|
|
2909
3074
|
try {
|
|
2910
3075
|
execSync2(
|
|
2911
|
-
`security delete-generic-password -s "${
|
|
3076
|
+
`security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
|
|
2912
3077
|
{ timeout: 5e3 }
|
|
2913
3078
|
);
|
|
2914
3079
|
} catch {
|
|
2915
3080
|
}
|
|
2916
3081
|
execSync2(
|
|
2917
|
-
`security add-generic-password -s "${
|
|
3082
|
+
`security add-generic-password -s "${service}" -a "${ACCOUNT}" -w "${value}"`,
|
|
2918
3083
|
{ timeout: 5e3 }
|
|
2919
3084
|
);
|
|
2920
3085
|
return true;
|
|
@@ -2922,11 +3087,12 @@ function macKeychainSet(value) {
|
|
|
2922
3087
|
return false;
|
|
2923
3088
|
}
|
|
2924
3089
|
}
|
|
2925
|
-
function macKeychainDelete() {
|
|
3090
|
+
function macKeychainDelete(service = SERVICE) {
|
|
3091
|
+
if (!nativeKeychainAllowed()) return false;
|
|
2926
3092
|
if (process.platform !== "darwin") return false;
|
|
2927
3093
|
try {
|
|
2928
3094
|
execSync2(
|
|
2929
|
-
`security delete-generic-password -s "${
|
|
3095
|
+
`security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
|
|
2930
3096
|
{ timeout: 5e3 }
|
|
2931
3097
|
);
|
|
2932
3098
|
return true;
|
|
@@ -2934,22 +3100,22 @@ function macKeychainDelete() {
|
|
|
2934
3100
|
return false;
|
|
2935
3101
|
}
|
|
2936
3102
|
}
|
|
2937
|
-
function linuxSecretGet() {
|
|
2938
|
-
if (
|
|
3103
|
+
function linuxSecretGet(service = SERVICE) {
|
|
3104
|
+
if (!linuxSecretAvailable()) return null;
|
|
2939
3105
|
try {
|
|
2940
3106
|
return execSync2(
|
|
2941
|
-
`secret-tool lookup service "${
|
|
3107
|
+
`secret-tool lookup service "${service}" account "${ACCOUNT}" 2>/dev/null`,
|
|
2942
3108
|
{ encoding: "utf-8", timeout: 5e3 }
|
|
2943
3109
|
).trim();
|
|
2944
3110
|
} catch {
|
|
2945
3111
|
return null;
|
|
2946
3112
|
}
|
|
2947
3113
|
}
|
|
2948
|
-
function linuxSecretSet(value) {
|
|
2949
|
-
if (
|
|
3114
|
+
function linuxSecretSet(value, service = SERVICE) {
|
|
3115
|
+
if (!linuxSecretAvailable()) return false;
|
|
2950
3116
|
try {
|
|
2951
3117
|
execSync2(
|
|
2952
|
-
`echo -n "${value}" | secret-tool store --label="exe-os master key" service "${
|
|
3118
|
+
`echo -n "${value}" | secret-tool store --label="exe-os master key" service "${service}" account "${ACCOUNT}" 2>/dev/null`,
|
|
2953
3119
|
{ timeout: 5e3 }
|
|
2954
3120
|
);
|
|
2955
3121
|
return true;
|
|
@@ -2957,11 +3123,12 @@ function linuxSecretSet(value) {
|
|
|
2957
3123
|
return false;
|
|
2958
3124
|
}
|
|
2959
3125
|
}
|
|
2960
|
-
function linuxSecretDelete() {
|
|
3126
|
+
function linuxSecretDelete(service = SERVICE) {
|
|
3127
|
+
if (!nativeKeychainAllowed()) return false;
|
|
2961
3128
|
if (process.platform !== "linux") return false;
|
|
2962
3129
|
try {
|
|
2963
3130
|
execSync2(
|
|
2964
|
-
`secret-tool clear service "${
|
|
3131
|
+
`secret-tool clear service "${service}" account "${ACCOUNT}" 2>/dev/null`,
|
|
2965
3132
|
{ timeout: 5e3 }
|
|
2966
3133
|
);
|
|
2967
3134
|
return true;
|
|
@@ -2970,6 +3137,7 @@ function linuxSecretDelete() {
|
|
|
2970
3137
|
}
|
|
2971
3138
|
}
|
|
2972
3139
|
async function tryKeytar() {
|
|
3140
|
+
if (!nativeKeychainAllowed()) return null;
|
|
2973
3141
|
try {
|
|
2974
3142
|
return await import("keytar");
|
|
2975
3143
|
} catch {
|
|
@@ -3043,7 +3211,19 @@ async function writeMachineBoundFileFallback(b64) {
|
|
|
3043
3211
|
return "plaintext";
|
|
3044
3212
|
}
|
|
3045
3213
|
async function getMasterKey() {
|
|
3046
|
-
|
|
3214
|
+
let nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
3215
|
+
if (!nativeValue) {
|
|
3216
|
+
const legacyValue = macKeychainGet(LEGACY_SERVICE) ?? linuxSecretGet(LEGACY_SERVICE);
|
|
3217
|
+
if (legacyValue) {
|
|
3218
|
+
const migrated = macKeychainSet(legacyValue) || linuxSecretSet(legacyValue);
|
|
3219
|
+
if (migrated) {
|
|
3220
|
+
macKeychainDelete(LEGACY_SERVICE);
|
|
3221
|
+
linuxSecretDelete(LEGACY_SERVICE);
|
|
3222
|
+
process.stderr.write("[keychain] Migrated keychain service from exe-mem to exe-os.\n");
|
|
3223
|
+
}
|
|
3224
|
+
nativeValue = legacyValue;
|
|
3225
|
+
}
|
|
3226
|
+
}
|
|
3047
3227
|
if (nativeValue) {
|
|
3048
3228
|
return Buffer.from(nativeValue, "base64");
|
|
3049
3229
|
}
|
|
@@ -3051,12 +3231,17 @@ async function getMasterKey() {
|
|
|
3051
3231
|
if (keytar) {
|
|
3052
3232
|
try {
|
|
3053
3233
|
const keytarValue = await keytar.getPassword(SERVICE, ACCOUNT);
|
|
3054
|
-
|
|
3055
|
-
|
|
3234
|
+
const legacyKeytarValue = keytarValue ?? await keytar.getPassword(LEGACY_SERVICE, ACCOUNT);
|
|
3235
|
+
if (legacyKeytarValue) {
|
|
3236
|
+
const migrated = macKeychainSet(legacyKeytarValue) || linuxSecretSet(legacyKeytarValue);
|
|
3056
3237
|
if (migrated) {
|
|
3057
3238
|
process.stderr.write("[keychain] Migrated key from keytar to native keychain.\n");
|
|
3239
|
+
try {
|
|
3240
|
+
await keytar.deletePassword(LEGACY_SERVICE, ACCOUNT);
|
|
3241
|
+
} catch {
|
|
3242
|
+
}
|
|
3058
3243
|
}
|
|
3059
|
-
return Buffer.from(
|
|
3244
|
+
return Buffer.from(legacyKeytarValue, "base64");
|
|
3060
3245
|
}
|
|
3061
3246
|
} catch {
|
|
3062
3247
|
}
|
|
@@ -3081,7 +3266,7 @@ async function getMasterKey() {
|
|
|
3081
3266
|
const decrypted = decryptWithMachineKey(content, machineKey);
|
|
3082
3267
|
if (!decrypted) {
|
|
3083
3268
|
process.stderr.write(
|
|
3084
|
-
"[keychain] Key decryption failed \u2014 machine may have changed.\n Use your 24-word recovery phrase: exe-os
|
|
3269
|
+
"[keychain] Key decryption failed \u2014 machine may have changed.\n Use your 24-word recovery phrase during setup: exe-os setup\n"
|
|
3085
3270
|
);
|
|
3086
3271
|
return null;
|
|
3087
3272
|
}
|
|
@@ -3090,6 +3275,9 @@ async function getMasterKey() {
|
|
|
3090
3275
|
b64Value = content;
|
|
3091
3276
|
}
|
|
3092
3277
|
const key = Buffer.from(b64Value, "base64");
|
|
3278
|
+
if (!content.startsWith(ENCRYPTED_PREFIX) && isRootOnlyTrustedServerKeyFile(keyPath)) {
|
|
3279
|
+
return key;
|
|
3280
|
+
}
|
|
3093
3281
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
3094
3282
|
if (migrated) {
|
|
3095
3283
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
@@ -3117,6 +3305,97 @@ async function getMasterKey() {
|
|
|
3117
3305
|
return null;
|
|
3118
3306
|
}
|
|
3119
3307
|
}
|
|
3308
|
+
async function getKeyStorageInfo() {
|
|
3309
|
+
if (macKeychainGet()) {
|
|
3310
|
+
return {
|
|
3311
|
+
kind: "macos-keychain",
|
|
3312
|
+
secure: true,
|
|
3313
|
+
note: "stored in macOS Keychain via built-in security CLI"
|
|
3314
|
+
};
|
|
3315
|
+
}
|
|
3316
|
+
if (macKeychainGet(LEGACY_SERVICE)) {
|
|
3317
|
+
return {
|
|
3318
|
+
kind: "macos-keychain",
|
|
3319
|
+
secure: true,
|
|
3320
|
+
note: "stored in legacy macOS Keychain service exe-mem; next key read migrates it to exe-os"
|
|
3321
|
+
};
|
|
3322
|
+
}
|
|
3323
|
+
if (linuxSecretGet()) {
|
|
3324
|
+
return {
|
|
3325
|
+
kind: "linux-secret-service",
|
|
3326
|
+
secure: true,
|
|
3327
|
+
note: "stored in Linux Secret Service via secret-tool"
|
|
3328
|
+
};
|
|
3329
|
+
}
|
|
3330
|
+
if (linuxSecretGet(LEGACY_SERVICE)) {
|
|
3331
|
+
return {
|
|
3332
|
+
kind: "linux-secret-service",
|
|
3333
|
+
secure: true,
|
|
3334
|
+
note: "stored in legacy Linux Secret Service service exe-mem; next key read migrates it to exe-os"
|
|
3335
|
+
};
|
|
3336
|
+
}
|
|
3337
|
+
const keytar = await tryKeytar();
|
|
3338
|
+
if (keytar) {
|
|
3339
|
+
try {
|
|
3340
|
+
if (await keytar.getPassword(SERVICE, ACCOUNT)) {
|
|
3341
|
+
return {
|
|
3342
|
+
kind: "legacy-keytar",
|
|
3343
|
+
secure: true,
|
|
3344
|
+
note: "stored in legacy keytar backend; will migrate to native keychain when possible"
|
|
3345
|
+
};
|
|
3346
|
+
}
|
|
3347
|
+
if (await keytar.getPassword(LEGACY_SERVICE, ACCOUNT)) {
|
|
3348
|
+
return {
|
|
3349
|
+
kind: "legacy-keytar",
|
|
3350
|
+
secure: true,
|
|
3351
|
+
note: "stored in legacy keytar service exe-mem; will migrate to native exe-os keychain when possible"
|
|
3352
|
+
};
|
|
3353
|
+
}
|
|
3354
|
+
} catch {
|
|
3355
|
+
}
|
|
3356
|
+
}
|
|
3357
|
+
const keyPath = getKeyPath();
|
|
3358
|
+
if (!existsSync8(keyPath)) {
|
|
3359
|
+
return {
|
|
3360
|
+
kind: "missing",
|
|
3361
|
+
secure: false,
|
|
3362
|
+
path: keyPath,
|
|
3363
|
+
note: "no key found in OS keychain, legacy keytar, or file fallback"
|
|
3364
|
+
};
|
|
3365
|
+
}
|
|
3366
|
+
try {
|
|
3367
|
+
const content = (await readFile3(keyPath, "utf-8")).trim();
|
|
3368
|
+
if (content.startsWith(ENCRYPTED_PREFIX)) {
|
|
3369
|
+
return {
|
|
3370
|
+
kind: "encrypted-file",
|
|
3371
|
+
secure: true,
|
|
3372
|
+
path: keyPath,
|
|
3373
|
+
note: "stored in machine-bound encrypted file fallback"
|
|
3374
|
+
};
|
|
3375
|
+
}
|
|
3376
|
+
if (isRootOnlyTrustedServerKeyFile(keyPath)) {
|
|
3377
|
+
return {
|
|
3378
|
+
kind: "server-secret-file",
|
|
3379
|
+
secure: true,
|
|
3380
|
+
path: keyPath,
|
|
3381
|
+
note: "stored as root-only trusted server secret file"
|
|
3382
|
+
};
|
|
3383
|
+
}
|
|
3384
|
+
return {
|
|
3385
|
+
kind: "plaintext-file",
|
|
3386
|
+
secure: false,
|
|
3387
|
+
path: keyPath,
|
|
3388
|
+
note: "stored in legacy plaintext file; reading it will migrate or encrypt it"
|
|
3389
|
+
};
|
|
3390
|
+
} catch {
|
|
3391
|
+
return {
|
|
3392
|
+
kind: "missing",
|
|
3393
|
+
secure: false,
|
|
3394
|
+
path: keyPath,
|
|
3395
|
+
note: "key file exists but could not be read"
|
|
3396
|
+
};
|
|
3397
|
+
}
|
|
3398
|
+
}
|
|
3120
3399
|
async function setMasterKey(key) {
|
|
3121
3400
|
const b64 = key.toString("base64");
|
|
3122
3401
|
if (macKeychainSet(b64) || linuxSecretSet(b64)) {
|
|
@@ -3142,10 +3421,13 @@ async function setMasterKey(key) {
|
|
|
3142
3421
|
async function deleteMasterKey() {
|
|
3143
3422
|
macKeychainDelete();
|
|
3144
3423
|
linuxSecretDelete();
|
|
3424
|
+
macKeychainDelete(LEGACY_SERVICE);
|
|
3425
|
+
linuxSecretDelete(LEGACY_SERVICE);
|
|
3145
3426
|
const keytar = await tryKeytar();
|
|
3146
3427
|
if (keytar) {
|
|
3147
3428
|
try {
|
|
3148
3429
|
await keytar.deletePassword(SERVICE, ACCOUNT);
|
|
3430
|
+
await keytar.deletePassword(LEGACY_SERVICE, ACCOUNT);
|
|
3149
3431
|
} catch {
|
|
3150
3432
|
}
|
|
3151
3433
|
}
|
|
@@ -3183,12 +3465,14 @@ async function importMnemonic(mnemonic) {
|
|
|
3183
3465
|
const entropy = mnemonicToEntropy(trimmed);
|
|
3184
3466
|
return Buffer.from(entropy, "hex");
|
|
3185
3467
|
}
|
|
3186
|
-
var SERVICE, ACCOUNT, ENCRYPTED_PREFIX;
|
|
3468
|
+
var SERVICE, LEGACY_SERVICE, ACCOUNT, linuxSecretAvailability, ENCRYPTED_PREFIX;
|
|
3187
3469
|
var init_keychain = __esm({
|
|
3188
3470
|
"src/lib/keychain.ts"() {
|
|
3189
3471
|
"use strict";
|
|
3190
|
-
SERVICE = "exe-
|
|
3472
|
+
SERVICE = "exe-os";
|
|
3473
|
+
LEGACY_SERVICE = "exe-mem";
|
|
3191
3474
|
ACCOUNT = "master-key";
|
|
3475
|
+
linuxSecretAvailability = null;
|
|
3192
3476
|
ENCRYPTED_PREFIX = "enc:";
|
|
3193
3477
|
}
|
|
3194
3478
|
});
|
|
@@ -3204,7 +3488,7 @@ __export(db_backup_exports, {
|
|
|
3204
3488
|
listBackups: () => listBackups,
|
|
3205
3489
|
rotateBackups: () => rotateBackups
|
|
3206
3490
|
});
|
|
3207
|
-
import { copyFileSync, existsSync as existsSync9, mkdirSync as mkdirSync4, readdirSync, unlinkSync as unlinkSync4, statSync as
|
|
3491
|
+
import { copyFileSync, existsSync as existsSync9, mkdirSync as mkdirSync4, readdirSync, unlinkSync as unlinkSync4, statSync as statSync3 } from "fs";
|
|
3208
3492
|
import path9 from "path";
|
|
3209
3493
|
function findActiveDb() {
|
|
3210
3494
|
for (const name of DB_NAMES) {
|
|
@@ -3248,7 +3532,7 @@ function rotateBackups(keepDays = DEFAULT_KEEP_DAYS) {
|
|
|
3248
3532
|
if (!file.endsWith(".db") && !file.endsWith(".db-wal") && !file.endsWith(".db-shm")) continue;
|
|
3249
3533
|
const filePath = path9.join(BACKUP_DIR, file);
|
|
3250
3534
|
try {
|
|
3251
|
-
const stat =
|
|
3535
|
+
const stat = statSync3(filePath);
|
|
3252
3536
|
if (stat.mtimeMs < cutoff) {
|
|
3253
3537
|
unlinkSync4(filePath);
|
|
3254
3538
|
deleted++;
|
|
@@ -3266,7 +3550,7 @@ function listBackups() {
|
|
|
3266
3550
|
const files = readdirSync(BACKUP_DIR).filter((f) => f.endsWith(".db") && !f.endsWith("-wal") && !f.endsWith("-shm"));
|
|
3267
3551
|
return files.map((name) => {
|
|
3268
3552
|
const p = path9.join(BACKUP_DIR, name);
|
|
3269
|
-
const stat =
|
|
3553
|
+
const stat = statSync3(p);
|
|
3270
3554
|
return { path: p, name, size: stat.size, date: stat.mtime };
|
|
3271
3555
|
}).sort((a, b) => b.date.getTime() - a.date.getTime());
|
|
3272
3556
|
} catch {
|
|
@@ -3298,7 +3582,7 @@ var init_db_backup = __esm({
|
|
|
3298
3582
|
|
|
3299
3583
|
// src/lib/cloud-sync.ts
|
|
3300
3584
|
init_database();
|
|
3301
|
-
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync10, readdirSync as readdirSync2, mkdirSync as mkdirSync5, appendFileSync, unlinkSync as unlinkSync5, openSync as openSync2, closeSync as closeSync2, statSync as
|
|
3585
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync10, readdirSync as readdirSync2, mkdirSync as mkdirSync5, appendFileSync, unlinkSync as unlinkSync5, openSync as openSync2, closeSync as closeSync2, statSync as statSync4 } from "fs";
|
|
3302
3586
|
import crypto3 from "crypto";
|
|
3303
3587
|
import path10 from "path";
|
|
3304
3588
|
import { homedir as homedir2 } from "os";
|
|
@@ -3453,18 +3737,36 @@ function loadPgClient() {
|
|
|
3453
3737
|
const { pathToFileURL: pathToFileURL3 } = await import("url");
|
|
3454
3738
|
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
3455
3739
|
if (explicitPath) {
|
|
3456
|
-
const
|
|
3457
|
-
const
|
|
3458
|
-
if (!
|
|
3459
|
-
return new
|
|
3740
|
+
const mod = await import(pathToFileURL3(explicitPath).href);
|
|
3741
|
+
const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
|
|
3742
|
+
if (!Ctor) throw new Error(`No PrismaClient at ${explicitPath}`);
|
|
3743
|
+
return new Ctor();
|
|
3460
3744
|
}
|
|
3461
3745
|
const exeDbRoot = process.env.EXE_DB_ROOT ?? path10.join(homedir2(), "exe-db");
|
|
3462
|
-
const
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3746
|
+
const packagePath = path10.join(exeDbRoot, "package.json");
|
|
3747
|
+
if (existsSync10(packagePath)) {
|
|
3748
|
+
const req = createRequire3(packagePath);
|
|
3749
|
+
const entry = req.resolve("@prisma/client");
|
|
3750
|
+
const mod = await import(pathToFileURL3(entry).href);
|
|
3751
|
+
const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
|
|
3752
|
+
if (!Ctor) throw new Error("No PrismaClient");
|
|
3753
|
+
return new Ctor();
|
|
3754
|
+
}
|
|
3755
|
+
const { Pool } = await import("pg");
|
|
3756
|
+
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
3757
|
+
return {
|
|
3758
|
+
async $queryRawUnsafe(query, ...values) {
|
|
3759
|
+
const result = await pool.query(query, values);
|
|
3760
|
+
return result.rows;
|
|
3761
|
+
},
|
|
3762
|
+
async $executeRawUnsafe(query, ...values) {
|
|
3763
|
+
const result = await pool.query(query, values);
|
|
3764
|
+
return result.rowCount ?? 0;
|
|
3765
|
+
},
|
|
3766
|
+
async $disconnect() {
|
|
3767
|
+
await pool.end();
|
|
3768
|
+
}
|
|
3769
|
+
};
|
|
3468
3770
|
})().catch(() => {
|
|
3469
3771
|
_pgFailed = true;
|
|
3470
3772
|
_pgPromise = null;
|
|
@@ -3485,7 +3787,7 @@ async function pushToPostgres(records) {
|
|
|
3485
3787
|
let inserted = 0;
|
|
3486
3788
|
for (const rec of records) {
|
|
3487
3789
|
try {
|
|
3488
|
-
await prisma.$executeRawUnsafe(
|
|
3790
|
+
const changed = await prisma.$executeRawUnsafe(
|
|
3489
3791
|
`INSERT INTO raw.raw_events (id, source, source_id, event_type, payload, metadata, timestamp)
|
|
3490
3792
|
VALUES (gen_random_uuid(), 'cloud_sync', $1, 'memory', $2::jsonb, $3::jsonb, $4)
|
|
3491
3793
|
ON CONFLICT (source, source_id, event_type) DO NOTHING`,
|
|
@@ -3494,7 +3796,7 @@ async function pushToPostgres(records) {
|
|
|
3494
3796
|
JSON.stringify({ agent_id: rec.agent_id, project_name: rec.project_name, tool_name: rec.tool_name }),
|
|
3495
3797
|
rec.timestamp ? new Date(String(rec.timestamp)) : /* @__PURE__ */ new Date()
|
|
3496
3798
|
);
|
|
3497
|
-
inserted
|
|
3799
|
+
inserted += Number(changed ?? 0);
|
|
3498
3800
|
} catch {
|
|
3499
3801
|
}
|
|
3500
3802
|
}
|
|
@@ -3599,6 +3901,23 @@ async function cloudPush(records, maxVersion, config) {
|
|
|
3599
3901
|
return false;
|
|
3600
3902
|
}
|
|
3601
3903
|
}
|
|
3904
|
+
async function cloudResetMemoryBlobs(config) {
|
|
3905
|
+
assertSecureEndpoint(config.endpoint);
|
|
3906
|
+
const resp = await fetchWithRetry(`${config.endpoint}/sync/reset-memory`, {
|
|
3907
|
+
method: "POST",
|
|
3908
|
+
headers: {
|
|
3909
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
3910
|
+
"Content-Type": "application/json",
|
|
3911
|
+
"X-Device-Id": loadDeviceId()
|
|
3912
|
+
},
|
|
3913
|
+
body: JSON.stringify({ confirm: "LOCAL DB IS SOURCE OF TRUTH" })
|
|
3914
|
+
});
|
|
3915
|
+
if (!resp.ok) {
|
|
3916
|
+
throw new Error(`cloud reset failed: HTTP ${resp.status}`);
|
|
3917
|
+
}
|
|
3918
|
+
const data = await resp.json();
|
|
3919
|
+
return { deleted: Number(data.deleted ?? 0), freedBytes: Number(data.freed_bytes ?? 0) };
|
|
3920
|
+
}
|
|
3602
3921
|
async function cloudPull(sinceVersion, config) {
|
|
3603
3922
|
assertSecureEndpoint(config.endpoint);
|
|
3604
3923
|
try {
|
|
@@ -3618,22 +3937,62 @@ async function cloudPull(sinceVersion, config) {
|
|
|
3618
3937
|
if (!response.ok) return { records: [], maxVersion: sinceVersion };
|
|
3619
3938
|
const data = await response.json();
|
|
3620
3939
|
const allRecords = [];
|
|
3621
|
-
|
|
3940
|
+
let maxReadableVersion = sinceVersion;
|
|
3941
|
+
let skippedBlobs = 0;
|
|
3942
|
+
for (const { version, blob } of data.blobs ?? []) {
|
|
3622
3943
|
try {
|
|
3623
3944
|
const compressed = decryptSyncBlob(blob);
|
|
3624
3945
|
const json = decompress(compressed).toString("utf8");
|
|
3625
3946
|
const records = JSON.parse(json);
|
|
3626
3947
|
allRecords.push(...records);
|
|
3948
|
+
const recordMax = records.reduce((max, rec) => {
|
|
3949
|
+
const v = Number(rec.version ?? 0);
|
|
3950
|
+
return Number.isFinite(v) ? Math.max(max, v) : max;
|
|
3951
|
+
}, 0);
|
|
3952
|
+
const blobVersion = Number(version ?? 0);
|
|
3953
|
+
maxReadableVersion = Math.max(
|
|
3954
|
+
maxReadableVersion,
|
|
3955
|
+
Number.isFinite(blobVersion) ? blobVersion : 0,
|
|
3956
|
+
recordMax
|
|
3957
|
+
);
|
|
3627
3958
|
} catch {
|
|
3959
|
+
skippedBlobs++;
|
|
3628
3960
|
continue;
|
|
3629
3961
|
}
|
|
3630
3962
|
}
|
|
3631
|
-
|
|
3963
|
+
if (skippedBlobs > 0) {
|
|
3964
|
+
logError(`[cloud-sync] PULL skipped ${skippedBlobs} undecryptable blob(s); pull cursor advanced only to last readable version ${maxReadableVersion}`);
|
|
3965
|
+
}
|
|
3966
|
+
return { records: allRecords, maxVersion: maxReadableVersion };
|
|
3632
3967
|
} catch (err) {
|
|
3633
3968
|
logError(`[cloud-sync] PULL FAILED: ${err instanceof Error ? err.message : String(err)}`);
|
|
3634
3969
|
return { records: [], maxVersion: sinceVersion };
|
|
3635
3970
|
}
|
|
3636
3971
|
}
|
|
3972
|
+
var CLOUD_REUPLOAD_REQUIRED_MESSAGE = "Cloud sync is blocked because this device rotated its memory encryption key. Run `exe-os cloud reupload` first to re-upload the cloud backup with the new key.";
|
|
3973
|
+
async function getCloudReuploadRequired(client = getClient()) {
|
|
3974
|
+
try {
|
|
3975
|
+
await client.execute("CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
|
|
3976
|
+
const result = await client.execute("SELECT key, value FROM sync_meta WHERE key IN ('cloud_reupload_required', 'cloud_relink_required')");
|
|
3977
|
+
return result.rows.some((row) => String(row.value ?? "") === "1");
|
|
3978
|
+
} catch {
|
|
3979
|
+
return false;
|
|
3980
|
+
}
|
|
3981
|
+
}
|
|
3982
|
+
async function clearCloudReuploadRequired(client = getClient()) {
|
|
3983
|
+
await client.execute("CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
|
|
3984
|
+
await client.execute("INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_reupload_required', '0')");
|
|
3985
|
+
await client.execute("INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_relink_required', '0')");
|
|
3986
|
+
await client.execute({
|
|
3987
|
+
sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_reuploaded_at', ?)",
|
|
3988
|
+
args: [(/* @__PURE__ */ new Date()).toISOString()]
|
|
3989
|
+
});
|
|
3990
|
+
await client.execute("DELETE FROM sync_meta WHERE key IN ('last_cloud_pull_version', 'last_cloud_push_version')");
|
|
3991
|
+
}
|
|
3992
|
+
async function markCloudReuploadRequired(client = getClient()) {
|
|
3993
|
+
await client.execute("CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
|
|
3994
|
+
await client.execute("INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_reupload_required', '1')");
|
|
3995
|
+
}
|
|
3637
3996
|
async function cloudSync(config) {
|
|
3638
3997
|
if (!isSyncCryptoInitialized()) {
|
|
3639
3998
|
try {
|
|
@@ -3655,13 +4014,10 @@ async function cloudSync(config) {
|
|
|
3655
4014
|
throw new Error("[cloud-sync] Database not initialized. Call initStore() before cloudSync().");
|
|
3656
4015
|
}
|
|
3657
4016
|
try {
|
|
3658
|
-
|
|
3659
|
-
if (String(relink.rows[0]?.value ?? "") === "1") {
|
|
3660
|
-
throw new Error("[cloud-sync] Paused after key rotation. Re-link/reupload cloud sync with the new recovery phrase before syncing.");
|
|
3661
|
-
}
|
|
4017
|
+
if (await getCloudReuploadRequired(client)) throw new Error(CLOUD_REUPLOAD_REQUIRED_MESSAGE);
|
|
3662
4018
|
} catch (err) {
|
|
3663
4019
|
const msg = err instanceof Error ? err.message : String(err);
|
|
3664
|
-
if (msg.includes("
|
|
4020
|
+
if (msg === CLOUD_REUPLOAD_REQUIRED_MESSAGE || msg.includes("key rotation")) throw err;
|
|
3665
4021
|
}
|
|
3666
4022
|
try {
|
|
3667
4023
|
const { getRawClient: getRawClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
@@ -3919,7 +4275,7 @@ async function cloudSync(config) {
|
|
|
3919
4275
|
const { getLatestBackup: getLatestBackup2 } = await Promise.resolve().then(() => (init_db_backup(), db_backup_exports));
|
|
3920
4276
|
const latestBackup = getLatestBackup2();
|
|
3921
4277
|
if (latestBackup) {
|
|
3922
|
-
const backupSize =
|
|
4278
|
+
const backupSize = statSync4(latestBackup).size;
|
|
3923
4279
|
const MAX_CLOUD_BACKUP_BYTES = 50 * 1024 * 1024;
|
|
3924
4280
|
if (backupSize <= MAX_CLOUD_BACKUP_BYTES) {
|
|
3925
4281
|
const backupData = readFileSync7(latestBackup);
|
|
@@ -4592,8 +4948,10 @@ async function cloudPullDocuments(config) {
|
|
|
4592
4948
|
return { pulled };
|
|
4593
4949
|
}
|
|
4594
4950
|
export {
|
|
4951
|
+
CLOUD_REUPLOAD_REQUIRED_MESSAGE,
|
|
4595
4952
|
assertSecureEndpoint,
|
|
4596
4953
|
buildRosterBlob,
|
|
4954
|
+
clearCloudReuploadRequired,
|
|
4597
4955
|
cloudPull,
|
|
4598
4956
|
cloudPullBehaviors,
|
|
4599
4957
|
cloudPullBlob,
|
|
@@ -4612,7 +4970,10 @@ export {
|
|
|
4612
4970
|
cloudPushGraphRAG,
|
|
4613
4971
|
cloudPushRoster,
|
|
4614
4972
|
cloudPushTasks,
|
|
4973
|
+
cloudResetMemoryBlobs,
|
|
4615
4974
|
cloudSync,
|
|
4975
|
+
getCloudReuploadRequired,
|
|
4976
|
+
markCloudReuploadRequired,
|
|
4616
4977
|
mergeConfig,
|
|
4617
4978
|
mergeRosterFromRemote,
|
|
4618
4979
|
pushToPostgres,
|