@askexenow/exe-os 0.9.30 → 0.9.32

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 (64) hide show
  1. package/dist/bin/backfill-conversations.js +135 -7
  2. package/dist/bin/backfill-responses.js +135 -7
  3. package/dist/bin/backfill-vectors.js +135 -7
  4. package/dist/bin/cleanup-stale-review-tasks.js +139 -11
  5. package/dist/bin/cli.js +812 -486
  6. package/dist/bin/exe-assign.js +135 -7
  7. package/dist/bin/exe-boot.js +422 -113
  8. package/dist/bin/exe-cloud.js +160 -9
  9. package/dist/bin/exe-dispatch.js +136 -8
  10. package/dist/bin/exe-doctor.js +255 -13
  11. package/dist/bin/exe-export-behaviors.js +136 -8
  12. package/dist/bin/exe-forget.js +136 -8
  13. package/dist/bin/exe-gateway.js +171 -24
  14. package/dist/bin/exe-heartbeat.js +141 -13
  15. package/dist/bin/exe-kill.js +140 -12
  16. package/dist/bin/exe-launch-agent.js +143 -15
  17. package/dist/bin/exe-link.js +357 -48
  18. package/dist/bin/exe-pending-messages.js +136 -8
  19. package/dist/bin/exe-pending-notifications.js +136 -8
  20. package/dist/bin/exe-pending-reviews.js +138 -10
  21. package/dist/bin/exe-review.js +136 -8
  22. package/dist/bin/exe-search.js +155 -20
  23. package/dist/bin/exe-session-cleanup.js +166 -38
  24. package/dist/bin/exe-start-codex.js +142 -14
  25. package/dist/bin/exe-start-opencode.js +140 -12
  26. package/dist/bin/exe-status.js +148 -20
  27. package/dist/bin/exe-team.js +136 -8
  28. package/dist/bin/git-sweep.js +138 -10
  29. package/dist/bin/graph-backfill.js +135 -7
  30. package/dist/bin/graph-export.js +136 -8
  31. package/dist/bin/intercom-check.js +153 -25
  32. package/dist/bin/scan-tasks.js +138 -10
  33. package/dist/bin/setup.js +447 -121
  34. package/dist/bin/shard-migrate.js +135 -7
  35. package/dist/gateway/index.js +151 -23
  36. package/dist/hooks/bug-report-worker.js +151 -23
  37. package/dist/hooks/codex-stop-task-finalizer.js +145 -17
  38. package/dist/hooks/commit-complete.js +138 -10
  39. package/dist/hooks/error-recall.js +159 -24
  40. package/dist/hooks/ingest.js +142 -14
  41. package/dist/hooks/instructions-loaded.js +136 -8
  42. package/dist/hooks/notification.js +136 -8
  43. package/dist/hooks/post-compact.js +136 -8
  44. package/dist/hooks/post-tool-combined.js +159 -24
  45. package/dist/hooks/pre-compact.js +136 -8
  46. package/dist/hooks/pre-tool-use.js +144 -16
  47. package/dist/hooks/prompt-submit.js +195 -55
  48. package/dist/hooks/session-end.js +141 -13
  49. package/dist/hooks/session-start.js +165 -30
  50. package/dist/hooks/stop.js +136 -8
  51. package/dist/hooks/subagent-stop.js +136 -8
  52. package/dist/hooks/summary-worker.js +374 -65
  53. package/dist/index.js +136 -8
  54. package/dist/lib/cloud-sync.js +355 -46
  55. package/dist/lib/consolidation.js +1 -0
  56. package/dist/lib/exe-daemon.js +469 -127
  57. package/dist/lib/hybrid-search.js +155 -20
  58. package/dist/lib/keychain.js +191 -7
  59. package/dist/lib/schedules.js +138 -10
  60. package/dist/lib/store.js +135 -7
  61. package/dist/mcp/server.js +706 -213
  62. package/dist/runtime/index.js +136 -8
  63. package/dist/tui/App.js +208 -31
  64. package/package.json +1 -1
