@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
@@ -3,12 +3,6 @@ var __defProp = Object.defineProperty;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
7
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
8
- }) : x)(function(x) {
9
- if (typeof require !== "undefined") return require.apply(this, arguments);
10
- throw Error('Dynamic require of "' + x + '" is not supported');
11
- });
12
6
  var __esm = (fn, res) => function __init() {
13
7
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
14
8
  };
@@ -123,6 +117,7 @@ async function ensureSchema() {
123
117
  const client = getRawClient();
124
118
  await client.execute("PRAGMA journal_mode = WAL");
125
119
  await client.execute("PRAGMA busy_timeout = 30000");
120
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
126
121
  try {
127
122
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
128
123
  } catch {
@@ -938,15 +933,15 @@ __export(config_exports, {
938
933
  migrateConfig: () => migrateConfig,
939
934
  saveConfig: () => saveConfig
940
935
  });
941
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
936
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
942
937
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
943
938
  import path2 from "path";
944
- import os from "os";
939
+ import os2 from "os";
945
940
  function resolveDataDir() {
946
941
  if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
947
942
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
948
- const newDir = path2.join(os.homedir(), ".exe-os");
949
- const legacyDir = path2.join(os.homedir(), ".exe-mem");
943
+ const newDir = path2.join(os2.homedir(), ".exe-os");
944
+ const legacyDir = path2.join(os2.homedir(), ".exe-mem");
950
945
  if (!existsSync2(newDir) && existsSync2(legacyDir)) {
951
946
  try {
952
947
  renameSync(legacyDir, newDir);
@@ -1033,7 +1028,7 @@ async function loadConfig() {
1033
1028
  normalizeAutoUpdate(migratedCfg);
1034
1029
  const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
1035
1030
  if (config.dbPath.startsWith("~")) {
1036
- config.dbPath = config.dbPath.replace(/^~/, os.homedir());
1031
+ config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
1037
1032
  }
1038
1033
  return config;
1039
1034
  } catch {
@@ -1064,6 +1059,9 @@ async function saveConfig(config) {
1064
1059
  await mkdir2(dir, { recursive: true });
1065
1060
  const configPath = path2.join(dir, "config.json");
1066
1061
  await writeFile2(configPath, JSON.stringify(config, null, 2) + "\n");
1062
+ if (config.cloud?.apiKey) {
1063
+ await chmod2(configPath, 384);
1064
+ }
1067
1065
  }
1068
1066
  async function loadConfigFrom(configPath) {
1069
1067
  const raw = await readFile2(configPath, "utf-8");
@@ -1179,7 +1177,7 @@ __export(shard_manager_exports, {
1179
1177
  shardExists: () => shardExists
1180
1178
  });
1181
1179
  import path3 from "path";
1182
- import { existsSync as existsSync3, mkdirSync } from "fs";
1180
+ import { existsSync as existsSync3, mkdirSync, readdirSync } from "fs";
1183
1181
  import { createClient as createClient2 } from "@libsql/client";
1184
1182
  function initShardManager(encryptionKey) {
1185
1183
  _encryptionKey = encryptionKey;
@@ -1218,8 +1216,7 @@ function shardExists(projectName) {
1218
1216
  }
1219
1217
  function listShards() {
1220
1218
  if (!existsSync3(SHARDS_DIR)) return [];
1221
- const { readdirSync: readdirSync3 } = __require("fs");
1222
- return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
1219
+ return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
1223
1220
  }
1224
1221
  async function ensureShardSchema(client) {
1225
1222
  await client.execute("PRAGMA journal_mode = WAL");
@@ -1458,6 +1455,10 @@ import path5 from "path";
1458
1455
  import { fileURLToPath } from "url";
1459
1456
  function handleData(chunk) {
1460
1457
  _buffer += chunk.toString();
1458
+ if (_buffer.length > MAX_BUFFER) {
1459
+ _buffer = "";
1460
+ return;
1461
+ }
1461
1462
  let newlineIdx;
1462
1463
  while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
1463
1464
  const line = _buffer.slice(0, newlineIdx).trim();
@@ -1765,7 +1766,7 @@ function disconnectClient() {
1765
1766
  entry.resolve({ error: "Client disconnected" });
1766
1767
  }
1767
1768
  }
1768
- var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, HEALTH_CHECK_INTERVAL, _pending;
1769
+ var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, HEALTH_CHECK_INTERVAL, _pending, MAX_BUFFER;
1769
1770
  var init_exe_daemon_client = __esm({
1770
1771
  "src/lib/exe-daemon-client.ts"() {
1771
1772
  "use strict";
@@ -1782,6 +1783,7 @@ var init_exe_daemon_client = __esm({
1782
1783
  _requestCount = 0;
1783
1784
  HEALTH_CHECK_INTERVAL = 100;
1784
1785
  _pending = /* @__PURE__ */ new Map();
1786
+ MAX_BUFFER = 1e7;
1785
1787
  }
1786
1788
  });
1787
1789
 
@@ -1884,10 +1886,10 @@ var init_employees = __esm({
1884
1886
  // src/lib/notifications.ts
1885
1887
  import crypto2 from "crypto";
1886
1888
  import path7 from "path";
1887
- import os2 from "os";
1889
+ import os3 from "os";
1888
1890
  import {
1889
1891
  readFileSync as readFileSync4,
1890
- readdirSync,
1892
+ readdirSync as readdirSync2,
1891
1893
  unlinkSync as unlinkSync2,
1892
1894
  existsSync as existsSync6,
1893
1895
  rmdirSync
@@ -2136,12 +2138,12 @@ var init_tasks_crud = __esm({
2136
2138
 
2137
2139
  // src/lib/session-registry.ts
2138
2140
  import path9 from "path";
2139
- import os3 from "os";
2141
+ import os4 from "os";
2140
2142
  var REGISTRY_PATH;
2141
2143
  var init_session_registry = __esm({
2142
2144
  "src/lib/session-registry.ts"() {
2143
2145
  "use strict";
2144
- REGISTRY_PATH = path9.join(os3.homedir(), ".exe-os", "session-registry.json");
2146
+ REGISTRY_PATH = path9.join(os4.homedir(), ".exe-os", "session-registry.json");
2145
2147
  }
2146
2148
  });
2147
2149
 
@@ -2319,7 +2321,7 @@ var init_provider_table = __esm({
2319
2321
  // src/lib/intercom-queue.ts
2320
2322
  import { readFileSync as readFileSync6, writeFileSync, renameSync as renameSync2, existsSync as existsSync8, mkdirSync as mkdirSync2 } from "fs";
2321
2323
  import path10 from "path";
2322
- import os4 from "os";
2324
+ import os5 from "os";
2323
2325
  function ensureDir() {
2324
2326
  const dir = path10.dirname(QUEUE_PATH);
2325
2327
  if (!existsSync8(dir)) mkdirSync2(dir, { recursive: true });
@@ -2359,9 +2361,9 @@ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
2359
2361
  var init_intercom_queue = __esm({
2360
2362
  "src/lib/intercom-queue.ts"() {
2361
2363
  "use strict";
2362
- QUEUE_PATH = path10.join(os4.homedir(), ".exe-os", "intercom-queue.json");
2364
+ QUEUE_PATH = path10.join(os5.homedir(), ".exe-os", "intercom-queue.json");
2363
2365
  TTL_MS = 60 * 60 * 1e3;
2364
- INTERCOM_LOG = path10.join(os4.homedir(), ".exe-os", "intercom.log");
2366
+ INTERCOM_LOG = path10.join(os5.homedir(), ".exe-os", "intercom.log");
2365
2367
  }
2366
2368
  });
2367
2369
 
@@ -2399,7 +2401,7 @@ var init_plan_limits = __esm({
2399
2401
  // src/lib/tmux-routing.ts
2400
2402
  import { readFileSync as readFileSync9, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, existsSync as existsSync11, appendFileSync } from "fs";
2401
2403
  import path13 from "path";
2402
- import os5 from "os";
2404
+ import os6 from "os";
2403
2405
  import { fileURLToPath as fileURLToPath2 } from "url";
2404
2406
  function getParentExe(sessionKey) {
2405
2407
  try {
@@ -2522,10 +2524,10 @@ var init_tmux_routing = __esm({
2522
2524
  init_provider_table();
2523
2525
  init_intercom_queue();
2524
2526
  init_plan_limits();
2525
- SPAWN_LOCK_DIR = path13.join(os5.homedir(), ".exe-os", "spawn-locks");
2526
- SESSION_CACHE = path13.join(os5.homedir(), ".exe-os", "session-cache");
2527
+ SPAWN_LOCK_DIR = path13.join(os6.homedir(), ".exe-os", "spawn-locks");
2528
+ SESSION_CACHE = path13.join(os6.homedir(), ".exe-os", "session-cache");
2527
2529
  INTERCOM_DEBOUNCE_MS = 3e4;
2528
- INTERCOM_LOG2 = path13.join(os5.homedir(), ".exe-os", "intercom.log");
2530
+ INTERCOM_LOG2 = path13.join(os6.homedir(), ".exe-os", "intercom.log");
2529
2531
  DEBOUNCE_FILE = path13.join(SESSION_CACHE, "intercom-debounce.json");
2530
2532
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
2531
2533
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
@@ -2543,7 +2545,7 @@ __export(tasks_review_exports, {
2543
2545
  listPendingReviews: () => listPendingReviews
2544
2546
  });
2545
2547
  import path14 from "path";
2546
- import { existsSync as existsSync12, readdirSync as readdirSync2, unlinkSync as unlinkSync3 } from "fs";
2548
+ import { existsSync as existsSync12, readdirSync as readdirSync3, unlinkSync as unlinkSync3 } from "fs";
2547
2549
  async function countPendingReviews() {
2548
2550
  const client = getClient();
2549
2551
  const result = await client.execute({
@@ -2755,7 +2757,7 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
2755
2757
  try {
2756
2758
  const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
2757
2759
  if (existsSync12(cacheDir)) {
2758
- for (const f of readdirSync2(cacheDir)) {
2760
+ for (const f of readdirSync3(cacheDir)) {
2759
2761
  if (f.startsWith("review-notified-")) {
2760
2762
  unlinkSync3(path14.join(cacheDir, f));
2761
2763
  }
@@ -3000,11 +3002,12 @@ init_database();
3000
3002
  import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
3001
3003
  import { existsSync } from "fs";
3002
3004
  import path from "path";
3005
+ import os from "os";
3003
3006
  import crypto from "crypto";
3004
3007
  var SERVICE = "exe-mem";
3005
3008
  var ACCOUNT = "master-key";
3006
3009
  function getKeyDir() {
3007
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(process.env.HOME ?? "/tmp", ".exe-os");
3010
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
3008
3011
  }
3009
3012
  function getKeyPath() {
3010
3013
  return path.join(getKeyDir(), "master.key");
@@ -3041,6 +3044,30 @@ async function getMasterKey() {
3041
3044
 
3042
3045
  // src/lib/store.ts
3043
3046
  init_config();
3047
+ var INIT_MAX_RETRIES = 3;
3048
+ var INIT_RETRY_DELAY_MS = 1e3;
3049
+ function isBusyError2(err) {
3050
+ if (err instanceof Error) {
3051
+ const msg = err.message.toLowerCase();
3052
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
3053
+ }
3054
+ return false;
3055
+ }
3056
+ async function retryOnBusy2(fn, label) {
3057
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
3058
+ try {
3059
+ return await fn();
3060
+ } catch (err) {
3061
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
3062
+ process.stderr.write(
3063
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
3064
+ `
3065
+ );
3066
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
3067
+ }
3068
+ }
3069
+ throw new Error("unreachable");
3070
+ }
3044
3071
  var _pendingRecords = [];
3045
3072
  var _batchSize = 20;
3046
3073
  var _flushIntervalMs = 1e4;
@@ -3075,14 +3102,17 @@ async function initStore(options) {
3075
3102
  dbPath,
3076
3103
  encryptionKey: hexKey
3077
3104
  });
3078
- await ensureSchema();
3105
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
3079
3106
  try {
3080
3107
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
3081
3108
  initShardManager2(hexKey);
3082
3109
  } catch {
3083
3110
  }
3084
3111
  const client = getClient();
3085
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
3112
+ const vResult = await retryOnBusy2(
3113
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
3114
+ "version-query"
3115
+ );
3086
3116
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
3087
3117
  }
3088
3118
  function classifyTier(record) {
@@ -3125,6 +3155,12 @@ async function writeMemory(record) {
3125
3155
  supersedes_id: record.supersedes_id ?? null
3126
3156
  };
3127
3157
  _pendingRecords.push(dbRow);
3158
+ const MAX_PENDING = 1e3;
3159
+ if (_pendingRecords.length > MAX_PENDING) {
3160
+ const dropped = _pendingRecords.length - MAX_PENDING;
3161
+ _pendingRecords = _pendingRecords.slice(-MAX_PENDING);
3162
+ console.warn(`[store] Dropped ${dropped} oldest pending records (overflow)`);
3163
+ }
3128
3164
  if (_flushTimer === null) {
3129
3165
  _flushTimer = setInterval(() => {
3130
3166
  void flushBatch();
@@ -2,7 +2,7 @@
2
2
  import { createInterface } from "readline";
3
3
 
4
4
  // src/lib/config.ts
5
- import { readFile, writeFile, mkdir } from "fs/promises";
5
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
6
6
  import { readFileSync, existsSync, renameSync } from "fs";
7
7
  import path from "path";
8
8
  import os from "os";
@@ -188,6 +188,9 @@ async function saveConfig(config) {
188
188
  await mkdir(dir, { recursive: true });
189
189
  const configPath = path.join(dir, "config.json");
190
190
  await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
191
+ if (config.cloud?.apiKey) {
192
+ await chmod(configPath, 384);
193
+ }
191
194
  }
192
195
 
193
196
  // src/lib/is-main.ts
@@ -204,6 +207,62 @@ function isMainModule(importMetaUrl) {
204
207
  }
205
208
  }
206
209
 
210
+ // src/lib/cloud-sync.ts
211
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync5, readdirSync, mkdirSync as mkdirSync2, appendFileSync, unlinkSync } from "fs";
212
+ import path5 from "path";
213
+ import { homedir } from "os";
214
+
215
+ // src/lib/database.ts
216
+ import { createClient } from "@libsql/client";
217
+
218
+ // src/lib/crypto.ts
219
+ import crypto from "crypto";
220
+
221
+ // src/lib/compress.ts
222
+ import { brotliCompressSync, brotliDecompressSync, constants } from "zlib";
223
+
224
+ // src/lib/plan-limits.ts
225
+ import { readFileSync as readFileSync4, existsSync as existsSync4 } from "fs";
226
+ import path4 from "path";
227
+
228
+ // src/lib/employees.ts
229
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
230
+ import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2 } from "fs";
231
+ import { execSync } from "child_process";
232
+ import path2 from "path";
233
+ var EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
234
+
235
+ // src/lib/license.ts
236
+ import { readFileSync as readFileSync3, writeFileSync, existsSync as existsSync3, mkdirSync } from "fs";
237
+ import { randomUUID } from "crypto";
238
+ import path3 from "path";
239
+ import { jwtVerify, importSPKI } from "jose";
240
+ var LICENSE_PATH = path3.join(EXE_AI_DIR, "license.key");
241
+ var CACHE_PATH = path3.join(EXE_AI_DIR, "license-cache.json");
242
+ var DEVICE_ID_PATH = path3.join(EXE_AI_DIR, "device-id");
243
+
244
+ // src/lib/plan-limits.ts
245
+ var CACHE_PATH2 = path4.join(EXE_AI_DIR, "license-cache.json");
246
+
247
+ // src/lib/cloud-sync.ts
248
+ var LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
249
+ var ROSTER_LOCK_PATH = path5.join(EXE_AI_DIR, "roster-merge.lock");
250
+ function assertSecureEndpoint(endpoint) {
251
+ if (endpoint.startsWith("https://")) return;
252
+ if (endpoint.startsWith("http://")) {
253
+ try {
254
+ const parsed = new URL(endpoint);
255
+ if (LOCALHOST_PATTERNS.test(parsed.hostname)) return;
256
+ } catch {
257
+ return;
258
+ }
259
+ throw new Error(
260
+ `Insecure cloud endpoint rejected: "${endpoint}". Use https:// for remote hosts. Plain http:// is only allowed for localhost.`
261
+ );
262
+ }
263
+ }
264
+ var ROSTER_DELETIONS_PATH = path5.join(EXE_AI_DIR, "roster-deletions.json");
265
+
207
266
  // src/bin/exe-settings.ts
208
267
  function label(value) {
209
268
  return value ? "ON" : "OFF";
@@ -315,9 +374,11 @@ async function main() {
315
374
  const endpoint = ep.trim() || "https://askexe.com/cloud";
316
375
  console.log(" Validating...");
317
376
  try {
377
+ assertSecureEndpoint(endpoint);
318
378
  const resp = await fetch(`${endpoint}/auth/verify`, {
319
379
  method: "POST",
320
- headers: { "Authorization": `Bearer ${key}`, "Content-Type": "application/json" }
380
+ headers: { "Authorization": `Bearer ${key}`, "Content-Type": "application/json" },
381
+ signal: AbortSignal.timeout(3e4)
321
382
  });
322
383
  if (resp.ok) {
323
384
  config.cloud = { apiKey: key, endpoint };
@@ -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,7 +10,7 @@ var __export = (target, all) => {
16
10
  };
17
11
 
18
12
  // src/lib/config.ts
19
- import { readFile, writeFile, mkdir } from "fs/promises";
13
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
20
14
  import { readFileSync, existsSync, renameSync } from "fs";
21
15
  import path from "path";
22
16
  import os from "os";
@@ -218,7 +212,7 @@ __export(shard_manager_exports, {
218
212
  shardExists: () => shardExists
219
213
  });
220
214
  import path4 from "path";
221
- import { existsSync as existsSync4, mkdirSync } from "fs";
215
+ import { existsSync as existsSync4, 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 (!existsSync4(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) {
@@ -552,6 +545,7 @@ async function ensureSchema() {
552
545
  const client = getRawClient();
553
546
  await client.execute("PRAGMA journal_mode = WAL");
554
547
  await client.execute("PRAGMA busy_timeout = 30000");
548
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
555
549
  try {
556
550
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
557
551
  } catch {
@@ -1342,14 +1336,15 @@ async function ensureSchema() {
1342
1336
  }
1343
1337
 
1344
1338
  // src/lib/keychain.ts
1345
- import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod } from "fs/promises";
1339
+ import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
1346
1340
  import { existsSync as existsSync3 } from "fs";
1347
1341
  import path3 from "path";
1342
+ import os2 from "os";
1348
1343
  import crypto from "crypto";
1349
1344
  var SERVICE = "exe-mem";
1350
1345
  var ACCOUNT = "master-key";
1351
1346
  function getKeyDir() {
1352
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(process.env.HOME ?? "/tmp", ".exe-os");
1347
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(os2.homedir(), ".exe-os");
1353
1348
  }
1354
1349
  function getKeyPath() {
1355
1350
  return path3.join(getKeyDir(), "master.key");
@@ -1386,6 +1381,30 @@ async function getMasterKey() {
1386
1381
 
1387
1382
  // src/lib/store.ts
1388
1383
  init_config();
1384
+ var INIT_MAX_RETRIES = 3;
1385
+ var INIT_RETRY_DELAY_MS = 1e3;
1386
+ function isBusyError2(err) {
1387
+ if (err instanceof Error) {
1388
+ const msg = err.message.toLowerCase();
1389
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
1390
+ }
1391
+ return false;
1392
+ }
1393
+ async function retryOnBusy2(fn, label) {
1394
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
1395
+ try {
1396
+ return await fn();
1397
+ } catch (err) {
1398
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
1399
+ process.stderr.write(
1400
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
1401
+ `
1402
+ );
1403
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
1404
+ }
1405
+ }
1406
+ throw new Error("unreachable");
1407
+ }
1389
1408
  var _pendingRecords = [];
1390
1409
  var _batchSize = 20;
1391
1410
  var _flushIntervalMs = 1e4;
@@ -1420,14 +1439,17 @@ async function initStore(options) {
1420
1439
  dbPath,
1421
1440
  encryptionKey: hexKey
1422
1441
  });
1423
- await ensureSchema();
1442
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1424
1443
  try {
1425
1444
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1426
1445
  initShardManager2(hexKey);
1427
1446
  } catch {
1428
1447
  }
1429
1448
  const client = getClient();
1430
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
1449
+ const vResult = await retryOnBusy2(
1450
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
1451
+ "version-query"
1452
+ );
1431
1453
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1432
1454
  }
1433
1455
 
@@ -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,7 +10,7 @@ var __export = (target, all) => {
16
10
  };
17
11
 
18
12
  // src/lib/config.ts
19
- import { readFile, writeFile, mkdir } from "fs/promises";
13
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
20
14
  import { readFileSync, existsSync, renameSync } from "fs";
21
15
  import path from "path";
22
16
  import os from "os";
@@ -218,7 +212,7 @@ __export(shard_manager_exports, {
218
212
  shardExists: () => shardExists
219
213
  });
220
214
  import path4 from "path";
221
- import { existsSync as existsSync4, mkdirSync } from "fs";
215
+ import { existsSync as existsSync4, 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 (!existsSync4(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) {
@@ -552,6 +545,7 @@ async function ensureSchema() {
552
545
  const client = getRawClient();
553
546
  await client.execute("PRAGMA journal_mode = WAL");
554
547
  await client.execute("PRAGMA busy_timeout = 30000");
548
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
555
549
  try {
556
550
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
557
551
  } catch {
@@ -1342,14 +1336,15 @@ async function ensureSchema() {
1342
1336
  }
1343
1337
 
1344
1338
  // src/lib/keychain.ts
1345
- import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod } from "fs/promises";
1339
+ import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
1346
1340
  import { existsSync as existsSync3 } from "fs";
1347
1341
  import path3 from "path";
1342
+ import os2 from "os";
1348
1343
  import crypto from "crypto";
1349
1344
  var SERVICE = "exe-mem";
1350
1345
  var ACCOUNT = "master-key";
1351
1346
  function getKeyDir() {
1352
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(process.env.HOME ?? "/tmp", ".exe-os");
1347
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(os2.homedir(), ".exe-os");
1353
1348
  }
1354
1349
  function getKeyPath() {
1355
1350
  return path3.join(getKeyDir(), "master.key");
@@ -1386,6 +1381,30 @@ async function getMasterKey() {
1386
1381
 
1387
1382
  // src/lib/store.ts
1388
1383
  init_config();
1384
+ var INIT_MAX_RETRIES = 3;
1385
+ var INIT_RETRY_DELAY_MS = 1e3;
1386
+ function isBusyError2(err) {
1387
+ if (err instanceof Error) {
1388
+ const msg = err.message.toLowerCase();
1389
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
1390
+ }
1391
+ return false;
1392
+ }
1393
+ async function retryOnBusy2(fn, label) {
1394
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
1395
+ try {
1396
+ return await fn();
1397
+ } catch (err) {
1398
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
1399
+ process.stderr.write(
1400
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
1401
+ `
1402
+ );
1403
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
1404
+ }
1405
+ }
1406
+ throw new Error("unreachable");
1407
+ }
1389
1408
  var _pendingRecords = [];
1390
1409
  var _batchSize = 20;
1391
1410
  var _flushIntervalMs = 1e4;
@@ -1420,14 +1439,17 @@ async function initStore(options) {
1420
1439
  dbPath,
1421
1440
  encryptionKey: hexKey
1422
1441
  });
1423
- await ensureSchema();
1442
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1424
1443
  try {
1425
1444
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1426
1445
  initShardManager2(hexKey);
1427
1446
  } catch {
1428
1447
  }
1429
1448
  const client = getClient();
1430
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
1449
+ const vResult = await retryOnBusy2(
1450
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
1451
+ "version-query"
1452
+ );
1431
1453
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1432
1454
  }
1433
1455