@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
|
@@ -188,6 +188,11 @@ function normalizeAutoUpdate(raw) {
|
|
|
188
188
|
const userAU = raw.autoUpdate ?? {};
|
|
189
189
|
raw.autoUpdate = { ...defaultAU, ...userAU };
|
|
190
190
|
}
|
|
191
|
+
function normalizeOrchestration(raw) {
|
|
192
|
+
const defaultOrg = DEFAULT_CONFIG.orchestration;
|
|
193
|
+
const userOrg = raw.orchestration ?? {};
|
|
194
|
+
raw.orchestration = { ...defaultOrg, ...userOrg };
|
|
195
|
+
}
|
|
191
196
|
async function loadConfig() {
|
|
192
197
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
193
198
|
await ensurePrivateDir(dir);
|
|
@@ -212,10 +217,15 @@ async function loadConfig() {
|
|
|
212
217
|
normalizeScalingRoadmap(migratedCfg);
|
|
213
218
|
normalizeSessionLifecycle(migratedCfg);
|
|
214
219
|
normalizeAutoUpdate(migratedCfg);
|
|
220
|
+
normalizeOrchestration(migratedCfg);
|
|
215
221
|
const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
|
|
216
222
|
if (config.dbPath.startsWith("~")) {
|
|
217
223
|
config.dbPath = config.dbPath.replace(/^~/, os.homedir());
|
|
218
224
|
}
|
|
225
|
+
const envDbPath = path.join(dir, "memories.db");
|
|
226
|
+
if (process.env.EXE_OS_DIR && config.dbPath !== envDbPath && !existsSync2(config.dbPath) && existsSync2(envDbPath)) {
|
|
227
|
+
config.dbPath = envDbPath;
|
|
228
|
+
}
|
|
219
229
|
return config;
|
|
220
230
|
} catch {
|
|
221
231
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
@@ -287,6 +297,10 @@ var init_config = __esm({
|
|
|
287
297
|
checkOnBoot: true,
|
|
288
298
|
autoInstall: false,
|
|
289
299
|
checkIntervalMs: 24 * 60 * 60 * 1e3
|
|
300
|
+
},
|
|
301
|
+
orchestration: {
|
|
302
|
+
phase: "phase_1_coo",
|
|
303
|
+
phaseSetBy: "default"
|
|
290
304
|
}
|
|
291
305
|
};
|
|
292
306
|
CONFIG_MIGRATIONS = [
|
|
@@ -1655,6 +1669,9 @@ function getClient() {
|
|
|
1655
1669
|
if (_daemonClient && _daemonClient._isDaemonActive()) {
|
|
1656
1670
|
return _daemonClient;
|
|
1657
1671
|
}
|
|
1672
|
+
if (!_resilientClient) {
|
|
1673
|
+
return _adapterClient;
|
|
1674
|
+
}
|
|
1658
1675
|
return _resilientClient;
|
|
1659
1676
|
}
|
|
1660
1677
|
async function initDaemonClient() {
|
|
@@ -2687,6 +2704,127 @@ async function ensureSchema() {
|
|
|
2687
2704
|
VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
|
|
2688
2705
|
END;
|
|
2689
2706
|
`);
|
|
2707
|
+
await client.executeMultiple(`
|
|
2708
|
+
CREATE TABLE IF NOT EXISTS agent_sessions (
|
|
2709
|
+
id TEXT PRIMARY KEY,
|
|
2710
|
+
agent_id TEXT NOT NULL,
|
|
2711
|
+
project_name TEXT,
|
|
2712
|
+
started_at TEXT NOT NULL,
|
|
2713
|
+
last_event_at TEXT NOT NULL,
|
|
2714
|
+
event_count INTEGER NOT NULL DEFAULT 0,
|
|
2715
|
+
properties TEXT DEFAULT '{}'
|
|
2716
|
+
);
|
|
2717
|
+
|
|
2718
|
+
CREATE INDEX IF NOT EXISTS idx_agent_sessions_agent_time
|
|
2719
|
+
ON agent_sessions(agent_id, started_at);
|
|
2720
|
+
|
|
2721
|
+
CREATE TABLE IF NOT EXISTS agent_goals (
|
|
2722
|
+
id TEXT PRIMARY KEY,
|
|
2723
|
+
statement TEXT NOT NULL,
|
|
2724
|
+
owner_agent_id TEXT,
|
|
2725
|
+
project_name TEXT,
|
|
2726
|
+
status TEXT NOT NULL DEFAULT 'open',
|
|
2727
|
+
priority INTEGER NOT NULL DEFAULT 5,
|
|
2728
|
+
success_criteria TEXT,
|
|
2729
|
+
parent_goal_id TEXT,
|
|
2730
|
+
due_at TEXT,
|
|
2731
|
+
achieved_at TEXT,
|
|
2732
|
+
supersedes_id TEXT,
|
|
2733
|
+
created_at TEXT NOT NULL,
|
|
2734
|
+
updated_at TEXT NOT NULL,
|
|
2735
|
+
source_memory_id TEXT
|
|
2736
|
+
);
|
|
2737
|
+
|
|
2738
|
+
CREATE INDEX IF NOT EXISTS idx_agent_goals_project_status
|
|
2739
|
+
ON agent_goals(project_name, status, priority);
|
|
2740
|
+
|
|
2741
|
+
CREATE TABLE IF NOT EXISTS agent_events (
|
|
2742
|
+
id TEXT PRIMARY KEY,
|
|
2743
|
+
event_type TEXT NOT NULL,
|
|
2744
|
+
occurred_at TEXT NOT NULL,
|
|
2745
|
+
sequence_index INTEGER NOT NULL,
|
|
2746
|
+
actor_agent_id TEXT,
|
|
2747
|
+
agent_role TEXT,
|
|
2748
|
+
project_name TEXT,
|
|
2749
|
+
session_id TEXT,
|
|
2750
|
+
task_id TEXT,
|
|
2751
|
+
goal_id TEXT,
|
|
2752
|
+
parent_event_id TEXT,
|
|
2753
|
+
intention TEXT,
|
|
2754
|
+
outcome TEXT,
|
|
2755
|
+
evidence_memory_id TEXT,
|
|
2756
|
+
impact TEXT,
|
|
2757
|
+
payload TEXT DEFAULT '{}',
|
|
2758
|
+
created_at TEXT NOT NULL
|
|
2759
|
+
);
|
|
2760
|
+
|
|
2761
|
+
CREATE INDEX IF NOT EXISTS idx_agent_events_time
|
|
2762
|
+
ON agent_events(occurred_at, sequence_index);
|
|
2763
|
+
|
|
2764
|
+
CREATE INDEX IF NOT EXISTS idx_agent_events_session_seq
|
|
2765
|
+
ON agent_events(session_id, sequence_index);
|
|
2766
|
+
|
|
2767
|
+
CREATE INDEX IF NOT EXISTS idx_agent_events_goal_time
|
|
2768
|
+
ON agent_events(goal_id, occurred_at);
|
|
2769
|
+
|
|
2770
|
+
CREATE INDEX IF NOT EXISTS idx_agent_events_memory
|
|
2771
|
+
ON agent_events(evidence_memory_id);
|
|
2772
|
+
|
|
2773
|
+
CREATE TABLE IF NOT EXISTS agent_goal_links (
|
|
2774
|
+
id TEXT PRIMARY KEY,
|
|
2775
|
+
goal_id TEXT NOT NULL,
|
|
2776
|
+
link_type TEXT NOT NULL,
|
|
2777
|
+
target_id TEXT NOT NULL,
|
|
2778
|
+
target_type TEXT NOT NULL,
|
|
2779
|
+
created_at TEXT NOT NULL
|
|
2780
|
+
);
|
|
2781
|
+
|
|
2782
|
+
CREATE INDEX IF NOT EXISTS idx_agent_goal_links_goal
|
|
2783
|
+
ON agent_goal_links(goal_id, target_type);
|
|
2784
|
+
|
|
2785
|
+
CREATE TABLE IF NOT EXISTS agent_semantic_labels (
|
|
2786
|
+
id TEXT PRIMARY KEY,
|
|
2787
|
+
source_memory_id TEXT NOT NULL,
|
|
2788
|
+
event_id TEXT,
|
|
2789
|
+
labeler TEXT NOT NULL,
|
|
2790
|
+
schema_version INTEGER NOT NULL DEFAULT 1,
|
|
2791
|
+
confidence REAL NOT NULL DEFAULT 0,
|
|
2792
|
+
labels TEXT NOT NULL,
|
|
2793
|
+
created_at TEXT NOT NULL,
|
|
2794
|
+
updated_at TEXT NOT NULL
|
|
2795
|
+
);
|
|
2796
|
+
|
|
2797
|
+
CREATE INDEX IF NOT EXISTS idx_agent_semantic_labels_memory
|
|
2798
|
+
ON agent_semantic_labels(source_memory_id, labeler);
|
|
2799
|
+
|
|
2800
|
+
CREATE INDEX IF NOT EXISTS idx_agent_semantic_labels_event
|
|
2801
|
+
ON agent_semantic_labels(event_id);
|
|
2802
|
+
|
|
2803
|
+
CREATE TABLE IF NOT EXISTS agent_reflection_checkpoints (
|
|
2804
|
+
id TEXT PRIMARY KEY,
|
|
2805
|
+
project_name TEXT,
|
|
2806
|
+
session_id TEXT,
|
|
2807
|
+
window_start_at TEXT NOT NULL,
|
|
2808
|
+
window_end_at TEXT NOT NULL,
|
|
2809
|
+
event_count INTEGER NOT NULL DEFAULT 0,
|
|
2810
|
+
goal_count INTEGER NOT NULL DEFAULT 0,
|
|
2811
|
+
success_count INTEGER NOT NULL DEFAULT 0,
|
|
2812
|
+
failure_count INTEGER NOT NULL DEFAULT 0,
|
|
2813
|
+
risk_count INTEGER NOT NULL DEFAULT 0,
|
|
2814
|
+
summary TEXT NOT NULL,
|
|
2815
|
+
learnings TEXT NOT NULL DEFAULT '[]',
|
|
2816
|
+
next_actions TEXT NOT NULL DEFAULT '[]',
|
|
2817
|
+
evidence_event_ids TEXT NOT NULL DEFAULT '[]',
|
|
2818
|
+
confidence REAL NOT NULL DEFAULT 0,
|
|
2819
|
+
created_at TEXT NOT NULL
|
|
2820
|
+
);
|
|
2821
|
+
|
|
2822
|
+
CREATE INDEX IF NOT EXISTS idx_agent_reflection_project_time
|
|
2823
|
+
ON agent_reflection_checkpoints(project_name, window_end_at);
|
|
2824
|
+
|
|
2825
|
+
CREATE INDEX IF NOT EXISTS idx_agent_reflection_session_time
|
|
2826
|
+
ON agent_reflection_checkpoints(session_id, window_end_at);
|
|
2827
|
+
`);
|
|
2690
2828
|
try {
|
|
2691
2829
|
await client.execute({
|
|
2692
2830
|
sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
|
|
@@ -2850,7 +2988,7 @@ __export(shard_manager_exports, {
|
|
|
2850
2988
|
shardExists: () => shardExists
|
|
2851
2989
|
});
|
|
2852
2990
|
import path7 from "path";
|
|
2853
|
-
import { existsSync as existsSync7, mkdirSync as mkdirSync2, readdirSync, renameSync as renameSync3, statSync as
|
|
2991
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync2, readdirSync, renameSync as renameSync3, statSync as statSync3 } from "fs";
|
|
2854
2992
|
import { createClient as createClient2 } from "@libsql/client";
|
|
2855
2993
|
function initShardManager(encryptionKey) {
|
|
2856
2994
|
_encryptionKey = encryptionKey;
|
|
@@ -2914,7 +3052,7 @@ async function auditShardHealth(options = {}) {
|
|
|
2914
3052
|
const shards = [];
|
|
2915
3053
|
for (const name of names) {
|
|
2916
3054
|
const dbPath = path7.join(SHARDS_DIR, `${name}.db`);
|
|
2917
|
-
const stat =
|
|
3055
|
+
const stat = statSync3(dbPath);
|
|
2918
3056
|
const item = {
|
|
2919
3057
|
name,
|
|
2920
3058
|
path: dbPath,
|
|
@@ -3167,7 +3305,7 @@ async function getReadyShardClient(projectName) {
|
|
|
3167
3305
|
_shardLastAccess.delete(safeName);
|
|
3168
3306
|
const dbPath = path7.join(SHARDS_DIR, `${safeName}.db`);
|
|
3169
3307
|
if (existsSync7(dbPath)) {
|
|
3170
|
-
const stat =
|
|
3308
|
+
const stat = statSync3(dbPath);
|
|
3171
3309
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3172
3310
|
const archivedPath = path7.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
|
|
3173
3311
|
renameSync3(dbPath, archivedPath);
|
|
@@ -3287,6 +3425,12 @@ var init_platform_procedures = __esm({
|
|
|
3287
3425
|
priority: "p0",
|
|
3288
3426
|
content: "Founder -> coordinator (the executive agent, internally routed as 'COO') -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the coordinator does not bypass managers for specialist work. Specialists report to their manager. If you need cross-team info, use ask_team_memory \u2014 don't read other agents' task folders. Each level owns dispatch downward and review upward."
|
|
3289
3427
|
},
|
|
3428
|
+
{
|
|
3429
|
+
title: "Customer orchestration maturity \u2014 recommend, never trap",
|
|
3430
|
+
domain: "workflow",
|
|
3431
|
+
priority: "p1",
|
|
3432
|
+
content: "New customers start best in Phase 1: founder \u2194 coordinator/Chief of Staff, building company context. Suggest Phase 2 executives when domain work repeats; suggest Phase 3 parallel execution only when review/permission gates are ready. This is guidance, not a blocker: users may jump phases anytime. Never overwrite their phase, role titles, identities, or custom org design."
|
|
3433
|
+
},
|
|
3290
3434
|
{
|
|
3291
3435
|
title: "Single dispatch path \u2014 create_task only",
|
|
3292
3436
|
domain: "workflow",
|
|
@@ -3345,6 +3489,12 @@ var init_platform_procedures = __esm({
|
|
|
3345
3489
|
priority: "p0",
|
|
3346
3490
|
content: "exe-build-adv is MANDATORY for ALL work touching 3+ files. Run /exe-build-adv --auto BEFORE implementation. Pipeline: Spec \u2192 AC \u2192 Tests \u2192 Evaluate \u2192 Fix. No multi-file feature ships without pipeline artifacts. No exceptions \u2014 managers reject work without them."
|
|
3347
3491
|
},
|
|
3492
|
+
{
|
|
3493
|
+
title: "Commit discipline \u2014 never leave verified work floating",
|
|
3494
|
+
domain: "workflow",
|
|
3495
|
+
priority: "p1",
|
|
3496
|
+
content: "After any code-change batch passes typecheck/tests/build, run git status, summarize changed files, and commit with a clear message before ending the session. If work must remain uncommitted for review/dogfood, explicitly say so, list the files, and state the blocker. Never imply work is complete while verified changes are still floating locally."
|
|
3497
|
+
},
|
|
3348
3498
|
{
|
|
3349
3499
|
title: "Desktop and TUI are the same product",
|
|
3350
3500
|
domain: "architecture",
|
|
@@ -3510,11 +3660,12 @@ init_database();
|
|
|
3510
3660
|
|
|
3511
3661
|
// src/lib/keychain.ts
|
|
3512
3662
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
3513
|
-
import { existsSync as existsSync6 } from "fs";
|
|
3663
|
+
import { existsSync as existsSync6, statSync as statSync2 } from "fs";
|
|
3514
3664
|
import { execSync as execSync2 } from "child_process";
|
|
3515
3665
|
import path6 from "path";
|
|
3516
3666
|
import os5 from "os";
|
|
3517
|
-
var SERVICE = "exe-
|
|
3667
|
+
var SERVICE = "exe-os";
|
|
3668
|
+
var LEGACY_SERVICE = "exe-mem";
|
|
3518
3669
|
var ACCOUNT = "master-key";
|
|
3519
3670
|
function getKeyDir() {
|
|
3520
3671
|
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path6.join(os5.homedir(), ".exe-os");
|
|
@@ -3522,29 +3673,66 @@ function getKeyDir() {
|
|
|
3522
3673
|
function getKeyPath() {
|
|
3523
3674
|
return path6.join(getKeyDir(), "master.key");
|
|
3524
3675
|
}
|
|
3525
|
-
function
|
|
3676
|
+
function nativeKeychainAllowed() {
|
|
3677
|
+
return process.env.EXE_OS_DISABLE_NATIVE_KEYCHAIN !== "1";
|
|
3678
|
+
}
|
|
3679
|
+
var linuxSecretAvailability = null;
|
|
3680
|
+
function linuxSecretAvailable() {
|
|
3681
|
+
if (!nativeKeychainAllowed()) return false;
|
|
3682
|
+
if (process.platform !== "linux") return false;
|
|
3683
|
+
if (linuxSecretAvailability !== null) return linuxSecretAvailability;
|
|
3684
|
+
try {
|
|
3685
|
+
execSync2("command -v secret-tool >/dev/null 2>&1", { timeout: 1e3 });
|
|
3686
|
+
} catch {
|
|
3687
|
+
linuxSecretAvailability = false;
|
|
3688
|
+
return false;
|
|
3689
|
+
}
|
|
3690
|
+
try {
|
|
3691
|
+
execSync2("secret-tool search --all exe-os probe >/dev/null 2>&1", { timeout: 1e3 });
|
|
3692
|
+
linuxSecretAvailability = true;
|
|
3693
|
+
} catch {
|
|
3694
|
+
linuxSecretAvailability = false;
|
|
3695
|
+
}
|
|
3696
|
+
return linuxSecretAvailability;
|
|
3697
|
+
}
|
|
3698
|
+
function isRootOnlyTrustedServerKeyFile(keyPath) {
|
|
3699
|
+
if (process.platform !== "linux") return false;
|
|
3700
|
+
try {
|
|
3701
|
+
const uid = typeof os5.userInfo().uid === "number" ? os5.userInfo().uid : -1;
|
|
3702
|
+
const st = statSync2(keyPath);
|
|
3703
|
+
if (!st.isFile() || (st.mode & 63) !== 0) return false;
|
|
3704
|
+
if (uid === 0) return true;
|
|
3705
|
+
const exeOsDir = process.env.EXE_OS_DIR;
|
|
3706
|
+
return Boolean(exeOsDir && path6.resolve(keyPath).startsWith(path6.resolve(exeOsDir) + path6.sep));
|
|
3707
|
+
} catch {
|
|
3708
|
+
return false;
|
|
3709
|
+
}
|
|
3710
|
+
}
|
|
3711
|
+
function macKeychainGet(service = SERVICE) {
|
|
3712
|
+
if (!nativeKeychainAllowed()) return null;
|
|
3526
3713
|
if (process.platform !== "darwin") return null;
|
|
3527
3714
|
try {
|
|
3528
3715
|
return execSync2(
|
|
3529
|
-
`security find-generic-password -s "${
|
|
3716
|
+
`security find-generic-password -s "${service}" -a "${ACCOUNT}" -w 2>/dev/null`,
|
|
3530
3717
|
{ encoding: "utf-8", timeout: 5e3 }
|
|
3531
3718
|
).trim();
|
|
3532
3719
|
} catch {
|
|
3533
3720
|
return null;
|
|
3534
3721
|
}
|
|
3535
3722
|
}
|
|
3536
|
-
function macKeychainSet(value) {
|
|
3723
|
+
function macKeychainSet(value, service = SERVICE) {
|
|
3724
|
+
if (!nativeKeychainAllowed()) return false;
|
|
3537
3725
|
if (process.platform !== "darwin") return false;
|
|
3538
3726
|
try {
|
|
3539
3727
|
try {
|
|
3540
3728
|
execSync2(
|
|
3541
|
-
`security delete-generic-password -s "${
|
|
3729
|
+
`security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
|
|
3542
3730
|
{ timeout: 5e3 }
|
|
3543
3731
|
);
|
|
3544
3732
|
} catch {
|
|
3545
3733
|
}
|
|
3546
3734
|
execSync2(
|
|
3547
|
-
`security add-generic-password -s "${
|
|
3735
|
+
`security add-generic-password -s "${service}" -a "${ACCOUNT}" -w "${value}"`,
|
|
3548
3736
|
{ timeout: 5e3 }
|
|
3549
3737
|
);
|
|
3550
3738
|
return true;
|
|
@@ -3552,22 +3740,48 @@ function macKeychainSet(value) {
|
|
|
3552
3740
|
return false;
|
|
3553
3741
|
}
|
|
3554
3742
|
}
|
|
3555
|
-
function
|
|
3556
|
-
if (
|
|
3743
|
+
function macKeychainDelete(service = SERVICE) {
|
|
3744
|
+
if (!nativeKeychainAllowed()) return false;
|
|
3745
|
+
if (process.platform !== "darwin") return false;
|
|
3746
|
+
try {
|
|
3747
|
+
execSync2(
|
|
3748
|
+
`security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
|
|
3749
|
+
{ timeout: 5e3 }
|
|
3750
|
+
);
|
|
3751
|
+
return true;
|
|
3752
|
+
} catch {
|
|
3753
|
+
return false;
|
|
3754
|
+
}
|
|
3755
|
+
}
|
|
3756
|
+
function linuxSecretGet(service = SERVICE) {
|
|
3757
|
+
if (!linuxSecretAvailable()) return null;
|
|
3557
3758
|
try {
|
|
3558
3759
|
return execSync2(
|
|
3559
|
-
`secret-tool lookup service "${
|
|
3760
|
+
`secret-tool lookup service "${service}" account "${ACCOUNT}" 2>/dev/null`,
|
|
3560
3761
|
{ encoding: "utf-8", timeout: 5e3 }
|
|
3561
3762
|
).trim();
|
|
3562
3763
|
} catch {
|
|
3563
3764
|
return null;
|
|
3564
3765
|
}
|
|
3565
3766
|
}
|
|
3566
|
-
function linuxSecretSet(value) {
|
|
3767
|
+
function linuxSecretSet(value, service = SERVICE) {
|
|
3768
|
+
if (!linuxSecretAvailable()) return false;
|
|
3769
|
+
try {
|
|
3770
|
+
execSync2(
|
|
3771
|
+
`echo -n "${value}" | secret-tool store --label="exe-os master key" service "${service}" account "${ACCOUNT}" 2>/dev/null`,
|
|
3772
|
+
{ timeout: 5e3 }
|
|
3773
|
+
);
|
|
3774
|
+
return true;
|
|
3775
|
+
} catch {
|
|
3776
|
+
return false;
|
|
3777
|
+
}
|
|
3778
|
+
}
|
|
3779
|
+
function linuxSecretDelete(service = SERVICE) {
|
|
3780
|
+
if (!nativeKeychainAllowed()) return false;
|
|
3567
3781
|
if (process.platform !== "linux") return false;
|
|
3568
3782
|
try {
|
|
3569
3783
|
execSync2(
|
|
3570
|
-
`
|
|
3784
|
+
`secret-tool clear service "${service}" account "${ACCOUNT}" 2>/dev/null`,
|
|
3571
3785
|
{ timeout: 5e3 }
|
|
3572
3786
|
);
|
|
3573
3787
|
return true;
|
|
@@ -3576,6 +3790,7 @@ function linuxSecretSet(value) {
|
|
|
3576
3790
|
}
|
|
3577
3791
|
}
|
|
3578
3792
|
async function tryKeytar() {
|
|
3793
|
+
if (!nativeKeychainAllowed()) return null;
|
|
3579
3794
|
try {
|
|
3580
3795
|
return await import("keytar");
|
|
3581
3796
|
} catch {
|
|
@@ -3650,7 +3865,19 @@ async function writeMachineBoundFileFallback(b64) {
|
|
|
3650
3865
|
return "plaintext";
|
|
3651
3866
|
}
|
|
3652
3867
|
async function getMasterKey() {
|
|
3653
|
-
|
|
3868
|
+
let nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
3869
|
+
if (!nativeValue) {
|
|
3870
|
+
const legacyValue = macKeychainGet(LEGACY_SERVICE) ?? linuxSecretGet(LEGACY_SERVICE);
|
|
3871
|
+
if (legacyValue) {
|
|
3872
|
+
const migrated = macKeychainSet(legacyValue) || linuxSecretSet(legacyValue);
|
|
3873
|
+
if (migrated) {
|
|
3874
|
+
macKeychainDelete(LEGACY_SERVICE);
|
|
3875
|
+
linuxSecretDelete(LEGACY_SERVICE);
|
|
3876
|
+
process.stderr.write("[keychain] Migrated keychain service from exe-mem to exe-os.\n");
|
|
3877
|
+
}
|
|
3878
|
+
nativeValue = legacyValue;
|
|
3879
|
+
}
|
|
3880
|
+
}
|
|
3654
3881
|
if (nativeValue) {
|
|
3655
3882
|
return Buffer.from(nativeValue, "base64");
|
|
3656
3883
|
}
|
|
@@ -3658,12 +3885,17 @@ async function getMasterKey() {
|
|
|
3658
3885
|
if (keytar) {
|
|
3659
3886
|
try {
|
|
3660
3887
|
const keytarValue = await keytar.getPassword(SERVICE, ACCOUNT);
|
|
3661
|
-
|
|
3662
|
-
|
|
3888
|
+
const legacyKeytarValue = keytarValue ?? await keytar.getPassword(LEGACY_SERVICE, ACCOUNT);
|
|
3889
|
+
if (legacyKeytarValue) {
|
|
3890
|
+
const migrated = macKeychainSet(legacyKeytarValue) || linuxSecretSet(legacyKeytarValue);
|
|
3663
3891
|
if (migrated) {
|
|
3664
3892
|
process.stderr.write("[keychain] Migrated key from keytar to native keychain.\n");
|
|
3893
|
+
try {
|
|
3894
|
+
await keytar.deletePassword(LEGACY_SERVICE, ACCOUNT);
|
|
3895
|
+
} catch {
|
|
3896
|
+
}
|
|
3665
3897
|
}
|
|
3666
|
-
return Buffer.from(
|
|
3898
|
+
return Buffer.from(legacyKeytarValue, "base64");
|
|
3667
3899
|
}
|
|
3668
3900
|
} catch {
|
|
3669
3901
|
}
|
|
@@ -3688,7 +3920,7 @@ async function getMasterKey() {
|
|
|
3688
3920
|
const decrypted = decryptWithMachineKey(content, machineKey);
|
|
3689
3921
|
if (!decrypted) {
|
|
3690
3922
|
process.stderr.write(
|
|
3691
|
-
"[keychain] Key decryption failed \u2014 machine may have changed.\n Use your 24-word recovery phrase: exe-os
|
|
3923
|
+
"[keychain] Key decryption failed \u2014 machine may have changed.\n Use your 24-word recovery phrase during setup: exe-os setup\n"
|
|
3692
3924
|
);
|
|
3693
3925
|
return null;
|
|
3694
3926
|
}
|
|
@@ -3697,6 +3929,9 @@ async function getMasterKey() {
|
|
|
3697
3929
|
b64Value = content;
|
|
3698
3930
|
}
|
|
3699
3931
|
const key = Buffer.from(b64Value, "base64");
|
|
3932
|
+
if (!content.startsWith(ENCRYPTED_PREFIX) && isRootOnlyTrustedServerKeyFile(keyPath)) {
|
|
3933
|
+
return key;
|
|
3934
|
+
}
|
|
3700
3935
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
3701
3936
|
if (migrated) {
|
|
3702
3937
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|