@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/bin/setup.js
CHANGED
|
@@ -139,6 +139,11 @@ function normalizeAutoUpdate(raw) {
|
|
|
139
139
|
const userAU = raw.autoUpdate ?? {};
|
|
140
140
|
raw.autoUpdate = { ...defaultAU, ...userAU };
|
|
141
141
|
}
|
|
142
|
+
function normalizeOrchestration(raw) {
|
|
143
|
+
const defaultOrg = DEFAULT_CONFIG.orchestration;
|
|
144
|
+
const userOrg = raw.orchestration ?? {};
|
|
145
|
+
raw.orchestration = { ...defaultOrg, ...userOrg };
|
|
146
|
+
}
|
|
142
147
|
async function loadConfig() {
|
|
143
148
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
144
149
|
await ensurePrivateDir(dir);
|
|
@@ -163,10 +168,15 @@ async function loadConfig() {
|
|
|
163
168
|
normalizeScalingRoadmap(migratedCfg);
|
|
164
169
|
normalizeSessionLifecycle(migratedCfg);
|
|
165
170
|
normalizeAutoUpdate(migratedCfg);
|
|
171
|
+
normalizeOrchestration(migratedCfg);
|
|
166
172
|
const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
|
|
167
173
|
if (config.dbPath.startsWith("~")) {
|
|
168
174
|
config.dbPath = config.dbPath.replace(/^~/, os.homedir());
|
|
169
175
|
}
|
|
176
|
+
const envDbPath = path.join(dir, "memories.db");
|
|
177
|
+
if (process.env.EXE_OS_DIR && config.dbPath !== envDbPath && !existsSync2(config.dbPath) && existsSync2(envDbPath)) {
|
|
178
|
+
config.dbPath = envDbPath;
|
|
179
|
+
}
|
|
170
180
|
return config;
|
|
171
181
|
} catch {
|
|
172
182
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
@@ -186,7 +196,16 @@ function loadConfigSync() {
|
|
|
186
196
|
normalizeScalingRoadmap(migratedCfg);
|
|
187
197
|
normalizeSessionLifecycle(migratedCfg);
|
|
188
198
|
normalizeAutoUpdate(migratedCfg);
|
|
189
|
-
|
|
199
|
+
normalizeOrchestration(migratedCfg);
|
|
200
|
+
const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
|
|
201
|
+
if (config.dbPath.startsWith("~")) {
|
|
202
|
+
config.dbPath = config.dbPath.replace(/^~/, os.homedir());
|
|
203
|
+
}
|
|
204
|
+
const envDbPath = path.join(dir, "memories.db");
|
|
205
|
+
if (process.env.EXE_OS_DIR && config.dbPath !== envDbPath && !existsSync2(config.dbPath) && existsSync2(envDbPath)) {
|
|
206
|
+
config.dbPath = envDbPath;
|
|
207
|
+
}
|
|
208
|
+
return config;
|
|
190
209
|
} catch {
|
|
191
210
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
192
211
|
}
|
|
@@ -207,6 +226,7 @@ async function loadConfigFrom(configPath) {
|
|
|
207
226
|
normalizeScalingRoadmap(migratedCfg);
|
|
208
227
|
normalizeSessionLifecycle(migratedCfg);
|
|
209
228
|
normalizeAutoUpdate(migratedCfg);
|
|
229
|
+
normalizeOrchestration(migratedCfg);
|
|
210
230
|
return { ...DEFAULT_CONFIG, ...migratedCfg };
|
|
211
231
|
} catch {
|
|
212
232
|
return { ...DEFAULT_CONFIG };
|
|
@@ -278,6 +298,10 @@ var init_config = __esm({
|
|
|
278
298
|
checkOnBoot: true,
|
|
279
299
|
autoInstall: false,
|
|
280
300
|
checkIntervalMs: 24 * 60 * 60 * 1e3
|
|
301
|
+
},
|
|
302
|
+
orchestration: {
|
|
303
|
+
phase: "phase_1_coo",
|
|
304
|
+
phaseSetBy: "default"
|
|
281
305
|
}
|
|
282
306
|
};
|
|
283
307
|
CONFIG_MIGRATIONS = [
|
|
@@ -298,12 +322,13 @@ var keychain_exports = {};
|
|
|
298
322
|
__export(keychain_exports, {
|
|
299
323
|
deleteMasterKey: () => deleteMasterKey,
|
|
300
324
|
exportMnemonic: () => exportMnemonic,
|
|
325
|
+
getKeyStorageInfo: () => getKeyStorageInfo,
|
|
301
326
|
getMasterKey: () => getMasterKey,
|
|
302
327
|
importMnemonic: () => importMnemonic,
|
|
303
328
|
setMasterKey: () => setMasterKey
|
|
304
329
|
});
|
|
305
330
|
import { readFile as readFile2, writeFile as writeFile2, unlink, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
|
|
306
|
-
import { existsSync as existsSync3 } from "fs";
|
|
331
|
+
import { existsSync as existsSync3, statSync } from "fs";
|
|
307
332
|
import { execSync } from "child_process";
|
|
308
333
|
import path2 from "path";
|
|
309
334
|
import os2 from "os";
|
|
@@ -313,29 +338,65 @@ function getKeyDir() {
|
|
|
313
338
|
function getKeyPath() {
|
|
314
339
|
return path2.join(getKeyDir(), "master.key");
|
|
315
340
|
}
|
|
316
|
-
function
|
|
341
|
+
function nativeKeychainAllowed() {
|
|
342
|
+
return process.env.EXE_OS_DISABLE_NATIVE_KEYCHAIN !== "1";
|
|
343
|
+
}
|
|
344
|
+
function linuxSecretAvailable() {
|
|
345
|
+
if (!nativeKeychainAllowed()) return false;
|
|
346
|
+
if (process.platform !== "linux") return false;
|
|
347
|
+
if (linuxSecretAvailability !== null) return linuxSecretAvailability;
|
|
348
|
+
try {
|
|
349
|
+
execSync("command -v secret-tool >/dev/null 2>&1", { timeout: 1e3 });
|
|
350
|
+
} catch {
|
|
351
|
+
linuxSecretAvailability = false;
|
|
352
|
+
return false;
|
|
353
|
+
}
|
|
354
|
+
try {
|
|
355
|
+
execSync("secret-tool search --all exe-os probe >/dev/null 2>&1", { timeout: 1e3 });
|
|
356
|
+
linuxSecretAvailability = true;
|
|
357
|
+
} catch {
|
|
358
|
+
linuxSecretAvailability = false;
|
|
359
|
+
}
|
|
360
|
+
return linuxSecretAvailability;
|
|
361
|
+
}
|
|
362
|
+
function isRootOnlyTrustedServerKeyFile(keyPath) {
|
|
363
|
+
if (process.platform !== "linux") return false;
|
|
364
|
+
try {
|
|
365
|
+
const uid = typeof os2.userInfo().uid === "number" ? os2.userInfo().uid : -1;
|
|
366
|
+
const st = statSync(keyPath);
|
|
367
|
+
if (!st.isFile() || (st.mode & 63) !== 0) return false;
|
|
368
|
+
if (uid === 0) return true;
|
|
369
|
+
const exeOsDir = process.env.EXE_OS_DIR;
|
|
370
|
+
return Boolean(exeOsDir && path2.resolve(keyPath).startsWith(path2.resolve(exeOsDir) + path2.sep));
|
|
371
|
+
} catch {
|
|
372
|
+
return false;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
function macKeychainGet(service = SERVICE) {
|
|
376
|
+
if (!nativeKeychainAllowed()) return null;
|
|
317
377
|
if (process.platform !== "darwin") return null;
|
|
318
378
|
try {
|
|
319
379
|
return execSync(
|
|
320
|
-
`security find-generic-password -s "${
|
|
380
|
+
`security find-generic-password -s "${service}" -a "${ACCOUNT}" -w 2>/dev/null`,
|
|
321
381
|
{ encoding: "utf-8", timeout: 5e3 }
|
|
322
382
|
).trim();
|
|
323
383
|
} catch {
|
|
324
384
|
return null;
|
|
325
385
|
}
|
|
326
386
|
}
|
|
327
|
-
function macKeychainSet(value) {
|
|
387
|
+
function macKeychainSet(value, service = SERVICE) {
|
|
388
|
+
if (!nativeKeychainAllowed()) return false;
|
|
328
389
|
if (process.platform !== "darwin") return false;
|
|
329
390
|
try {
|
|
330
391
|
try {
|
|
331
392
|
execSync(
|
|
332
|
-
`security delete-generic-password -s "${
|
|
393
|
+
`security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
|
|
333
394
|
{ timeout: 5e3 }
|
|
334
395
|
);
|
|
335
396
|
} catch {
|
|
336
397
|
}
|
|
337
398
|
execSync(
|
|
338
|
-
`security add-generic-password -s "${
|
|
399
|
+
`security add-generic-password -s "${service}" -a "${ACCOUNT}" -w "${value}"`,
|
|
339
400
|
{ timeout: 5e3 }
|
|
340
401
|
);
|
|
341
402
|
return true;
|
|
@@ -343,11 +404,12 @@ function macKeychainSet(value) {
|
|
|
343
404
|
return false;
|
|
344
405
|
}
|
|
345
406
|
}
|
|
346
|
-
function macKeychainDelete() {
|
|
407
|
+
function macKeychainDelete(service = SERVICE) {
|
|
408
|
+
if (!nativeKeychainAllowed()) return false;
|
|
347
409
|
if (process.platform !== "darwin") return false;
|
|
348
410
|
try {
|
|
349
411
|
execSync(
|
|
350
|
-
`security delete-generic-password -s "${
|
|
412
|
+
`security delete-generic-password -s "${service}" -a "${ACCOUNT}" 2>/dev/null`,
|
|
351
413
|
{ timeout: 5e3 }
|
|
352
414
|
);
|
|
353
415
|
return true;
|
|
@@ -355,22 +417,22 @@ function macKeychainDelete() {
|
|
|
355
417
|
return false;
|
|
356
418
|
}
|
|
357
419
|
}
|
|
358
|
-
function linuxSecretGet() {
|
|
359
|
-
if (
|
|
420
|
+
function linuxSecretGet(service = SERVICE) {
|
|
421
|
+
if (!linuxSecretAvailable()) return null;
|
|
360
422
|
try {
|
|
361
423
|
return execSync(
|
|
362
|
-
`secret-tool lookup service "${
|
|
424
|
+
`secret-tool lookup service "${service}" account "${ACCOUNT}" 2>/dev/null`,
|
|
363
425
|
{ encoding: "utf-8", timeout: 5e3 }
|
|
364
426
|
).trim();
|
|
365
427
|
} catch {
|
|
366
428
|
return null;
|
|
367
429
|
}
|
|
368
430
|
}
|
|
369
|
-
function linuxSecretSet(value) {
|
|
370
|
-
if (
|
|
431
|
+
function linuxSecretSet(value, service = SERVICE) {
|
|
432
|
+
if (!linuxSecretAvailable()) return false;
|
|
371
433
|
try {
|
|
372
434
|
execSync(
|
|
373
|
-
`echo -n "${value}" | secret-tool store --label="exe-os master key" service "${
|
|
435
|
+
`echo -n "${value}" | secret-tool store --label="exe-os master key" service "${service}" account "${ACCOUNT}" 2>/dev/null`,
|
|
374
436
|
{ timeout: 5e3 }
|
|
375
437
|
);
|
|
376
438
|
return true;
|
|
@@ -378,11 +440,12 @@ function linuxSecretSet(value) {
|
|
|
378
440
|
return false;
|
|
379
441
|
}
|
|
380
442
|
}
|
|
381
|
-
function linuxSecretDelete() {
|
|
443
|
+
function linuxSecretDelete(service = SERVICE) {
|
|
444
|
+
if (!nativeKeychainAllowed()) return false;
|
|
382
445
|
if (process.platform !== "linux") return false;
|
|
383
446
|
try {
|
|
384
447
|
execSync(
|
|
385
|
-
`secret-tool clear service "${
|
|
448
|
+
`secret-tool clear service "${service}" account "${ACCOUNT}" 2>/dev/null`,
|
|
386
449
|
{ timeout: 5e3 }
|
|
387
450
|
);
|
|
388
451
|
return true;
|
|
@@ -391,6 +454,7 @@ function linuxSecretDelete() {
|
|
|
391
454
|
}
|
|
392
455
|
}
|
|
393
456
|
async function tryKeytar() {
|
|
457
|
+
if (!nativeKeychainAllowed()) return null;
|
|
394
458
|
try {
|
|
395
459
|
return await import("keytar");
|
|
396
460
|
} catch {
|
|
@@ -415,8 +479,8 @@ function deriveMachineKey() {
|
|
|
415
479
|
}
|
|
416
480
|
function readMachineId() {
|
|
417
481
|
try {
|
|
418
|
-
const { readFileSync:
|
|
419
|
-
return
|
|
482
|
+
const { readFileSync: readFileSync14 } = __require("fs");
|
|
483
|
+
return readFileSync14("/etc/machine-id", "utf-8").trim();
|
|
420
484
|
} catch {
|
|
421
485
|
return "";
|
|
422
486
|
}
|
|
@@ -464,7 +528,19 @@ async function writeMachineBoundFileFallback(b64) {
|
|
|
464
528
|
return "plaintext";
|
|
465
529
|
}
|
|
466
530
|
async function getMasterKey() {
|
|
467
|
-
|
|
531
|
+
let nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
532
|
+
if (!nativeValue) {
|
|
533
|
+
const legacyValue = macKeychainGet(LEGACY_SERVICE) ?? linuxSecretGet(LEGACY_SERVICE);
|
|
534
|
+
if (legacyValue) {
|
|
535
|
+
const migrated = macKeychainSet(legacyValue) || linuxSecretSet(legacyValue);
|
|
536
|
+
if (migrated) {
|
|
537
|
+
macKeychainDelete(LEGACY_SERVICE);
|
|
538
|
+
linuxSecretDelete(LEGACY_SERVICE);
|
|
539
|
+
process.stderr.write("[keychain] Migrated keychain service from exe-mem to exe-os.\n");
|
|
540
|
+
}
|
|
541
|
+
nativeValue = legacyValue;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
468
544
|
if (nativeValue) {
|
|
469
545
|
return Buffer.from(nativeValue, "base64");
|
|
470
546
|
}
|
|
@@ -472,12 +548,17 @@ async function getMasterKey() {
|
|
|
472
548
|
if (keytar) {
|
|
473
549
|
try {
|
|
474
550
|
const keytarValue = await keytar.getPassword(SERVICE, ACCOUNT);
|
|
475
|
-
|
|
476
|
-
|
|
551
|
+
const legacyKeytarValue = keytarValue ?? await keytar.getPassword(LEGACY_SERVICE, ACCOUNT);
|
|
552
|
+
if (legacyKeytarValue) {
|
|
553
|
+
const migrated = macKeychainSet(legacyKeytarValue) || linuxSecretSet(legacyKeytarValue);
|
|
477
554
|
if (migrated) {
|
|
478
555
|
process.stderr.write("[keychain] Migrated key from keytar to native keychain.\n");
|
|
556
|
+
try {
|
|
557
|
+
await keytar.deletePassword(LEGACY_SERVICE, ACCOUNT);
|
|
558
|
+
} catch {
|
|
559
|
+
}
|
|
479
560
|
}
|
|
480
|
-
return Buffer.from(
|
|
561
|
+
return Buffer.from(legacyKeytarValue, "base64");
|
|
481
562
|
}
|
|
482
563
|
} catch {
|
|
483
564
|
}
|
|
@@ -502,7 +583,7 @@ async function getMasterKey() {
|
|
|
502
583
|
const decrypted = decryptWithMachineKey(content, machineKey);
|
|
503
584
|
if (!decrypted) {
|
|
504
585
|
process.stderr.write(
|
|
505
|
-
"[keychain] Key decryption failed \u2014 machine may have changed.\n Use your 24-word recovery phrase: exe-os
|
|
586
|
+
"[keychain] Key decryption failed \u2014 machine may have changed.\n Use your 24-word recovery phrase during setup: exe-os setup\n"
|
|
506
587
|
);
|
|
507
588
|
return null;
|
|
508
589
|
}
|
|
@@ -511,6 +592,9 @@ async function getMasterKey() {
|
|
|
511
592
|
b64Value = content;
|
|
512
593
|
}
|
|
513
594
|
const key = Buffer.from(b64Value, "base64");
|
|
595
|
+
if (!content.startsWith(ENCRYPTED_PREFIX) && isRootOnlyTrustedServerKeyFile(keyPath)) {
|
|
596
|
+
return key;
|
|
597
|
+
}
|
|
514
598
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
515
599
|
if (migrated) {
|
|
516
600
|
process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
|
|
@@ -538,6 +622,97 @@ async function getMasterKey() {
|
|
|
538
622
|
return null;
|
|
539
623
|
}
|
|
540
624
|
}
|
|
625
|
+
async function getKeyStorageInfo() {
|
|
626
|
+
if (macKeychainGet()) {
|
|
627
|
+
return {
|
|
628
|
+
kind: "macos-keychain",
|
|
629
|
+
secure: true,
|
|
630
|
+
note: "stored in macOS Keychain via built-in security CLI"
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
if (macKeychainGet(LEGACY_SERVICE)) {
|
|
634
|
+
return {
|
|
635
|
+
kind: "macos-keychain",
|
|
636
|
+
secure: true,
|
|
637
|
+
note: "stored in legacy macOS Keychain service exe-mem; next key read migrates it to exe-os"
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
if (linuxSecretGet()) {
|
|
641
|
+
return {
|
|
642
|
+
kind: "linux-secret-service",
|
|
643
|
+
secure: true,
|
|
644
|
+
note: "stored in Linux Secret Service via secret-tool"
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
if (linuxSecretGet(LEGACY_SERVICE)) {
|
|
648
|
+
return {
|
|
649
|
+
kind: "linux-secret-service",
|
|
650
|
+
secure: true,
|
|
651
|
+
note: "stored in legacy Linux Secret Service service exe-mem; next key read migrates it to exe-os"
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
const keytar = await tryKeytar();
|
|
655
|
+
if (keytar) {
|
|
656
|
+
try {
|
|
657
|
+
if (await keytar.getPassword(SERVICE, ACCOUNT)) {
|
|
658
|
+
return {
|
|
659
|
+
kind: "legacy-keytar",
|
|
660
|
+
secure: true,
|
|
661
|
+
note: "stored in legacy keytar backend; will migrate to native keychain when possible"
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
if (await keytar.getPassword(LEGACY_SERVICE, ACCOUNT)) {
|
|
665
|
+
return {
|
|
666
|
+
kind: "legacy-keytar",
|
|
667
|
+
secure: true,
|
|
668
|
+
note: "stored in legacy keytar service exe-mem; will migrate to native exe-os keychain when possible"
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
} catch {
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
const keyPath = getKeyPath();
|
|
675
|
+
if (!existsSync3(keyPath)) {
|
|
676
|
+
return {
|
|
677
|
+
kind: "missing",
|
|
678
|
+
secure: false,
|
|
679
|
+
path: keyPath,
|
|
680
|
+
note: "no key found in OS keychain, legacy keytar, or file fallback"
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
try {
|
|
684
|
+
const content = (await readFile2(keyPath, "utf-8")).trim();
|
|
685
|
+
if (content.startsWith(ENCRYPTED_PREFIX)) {
|
|
686
|
+
return {
|
|
687
|
+
kind: "encrypted-file",
|
|
688
|
+
secure: true,
|
|
689
|
+
path: keyPath,
|
|
690
|
+
note: "stored in machine-bound encrypted file fallback"
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
if (isRootOnlyTrustedServerKeyFile(keyPath)) {
|
|
694
|
+
return {
|
|
695
|
+
kind: "server-secret-file",
|
|
696
|
+
secure: true,
|
|
697
|
+
path: keyPath,
|
|
698
|
+
note: "stored as root-only trusted server secret file"
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
return {
|
|
702
|
+
kind: "plaintext-file",
|
|
703
|
+
secure: false,
|
|
704
|
+
path: keyPath,
|
|
705
|
+
note: "stored in legacy plaintext file; reading it will migrate or encrypt it"
|
|
706
|
+
};
|
|
707
|
+
} catch {
|
|
708
|
+
return {
|
|
709
|
+
kind: "missing",
|
|
710
|
+
secure: false,
|
|
711
|
+
path: keyPath,
|
|
712
|
+
note: "key file exists but could not be read"
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
}
|
|
541
716
|
async function setMasterKey(key) {
|
|
542
717
|
const b64 = key.toString("base64");
|
|
543
718
|
if (macKeychainSet(b64) || linuxSecretSet(b64)) {
|
|
@@ -563,10 +738,13 @@ async function setMasterKey(key) {
|
|
|
563
738
|
async function deleteMasterKey() {
|
|
564
739
|
macKeychainDelete();
|
|
565
740
|
linuxSecretDelete();
|
|
741
|
+
macKeychainDelete(LEGACY_SERVICE);
|
|
742
|
+
linuxSecretDelete(LEGACY_SERVICE);
|
|
566
743
|
const keytar = await tryKeytar();
|
|
567
744
|
if (keytar) {
|
|
568
745
|
try {
|
|
569
746
|
await keytar.deletePassword(SERVICE, ACCOUNT);
|
|
747
|
+
await keytar.deletePassword(LEGACY_SERVICE, ACCOUNT);
|
|
570
748
|
} catch {
|
|
571
749
|
}
|
|
572
750
|
}
|
|
@@ -604,12 +782,14 @@ async function importMnemonic(mnemonic) {
|
|
|
604
782
|
const entropy = mnemonicToEntropy(trimmed);
|
|
605
783
|
return Buffer.from(entropy, "hex");
|
|
606
784
|
}
|
|
607
|
-
var SERVICE, ACCOUNT, ENCRYPTED_PREFIX;
|
|
785
|
+
var SERVICE, LEGACY_SERVICE, ACCOUNT, linuxSecretAvailability, ENCRYPTED_PREFIX;
|
|
608
786
|
var init_keychain = __esm({
|
|
609
787
|
"src/lib/keychain.ts"() {
|
|
610
788
|
"use strict";
|
|
611
|
-
SERVICE = "exe-
|
|
789
|
+
SERVICE = "exe-os";
|
|
790
|
+
LEGACY_SERVICE = "exe-mem";
|
|
612
791
|
ACCOUNT = "master-key";
|
|
792
|
+
linuxSecretAvailability = null;
|
|
613
793
|
ENCRYPTED_PREFIX = "enc:";
|
|
614
794
|
}
|
|
615
795
|
});
|
|
@@ -665,7 +845,7 @@ import net from "net";
|
|
|
665
845
|
import os3 from "os";
|
|
666
846
|
import { spawn } from "child_process";
|
|
667
847
|
import { randomUUID } from "crypto";
|
|
668
|
-
import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
|
|
848
|
+
import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync as statSync2 } from "fs";
|
|
669
849
|
import path5 from "path";
|
|
670
850
|
import { fileURLToPath } from "url";
|
|
671
851
|
function handleData(chunk) {
|
|
@@ -815,7 +995,7 @@ function acquireSpawnLock() {
|
|
|
815
995
|
return true;
|
|
816
996
|
} catch {
|
|
817
997
|
try {
|
|
818
|
-
const stat =
|
|
998
|
+
const stat = statSync2(SPAWN_LOCK_PATH);
|
|
819
999
|
if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
|
|
820
1000
|
try {
|
|
821
1001
|
unlinkSync2(SPAWN_LOCK_PATH);
|
|
@@ -976,7 +1156,7 @@ function killAndRespawnDaemon() {
|
|
|
976
1156
|
}
|
|
977
1157
|
function isDaemonTooYoung() {
|
|
978
1158
|
try {
|
|
979
|
-
const stat =
|
|
1159
|
+
const stat = statSync2(PID_PATH);
|
|
980
1160
|
return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
|
|
981
1161
|
} catch {
|
|
982
1162
|
return false;
|
|
@@ -1129,10 +1309,10 @@ async function disposeEmbedder() {
|
|
|
1129
1309
|
async function embedDirect(text) {
|
|
1130
1310
|
const llamaCpp = await import("node-llama-cpp");
|
|
1131
1311
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
1132
|
-
const { existsSync:
|
|
1133
|
-
const
|
|
1134
|
-
const modelPath =
|
|
1135
|
-
if (!
|
|
1312
|
+
const { existsSync: existsSync18 } = await import("fs");
|
|
1313
|
+
const path18 = await import("path");
|
|
1314
|
+
const modelPath = path18.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
1315
|
+
if (!existsSync18(modelPath)) {
|
|
1136
1316
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
1137
1317
|
}
|
|
1138
1318
|
const llama = await llamaCpp.getLlama();
|
|
@@ -1421,8 +1601,8 @@ async function validateLicense(apiKey, deviceId) {
|
|
|
1421
1601
|
}
|
|
1422
1602
|
function getCacheAgeMs() {
|
|
1423
1603
|
try {
|
|
1424
|
-
const { statSync:
|
|
1425
|
-
const s =
|
|
1604
|
+
const { statSync: statSync5 } = __require("fs");
|
|
1605
|
+
const s = statSync5(CACHE_PATH);
|
|
1426
1606
|
return Date.now() - s.mtimeMs;
|
|
1427
1607
|
} catch {
|
|
1428
1608
|
return Infinity;
|
|
@@ -2972,6 +3152,9 @@ function getClient() {
|
|
|
2972
3152
|
if (_daemonClient && _daemonClient._isDaemonActive()) {
|
|
2973
3153
|
return _daemonClient;
|
|
2974
3154
|
}
|
|
3155
|
+
if (!_resilientClient) {
|
|
3156
|
+
return _adapterClient;
|
|
3157
|
+
}
|
|
2975
3158
|
return _resilientClient;
|
|
2976
3159
|
}
|
|
2977
3160
|
async function initDaemonClient() {
|
|
@@ -4004,6 +4187,127 @@ async function ensureSchema() {
|
|
|
4004
4187
|
VALUES (new.rowid, new.content, new.subject, new.predicate, new.object);
|
|
4005
4188
|
END;
|
|
4006
4189
|
`);
|
|
4190
|
+
await client.executeMultiple(`
|
|
4191
|
+
CREATE TABLE IF NOT EXISTS agent_sessions (
|
|
4192
|
+
id TEXT PRIMARY KEY,
|
|
4193
|
+
agent_id TEXT NOT NULL,
|
|
4194
|
+
project_name TEXT,
|
|
4195
|
+
started_at TEXT NOT NULL,
|
|
4196
|
+
last_event_at TEXT NOT NULL,
|
|
4197
|
+
event_count INTEGER NOT NULL DEFAULT 0,
|
|
4198
|
+
properties TEXT DEFAULT '{}'
|
|
4199
|
+
);
|
|
4200
|
+
|
|
4201
|
+
CREATE INDEX IF NOT EXISTS idx_agent_sessions_agent_time
|
|
4202
|
+
ON agent_sessions(agent_id, started_at);
|
|
4203
|
+
|
|
4204
|
+
CREATE TABLE IF NOT EXISTS agent_goals (
|
|
4205
|
+
id TEXT PRIMARY KEY,
|
|
4206
|
+
statement TEXT NOT NULL,
|
|
4207
|
+
owner_agent_id TEXT,
|
|
4208
|
+
project_name TEXT,
|
|
4209
|
+
status TEXT NOT NULL DEFAULT 'open',
|
|
4210
|
+
priority INTEGER NOT NULL DEFAULT 5,
|
|
4211
|
+
success_criteria TEXT,
|
|
4212
|
+
parent_goal_id TEXT,
|
|
4213
|
+
due_at TEXT,
|
|
4214
|
+
achieved_at TEXT,
|
|
4215
|
+
supersedes_id TEXT,
|
|
4216
|
+
created_at TEXT NOT NULL,
|
|
4217
|
+
updated_at TEXT NOT NULL,
|
|
4218
|
+
source_memory_id TEXT
|
|
4219
|
+
);
|
|
4220
|
+
|
|
4221
|
+
CREATE INDEX IF NOT EXISTS idx_agent_goals_project_status
|
|
4222
|
+
ON agent_goals(project_name, status, priority);
|
|
4223
|
+
|
|
4224
|
+
CREATE TABLE IF NOT EXISTS agent_events (
|
|
4225
|
+
id TEXT PRIMARY KEY,
|
|
4226
|
+
event_type TEXT NOT NULL,
|
|
4227
|
+
occurred_at TEXT NOT NULL,
|
|
4228
|
+
sequence_index INTEGER NOT NULL,
|
|
4229
|
+
actor_agent_id TEXT,
|
|
4230
|
+
agent_role TEXT,
|
|
4231
|
+
project_name TEXT,
|
|
4232
|
+
session_id TEXT,
|
|
4233
|
+
task_id TEXT,
|
|
4234
|
+
goal_id TEXT,
|
|
4235
|
+
parent_event_id TEXT,
|
|
4236
|
+
intention TEXT,
|
|
4237
|
+
outcome TEXT,
|
|
4238
|
+
evidence_memory_id TEXT,
|
|
4239
|
+
impact TEXT,
|
|
4240
|
+
payload TEXT DEFAULT '{}',
|
|
4241
|
+
created_at TEXT NOT NULL
|
|
4242
|
+
);
|
|
4243
|
+
|
|
4244
|
+
CREATE INDEX IF NOT EXISTS idx_agent_events_time
|
|
4245
|
+
ON agent_events(occurred_at, sequence_index);
|
|
4246
|
+
|
|
4247
|
+
CREATE INDEX IF NOT EXISTS idx_agent_events_session_seq
|
|
4248
|
+
ON agent_events(session_id, sequence_index);
|
|
4249
|
+
|
|
4250
|
+
CREATE INDEX IF NOT EXISTS idx_agent_events_goal_time
|
|
4251
|
+
ON agent_events(goal_id, occurred_at);
|
|
4252
|
+
|
|
4253
|
+
CREATE INDEX IF NOT EXISTS idx_agent_events_memory
|
|
4254
|
+
ON agent_events(evidence_memory_id);
|
|
4255
|
+
|
|
4256
|
+
CREATE TABLE IF NOT EXISTS agent_goal_links (
|
|
4257
|
+
id TEXT PRIMARY KEY,
|
|
4258
|
+
goal_id TEXT NOT NULL,
|
|
4259
|
+
link_type TEXT NOT NULL,
|
|
4260
|
+
target_id TEXT NOT NULL,
|
|
4261
|
+
target_type TEXT NOT NULL,
|
|
4262
|
+
created_at TEXT NOT NULL
|
|
4263
|
+
);
|
|
4264
|
+
|
|
4265
|
+
CREATE INDEX IF NOT EXISTS idx_agent_goal_links_goal
|
|
4266
|
+
ON agent_goal_links(goal_id, target_type);
|
|
4267
|
+
|
|
4268
|
+
CREATE TABLE IF NOT EXISTS agent_semantic_labels (
|
|
4269
|
+
id TEXT PRIMARY KEY,
|
|
4270
|
+
source_memory_id TEXT NOT NULL,
|
|
4271
|
+
event_id TEXT,
|
|
4272
|
+
labeler TEXT NOT NULL,
|
|
4273
|
+
schema_version INTEGER NOT NULL DEFAULT 1,
|
|
4274
|
+
confidence REAL NOT NULL DEFAULT 0,
|
|
4275
|
+
labels TEXT NOT NULL,
|
|
4276
|
+
created_at TEXT NOT NULL,
|
|
4277
|
+
updated_at TEXT NOT NULL
|
|
4278
|
+
);
|
|
4279
|
+
|
|
4280
|
+
CREATE INDEX IF NOT EXISTS idx_agent_semantic_labels_memory
|
|
4281
|
+
ON agent_semantic_labels(source_memory_id, labeler);
|
|
4282
|
+
|
|
4283
|
+
CREATE INDEX IF NOT EXISTS idx_agent_semantic_labels_event
|
|
4284
|
+
ON agent_semantic_labels(event_id);
|
|
4285
|
+
|
|
4286
|
+
CREATE TABLE IF NOT EXISTS agent_reflection_checkpoints (
|
|
4287
|
+
id TEXT PRIMARY KEY,
|
|
4288
|
+
project_name TEXT,
|
|
4289
|
+
session_id TEXT,
|
|
4290
|
+
window_start_at TEXT NOT NULL,
|
|
4291
|
+
window_end_at TEXT NOT NULL,
|
|
4292
|
+
event_count INTEGER NOT NULL DEFAULT 0,
|
|
4293
|
+
goal_count INTEGER NOT NULL DEFAULT 0,
|
|
4294
|
+
success_count INTEGER NOT NULL DEFAULT 0,
|
|
4295
|
+
failure_count INTEGER NOT NULL DEFAULT 0,
|
|
4296
|
+
risk_count INTEGER NOT NULL DEFAULT 0,
|
|
4297
|
+
summary TEXT NOT NULL,
|
|
4298
|
+
learnings TEXT NOT NULL DEFAULT '[]',
|
|
4299
|
+
next_actions TEXT NOT NULL DEFAULT '[]',
|
|
4300
|
+
evidence_event_ids TEXT NOT NULL DEFAULT '[]',
|
|
4301
|
+
confidence REAL NOT NULL DEFAULT 0,
|
|
4302
|
+
created_at TEXT NOT NULL
|
|
4303
|
+
);
|
|
4304
|
+
|
|
4305
|
+
CREATE INDEX IF NOT EXISTS idx_agent_reflection_project_time
|
|
4306
|
+
ON agent_reflection_checkpoints(project_name, window_end_at);
|
|
4307
|
+
|
|
4308
|
+
CREATE INDEX IF NOT EXISTS idx_agent_reflection_session_time
|
|
4309
|
+
ON agent_reflection_checkpoints(session_id, window_end_at);
|
|
4310
|
+
`);
|
|
4007
4311
|
try {
|
|
4008
4312
|
await client.execute({
|
|
4009
4313
|
sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
|
|
@@ -4408,7 +4712,7 @@ __export(db_backup_exports, {
|
|
|
4408
4712
|
listBackups: () => listBackups,
|
|
4409
4713
|
rotateBackups: () => rotateBackups
|
|
4410
4714
|
});
|
|
4411
|
-
import { copyFileSync, existsSync as existsSync11, mkdirSync as mkdirSync4, readdirSync, unlinkSync as unlinkSync5, statSync as
|
|
4715
|
+
import { copyFileSync, existsSync as existsSync11, mkdirSync as mkdirSync4, readdirSync, unlinkSync as unlinkSync5, statSync as statSync3 } from "fs";
|
|
4412
4716
|
import path11 from "path";
|
|
4413
4717
|
function findActiveDb() {
|
|
4414
4718
|
for (const name of DB_NAMES) {
|
|
@@ -4452,7 +4756,7 @@ function rotateBackups(keepDays = DEFAULT_KEEP_DAYS) {
|
|
|
4452
4756
|
if (!file.endsWith(".db") && !file.endsWith(".db-wal") && !file.endsWith(".db-shm")) continue;
|
|
4453
4757
|
const filePath = path11.join(BACKUP_DIR, file);
|
|
4454
4758
|
try {
|
|
4455
|
-
const stat =
|
|
4759
|
+
const stat = statSync3(filePath);
|
|
4456
4760
|
if (stat.mtimeMs < cutoff) {
|
|
4457
4761
|
unlinkSync5(filePath);
|
|
4458
4762
|
deleted++;
|
|
@@ -4470,7 +4774,7 @@ function listBackups() {
|
|
|
4470
4774
|
const files = readdirSync(BACKUP_DIR).filter((f) => f.endsWith(".db") && !f.endsWith("-wal") && !f.endsWith("-shm"));
|
|
4471
4775
|
return files.map((name) => {
|
|
4472
4776
|
const p = path11.join(BACKUP_DIR, name);
|
|
4473
|
-
const stat =
|
|
4777
|
+
const stat = statSync3(p);
|
|
4474
4778
|
return { path: p, name, size: stat.size, date: stat.mtime };
|
|
4475
4779
|
}).sort((a, b) => b.date.getTime() - a.date.getTime());
|
|
4476
4780
|
} catch {
|
|
@@ -4503,8 +4807,10 @@ var init_db_backup = __esm({
|
|
|
4503
4807
|
// src/lib/cloud-sync.ts
|
|
4504
4808
|
var cloud_sync_exports = {};
|
|
4505
4809
|
__export(cloud_sync_exports, {
|
|
4810
|
+
CLOUD_REUPLOAD_REQUIRED_MESSAGE: () => CLOUD_REUPLOAD_REQUIRED_MESSAGE,
|
|
4506
4811
|
assertSecureEndpoint: () => assertSecureEndpoint,
|
|
4507
4812
|
buildRosterBlob: () => buildRosterBlob,
|
|
4813
|
+
clearCloudReuploadRequired: () => clearCloudReuploadRequired,
|
|
4508
4814
|
cloudPull: () => cloudPull,
|
|
4509
4815
|
cloudPullBehaviors: () => cloudPullBehaviors,
|
|
4510
4816
|
cloudPullBlob: () => cloudPullBlob,
|
|
@@ -4523,13 +4829,16 @@ __export(cloud_sync_exports, {
|
|
|
4523
4829
|
cloudPushGraphRAG: () => cloudPushGraphRAG,
|
|
4524
4830
|
cloudPushRoster: () => cloudPushRoster,
|
|
4525
4831
|
cloudPushTasks: () => cloudPushTasks,
|
|
4832
|
+
cloudResetMemoryBlobs: () => cloudResetMemoryBlobs,
|
|
4526
4833
|
cloudSync: () => cloudSync,
|
|
4834
|
+
getCloudReuploadRequired: () => getCloudReuploadRequired,
|
|
4835
|
+
markCloudReuploadRequired: () => markCloudReuploadRequired,
|
|
4527
4836
|
mergeConfig: () => mergeConfig,
|
|
4528
4837
|
mergeRosterFromRemote: () => mergeRosterFromRemote,
|
|
4529
4838
|
pushToPostgres: () => pushToPostgres,
|
|
4530
4839
|
recordRosterDeletion: () => recordRosterDeletion
|
|
4531
4840
|
});
|
|
4532
|
-
import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync12, readdirSync as readdirSync2, mkdirSync as mkdirSync5, appendFileSync, unlinkSync as unlinkSync6, openSync as openSync2, closeSync as closeSync2, statSync as
|
|
4841
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync12, readdirSync as readdirSync2, mkdirSync as mkdirSync5, appendFileSync, unlinkSync as unlinkSync6, openSync as openSync2, closeSync as closeSync2, statSync as statSync4 } from "fs";
|
|
4533
4842
|
import crypto3 from "crypto";
|
|
4534
4843
|
import path12 from "path";
|
|
4535
4844
|
import { homedir as homedir2 } from "os";
|
|
@@ -4576,18 +4885,36 @@ function loadPgClient() {
|
|
|
4576
4885
|
const { pathToFileURL: pathToFileURL3 } = await import("url");
|
|
4577
4886
|
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
4578
4887
|
if (explicitPath) {
|
|
4579
|
-
const
|
|
4580
|
-
const
|
|
4581
|
-
if (!
|
|
4582
|
-
return new
|
|
4888
|
+
const mod = await import(pathToFileURL3(explicitPath).href);
|
|
4889
|
+
const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
|
|
4890
|
+
if (!Ctor) throw new Error(`No PrismaClient at ${explicitPath}`);
|
|
4891
|
+
return new Ctor();
|
|
4583
4892
|
}
|
|
4584
4893
|
const exeDbRoot = process.env.EXE_DB_ROOT ?? path12.join(homedir2(), "exe-db");
|
|
4585
|
-
const
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4894
|
+
const packagePath = path12.join(exeDbRoot, "package.json");
|
|
4895
|
+
if (existsSync12(packagePath)) {
|
|
4896
|
+
const req = createRequire3(packagePath);
|
|
4897
|
+
const entry = req.resolve("@prisma/client");
|
|
4898
|
+
const mod = await import(pathToFileURL3(entry).href);
|
|
4899
|
+
const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
|
|
4900
|
+
if (!Ctor) throw new Error("No PrismaClient");
|
|
4901
|
+
return new Ctor();
|
|
4902
|
+
}
|
|
4903
|
+
const { Pool } = await import("pg");
|
|
4904
|
+
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
4905
|
+
return {
|
|
4906
|
+
async $queryRawUnsafe(query, ...values) {
|
|
4907
|
+
const result = await pool.query(query, values);
|
|
4908
|
+
return result.rows;
|
|
4909
|
+
},
|
|
4910
|
+
async $executeRawUnsafe(query, ...values) {
|
|
4911
|
+
const result = await pool.query(query, values);
|
|
4912
|
+
return result.rowCount ?? 0;
|
|
4913
|
+
},
|
|
4914
|
+
async $disconnect() {
|
|
4915
|
+
await pool.end();
|
|
4916
|
+
}
|
|
4917
|
+
};
|
|
4591
4918
|
})().catch(() => {
|
|
4592
4919
|
_pgFailed = true;
|
|
4593
4920
|
_pgPromise = null;
|
|
@@ -4608,7 +4935,7 @@ async function pushToPostgres(records) {
|
|
|
4608
4935
|
let inserted = 0;
|
|
4609
4936
|
for (const rec of records) {
|
|
4610
4937
|
try {
|
|
4611
|
-
await prisma.$executeRawUnsafe(
|
|
4938
|
+
const changed = await prisma.$executeRawUnsafe(
|
|
4612
4939
|
`INSERT INTO raw.raw_events (id, source, source_id, event_type, payload, metadata, timestamp)
|
|
4613
4940
|
VALUES (gen_random_uuid(), 'cloud_sync', $1, 'memory', $2::jsonb, $3::jsonb, $4)
|
|
4614
4941
|
ON CONFLICT (source, source_id, event_type) DO NOTHING`,
|
|
@@ -4617,7 +4944,7 @@ async function pushToPostgres(records) {
|
|
|
4617
4944
|
JSON.stringify({ agent_id: rec.agent_id, project_name: rec.project_name, tool_name: rec.tool_name }),
|
|
4618
4945
|
rec.timestamp ? new Date(String(rec.timestamp)) : /* @__PURE__ */ new Date()
|
|
4619
4946
|
);
|
|
4620
|
-
inserted
|
|
4947
|
+
inserted += Number(changed ?? 0);
|
|
4621
4948
|
} catch {
|
|
4622
4949
|
}
|
|
4623
4950
|
}
|
|
@@ -4722,6 +5049,23 @@ async function cloudPush(records, maxVersion, config) {
|
|
|
4722
5049
|
return false;
|
|
4723
5050
|
}
|
|
4724
5051
|
}
|
|
5052
|
+
async function cloudResetMemoryBlobs(config) {
|
|
5053
|
+
assertSecureEndpoint(config.endpoint);
|
|
5054
|
+
const resp = await fetchWithRetry(`${config.endpoint}/sync/reset-memory`, {
|
|
5055
|
+
method: "POST",
|
|
5056
|
+
headers: {
|
|
5057
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
5058
|
+
"Content-Type": "application/json",
|
|
5059
|
+
"X-Device-Id": loadDeviceId()
|
|
5060
|
+
},
|
|
5061
|
+
body: JSON.stringify({ confirm: "LOCAL DB IS SOURCE OF TRUTH" })
|
|
5062
|
+
});
|
|
5063
|
+
if (!resp.ok) {
|
|
5064
|
+
throw new Error(`cloud reset failed: HTTP ${resp.status}`);
|
|
5065
|
+
}
|
|
5066
|
+
const data = await resp.json();
|
|
5067
|
+
return { deleted: Number(data.deleted ?? 0), freedBytes: Number(data.freed_bytes ?? 0) };
|
|
5068
|
+
}
|
|
4725
5069
|
async function cloudPull(sinceVersion, config) {
|
|
4726
5070
|
assertSecureEndpoint(config.endpoint);
|
|
4727
5071
|
try {
|
|
@@ -4741,22 +5085,61 @@ async function cloudPull(sinceVersion, config) {
|
|
|
4741
5085
|
if (!response.ok) return { records: [], maxVersion: sinceVersion };
|
|
4742
5086
|
const data = await response.json();
|
|
4743
5087
|
const allRecords = [];
|
|
4744
|
-
|
|
5088
|
+
let maxReadableVersion = sinceVersion;
|
|
5089
|
+
let skippedBlobs = 0;
|
|
5090
|
+
for (const { version, blob } of data.blobs ?? []) {
|
|
4745
5091
|
try {
|
|
4746
5092
|
const compressed = decryptSyncBlob(blob);
|
|
4747
5093
|
const json = decompress(compressed).toString("utf8");
|
|
4748
5094
|
const records = JSON.parse(json);
|
|
4749
5095
|
allRecords.push(...records);
|
|
5096
|
+
const recordMax = records.reduce((max, rec) => {
|
|
5097
|
+
const v = Number(rec.version ?? 0);
|
|
5098
|
+
return Number.isFinite(v) ? Math.max(max, v) : max;
|
|
5099
|
+
}, 0);
|
|
5100
|
+
const blobVersion = Number(version ?? 0);
|
|
5101
|
+
maxReadableVersion = Math.max(
|
|
5102
|
+
maxReadableVersion,
|
|
5103
|
+
Number.isFinite(blobVersion) ? blobVersion : 0,
|
|
5104
|
+
recordMax
|
|
5105
|
+
);
|
|
4750
5106
|
} catch {
|
|
5107
|
+
skippedBlobs++;
|
|
4751
5108
|
continue;
|
|
4752
5109
|
}
|
|
4753
5110
|
}
|
|
4754
|
-
|
|
5111
|
+
if (skippedBlobs > 0) {
|
|
5112
|
+
logError(`[cloud-sync] PULL skipped ${skippedBlobs} undecryptable blob(s); pull cursor advanced only to last readable version ${maxReadableVersion}`);
|
|
5113
|
+
}
|
|
5114
|
+
return { records: allRecords, maxVersion: maxReadableVersion };
|
|
4755
5115
|
} catch (err) {
|
|
4756
5116
|
logError(`[cloud-sync] PULL FAILED: ${err instanceof Error ? err.message : String(err)}`);
|
|
4757
5117
|
return { records: [], maxVersion: sinceVersion };
|
|
4758
5118
|
}
|
|
4759
5119
|
}
|
|
5120
|
+
async function getCloudReuploadRequired(client = getClient()) {
|
|
5121
|
+
try {
|
|
5122
|
+
await client.execute("CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
|
|
5123
|
+
const result = await client.execute("SELECT key, value FROM sync_meta WHERE key IN ('cloud_reupload_required', 'cloud_relink_required')");
|
|
5124
|
+
return result.rows.some((row) => String(row.value ?? "") === "1");
|
|
5125
|
+
} catch {
|
|
5126
|
+
return false;
|
|
5127
|
+
}
|
|
5128
|
+
}
|
|
5129
|
+
async function clearCloudReuploadRequired(client = getClient()) {
|
|
5130
|
+
await client.execute("CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
|
|
5131
|
+
await client.execute("INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_reupload_required', '0')");
|
|
5132
|
+
await client.execute("INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_relink_required', '0')");
|
|
5133
|
+
await client.execute({
|
|
5134
|
+
sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_reuploaded_at', ?)",
|
|
5135
|
+
args: [(/* @__PURE__ */ new Date()).toISOString()]
|
|
5136
|
+
});
|
|
5137
|
+
await client.execute("DELETE FROM sync_meta WHERE key IN ('last_cloud_pull_version', 'last_cloud_push_version')");
|
|
5138
|
+
}
|
|
5139
|
+
async function markCloudReuploadRequired(client = getClient()) {
|
|
5140
|
+
await client.execute("CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
|
|
5141
|
+
await client.execute("INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('cloud_reupload_required', '1')");
|
|
5142
|
+
}
|
|
4760
5143
|
async function cloudSync(config) {
|
|
4761
5144
|
if (!isSyncCryptoInitialized()) {
|
|
4762
5145
|
try {
|
|
@@ -4778,13 +5161,10 @@ async function cloudSync(config) {
|
|
|
4778
5161
|
throw new Error("[cloud-sync] Database not initialized. Call initStore() before cloudSync().");
|
|
4779
5162
|
}
|
|
4780
5163
|
try {
|
|
4781
|
-
|
|
4782
|
-
if (String(relink.rows[0]?.value ?? "") === "1") {
|
|
4783
|
-
throw new Error("[cloud-sync] Paused after key rotation. Re-link/reupload cloud sync with the new recovery phrase before syncing.");
|
|
4784
|
-
}
|
|
5164
|
+
if (await getCloudReuploadRequired(client)) throw new Error(CLOUD_REUPLOAD_REQUIRED_MESSAGE);
|
|
4785
5165
|
} catch (err) {
|
|
4786
5166
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4787
|
-
if (msg.includes("
|
|
5167
|
+
if (msg === CLOUD_REUPLOAD_REQUIRED_MESSAGE || msg.includes("key rotation")) throw err;
|
|
4788
5168
|
}
|
|
4789
5169
|
try {
|
|
4790
5170
|
const { getRawClient: getRawClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
@@ -5042,7 +5422,7 @@ async function cloudSync(config) {
|
|
|
5042
5422
|
const { getLatestBackup: getLatestBackup2 } = await Promise.resolve().then(() => (init_db_backup(), db_backup_exports));
|
|
5043
5423
|
const latestBackup = getLatestBackup2();
|
|
5044
5424
|
if (latestBackup) {
|
|
5045
|
-
const backupSize =
|
|
5425
|
+
const backupSize = statSync4(latestBackup).size;
|
|
5046
5426
|
const MAX_CLOUD_BACKUP_BYTES = 50 * 1024 * 1024;
|
|
5047
5427
|
if (backupSize <= MAX_CLOUD_BACKUP_BYTES) {
|
|
5048
5428
|
const backupData = readFileSync8(latestBackup);
|
|
@@ -5713,7 +6093,7 @@ async function cloudPullDocuments(config) {
|
|
|
5713
6093
|
}
|
|
5714
6094
|
return { pulled };
|
|
5715
6095
|
}
|
|
5716
|
-
var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, _pgPromise, _pgFailed, ROSTER_DELETIONS_PATH;
|
|
6096
|
+
var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, _pgPromise, _pgFailed, CLOUD_REUPLOAD_REQUIRED_MESSAGE, ROSTER_DELETIONS_PATH;
|
|
5717
6097
|
var init_cloud_sync = __esm({
|
|
5718
6098
|
"src/lib/cloud-sync.ts"() {
|
|
5719
6099
|
"use strict";
|
|
@@ -5732,43 +6112,194 @@ var init_cloud_sync = __esm({
|
|
|
5732
6112
|
LOCK_STALE_MS = 3e4;
|
|
5733
6113
|
_pgPromise = null;
|
|
5734
6114
|
_pgFailed = false;
|
|
6115
|
+
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.";
|
|
5735
6116
|
ROSTER_DELETIONS_PATH = path12.join(EXE_AI_DIR, "roster-deletions.json");
|
|
5736
6117
|
}
|
|
5737
6118
|
});
|
|
5738
6119
|
|
|
6120
|
+
// src/lib/key-backup-status.ts
|
|
6121
|
+
var key_backup_status_exports = {};
|
|
6122
|
+
__export(key_backup_status_exports, {
|
|
6123
|
+
getKeyBackupStatus: () => getKeyBackupStatus,
|
|
6124
|
+
keyBackupMarkerPath: () => keyBackupMarkerPath,
|
|
6125
|
+
markKeyBackupConfirmed: () => markKeyBackupConfirmed
|
|
6126
|
+
});
|
|
6127
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync6, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
|
|
6128
|
+
import path13 from "path";
|
|
6129
|
+
function keyBackupMarkerPath() {
|
|
6130
|
+
return path13.join(EXE_AI_DIR, "key-backup-confirmed.json");
|
|
6131
|
+
}
|
|
6132
|
+
function getKeyBackupStatus() {
|
|
6133
|
+
const marker = keyBackupMarkerPath();
|
|
6134
|
+
if (!existsSync13(marker)) return { exists: false };
|
|
6135
|
+
try {
|
|
6136
|
+
const parsed = JSON.parse(readFileSync9(marker, "utf8"));
|
|
6137
|
+
return {
|
|
6138
|
+
exists: true,
|
|
6139
|
+
confirmedAt: parsed.confirmedAt,
|
|
6140
|
+
source: parsed.source
|
|
6141
|
+
};
|
|
6142
|
+
} catch {
|
|
6143
|
+
return { exists: true };
|
|
6144
|
+
}
|
|
6145
|
+
}
|
|
6146
|
+
function markKeyBackupConfirmed(source) {
|
|
6147
|
+
mkdirSync6(EXE_AI_DIR, { recursive: true, mode: 448 });
|
|
6148
|
+
writeFileSync7(
|
|
6149
|
+
keyBackupMarkerPath(),
|
|
6150
|
+
JSON.stringify({ confirmedAt: (/* @__PURE__ */ new Date()).toISOString(), source }, null, 2) + "\n",
|
|
6151
|
+
{ mode: 384 }
|
|
6152
|
+
);
|
|
6153
|
+
}
|
|
6154
|
+
var init_key_backup_status = __esm({
|
|
6155
|
+
"src/lib/key-backup-status.ts"() {
|
|
6156
|
+
"use strict";
|
|
6157
|
+
init_config();
|
|
6158
|
+
}
|
|
6159
|
+
});
|
|
6160
|
+
|
|
6161
|
+
// src/lib/orchestration-phase.ts
|
|
6162
|
+
var orchestration_phase_exports = {};
|
|
6163
|
+
__export(orchestration_phase_exports, {
|
|
6164
|
+
ORCHESTRATION_PHASES: () => ORCHESTRATION_PHASES,
|
|
6165
|
+
applyDefaultOrchestrationPhase: () => applyDefaultOrchestrationPhase,
|
|
6166
|
+
getConfigOrchestrationPhase: () => getConfigOrchestrationPhase,
|
|
6167
|
+
getOrchestrationPhaseInfo: () => getOrchestrationPhaseInfo,
|
|
6168
|
+
loadOrchestrationPhase: () => loadOrchestrationPhase,
|
|
6169
|
+
normalizeOrchestrationPhase: () => normalizeOrchestrationPhase,
|
|
6170
|
+
phaseReminderLine: () => phaseReminderLine,
|
|
6171
|
+
setOrchestrationPhase: () => setOrchestrationPhase
|
|
6172
|
+
});
|
|
6173
|
+
function normalizeOrchestrationPhase(input) {
|
|
6174
|
+
if (typeof input !== "string") return "phase_1_coo";
|
|
6175
|
+
const normalized = input.trim().toLowerCase().replace(/\s+/g, "_");
|
|
6176
|
+
if (["1", "phase1", "phase_1", "coo", "coo_mode", "chief_of_staff", "phase_1_coo"].includes(normalized)) {
|
|
6177
|
+
return "phase_1_coo";
|
|
6178
|
+
}
|
|
6179
|
+
if (["2", "phase2", "phase_2", "executives", "executive", "executive_bench", "phase_2_executives"].includes(normalized)) {
|
|
6180
|
+
return "phase_2_executives";
|
|
6181
|
+
}
|
|
6182
|
+
if (["3", "phase3", "phase_3", "parallel", "parallel_org", "parallel_execution", "phase_3_parallel_org"].includes(normalized)) {
|
|
6183
|
+
return "phase_3_parallel_org";
|
|
6184
|
+
}
|
|
6185
|
+
if (ORCHESTRATION_PHASES.includes(normalized)) return normalized;
|
|
6186
|
+
return "phase_1_coo";
|
|
6187
|
+
}
|
|
6188
|
+
function getOrchestrationPhaseInfo(phase) {
|
|
6189
|
+
return PHASE_INFO[normalizeOrchestrationPhase(phase)];
|
|
6190
|
+
}
|
|
6191
|
+
function getConfigOrchestrationPhase(config) {
|
|
6192
|
+
const orchestration = config.orchestration;
|
|
6193
|
+
return normalizeOrchestrationPhase(orchestration?.phase);
|
|
6194
|
+
}
|
|
6195
|
+
function applyDefaultOrchestrationPhase(config) {
|
|
6196
|
+
if (!config.orchestration) {
|
|
6197
|
+
config.orchestration = {
|
|
6198
|
+
phase: "phase_1_coo",
|
|
6199
|
+
phaseSetAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6200
|
+
phaseSetBy: "setup-default"
|
|
6201
|
+
};
|
|
6202
|
+
return config;
|
|
6203
|
+
}
|
|
6204
|
+
if (!config.orchestration.phase) {
|
|
6205
|
+
config.orchestration = {
|
|
6206
|
+
...config.orchestration,
|
|
6207
|
+
phase: "phase_1_coo",
|
|
6208
|
+
phaseSetAt: config.orchestration.phaseSetAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
6209
|
+
phaseSetBy: config.orchestration.phaseSetBy ?? "setup-default"
|
|
6210
|
+
};
|
|
6211
|
+
}
|
|
6212
|
+
return config;
|
|
6213
|
+
}
|
|
6214
|
+
async function loadOrchestrationPhase() {
|
|
6215
|
+
const config = await loadConfig();
|
|
6216
|
+
return getOrchestrationPhaseInfo(config.orchestration?.phase);
|
|
6217
|
+
}
|
|
6218
|
+
async function setOrchestrationPhase(phaseInput, setBy = "user") {
|
|
6219
|
+
const config = await loadConfig();
|
|
6220
|
+
const phase = normalizeOrchestrationPhase(phaseInput);
|
|
6221
|
+
config.orchestration = {
|
|
6222
|
+
...config.orchestration ?? {},
|
|
6223
|
+
phase,
|
|
6224
|
+
phaseSetAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6225
|
+
phaseSetBy: setBy
|
|
6226
|
+
};
|
|
6227
|
+
await saveConfig(config);
|
|
6228
|
+
return getOrchestrationPhaseInfo(phase);
|
|
6229
|
+
}
|
|
6230
|
+
function phaseReminderLine(phase) {
|
|
6231
|
+
const info = getOrchestrationPhaseInfo(phase);
|
|
6232
|
+
return `${info.shortLabel}: ${info.focus}. ${info.nextSuggestion}`;
|
|
6233
|
+
}
|
|
6234
|
+
var ORCHESTRATION_PHASES, PHASE_INFO;
|
|
6235
|
+
var init_orchestration_phase = __esm({
|
|
6236
|
+
"src/lib/orchestration-phase.ts"() {
|
|
6237
|
+
"use strict";
|
|
6238
|
+
init_config();
|
|
6239
|
+
ORCHESTRATION_PHASES = [
|
|
6240
|
+
"phase_1_coo",
|
|
6241
|
+
"phase_2_executives",
|
|
6242
|
+
"phase_3_parallel_org"
|
|
6243
|
+
];
|
|
6244
|
+
PHASE_INFO = {
|
|
6245
|
+
phase_1_coo: {
|
|
6246
|
+
phase: "phase_1_coo",
|
|
6247
|
+
shortLabel: "Phase 1 \u2014 COO mode",
|
|
6248
|
+
label: "Phase 1 \u2014 COO / Chief of Staff mode",
|
|
6249
|
+
focus: "building company context before delegation",
|
|
6250
|
+
nextSuggestion: "Unlock executives when technical, marketing, ops, legal, or finance work repeats."
|
|
6251
|
+
},
|
|
6252
|
+
phase_2_executives: {
|
|
6253
|
+
phase: "phase_2_executives",
|
|
6254
|
+
shortLabel: "Phase 2 \u2014 Executive bench",
|
|
6255
|
+
label: "Phase 2 \u2014 Executive bench",
|
|
6256
|
+
focus: "COO works with domain executives like CTO/CMO before specialist fan-out",
|
|
6257
|
+
nextSuggestion: "Unlock parallel execution when review gates, permissions, and workflows are ready."
|
|
6258
|
+
},
|
|
6259
|
+
phase_3_parallel_org: {
|
|
6260
|
+
phase: "phase_3_parallel_org",
|
|
6261
|
+
shortLabel: "Phase 3 \u2014 Parallel org",
|
|
6262
|
+
label: "Phase 3 \u2014 Parallel execution org",
|
|
6263
|
+
focus: "executives can delegate to specialists in parallel with review gates",
|
|
6264
|
+
nextSuggestion: "Keep review/CI/permission gates healthy; downgrade anytime if you want simpler COO-only mode."
|
|
6265
|
+
}
|
|
6266
|
+
};
|
|
6267
|
+
}
|
|
6268
|
+
});
|
|
6269
|
+
|
|
5739
6270
|
// src/lib/preferences.ts
|
|
5740
6271
|
var preferences_exports = {};
|
|
5741
6272
|
__export(preferences_exports, {
|
|
5742
6273
|
loadPreferences: () => loadPreferences,
|
|
5743
6274
|
savePreferences: () => savePreferences
|
|
5744
6275
|
});
|
|
5745
|
-
import { existsSync as
|
|
5746
|
-
import
|
|
6276
|
+
import { existsSync as existsSync14, readFileSync as readFileSync10, writeFileSync as writeFileSync8 } from "fs";
|
|
6277
|
+
import path14 from "path";
|
|
5747
6278
|
import os7 from "os";
|
|
5748
6279
|
function loadPreferences(homeDir = os7.homedir()) {
|
|
5749
|
-
const configPath =
|
|
5750
|
-
if (!
|
|
6280
|
+
const configPath = path14.join(homeDir, ".exe-os", "config.json");
|
|
6281
|
+
if (!existsSync14(configPath)) return {};
|
|
5751
6282
|
try {
|
|
5752
|
-
const config = JSON.parse(
|
|
6283
|
+
const config = JSON.parse(readFileSync10(configPath, "utf-8"));
|
|
5753
6284
|
return config.preferences ?? {};
|
|
5754
6285
|
} catch {
|
|
5755
6286
|
return {};
|
|
5756
6287
|
}
|
|
5757
6288
|
}
|
|
5758
6289
|
function savePreferences(prefs, homeDir = os7.homedir()) {
|
|
5759
|
-
const configDir =
|
|
5760
|
-
const configPath =
|
|
6290
|
+
const configDir = path14.join(homeDir, ".exe-os");
|
|
6291
|
+
const configPath = path14.join(configDir, "config.json");
|
|
5761
6292
|
ensurePrivateDirSync(configDir);
|
|
5762
6293
|
let config = {};
|
|
5763
|
-
if (
|
|
6294
|
+
if (existsSync14(configPath)) {
|
|
5764
6295
|
try {
|
|
5765
|
-
config = JSON.parse(
|
|
6296
|
+
config = JSON.parse(readFileSync10(configPath, "utf-8"));
|
|
5766
6297
|
} catch {
|
|
5767
6298
|
config = {};
|
|
5768
6299
|
}
|
|
5769
6300
|
}
|
|
5770
6301
|
config.preferences = prefs;
|
|
5771
|
-
|
|
6302
|
+
writeFileSync8(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
5772
6303
|
enforcePrivateFileSync(configPath);
|
|
5773
6304
|
}
|
|
5774
6305
|
var init_preferences = __esm({
|
|
@@ -5816,6 +6347,12 @@ var init_platform_procedures = __esm({
|
|
|
5816
6347
|
priority: "p0",
|
|
5817
6348
|
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."
|
|
5818
6349
|
},
|
|
6350
|
+
{
|
|
6351
|
+
title: "Customer orchestration maturity \u2014 recommend, never trap",
|
|
6352
|
+
domain: "workflow",
|
|
6353
|
+
priority: "p1",
|
|
6354
|
+
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."
|
|
6355
|
+
},
|
|
5819
6356
|
{
|
|
5820
6357
|
title: "Single dispatch path \u2014 create_task only",
|
|
5821
6358
|
domain: "workflow",
|
|
@@ -5874,6 +6411,12 @@ var init_platform_procedures = __esm({
|
|
|
5874
6411
|
priority: "p0",
|
|
5875
6412
|
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."
|
|
5876
6413
|
},
|
|
6414
|
+
{
|
|
6415
|
+
title: "Commit discipline \u2014 never leave verified work floating",
|
|
6416
|
+
domain: "workflow",
|
|
6417
|
+
priority: "p1",
|
|
6418
|
+
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."
|
|
6419
|
+
},
|
|
5877
6420
|
{
|
|
5878
6421
|
title: "Desktop and TUI are the same product",
|
|
5879
6422
|
domain: "architecture",
|
|
@@ -6641,17 +7184,17 @@ __export(identity_exports, {
|
|
|
6641
7184
|
listIdentities: () => listIdentities,
|
|
6642
7185
|
updateIdentity: () => updateIdentity
|
|
6643
7186
|
});
|
|
6644
|
-
import { existsSync as
|
|
7187
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync7, readFileSync as readFileSync11, writeFileSync as writeFileSync9 } from "fs";
|
|
6645
7188
|
import { readdirSync as readdirSync3 } from "fs";
|
|
6646
|
-
import
|
|
7189
|
+
import path15 from "path";
|
|
6647
7190
|
import { createHash as createHash2 } from "crypto";
|
|
6648
7191
|
function ensureDir() {
|
|
6649
|
-
if (!
|
|
6650
|
-
|
|
7192
|
+
if (!existsSync15(IDENTITY_DIR2)) {
|
|
7193
|
+
mkdirSync7(IDENTITY_DIR2, { recursive: true });
|
|
6651
7194
|
}
|
|
6652
7195
|
}
|
|
6653
7196
|
function identityPath(agentId) {
|
|
6654
|
-
return
|
|
7197
|
+
return path15.join(IDENTITY_DIR2, `${agentId}.md`);
|
|
6655
7198
|
}
|
|
6656
7199
|
function sanitizeIdentityBody(body) {
|
|
6657
7200
|
return body.replace(/<!--[\s\S]*?-->/g, "").trim();
|
|
@@ -6695,8 +7238,8 @@ function contentHash(content) {
|
|
|
6695
7238
|
}
|
|
6696
7239
|
function getIdentity(agentId) {
|
|
6697
7240
|
const filePath = identityPath(agentId);
|
|
6698
|
-
if (!
|
|
6699
|
-
const raw =
|
|
7241
|
+
if (!existsSync15(filePath)) return null;
|
|
7242
|
+
const raw = readFileSync11(filePath, "utf-8");
|
|
6700
7243
|
const { frontmatter, body } = parseFrontmatter(raw);
|
|
6701
7244
|
return {
|
|
6702
7245
|
agentId,
|
|
@@ -6710,7 +7253,7 @@ async function updateIdentity(agentId, content, updatedBy) {
|
|
|
6710
7253
|
ensureDir();
|
|
6711
7254
|
const filePath = identityPath(agentId);
|
|
6712
7255
|
const hash = contentHash(content);
|
|
6713
|
-
|
|
7256
|
+
writeFileSync9(filePath, content, "utf-8");
|
|
6714
7257
|
try {
|
|
6715
7258
|
const client = getClient();
|
|
6716
7259
|
await client.execute({
|
|
@@ -6768,7 +7311,7 @@ var init_identity = __esm({
|
|
|
6768
7311
|
"use strict";
|
|
6769
7312
|
init_config();
|
|
6770
7313
|
init_database();
|
|
6771
|
-
IDENTITY_DIR2 =
|
|
7314
|
+
IDENTITY_DIR2 = path15.join(EXE_AI_DIR, "identity");
|
|
6772
7315
|
}
|
|
6773
7316
|
});
|
|
6774
7317
|
|
|
@@ -7319,36 +7862,36 @@ __export(session_wrappers_exports, {
|
|
|
7319
7862
|
generateSessionWrappers: () => generateSessionWrappers
|
|
7320
7863
|
});
|
|
7321
7864
|
import {
|
|
7322
|
-
existsSync as
|
|
7323
|
-
readFileSync as
|
|
7324
|
-
writeFileSync as
|
|
7325
|
-
mkdirSync as
|
|
7865
|
+
existsSync as existsSync16,
|
|
7866
|
+
readFileSync as readFileSync12,
|
|
7867
|
+
writeFileSync as writeFileSync10,
|
|
7868
|
+
mkdirSync as mkdirSync8,
|
|
7326
7869
|
chmodSync as chmodSync2,
|
|
7327
7870
|
readdirSync as readdirSync4,
|
|
7328
7871
|
unlinkSync as unlinkSync7
|
|
7329
7872
|
} from "fs";
|
|
7330
|
-
import
|
|
7873
|
+
import path16 from "path";
|
|
7331
7874
|
import { homedir as homedir3 } from "os";
|
|
7332
7875
|
function generateSessionWrappers(packageRoot, homeDir) {
|
|
7333
7876
|
const home = homeDir ?? homedir3();
|
|
7334
|
-
const binDir =
|
|
7335
|
-
const rosterPath =
|
|
7336
|
-
|
|
7337
|
-
const exeStartDst =
|
|
7877
|
+
const binDir = path16.join(home, ".exe-os", "bin");
|
|
7878
|
+
const rosterPath = path16.join(home, ".exe-os", "exe-employees.json");
|
|
7879
|
+
mkdirSync8(binDir, { recursive: true });
|
|
7880
|
+
const exeStartDst = path16.join(binDir, "exe-start");
|
|
7338
7881
|
const candidates = [
|
|
7339
|
-
|
|
7340
|
-
|
|
7882
|
+
path16.join(packageRoot, "dist", "bin", "exe-start.sh"),
|
|
7883
|
+
path16.join(packageRoot, "src", "bin", "exe-start.sh")
|
|
7341
7884
|
];
|
|
7342
7885
|
for (const src of candidates) {
|
|
7343
|
-
if (
|
|
7344
|
-
|
|
7886
|
+
if (existsSync16(src)) {
|
|
7887
|
+
writeFileSync10(exeStartDst, readFileSync12(src));
|
|
7345
7888
|
chmodSync2(exeStartDst, 493);
|
|
7346
7889
|
break;
|
|
7347
7890
|
}
|
|
7348
7891
|
}
|
|
7349
7892
|
let employees = [];
|
|
7350
7893
|
try {
|
|
7351
|
-
employees = JSON.parse(
|
|
7894
|
+
employees = JSON.parse(readFileSync12(rosterPath, "utf8"));
|
|
7352
7895
|
} catch {
|
|
7353
7896
|
return { created: 0, pathConfigured: false };
|
|
7354
7897
|
}
|
|
@@ -7358,9 +7901,9 @@ function generateSessionWrappers(packageRoot, homeDir) {
|
|
|
7358
7901
|
try {
|
|
7359
7902
|
for (const f of readdirSync4(binDir)) {
|
|
7360
7903
|
if (f === "exe-start") continue;
|
|
7361
|
-
const fPath =
|
|
7904
|
+
const fPath = path16.join(binDir, f);
|
|
7362
7905
|
try {
|
|
7363
|
-
const content =
|
|
7906
|
+
const content = readFileSync12(fPath, "utf8");
|
|
7364
7907
|
if (content.includes("exe-start")) {
|
|
7365
7908
|
unlinkSync7(fPath);
|
|
7366
7909
|
}
|
|
@@ -7375,34 +7918,34 @@ exec "${exeStartDst}" "$0" "$@"
|
|
|
7375
7918
|
`;
|
|
7376
7919
|
for (const emp of employees) {
|
|
7377
7920
|
for (let n = 1; n <= MAX_N; n++) {
|
|
7378
|
-
const wrapperPath =
|
|
7379
|
-
|
|
7921
|
+
const wrapperPath = path16.join(binDir, `${emp.name}${n}`);
|
|
7922
|
+
writeFileSync10(wrapperPath, wrapperContent);
|
|
7380
7923
|
chmodSync2(wrapperPath, 493);
|
|
7381
7924
|
created++;
|
|
7382
|
-
const codexPath =
|
|
7383
|
-
|
|
7925
|
+
const codexPath = path16.join(binDir, `${emp.name}${n}-codex`);
|
|
7926
|
+
writeFileSync10(codexPath, wrapperContent);
|
|
7384
7927
|
chmodSync2(codexPath, 493);
|
|
7385
7928
|
created++;
|
|
7386
7929
|
}
|
|
7387
7930
|
}
|
|
7388
7931
|
const codexLauncherCandidates = [
|
|
7389
|
-
|
|
7390
|
-
|
|
7932
|
+
path16.join(packageRoot, "dist", "bin", "exe-start-codex.js"),
|
|
7933
|
+
path16.join(packageRoot, "src", "bin", "exe-start-codex.ts")
|
|
7391
7934
|
];
|
|
7392
7935
|
let codexLauncher = null;
|
|
7393
7936
|
for (const c of codexLauncherCandidates) {
|
|
7394
|
-
if (
|
|
7937
|
+
if (existsSync16(c)) {
|
|
7395
7938
|
codexLauncher = c;
|
|
7396
7939
|
break;
|
|
7397
7940
|
}
|
|
7398
7941
|
}
|
|
7399
7942
|
if (codexLauncher) {
|
|
7400
7943
|
for (const emp of employees) {
|
|
7401
|
-
const wrapperPath =
|
|
7944
|
+
const wrapperPath = path16.join(binDir, `${emp.name}-codex`);
|
|
7402
7945
|
const content = `#!/bin/bash
|
|
7403
7946
|
exec node "${codexLauncher}" --agent ${emp.name} "$@"
|
|
7404
7947
|
`;
|
|
7405
|
-
|
|
7948
|
+
writeFileSync10(wrapperPath, content);
|
|
7406
7949
|
chmodSync2(wrapperPath, 493);
|
|
7407
7950
|
created++;
|
|
7408
7951
|
}
|
|
@@ -7421,24 +7964,24 @@ export PATH="${binDir}:$PATH"
|
|
|
7421
7964
|
const shell = process.env.SHELL ?? "/bin/bash";
|
|
7422
7965
|
const profilePaths = [];
|
|
7423
7966
|
if (shell.includes("zsh")) {
|
|
7424
|
-
profilePaths.push(
|
|
7967
|
+
profilePaths.push(path16.join(home, ".zshrc"));
|
|
7425
7968
|
} else if (shell.includes("bash")) {
|
|
7426
|
-
profilePaths.push(
|
|
7427
|
-
profilePaths.push(
|
|
7969
|
+
profilePaths.push(path16.join(home, ".bashrc"));
|
|
7970
|
+
profilePaths.push(path16.join(home, ".bash_profile"));
|
|
7428
7971
|
} else {
|
|
7429
|
-
profilePaths.push(
|
|
7972
|
+
profilePaths.push(path16.join(home, ".profile"));
|
|
7430
7973
|
}
|
|
7431
7974
|
for (const profilePath of profilePaths) {
|
|
7432
7975
|
try {
|
|
7433
7976
|
let content = "";
|
|
7434
7977
|
try {
|
|
7435
|
-
content =
|
|
7978
|
+
content = readFileSync12(profilePath, "utf8");
|
|
7436
7979
|
} catch {
|
|
7437
7980
|
}
|
|
7438
7981
|
if (content.includes(".exe-os/bin")) {
|
|
7439
7982
|
return false;
|
|
7440
7983
|
}
|
|
7441
|
-
|
|
7984
|
+
writeFileSync10(profilePath, content + exportLine);
|
|
7442
7985
|
return true;
|
|
7443
7986
|
} catch {
|
|
7444
7987
|
continue;
|
|
@@ -7458,9 +8001,9 @@ var init_session_wrappers = __esm({
|
|
|
7458
8001
|
init_config();
|
|
7459
8002
|
init_keychain();
|
|
7460
8003
|
import crypto4 from "crypto";
|
|
7461
|
-
import { existsSync as
|
|
8004
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync9, readFileSync as readFileSync13, writeFileSync as writeFileSync11, unlinkSync as unlinkSync8 } from "fs";
|
|
7462
8005
|
import os8 from "os";
|
|
7463
|
-
import
|
|
8006
|
+
import path17 from "path";
|
|
7464
8007
|
import { createInterface } from "readline";
|
|
7465
8008
|
|
|
7466
8009
|
// src/lib/model-downloader.ts
|
|
@@ -7552,32 +8095,32 @@ async function fileHash(filePath) {
|
|
|
7552
8095
|
|
|
7553
8096
|
// src/lib/setup-wizard.ts
|
|
7554
8097
|
function findPackageRoot2() {
|
|
7555
|
-
let dir =
|
|
7556
|
-
const root =
|
|
8098
|
+
let dir = path17.dirname(new URL(import.meta.url).pathname);
|
|
8099
|
+
const root = path17.parse(dir).root;
|
|
7557
8100
|
while (dir !== root) {
|
|
7558
|
-
const pkgPath =
|
|
7559
|
-
if (
|
|
8101
|
+
const pkgPath = path17.join(dir, "package.json");
|
|
8102
|
+
if (existsSync17(pkgPath)) {
|
|
7560
8103
|
try {
|
|
7561
|
-
const pkg = JSON.parse(
|
|
8104
|
+
const pkg = JSON.parse(readFileSync13(pkgPath, "utf-8"));
|
|
7562
8105
|
if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
|
|
7563
8106
|
} catch {
|
|
7564
8107
|
}
|
|
7565
8108
|
}
|
|
7566
|
-
dir =
|
|
8109
|
+
dir = path17.dirname(dir);
|
|
7567
8110
|
}
|
|
7568
8111
|
return null;
|
|
7569
8112
|
}
|
|
7570
|
-
var SETUP_STATE_PATH =
|
|
8113
|
+
var SETUP_STATE_PATH = path17.join(os8.homedir(), ".exe-os", "setup-state.json");
|
|
7571
8114
|
function loadSetupState() {
|
|
7572
8115
|
try {
|
|
7573
|
-
return JSON.parse(
|
|
8116
|
+
return JSON.parse(readFileSync13(SETUP_STATE_PATH, "utf8"));
|
|
7574
8117
|
} catch {
|
|
7575
8118
|
return { completedSteps: [], startedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
7576
8119
|
}
|
|
7577
8120
|
}
|
|
7578
8121
|
function saveSetupState(state) {
|
|
7579
|
-
|
|
7580
|
-
|
|
8122
|
+
mkdirSync9(path17.dirname(SETUP_STATE_PATH), { recursive: true });
|
|
8123
|
+
writeFileSync11(SETUP_STATE_PATH, JSON.stringify(state, null, 2));
|
|
7581
8124
|
}
|
|
7582
8125
|
function clearSetupState() {
|
|
7583
8126
|
try {
|
|
@@ -7627,10 +8170,10 @@ async function validateModel(log) {
|
|
|
7627
8170
|
if (totalGB <= 8 || isLowMemory()) {
|
|
7628
8171
|
log(`System memory: ${totalGB.toFixed(0)}GB total, ${freeGB.toFixed(1)}GB free`);
|
|
7629
8172
|
log("Skipping in-memory model validation (low memory \u2014 will validate on first use).");
|
|
7630
|
-
const modelPath =
|
|
7631
|
-
if (
|
|
7632
|
-
const { statSync:
|
|
7633
|
-
const size =
|
|
8173
|
+
const modelPath = path17.join(MODELS_DIR, LOCAL_FILENAME);
|
|
8174
|
+
if (existsSync17(modelPath)) {
|
|
8175
|
+
const { statSync: statSync5 } = await import("fs");
|
|
8176
|
+
const size = statSync5(modelPath).size;
|
|
7634
8177
|
if (size > 300 * 1e6) {
|
|
7635
8178
|
log(`Model file verified (${(size / 1e6).toFixed(0)} MB).`);
|
|
7636
8179
|
return;
|
|
@@ -7659,10 +8202,10 @@ async function runSetupWizard(opts = {}) {
|
|
|
7659
8202
|
log("");
|
|
7660
8203
|
log("=== exe-os Setup ===");
|
|
7661
8204
|
log("");
|
|
7662
|
-
log("
|
|
8205
|
+
log("What are you setting up?");
|
|
7663
8206
|
log("");
|
|
7664
|
-
log(" [1]
|
|
7665
|
-
log(" [2]
|
|
8207
|
+
log(" [1] First device / brand new Exe OS memory");
|
|
8208
|
+
log(" [2] Another device / I already have a 24-word recovery phrase");
|
|
7666
8209
|
log("");
|
|
7667
8210
|
const installType = await ask(rl, "Choice (1/2): ");
|
|
7668
8211
|
let isPairing = false;
|
|
@@ -7671,16 +8214,20 @@ async function runSetupWizard(opts = {}) {
|
|
|
7671
8214
|
isPairing = true;
|
|
7672
8215
|
log("");
|
|
7673
8216
|
const { importMnemonic: importMnemonic2 } = await Promise.resolve().then(() => (init_keychain(), keychain_exports));
|
|
7674
|
-
|
|
8217
|
+
log("On your existing device, open Terminal and run:");
|
|
8218
|
+
log(" exe-os cloud link --show-full");
|
|
8219
|
+
log("Then copy the 24-word phrase here.");
|
|
8220
|
+
log("");
|
|
8221
|
+
const mnemonic = await ask(rl, "Paste the 24-word recovery phrase: ");
|
|
7675
8222
|
try {
|
|
7676
8223
|
const key = await importMnemonic2(mnemonic);
|
|
7677
8224
|
await setMasterKey(key);
|
|
7678
8225
|
log("Master key imported and stored securely.");
|
|
7679
8226
|
log("");
|
|
7680
|
-
log("
|
|
7681
|
-
log("
|
|
8227
|
+
log("Now paste the Cloud API key from that same `exe-os cloud link --show-full` output.");
|
|
8228
|
+
log("It starts with: exe_sk_");
|
|
7682
8229
|
log("");
|
|
7683
|
-
const apiKey = await ask(rl, "API key (exe_sk_
|
|
8230
|
+
const apiKey = await ask(rl, "Cloud API key (starts with exe_sk_): ");
|
|
7684
8231
|
if (apiKey && apiKey.startsWith("exe_sk_")) {
|
|
7685
8232
|
const cloudEndpoint = "https://askexe.com/cloud";
|
|
7686
8233
|
const cloudCfg = { apiKey, endpoint: cloudEndpoint };
|
|
@@ -7719,7 +8266,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
7719
8266
|
if (state.completedSteps.length > 0) {
|
|
7720
8267
|
log(`Resuming setup from step ${Math.max(...state.completedSteps) + 1}...`);
|
|
7721
8268
|
}
|
|
7722
|
-
if (
|
|
8269
|
+
if (existsSync17(LEGACY_LANCE_PATH)) {
|
|
7723
8270
|
log("\u26A0 Found v1.0 LanceDB at ~/.exe-os/local.lance");
|
|
7724
8271
|
log(" v1.1 uses libSQL (SQLite). Your existing memories are not automatically migrated.");
|
|
7725
8272
|
log(" The old directory will not be modified or deleted.");
|
|
@@ -7743,14 +8290,22 @@ async function runSetupWizard(opts = {}) {
|
|
|
7743
8290
|
log("");
|
|
7744
8291
|
log(" " + mnemonic);
|
|
7745
8292
|
log("");
|
|
7746
|
-
log(" SAVE THIS NOW
|
|
7747
|
-
log("
|
|
7748
|
-
log("
|
|
8293
|
+
log(" SAVE THIS NOW before continuing.");
|
|
8294
|
+
log(" Best: put it in your password manager, or write it down and keep it safe.");
|
|
8295
|
+
log(" This phrase is the ONLY way to decrypt your memories if you lose this computer.");
|
|
8296
|
+
log(" We cannot recover it for you.");
|
|
7749
8297
|
log("");
|
|
7750
8298
|
log(" Like a Bitcoin wallet \u2014 lose the phrase, lose everything.");
|
|
7751
8299
|
log("=============================================================");
|
|
7752
8300
|
log("");
|
|
7753
|
-
await ask(rl, "
|
|
8301
|
+
const confirmation = await ask(rl, 'Type "I SAVED MY RECOVERY PHRASE" after you have saved it: ');
|
|
8302
|
+
if (confirmation !== "I SAVED MY RECOVERY PHRASE") {
|
|
8303
|
+
throw new Error(
|
|
8304
|
+
"Setup cancelled: recovery phrase was not confirmed. Save the 24-word phrase before continuing \u2014 it is required to recover encrypted memories."
|
|
8305
|
+
);
|
|
8306
|
+
}
|
|
8307
|
+
const { markKeyBackupConfirmed: markKeyBackupConfirmed2 } = await Promise.resolve().then(() => (init_key_backup_status(), key_backup_status_exports));
|
|
8308
|
+
markKeyBackupConfirmed2("setup-wizard");
|
|
7754
8309
|
}
|
|
7755
8310
|
state.completedSteps.push(1);
|
|
7756
8311
|
saveSetupState(state);
|
|
@@ -7897,13 +8452,15 @@ async function runSetupWizard(opts = {}) {
|
|
|
7897
8452
|
if (cloudConfig) {
|
|
7898
8453
|
config.cloud = cloudConfig;
|
|
7899
8454
|
}
|
|
8455
|
+
const { applyDefaultOrchestrationPhase: applyDefaultOrchestrationPhase2 } = await Promise.resolve().then(() => (init_orchestration_phase(), orchestration_phase_exports));
|
|
8456
|
+
applyDefaultOrchestrationPhase2(config);
|
|
7900
8457
|
await saveConfig(config);
|
|
7901
8458
|
log("");
|
|
7902
8459
|
try {
|
|
7903
|
-
const claudeJsonPath =
|
|
8460
|
+
const claudeJsonPath = path17.join(os8.homedir(), ".claude.json");
|
|
7904
8461
|
let claudeJson = {};
|
|
7905
8462
|
try {
|
|
7906
|
-
claudeJson = JSON.parse(
|
|
8463
|
+
claudeJson = JSON.parse(readFileSync13(claudeJsonPath, "utf8"));
|
|
7907
8464
|
} catch {
|
|
7908
8465
|
}
|
|
7909
8466
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -7912,7 +8469,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
7912
8469
|
if (!projects[dir]) projects[dir] = {};
|
|
7913
8470
|
projects[dir].hasTrustDialogAccepted = true;
|
|
7914
8471
|
}
|
|
7915
|
-
|
|
8472
|
+
writeFileSync11(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
7916
8473
|
} catch {
|
|
7917
8474
|
}
|
|
7918
8475
|
state.completedSteps.push(5);
|
|
@@ -7926,7 +8483,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
7926
8483
|
const prefs = { ...existingPrefs };
|
|
7927
8484
|
log("=== Config Defaults ===");
|
|
7928
8485
|
log("");
|
|
7929
|
-
const ghosttyDetected =
|
|
8486
|
+
const ghosttyDetected = existsSync17(path17.join(os8.homedir(), ".config", "ghostty")) || existsSync17(path17.join(os8.homedir(), "Library", "Application Support", "com.mitchellh.ghostty"));
|
|
7930
8487
|
if (ghosttyDetected) {
|
|
7931
8488
|
const ghosttyAnswer = await ask(rl, "Detected Ghostty terminal. Use exe-os Ghostty defaults? (Y/n) ");
|
|
7932
8489
|
prefs.ghostty = ghosttyAnswer.toLowerCase() !== "n";
|
|
@@ -7973,7 +8530,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
7973
8530
|
let missingIdentities = [];
|
|
7974
8531
|
for (const emp of roster) {
|
|
7975
8532
|
const idPath = identityPath2(emp.name);
|
|
7976
|
-
if (!
|
|
8533
|
+
if (!existsSync17(idPath)) {
|
|
7977
8534
|
missingIdentities.push(emp.name);
|
|
7978
8535
|
}
|
|
7979
8536
|
}
|
|
@@ -8005,7 +8562,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
8005
8562
|
}
|
|
8006
8563
|
missingIdentities = [];
|
|
8007
8564
|
for (const emp of roster) {
|
|
8008
|
-
if (!
|
|
8565
|
+
if (!existsSync17(identityPath2(emp.name))) {
|
|
8009
8566
|
missingIdentities.push(emp.name);
|
|
8010
8567
|
}
|
|
8011
8568
|
}
|
|
@@ -8064,9 +8621,9 @@ async function runSetupWizard(opts = {}) {
|
|
|
8064
8621
|
const cooIdentityContent = getIdentityTemplate("coo");
|
|
8065
8622
|
if (cooIdentityContent) {
|
|
8066
8623
|
const cooIdPath = identityPath2(cooName);
|
|
8067
|
-
|
|
8624
|
+
mkdirSync9(path17.dirname(cooIdPath), { recursive: true });
|
|
8068
8625
|
const replaced = cooIdentityContent.replace(/agent_id:\s*exe/g, `agent_id: ${cooName}`).replace(/\$\{agent_id\}/g, cooName);
|
|
8069
|
-
|
|
8626
|
+
writeFileSync11(cooIdPath, replaced, "utf-8");
|
|
8070
8627
|
}
|
|
8071
8628
|
registerBinSymlinks2(cooName);
|
|
8072
8629
|
createdEmployees.push({ name: cooName, role: "COO" });
|
|
@@ -8117,7 +8674,8 @@ async function runSetupWizard(opts = {}) {
|
|
|
8117
8674
|
}
|
|
8118
8675
|
if (!isLicensed) {
|
|
8119
8676
|
log("");
|
|
8120
|
-
log("You're all set.
|
|
8677
|
+
log("You're all set. You're starting in Phase 1: COO / Chief of Staff mode.");
|
|
8678
|
+
log("Your COO will learn your company context first. You can unlock executives later anytime.");
|
|
8121
8679
|
log("When you're ready for specialists, add a license key at askexe.com.");
|
|
8122
8680
|
log("");
|
|
8123
8681
|
log(`Type your COO's name to start: /${cooName}`);
|
|
@@ -8132,67 +8690,89 @@ async function runSetupWizard(opts = {}) {
|
|
|
8132
8690
|
}
|
|
8133
8691
|
if (!state.completedSteps.includes(8)) {
|
|
8134
8692
|
let employees = await loadEmployees2(EMPLOYEES_PATH2).catch(() => []);
|
|
8135
|
-
log("===
|
|
8693
|
+
log("=== Orchestration Phase ===");
|
|
8136
8694
|
log("");
|
|
8137
|
-
log("
|
|
8138
|
-
log("
|
|
8695
|
+
log("Recommended default: Phase 1 \u2014 COO / Chief of Staff mode.");
|
|
8696
|
+
log("Start with one-on-one conversations so your COO learns your company,");
|
|
8697
|
+
log("vision, priorities, constraints, and operating style before delegating.");
|
|
8139
8698
|
log("");
|
|
8140
|
-
|
|
8141
|
-
const ctoDefault = ctoTemplate?.name ?? "cto";
|
|
8142
|
-
log(` CTO \u2014 engineering, architecture, code reviews (default: ${ctoDefault})`);
|
|
8143
|
-
const cmoTemplate = getTemplateByRole2("CMO");
|
|
8144
|
-
const cmoDefault = cmoTemplate?.name ?? "cmo";
|
|
8145
|
-
log(` CMO \u2014 design, brand, content, marketing (default: ${cmoDefault})`);
|
|
8699
|
+
log("You can jump ahead now if you want. This is a suggestion, not a blocker.");
|
|
8146
8700
|
log("");
|
|
8147
|
-
|
|
8148
|
-
|
|
8149
|
-
|
|
8150
|
-
const ctoEmployee = {
|
|
8151
|
-
name: ctoName,
|
|
8152
|
-
role: "CTO",
|
|
8153
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8154
|
-
templateName: ctoDefault,
|
|
8155
|
-
templateVersion: 1
|
|
8156
|
-
};
|
|
8157
|
-
employees = addEmployee2(employees, ctoEmployee);
|
|
8158
|
-
await saveEmployees2(employees, EMPLOYEES_PATH2);
|
|
8159
|
-
}
|
|
8160
|
-
const ctoIdentityContent = getIdentityTemplate("cto");
|
|
8161
|
-
if (ctoIdentityContent) {
|
|
8162
|
-
const ctoIdPath = identityPath2(ctoName);
|
|
8163
|
-
mkdirSync8(path16.dirname(ctoIdPath), { recursive: true });
|
|
8164
|
-
const replaced = ctoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${ctoName}`).replace(/\$\{agent_id\}/g, ctoName);
|
|
8165
|
-
writeFileSync10(ctoIdPath, replaced, "utf-8");
|
|
8166
|
-
}
|
|
8167
|
-
registerBinSymlinks2(ctoName);
|
|
8168
|
-
createdEmployees.push({ name: ctoName, role: "CTO" });
|
|
8169
|
-
log(`Created ${ctoName} (CTO)`);
|
|
8170
|
-
const cmoNameInput = await ask(rl, `Name your CMO (default: ${cmoDefault}): `);
|
|
8171
|
-
const cmoName = (cmoNameInput || cmoDefault).toLowerCase();
|
|
8172
|
-
if (!employees.some((e) => e.name === cmoName)) {
|
|
8173
|
-
const cmoEmployee = {
|
|
8174
|
-
name: cmoName,
|
|
8175
|
-
role: "CMO",
|
|
8176
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8177
|
-
templateName: cmoDefault,
|
|
8178
|
-
templateVersion: 1
|
|
8179
|
-
};
|
|
8180
|
-
employees = addEmployee2(employees, cmoEmployee);
|
|
8181
|
-
await saveEmployees2(employees, EMPLOYEES_PATH2);
|
|
8182
|
-
}
|
|
8183
|
-
const cmoIdentityContent = getIdentityTemplate("cmo");
|
|
8184
|
-
if (cmoIdentityContent) {
|
|
8185
|
-
const cmoIdPath = identityPath2(cmoName);
|
|
8186
|
-
mkdirSync8(path16.dirname(cmoIdPath), { recursive: true });
|
|
8187
|
-
const replaced = cmoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${cmoName}`).replace(/\$\{agent_id\}/g, cmoName);
|
|
8188
|
-
writeFileSync10(cmoIdPath, replaced, "utf-8");
|
|
8189
|
-
}
|
|
8190
|
-
registerBinSymlinks2(cmoName);
|
|
8191
|
-
createdEmployees.push({ name: cmoName, role: "CMO" });
|
|
8192
|
-
log(`Created ${cmoName} (CMO)`);
|
|
8701
|
+
log(" Phase 1: COO only \u2014 build company context first");
|
|
8702
|
+
log(" Phase 2: Executive bench \u2014 add CTO/CMO for repeated domain work");
|
|
8703
|
+
log(" Phase 3: Parallel org \u2014 specialists execute in parallel with reviews");
|
|
8193
8704
|
log("");
|
|
8194
|
-
|
|
8195
|
-
|
|
8705
|
+
const unlockExecutives = (await ask(rl, "Unlock Phase 2 executives now? (y/N): ")).toLowerCase() === "y";
|
|
8706
|
+
const { setOrchestrationPhase: setOrchestrationPhase2 } = await Promise.resolve().then(() => (init_orchestration_phase(), orchestration_phase_exports));
|
|
8707
|
+
if (!unlockExecutives) {
|
|
8708
|
+
await setOrchestrationPhase2("phase_1_coo", "setup-wizard");
|
|
8709
|
+
log("");
|
|
8710
|
+
log("Starting in Phase 1. Your executive templates are available when you're ready.");
|
|
8711
|
+
log("Later: run `exe-os org unlock executives` to move to Phase 2.");
|
|
8712
|
+
state.completedSteps.push(8);
|
|
8713
|
+
saveSetupState(state);
|
|
8714
|
+
} else {
|
|
8715
|
+
await setOrchestrationPhase2("phase_2_executives", "setup-wizard");
|
|
8716
|
+
log("");
|
|
8717
|
+
log("Phase 2 enabled. Let's name your executive bench.");
|
|
8718
|
+
log("");
|
|
8719
|
+
const ctoTemplate = getTemplateByRole2("CTO");
|
|
8720
|
+
const ctoDefault = ctoTemplate?.name ?? "cto";
|
|
8721
|
+
log(` CTO \u2014 engineering, architecture, code reviews (default: ${ctoDefault})`);
|
|
8722
|
+
const cmoTemplate = getTemplateByRole2("CMO");
|
|
8723
|
+
const cmoDefault = cmoTemplate?.name ?? "cmo";
|
|
8724
|
+
log(` CMO \u2014 design, brand, content, marketing (default: ${cmoDefault})`);
|
|
8725
|
+
log("");
|
|
8726
|
+
const ctoNameInput = await ask(rl, `Name your CTO (default: ${ctoDefault}): `);
|
|
8727
|
+
const ctoName = (ctoNameInput || ctoDefault).toLowerCase();
|
|
8728
|
+
if (!employees.some((e) => e.name === ctoName)) {
|
|
8729
|
+
const ctoEmployee = {
|
|
8730
|
+
name: ctoName,
|
|
8731
|
+
role: "CTO",
|
|
8732
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8733
|
+
templateName: ctoDefault,
|
|
8734
|
+
templateVersion: 1
|
|
8735
|
+
};
|
|
8736
|
+
employees = addEmployee2(employees, ctoEmployee);
|
|
8737
|
+
await saveEmployees2(employees, EMPLOYEES_PATH2);
|
|
8738
|
+
}
|
|
8739
|
+
const ctoIdentityContent = getIdentityTemplate("cto");
|
|
8740
|
+
if (ctoIdentityContent) {
|
|
8741
|
+
const ctoIdPath = identityPath2(ctoName);
|
|
8742
|
+
mkdirSync9(path17.dirname(ctoIdPath), { recursive: true });
|
|
8743
|
+
const replaced = ctoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${ctoName}`).replace(/\$\{agent_id\}/g, ctoName);
|
|
8744
|
+
writeFileSync11(ctoIdPath, replaced, "utf-8");
|
|
8745
|
+
}
|
|
8746
|
+
registerBinSymlinks2(ctoName);
|
|
8747
|
+
createdEmployees.push({ name: ctoName, role: "CTO" });
|
|
8748
|
+
log(`Created ${ctoName} (CTO)`);
|
|
8749
|
+
const cmoNameInput = await ask(rl, `Name your CMO (default: ${cmoDefault}): `);
|
|
8750
|
+
const cmoName = (cmoNameInput || cmoDefault).toLowerCase();
|
|
8751
|
+
if (!employees.some((e) => e.name === cmoName)) {
|
|
8752
|
+
const cmoEmployee = {
|
|
8753
|
+
name: cmoName,
|
|
8754
|
+
role: "CMO",
|
|
8755
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8756
|
+
templateName: cmoDefault,
|
|
8757
|
+
templateVersion: 1
|
|
8758
|
+
};
|
|
8759
|
+
employees = addEmployee2(employees, cmoEmployee);
|
|
8760
|
+
await saveEmployees2(employees, EMPLOYEES_PATH2);
|
|
8761
|
+
}
|
|
8762
|
+
const cmoIdentityContent = getIdentityTemplate("cmo");
|
|
8763
|
+
if (cmoIdentityContent) {
|
|
8764
|
+
const cmoIdPath = identityPath2(cmoName);
|
|
8765
|
+
mkdirSync9(path17.dirname(cmoIdPath), { recursive: true });
|
|
8766
|
+
const replaced = cmoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${cmoName}`).replace(/\$\{agent_id\}/g, cmoName);
|
|
8767
|
+
writeFileSync11(cmoIdPath, replaced, "utf-8");
|
|
8768
|
+
}
|
|
8769
|
+
registerBinSymlinks2(cmoName);
|
|
8770
|
+
createdEmployees.push({ name: cmoName, role: "CMO" });
|
|
8771
|
+
log(`Created ${cmoName} (CMO)`);
|
|
8772
|
+
log("");
|
|
8773
|
+
state.completedSteps.push(8);
|
|
8774
|
+
saveSetupState(state);
|
|
8775
|
+
}
|
|
8196
8776
|
} else {
|
|
8197
8777
|
log("Step 8 already complete \u2014 skipping.");
|
|
8198
8778
|
}
|
|
@@ -8207,7 +8787,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
8207
8787
|
log(`Session shortcuts generated (${cooName}1, ${cooName}2, ...)`);
|
|
8208
8788
|
}
|
|
8209
8789
|
if (wrapResult.pathConfigured) {
|
|
8210
|
-
const binDir =
|
|
8790
|
+
const binDir = path17.join(os8.homedir(), ".exe-os", "bin");
|
|
8211
8791
|
process.env.PATH = `${binDir}:${process.env.PATH ?? ""}`;
|
|
8212
8792
|
pathJustConfigured = true;
|
|
8213
8793
|
}
|
|
@@ -8250,7 +8830,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
8250
8830
|
const pkgRoot2 = findPackageRoot2();
|
|
8251
8831
|
if (pkgRoot2) {
|
|
8252
8832
|
try {
|
|
8253
|
-
version = JSON.parse(
|
|
8833
|
+
version = JSON.parse(readFileSync13(path17.join(pkgRoot2, "package.json"), "utf-8")).version;
|
|
8254
8834
|
} catch {
|
|
8255
8835
|
}
|
|
8256
8836
|
}
|
|
@@ -8264,6 +8844,10 @@ async function runSetupWizard(opts = {}) {
|
|
|
8264
8844
|
log("");
|
|
8265
8845
|
log("=== Next Steps ===");
|
|
8266
8846
|
log("");
|
|
8847
|
+
log(" Recommended start: Phase 1 \u2014 talk to your COO first");
|
|
8848
|
+
log(" Check/change phase: exe-os org phase");
|
|
8849
|
+
log(" Unlock executives later: exe-os org unlock executives");
|
|
8850
|
+
log("");
|
|
8267
8851
|
log(` cd into a project folder: cd ~/my-project`);
|
|
8268
8852
|
log(` Launch your COO: ${cooName}1`);
|
|
8269
8853
|
if (pathJustConfigured) {
|