@askexenow/exe-os 0.8.37 → 0.8.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/README.md +17 -8
  2. package/dist/bin/backfill-conversations.js +112 -70
  3. package/dist/bin/backfill-responses.js +53 -18
  4. package/dist/bin/backfill-vectors.js +43 -16
  5. package/dist/bin/cleanup-stale-review-tasks.js +38 -16
  6. package/dist/bin/cli.js +790 -468
  7. package/dist/bin/exe-agent.js +19 -4
  8. package/dist/bin/exe-assign.js +46 -13
  9. package/dist/bin/exe-boot.js +288 -129
  10. package/dist/bin/exe-call.js +20 -10
  11. package/dist/bin/exe-cloud.js +135 -30
  12. package/dist/bin/exe-dispatch.js +1 -1
  13. package/dist/bin/exe-doctor.js +38 -16
  14. package/dist/bin/exe-export-behaviors.js +43 -21
  15. package/dist/bin/exe-forget.js +39 -17
  16. package/dist/bin/exe-gateway.js +159 -50
  17. package/dist/bin/exe-heartbeat.js +53 -31
  18. package/dist/bin/exe-kill.js +40 -18
  19. package/dist/bin/exe-launch-agent.js +109 -36
  20. package/dist/bin/exe-link.js +196 -87
  21. package/dist/bin/exe-new-employee.js +56 -17
  22. package/dist/bin/exe-pending-messages.js +47 -25
  23. package/dist/bin/exe-pending-notifications.js +38 -16
  24. package/dist/bin/exe-pending-reviews.js +51 -29
  25. package/dist/bin/exe-rename.js +21 -7
  26. package/dist/bin/exe-review.js +41 -13
  27. package/dist/bin/exe-search.js +57 -21
  28. package/dist/bin/exe-session-cleanup.js +67 -31
  29. package/dist/bin/exe-settings.js +63 -2
  30. package/dist/bin/exe-status.js +35 -13
  31. package/dist/bin/exe-team.js +35 -13
  32. package/dist/bin/git-sweep.js +45 -17
  33. package/dist/bin/graph-backfill.js +38 -16
  34. package/dist/bin/graph-export.js +38 -16
  35. package/dist/bin/install.js +10 -1
  36. package/dist/bin/scan-tasks.js +47 -19
  37. package/dist/bin/setup.js +444 -259
  38. package/dist/bin/shard-migrate.js +38 -16
  39. package/dist/bin/wiki-sync.js +40 -17
  40. package/dist/gateway/index.js +113 -48
  41. package/dist/hooks/bug-report-worker.js +66 -39
  42. package/dist/hooks/commit-complete.js +45 -17
  43. package/dist/hooks/error-recall.js +60 -20
  44. package/dist/hooks/exe-heartbeat-hook.js +3 -2
  45. package/dist/hooks/ingest-worker.js +174 -45
  46. package/dist/hooks/ingest.js +74 -28
  47. package/dist/hooks/instructions-loaded.js +46 -17
  48. package/dist/hooks/notification.js +44 -15
  49. package/dist/hooks/post-compact.js +44 -15
  50. package/dist/hooks/pre-compact.js +42 -14
  51. package/dist/hooks/pre-tool-use.js +59 -22
  52. package/dist/hooks/prompt-ingest-worker.js +75 -14
  53. package/dist/hooks/prompt-submit.js +75 -32
  54. package/dist/hooks/response-ingest-worker.js +76 -15
  55. package/dist/hooks/session-end.js +54 -22
  56. package/dist/hooks/session-start.js +57 -20
  57. package/dist/hooks/stop.js +44 -15
  58. package/dist/hooks/subagent-stop.js +44 -15
  59. package/dist/hooks/summary-worker.js +339 -106
  60. package/dist/index.js +94 -23
  61. package/dist/lib/cloud-sync.js +191 -80
  62. package/dist/lib/config.js +4 -1
  63. package/dist/lib/consolidation.js +5 -4
  64. package/dist/lib/database.js +1 -0
  65. package/dist/lib/device-registry.js +2 -1
  66. package/dist/lib/embedder.js +9 -1
  67. package/dist/lib/employee-templates.js +5 -0
  68. package/dist/lib/employees.js +11 -6
  69. package/dist/lib/exe-daemon-client.js +6 -1
  70. package/dist/lib/exe-daemon.js +95 -36
  71. package/dist/lib/hybrid-search.js +57 -21
  72. package/dist/lib/identity-templates.js +16 -7
  73. package/dist/lib/identity.js +1 -1
  74. package/dist/lib/keychain.js +2 -1
  75. package/dist/lib/license.js +56 -6
  76. package/dist/lib/messaging.js +1 -1
  77. package/dist/lib/reminders.js +2 -2
  78. package/dist/lib/schedules.js +38 -16
  79. package/dist/lib/skill-learning.js +1 -1
  80. package/dist/lib/store.js +44 -16
  81. package/dist/lib/tasks.js +1 -1
  82. package/dist/lib/tmux-routing.js +1 -1
  83. package/dist/mcp/server.js +280 -155
  84. package/dist/mcp/tools/complete-reminder.js +1 -1
  85. package/dist/mcp/tools/create-task.js +14 -6
  86. package/dist/mcp/tools/deactivate-behavior.js +2 -2
  87. package/dist/mcp/tools/list-reminders.js +1 -1
  88. package/dist/mcp/tools/list-tasks.js +36 -28
  89. package/dist/mcp/tools/send-message.js +1 -1
  90. package/dist/mcp/tools/update-task.js +1 -1
  91. package/dist/runtime/index.js +42 -8
  92. package/dist/tui/App.js +220 -99
  93. package/package.json +5 -3
