@askexenow/exe-os 0.9.63 → 0.9.65
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/dist/bin/cli.js +70 -117
- package/dist/bin/exe-boot.js +27 -6
- package/dist/bin/exe-doctor.js +1154 -2588
- package/dist/bin/exe-link.js +27 -6
- package/dist/bin/exe-new-employee.js +1 -111
- package/dist/bin/exe-start-codex.js +1 -111
- package/dist/bin/exe-start-opencode.js +1 -125
- package/dist/bin/install.js +1 -111
- package/dist/bin/setup.js +27 -6
- package/dist/bin/stack-update.js +42 -0
- package/dist/hooks/bug-report-worker.js +42 -111
- package/dist/hooks/pre-compact.js +109 -236
- package/dist/hooks/session-end.js +122 -137
- package/dist/hooks/summary-worker.js +99 -130
- package/dist/lib/cloud-sync.js +27 -6
- package/dist/lib/exe-daemon.js +95 -387
- package/dist/mcp/server.js +32 -378
- package/package.json +1 -1
package/dist/lib/cloud-sync.js
CHANGED
|
@@ -3421,31 +3421,43 @@ var ROSTER_LOCK_PATH = path10.join(EXE_AI_DIR, "roster-merge.lock");
|
|
|
3421
3421
|
var LOCK_STALE_MS = 3e4;
|
|
3422
3422
|
var _pgPromise = null;
|
|
3423
3423
|
var _pgFailed = false;
|
|
3424
|
+
function isTruthyEnv(value) {
|
|
3425
|
+
return /^(1|true|yes|on)$/i.test(value ?? "");
|
|
3426
|
+
}
|
|
3424
3427
|
function loadPgClient() {
|
|
3425
3428
|
if (_pgFailed) return null;
|
|
3426
|
-
const postgresUrl = process.env.DATABASE_URL;
|
|
3427
3429
|
const configPath = path10.join(EXE_AI_DIR, "config.json");
|
|
3428
3430
|
let cloudPostgresUrl;
|
|
3431
|
+
let configEnabled = false;
|
|
3429
3432
|
try {
|
|
3430
3433
|
if (existsSync10(configPath)) {
|
|
3431
3434
|
const cfg = JSON.parse(readFileSync7(configPath, "utf8"));
|
|
3432
3435
|
cloudPostgresUrl = cfg.cloud?.postgresUrl;
|
|
3433
|
-
|
|
3434
|
-
_pgFailed = true;
|
|
3435
|
-
return null;
|
|
3436
|
-
}
|
|
3436
|
+
configEnabled = cfg.cloud?.syncToPostgres === true;
|
|
3437
3437
|
}
|
|
3438
3438
|
} catch {
|
|
3439
3439
|
}
|
|
3440
|
-
const
|
|
3440
|
+
const envEnabled = isTruthyEnv(process.env.EXE_CLOUD_SYNC_TO_POSTGRES);
|
|
3441
|
+
if (!envEnabled && !configEnabled) {
|
|
3442
|
+
return null;
|
|
3443
|
+
}
|
|
3444
|
+
const url = process.env.DATABASE_URL || cloudPostgresUrl;
|
|
3441
3445
|
if (!url) {
|
|
3442
3446
|
_pgFailed = true;
|
|
3443
3447
|
return null;
|
|
3444
3448
|
}
|
|
3445
3449
|
if (!_pgPromise) {
|
|
3446
3450
|
_pgPromise = (async () => {
|
|
3451
|
+
if (!process.env.DATABASE_URL) process.env.DATABASE_URL = url;
|
|
3447
3452
|
const { createRequire: createRequire3 } = await import("module");
|
|
3448
3453
|
const { pathToFileURL: pathToFileURL3 } = await import("url");
|
|
3454
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
3455
|
+
if (explicitPath) {
|
|
3456
|
+
const mod2 = await import(pathToFileURL3(explicitPath).href);
|
|
3457
|
+
const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
|
|
3458
|
+
if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
|
|
3459
|
+
return new Ctor2();
|
|
3460
|
+
}
|
|
3449
3461
|
const exeDbRoot = process.env.EXE_DB_ROOT ?? path10.join(homedir2(), "exe-db");
|
|
3450
3462
|
const req = createRequire3(path10.join(exeDbRoot, "package.json"));
|
|
3451
3463
|
const entry = req.resolve("@prisma/client");
|
|
@@ -3642,6 +3654,15 @@ async function cloudSync(config) {
|
|
|
3642
3654
|
} catch {
|
|
3643
3655
|
throw new Error("[cloud-sync] Database not initialized. Call initStore() before cloudSync().");
|
|
3644
3656
|
}
|
|
3657
|
+
try {
|
|
3658
|
+
const relink = await client.execute("SELECT value FROM sync_meta WHERE key = 'cloud_relink_required' LIMIT 1");
|
|
3659
|
+
if (String(relink.rows[0]?.value ?? "") === "1") {
|
|
3660
|
+
throw new Error("[cloud-sync] Paused after key rotation. Re-link/reupload cloud sync with the new recovery phrase before syncing.");
|
|
3661
|
+
}
|
|
3662
|
+
} catch (err) {
|
|
3663
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3664
|
+
if (msg.includes("Paused after key rotation")) throw err;
|
|
3665
|
+
}
|
|
3645
3666
|
try {
|
|
3646
3667
|
const { getRawClient: getRawClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
3647
3668
|
await getRawClient2().execute("PRAGMA wal_checkpoint(PASSIVE)");
|
package/dist/lib/exe-daemon.js
CHANGED
|
@@ -313,15 +313,6 @@ var init_memory = __esm({
|
|
|
313
313
|
});
|
|
314
314
|
|
|
315
315
|
// src/lib/daemon-protocol.ts
|
|
316
|
-
var daemon_protocol_exports = {};
|
|
317
|
-
__export(daemon_protocol_exports, {
|
|
318
|
-
deserializeArgs: () => deserializeArgs,
|
|
319
|
-
deserializeResultSet: () => deserializeResultSet,
|
|
320
|
-
deserializeValue: () => deserializeValue,
|
|
321
|
-
serializeArgs: () => serializeArgs,
|
|
322
|
-
serializeResultSet: () => serializeResultSet,
|
|
323
|
-
serializeValue: () => serializeValue
|
|
324
|
-
});
|
|
325
316
|
function serializeValue(v) {
|
|
326
317
|
if (v === null || v === void 0) return null;
|
|
327
318
|
if (typeof v === "bigint") return Number(v);
|
|
@@ -346,9 +337,6 @@ function deserializeValue(v) {
|
|
|
346
337
|
}
|
|
347
338
|
return v;
|
|
348
339
|
}
|
|
349
|
-
function serializeArgs(args) {
|
|
350
|
-
return args.map(serializeValue);
|
|
351
|
-
}
|
|
352
340
|
function deserializeArgs(args) {
|
|
353
341
|
return args.map(deserializeValue);
|
|
354
342
|
}
|
|
@@ -3510,10 +3498,12 @@ var init_memory_write_governor = __esm({
|
|
|
3510
3498
|
// src/lib/projection-worker.ts
|
|
3511
3499
|
var projection_worker_exports = {};
|
|
3512
3500
|
__export(projection_worker_exports, {
|
|
3501
|
+
computeProjectionBackoffMs: () => computeProjectionBackoffMs,
|
|
3513
3502
|
processProjectionBatch: () => processProjectionBatch,
|
|
3514
3503
|
projectionHandlersForTests: () => projectionHandlersForTests,
|
|
3515
3504
|
resetProjectionWorkerForTests: () => resetProjectionWorkerForTests,
|
|
3516
3505
|
setProjectionWorkerPrismaClientForTests: () => setProjectionWorkerPrismaClientForTests,
|
|
3506
|
+
shouldStartProjectionWorker: () => shouldStartProjectionWorker,
|
|
3517
3507
|
startProjectionWorker: () => startProjectionWorker,
|
|
3518
3508
|
stopProjectionWorker: () => stopProjectionWorker
|
|
3519
3509
|
});
|
|
@@ -3552,8 +3542,12 @@ function resetProjectionWorkerForTests() {
|
|
|
3552
3542
|
clearTimeout(pollTimer);
|
|
3553
3543
|
pollTimer = null;
|
|
3554
3544
|
}
|
|
3545
|
+
consecutivePollErrors = 0;
|
|
3555
3546
|
prismaPromise = null;
|
|
3556
3547
|
}
|
|
3548
|
+
function isTruthyEnv(value) {
|
|
3549
|
+
return /^(1|true|yes|on)$/i.test(value ?? "");
|
|
3550
|
+
}
|
|
3557
3551
|
async function processBatch() {
|
|
3558
3552
|
const prisma = await loadPrisma();
|
|
3559
3553
|
const events = await prisma.$queryRawUnsafe(
|
|
@@ -3595,36 +3589,65 @@ async function processBatch() {
|
|
|
3595
3589
|
}
|
|
3596
3590
|
return processed;
|
|
3597
3591
|
}
|
|
3592
|
+
async function shouldStartProjectionWorker() {
|
|
3593
|
+
try {
|
|
3594
|
+
const config2 = await loadConfig();
|
|
3595
|
+
const envEnabled = isTruthyEnv(process.env.EXE_CLOUD_SYNC_TO_POSTGRES);
|
|
3596
|
+
if (!envEnabled && config2.cloud?.syncToPostgres !== true) {
|
|
3597
|
+
return { start: false, reason: "cloud.syncToPostgres is not enabled" };
|
|
3598
|
+
}
|
|
3599
|
+
} catch (err) {
|
|
3600
|
+
return { start: false, reason: `config unavailable: ${err instanceof Error ? err.message : String(err)}` };
|
|
3601
|
+
}
|
|
3602
|
+
if (!process.env.DATABASE_URL) {
|
|
3603
|
+
return { start: false, reason: "DATABASE_URL is not set" };
|
|
3604
|
+
}
|
|
3605
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path8.join(os5.homedir(), "exe-db");
|
|
3606
|
+
if (!existsSync8(path8.join(exeDbRoot, "package.json")) && !process.env.EXE_OS_PRISMA_CLIENT_PATH) {
|
|
3607
|
+
return { start: false, reason: "exe-db Prisma client not found" };
|
|
3608
|
+
}
|
|
3609
|
+
return { start: true };
|
|
3610
|
+
}
|
|
3611
|
+
function computeProjectionBackoffMs(errorCount) {
|
|
3612
|
+
if (errorCount <= 0) return POLL_INTERVAL_MS;
|
|
3613
|
+
return Math.min(POLL_INTERVAL_MS * 2 ** Math.min(errorCount, 5), MAX_POLL_BACKOFF_MS);
|
|
3614
|
+
}
|
|
3598
3615
|
function startProjectionWorker() {
|
|
3599
3616
|
if (running) return;
|
|
3600
|
-
|
|
3601
|
-
const
|
|
3602
|
-
if (!
|
|
3603
|
-
process.stderr.write(
|
|
3617
|
+
void (async () => {
|
|
3618
|
+
const decision = await shouldStartProjectionWorker();
|
|
3619
|
+
if (!decision.start) {
|
|
3620
|
+
process.stderr.write(`[projection-worker] Skipped \u2014 ${decision.reason}.
|
|
3621
|
+
`);
|
|
3604
3622
|
return;
|
|
3605
3623
|
}
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3624
|
+
running = true;
|
|
3625
|
+
consecutivePollErrors = 0;
|
|
3626
|
+
process.stderr.write("[projection-worker] Starting...\n");
|
|
3627
|
+
const tick = async () => {
|
|
3628
|
+
if (!running) return;
|
|
3629
|
+
let nextDelay = POLL_INTERVAL_MS;
|
|
3630
|
+
try {
|
|
3631
|
+
const count = await processBatch();
|
|
3632
|
+
consecutivePollErrors = 0;
|
|
3633
|
+
if (count > 0) {
|
|
3634
|
+
process.stderr.write(`[projection-worker] Processed ${count} events.
|
|
3615
3635
|
`);
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3636
|
+
}
|
|
3637
|
+
} catch (err) {
|
|
3638
|
+
consecutivePollErrors++;
|
|
3639
|
+
nextDelay = computeProjectionBackoffMs(consecutivePollErrors);
|
|
3640
|
+
process.stderr.write(
|
|
3641
|
+
`[projection-worker] Poll error (${consecutivePollErrors}; next retry ${Math.round(nextDelay / 1e3)}s): ${err instanceof Error ? err.message : String(err)}
|
|
3620
3642
|
`
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3643
|
+
);
|
|
3644
|
+
}
|
|
3645
|
+
if (running) {
|
|
3646
|
+
pollTimer = setTimeout(tick, nextDelay);
|
|
3647
|
+
}
|
|
3648
|
+
};
|
|
3649
|
+
void tick();
|
|
3650
|
+
})();
|
|
3628
3651
|
}
|
|
3629
3652
|
function stopProjectionWorker() {
|
|
3630
3653
|
running = false;
|
|
@@ -3637,10 +3660,11 @@ function stopProjectionWorker() {
|
|
|
3637
3660
|
async function processProjectionBatch() {
|
|
3638
3661
|
return processBatch();
|
|
3639
3662
|
}
|
|
3640
|
-
var prismaPromise, projectionHandlers, projectionHandlersForTests, defaultHandler, BATCH_SIZE, POLL_INTERVAL_MS, running, pollTimer;
|
|
3663
|
+
var prismaPromise, projectionHandlers, projectionHandlersForTests, defaultHandler, BATCH_SIZE, POLL_INTERVAL_MS, MAX_POLL_BACKOFF_MS, running, pollTimer, consecutivePollErrors;
|
|
3641
3664
|
var init_projection_worker = __esm({
|
|
3642
3665
|
"src/lib/projection-worker.ts"() {
|
|
3643
3666
|
"use strict";
|
|
3667
|
+
init_config();
|
|
3644
3668
|
prismaPromise = null;
|
|
3645
3669
|
projectionHandlers = {
|
|
3646
3670
|
async whatsapp(event, prisma) {
|
|
@@ -3751,8 +3775,10 @@ var init_projection_worker = __esm({
|
|
|
3751
3775
|
};
|
|
3752
3776
|
BATCH_SIZE = 50;
|
|
3753
3777
|
POLL_INTERVAL_MS = 1e4;
|
|
3778
|
+
MAX_POLL_BACKOFF_MS = 5 * 6e4;
|
|
3754
3779
|
running = false;
|
|
3755
3780
|
pollTimer = null;
|
|
3781
|
+
consecutivePollErrors = 0;
|
|
3756
3782
|
}
|
|
3757
3783
|
});
|
|
3758
3784
|
|
|
@@ -21003,218 +21029,6 @@ var init_conflict_detector = __esm({
|
|
|
21003
21029
|
}
|
|
21004
21030
|
});
|
|
21005
21031
|
|
|
21006
|
-
// src/adapters/runtime-hook-manifest.ts
|
|
21007
|
-
function manifestEntryForCommand(command) {
|
|
21008
|
-
return EXE_HOOK_MANIFEST.find((entry) => command.includes(entry.commandMarker));
|
|
21009
|
-
}
|
|
21010
|
-
var EXE_HOOKS, EXE_HOOK_MANIFEST, LEGACY_SPLIT_POST_TOOL_HOOK_MARKERS;
|
|
21011
|
-
var init_runtime_hook_manifest = __esm({
|
|
21012
|
-
"src/adapters/runtime-hook-manifest.ts"() {
|
|
21013
|
-
"use strict";
|
|
21014
|
-
EXE_HOOKS = {
|
|
21015
|
-
postToolCombined: "dist/hooks/post-tool-combined.js",
|
|
21016
|
-
sessionStart: "dist/hooks/session-start.js",
|
|
21017
|
-
promptSubmit: "dist/hooks/prompt-submit.js",
|
|
21018
|
-
heartbeat: "dist/hooks/exe-heartbeat-hook.js",
|
|
21019
|
-
stop: "dist/hooks/stop.js",
|
|
21020
|
-
preToolUse: "dist/hooks/pre-tool-use.js",
|
|
21021
|
-
subagentStop: "dist/hooks/subagent-stop.js",
|
|
21022
|
-
preCompact: "dist/hooks/pre-compact.js",
|
|
21023
|
-
postCompact: "dist/hooks/post-compact.js",
|
|
21024
|
-
sessionEnd: "dist/hooks/session-end.js",
|
|
21025
|
-
notification: "dist/hooks/notification.js",
|
|
21026
|
-
instructionsLoaded: "dist/hooks/instructions-loaded.js"
|
|
21027
|
-
};
|
|
21028
|
-
EXE_HOOK_MANIFEST = [
|
|
21029
|
-
{
|
|
21030
|
-
key: "postToolCombined",
|
|
21031
|
-
event: "PostToolUse",
|
|
21032
|
-
commandMarker: EXE_HOOKS.postToolCombined,
|
|
21033
|
-
owner: "exe-os",
|
|
21034
|
-
purpose: "Single PostToolUse entrypoint for ingestion, error recall, summaries, and bug detection.",
|
|
21035
|
-
runtimes: ["claude", "codex", "opencode"],
|
|
21036
|
-
checkpointRole: "none"
|
|
21037
|
-
},
|
|
21038
|
-
{
|
|
21039
|
-
key: "sessionStart",
|
|
21040
|
-
event: "SessionStart",
|
|
21041
|
-
commandMarker: EXE_HOOKS.sessionStart,
|
|
21042
|
-
owner: "exe-os",
|
|
21043
|
-
purpose: "Loads agent identity, procedures, and boot context.",
|
|
21044
|
-
runtimes: ["claude", "codex", "opencode"],
|
|
21045
|
-
checkpointRole: "none"
|
|
21046
|
-
},
|
|
21047
|
-
{
|
|
21048
|
-
key: "promptSubmit",
|
|
21049
|
-
event: "UserPromptSubmit",
|
|
21050
|
-
commandMarker: EXE_HOOKS.promptSubmit,
|
|
21051
|
-
owner: "exe-os",
|
|
21052
|
-
purpose: "Injects current tasks, pending reviews, and local context before each prompt.",
|
|
21053
|
-
runtimes: ["claude", "codex", "opencode"],
|
|
21054
|
-
checkpointRole: "none"
|
|
21055
|
-
},
|
|
21056
|
-
{
|
|
21057
|
-
key: "heartbeat",
|
|
21058
|
-
event: "UserPromptSubmit",
|
|
21059
|
-
commandMarker: EXE_HOOKS.heartbeat,
|
|
21060
|
-
owner: "exe-os",
|
|
21061
|
-
purpose: "Lightweight heartbeat/status sidecar for Claude Code sessions.",
|
|
21062
|
-
runtimes: ["claude"],
|
|
21063
|
-
checkpointRole: "none"
|
|
21064
|
-
},
|
|
21065
|
-
{
|
|
21066
|
-
key: "stop",
|
|
21067
|
-
event: "Stop",
|
|
21068
|
-
commandMarker: EXE_HOOKS.stop,
|
|
21069
|
-
owner: "exe-os",
|
|
21070
|
-
purpose: "Finalizes task state, capacity signals, and emergency checkpointing.",
|
|
21071
|
-
runtimes: ["claude", "codex", "opencode"],
|
|
21072
|
-
checkpointRole: "capacity_checkpoint"
|
|
21073
|
-
},
|
|
21074
|
-
{
|
|
21075
|
-
key: "preToolUse",
|
|
21076
|
-
event: "PreToolUse",
|
|
21077
|
-
commandMarker: EXE_HOOKS.preToolUse,
|
|
21078
|
-
owner: "exe-os",
|
|
21079
|
-
purpose: "Preflight guardrails before shell/tool execution.",
|
|
21080
|
-
runtimes: ["claude", "codex", "opencode"],
|
|
21081
|
-
checkpointRole: "none"
|
|
21082
|
-
},
|
|
21083
|
-
{
|
|
21084
|
-
key: "subagentStop",
|
|
21085
|
-
event: "SubagentStop",
|
|
21086
|
-
commandMarker: EXE_HOOKS.subagentStop,
|
|
21087
|
-
owner: "exe-os",
|
|
21088
|
-
purpose: "Captures subagent completion context.",
|
|
21089
|
-
runtimes: ["claude"],
|
|
21090
|
-
checkpointRole: "none"
|
|
21091
|
-
},
|
|
21092
|
-
{
|
|
21093
|
-
key: "preCompact",
|
|
21094
|
-
event: "PreCompact",
|
|
21095
|
-
commandMarker: EXE_HOOKS.preCompact,
|
|
21096
|
-
owner: "exe-os",
|
|
21097
|
-
purpose: "Writes active-task snapshot and compaction recovery context.",
|
|
21098
|
-
runtimes: ["claude"],
|
|
21099
|
-
checkpointRole: "recovery_context"
|
|
21100
|
-
},
|
|
21101
|
-
{
|
|
21102
|
-
key: "postCompact",
|
|
21103
|
-
event: "PostCompact",
|
|
21104
|
-
commandMarker: EXE_HOOKS.postCompact,
|
|
21105
|
-
owner: "exe-os",
|
|
21106
|
-
purpose: "Rehydrates recovery context after compaction.",
|
|
21107
|
-
runtimes: ["claude"],
|
|
21108
|
-
checkpointRole: "recovery_context"
|
|
21109
|
-
},
|
|
21110
|
-
{
|
|
21111
|
-
key: "sessionEnd",
|
|
21112
|
-
event: "SessionEnd",
|
|
21113
|
-
commandMarker: EXE_HOOKS.sessionEnd,
|
|
21114
|
-
owner: "exe-os",
|
|
21115
|
-
purpose: "Stores session-end checkpoint and triages orphaned in-progress tasks.",
|
|
21116
|
-
runtimes: ["claude"],
|
|
21117
|
-
checkpointRole: "session_summary"
|
|
21118
|
-
},
|
|
21119
|
-
{
|
|
21120
|
-
key: "notification",
|
|
21121
|
-
event: "Notification",
|
|
21122
|
-
commandMarker: EXE_HOOKS.notification,
|
|
21123
|
-
owner: "exe-os",
|
|
21124
|
-
purpose: "Captures runtime notifications and nudges.",
|
|
21125
|
-
runtimes: ["claude"],
|
|
21126
|
-
checkpointRole: "none"
|
|
21127
|
-
},
|
|
21128
|
-
{
|
|
21129
|
-
key: "instructionsLoaded",
|
|
21130
|
-
event: "InstructionsLoaded",
|
|
21131
|
-
commandMarker: EXE_HOOKS.instructionsLoaded,
|
|
21132
|
-
owner: "exe-os",
|
|
21133
|
-
purpose: "Applies runtime instruction post-processing.",
|
|
21134
|
-
runtimes: ["claude"],
|
|
21135
|
-
checkpointRole: "none"
|
|
21136
|
-
}
|
|
21137
|
-
];
|
|
21138
|
-
LEGACY_SPLIT_POST_TOOL_HOOK_MARKERS = [
|
|
21139
|
-
"dist/hooks/ingest.js",
|
|
21140
|
-
"dist/hooks/error-recall.js",
|
|
21141
|
-
"dist/hooks/ingest-worker.js"
|
|
21142
|
-
];
|
|
21143
|
-
}
|
|
21144
|
-
});
|
|
21145
|
-
|
|
21146
|
-
// src/bin/fast-db-init.ts
|
|
21147
|
-
var fast_db_init_exports = {};
|
|
21148
|
-
__export(fast_db_init_exports, {
|
|
21149
|
-
fastDbInit: () => fastDbInit
|
|
21150
|
-
});
|
|
21151
|
-
async function fastDbInit() {
|
|
21152
|
-
const { isInitialized: isInitialized2, getClient: getClient2, setExternalClient: setExternalClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
21153
|
-
if (isInitialized2()) {
|
|
21154
|
-
return getClient2();
|
|
21155
|
-
}
|
|
21156
|
-
try {
|
|
21157
|
-
const { connectEmbedDaemon: connectEmbedDaemon2, sendDaemonRequest: sendDaemonRequest2, isClientConnected: isClientConnected2 } = await Promise.resolve().then(() => (init_exe_daemon_client(), exe_daemon_client_exports));
|
|
21158
|
-
const { deserializeResultSet: deserializeResultSet2 } = await Promise.resolve().then(() => (init_daemon_protocol(), daemon_protocol_exports));
|
|
21159
|
-
await connectEmbedDaemon2();
|
|
21160
|
-
if (isClientConnected2()) {
|
|
21161
|
-
const daemonClient = {
|
|
21162
|
-
async execute(stmt) {
|
|
21163
|
-
const sql = typeof stmt === "string" ? stmt : stmt.sql;
|
|
21164
|
-
const args = typeof stmt === "string" ? [] : Array.isArray(stmt.args) ? stmt.args : [];
|
|
21165
|
-
const resp = await sendDaemonRequest2({ type: "db-execute", sql, args });
|
|
21166
|
-
if (resp.error) throw new Error(String(resp.error));
|
|
21167
|
-
if (resp.db) return deserializeResultSet2(resp.db);
|
|
21168
|
-
throw new Error("Unexpected daemon response");
|
|
21169
|
-
},
|
|
21170
|
-
async batch(stmts, mode) {
|
|
21171
|
-
const statements = stmts.map((s) => {
|
|
21172
|
-
const sql = typeof s === "string" ? s : s.sql;
|
|
21173
|
-
const args = typeof s === "string" ? [] : Array.isArray(s.args) ? s.args : [];
|
|
21174
|
-
return { sql, args };
|
|
21175
|
-
});
|
|
21176
|
-
const resp = await sendDaemonRequest2({ type: "db-batch", statements, mode: mode ?? "deferred" });
|
|
21177
|
-
if (resp.error) throw new Error(String(resp.error));
|
|
21178
|
-
const batchResults = resp["db-batch"];
|
|
21179
|
-
if (batchResults) return batchResults.map(deserializeResultSet2);
|
|
21180
|
-
throw new Error("Unexpected daemon batch response");
|
|
21181
|
-
},
|
|
21182
|
-
async transaction(_mode) {
|
|
21183
|
-
throw new Error("Transactions not supported via daemon socket");
|
|
21184
|
-
},
|
|
21185
|
-
async executeMultiple(_sql) {
|
|
21186
|
-
throw new Error("executeMultiple not supported via daemon socket");
|
|
21187
|
-
},
|
|
21188
|
-
async migrate(_stmts) {
|
|
21189
|
-
throw new Error("migrate not supported via daemon socket");
|
|
21190
|
-
},
|
|
21191
|
-
sync() {
|
|
21192
|
-
return Promise.resolve(void 0);
|
|
21193
|
-
},
|
|
21194
|
-
close() {
|
|
21195
|
-
},
|
|
21196
|
-
get closed() {
|
|
21197
|
-
return false;
|
|
21198
|
-
},
|
|
21199
|
-
get protocol() {
|
|
21200
|
-
return "file";
|
|
21201
|
-
}
|
|
21202
|
-
};
|
|
21203
|
-
setExternalClient2(daemonClient);
|
|
21204
|
-
return daemonClient;
|
|
21205
|
-
}
|
|
21206
|
-
} catch {
|
|
21207
|
-
}
|
|
21208
|
-
const { initStore: initStore2 } = await Promise.resolve().then(() => (init_store(), store_exports));
|
|
21209
|
-
await initStore2({ lightweight: true });
|
|
21210
|
-
return getClient2();
|
|
21211
|
-
}
|
|
21212
|
-
var init_fast_db_init = __esm({
|
|
21213
|
-
"src/bin/fast-db-init.ts"() {
|
|
21214
|
-
"use strict";
|
|
21215
|
-
}
|
|
21216
|
-
});
|
|
21217
|
-
|
|
21218
21032
|
// src/lib/db-backup.ts
|
|
21219
21033
|
var db_backup_exports = {};
|
|
21220
21034
|
__export(db_backup_exports, {
|
|
@@ -21521,121 +21335,6 @@ function auditHookHealth() {
|
|
|
21521
21335
|
const topPatterns = [...patternCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5).map(([pattern, count]) => ({ pattern, count }));
|
|
21522
21336
|
return { logExists: true, totalLines, errorsLastHour, topPatterns };
|
|
21523
21337
|
}
|
|
21524
|
-
function safeReadJson(filePath) {
|
|
21525
|
-
if (!existsSync31(filePath)) return null;
|
|
21526
|
-
try {
|
|
21527
|
-
return JSON.parse(readFileSync23(filePath, "utf-8"));
|
|
21528
|
-
} catch {
|
|
21529
|
-
return null;
|
|
21530
|
-
}
|
|
21531
|
-
}
|
|
21532
|
-
function collectHookCommandsFromClaudeSettings(settings) {
|
|
21533
|
-
const hooks = settings?.hooks;
|
|
21534
|
-
if (!hooks || typeof hooks !== "object") return [];
|
|
21535
|
-
const commands = [];
|
|
21536
|
-
for (const [event, groups] of Object.entries(hooks)) {
|
|
21537
|
-
if (!Array.isArray(groups)) continue;
|
|
21538
|
-
for (const group of groups) {
|
|
21539
|
-
const hooksForGroup = group?.hooks;
|
|
21540
|
-
if (!Array.isArray(hooksForGroup)) continue;
|
|
21541
|
-
for (const hook of hooksForGroup) {
|
|
21542
|
-
const command = hook?.command;
|
|
21543
|
-
if (typeof command === "string") commands.push({ event, command });
|
|
21544
|
-
}
|
|
21545
|
-
}
|
|
21546
|
-
}
|
|
21547
|
-
return commands;
|
|
21548
|
-
}
|
|
21549
|
-
function collectHookCommandsFromCodexHooks(config2) {
|
|
21550
|
-
const commands = [];
|
|
21551
|
-
if (!config2 || typeof config2 !== "object") return commands;
|
|
21552
|
-
const root = config2;
|
|
21553
|
-
for (const [event, value] of Object.entries(root)) {
|
|
21554
|
-
const entries = Array.isArray(value) ? value : value && typeof value === "object" ? Object.values(value) : [];
|
|
21555
|
-
for (const entry of entries) {
|
|
21556
|
-
if (typeof entry === "string") commands.push({ event, command: entry });
|
|
21557
|
-
const command = entry?.command;
|
|
21558
|
-
if (typeof command === "string") commands.push({ event, command });
|
|
21559
|
-
}
|
|
21560
|
-
}
|
|
21561
|
-
return commands;
|
|
21562
|
-
}
|
|
21563
|
-
function buildHookOwnershipIssues(runtime, commands) {
|
|
21564
|
-
const issues = [];
|
|
21565
|
-
for (const marker of LEGACY_SPLIT_POST_TOOL_HOOK_MARKERS) {
|
|
21566
|
-
const count = commands.filter((cmd) => cmd.command.includes(marker)).length;
|
|
21567
|
-
if (count > 0) {
|
|
21568
|
-
issues.push({
|
|
21569
|
-
runtime,
|
|
21570
|
-
event: "PostToolUse",
|
|
21571
|
-
marker,
|
|
21572
|
-
count,
|
|
21573
|
-
message: `Legacy split PostToolUse hook still installed: ${marker}`
|
|
21574
|
-
});
|
|
21575
|
-
}
|
|
21576
|
-
}
|
|
21577
|
-
for (const entry of EXE_HOOK_MANIFEST.filter((hook) => hook.runtimes.includes(runtime))) {
|
|
21578
|
-
const matching = commands.filter((cmd) => cmd.command.includes(entry.commandMarker));
|
|
21579
|
-
if (matching.length > 1) {
|
|
21580
|
-
issues.push({
|
|
21581
|
-
runtime,
|
|
21582
|
-
event: entry.event,
|
|
21583
|
-
marker: entry.commandMarker,
|
|
21584
|
-
count: matching.length,
|
|
21585
|
-
message: `Duplicate exe-os hook owner for ${entry.event}: ${entry.commandMarker}`
|
|
21586
|
-
});
|
|
21587
|
-
}
|
|
21588
|
-
for (const cmd of matching) {
|
|
21589
|
-
if (cmd.event !== entry.event) {
|
|
21590
|
-
issues.push({
|
|
21591
|
-
runtime,
|
|
21592
|
-
event: cmd.event,
|
|
21593
|
-
marker: entry.commandMarker,
|
|
21594
|
-
count: 1,
|
|
21595
|
-
message: `exe-os hook ${entry.commandMarker} is registered under ${cmd.event}, expected ${entry.event}`
|
|
21596
|
-
});
|
|
21597
|
-
}
|
|
21598
|
-
}
|
|
21599
|
-
}
|
|
21600
|
-
for (const cmd of commands) {
|
|
21601
|
-
if (!cmd.command.includes("dist/hooks/")) continue;
|
|
21602
|
-
if (!cmd.command.includes("exe-os")) continue;
|
|
21603
|
-
const entry = manifestEntryForCommand(cmd.command);
|
|
21604
|
-
const isLegacy = LEGACY_SPLIT_POST_TOOL_HOOK_MARKERS.some((marker) => cmd.command.includes(marker));
|
|
21605
|
-
if (!entry && !isLegacy) {
|
|
21606
|
-
issues.push({
|
|
21607
|
-
runtime,
|
|
21608
|
-
event: cmd.event,
|
|
21609
|
-
marker: "dist/hooks/",
|
|
21610
|
-
count: 1,
|
|
21611
|
-
message: `Unknown exe-os hook command not present in ownership manifest: ${cmd.command.slice(0, 160)}`
|
|
21612
|
-
});
|
|
21613
|
-
}
|
|
21614
|
-
}
|
|
21615
|
-
return issues;
|
|
21616
|
-
}
|
|
21617
|
-
function auditHookOwnership() {
|
|
21618
|
-
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
21619
|
-
const checkedFiles = [];
|
|
21620
|
-
const issues = [];
|
|
21621
|
-
const claudeSettingsPath = path36.join(home, ".claude", "settings.json");
|
|
21622
|
-
const claudeSettings = safeReadJson(claudeSettingsPath);
|
|
21623
|
-
if (claudeSettings) {
|
|
21624
|
-
checkedFiles.push(claudeSettingsPath);
|
|
21625
|
-
issues.push(...buildHookOwnershipIssues("claude", collectHookCommandsFromClaudeSettings(claudeSettings)));
|
|
21626
|
-
}
|
|
21627
|
-
const codexHooksPath = path36.join(home, ".codex", "hooks.json");
|
|
21628
|
-
const codexHooks = safeReadJson(codexHooksPath);
|
|
21629
|
-
if (codexHooks) {
|
|
21630
|
-
checkedFiles.push(codexHooksPath);
|
|
21631
|
-
issues.push(...buildHookOwnershipIssues("codex", collectHookCommandsFromCodexHooks(codexHooks)));
|
|
21632
|
-
}
|
|
21633
|
-
return {
|
|
21634
|
-
checkedFiles,
|
|
21635
|
-
issues,
|
|
21636
|
-
staleLegacyHooks: issues.filter((issue) => issue.message.includes("Legacy split"))
|
|
21637
|
-
};
|
|
21638
|
-
}
|
|
21639
21338
|
async function auditShards() {
|
|
21640
21339
|
try {
|
|
21641
21340
|
const { auditShardHealth: auditShardHealth2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
|
|
@@ -21680,8 +21379,7 @@ async function runAudit(client, flags) {
|
|
|
21680
21379
|
}
|
|
21681
21380
|
const duplicateCount = duplicates.reduce((sum, d) => sum + d.delete_ids.length, 0);
|
|
21682
21381
|
const hookHealth = auditHookHealth();
|
|
21683
|
-
|
|
21684
|
-
return { stats, nullVectors, duplicates, duplicateCount, bloated, fts, orphanedProjects, conflicts, hookHealth, hookOwnership, shards };
|
|
21382
|
+
return { stats, nullVectors, duplicates, duplicateCount, bloated, fts, orphanedProjects, conflicts, hookHealth, shards };
|
|
21685
21383
|
}
|
|
21686
21384
|
function indicator(value, warn) {
|
|
21687
21385
|
if (value === 0) return "\u{1F7E2}";
|
|
@@ -21760,15 +21458,6 @@ function formatReport(report, flags) {
|
|
|
21760
21458
|
lines.push(` ${p.count}x: ${p.pattern}`);
|
|
21761
21459
|
}
|
|
21762
21460
|
}
|
|
21763
|
-
const ho = report.hookOwnership;
|
|
21764
|
-
if (ho.issues.length === 0) {
|
|
21765
|
-
lines.push(`\u{1F7E2} Hook ownership: ${ho.checkedFiles.length > 0 ? "manifest clean" : "no local hook config found"}`);
|
|
21766
|
-
} else {
|
|
21767
|
-
lines.push(`\u{1F534} Hook ownership: ${fmtNum(ho.issues.length)} issue(s)`);
|
|
21768
|
-
for (const issue of ho.issues.slice(0, 5)) {
|
|
21769
|
-
lines.push(` [${issue.runtime}/${issue.event}] ${issue.message}`);
|
|
21770
|
-
}
|
|
21771
|
-
}
|
|
21772
21461
|
const sh = report.shards;
|
|
21773
21462
|
if (sh.total > 0) {
|
|
21774
21463
|
if (sh.unreadable === 0) {
|
|
@@ -21838,9 +21527,6 @@ function formatReport(report, flags) {
|
|
|
21838
21527
|
if (report.conflicts.superseded > 0) {
|
|
21839
21528
|
recs.push(`${fmtNum(report.conflicts.superseded)} superseded memories can be deactivated`);
|
|
21840
21529
|
}
|
|
21841
|
-
if (report.hookOwnership.issues.length > 0) {
|
|
21842
|
-
recs.push(`Run exe-os install to refresh hook config; remove stale exe-os hook commands if they remain`);
|
|
21843
|
-
}
|
|
21844
21530
|
if (recs.length > 0) {
|
|
21845
21531
|
lines.push("Recommendations:");
|
|
21846
21532
|
for (const r of recs) {
|
|
@@ -21974,8 +21660,8 @@ function splitAtSentences(text3, maxChunkSize) {
|
|
|
21974
21660
|
}
|
|
21975
21661
|
async function main(argv = process.argv.slice(2)) {
|
|
21976
21662
|
const flags = parseFlags(argv);
|
|
21977
|
-
|
|
21978
|
-
const client =
|
|
21663
|
+
await initStore();
|
|
21664
|
+
const client = getClient();
|
|
21979
21665
|
const report = await runAudit(client, flags);
|
|
21980
21666
|
console.log(formatReport(report, flags));
|
|
21981
21667
|
if (flags.fix || flags.dryRun) {
|
|
@@ -22035,9 +21721,10 @@ ${mode} Complete.`);
|
|
|
22035
21721
|
var init_exe_doctor = __esm({
|
|
22036
21722
|
"src/bin/exe-doctor.ts"() {
|
|
22037
21723
|
"use strict";
|
|
21724
|
+
init_store();
|
|
21725
|
+
init_database();
|
|
22038
21726
|
init_is_main();
|
|
22039
21727
|
init_conflict_detector();
|
|
22040
|
-
init_runtime_hook_manifest();
|
|
22041
21728
|
if (isMainModule(import.meta.url) && (process.argv[1] ?? "").includes("exe-doctor")) {
|
|
22042
21729
|
main().catch((err) => {
|
|
22043
21730
|
console.error(err instanceof Error ? err.message : String(err));
|
|
@@ -22524,31 +22211,43 @@ function logError(msg) {
|
|
|
22524
22211
|
} catch {
|
|
22525
22212
|
}
|
|
22526
22213
|
}
|
|
22214
|
+
function isTruthyEnv2(value) {
|
|
22215
|
+
return /^(1|true|yes|on)$/i.test(value ?? "");
|
|
22216
|
+
}
|
|
22527
22217
|
function loadPgClient() {
|
|
22528
22218
|
if (_pgFailed) return null;
|
|
22529
|
-
const postgresUrl = process.env.DATABASE_URL;
|
|
22530
22219
|
const configPath = path38.join(EXE_AI_DIR, "config.json");
|
|
22531
22220
|
let cloudPostgresUrl;
|
|
22221
|
+
let configEnabled = false;
|
|
22532
22222
|
try {
|
|
22533
22223
|
if (existsSync33(configPath)) {
|
|
22534
22224
|
const cfg = JSON.parse(readFileSync25(configPath, "utf8"));
|
|
22535
22225
|
cloudPostgresUrl = cfg.cloud?.postgresUrl;
|
|
22536
|
-
|
|
22537
|
-
_pgFailed = true;
|
|
22538
|
-
return null;
|
|
22539
|
-
}
|
|
22226
|
+
configEnabled = cfg.cloud?.syncToPostgres === true;
|
|
22540
22227
|
}
|
|
22541
22228
|
} catch {
|
|
22542
22229
|
}
|
|
22543
|
-
const
|
|
22230
|
+
const envEnabled = isTruthyEnv2(process.env.EXE_CLOUD_SYNC_TO_POSTGRES);
|
|
22231
|
+
if (!envEnabled && !configEnabled) {
|
|
22232
|
+
return null;
|
|
22233
|
+
}
|
|
22234
|
+
const url = process.env.DATABASE_URL || cloudPostgresUrl;
|
|
22544
22235
|
if (!url) {
|
|
22545
22236
|
_pgFailed = true;
|
|
22546
22237
|
return null;
|
|
22547
22238
|
}
|
|
22548
22239
|
if (!_pgPromise) {
|
|
22549
22240
|
_pgPromise = (async () => {
|
|
22241
|
+
if (!process.env.DATABASE_URL) process.env.DATABASE_URL = url;
|
|
22550
22242
|
const { createRequire: createRequire7 } = await import("module");
|
|
22551
22243
|
const { pathToFileURL: pathToFileURL7 } = await import("url");
|
|
22244
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
22245
|
+
if (explicitPath) {
|
|
22246
|
+
const mod2 = await import(pathToFileURL7(explicitPath).href);
|
|
22247
|
+
const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
|
|
22248
|
+
if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
|
|
22249
|
+
return new Ctor2();
|
|
22250
|
+
}
|
|
22552
22251
|
const exeDbRoot = process.env.EXE_DB_ROOT ?? path38.join(homedir6(), "exe-db");
|
|
22553
22252
|
const req = createRequire7(path38.join(exeDbRoot, "package.json"));
|
|
22554
22253
|
const entry = req.resolve("@prisma/client");
|
|
@@ -22745,6 +22444,15 @@ async function cloudSync(config2) {
|
|
|
22745
22444
|
} catch {
|
|
22746
22445
|
throw new Error("[cloud-sync] Database not initialized. Call initStore() before cloudSync().");
|
|
22747
22446
|
}
|
|
22447
|
+
try {
|
|
22448
|
+
const relink = await client.execute("SELECT value FROM sync_meta WHERE key = 'cloud_relink_required' LIMIT 1");
|
|
22449
|
+
if (String(relink.rows[0]?.value ?? "") === "1") {
|
|
22450
|
+
throw new Error("[cloud-sync] Paused after key rotation. Re-link/reupload cloud sync with the new recovery phrase before syncing.");
|
|
22451
|
+
}
|
|
22452
|
+
} catch (err) {
|
|
22453
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
22454
|
+
if (msg.includes("Paused after key rotation")) throw err;
|
|
22455
|
+
}
|
|
22748
22456
|
try {
|
|
22749
22457
|
const { getRawClient: getRawClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
22750
22458
|
await getRawClient2().execute("PRAGMA wal_checkpoint(PASSIVE)");
|