@@ -1172,8 +1172,8 @@ function findPackageRoot() {
1172
1172
  function getAvailableMemoryGB() {
1173
1173
  if (process.platform === "darwin") {
1174
1174
  try {
1175
- const { execSync: execSync10 } = __require("child_process");
1176
- const vmstat = execSync10("vm_stat", { encoding: "utf8" });
1175
+ const { execSync: execSync11 } = __require("child_process");
1176
+ const vmstat = execSync11("vm_stat", { encoding: "utf8" });
1177
1177
  const pageSize = 16384;
1178
1178
  const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1179
1179
  const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
@@ -3114,6 +3114,7 @@ __export(keychain_exports, {
3114
3114
  });
3115
3115
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
3116
3116
  import { existsSync as existsSync6 } from "fs";
3117
+ import { execSync as execSync2 } from "child_process";
3117
3118
  import path6 from "path";
3118
3119
  import os5 from "os";
3119
3120
  function getKeyDir() {
@@ -3122,6 +3123,83 @@ function getKeyDir() {
3122
3123
  function getKeyPath() {
3123
3124
  return path6.join(getKeyDir(), "master.key");
3124
3125
  }
3126
+ function macKeychainGet() {
3127
+ if (process.platform !== "darwin") return null;
3128
+ try {
3129
+ return execSync2(
3130
+ `security find-generic-password -s "${SERVICE}" -a "${ACCOUNT}" -w 2>/dev/null`,
3131
+ { encoding: "utf-8", timeout: 5e3 }
3132
+ ).trim();
3133
+ } catch {
3134
+ return null;
3135
+ }
3136
+ }
3137
+ function macKeychainSet(value) {
3138
+ if (process.platform !== "darwin") return false;
3139
+ try {
3140
+ try {
3141
+ execSync2(
3142
+ `security delete-generic-password -s "${SERVICE}" -a "${ACCOUNT}" 2>/dev/null`,
3143
+ { timeout: 5e3 }
3144
+ );
3145
+ } catch {
3146
+ }
3147
+ execSync2(
3148
+ `security add-generic-password -s "${SERVICE}" -a "${ACCOUNT}" -w "${value}"`,
3149
+ { timeout: 5e3 }
3150
+ );
3151
+ return true;
3152
+ } catch {
3153
+ return false;
3154
+ }
3155
+ }
3156
+ function macKeychainDelete() {
3157
+ if (process.platform !== "darwin") return false;
3158
+ try {
3159
+ execSync2(
3160
+ `security delete-generic-password -s "${SERVICE}" -a "${ACCOUNT}" 2>/dev/null`,
3161
+ { timeout: 5e3 }
3162
+ );
3163
+ return true;
3164
+ } catch {
3165
+ return false;
3166
+ }
3167
+ }
3168
+ function linuxSecretGet() {
3169
+ if (process.platform !== "linux") return null;
3170
+ try {
3171
+ return execSync2(
3172
+ `secret-tool lookup service "${SERVICE}" account "${ACCOUNT}" 2>/dev/null`,
3173
+ { encoding: "utf-8", timeout: 5e3 }
3174
+ ).trim();
3175
+ } catch {
3176
+ return null;
3177
+ }
3178
+ }
3179
+ function linuxSecretSet(value) {
3180
+ if (process.platform !== "linux") return false;
3181
+ try {
3182
+ execSync2(
3183
+ `echo -n "${value}" | secret-tool store --label="exe-os master key" service "${SERVICE}" account "${ACCOUNT}"`,
3184
+ { timeout: 5e3 }
3185
+ );
3186
+ return true;
3187
+ } catch {
3188
+ return false;
3189
+ }
3190
+ }
3191
+ function linuxSecretDelete() {
3192
+ if (process.platform !== "linux") return false;
3193
+ try {
3194
+ execSync2(
3195
+ `secret-tool clear service "${SERVICE}" account "${ACCOUNT}" 2>/dev/null`,
3196
+ { timeout: 5e3 }
3197
+ );
3198
+ return true;
3199
+ } catch {
3200
+ return false;
3201
+ }
3202
+ }
3125
3203
  async function tryKeytar() {
3126
3204
  try {
3127
3205
  return await import("keytar");
@@ -3129,13 +3207,72 @@ async function tryKeytar() {
3129
3207
  return null;
3130
3208
  }
3131
3209
  }
3210
+ function deriveMachineKey() {
3211
+ try {
3212
+ const crypto10 = __require("crypto");
3213
+ const material = [
3214
+ os5.hostname(),
3215
+ os5.userInfo().username,
3216
+ os5.arch(),
3217
+ os5.platform(),
3218
+ // Machine ID on Linux (stable across reboots)
3219
+ process.platform === "linux" ? readMachineId() : ""
3220
+ ].join("|");
3221
+ return crypto10.createHash("sha256").update(material).digest();
3222
+ } catch {
3223
+ return null;
3224
+ }
3225
+ }
3226
+ function readMachineId() {
3227
+ try {
3228
+ const { readFileSync: readFileSync17 } = __require("fs");
3229
+ return readFileSync17("/etc/machine-id", "utf-8").trim();
3230
+ } catch {
3231
+ return "";
3232
+ }
3233
+ }
3234
+ function encryptWithMachineKey(plaintext, machineKey) {
3235
+ const crypto10 = __require("crypto");
3236
+ const iv = crypto10.randomBytes(12);
3237
+ const cipher = crypto10.createCipheriv("aes-256-gcm", machineKey, iv);
3238
+ let encrypted = cipher.update(plaintext, "utf-8", "base64");
3239
+ encrypted += cipher.final("base64");
3240
+ const authTag = cipher.getAuthTag().toString("base64");
3241
+ return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
3242
+ }
3243
+ function decryptWithMachineKey(encrypted, machineKey) {
3244
+ if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
3245
+ try {
3246
+ const crypto10 = __require("crypto");
3247
+ const parts = encrypted.slice(ENCRYPTED_PREFIX.length).split(":");
3248
+ if (parts.length !== 3) return null;
3249
+ const [ivB64, tagB64, cipherB64] = parts;
3250
+ const iv = Buffer.from(ivB64, "base64");
3251
+ const authTag = Buffer.from(tagB64, "base64");
3252
+ const decipher = crypto10.createDecipheriv("aes-256-gcm", machineKey, iv);
3253
+ decipher.setAuthTag(authTag);
3254
+ let decrypted = decipher.update(cipherB64, "base64", "utf-8");
3255
+ decrypted += decipher.final("utf-8");
3256
+ return decrypted;
3257
+ } catch {
3258
+ return null;
3259
+ }
3260
+ }
3132
3261
  async function getMasterKey() {
3262
+ const nativeValue = macKeychainGet() ?? linuxSecretGet();
3263
+ if (nativeValue) {
3264
+ return Buffer.from(nativeValue, "base64");
3265
+ }
3133
3266
  const keytar = await tryKeytar();
3134
3267
  if (keytar) {
3135
3268
  try {
3136
- const stored = await keytar.getPassword(SERVICE, ACCOUNT);
3137
- if (stored) {
3138
- return Buffer.from(stored, "base64");
3269
+ const keytarValue = await keytar.getPassword(SERVICE, ACCOUNT);
3270
+ if (keytarValue) {
3271
+ const migrated = macKeychainSet(keytarValue) || linuxSecretSet(keytarValue);
3272
+ if (migrated) {
3273
+ process.stderr.write("[keychain] Migrated key from keytar to native keychain.\n");
3274
+ }
3275
+ return Buffer.from(keytarValue, "base64");
3139
3276
  }
3140
3277
  } catch {
3141
3278
  }
@@ -3149,8 +3286,31 @@ async function getMasterKey() {
3149
3286
  return null;
3150
3287
  }
3151
3288
  try {
3152
- const content = await readFile3(keyPath, "utf-8");
3153
- return Buffer.from(content.trim(), "base64");
3289
+ const content = (await readFile3(keyPath, "utf-8")).trim();
3290
+ let b64Value;
3291
+ if (content.startsWith(ENCRYPTED_PREFIX)) {
3292
+ const machineKey = deriveMachineKey();
3293
+ if (!machineKey) {
3294
+ process.stderr.write("[keychain] Cannot derive machine key to decrypt stored key.\n");
3295
+ return null;
3296
+ }
3297
+ const decrypted = decryptWithMachineKey(content, machineKey);
3298
+ if (!decrypted) {
3299
+ process.stderr.write(
3300
+ "[keychain] Key decryption failed \u2014 machine may have changed.\n Use your 24-word recovery phrase: exe-os link import\n"
3301
+ );
3302
+ return null;
3303
+ }
3304
+ b64Value = decrypted;
3305
+ } else {
3306
+ b64Value = content;
3307
+ }
3308
+ const key = Buffer.from(b64Value, "base64");
3309
+ const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
3310
+ if (migrated) {
3311
+ process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
3312
+ }
3313
+ return key;
3154
3314
  } catch (err) {
3155
3315
  process.stderr.write(
3156
3316
  `[keychain] Key read failed at ${keyPath}: ${err instanceof Error ? err.message : String(err)}
@@ -3161,6 +3321,9 @@ async function getMasterKey() {
3161
3321
  }
3162
3322
  async function setMasterKey(key) {
3163
3323
  const b64 = key.toString("base64");
3324
+ if (macKeychainSet(b64) || linuxSecretSet(b64)) {
3325
+ return;
3326
+ }
3164
3327
  const keytar = await tryKeytar();
3165
3328
  if (keytar) {
3166
3329
  try {
@@ -3172,10 +3335,23 @@ async function setMasterKey(key) {
3172
3335
  const dir = getKeyDir();
3173
3336
  await mkdir3(dir, { recursive: true });
3174
3337
  const keyPath = getKeyPath();
3175
- await writeFile3(keyPath, b64 + "\n", "utf-8");
3176
- await chmod2(keyPath, 384);
3338
+ const machineKey = deriveMachineKey();
3339
+ if (machineKey) {
3340
+ const encrypted = encryptWithMachineKey(b64, machineKey);
3341
+ await writeFile3(keyPath, encrypted + "\n", "utf-8");
3342
+ await chmod2(keyPath, 384);
3343
+ process.stderr.write("[keychain] Key stored encrypted (machine-bound).\n");
3344
+ } else {
3345
+ await writeFile3(keyPath, b64 + "\n", "utf-8");
3346
+ await chmod2(keyPath, 384);
3347
+ process.stderr.write(
3348
+ "[keychain] WARNING: Key stored in plaintext file \u2014 no OS keychain available.\n"
3349
+ );
3350
+ }
3177
3351
  }
3178
3352
  async function deleteMasterKey() {
3353
+ macKeychainDelete();
3354
+ linuxSecretDelete();
3179
3355
  const keytar = await tryKeytar();
3180
3356
  if (keytar) {
3181
3357
  try {
@@ -3217,12 +3393,13 @@ async function importMnemonic(mnemonic) {
3217
3393
  const entropy = mnemonicToEntropy(trimmed);
3218
3394
  return Buffer.from(entropy, "hex");
3219
3395
  }
3220
- var SERVICE, ACCOUNT;
3396
+ var SERVICE, ACCOUNT, ENCRYPTED_PREFIX;
3221
3397
  var init_keychain = __esm({
3222
3398
  "src/lib/keychain.ts"() {
3223
3399
  "use strict";
3224
3400
  SERVICE = "exe-mem";
3225
3401
  ACCOUNT = "master-key";
3402
+ ENCRYPTED_PREFIX = "enc:";
3226
3403
  }
3227
3404
  });
3228
3405
 
@@ -3709,7 +3886,7 @@ __export(session_registry_exports, {
3709
3886
  registerSession: () => registerSession
3710
3887
  });
3711
3888
  import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync8 } from "fs";
3712
- import { execSync as execSync2 } from "child_process";
3889
+ import { execSync as execSync3 } from "child_process";
3713
3890
  import path8 from "path";
3714
3891
  import os6 from "os";
3715
3892
  function registerSession(entry) {
@@ -3749,7 +3926,7 @@ function pruneStaleSessions() {
3749
3926
  if (sessions.length === 0) return 0;
3750
3927
  let liveSessions = [];
3751
3928
  try {
3752
- liveSessions = execSync2("tmux list-sessions -F '#{session_name}' 2>/dev/null", {
3929
+ liveSessions = execSync3("tmux list-sessions -F '#{session_name}' 2>/dev/null", {
3753
3930
  encoding: "utf8"
3754
3931
  }).trim().split("\n").filter(Boolean);
3755
3932
  } catch {
@@ -3772,7 +3949,7 @@ var init_session_registry = __esm({
3772
3949
  });
3773
3950
 
3774
3951
  // src/lib/session-key.ts
3775
- import { execSync as execSync3 } from "child_process";
3952
+ import { execSync as execSync4 } from "child_process";
3776
3953
  function normalizeCommand(command) {
3777
3954
  const trimmed = command.trim().toLowerCase();
3778
3955
  const parts = trimmed.split(/[\\/]/);
@@ -3791,7 +3968,7 @@ function resolveRuntimeProcess() {
3791
3968
  let pid = process.ppid;
3792
3969
  for (let i = 0; i < 10; i++) {
3793
3970
  try {
3794
- const info = execSync3(`ps -p ${pid} -o ppid=,comm=`, {
3971
+ const info = execSync4(`ps -p ${pid} -o ppid=,comm=`, {
3795
3972
  encoding: "utf8",
3796
3973
  timeout: 2e3
3797
3974
  }).trim();
@@ -3957,14 +4134,14 @@ var init_transport = __esm({
3957
4134
  });
3958
4135
 
3959
4136
  // src/lib/cc-agent-support.ts
3960
- import { execSync as execSync4 } from "child_process";
4137
+ import { execSync as execSync5 } from "child_process";
3961
4138
  function _resetCcAgentSupportCache() {
3962
4139
  _cachedSupport = null;
3963
4140
  }
3964
4141
  function claudeSupportsAgentFlag() {
3965
4142
  if (_cachedSupport !== null) return _cachedSupport;
3966
4143
  try {
3967
- const helpOutput = execSync4("claude --help 2>&1", {
4144
+ const helpOutput = execSync5("claude --help 2>&1", {
3968
4145
  encoding: "utf-8",
3969
4146
  timeout: 5e3
3970
4147
  });
@@ -4478,8 +4655,8 @@ async function validateLicense(apiKey, deviceId) {
4478
4655
  }
4479
4656
  function getCacheAgeMs() {
4480
4657
  try {
4481
- const { statSync: statSync2 } = __require("fs");
4482
- const s = statSync2(CACHE_PATH);
4658
+ const { statSync: statSync3 } = __require("fs");
4659
+ const s = statSync3(CACHE_PATH);
4483
4660
  return Date.now() - s.mtimeMs;
4484
4661
  } catch {
4485
4662
  return Infinity;
@@ -5056,7 +5233,7 @@ __export(project_name_exports, {
5056
5233
  _resetCache: () => _resetCache,
5057
5234
  getProjectName: () => getProjectName
5058
5235
  });
5059
- import { execSync as execSync5 } from "child_process";
5236
+ import { execSync as execSync6 } from "child_process";
5060
5237
  import path14 from "path";
5061
5238
  function getProjectName(cwd) {
5062
5239
  const dir = cwd ?? process.cwd();
@@ -5064,7 +5241,7 @@ function getProjectName(cwd) {
5064
5241
  try {
5065
5242
  let repoRoot;
5066
5243
  try {
5067
- const gitCommonDir = execSync5("git rev-parse --path-format=absolute --git-common-dir", {
5244
+ const gitCommonDir = execSync6("git rev-parse --path-format=absolute --git-common-dir", {
5068
5245
  cwd: dir,
5069
5246
  encoding: "utf8",
5070
5247
  timeout: 2e3,
@@ -5072,7 +5249,7 @@ function getProjectName(cwd) {
5072
5249
  }).trim();
5073
5250
  repoRoot = path14.dirname(gitCommonDir);
5074
5251
  } catch {
5075
- repoRoot = execSync5("git rev-parse --show-toplevel", {
5252
+ repoRoot = execSync6("git rev-parse --show-toplevel", {
5076
5253
  cwd: dir,
5077
5254
  encoding: "utf8",
5078
5255
  timeout: 2e3,
@@ -5167,7 +5344,7 @@ var init_session_scope = __esm({
5167
5344
  import crypto4 from "crypto";
5168
5345
  import path15 from "path";
5169
5346
  import os10 from "os";
5170
- import { execSync as execSync6 } from "child_process";
5347
+ import { execSync as execSync7 } from "child_process";
5171
5348
  import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
5172
5349
  import { existsSync as existsSync14, readFileSync as readFileSync11 } from "fs";
5173
5350
  async function writeCheckpoint(input) {
@@ -5512,14 +5689,14 @@ function isTmuxSessionAlive(identifier) {
5512
5689
  if (!identifier || identifier === "unknown") return true;
5513
5690
  try {
5514
5691
  if (identifier.startsWith("%")) {
5515
- const output = execSync6("tmux list-panes -a -F '#{pane_id}'", {
5692
+ const output = execSync7("tmux list-panes -a -F '#{pane_id}'", {
5516
5693
  timeout: 2e3,
5517
5694
  encoding: "utf8",
5518
5695
  stdio: ["pipe", "pipe", "pipe"]
5519
5696
  });
5520
5697
  return output.split("\n").some((l) => l.trim() === identifier);
5521
5698
  } else {
5522
- execSync6(`tmux has-session -t ${JSON.stringify(identifier)}`, {
5699
+ execSync7(`tmux has-session -t ${JSON.stringify(identifier)}`, {
5523
5700
  timeout: 2e3,
5524
5701
  stdio: ["pipe", "pipe", "pipe"]
5525
5702
  });
@@ -5528,7 +5705,7 @@ function isTmuxSessionAlive(identifier) {
5528
5705
  } catch {
5529
5706
  if (identifier.startsWith("%")) return true;
5530
5707
  try {
5531
- execSync6("tmux list-sessions", {
5708
+ execSync7("tmux list-sessions", {
5532
5709
  timeout: 2e3,
5533
5710
  stdio: ["pipe", "pipe", "pipe"]
5534
5711
  });
@@ -5543,12 +5720,12 @@ function checkStaleCompletion(taskContext, taskCreatedAt) {
5543
5720
  if (!DELEGATION_KEYWORDS.test(taskContext)) return null;
5544
5721
  try {
5545
5722
  const since = new Date(taskCreatedAt).toISOString();
5546
- const branch = execSync6(
5723
+ const branch = execSync7(
5547
5724
  "git rev-parse --abbrev-ref HEAD 2>/dev/null",
5548
5725
  { encoding: "utf8", timeout: 3e3 }
5549
5726
  ).trim();
5550
5727
  const branchArg = branch && branch !== "HEAD" ? branch : "";
5551
- const commitCount = execSync6(
5728
+ const commitCount = execSync7(
5552
5729
  `git log --oneline --since="${since}" ${branchArg} 2>/dev/null | wc -l`,
5553
5730
  { encoding: "utf8", timeout: 5e3 }
5554
5731
  ).trim();
@@ -6286,10 +6463,10 @@ async function disposeEmbedder() {
6286
6463
  async function embedDirect(text) {
6287
6464
  const llamaCpp = await import("node-llama-cpp");
6288
6465
  const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
6289
- const { existsSync: existsSync21 } = await import("fs");
6290
- const path25 = await import("path");
6291
- const modelPath = path25.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
6292
- if (!existsSync21(modelPath)) {
6466
+ const { existsSync: existsSync22 } = await import("fs");
6467
+ const path26 = await import("path");
6468
+ const modelPath = path26.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
6469
+ if (!existsSync22(modelPath)) {
6293
6470
  throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
6294
6471
  }
6295
6472
  const llama = await llamaCpp.getLlama();
@@ -7146,7 +7323,7 @@ __export(tmux_routing_exports, {
7146
7323
  spawnEmployee: () => spawnEmployee,
7147
7324
  verifyPaneAtCapacity: () => verifyPaneAtCapacity
7148
7325
  });
7149
- import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
7326
+ import { execFileSync as execFileSync2, execSync as execSync8 } from "child_process";
7150
7327
  import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, mkdirSync as mkdirSync7, existsSync as existsSync16, appendFileSync, readdirSync as readdirSync4 } from "fs";
7151
7328
  import path19 from "path";
7152
7329
  import os11 from "os";
@@ -7856,7 +8033,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7856
8033
  let booted = false;
7857
8034
  for (let i = 0; i < 30; i++) {
7858
8035
  try {
7859
- execSync7("sleep 0.5");
8036
+ execSync8("sleep 0.5");
7860
8037
  } catch {
7861
8038
  }
7862
8039
  try {
@@ -8396,6 +8573,109 @@ var init_crdt_sync = __esm({
8396
8573
  }
8397
8574
  });
8398
8575
 
8576
+ // src/lib/db-backup.ts
8577
+ var db_backup_exports = {};
8578
+ __export(db_backup_exports, {
8579
+ createBackup: () => createBackup,
8580
+ findActiveDb: () => findActiveDb,
8581
+ getBackupDir: () => getBackupDir,
8582
+ getLatestBackup: () => getLatestBackup,
8583
+ hasBackupToday: () => hasBackupToday,
8584
+ listBackups: () => listBackups,
8585
+ rotateBackups: () => rotateBackups
8586
+ });
8587
+ import { copyFileSync, existsSync as existsSync19, mkdirSync as mkdirSync11, readdirSync as readdirSync7, unlinkSync as unlinkSync10, statSync as statSync2 } from "fs";
8588
+ import path23 from "path";
8589
+ function findActiveDb() {
8590
+ for (const name of DB_NAMES) {
8591
+ const p = path23.join(EXE_AI_DIR, name);
8592
+ if (existsSync19(p)) return p;
8593
+ }
8594
+ return null;
8595
+ }
8596
+ function createBackup(reason = "manual") {
8597
+ const dbPath = findActiveDb();
8598
+ if (!dbPath) return null;
8599
+ mkdirSync11(BACKUP_DIR, { recursive: true });
8600
+ const dbName = path23.basename(dbPath, ".db");
8601
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
8602
+ const backupName = `${dbName}-${reason}-${timestamp}.db`;
8603
+ const backupPath = path23.join(BACKUP_DIR, backupName);
8604
+ copyFileSync(dbPath, backupPath);
8605
+ const walPath = dbPath + "-wal";
8606
+ if (existsSync19(walPath)) {
8607
+ try {
8608
+ copyFileSync(walPath, backupPath + "-wal");
8609
+ } catch {
8610
+ }
8611
+ }
8612
+ const shmPath = dbPath + "-shm";
8613
+ if (existsSync19(shmPath)) {
8614
+ try {
8615
+ copyFileSync(shmPath, backupPath + "-shm");
8616
+ } catch {
8617
+ }
8618
+ }
8619
+ return backupPath;
8620
+ }
8621
+ function rotateBackups(keepDays = DEFAULT_KEEP_DAYS) {
8622
+ if (!existsSync19(BACKUP_DIR)) return 0;
8623
+ const cutoff = Date.now() - keepDays * 24 * 60 * 60 * 1e3;
8624
+ let deleted = 0;
8625
+ try {
8626
+ const files = readdirSync7(BACKUP_DIR);
8627
+ for (const file of files) {
8628
+ if (!file.endsWith(".db") && !file.endsWith(".db-wal") && !file.endsWith(".db-shm")) continue;
8629
+ const filePath = path23.join(BACKUP_DIR, file);
8630
+ try {
8631
+ const stat = statSync2(filePath);
8632
+ if (stat.mtimeMs < cutoff) {
8633
+ unlinkSync10(filePath);
8634
+ deleted++;
8635
+ }
8636
+ } catch {
8637
+ }
8638
+ }
8639
+ } catch {
8640
+ }
8641
+ return deleted;
8642
+ }
8643
+ function listBackups() {
8644
+ if (!existsSync19(BACKUP_DIR)) return [];
8645
+ try {
8646
+ const files = readdirSync7(BACKUP_DIR).filter((f) => f.endsWith(".db") && !f.endsWith("-wal") && !f.endsWith("-shm"));
8647
+ return files.map((name) => {
8648
+ const p = path23.join(BACKUP_DIR, name);
8649
+ const stat = statSync2(p);
8650
+ return { path: p, name, size: stat.size, date: stat.mtime };
8651
+ }).sort((a, b) => b.date.getTime() - a.date.getTime());
8652
+ } catch {
8653
+ return [];
8654
+ }
8655
+ }
8656
+ function hasBackupToday(reason) {
8657
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
8658
+ const backups = listBackups();
8659
+ return backups.some((b) => b.name.includes(reason) && b.name.includes(today.replace(/-/g, "-")));
8660
+ }
8661
+ function getLatestBackup() {
8662
+ const backups = listBackups();
8663
+ return backups.length > 0 ? backups[0].path : null;
8664
+ }
8665
+ function getBackupDir() {
8666
+ return BACKUP_DIR;
8667
+ }
8668
+ var BACKUP_DIR, DEFAULT_KEEP_DAYS, DB_NAMES;
8669
+ var init_db_backup = __esm({
8670
+ "src/lib/db-backup.ts"() {
8671
+ "use strict";
8672
+ init_config();
8673
+ BACKUP_DIR = path23.join(EXE_AI_DIR, "backups");
8674
+ DEFAULT_KEEP_DAYS = 3;
8675
+ DB_NAMES = ["memories.db", "exe-mem.db", "exe-os.db", "exe.db"];
8676
+ }
8677
+ });
8678
+
8399
8679
  // src/lib/cloud-sync.ts
8400
8680
  var cloud_sync_exports = {};
8401
8681
  __export(cloud_sync_exports, {
@@ -8425,16 +8705,16 @@ __export(cloud_sync_exports, {
8425
8705
  pushToPostgres: () => pushToPostgres,
8426
8706
  recordRosterDeletion: () => recordRosterDeletion
8427
8707
  });
8428
- import { readFileSync as readFileSync15, writeFileSync as writeFileSync12, existsSync as existsSync19, readdirSync as readdirSync7, mkdirSync as mkdirSync11, appendFileSync as appendFileSync2, unlinkSync as unlinkSync10, openSync as openSync2, closeSync as closeSync2 } from "fs";
8708
+ import { readFileSync as readFileSync15, writeFileSync as writeFileSync12, existsSync as existsSync20, readdirSync as readdirSync8, mkdirSync as mkdirSync12, appendFileSync as appendFileSync2, unlinkSync as unlinkSync11, openSync as openSync2, closeSync as closeSync2 } from "fs";
8429
8709
  import crypto8 from "crypto";
8430
- import path23 from "path";
8710
+ import path24 from "path";
8431
8711
  import { homedir as homedir2 } from "os";
8432
8712
  function sqlSafe(v) {
8433
8713
  return v === void 0 ? null : v;
8434
8714
  }
8435
8715
  function logError(msg) {
8436
8716
  try {
8437
- const logPath = path23.join(homedir2(), ".exe-os", "workers.log");
8717
+ const logPath = path24.join(homedir2(), ".exe-os", "workers.log");
8438
8718
  appendFileSync2(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
8439
8719
  `);
8440
8720
  } catch {
@@ -8443,10 +8723,10 @@ function logError(msg) {
8443
8723
  function loadPgClient() {
8444
8724
  if (_pgFailed) return null;
8445
8725
  const postgresUrl = process.env.DATABASE_URL;
8446
- const configPath = path23.join(EXE_AI_DIR, "config.json");
8726
+ const configPath = path24.join(EXE_AI_DIR, "config.json");
8447
8727
  let cloudPostgresUrl;
8448
8728
  try {
8449
- if (existsSync19(configPath)) {
8729
+ if (existsSync20(configPath)) {
8450
8730
  const cfg = JSON.parse(readFileSync15(configPath, "utf8"));
8451
8731
  cloudPostgresUrl = cfg.cloud?.postgresUrl;
8452
8732
  if (cfg.cloud?.syncToPostgres === false) {
@@ -8465,8 +8745,8 @@ function loadPgClient() {
8465
8745
  _pgPromise = (async () => {
8466
8746
  const { createRequire: createRequire3 } = await import("module");
8467
8747
  const { pathToFileURL: pathToFileURL3 } = await import("url");
8468
- const exeDbRoot = process.env.EXE_DB_ROOT ?? path23.join(homedir2(), "exe-db");
8469
- const req = createRequire3(path23.join(exeDbRoot, "package.json"));
8748
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path24.join(homedir2(), "exe-db");
8749
+ const req = createRequire3(path24.join(exeDbRoot, "package.json"));
8470
8750
  const entry = req.resolve("@prisma/client");
8471
8751
  const mod = await import(pathToFileURL3(entry).href);
8472
8752
  const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
@@ -8519,7 +8799,7 @@ async function withRosterLock(fn) {
8519
8799
  if (Date.now() - ts < LOCK_STALE_MS) {
8520
8800
  throw new Error("Roster merge already in progress \u2014 another sync is running");
8521
8801
  }
8522
- unlinkSync10(ROSTER_LOCK_PATH);
8802
+ unlinkSync11(ROSTER_LOCK_PATH);
8523
8803
  const fd = openSync2(ROSTER_LOCK_PATH, "wx");
8524
8804
  closeSync2(fd);
8525
8805
  writeFileSync12(ROSTER_LOCK_PATH, String(Date.now()));
@@ -8535,7 +8815,7 @@ async function withRosterLock(fn) {
8535
8815
  return await fn();
8536
8816
  } finally {
8537
8817
  try {
8538
- unlinkSync10(ROSTER_LOCK_PATH);
8818
+ unlinkSync11(ROSTER_LOCK_PATH);
8539
8819
  } catch {
8540
8820
  }
8541
8821
  }
@@ -8906,13 +9186,42 @@ async function cloudSync(config) {
8906
9186
  try {
8907
9187
  const employees = await loadEmployees();
8908
9188
  rosterResult.employees = employees.length;
8909
- const idDir = path23.join(EXE_AI_DIR, "identity");
8910
- if (existsSync19(idDir)) {
8911
- rosterResult.identities = readdirSync7(idDir).filter((f) => f.endsWith(".md")).length;
9189
+ const idDir = path24.join(EXE_AI_DIR, "identity");
9190
+ if (existsSync20(idDir)) {
9191
+ rosterResult.identities = readdirSync8(idDir).filter((f) => f.endsWith(".md")).length;
8912
9192
  }
8913
9193
  } catch {
8914
9194
  }
8915
9195
  const totalMemories = await countRows("SELECT COUNT(*) as cnt FROM memories WHERE status = 'active' OR status IS NULL");
9196
+ try {
9197
+ const { getLatestBackup: getLatestBackup2 } = await Promise.resolve().then(() => (init_db_backup(), db_backup_exports));
9198
+ const { statSync: statFile } = await import("fs");
9199
+ const latestBackup = getLatestBackup2();
9200
+ if (latestBackup) {
9201
+ const backupSize = statFile(latestBackup).size;
9202
+ const MAX_CLOUD_BACKUP_BYTES = 50 * 1024 * 1024;
9203
+ if (backupSize <= MAX_CLOUD_BACKUP_BYTES) {
9204
+ const backupData = readFileSync15(latestBackup);
9205
+ const deviceId = loadDeviceId() ?? "unknown";
9206
+ const encrypted = encryptSyncBlob(backupData);
9207
+ const backupRes = await fetchWithRetry(`${config.endpoint}/sync/push-db-backup`, {
9208
+ method: "POST",
9209
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${config.apiKey}` },
9210
+ body: JSON.stringify({
9211
+ device_id: deviceId,
9212
+ filename: path24.basename(latestBackup),
9213
+ blob: encrypted,
9214
+ size: backupData.length
9215
+ })
9216
+ });
9217
+ if (backupRes && !backupRes.ok) {
9218
+ logError(`[cloud-sync] DB backup upload failed: ${backupRes.status}`);
9219
+ }
9220
+ }
9221
+ }
9222
+ } catch (err) {
9223
+ logError(`[cloud-sync] DB backup upload error: ${err instanceof Error ? err.message : String(err)}`);
9224
+ }
8916
9225
  return {
8917
9226
  pushed,
8918
9227
  pulled,
@@ -8928,7 +9237,7 @@ async function cloudSync(config) {
8928
9237
  function recordRosterDeletion(name) {
8929
9238
  let deletions = [];
8930
9239
  try {
8931
- if (existsSync19(ROSTER_DELETIONS_PATH)) {
9240
+ if (existsSync20(ROSTER_DELETIONS_PATH)) {
8932
9241
  deletions = JSON.parse(readFileSync15(ROSTER_DELETIONS_PATH, "utf-8"));
8933
9242
  }
8934
9243
  } catch {
@@ -8938,7 +9247,7 @@ function recordRosterDeletion(name) {
8938
9247
  }
8939
9248
  function consumeRosterDeletions() {
8940
9249
  try {
8941
- if (!existsSync19(ROSTER_DELETIONS_PATH)) return [];
9250
+ if (!existsSync20(ROSTER_DELETIONS_PATH)) return [];
8942
9251
  const deletions = JSON.parse(readFileSync15(ROSTER_DELETIONS_PATH, "utf-8"));
8943
9252
  writeFileSync12(ROSTER_DELETIONS_PATH, "[]");
8944
9253
  return deletions;
@@ -8947,35 +9256,35 @@ function consumeRosterDeletions() {
8947
9256
  }
8948
9257
  }
8949
9258
  function buildRosterBlob(paths) {
8950
- const rosterPath = paths?.rosterPath ?? path23.join(EXE_AI_DIR, "exe-employees.json");
8951
- const identityDir = paths?.identityDir ?? path23.join(EXE_AI_DIR, "identity");
8952
- const configPath = paths?.configPath ?? path23.join(EXE_AI_DIR, "config.json");
9259
+ const rosterPath = paths?.rosterPath ?? path24.join(EXE_AI_DIR, "exe-employees.json");
9260
+ const identityDir = paths?.identityDir ?? path24.join(EXE_AI_DIR, "identity");
9261
+ const configPath = paths?.configPath ?? path24.join(EXE_AI_DIR, "config.json");
8953
9262
  let roster = [];
8954
- if (existsSync19(rosterPath)) {
9263
+ if (existsSync20(rosterPath)) {
8955
9264
  try {
8956
9265
  roster = JSON.parse(readFileSync15(rosterPath, "utf-8"));
8957
9266
  } catch {
8958
9267
  }
8959
9268
  }
8960
9269
  const identities = {};
8961
- if (existsSync19(identityDir)) {
8962
- for (const file of readdirSync7(identityDir).filter((f) => f.endsWith(".md"))) {
9270
+ if (existsSync20(identityDir)) {
9271
+ for (const file of readdirSync8(identityDir).filter((f) => f.endsWith(".md"))) {
8963
9272
  try {
8964
- identities[file] = readFileSync15(path23.join(identityDir, file), "utf-8");
9273
+ identities[file] = readFileSync15(path24.join(identityDir, file), "utf-8");
8965
9274
  } catch {
8966
9275
  }
8967
9276
  }
8968
9277
  }
8969
9278
  let config;
8970
- if (existsSync19(configPath)) {
9279
+ if (existsSync20(configPath)) {
8971
9280
  try {
8972
9281
  config = JSON.parse(readFileSync15(configPath, "utf-8"));
8973
9282
  } catch {
8974
9283
  }
8975
9284
  }
8976
9285
  let agentConfig;
8977
- const agentConfigPath = path23.join(EXE_AI_DIR, "agent-config.json");
8978
- if (existsSync19(agentConfigPath)) {
9286
+ const agentConfigPath = path24.join(EXE_AI_DIR, "agent-config.json");
9287
+ if (existsSync20(agentConfigPath)) {
8979
9288
  try {
8980
9289
  agentConfig = JSON.parse(readFileSync15(agentConfigPath, "utf-8"));
8981
9290
  } catch {
@@ -9053,16 +9362,16 @@ async function cloudPullRoster(config) {
9053
9362
  }
9054
9363
  }
9055
9364
  function mergeConfig(remoteConfig, configPath) {
9056
- const cfgPath = configPath ?? path23.join(EXE_AI_DIR, "config.json");
9365
+ const cfgPath = configPath ?? path24.join(EXE_AI_DIR, "config.json");
9057
9366
  let local = {};
9058
- if (existsSync19(cfgPath)) {
9367
+ if (existsSync20(cfgPath)) {
9059
9368
  try {
9060
9369
  local = JSON.parse(readFileSync15(cfgPath, "utf-8"));
9061
9370
  } catch {
9062
9371
  }
9063
9372
  }
9064
9373
  const merged = { ...remoteConfig, ...local };
9065
- const dir = path23.dirname(cfgPath);
9374
+ const dir = path24.dirname(cfgPath);
9066
9375
  ensurePrivateDirSync(dir);
9067
9376
  writeFileSync12(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
9068
9377
  enforcePrivateFileSync(cfgPath);
@@ -9070,7 +9379,7 @@ function mergeConfig(remoteConfig, configPath) {
9070
9379
  async function mergeRosterFromRemote(remote, paths) {
9071
9380
  return withRosterLock(async () => {
9072
9381
  const rosterPath = paths?.rosterPath ?? void 0;
9073
- const identityDir = paths?.identityDir ?? path23.join(EXE_AI_DIR, "identity");
9382
+ const identityDir = paths?.identityDir ?? path24.join(EXE_AI_DIR, "identity");
9074
9383
  const localEmployees = await loadEmployees(rosterPath);
9075
9384
  const localNames = new Set(localEmployees.map((e) => e.name));
9076
9385
  let added = 0;
@@ -9091,11 +9400,11 @@ async function mergeRosterFromRemote(remote, paths) {
9091
9400
  ) ?? lookupKey;
9092
9401
  const remoteIdentity = remote.identities[matchedKey];
9093
9402
  if (remoteIdentity) {
9094
- if (!existsSync19(identityDir)) mkdirSync11(identityDir, { recursive: true });
9095
- const idPath = path23.join(identityDir, `${remoteEmp.name}.md`);
9403
+ if (!existsSync20(identityDir)) mkdirSync12(identityDir, { recursive: true });
9404
+ const idPath = path24.join(identityDir, `${remoteEmp.name}.md`);
9096
9405
  let localIdentity = null;
9097
9406
  try {
9098
- localIdentity = existsSync19(idPath) ? readFileSync15(idPath, "utf-8") : null;
9407
+ localIdentity = existsSync20(idPath) ? readFileSync15(idPath, "utf-8") : null;
9099
9408
  } catch {
9100
9409
  }
9101
9410
  if (localIdentity !== remoteIdentity) {
@@ -9125,16 +9434,16 @@ async function mergeRosterFromRemote(remote, paths) {
9125
9434
  }
9126
9435
  if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
9127
9436
  try {
9128
- const agentConfigPath = path23.join(EXE_AI_DIR, "agent-config.json");
9437
+ const agentConfigPath = path24.join(EXE_AI_DIR, "agent-config.json");
9129
9438
  let local = {};
9130
- if (existsSync19(agentConfigPath)) {
9439
+ if (existsSync20(agentConfigPath)) {
9131
9440
  try {
9132
9441
  local = JSON.parse(readFileSync15(agentConfigPath, "utf-8"));
9133
9442
  } catch {
9134
9443
  }
9135
9444
  }
9136
9445
  const merged = { ...remote.agentConfig, ...local };
9137
- ensurePrivateDirSync(path23.dirname(agentConfigPath));
9446
+ ensurePrivateDirSync(path24.dirname(agentConfigPath));
9138
9447
  writeFileSync12(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
9139
9448
  enforcePrivateFileSync(agentConfigPath);
9140
9449
  } catch {
@@ -9575,11 +9884,11 @@ var init_cloud_sync = __esm({
9575
9884
  LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
9576
9885
  FETCH_TIMEOUT_MS = 3e4;
9577
9886
  PUSH_BATCH_SIZE = 5e3;
9578
- ROSTER_LOCK_PATH = path23.join(EXE_AI_DIR, "roster-merge.lock");
9887
+ ROSTER_LOCK_PATH = path24.join(EXE_AI_DIR, "roster-merge.lock");
9579
9888
  LOCK_STALE_MS = 3e4;
9580
9889
  _pgPromise = null;
9581
9890
  _pgFailed = false;
9582
- ROSTER_DELETIONS_PATH = path23.join(EXE_AI_DIR, "roster-deletions.json");
9891
+ ROSTER_DELETIONS_PATH = path24.join(EXE_AI_DIR, "roster-deletions.json");
9583
9892
  }
9584
9893
  });
9585
9894
 
@@ -9592,7 +9901,7 @@ __export(schedules_exports, {
9592
9901
  parseHumanCron: () => parseHumanCron
9593
9902
  });
9594
9903
  import crypto9 from "crypto";
9595
- import { execSync as execSync9 } from "child_process";
9904
+ import { execSync as execSync10 } from "child_process";
9596
9905
  function isValidCron(cron) {
9597
9906
  const fields = cron.trim().split(/\s+/);
9598
9907
  if (fields.length !== 5) return false;
@@ -9752,7 +10061,7 @@ function addToCrontab(id, cron, prompt, projectDir) {
9752
10061
  const cwd = projectDir ? `cd ${JSON.stringify(projectDir)} && ` : "";
9753
10062
  const escapedPrompt = prompt.replace(/"/g, '\\"');
9754
10063
  const entry = `${cron} ${cwd}claude -p --dangerously-skip-permissions "${escapedPrompt}" # exe-schedule:${id}`;
9755
- execSync9(
10064
+ execSync10(
9756
10065
  `(crontab -l 2>/dev/null; echo ${JSON.stringify(entry)}) | crontab -`,
9757
10066
  { timeout: 5e3, stdio: "ignore" }
9758
10067
  );
@@ -9763,7 +10072,7 @@ function addToCrontab(id, cron, prompt, projectDir) {
9763
10072
  function removeFromCrontab(id) {
9764
10073
  if (!isValidScheduleId(id)) return;
9765
10074
  try {
9766
- execSync9(
10075
+ execSync10(
9767
10076
  `crontab -l 2>/dev/null | grep -v "exe-schedule:${id}" | crontab -`,
9768
10077
  { timeout: 5e3, stdio: "ignore" }
9769
10078
  );
@@ -9783,9 +10092,9 @@ var init_schedules = __esm({
9783
10092
 
9784
10093
  // src/bin/exe-boot.ts
9785
10094
  init_employees();
9786
- import path24 from "path";
10095
+ import path25 from "path";
9787
10096
  import { mkdir as mkdir5, writeFile as writeFile6 } from "fs/promises";
9788
- import { existsSync as existsSync20, readFileSync as readFileSync16, readdirSync as readdirSync8, unlinkSync as unlinkSync11 } from "fs";
10097
+ import { existsSync as existsSync21, readFileSync as readFileSync16, readdirSync as readdirSync9, unlinkSync as unlinkSync12 } from "fs";
9789
10098
  import os12 from "os";
9790
10099
 
9791
10100
  // src/lib/employee-templates.ts
@@ -10289,7 +10598,7 @@ init_config();
10289
10598
  init_session_key();
10290
10599
  init_employees();
10291
10600
  import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, mkdirSync as mkdirSync8, unlinkSync as unlinkSync7, readdirSync as readdirSync5 } from "fs";
10292
- import { execSync as execSync8 } from "child_process";
10601
+ import { execSync as execSync9 } from "child_process";
10293
10602
  import path20 from "path";
10294
10603
  var CACHE_DIR = path20.join(EXE_AI_DIR, "session-cache");
10295
10604
  var STALE_MS = 24 * 60 * 60 * 1e3;
@@ -10395,18 +10704,18 @@ async function boot(options) {
10395
10704
  } catch {
10396
10705
  }
10397
10706
  try {
10398
- const { readdirSync: readdirSync9, readFileSync: readFs } = await import("fs");
10707
+ const { readdirSync: readdirSync10, readFileSync: readFs } = await import("fs");
10399
10708
  const { STATUS_RE: STATUS_RE2, PRIORITY_RE: PRIORITY_RE2, TITLE_RE: TITLE_RE2 } = await Promise.resolve().then(() => (init_task_scanner(), task_scanner_exports));
10400
10709
  const { getProjectName: getProjectName2 } = await Promise.resolve().then(() => (init_project_name(), project_name_exports));
10401
10710
  const exeDir = "exe";
10402
- const entries = readdirSync9(exeDir, { withFileTypes: true });
10711
+ const entries = readdirSync10(exeDir, { withFileTypes: true });
10403
10712
  const employeeDirs = entries.filter((e) => e.isDirectory() && !["output", "research"].includes(e.name));
10404
10713
  for (const dir of employeeDirs) {
10405
10714
  const employee = dir.name;
10406
- const taskDir = path24.join(exeDir, employee);
10715
+ const taskDir = path25.join(exeDir, employee);
10407
10716
  let files;
10408
10717
  try {
10409
- files = readdirSync9(taskDir).filter((f) => f.endsWith(".md"));
10718
+ files = readdirSync10(taskDir).filter((f) => f.endsWith(".md"));
10410
10719
  } catch {
10411
10720
  continue;
10412
10721
  }
@@ -10414,7 +10723,7 @@ async function boot(options) {
10414
10723
  const taskFilePath = `exe/${employee}/${file}`;
10415
10724
  let content;
10416
10725
  try {
10417
- content = readFs(path24.join(taskDir, file), "utf8");
10726
+ content = readFs(path25.join(taskDir, file), "utf8");
10418
10727
  } catch {
10419
10728
  continue;
10420
10729
  }
@@ -10500,12 +10809,12 @@ async function boot(options) {
10500
10809
  }
10501
10810
  try {
10502
10811
  for (const reviewDirName of /* @__PURE__ */ new Set(["exe", coordinatorName])) {
10503
- const reviewDir = path24.join(process.cwd(), "exe", reviewDirName);
10504
- if (existsSync20(reviewDir)) {
10505
- for (const f of readdirSync8(reviewDir)) {
10812
+ const reviewDir = path25.join(process.cwd(), "exe", reviewDirName);
10813
+ if (existsSync21(reviewDir)) {
10814
+ for (const f of readdirSync9(reviewDir)) {
10506
10815
  if (f.startsWith("review-") && f.endsWith(".md")) {
10507
10816
  try {
10508
- unlinkSync11(path24.join(reviewDir, f));
10817
+ unlinkSync12(path25.join(reviewDir, f));
10509
10818
  } catch {
10510
10819
  }
10511
10820
  }
@@ -10551,8 +10860,8 @@ async function boot(options) {
10551
10860
  });
10552
10861
  const taskFile = String(r.task_file);
10553
10862
  try {
10554
- const filePath = path24.join(process.cwd(), taskFile);
10555
- if (existsSync20(filePath)) {
10863
+ const filePath = path25.join(process.cwd(), taskFile);
10864
+ if (existsSync21(filePath)) {
10556
10865
  let content = readFileSync16(filePath, "utf8");
10557
10866
  content = content.replace(/\*\*Status:\*\* needs_review/, "**Status:** done");
10558
10867
  const { writeFileSync: writeFileSync13 } = await import("fs");
@@ -11026,8 +11335,8 @@ async function boot(options) {
11026
11335
  updatedAt: String(row.updated_at)
11027
11336
  }));
11028
11337
  try {
11029
- const { execSync: execSync10 } = await import("child_process");
11030
- const gitLog = execSync10(
11338
+ const { execSync: execSync11 } = await import("child_process");
11339
+ const gitLog = execSync11(
11031
11340
  "git log --oneline -10 --grep='Co-Authored-By: Claude' --grep='task(' --all-match 2>/dev/null || git log --oneline -5 --grep='Co-Authored-By: Claude' 2>/dev/null || git log --oneline -5 --grep='^task(' 2>/dev/null",
11032
11341
  {
11033
11342
  encoding: "utf8",
@@ -11050,19 +11359,19 @@ async function boot(options) {
11050
11359
  })()
11051
11360
  ]);
11052
11361
  try {
11053
- const configPath = path24.join(
11054
- process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path24.join(os12.homedir(), ".exe-os"),
11362
+ const configPath = path25.join(
11363
+ process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path25.join(os12.homedir(), ".exe-os"),
11055
11364
  "config.json"
11056
11365
  );
11057
- if (existsSync20(configPath)) {
11366
+ if (existsSync21(configPath)) {
11058
11367
  const raw = JSON.parse(readFileSync16(configPath, "utf8"));
11059
11368
  briefData.cloudConnected = !!(raw.cloud || raw.turso);
11060
11369
  }
11061
11370
  } catch {
11062
11371
  }
11063
11372
  try {
11064
- const backfillFlagPath = path24.join(EXE_AI_DIR, "session-cache", "needs-backfill");
11065
- const isBackfillNeeded = () => existsSync20(backfillFlagPath);
11373
+ const backfillFlagPath = path25.join(EXE_AI_DIR, "session-cache", "needs-backfill");
11374
+ const isBackfillNeeded = () => existsSync21(backfillFlagPath);
11066
11375
  const coverageResult = await client.execute({
11067
11376
  sql: `SELECT COUNT(*) as total,
11068
11377
  SUM(CASE WHEN vector IS NOT NULL THEN 1 ELSE 0 END) as with_vectors
@@ -11084,8 +11393,8 @@ async function boot(options) {
11084
11393
  let daemonRunning = false;
11085
11394
  let daemonUptime;
11086
11395
  let daemonRequestsServed;
11087
- const socketPath = path24.join(EXE_AI_DIR, "exed.sock");
11088
- if (existsSync20(socketPath)) {
11396
+ const socketPath = path25.join(EXE_AI_DIR, "exed.sock");
11397
+ if (existsSync21(socketPath)) {
11089
11398
  try {
11090
11399
  const net2 = await import("net");
11091
11400
  const health = await new Promise((resolve) => {
@@ -11127,8 +11436,8 @@ async function boot(options) {
11127
11436
  }
11128
11437
  }
11129
11438
  if (!daemonRunning) {
11130
- const pidPath = path24.join(EXE_AI_DIR, "exed.pid");
11131
- if (existsSync20(pidPath)) {
11439
+ const pidPath = path25.join(EXE_AI_DIR, "exed.pid");
11440
+ if (existsSync21(pidPath)) {
11132
11441
  try {
11133
11442
  const pid = parseInt(readFileSync16(pidPath, "utf8").trim(), 10);
11134
11443
  if (pid > 0) {
@@ -11141,10 +11450,10 @@ async function boot(options) {
11141
11450
  }
11142
11451
  if (nullCount === 0) {
11143
11452
  try {
11144
- const flagPath = path24.join(EXE_AI_DIR, "session-cache", "needs-backfill");
11145
- if (existsSync20(flagPath)) {
11146
- const { unlinkSync: unlinkSync12 } = await import("fs");
11147
- unlinkSync12(flagPath);
11453
+ const flagPath = path25.join(EXE_AI_DIR, "session-cache", "needs-backfill");
11454
+ if (existsSync21(flagPath)) {
11455
+ const { unlinkSync: unlinkSync13 } = await import("fs");
11456
+ unlinkSync13(flagPath);
11148
11457
  }
11149
11458
  } catch {
11150
11459
  }
@@ -11168,10 +11477,10 @@ async function boot(options) {
11168
11477
  const { spawn: spawn2 } = await import("child_process");
11169
11478
  const { fileURLToPath: fileURLToPath4 } = await import("url");
11170
11479
  const thisFile = fileURLToPath4(import.meta.url);
11171
- const backfillPath = path24.resolve(path24.dirname(thisFile), "backfill-vectors.js");
11172
- if (existsSync20(backfillPath)) {
11480
+ const backfillPath = path25.resolve(path25.dirname(thisFile), "backfill-vectors.js");
11481
+ if (existsSync21(backfillPath)) {
11173
11482
  const { openSync: openSync3, closeSync: closeSync3 } = await import("fs");
11174
- const workerLogPath = path24.join(EXE_AI_DIR, "workers.log");
11483
+ const workerLogPath = path25.join(EXE_AI_DIR, "workers.log");
11175
11484
  let stderrFd = "ignore";
11176
11485
  try {
11177
11486
  stderrFd = openSync3(workerLogPath, "a");
@@ -11201,8 +11510,8 @@ async function boot(options) {
11201
11510
  const criticalBinaries = ["backfill-vectors.js", "scan-tasks.js"];
11202
11511
  const missing = [];
11203
11512
  for (const bin of criticalBinaries) {
11204
- const binPath = path24.resolve(path24.dirname(thisFile), bin);
11205
- if (!existsSync20(binPath)) {
11513
+ const binPath = path25.resolve(path25.dirname(thisFile), bin);
11514
+ if (!existsSync21(binPath)) {
11206
11515
  missing.push(`dist/bin/${bin}`);
11207
11516
  }
11208
11517
  }
@@ -11231,7 +11540,7 @@ async function boot(options) {
11231
11540
  console.log(brief);
11232
11541
  return;
11233
11542
  }
11234
- const sessionDir = path24.join(EXE_AI_DIR, "sessions", coordinatorName);
11543
+ const sessionDir = path25.join(EXE_AI_DIR, "sessions", coordinatorName);
11235
11544
  await mkdir5(sessionDir, { recursive: true });
11236
11545
  const claudeMdContent = `${getSessionPrompt(coordinatorEmployee.systemPrompt)}
11237
11546
 
@@ -11240,7 +11549,7 @@ async function boot(options) {
11240
11549
  # Status Brief
11241
11550
 
11242
11551
  ${brief}`;
11243
- await writeFile6(path24.join(sessionDir, "CLAUDE.md"), claudeMdContent, "utf-8");
11552
+ await writeFile6(path25.join(sessionDir, "CLAUDE.md"), claudeMdContent, "utf-8");
11244
11553
  const unread = await readUnreadNotifications();
11245
11554
  if (unread.length > 0) {
11246
11555
  console.log(`\u{1F4EC} ${unread.length} unread notification${unread.length === 1 ? "" : "s"}`);
@@ -11249,11 +11558,11 @@ ${brief}`;
11249
11558
  await cleanupOldNotifications();
11250
11559
  console.log(brief);
11251
11560
  try {
11252
- const configPath2 = path24.join(
11253
- process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path24.join(os12.homedir(), ".exe-os"),
11561
+ const configPath2 = path25.join(
11562
+ process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path25.join(os12.homedir(), ".exe-os"),
11254
11563
  "config.json"
11255
11564
  );
11256
- if (existsSync20(configPath2)) {
11565
+ if (existsSync21(configPath2)) {
11257
11566
  const rawCfg = JSON.parse(readFileSync16(configPath2, "utf8"));
11258
11567
  const cloudCfg = rawCfg.cloud;
11259
11568
  if (cloudCfg?.apiKey) {
@@ -11314,11 +11623,11 @@ async function updateIdleKillSuspectStreak(client, killsToday, liveSessions, tod
11314
11623
  }
11315
11624
  function runSplash() {
11316
11625
  try {
11317
- const { execSync: execSync10 } = __require("child_process");
11626
+ const { execSync: execSync11 } = __require("child_process");
11318
11627
  const { loadConfigSync: loadConfigSync2 } = (init_config(), __toCommonJS(config_exports));
11319
11628
  const config = loadConfigSync2();
11320
11629
  if (!config.splashEffect) return;
11321
- execSync10(
11630
+ execSync11(
11322
11631
  'echo "EXE OS" | python3 -m terminaltexteffects decrypt --typing-speed 2 --ciphertext-colors 6B4C9A F5D76E --final-gradient-stops F5D76E F0EDE8 --final-gradient-direction vertical',
11323
11632
  { stdio: "inherit", timeout: 5e3 }
11324
11633
  );