@@ -1,6 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  var __defProp = Object.defineProperty;
3
3
  var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
5
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
6
+ }) : x)(function(x) {
7
+ if (typeof require !== "undefined") return require.apply(this, arguments);
8
+ throw Error('Dynamic require of "' + x + '" is not supported');
9
+ });
4
10
  var __esm = (fn, res) => function __init() {
5
11
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
12
  };
@@ -10,7 +16,7 @@ var __export = (target, all) => {
10
16
  };
11
17
 
12
18
  // src/lib/config.ts
13
- import { readFile, writeFile, mkdir } from "fs/promises";
19
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
14
20
  import { readFileSync, existsSync, renameSync } from "fs";
15
21
  import path from "path";
16
22
  import os from "os";
@@ -134,6 +140,7 @@ var init_database = __esm({
134
140
  var identity_templates_exports = {};
135
141
  __export(identity_templates_exports, {
136
142
  IDENTITY_TEMPLATES: () => IDENTITY_TEMPLATES,
143
+ PLAN_MODE_COMPAT: () => PLAN_MODE_COMPAT,
137
144
  POST_WORK_CHECKLIST: () => POST_WORK_CHECKLIST,
138
145
  getTemplate: () => getTemplate2,
139
146
  getTemplateForTitle: () => getTemplateForTitle
@@ -153,10 +160,18 @@ function getTemplateForTitle(title) {
153
160
  if (t.includes("review") || t.includes("audit") || t.includes("qa")) return IDENTITY_TEMPLATES["staff-code-reviewer"];
154
161
  return null;
155
162
  }
156
- var POST_WORK_CHECKLIST, IDENTITY_TEMPLATES;
163
+ var PLAN_MODE_COMPAT, POST_WORK_CHECKLIST, IDENTITY_TEMPLATES;
157
164
  var init_identity_templates = __esm({
158
165
  "src/lib/identity-templates.ts"() {
159
166
  "use strict";
167
+ PLAN_MODE_COMPAT = `
168
+ ## Plan Mode Compatibility
169
+ If tool execution is unavailable (e.g., CC plan mode), switch to planning:
170
+ - Reason about the task and create a written plan
171
+ - Document what tools you would call and with what parameters
172
+ - Output structured text that can be acted on when tools become available
173
+ Do not repeatedly attempt tool calls that fail \u2014 switch to planning mode.
174
+ `;
160
175
  POST_WORK_CHECKLIST = `
161
176
  5. Check for pending reviews (list_tasks status='needs_review' where you are reviewer) \u2014 reviews are work, process before new tasks
162
177
  6. Check for blocked tasks (list_tasks status='blocked') \u2014 can you unblock it? Do it now. Can't? Escalate to exe immediately.
@@ -236,7 +251,7 @@ Never say "I have no memories" without first searching broadly. Your memory may
236
251
  - **update_identity** \u2014 rewrite any agent's identity when role/responsibilities change (exe/founder only)
237
252
  - **get_identity** \u2014 read any agent's identity for coordination
238
253
  - **send_message** \u2014 direct intercom to employees
239
-
254
+ ${PLAN_MODE_COMPAT}
240
255
  ## Completion Workflow
241
256
 
242
257
  1. Read the task file and verify the deliverable matches the brief
@@ -307,7 +322,7 @@ You are \${agent_id}. CTO. You hold deep context on the entire codebase, archite
307
322
  - **store_behavior** \u2014 record corrections for engineers (p0 = always injected)
308
323
  - **get_identity** \u2014 read any agent's identity for review context
309
324
  - **query_relationships** \u2014 GraphRAG entity connections for architecture analysis
310
-
325
+ ${PLAN_MODE_COMPAT}
311
326
  ## Completion Workflow
312
327
 
313
328
  1. Read ARCHITECTURE.md before starting work on any repo
@@ -374,7 +389,7 @@ You are \${agent_id}. CMO. You hold deep context on design, branding, storytelli
374
389
  - **update_task** \u2014 mark tasks done with result summary
375
390
  - **store_memory** \u2014 report completions with brand alignment notes, SEO considerations
376
391
  - **get_identity** \u2014 read team identities for brand-consistent communication
377
-
392
+ ${PLAN_MODE_COMPAT}
378
393
  ## Completion Workflow
379
394
 
380
395
  1. Read the task file and understand the brief \u2014 tone, format, channel requirements
@@ -441,7 +456,7 @@ You are a principal engineer. You write production-grade code with zero shortcut
441
456
  - **recall_my_memory** \u2014 check past work, patterns, gotchas in this project
442
457
  - **store_memory** \u2014 report completions for org visibility
443
458
  - **ask_team_memory** \u2014 pull context from colleagues when specs reference their work
444
-
459
+ ${PLAN_MODE_COMPAT}
445
460
  ## Completion Workflow
446
461
 
447
462
  1. Read ARCHITECTURE.md if it exists \u2014 understand architecture before changing anything
@@ -501,7 +516,7 @@ You are the content production specialist. You turn scripts and creative briefs
501
516
  - **update_task** \u2014 mark tasks done with result summary
502
517
  - **recall_my_memory** \u2014 check past work: which models worked, which prompts produced good results
503
518
  - **store_memory** \u2014 report completions with production decisions for future reference
504
-
519
+ ${PLAN_MODE_COMPAT}
505
520
  ## Completion Workflow
506
521
 
507
522
  1. Read the task file \u2014 understand the brief, check budget constraints
@@ -573,7 +588,7 @@ You are the AI Product Lead \u2014 the competitive intelligence engine. You stud
573
588
  - **update_task** \u2014 mark tasks done with analysis results
574
589
  - **store_memory** \u2014 persist competitive analyses, evaluations, recommendations
575
590
  - **create_task** \u2014 when a feature is worth building, spec it for the CTO
576
-
591
+ ${PLAN_MODE_COMPAT}
577
592
  ## Completion Workflow
578
593
 
579
594
  1. Read the task \u2014 understand what capability is needed
@@ -636,7 +651,7 @@ You are \${agent_id}. Staff Code Reviewer and System Auditor. Last line of defen
636
651
  - **store_behavior** \u2014 record new patterns
637
652
  - **update_task** \u2014 mark reviews done with structured findings
638
653
  - **create_task** \u2014 assign fixes to the CTO
639
-
654
+ ${PLAN_MODE_COMPAT}
640
655
  ## Completion Workflow
641
656
 
642
657
  1. Read the task brief and understand the audit scope
@@ -834,15 +849,20 @@ function addEmployee(employees, employee) {
834
849
  }
835
850
  return [...employees, normalized];
836
851
  }
852
+ function findExeBin() {
853
+ try {
854
+ return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
855
+ } catch {
856
+ return null;
857
+ }
858
+ }
837
859
  function registerBinSymlinks(name) {
838
860
  const created = [];
839
861
  const skipped = [];
840
862
  const errors = [];
841
- let exeBinPath;
842
- try {
843
- exeBinPath = execSync("which exe", { encoding: "utf-8" }).trim();
844
- } catch {
845
- errors.push("Could not find 'exe' in PATH");
863
+ const exeBinPath = findExeBin();
864
+ if (!exeBinPath) {
865
+ errors.push("Could not find 'exe-os' in PATH");
846
866
  return { created, skipped, errors };
847
867
  }
848
868
  const binDir = path2.dirname(exeBinPath);
@@ -1223,6 +1243,15 @@ var LICENSE_PATH = path3.join(EXE_AI_DIR, "license.key");
1223
1243
  var CACHE_PATH = path3.join(EXE_AI_DIR, "license-cache.json");
1224
1244
  var DEVICE_ID_PATH = path3.join(EXE_AI_DIR, "device-id");
1225
1245
  var API_BASE = "https://askexe.com/cloud";
1246
+ var RETRY_DELAY_MS = 500;
1247
+ async function fetchRetry(url, init) {
1248
+ try {
1249
+ return await fetch(url, init);
1250
+ } catch {
1251
+ await new Promise((r) => setTimeout(r, RETRY_DELAY_MS));
1252
+ return fetch(url, { ...init, signal: AbortSignal.timeout(1e4) });
1253
+ }
1254
+ }
1226
1255
  var LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
1227
1256
  MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
1228
1257
  4uj+UqeKCcvtgNHKmOK278HJaJcANe9xAeji8AFYu27q3WtzCi04pHudow==
@@ -1314,7 +1343,7 @@ function cacheResponse(token) {
1314
1343
  async function validateLicense(apiKey, deviceId) {
1315
1344
  const did = deviceId ?? loadDeviceId();
1316
1345
  try {
1317
- const res = await fetch(`${API_BASE}/auth/activate`, {
1346
+ const res = await fetchRetry(`${API_BASE}/auth/activate`, {
1318
1347
  method: "POST",
1319
1348
  headers: { "Content-Type": "application/json" },
1320
1349
  body: JSON.stringify({ apiKey, deviceId: did }),
@@ -1349,14 +1378,24 @@ async function validateLicense(apiKey, deviceId) {
1349
1378
  } catch {
1350
1379
  const cached = await getCachedLicense();
1351
1380
  if (cached) return cached;
1352
- return FREE_LICENSE;
1381
+ return { ...FREE_LICENSE, valid: false, error: "offline" };
1382
+ }
1383
+ }
1384
+ var CACHE_MAX_AGE_MS = 36e5;
1385
+ function getCacheAgeMs() {
1386
+ try {
1387
+ const { statSync } = __require("fs");
1388
+ const s = statSync(CACHE_PATH);
1389
+ return Date.now() - s.mtimeMs;
1390
+ } catch {
1391
+ return Infinity;
1353
1392
  }
1354
1393
  }
1355
1394
  async function checkLicense() {
1356
1395
  const key = loadLicense();
1357
1396
  if (!key) return FREE_LICENSE;
1358
1397
  const cached = await getCachedLicense();
1359
- if (cached) return cached;
1398
+ if (cached && getCacheAgeMs() < CACHE_MAX_AGE_MS) return cached;
1360
1399
  const deviceId = loadDeviceId();
1361
1400
  return validateLicense(key, deviceId);
1362
1401
  }
@@ -1,11 +1,5 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __getOwnPropNames = Object.getOwnPropertyNames;
3
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
4
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
5
- }) : x)(function(x) {
6
- if (typeof require !== "undefined") return require.apply(this, arguments);
7
- throw Error('Dynamic require of "' + x + '" is not supported');
8
- });
9
3
  var __esm = (fn, res) => function __init() {
10
4
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
5
  };
@@ -102,6 +96,7 @@ async function ensureSchema() {
102
96
  const client = getRawClient();
103
97
  await client.execute("PRAGMA journal_mode = WAL");
104
98
  await client.execute("PRAGMA busy_timeout = 30000");
99
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
105
100
  try {
106
101
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
107
102
  } catch {
@@ -902,15 +897,15 @@ var init_database = __esm({
902
897
  });
903
898
 
904
899
  // src/lib/config.ts
905
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
900
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
906
901
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
907
902
  import path2 from "path";
908
- import os from "os";
903
+ import os2 from "os";
909
904
  function resolveDataDir() {
910
905
  if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
911
906
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
912
- const newDir = path2.join(os.homedir(), ".exe-os");
913
- const legacyDir = path2.join(os.homedir(), ".exe-mem");
907
+ const newDir = path2.join(os2.homedir(), ".exe-os");
908
+ const legacyDir = path2.join(os2.homedir(), ".exe-mem");
914
909
  if (!existsSync2(newDir) && existsSync2(legacyDir)) {
915
910
  try {
916
911
  renameSync(legacyDir, newDir);
@@ -997,7 +992,7 @@ async function loadConfig() {
997
992
  normalizeAutoUpdate(migratedCfg);
998
993
  const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
999
994
  if (config.dbPath.startsWith("~")) {
1000
- config.dbPath = config.dbPath.replace(/^~/, os.homedir());
995
+ config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
1001
996
  }
1002
997
  return config;
1003
998
  } catch {
@@ -1104,7 +1099,7 @@ __export(shard_manager_exports, {
1104
1099
  shardExists: () => shardExists
1105
1100
  });
1106
1101
  import path3 from "path";
1107
- import { existsSync as existsSync3, mkdirSync } from "fs";
1102
+ import { existsSync as existsSync3, mkdirSync, readdirSync } from "fs";
1108
1103
  import { createClient as createClient2 } from "@libsql/client";
1109
1104
  function initShardManager(encryptionKey) {
1110
1105
  _encryptionKey = encryptionKey;
@@ -1143,7 +1138,6 @@ function shardExists(projectName) {
1143
1138
  }
1144
1139
  function listShards() {
1145
1140
  if (!existsSync3(SHARDS_DIR)) return [];
1146
- const { readdirSync } = __require("fs");
1147
1141
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
1148
1142
  }
1149
1143
  async function ensureShardSchema(client) {
@@ -1334,12 +1328,12 @@ var init_shard_manager = __esm({
1334
1328
 
1335
1329
  // src/lib/session-registry.ts
1336
1330
  import path4 from "path";
1337
- import os2 from "os";
1331
+ import os3 from "os";
1338
1332
  var REGISTRY_PATH;
1339
1333
  var init_session_registry = __esm({
1340
1334
  "src/lib/session-registry.ts"() {
1341
1335
  "use strict";
1342
- REGISTRY_PATH = path4.join(os2.homedir(), ".exe-os", "session-registry.json");
1336
+ REGISTRY_PATH = path4.join(os3.homedir(), ".exe-os", "session-registry.json");
1343
1337
  }
1344
1338
  });
1345
1339
 
@@ -1390,14 +1384,14 @@ var init_provider_table = __esm({
1390
1384
  // src/lib/intercom-queue.ts
1391
1385
  import { readFileSync as readFileSync2, writeFileSync, renameSync as renameSync2, existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
1392
1386
  import path5 from "path";
1393
- import os3 from "os";
1387
+ import os4 from "os";
1394
1388
  var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
1395
1389
  var init_intercom_queue = __esm({
1396
1390
  "src/lib/intercom-queue.ts"() {
1397
1391
  "use strict";
1398
- QUEUE_PATH = path5.join(os3.homedir(), ".exe-os", "intercom-queue.json");
1392
+ QUEUE_PATH = path5.join(os4.homedir(), ".exe-os", "intercom-queue.json");
1399
1393
  TTL_MS = 60 * 60 * 1e3;
1400
- INTERCOM_LOG = path5.join(os3.homedir(), ".exe-os", "intercom.log");
1394
+ INTERCOM_LOG = path5.join(os4.homedir(), ".exe-os", "intercom.log");
1401
1395
  }
1402
1396
  });
1403
1397
 
@@ -1448,7 +1442,7 @@ var init_plan_limits = __esm({
1448
1442
 
1449
1443
  // src/lib/tmux-routing.ts
1450
1444
  import path9 from "path";
1451
- import os4 from "os";
1445
+ import os5 from "os";
1452
1446
  import { fileURLToPath } from "url";
1453
1447
  var SPAWN_LOCK_DIR, SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
1454
1448
  var init_tmux_routing = __esm({
@@ -1462,9 +1456,9 @@ var init_tmux_routing = __esm({
1462
1456
  init_provider_table();
1463
1457
  init_intercom_queue();
1464
1458
  init_plan_limits();
1465
- SPAWN_LOCK_DIR = path9.join(os4.homedir(), ".exe-os", "spawn-locks");
1466
- SESSION_CACHE = path9.join(os4.homedir(), ".exe-os", "session-cache");
1467
- INTERCOM_LOG2 = path9.join(os4.homedir(), ".exe-os", "intercom.log");
1459
+ SPAWN_LOCK_DIR = path9.join(os5.homedir(), ".exe-os", "spawn-locks");
1460
+ SESSION_CACHE = path9.join(os5.homedir(), ".exe-os", "session-cache");
1461
+ INTERCOM_LOG2 = path9.join(os5.homedir(), ".exe-os", "intercom.log");
1468
1462
  DEBOUNCE_FILE = path9.join(SESSION_CACHE, "intercom-debounce.json");
1469
1463
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
1470
1464
  }
@@ -1477,11 +1471,12 @@ init_database();
1477
1471
  import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
1478
1472
  import { existsSync } from "fs";
1479
1473
  import path from "path";
1474
+ import os from "os";
1480
1475
  import crypto from "crypto";
1481
1476
  var SERVICE = "exe-mem";
1482
1477
  var ACCOUNT = "master-key";
1483
1478
  function getKeyDir() {
1484
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(process.env.HOME ?? "/tmp", ".exe-os");
1479
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
1485
1480
  }
1486
1481
  function getKeyPath() {
1487
1482
  return path.join(getKeyDir(), "master.key");
@@ -1518,6 +1513,30 @@ async function getMasterKey() {
1518
1513
 
1519
1514
  // src/lib/store.ts
1520
1515
  init_config();
1516
+ var INIT_MAX_RETRIES = 3;
1517
+ var INIT_RETRY_DELAY_MS = 1e3;
1518
+ function isBusyError2(err) {
1519
+ if (err instanceof Error) {
1520
+ const msg = err.message.toLowerCase();
1521
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
1522
+ }
1523
+ return false;
1524
+ }
1525
+ async function retryOnBusy2(fn, label) {
1526
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
1527
+ try {
1528
+ return await fn();
1529
+ } catch (err) {
1530
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
1531
+ process.stderr.write(
1532
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
1533
+ `
1534
+ );
1535
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
1536
+ }
1537
+ }
1538
+ throw new Error("unreachable");
1539
+ }
1521
1540
  var _pendingRecords = [];
1522
1541
  var _batchSize = 20;
1523
1542
  var _flushIntervalMs = 1e4;
@@ -1552,14 +1571,17 @@ async function initStore(options) {
1552
1571
  dbPath,
1553
1572
  encryptionKey: hexKey
1554
1573
  });
1555
- await ensureSchema();
1574
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1556
1575
  try {
1557
1576
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1558
1577
  initShardManager2(hexKey);
1559
1578
  } catch {
1560
1579
  }
1561
1580
  const client = getClient();
1562
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
1581
+ const vResult = await retryOnBusy2(
1582
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
1583
+ "version-query"
1584
+ );
1563
1585
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1564
1586
  }
1565
1587
 
@@ -1,12 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  var __defProp = Object.defineProperty;
3
3
  var __getOwnPropNames = Object.getOwnPropertyNames;
4
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
5
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
6
- }) : x)(function(x) {
7
- if (typeof require !== "undefined") return require.apply(this, arguments);
8
- throw Error('Dynamic require of "' + x + '" is not supported');
9
- });
10
4
  var __esm = (fn, res) => function __init() {
11
5
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
12
6
  };
@@ -16,15 +10,15 @@ var __export = (target, all) => {
16
10
  };
17
11
 
18
12
  // src/lib/config.ts
19
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
13
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
20
14
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
21
15
  import path2 from "path";
22
- import os from "os";
16
+ import os2 from "os";
23
17
  function resolveDataDir() {
24
18
  if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
25
19
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
26
- const newDir = path2.join(os.homedir(), ".exe-os");
27
- const legacyDir = path2.join(os.homedir(), ".exe-mem");
20
+ const newDir = path2.join(os2.homedir(), ".exe-os");
21
+ const legacyDir = path2.join(os2.homedir(), ".exe-mem");
28
22
  if (!existsSync2(newDir) && existsSync2(legacyDir)) {
29
23
  try {
30
24
  renameSync(legacyDir, newDir);
@@ -111,7 +105,7 @@ async function loadConfig() {
111
105
  normalizeAutoUpdate(migratedCfg);
112
106
  const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
113
107
  if (config.dbPath.startsWith("~")) {
114
- config.dbPath = config.dbPath.replace(/^~/, os.homedir());
108
+ config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
115
109
  }
116
110
  return config;
117
111
  } catch {
@@ -218,7 +212,7 @@ __export(shard_manager_exports, {
218
212
  shardExists: () => shardExists
219
213
  });
220
214
  import path3 from "path";
221
- import { existsSync as existsSync3, mkdirSync } from "fs";
215
+ import { existsSync as existsSync3, mkdirSync, readdirSync } from "fs";
222
216
  import { createClient as createClient2 } from "@libsql/client";
223
217
  function initShardManager(encryptionKey) {
224
218
  _encryptionKey = encryptionKey;
@@ -257,7 +251,6 @@ function shardExists(projectName) {
257
251
  }
258
252
  function listShards() {
259
253
  if (!existsSync3(SHARDS_DIR)) return [];
260
- const { readdirSync } = __require("fs");
261
254
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
262
255
  }
263
256
  async function ensureShardSchema(client) {
@@ -533,6 +526,7 @@ async function ensureSchema() {
533
526
  const client = getRawClient();
534
527
  await client.execute("PRAGMA journal_mode = WAL");
535
528
  await client.execute("PRAGMA busy_timeout = 30000");
529
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
536
530
  try {
537
531
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
538
532
  } catch {
@@ -1326,11 +1320,12 @@ async function ensureSchema() {
1326
1320
  import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
1327
1321
  import { existsSync } from "fs";
1328
1322
  import path from "path";
1323
+ import os from "os";
1329
1324
  import crypto from "crypto";
1330
1325
  var SERVICE = "exe-mem";
1331
1326
  var ACCOUNT = "master-key";
1332
1327
  function getKeyDir() {
1333
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(process.env.HOME ?? "/tmp", ".exe-os");
1328
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
1334
1329
  }
1335
1330
  function getKeyPath() {
1336
1331
  return path.join(getKeyDir(), "master.key");
@@ -1367,6 +1362,30 @@ async function getMasterKey() {
1367
1362
 
1368
1363
  // src/lib/store.ts
1369
1364
  init_config();
1365
+ var INIT_MAX_RETRIES = 3;
1366
+ var INIT_RETRY_DELAY_MS = 1e3;
1367
+ function isBusyError2(err) {
1368
+ if (err instanceof Error) {
1369
+ const msg = err.message.toLowerCase();
1370
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
1371
+ }
1372
+ return false;
1373
+ }
1374
+ async function retryOnBusy2(fn, label) {
1375
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
1376
+ try {
1377
+ return await fn();
1378
+ } catch (err) {
1379
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
1380
+ process.stderr.write(
1381
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
1382
+ `
1383
+ );
1384
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
1385
+ }
1386
+ }
1387
+ throw new Error("unreachable");
1388
+ }
1370
1389
  var _pendingRecords = [];
1371
1390
  var _batchSize = 20;
1372
1391
  var _flushIntervalMs = 1e4;
@@ -1401,14 +1420,17 @@ async function initStore(options) {
1401
1420
  dbPath,
1402
1421
  encryptionKey: hexKey
1403
1422
  });
1404
- await ensureSchema();
1423
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1405
1424
  try {
1406
1425
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1407
1426
  initShardManager2(hexKey);
1408
1427
  } catch {
1409
1428
  }
1410
1429
  const client = getClient();
1411
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
1430
+ const vResult = await retryOnBusy2(
1431
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
1432
+ "version-query"
1433
+ );
1412
1434
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1413
1435
  }
1414
1436