abtars 0.2.0 → 0.2.1-alpha.10

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 (146) hide show
  1. package/README.md +15 -18
  2. package/bundle/{_registry.generated-6GO2PAMD.js → _registry.generated-4P6KTBTH.js} +8 -8
  3. package/bundle/_registry.generated-G2FSGHNY.js +36 -0
  4. package/bundle/_registry.generated-G2FSGHNY.js.map +7 -0
  5. package/bundle/_registry.generated-N74SMTUJ.js +36 -0
  6. package/bundle/_registry.generated-N74SMTUJ.js.map +7 -0
  7. package/bundle/_registry.generated-Y4VT5MBX.js +36 -0
  8. package/bundle/_registry.generated-Y4VT5MBX.js.map +7 -0
  9. package/bundle/abtars-browser.js +0 -1
  10. package/bundle/abtars-browser.js.map +2 -2
  11. package/bundle/abtars-cli.js +589 -547
  12. package/bundle/abtars-cli.js.map +4 -4
  13. package/bundle/abtars-restart.js +1 -1
  14. package/bundle/abtars-rss.js +0 -1
  15. package/bundle/abtars-rss.js.map +2 -2
  16. package/bundle/abtars-task.js +1 -2
  17. package/bundle/abtars-task.js.map +2 -2
  18. package/bundle/abtars.js +58 -33
  19. package/bundle/abtars.js.map +2 -2
  20. package/bundle/{agent-registry-SYUFNSVB.js → agent-registry-TM7EISOV.js} +2 -2
  21. package/bundle/{browse-delivery-64GQIUHG.js → browse-delivery-VTLEAVYA.js} +4 -4
  22. package/bundle/{browser-5H2Q7GEC.js → browser-JQGYFZAG.js} +5 -5
  23. package/bundle/browser-M4KDLN75.js +19 -0
  24. package/bundle/browser-REIXOJ6S.js +19 -0
  25. package/bundle/browser-VG2GTEND.js +19 -0
  26. package/bundle/{chunk-KFENC7BM.js → chunk-4YOTNYAE.js} +2 -2
  27. package/bundle/{chunk-KWBGYWDO.js → chunk-7I5C2U3C.js} +1 -1
  28. package/bundle/chunk-7I5C2U3C.js.map +7 -0
  29. package/bundle/{chunk-7STAQF5O.js → chunk-CNWXVXGH.js} +4 -4
  30. package/bundle/{chunk-7STAQF5O.js.map → chunk-CNWXVXGH.js.map} +2 -2
  31. package/bundle/{chunk-JX3ZZU3O.js → chunk-CU3O53U3.js} +28 -14
  32. package/bundle/chunk-CU3O53U3.js.map +7 -0
  33. package/bundle/chunk-EZZBO2QK.js +85 -0
  34. package/bundle/chunk-EZZBO2QK.js.map +7 -0
  35. package/bundle/{chunk-XLLSPBBT.js → chunk-GPL57SRN.js} +2 -2
  36. package/bundle/chunk-GPL57SRN.js.map +7 -0
  37. package/bundle/{chunk-PQ62LZNA.js → chunk-HEHD3GG5.js} +2 -2
  38. package/bundle/{chunk-A5OJYQNU.js → chunk-HTGVYNPE.js} +4 -4
  39. package/bundle/{chunk-WUTCGJDZ.js → chunk-HZRUBJ4Q.js} +148 -9
  40. package/bundle/chunk-HZRUBJ4Q.js.map +7 -0
  41. package/bundle/{chunk-ZVDVNSXK.js → chunk-J7CFCY4U.js} +2 -2
  42. package/bundle/{chunk-HXJRZWKA.js → chunk-JRK3UFBS.js} +106 -58
  43. package/bundle/chunk-JRK3UFBS.js.map +7 -0
  44. package/bundle/{chunk-GZUQQWBZ.js → chunk-KJOCXWJ5.js} +3 -3
  45. package/bundle/chunk-KJOCXWJ5.js.map +7 -0
  46. package/bundle/{chunk-K7P74UNQ.js → chunk-LDKSCXGL.js} +3 -3
  47. package/bundle/{chunk-K7P74UNQ.js.map → chunk-LDKSCXGL.js.map} +2 -2
  48. package/bundle/chunk-MNKLPRGY.js +138 -0
  49. package/bundle/chunk-MNKLPRGY.js.map +7 -0
  50. package/bundle/chunk-O73EZGKR.js +135 -0
  51. package/bundle/chunk-O73EZGKR.js.map +7 -0
  52. package/bundle/{chunk-OZ4IZFV4.js → chunk-Q7F5R2LL.js} +1 -1
  53. package/bundle/{chunk-OZ4IZFV4.js.map → chunk-Q7F5R2LL.js.map} +2 -2
  54. package/bundle/{chunk-XOCP5BMO.js → chunk-URAQLQ2U.js} +2 -2
  55. package/bundle/chunk-URAQLQ2U.js.map +7 -0
  56. package/bundle/{chunk-PPSB2ZIN.js → chunk-VREI2GUO.js} +18 -7
  57. package/bundle/chunk-VREI2GUO.js.map +7 -0
  58. package/bundle/chunk-VYR25H37.js +135 -0
  59. package/bundle/chunk-VYR25H37.js.map +7 -0
  60. package/bundle/{commands-E3Y4QUNF.js → commands-J6JAZ7D2.js} +5 -5
  61. package/bundle/{deploy-lib-import-ODLDL2DB.js → deploy-lib-import-PJXUADPS.js} +18 -16
  62. package/bundle/{direct-api-transport-TRV45NO6.js → direct-api-transport-ZSJHKYBX.js} +1 -1
  63. package/bundle/direct-api-transport-ZSJHKYBX.js.map +7 -0
  64. package/bundle/{discord-adapter-5DS7UTBE.js → discord-adapter-OCWSJTJU.js} +8 -8
  65. package/bundle/{doctor-R54GZPKL.js → doctor-XOGVHA4W.js} +13 -2
  66. package/bundle/{doctor-R54GZPKL.js.map → doctor-XOGVHA4W.js.map} +2 -2
  67. package/bundle/{install-RSLOS3Q5.js → install-6H3APAIJ.js} +3 -3
  68. package/bundle/{irc-adapter-RKRUSZXB.js → irc-adapter-AIEP6OX6.js} +6 -2
  69. package/bundle/irc-adapter-AIEP6OX6.js.map +7 -0
  70. package/bundle/{message-pipeline-CFVO73CK.js → message-pipeline-TCPHRBAK.js} +5 -5
  71. package/bundle/meta.json +4005 -3868
  72. package/bundle/{phase-transport-FEZ4SIJJ.js → phase-transport-ME2XDRR2.js} +3 -3
  73. package/bundle/restore-UH2UE2K5.js +151 -0
  74. package/bundle/restore-UH2UE2K5.js.map +7 -0
  75. package/bundle/rollback-D7AYTC7Z.js +11 -0
  76. package/bundle/{sleep-BPWX3FCN.js → sleep-IOWYTSMK.js} +2 -2
  77. package/bundle/{soul-bundle-BRIUDEQ2.js → soul-bundle-AH5S3MVI.js} +2 -2
  78. package/bundle/{soul-loader-GBXJ7EBH.js → soul-loader-N5FA4I6C.js} +3 -3
  79. package/bundle/soul-loader-N5FA4I6C.js.map +7 -0
  80. package/bundle/{start-4IWBKLWO.js → start-CBVKNEAT.js} +1 -1
  81. package/bundle/{subagent-runtime-MDBO6LHS.js → subagent-runtime-OOJXCKJF.js} +2 -2
  82. package/bundle/subagent-runtime-OOJXCKJF.js.map +7 -0
  83. package/bundle/{system-status-WAY63BYC.js → system-status-4JP36OKC.js} +12 -5
  84. package/bundle/{system-status-WAY63BYC.js.map → system-status-4JP36OKC.js.map} +2 -2
  85. package/bundle/{task-store-KIBFZL5A.js → task-store-LC7ZMS72.js} +2 -2
  86. package/bundle/task-store-LC7ZMS72.js.map +7 -0
  87. package/bundle/{telegram-adapter-MVA33IO2.js → telegram-adapter-QXHJQLLX.js} +5 -5
  88. package/bundle/{update-check-O5MS6B3L.js → update-check-XVY2IK2L.js} +2 -2
  89. package/bundle/update-check-XVY2IK2L.js.map +7 -0
  90. package/core/skills/tools/rss/SKILL.md +51 -0
  91. package/install-manifest.json +4 -3
  92. package/package.json +3 -4
  93. package/scripts/abtars-daemon.service +1 -1
  94. package/scripts/abtars-watchdog.service +1 -1
  95. package/scripts/abtars.sh +3 -5
  96. package/scripts/abtars@.service +1 -1
  97. package/scripts/build-and-deploy.sh +6 -0
  98. package/scripts/com.abtars.daemon.plist +1 -1
  99. package/scripts/com.abtars.watchdog.plist +1 -1
  100. package/scripts/daily-backup.sh +1 -61
  101. package/scripts/doctor.sh +48 -33
  102. package/scripts/watchdog.sh +53 -8
  103. package/bundle/chunk-GZUQQWBZ.js.map +0 -7
  104. package/bundle/chunk-HXJRZWKA.js.map +0 -7
  105. package/bundle/chunk-JX3ZZU3O.js.map +0 -7
  106. package/bundle/chunk-KWBGYWDO.js.map +0 -7
  107. package/bundle/chunk-PPSB2ZIN.js.map +0 -7
  108. package/bundle/chunk-WUTCGJDZ.js.map +0 -7
  109. package/bundle/chunk-XLLSPBBT.js.map +0 -7
  110. package/bundle/chunk-XOCP5BMO.js.map +0 -7
  111. package/bundle/direct-api-transport-TRV45NO6.js.map +0 -7
  112. package/bundle/irc-adapter-RKRUSZXB.js.map +0 -7
  113. package/bundle/passwd-QSHZJ2CG.js +0 -134
  114. package/bundle/passwd-QSHZJ2CG.js.map +0 -7
  115. package/bundle/restore-ROJF22R2.js +0 -47
  116. package/bundle/restore-ROJF22R2.js.map +0 -7
  117. package/core/core_templates/SOUL.md +0 -24
  118. package/core/core_templates/agent_notes.md +0 -7
  119. package/core/core_templates/core_facts.md +0 -10
  120. package/core/core_templates/user_profile.md +0 -7
  121. package/core/skills/tools/fxtwitter/SKILL.md +0 -52
  122. package/core/skills/tools/twitterX/SKILL.md +0 -52
  123. package/core/skills/tools/twitterX/scripts/abtars-tweet.js +0 -532
  124. package/core/skills/tools/twitterX/scripts/package.json +0 -1
  125. /package/bundle/{_registry.generated-6GO2PAMD.js.map → _registry.generated-4P6KTBTH.js.map} +0 -0
  126. /package/bundle/{agent-registry-SYUFNSVB.js.map → agent-registry-TM7EISOV.js.map} +0 -0
  127. /package/bundle/{browse-delivery-64GQIUHG.js.map → browse-delivery-VTLEAVYA.js.map} +0 -0
  128. /package/bundle/{browser-5H2Q7GEC.js.map → browser-JQGYFZAG.js.map} +0 -0
  129. /package/bundle/{commands-E3Y4QUNF.js.map → browser-M4KDLN75.js.map} +0 -0
  130. /package/bundle/{deploy-lib-import-ODLDL2DB.js.map → browser-REIXOJ6S.js.map} +0 -0
  131. /package/bundle/{install-RSLOS3Q5.js.map → browser-VG2GTEND.js.map} +0 -0
  132. /package/bundle/{chunk-KFENC7BM.js.map → chunk-4YOTNYAE.js.map} +0 -0
  133. /package/bundle/{chunk-PQ62LZNA.js.map → chunk-HEHD3GG5.js.map} +0 -0
  134. /package/bundle/{chunk-A5OJYQNU.js.map → chunk-HTGVYNPE.js.map} +0 -0
  135. /package/bundle/{chunk-ZVDVNSXK.js.map → chunk-J7CFCY4U.js.map} +0 -0
  136. /package/bundle/{message-pipeline-CFVO73CK.js.map → commands-J6JAZ7D2.js.map} +0 -0
  137. /package/bundle/{phase-transport-FEZ4SIJJ.js.map → deploy-lib-import-PJXUADPS.js.map} +0 -0
  138. /package/bundle/{discord-adapter-5DS7UTBE.js.map → discord-adapter-OCWSJTJU.js.map} +0 -0
  139. /package/bundle/{sleep-BPWX3FCN.js.map → install-6H3APAIJ.js.map} +0 -0
  140. /package/bundle/{soul-bundle-BRIUDEQ2.js.map → message-pipeline-TCPHRBAK.js.map} +0 -0
  141. /package/bundle/{soul-loader-GBXJ7EBH.js.map → phase-transport-ME2XDRR2.js.map} +0 -0
  142. /package/bundle/{subagent-runtime-MDBO6LHS.js.map → rollback-D7AYTC7Z.js.map} +0 -0
  143. /package/bundle/{task-store-KIBFZL5A.js.map → sleep-IOWYTSMK.js.map} +0 -0
  144. /package/bundle/{update-check-O5MS6B3L.js.map → soul-bundle-AH5S3MVI.js.map} +0 -0
  145. /package/bundle/{start-4IWBKLWO.js.map → start-CBVKNEAT.js.map} +0 -0
  146. /package/bundle/{telegram-adapter-MVA33IO2.js.map → telegram-adapter-QXHJQLLX.js.map} +0 -0
@@ -2,24 +2,27 @@
2
2
  import { createRequire as __bundleCreateRequire } from 'node:module'; import { fileURLToPath as __bundleFileURLToPath } from 'node:url'; import { dirname as __bundleDirname } from 'node:path'; const require = __bundleCreateRequire(import.meta.url); const __chunk_filename = __bundleFileURLToPath(import.meta.url); const __chunk_dirname = __bundleDirname(__chunk_filename);
3
3
  import {
4
4
  install
5
- } from "./chunk-PPSB2ZIN.js";
5
+ } from "./chunk-VREI2GUO.js";
6
+ import {
7
+ rollback
8
+ } from "./chunk-EZZBO2QK.js";
6
9
  import {
7
- RETENTION,
8
10
  acquireLock,
9
- activate,
11
+ atomicSwap,
12
+ cleanStaleStaging,
13
+ configSnapshot,
10
14
  emptyManifest,
11
15
  hashFile,
12
- inspectLock,
16
+ healthProbe,
13
17
  packagePaths,
14
- pruneReleases,
15
- readCurrent,
16
18
  readManifest,
17
- releaseExists,
18
- writeManifest
19
- } from "./chunk-HXJRZWKA.js";
19
+ readSentinel,
20
+ writeManifest,
21
+ writeSentinel
22
+ } from "./chunk-JRK3UFBS.js";
20
23
  import {
21
24
  restart
22
- } from "./chunk-XOCP5BMO.js";
25
+ } from "./chunk-URAQLQ2U.js";
23
26
  import {
24
27
  init_log_and_swallow,
25
28
  logAndSwallow
@@ -301,122 +304,210 @@ async function uninstall(opts) {
301
304
 
302
305
  // src/cli/commands/backup.ts
303
306
  init_paths();
304
- import { existsSync as existsSync3, mkdirSync, readdirSync as readdirSync2, statSync, renameSync, unlinkSync as unlinkSync3, copyFileSync } from "node:fs";
305
- import { join as join4, dirname } from "node:path";
307
+ import { existsSync as existsSync3, mkdirSync, readdirSync as readdirSync2, statSync, unlinkSync as unlinkSync3, renameSync, readFileSync as readFileSync2, writeFileSync } from "node:fs";
308
+ import { join as join4, dirname, basename } from "node:path";
306
309
  import { spawnSync } from "node:child_process";
307
- var AB_SAVE = ["config", "secret", "tasks", "logo", "workspace", "skills/custom", "skills/self"];
310
+ import { createCipheriv, hkdfSync, randomBytes } from "node:crypto";
308
311
  var DEFAULT_PRUNE_DAYS = 7;
309
- async function backup(outputDir, pruneDays) {
312
+ var ABTARS_EXCLUDE = [
313
+ "releases",
314
+ "current",
315
+ "bin",
316
+ "app",
317
+ "logs",
318
+ "node_modules",
319
+ "overflow",
320
+ "browser-socket",
321
+ "bridge.lock",
322
+ "watchdog.lock",
323
+ "browse-spawn.sock",
324
+ "manifest.json",
325
+ "*.sock"
326
+ ];
327
+ var ABMIND_EXCLUDE = [
328
+ "memory/memory.db",
329
+ "lib",
330
+ "node_modules",
331
+ "backups",
332
+ "*.sock",
333
+ "*.db-wal",
334
+ "*.db-shm"
335
+ ];
336
+ var CONFIG_DIRS = ["config", "secret", "tasks", "skills", "core", "agents"];
337
+ async function backup(opts = {}) {
310
338
  const abHome = abtarsHome();
339
+ const abmindHome = process.env["ABMIND_HOME"] ?? join4(dirname(abHome), ".abmind");
311
340
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
312
- const destDir = outputDir ?? join4(dirname(abHome), ".backup-abtars");
341
+ const destDir = opts.outputDir ?? join4(dirname(abHome), ".backup-abtars");
313
342
  mkdirSync(destDir, { recursive: true });
314
- const abmindPaths = [
315
- join4(dirname(abHome), ".abmind", "lib", "node_modules", "abmind", "dist", "cli", "abmind-backup.js"),
316
- join4(dirname(abHome), "workspace", "ab", "abmind", "dist", "cli", "abmind-backup.js")
317
- ];
318
- const abmindBin = abmindPaths.find((p) => existsSync3(p));
319
- let abmResult;
320
- if (abmindBin) {
321
- abmResult = spawnSync("node", [abmindBin], { encoding: "utf-8", env: { ...process.env } });
343
+ const isConfig = opts.config === true;
344
+ const prefix = isConfig ? `abtars-config-${date}` : `abtars-${date}`;
345
+ const zipExcludes = [];
346
+ if (isConfig) {
322
347
  } else {
323
- abmResult = spawnSync("abmind", ["backup"], { encoding: "utf-8", env: { ...process.env } });
324
- }
325
- if (abmResult.status !== 0) {
326
- process.stderr.write(`\u26A0\uFE0F abmind backup failed: ${abmResult.stderr || abmResult.stdout}
327
- `);
328
- return 1;
329
- }
330
- const abmindBackupsDir = join4(dirname(abHome), ".abmind", "backups");
331
- let abmFile;
332
- if (existsSync3(abmindBackupsDir)) {
333
- const abmFiles = readdirSync2(abmindBackupsDir).filter((f) => f.endsWith(".abm")).sort().reverse();
334
- if (abmFiles.length > 0) abmFile = abmFiles[0];
335
- }
336
- if (abmFile) {
337
- const src = join4(abmindBackupsDir, abmFile);
338
- const dest = join4(destDir, `abmind-${date}.abm`);
339
- renameSync(src, dest);
340
- const size = (statSync(dest).size / 1024).toFixed(0);
341
- process.stdout.write(`\u2713 abmind-${date}.abm (${size}KB, encrypted)
342
- `);
343
- } else {
344
- process.stderr.write(`\u26A0\uFE0F abmind backup produced no .abm file
345
- `);
346
- }
347
- const files = [];
348
- for (const dir of AB_SAVE) {
349
- const abs = join4(abHome, dir);
350
- if (existsSync3(abs)) collectDir(abs, dir, files);
351
- }
352
- if (files.length === 0) {
353
- process.stderr.write("Nothing to zip from abtars.\n");
354
- return 1;
355
- }
356
- const tmpDir = join4(process.env["TMPDIR"] ?? "/tmp", `abtars-backup-${Date.now()}`);
357
- mkdirSync(tmpDir, { recursive: true });
358
- for (const f of files) {
359
- const dest = join4(tmpDir, f.zipPath);
360
- mkdirSync(dirname(dest), { recursive: true });
361
- copyFileSync(f.absPath, dest);
348
+ for (const ex of ABTARS_EXCLUDE) {
349
+ zipExcludes.push(`${ex}/*`, ex);
350
+ }
351
+ zipExcludes.push("*.db-wal", "*.db-shm", "*.sock");
362
352
  }
363
- const zipPath = join4(destDir, `abtars-${date}.zip`);
364
353
  const has7z = spawnSync("which", ["7z"], { encoding: "utf-8" }).status === 0;
354
+ const zipExt = has7z ? ".7z" : ".zip";
355
+ let zipPath = join4(destDir, prefix + zipExt);
365
356
  let zipOk;
366
- if (has7z) {
367
- const r = spawnSync("7z", ["a", zipPath.replace(/\.zip$/, ".7z"), "."], { cwd: tmpDir, encoding: "utf-8" });
368
- zipOk = r.status === 0;
357
+ if (isConfig) {
358
+ const existingDirs = CONFIG_DIRS.filter((d) => existsSync3(join4(abHome, d)));
359
+ if (existingDirs.length === 0) {
360
+ process.stderr.write("Nothing to backup \u2014 no config dirs found\n");
361
+ return 1;
362
+ }
363
+ if (has7z) {
364
+ const r = spawnSync("7z", ["a", zipPath, ...existingDirs], { cwd: abHome, encoding: "utf-8" });
365
+ zipOk = r.status === 0;
366
+ } else {
367
+ const r = spawnSync("zip", ["-qr", zipPath, ...existingDirs], { cwd: abHome, encoding: "utf-8" });
368
+ zipOk = r.status === 0;
369
+ }
369
370
  } else {
370
- const r = spawnSync("zip", ["-r", zipPath, "."], { cwd: tmpDir, encoding: "utf-8" });
371
- zipOk = r.status === 0;
371
+ if (has7z) {
372
+ const excludeArgs = ABTARS_EXCLUDE.flatMap((ex) => ["-xr!" + ex]);
373
+ excludeArgs.push("-xr!*.db-wal", "-xr!*.db-shm", "-xr!*.sock");
374
+ const r = spawnSync("7z", ["a", zipPath, ".", ...excludeArgs], { cwd: abHome, encoding: "utf-8" });
375
+ zipOk = r.status === 0;
376
+ } else {
377
+ const excludeArgs = ABTARS_EXCLUDE.flatMap((ex) => ["-x", `${ex}/*`, "-x", ex]);
378
+ excludeArgs.push("-x", "*.db-wal", "-x", "*.db-shm", "-x", "*.sock");
379
+ const r = spawnSync("zip", ["-qr", zipPath, ".", ...excludeArgs], { cwd: abHome, encoding: "utf-8" });
380
+ zipOk = r.status === 0;
381
+ }
382
+ if (zipOk && existsSync3(abmindHome)) {
383
+ if (has7z) {
384
+ const excludeArgs = ABMIND_EXCLUDE.flatMap((ex) => ["-xr!" + ex]);
385
+ const r = spawnSync("7z", ["a", zipPath, ".", ...excludeArgs], { cwd: abmindHome, encoding: "utf-8" });
386
+ zipOk = r.status === 0;
387
+ } else {
388
+ const excludeArgs = ABMIND_EXCLUDE.flatMap((ex) => ["-x", `${ex}/*`, "-x", ex]);
389
+ const r = spawnSync("zip", ["-qr", zipPath, "-g", ".", ...excludeArgs], { cwd: abmindHome, encoding: "utf-8" });
390
+ zipOk = r.status === 0;
391
+ }
392
+ }
393
+ const dbPath = join4(abmindHome, "memory", "memory.db");
394
+ if (zipOk && existsSync3(dbPath)) {
395
+ const tmpDb = join4(process.env["TMPDIR"] ?? "/tmp", `abtars-backup-memory-${Date.now()}.db`);
396
+ const sqliteResult = spawnSync("sqlite3", [dbPath, `.backup '${tmpDb}'`], { encoding: "utf-8" });
397
+ if (sqliteResult.status === 0 && existsSync3(tmpDb)) {
398
+ if (has7z) {
399
+ spawnSync("7z", ["a", zipPath, tmpDb], { encoding: "utf-8" });
400
+ } else {
401
+ spawnSync("zip", ["-qj", zipPath, tmpDb], { encoding: "utf-8" });
402
+ }
403
+ try {
404
+ unlinkSync3(tmpDb);
405
+ } catch {
406
+ }
407
+ }
408
+ }
372
409
  }
373
- spawnSync("rm", ["-rf", tmpDir]);
374
410
  if (!zipOk) {
375
- process.stderr.write(`\u26A0\uFE0F zip/7z failed
376
- `);
411
+ process.stderr.write("Backup zip failed\n");
377
412
  return 1;
378
413
  }
379
- const actualZip = has7z ? zipPath.replace(/\.zip$/, ".7z") : zipPath;
380
- const sizeMb = (statSync(actualZip).size / 1024 / 1024).toFixed(1);
381
- process.stdout.write(`\u2713 ${actualZip.split("/").pop()} (${sizeMb}MB, ${files.length} files)
414
+ if (!isConfig) {
415
+ const actual = statSync(zipPath).size;
416
+ if (actual < 1e5) {
417
+ process.stderr.write(`\u26A0\uFE0F Backup suspiciously small (${actual} bytes)
382
418
  `);
383
- const maxAge = (pruneDays ?? DEFAULT_PRUNE_DAYS) * 864e5;
384
- const now = Date.now();
385
- for (const f of readdirSync2(destDir)) {
386
- if (!(f.startsWith("abtars-") || f.startsWith("abmind-"))) continue;
387
- if (!(f.endsWith(".zip") || f.endsWith(".7z") || f.endsWith(".abm"))) continue;
388
- const fPath = join4(destDir, f);
389
- try {
390
- if (now - statSync(fPath).mtimeMs > maxAge) {
391
- unlinkSync3(fPath);
392
- process.stdout.write(` \u{1F5D1} pruned ${f}
419
+ }
420
+ }
421
+ const zipSize = (statSync(zipPath).size / 1024 / 1024).toFixed(1);
422
+ process.stdout.write(`\u2713 ${basename(zipPath)} (${zipSize}MB)
423
+ `);
424
+ if (opts.encrypt) {
425
+ const encPath = zipPath + ".enc";
426
+ const ok = encryptFile(zipPath, encPath, abmindHome);
427
+ if (!ok) return 1;
428
+ unlinkSync3(zipPath);
429
+ zipPath = encPath;
430
+ process.stdout.write(`\u2713 encrypted \u2192 ${basename(encPath)}
431
+ `);
432
+ }
433
+ if (!isConfig) {
434
+ const abmResult = runAbmindBackup(abHome, abmindHome);
435
+ if (abmResult) {
436
+ const dest = join4(destDir, `abmind-${date}.abm`);
437
+ renameSync(abmResult, dest);
438
+ const size = (statSync(dest).size / 1024).toFixed(0);
439
+ process.stdout.write(`\u2713 abmind-${date}.abm (${size}KB, encrypted)
393
440
  `);
441
+ } else {
442
+ process.stderr.write("\u26A0\uFE0F abmind backup failed or produced no .abm\n");
443
+ }
444
+ }
445
+ const pruneDays = opts.pruneDays ?? DEFAULT_PRUNE_DAYS;
446
+ if (pruneDays > 0) {
447
+ const maxAge = pruneDays * 864e5;
448
+ const now = Date.now();
449
+ for (const f of readdirSync2(destDir)) {
450
+ if (!(f.startsWith("abtars-") || f.startsWith("abmind-"))) continue;
451
+ if (!(f.endsWith(".zip") || f.endsWith(".7z") || f.endsWith(".abm") || f.endsWith(".enc"))) continue;
452
+ const fPath = join4(destDir, f);
453
+ try {
454
+ if (now - statSync(fPath).mtimeMs > maxAge) {
455
+ unlinkSync3(fPath);
456
+ process.stdout.write(` \u{1F5D1} pruned ${f}
457
+ `);
458
+ }
459
+ } catch {
394
460
  }
395
- } catch {
396
461
  }
397
462
  }
398
463
  return 0;
399
464
  }
400
- function collectDir(dir, prefix, out) {
401
- for (const entry of readdirSync2(dir, { withFileTypes: true })) {
402
- const abs = join4(dir, entry.name);
403
- const rel = `${prefix}/${entry.name}`;
404
- if (entry.isDirectory()) {
405
- collectDir(abs, rel, out);
406
- } else if (entry.isFile()) {
407
- out.push({ absPath: abs, zipPath: rel });
408
- }
465
+ function runAbmindBackup(abHome, abmindHome) {
466
+ const abmindPaths = [
467
+ join4(dirname(abHome), "workspace", "ab", "abmind", "dist", "cli", "abmind-backup.js"),
468
+ join4(abmindHome, "lib", "node_modules", "abmind", "dist", "cli", "abmind-backup.js")
469
+ ];
470
+ const abmindBin = abmindPaths.find((p) => existsSync3(p));
471
+ let result;
472
+ if (abmindBin) {
473
+ result = spawnSync("node", [abmindBin], { encoding: "utf-8", env: { ...process.env } });
474
+ } else {
475
+ result = spawnSync("abmind", ["backup"], { encoding: "utf-8", env: { ...process.env } });
476
+ }
477
+ if (result.status !== 0) return null;
478
+ const backupsDir = join4(abmindHome, "backups");
479
+ if (!existsSync3(backupsDir)) return null;
480
+ const abmFiles = readdirSync2(backupsDir).filter((f) => f.endsWith(".abm")).sort().reverse();
481
+ return abmFiles[0] ? join4(backupsDir, abmFiles[0]) : null;
482
+ }
483
+ function encryptFile(inputPath, outputPath, abmindHome) {
484
+ const keyPath = join4(abmindHome, "secret", "abmind.key");
485
+ if (!existsSync3(keyPath)) {
486
+ process.stderr.write(`\u26A0\uFE0F --encrypt requires ${keyPath}
487
+ `);
488
+ return false;
409
489
  }
490
+ const master = Buffer.from(readFileSync2(keyPath, "utf-8").trim(), "hex");
491
+ const key = Buffer.from(hkdfSync("sha256", master, "", "abtars-backup-v1", 32));
492
+ const iv = randomBytes(12);
493
+ const cipher = createCipheriv("aes-256-gcm", key, iv);
494
+ const input = readFileSync2(inputPath);
495
+ const encrypted = Buffer.concat([cipher.update(input), cipher.final()]);
496
+ const tag = cipher.getAuthTag();
497
+ const out = Buffer.concat([iv, encrypted, tag]);
498
+ writeFileSync(outputPath, out);
499
+ return true;
410
500
  }
411
501
 
412
502
  // src/cli/commands/onboard.ts
413
503
  init_log_and_swallow();
414
504
  import { mkdir, readFile, writeFile } from "node:fs/promises";
415
505
  import { dirname as dirname2, join as join6 } from "node:path";
506
+ import { homedir as homedir2 } from "node:os";
416
507
  import { fileURLToPath } from "node:url";
417
508
 
418
509
  // src/components/hints.ts
419
- import { existsSync as existsSync4, readFileSync as readFileSync2, writeFileSync, renameSync as renameSync2 } from "node:fs";
510
+ import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2, renameSync as renameSync2 } from "node:fs";
420
511
  import { join as join5 } from "node:path";
421
512
  function manifestPath() {
422
513
  const home = process.env["ABTARS_HOME"] ?? join5(process.env["HOME"] ?? "", ".abtars");
@@ -426,7 +517,7 @@ function readManifest2() {
426
517
  const path = manifestPath();
427
518
  if (!existsSync4(path)) return {};
428
519
  try {
429
- return JSON.parse(readFileSync2(path, "utf-8"));
520
+ return JSON.parse(readFileSync3(path, "utf-8"));
430
521
  } catch {
431
522
  return {};
432
523
  }
@@ -434,7 +525,7 @@ function readManifest2() {
434
525
  function writeManifestAtomic(data) {
435
526
  const path = manifestPath();
436
527
  const tmp = path + ".tmp";
437
- writeFileSync(tmp, JSON.stringify(data, null, 2) + "\n");
528
+ writeFileSync2(tmp, JSON.stringify(data, null, 2) + "\n");
438
529
  renameSync2(tmp, path);
439
530
  }
440
531
  function showHintOnce(id, text) {
@@ -487,7 +578,7 @@ async function runInteractive(existing) {
487
578
  const { intro, outro, text, select, confirm: confirm2, isCancel, cancel } = await import("./dist-J3T4XVKX.js");
488
579
  intro("abtars onboard \u2014 first-time setup");
489
580
  const noteEmpty = "press Enter to skip";
490
- const { packagePaths: pp, readManifest: rm3 } = await import("./deploy-lib-import-ODLDL2DB.js");
581
+ const { packagePaths: pp, readManifest: rm3 } = await import("./deploy-lib-import-PJXUADPS.js");
491
582
  const mfPaths = pp("abtars");
492
583
  const mf = await rm3(mfPaths.manifest);
493
584
  const installMode = mf?.installMode ?? "supervised";
@@ -753,7 +844,7 @@ function validateNonInteractive(opts) {
753
844
  }
754
845
  return {
755
846
  installMode: "supervised",
756
- userName: "",
847
+ userName: opts.userName ?? "",
757
848
  passphrase: "",
758
849
  telegramToken: opts.telegramToken ?? "",
759
850
  telegramChatId: opts.telegramChatId ?? "",
@@ -818,7 +909,10 @@ function mergeEnvContent(existing, answers) {
818
909
  "WAKE_TIME",
819
910
  "HEARTBEAT_INTERVAL_SEC",
820
911
  "EMBEDDING_ENABLED",
821
- "TRUST_MODE"
912
+ "TRUST_MODE",
913
+ "TELEGRAM_ENABLED",
914
+ "DISCORD_ENABLED",
915
+ "IRC_ENABLED"
822
916
  ]);
823
917
  const keptLines = [];
824
918
  for (const line of existing.split("\n")) {
@@ -843,6 +937,9 @@ function mergeEnvContent(existing, answers) {
843
937
  newBlock.push(`EMBEDDING_ENABLED=${answers.embeddingEnabled ? "true" : "false"}`);
844
938
  newBlock.push(`SECURITY_MODE=${answers.securityMode}`);
845
939
  newBlock.push(`TRUST_MODE=${answers.trustMode ? "true" : "false"}`);
940
+ newBlock.push(`TELEGRAM_ENABLED=${answers.telegramToken ? "true" : "false"}`);
941
+ newBlock.push(`DISCORD_ENABLED=${answers.discordBotToken ? "true" : "false"}`);
942
+ newBlock.push(`IRC_ENABLED=false`);
846
943
  return [...keptLines, ...newBlock, ""].join("\n");
847
944
  }
848
945
  async function onboard(opts) {
@@ -917,7 +1014,7 @@ async function onboard(opts) {
917
1014
  }
918
1015
  if (secrets.length > 0) process.stdout.write(`\u2713 ${secrets.length} secrets \u2192 ${secretDirPath}
919
1016
  `);
920
- const { writeManifest: writeManifest2 } = await import("./deploy-lib-import-ODLDL2DB.js");
1017
+ const { writeManifest: writeManifest2 } = await import("./deploy-lib-import-PJXUADPS.js");
921
1018
  const mf = await readManifest(paths.manifest);
922
1019
  if (mf) await writeManifest2(paths.manifest, { ...mf, installMode: answers.installMode });
923
1020
  process.stdout.write(`\u2713 install mode: ${answers.installMode}
@@ -974,6 +1071,15 @@ async function onboard(opts) {
974
1071
  await writeFile(usersPath, JSON.stringify(users, null, 2) + "\n", { mode: 384 });
975
1072
  process.stdout.write(`\u2713 users.json \u2192 ${usersPath}
976
1073
  `);
1074
+ try {
1075
+ const manifestPath2 = join6(homedir2(), ".abmind", "manifest.json");
1076
+ const manifest2 = JSON.parse((await import("node:fs")).readFileSync(manifestPath2, "utf-8"));
1077
+ if (manifest2.encryptionUser && manifest2.encryptionUser !== answers.userName) {
1078
+ process.stdout.write(`\u26A0\uFE0F abmind encryption uses name '${manifest2.encryptionUser}' but you entered '${answers.userName}'. Backup restore will need the encryption name ('${manifest2.encryptionUser}').
1079
+ `);
1080
+ }
1081
+ } catch {
1082
+ }
977
1083
  }
978
1084
  if (answers.userName) {
979
1085
  const abmindHome = process.env["ABMIND_HOME"] ?? join6(dirname2(paths.home), ".abmind");
@@ -1039,8 +1145,8 @@ Next: 'abtars update' to build, then start the bridge.
1039
1145
  `);
1040
1146
  return 0;
1041
1147
  }
1042
- const { existsSync: existsSync8 } = await import("node:fs");
1043
- const hasRelease = existsSync8(join6(paths.home, "current"));
1148
+ const { existsSync: existsSync9 } = await import("node:fs");
1149
+ const hasRelease = existsSync9(join6(paths.home, "current"));
1044
1150
  if (!hasRelease) {
1045
1151
  process.stdout.write(`
1046
1152
  Next: run 'abtars update' to stage the release, then start the bridge.
@@ -1060,9 +1166,9 @@ Next: start the bridge
1060
1166
  return 0;
1061
1167
  }
1062
1168
  async function seedDefaultTasks(chatId, abtarsHome4) {
1063
- const { existsSync: existsSync8 } = await import("node:fs");
1064
- const tasksJson = join6(abtarsHome4, "state", "tasks.json");
1065
- if (existsSync8(tasksJson)) {
1169
+ const { existsSync: existsSync9 } = await import("node:fs");
1170
+ const tasksJson = join6(abtarsHome4, "tasks", "tasks.json");
1171
+ if (existsSync9(tasksJson)) {
1066
1172
  process.stdout.write(`\u2022 tasks.json exists \u2014 skipping default-task seed
1067
1173
  `);
1068
1174
  return;
@@ -1075,7 +1181,7 @@ async function seedDefaultTasks(chatId, abtarsHome4) {
1075
1181
  ];
1076
1182
  let templatePath = null;
1077
1183
  for (const p of candidates) {
1078
- if (existsSync8(p)) {
1184
+ if (existsSync9(p)) {
1079
1185
  templatePath = p;
1080
1186
  break;
1081
1187
  }
@@ -1131,95 +1237,13 @@ async function seedDefaultTasks(chatId, abtarsHome4) {
1131
1237
  }
1132
1238
  }
1133
1239
 
1134
- // src/cli/commands/rollback.ts
1135
- import { hostname } from "node:os";
1136
- async function rollback(opts) {
1137
- const paths = packagePaths("abtars");
1138
- const manifest = await readManifest(paths.manifest);
1139
- if (!manifest || !manifest.version) {
1140
- process.stderr.write(`No active release; nothing to roll back.
1141
- `);
1142
- return 2;
1143
- }
1144
- if (manifest.priorReleases.length === 0 && opts.to === void 0) {
1145
- process.stderr.write(`No prior releases recorded; nothing to roll back to.
1146
- `);
1147
- return 2;
1148
- }
1149
- const target = opts.to ?? manifest.priorReleases[0].version;
1150
- if (target === manifest.version) {
1151
- process.stdout.write(`Already at ${target}; no-op.
1152
- `);
1153
- return 0;
1154
- }
1155
- if (!await releaseExists(paths.releases, target)) {
1156
- const available = [manifest.version, ...manifest.priorReleases.map((r) => r.version)];
1157
- process.stderr.write(
1158
- `Target release '${target}' does not exist in ${paths.releases}.
1159
- Available: ${available.join(", ")}
1160
- If pruned, rebuild from the target's git SHA with 'abtars update' instead.
1161
- `
1162
- );
1163
- return 2;
1164
- }
1165
- const targetRecord = target === manifest.version ? {
1166
- version: manifest.version,
1167
- commit: manifest.commit,
1168
- activatedAt: manifest.activatedAt,
1169
- packageLockHash: manifest.packageLockHash
1170
- } : manifest.priorReleases.find((r) => r.version === target) ?? null;
1171
- if (manifest.packageLockHash && targetRecord?.packageLockHash && manifest.packageLockHash !== targetRecord.packageLockHash) {
1172
- process.stderr.write(
1173
- `v${manifest.version} pinned different deps than v${target} (package-lock hashes differ).
1174
- Rollback via symlink is unsafe. Instead:
1175
- git checkout ${targetRecord.commit ?? "<commit-from-manifest>"}
1176
- abtars update --from-local
1177
- This rebuilds node_modules/ to match the target release.
1178
- `
1179
- );
1180
- return 3;
1181
- }
1182
- const release = await acquireLock(paths.lock, `rollback --to ${target}`);
1183
- try {
1184
- await activate(paths.current, target);
1185
- process.stdout.write(`\u2713 current -> releases/${target}
1186
- `);
1187
- const newPrior = {
1188
- version: manifest.version,
1189
- commit: manifest.commit,
1190
- activatedAt: manifest.activatedAt,
1191
- packageLockHash: manifest.packageLockHash
1192
- };
1193
- const remainingPriors = manifest.priorReleases.filter((r) => r.version !== target);
1194
- await writeManifest(paths.manifest, {
1195
- ...manifest,
1196
- version: target,
1197
- commit: targetRecord?.commit ?? null,
1198
- packageLockHash: targetRecord?.packageLockHash ?? null,
1199
- activatedAt: (/* @__PURE__ */ new Date()).toISOString(),
1200
- priorReleases: [newPrior, ...remainingPriors]
1201
- });
1202
- process.stdout.write(`\u2713 manifest updated
1203
- `);
1204
- process.stdout.write(`
1205
- Rollback complete: ${target}
1206
- `);
1207
- void emptyManifest;
1208
- void hostname;
1209
- return 0;
1210
- } finally {
1211
- await release();
1212
- }
1213
- }
1214
-
1215
1240
  // src/cli/commands/status.ts
1216
- import { readFileSync as readFileSync3 } from "node:fs";
1241
+ import { existsSync as existsSync5 } from "node:fs";
1242
+ import { readFileSync as readFileSync4 } from "node:fs";
1217
1243
  import { join as join7 } from "node:path";
1218
1244
  async function status() {
1219
1245
  const paths = packagePaths("abtars");
1220
1246
  const manifest = await readManifest(paths.manifest);
1221
- const current = await readCurrent(paths.current);
1222
- const lock = await inspectLock(paths.lock);
1223
1247
  if (!manifest) {
1224
1248
  process.stdout.write(
1225
1249
  `abtars: not installed (no manifest at ${paths.manifest})
@@ -1228,6 +1252,8 @@ Run 'abtars install' to set up.
1228
1252
  );
1229
1253
  return 1;
1230
1254
  }
1255
+ const appExists = existsSync5(paths.app);
1256
+ const appPrevExists = existsSync5(paths.appPrev);
1231
1257
  const lines = [
1232
1258
  `abtars status`,
1233
1259
  ` home: ${paths.home}`,
@@ -1237,66 +1263,60 @@ Run 'abtars install' to set up.
1237
1263
  ` source: ${manifest.source}`,
1238
1264
  ` mode: ${manifest.installMode ?? "supervised"}`,
1239
1265
  ` activated: ${manifest.activatedAt}`,
1240
- ` current ->: ${current ?? "(missing)"}`,
1241
- ` host: ${manifest.host}`,
1242
- ` prior: ${manifest.priorReleases.length > 0 ? manifest.priorReleases.map((r) => r.version).join(", ") : "(none)"}`
1266
+ ` app/: ${appExists ? "\u2713 present" : "\u2717 missing"}`,
1267
+ ` app.prev/: ${appPrevExists ? "\u2713 present" : "\u25CB none"}`,
1268
+ ` previous: ${manifest.previousVersion ?? "(none)"}`,
1269
+ ` host: ${manifest.host}`
1243
1270
  ];
1244
- if (lock.held) {
1245
- const alive = (() => {
1246
- try {
1247
- process.kill(lock.content.pid, 0);
1248
- return true;
1249
- } catch {
1250
- return false;
1251
- }
1252
- })();
1253
- lines.push(
1254
- ` bridge: ${alive ? "\u25CF running" : "\u2717 dead"} (pid ${lock.content.pid})${lock.stale ? " \u2014 STALE" : ""}`
1255
- );
1256
- } else {
1257
- try {
1258
- const bridgeLock = JSON.parse(readFileSync3(join7(paths.home, "bridge.lock"), "utf-8"));
1259
- if (bridgeLock.pid) {
1260
- const alive = (() => {
1261
- try {
1262
- process.kill(bridgeLock.pid, 0);
1263
- return true;
1264
- } catch {
1265
- return false;
1266
- }
1267
- })();
1268
- lines.push(` bridge: ${alive ? "\u25CF running" : "\u2717 dead"} (pid ${bridgeLock.pid})`);
1269
- } else {
1270
- lines.push(` bridge: \u25CB stopped`);
1271
- }
1272
- } catch {
1271
+ try {
1272
+ const bridgeLock = JSON.parse(readFileSync4(join7(paths.home, "bridge.lock"), "utf-8"));
1273
+ if (bridgeLock.pid) {
1274
+ const alive = (() => {
1275
+ try {
1276
+ process.kill(bridgeLock.pid, 0);
1277
+ return true;
1278
+ } catch {
1279
+ return false;
1280
+ }
1281
+ })();
1282
+ lines.push(` bridge: ${alive ? "\u25CF running" : "\u2717 dead"} (pid ${bridgeLock.pid})`);
1283
+ } else {
1273
1284
  lines.push(` bridge: \u25CB stopped`);
1274
1285
  }
1286
+ } catch {
1287
+ lines.push(` bridge: \u25CB stopped`);
1275
1288
  }
1276
1289
  process.stdout.write(`${lines.join("\n")}
1277
1290
  `);
1278
- if (current !== null && manifest.version !== "" && current !== manifest.version) {
1279
- process.stderr.write(
1280
- `
1281
- Warning: current symlink points at '${current}' but manifest says '${manifest.version}'.
1282
- Re-run 'abtars update' or 'abtars rollback' to reconcile.
1283
- `
1284
- );
1291
+ const sentinel = readSentinel(paths.home);
1292
+ if (sentinel?.status === "pending") {
1293
+ const age = Date.now() - new Date(sentinel.startedAt).getTime();
1294
+ if (age > 5 * 6e4) {
1295
+ process.stderr.write(`
1296
+ \u26A0\uFE0F Last update (${sentinel.version}) may have failed \u2014 bridge never confirmed boot.
1297
+ `);
1298
+ return 1;
1299
+ }
1300
+ }
1301
+ if (!appExists) {
1302
+ process.stderr.write(`
1303
+ \u26A0\uFE0F app/ directory missing. Run 'abtars update' to deploy.
1304
+ `);
1285
1305
  return 1;
1286
1306
  }
1287
1307
  return 0;
1288
1308
  }
1289
1309
 
1290
1310
  // src/cli/commands/update.ts
1291
- init_log_and_swallow();
1292
- import { hostname as hostname2 } from "node:os";
1293
- import { join as join10, dirname as dirname3 } from "node:path";
1294
- import { readFileSync as readFileSync5, existsSync as existsSync7, mkdirSync as mkdirSync2 } from "node:fs";
1295
- import { copyFile as copyFile2, mkdir as mkdir4, chmod, readdir, readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
1311
+ import { hostname } from "node:os";
1312
+ import { join as join10 } from "node:path";
1313
+ import { readFileSync as readFileSync6, existsSync as existsSync8 } from "node:fs";
1314
+ import { copyFile as copyFile2, mkdir as mkdir4, chmod, readdir } from "node:fs/promises";
1315
+ import { rmSync as rmSync2, cpSync, readdirSync as readdirSync3, mkdirSync as mkdirSync2 } from "node:fs";
1296
1316
 
1297
1317
  // src/cli/update-sources/local.ts
1298
1318
  import { spawnSync as spawnSync2 } from "node:child_process";
1299
- import { existsSync as existsSync5 } from "node:fs";
1319
+ import { existsSync as existsSync6 } from "node:fs";
1300
1320
  import { copyFile, cp, mkdir as mkdir2, readFile as readFile2, rm, writeFile as writeFile2 } from "node:fs/promises";
1301
1321
  import { join as join8 } from "node:path";
1302
1322
  var LocalBuildError = class extends Error {
@@ -1368,29 +1388,29 @@ async function readPackageVersion(repoRoot) {
1368
1388
  }
1369
1389
  function makeLocalBuildSource(opts = {}) {
1370
1390
  const repoRoot = opts.repoRoot ?? process.cwd();
1371
- const isNpmPackage = !existsSync5(join8(repoRoot, ".git"));
1391
+ const isNpmPackage = !existsSync6(join8(repoRoot, ".git"));
1372
1392
  return {
1373
1393
  name: "local",
1374
1394
  async prepare(ctx) {
1375
1395
  if (isNpmPackage) {
1376
1396
  const bundleDir = join8(repoRoot, "bundle");
1377
- if (!existsSync5(bundleDir)) {
1397
+ if (!existsSync6(bundleDir)) {
1378
1398
  throw new Error(`No bundle/ found at ${repoRoot}. Run from the abtars npm package or a git checkout.`);
1379
1399
  }
1380
1400
  const pkgVersion2 = await readPackageVersion(repoRoot);
1381
1401
  const version2 = pkgVersion2;
1382
- const stagedPath2 = join8(ctx.releasesDir, version2);
1402
+ const stagedPath2 = ctx.stagingDir;
1383
1403
  await rm(stagedPath2, { recursive: true, force: true });
1384
1404
  await mkdir2(stagedPath2, { recursive: true });
1385
1405
  await cp(bundleDir, join8(stagedPath2, "bundle"), { recursive: true });
1386
1406
  const coreDir = join8(repoRoot, "core");
1387
- if (existsSync5(coreDir)) await cp(coreDir, join8(stagedPath2, "core"), { recursive: true });
1407
+ if (existsSync6(coreDir)) await cp(coreDir, join8(stagedPath2, "core"), { recursive: true });
1388
1408
  const scriptsSrc = join8(repoRoot, "scripts");
1389
- if (existsSync5(scriptsSrc)) await cp(scriptsSrc, join8(stagedPath2, "scripts"), { recursive: true });
1409
+ if (existsSync6(scriptsSrc)) await cp(scriptsSrc, join8(stagedPath2, "scripts"), { recursive: true });
1390
1410
  const configSrc = join8(repoRoot, "config");
1391
- if (existsSync5(configSrc)) await cp(configSrc, join8(stagedPath2, "config"), { recursive: true });
1411
+ if (existsSync6(configSrc)) await cp(configSrc, join8(stagedPath2, "config"), { recursive: true });
1392
1412
  const manifestSrc = join8(repoRoot, "install-manifest.json");
1393
- if (existsSync5(manifestSrc)) await copyFile(manifestSrc, join8(stagedPath2, "install-manifest.json"));
1413
+ if (existsSync6(manifestSrc)) await copyFile(manifestSrc, join8(stagedPath2, "install-manifest.json"));
1394
1414
  await writeFile2(join8(stagedPath2, "package.json"), JSON.stringify({ type: "module", name: "abtars", version: version2 }, null, 2) + "\n");
1395
1415
  process.stdout.write(`\u2713 staged ${version2} (from npm package)
1396
1416
  `);
@@ -1402,41 +1422,17 @@ function makeLocalBuildSource(opts = {}) {
1402
1422
  if (opts.skipInstall !== true) {
1403
1423
  runCmd("npm", ["install", "--no-audit", "--no-fund"], repoRoot);
1404
1424
  }
1405
- const useBundle = process.env["AGENTBRIDGE_BUILD_MODE"] !== "tsc";
1406
- if (useBundle) {
1407
- runCmd("npm", ["run", "bundle"], repoRoot);
1408
- const stagedPath2 = join8(ctx.releasesDir, version);
1409
- await rm(stagedPath2, { recursive: true, force: true });
1410
- await mkdir2(stagedPath2, { recursive: true });
1411
- await cp(join8(repoRoot, "bundle"), join8(stagedPath2, "bundle"), { recursive: true });
1412
- const coreSkillsSrc = join8(repoRoot, "core", "skills");
1413
- if (existsSync5(coreSkillsSrc)) {
1414
- await cp(coreSkillsSrc, join8(stagedPath2, "core", "skills"), { recursive: true });
1415
- }
1416
- await writeFile2(join8(stagedPath2, "package.json"), JSON.stringify({ type: "module", name: "abtars", version }, null, 2) + "\n");
1417
- await copyFile(join8(repoRoot, "install-manifest.json"), join8(stagedPath2, "install-manifest.json"));
1418
- const packageLockHash2 = await hashFile(join8(repoRoot, "package-lock.json"));
1419
- return { version, stagedPath: stagedPath2, commit, branch, packageLockHash: packageLockHash2, source: "local" };
1420
- }
1421
- runCmd("npm", ["run", "build"], repoRoot);
1422
- const stagedPath = join8(ctx.releasesDir, version);
1425
+ runCmd("npm", ["run", "bundle"], repoRoot);
1426
+ const stagedPath = ctx.stagingDir;
1423
1427
  await rm(stagedPath, { recursive: true, force: true });
1424
1428
  await mkdir2(stagedPath, { recursive: true });
1425
- await cp(join8(repoRoot, "dist"), join8(stagedPath, "dist"), { recursive: true });
1426
- await copyFile(join8(repoRoot, "install-manifest.json"), join8(stagedPath, "install-manifest.json"));
1427
- await rm(ctx.nodeModulesDir, { recursive: true, force: true });
1428
- await mkdir2(ctx.nodeModulesDir, { recursive: true });
1429
- const rsyncResult = spawnSync2(
1430
- "rsync",
1431
- ["-aL", "--quiet", `${join8(repoRoot, "node_modules")}/`, `${ctx.nodeModulesDir}/`],
1432
- { stdio: "inherit" }
1433
- );
1434
- if (rsyncResult.status !== 0) {
1435
- throw new LocalBuildError(
1436
- `rsync of node_modules failed (status ${rsyncResult.status ?? -1})`,
1437
- `Ensure rsync is installed. Falling back to node cp would re-create symlinks.`
1438
- );
1429
+ await cp(join8(repoRoot, "bundle"), join8(stagedPath, "bundle"), { recursive: true });
1430
+ const coreSkillsSrc = join8(repoRoot, "core", "skills");
1431
+ if (existsSync6(coreSkillsSrc)) {
1432
+ await cp(coreSkillsSrc, join8(stagedPath, "core", "skills"), { recursive: true });
1439
1433
  }
1434
+ await writeFile2(join8(stagedPath, "package.json"), JSON.stringify({ type: "module", name: "abtars", version }, null, 2) + "\n");
1435
+ await copyFile(join8(repoRoot, "install-manifest.json"), join8(stagedPath, "install-manifest.json"));
1440
1436
  const packageLockHash = await hashFile(join8(repoRoot, "package-lock.json"));
1441
1437
  return { version, stagedPath, commit, branch, packageLockHash, source: "local" };
1442
1438
  }
@@ -1445,7 +1441,7 @@ function makeLocalBuildSource(opts = {}) {
1445
1441
 
1446
1442
  // src/cli/update-sources/npm.ts
1447
1443
  import { spawnSync as spawnSync3 } from "node:child_process";
1448
- import { existsSync as existsSync6, readFileSync as readFileSync4, unlinkSync as unlinkSync4 } from "node:fs";
1444
+ import { existsSync as existsSync7, readFileSync as readFileSync5, unlinkSync as unlinkSync4 } from "node:fs";
1449
1445
  import { mkdir as mkdir3, rm as rm2 } from "node:fs/promises";
1450
1446
  import { join as join9 } from "node:path";
1451
1447
  var TIMEOUT_MS = 6e4;
@@ -1457,7 +1453,7 @@ function run(cmd, args, cwd) {
1457
1453
  }
1458
1454
  function readLocalVersion(home) {
1459
1455
  try {
1460
- const pkg = JSON.parse(readFileSync4(join9(home, "current", "package.json"), "utf-8"));
1456
+ const pkg = JSON.parse(readFileSync5(join9(home, "app", "package.json"), "utf-8"));
1461
1457
  return pkg.version ?? null;
1462
1458
  } catch {
1463
1459
  return null;
@@ -1472,14 +1468,14 @@ function makeNpmSource(packageName) {
1472
1468
  if (latest === current) {
1473
1469
  throw new Error(`Already at latest version (${latest}). Nothing to update.`);
1474
1470
  }
1475
- const stagedPath = join9(ctx.releasesDir, latest);
1471
+ const stagedPath = ctx.stagingDir;
1476
1472
  await rm2(stagedPath, { recursive: true, force: true });
1477
1473
  await mkdir3(stagedPath, { recursive: true });
1478
1474
  run("npm", ["pack", `${packageName}@${latest}`, "--pack-destination", stagedPath], stagedPath);
1479
1475
  const tgzName = `${packageName}-${latest}.tgz`.replace("@", "").replace("/", "-");
1480
1476
  const tgzPath = join9(stagedPath, tgzName);
1481
1477
  run("tar", ["-xzf", tgzPath, "--strip-components=1"], stagedPath);
1482
- if (existsSync6(tgzPath)) unlinkSync4(tgzPath);
1478
+ if (existsSync7(tgzPath)) unlinkSync4(tgzPath);
1483
1479
  run("npm", ["install", "--omit=dev", "--no-audit", "--no-fund"], stagedPath);
1484
1480
  return { version: latest, stagedPath, commit: null, branch: null, packageLockHash: null, source: "npm" };
1485
1481
  }
@@ -1489,7 +1485,7 @@ function makeNpmSource(packageName) {
1489
1485
  // src/cli/commands/update.ts
1490
1486
  function readJsonField2(file, field) {
1491
1487
  try {
1492
- return JSON.parse(readFileSync5(file, "utf-8"))[field];
1488
+ return JSON.parse(readFileSync6(file, "utf-8"))[field];
1493
1489
  } catch {
1494
1490
  return void 0;
1495
1491
  }
@@ -1502,42 +1498,66 @@ Use --source local (default) or --source npm.
1502
1498
  return 2;
1503
1499
  }
1504
1500
  const paths = packagePaths("abtars");
1505
- if (!existsSync7(paths.releases) && existsSync7(join10(paths.home, "current"))) {
1506
- process.stdout.write("Migrating old layout \u2192 releases/...\n");
1507
- mkdirSync2(paths.releases, { recursive: true });
1501
+ const sentinel = readSentinel(paths.home);
1502
+ if (sentinel?.status === "pending") {
1503
+ const age = Date.now() - new Date(sentinel.startedAt).getTime();
1504
+ if (age > 5 * 6e4) {
1505
+ process.stderr.write(`\u26A0\uFE0F Previous update (${sentinel.version}) never completed successfully. Proceeding...
1506
+ `);
1507
+ }
1508
+ }
1509
+ if (opts.check) {
1510
+ return checkForUpdates(paths.home, opts);
1508
1511
  }
1509
1512
  const release = await acquireLock(paths.lock, `update --source ${opts.source}`);
1510
1513
  try {
1514
+ cleanStaleStaging(paths.appStaging);
1515
+ const cleanupHandler = () => {
1516
+ if (existsSync8(paths.appStaging) && existsSync8(paths.app)) {
1517
+ rmSync2(paths.appStaging, { recursive: true, force: true });
1518
+ }
1519
+ };
1520
+ process.on("SIGHUP", () => {
1521
+ cleanupHandler();
1522
+ process.exit(130);
1523
+ });
1524
+ process.on("SIGTERM", () => {
1525
+ cleanupHandler();
1526
+ process.exit(143);
1527
+ });
1511
1528
  let repoRoot = opts.repoRoot ?? process.cwd();
1512
- if (!opts.repoRoot && !existsSync7(join10(repoRoot, ".git"))) {
1529
+ if (!opts.repoRoot && !existsSync8(join10(repoRoot, ".git"))) {
1513
1530
  const { realpathSync } = await import("node:fs");
1514
1531
  const scriptPath = realpathSync(process.argv[1] ?? "");
1532
+ const { dirname: dirname3 } = await import("node:path");
1515
1533
  const candidate = join10(dirname3(scriptPath), "..");
1516
- if (existsSync7(join10(candidate, "bundle"))) repoRoot = candidate;
1534
+ if (existsSync8(join10(candidate, "bundle"))) repoRoot = candidate;
1517
1535
  }
1518
1536
  const source = opts.source === "npm" ? makeNpmSource("abtars") : makeLocalBuildSource({ repoRoot, allowStale: opts.fromLocal });
1519
1537
  if (opts.fromLocal) {
1520
1538
  showHintOnce("update-from-local", "Building from working copy (--from-local). To sync with remote first: git pull && abtars update");
1521
1539
  }
1522
- process.stdout.write(`Building from local checkout (${process.cwd()})...
1540
+ if (opts.dryRun) {
1541
+ return printDryRun(paths, repoRoot, opts);
1542
+ }
1543
+ process.stdout.write(`Building from local checkout (${repoRoot})...
1523
1544
  `);
1524
1545
  const staged = await source.prepare({
1525
- releasesDir: paths.releases,
1526
- nodeModulesDir: paths.nodeModules,
1546
+ stagingDir: paths.appStaging,
1527
1547
  home: paths.home,
1528
1548
  allowStale: opts.fromLocal
1529
1549
  });
1530
- process.stdout.write(`\u2713 staged ${staged.version} at ${staged.stagedPath}
1550
+ process.stdout.write(`\u2713 staged ${staged.version}
1531
1551
  `);
1532
1552
  {
1533
1553
  const pkgPath = join10(staged.stagedPath, "package.json");
1534
- const { readFileSync: readFileSync6, writeFileSync: writeFileSync2 } = await import("node:fs");
1535
1554
  try {
1536
1555
  const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
1537
1556
  const externals = { patchright: "^1.59.4", "rettiwt-api": "^4.1.3" };
1538
1557
  pkg.dependencies = { ...pkg.dependencies, ...externals };
1539
1558
  if (pkg.dependencies?.abmind?.startsWith("file:")) delete pkg.dependencies.abmind;
1540
- writeFileSync2(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
1559
+ const { writeFileSync: writeFileSync3 } = await import("node:fs");
1560
+ writeFileSync3(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
1541
1561
  const { execSync } = await import("node:child_process");
1542
1562
  execSync("npm install --omit=dev --ignore-scripts 2>/dev/null", { cwd: staged.stagedPath, timeout: 6e4 });
1543
1563
  process.stdout.write(`\u2713 external deps installed
@@ -1547,288 +1567,293 @@ Use --source local (default) or --source npm.
1547
1567
  `);
1548
1568
  }
1549
1569
  }
1550
- {
1551
- const { existsSync: existsSync8, unlinkSync: unlinkSync5, symlinkSync } = await import("node:fs");
1552
- const mainLink = join10(staged.stagedPath, "main.js");
1553
- try {
1554
- unlinkSync5(mainLink);
1555
- } catch (err) {
1556
- logAndSwallow("update", "op", err);
1557
- }
1558
- const entry = existsSync8(join10(staged.stagedPath, "bundle", "abtars.js")) ? "bundle/abtars.js" : "dist/main.js";
1559
- symlinkSync(entry, mainLink);
1560
- }
1561
- const { existsSync: ex2, symlinkSync: sl2, mkdirSync: mk2, unlinkSync: ul2 } = await import("node:fs");
1562
- const { dirname: dn2 } = await import("node:path");
1563
- await activate(paths.current, staged.version);
1564
- process.stdout.write(`\u2713 current -> releases/${staged.version}
1565
- `);
1566
- const globalModules = join10(dn2(process.execPath), "..", "lib", "node_modules");
1567
- const abmindHome = process.env["ABMIND_HOME"] ?? join10(process.env["HOME"] ?? "", ".abmind");
1568
- const links = [];
1569
- const globalAbmind = join10(globalModules, "abmind");
1570
- const homeAbmind = join10(process.env["HOME"] ?? "", "abmind");
1571
- const localAbmind = join10(paths.home, "current", "node_modules", "abmind");
1572
- if (ex2(globalAbmind)) links.push({ name: "abmind", target: globalAbmind });
1573
- else if (ex2(homeAbmind)) links.push({ name: "abmind", target: homeAbmind });
1574
- else if (ex2(localAbmind)) links.push({ name: "abmind", target: localAbmind });
1575
- const bsq3 = join10(abmindHome, "lib", "node_modules", "better-sqlite3");
1576
- if (ex2(bsq3)) links.push({ name: "better-sqlite3", target: bsq3 });
1577
- if (links.length > 0) {
1578
- const bundleNm = join10(paths.home, "current", "bundle", "node_modules");
1579
- const rootNm = join10(paths.home, "current", "node_modules");
1580
- for (const dir of [bundleNm, rootNm]) {
1581
- mk2(dir, { recursive: true });
1582
- for (const { name, target } of links) {
1583
- const dest = join10(dir, name);
1584
- try {
1585
- if (ex2(dest)) ul2(dest);
1586
- } catch {
1587
- }
1588
- try {
1589
- sl2(target, dest);
1590
- } catch {
1591
- }
1592
- }
1593
- }
1570
+ await copyAbmind(staged.stagedPath, repoRoot);
1571
+ const entryPoint = join10(staged.stagedPath, "bundle", "abtars.js");
1572
+ if (!existsSync8(entryPoint)) {
1573
+ process.stderr.write(`\u274C Entry point not found: ${entryPoint}
1574
+ `);
1575
+ return 1;
1594
1576
  }
1577
+ configSnapshot(paths.config);
1578
+ process.stdout.write(`\u2713 config snapshot (3-slot rotation)
1579
+ `);
1595
1580
  const prior = await readManifest(paths.manifest);
1596
- const now = (/* @__PURE__ */ new Date()).toISOString();
1597
- const newPriorReleases = prior?.version ? [
1598
- {
1599
- version: prior.version,
1600
- commit: prior.commit,
1601
- activatedAt: prior.activatedAt,
1602
- packageLockHash: prior.packageLockHash
1603
- },
1604
- ...prior.priorReleases ?? []
1605
- ].slice(0, RETENTION - 1) : prior?.priorReleases ?? [];
1581
+ const sentinelData = {
1582
+ version: staged.version,
1583
+ previousVersion: prior?.version ?? null,
1584
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
1585
+ status: "pending"
1586
+ };
1587
+ writeSentinel(paths.home, sentinelData);
1588
+ atomicSwap(paths.app, paths.appPrev, paths.appStaging);
1589
+ process.stdout.write(`\u2713 atomic swap: app.staging/ \u2192 app/
1590
+ `);
1591
+ await postSwapHousekeeping(paths, repoRoot, staged);
1606
1592
  await writeManifest(paths.manifest, {
1607
- ...prior ?? emptyManifest("abtars", hostname2()),
1593
+ ...prior ?? emptyManifest("abtars", hostname()),
1608
1594
  version: staged.version,
1609
1595
  commit: staged.commit,
1610
1596
  branch: staged.branch,
1611
1597
  packageLockHash: staged.packageLockHash,
1612
- activatedAt: now,
1598
+ activatedAt: (/* @__PURE__ */ new Date()).toISOString(),
1613
1599
  source: "local",
1614
- priorReleases: newPriorReleases
1600
+ previousVersion: prior?.version ?? null,
1601
+ previousCommit: prior?.commit ?? null,
1602
+ installMode: prior?.installMode ?? "supervised",
1603
+ repoRoot: opts.fromLocal && existsSync8(join10(repoRoot, ".git")) ? repoRoot : prior?.repoRoot ?? null
1615
1604
  });
1616
1605
  process.stdout.write(`\u2713 manifest updated
1617
1606
  `);
1618
- const pruned = await pruneReleases(
1619
- paths.releases,
1620
- [staged.version, ...newPriorReleases.map((r) => r.version)],
1621
- staged.version,
1622
- RETENTION
1623
- );
1624
- if (pruned.length > 0) {
1625
- process.stdout.write(`\u2713 pruned ${pruned.length} old release${pruned.length === 1 ? "" : "s"}: ${pruned.join(", ")}
1607
+ const restartTimestamp = Date.now();
1608
+ const restarted = await restartBridge(paths);
1609
+ if (!restarted) {
1610
+ process.stdout.write(`\u26A0\uFE0F Could not restart bridge. Start manually.
1626
1611
  `);
1612
+ return 0;
1627
1613
  }
1628
- process.stdout.write(`
1629
- Update complete: ${staged.version}
1614
+ process.stdout.write(`Waiting for bridge health...
1630
1615
  `);
1631
- const transportJson = join10(paths.home, "config", "transport.json");
1632
- if (existsSync7(transportJson)) {
1633
- try {
1634
- const tc = JSON.parse(readFileSync5(transportJson, "utf-8"));
1635
- let cleared = false;
1636
- for (const agent of Object.values(tc.agents ?? {})) {
1637
- if (agent.demoted) {
1638
- delete agent.demoted;
1639
- delete agent.demotedReason;
1640
- delete agent.demotedModel;
1641
- cleared = true;
1642
- }
1643
- for (const fb of agent.fallbacks ?? []) {
1644
- if (fb.demoted) {
1645
- delete fb.demoted;
1646
- delete fb.demotedReason;
1647
- delete fb.demotedModel;
1648
- cleared = true;
1649
- }
1650
- }
1651
- }
1652
- if (cleared) {
1653
- const { writeFileSync: wfs } = await import("node:fs");
1654
- wfs(transportJson, JSON.stringify(tc, null, 2) + "\n");
1655
- }
1656
- } catch {
1657
- }
1658
- }
1659
- const { loadManifest } = await import("./install-manifest-QRWID3KZ.js");
1660
- const sourceRoot = staged.stagedPath;
1661
- const installManifest = loadManifest(sourceRoot);
1662
- const repoScripts = join10(sourceRoot, "scripts");
1663
- const destScripts = join10(paths.home, "scripts");
1664
- await mkdir4(destScripts, { recursive: true });
1665
- const allScriptFiles = await readdir(repoScripts).catch(() => []);
1666
- const matchesInclude = (name) => installManifest.scripts.include.some((pattern) => {
1667
- const ext = pattern.replace("*", "");
1668
- return name.endsWith(ext);
1669
- });
1670
- const scriptFiles = allScriptFiles.filter(matchesInclude);
1671
- const home = process.env["HOME"] ?? "";
1672
- let serviceChanged = false;
1673
- const installMode = (await readManifest(paths.manifest))?.installMode ?? "supervised";
1674
- const isExecutable = (name) => {
1675
- const ext = installManifest.scripts.executable.replace("*", "");
1676
- return name.endsWith(ext);
1677
- };
1678
- for (const name of scriptFiles) {
1679
- await copyFile2(join10(repoScripts, name), join10(destScripts, name));
1680
- if (isExecutable(name)) await chmod(join10(destScripts, name), 493);
1681
- if (isExecutable(name)) {
1682
- await copyFile2(join10(repoScripts, name), join10(paths.home, name));
1683
- await chmod(join10(paths.home, name), 493);
1684
- }
1685
- const macService = installManifest.services.supervised.macos;
1686
- if (macService && name === macService.plist && process.platform === "darwin" && home && installMode === "supervised") {
1687
- const launchAgentsDir = join10(home, "Library", "LaunchAgents");
1688
- await mkdir4(launchAgentsDir, { recursive: true });
1689
- const dst = join10(launchAgentsDir, name);
1690
- const oldContent = await readFile3(dst, "utf-8").catch(() => "");
1691
- let templated = await readFile3(join10(repoScripts, name), "utf-8");
1692
- for (const ph of macService.placeholders) templated = templated.replaceAll(ph, home);
1693
- await writeFile3(dst, templated);
1694
- if (oldContent !== templated) serviceChanged = true;
1695
- }
1696
- const linuxService = installManifest.services.supervised.linux;
1697
- if (linuxService?.units.includes(name) && process.platform === "linux" && home && installMode === "supervised") {
1698
- const systemdDir = join10(home, ".config", "systemd", "user");
1699
- await mkdir4(systemdDir, { recursive: true });
1700
- const dst = join10(systemdDir, name);
1701
- const oldContent = await readFile3(dst, "utf-8").catch(() => "");
1702
- await copyFile2(join10(repoScripts, name), dst);
1703
- const newContent = await readFile3(dst, "utf-8").catch(() => "");
1704
- if (oldContent !== newContent) serviceChanged = true;
1705
- }
1616
+ const health = await healthProbe(paths.home, restartTimestamp, 6e4);
1617
+ if (health.healthy) {
1618
+ writeSentinel(paths.home, { ...sentinelData, status: "success" });
1619
+ process.stdout.write(`\u2713 Bridge healthy (PID ${health.pid}, tick at ${new Date(health.heartbeat).toISOString()})
1620
+ `);
1621
+ return 0;
1706
1622
  }
1707
- process.stdout.write(`\u2713 scripts refreshed (${scriptFiles.length} files)
1623
+ process.stderr.write(`\u274C Bridge unhealthy after 60s. Auto-rolling back...
1624
+ `);
1625
+ if (!existsSync8(paths.appPrev1)) {
1626
+ process.stderr.write(`\u274C No app.prev.1/ to roll back to. Manual intervention required.
1708
1627
  `);
1709
- const { writeWrapper } = await import("./install-RSLOS3Q5.js");
1710
- await mkdir4(paths.bin, { recursive: true });
1711
- for (const name of installManifest.cliWrappers) {
1712
- await writeWrapper(paths.bin, name, paths.current, false);
1628
+ process.stderr.write(` Check: ~/.abtars/logs/bridge.log
1629
+ `);
1630
+ return 2;
1631
+ }
1632
+ const brokenDir = join10(paths.home, "app.broken");
1633
+ rmSync2(brokenDir, { recursive: true, force: true });
1634
+ const { renameSync: renameSync3 } = await import("node:fs");
1635
+ renameSync3(paths.app, brokenDir);
1636
+ renameSync3(paths.appPrev1, paths.app);
1637
+ if (prior) {
1638
+ await writeManifest(paths.manifest, prior);
1713
1639
  }
1714
- process.stdout.write(`\u2713 wrappers refreshed (${installManifest.cliWrappers.length} files)
1715
- `);
1716
- const { rmSync: rmSync2, cpSync, readdirSync: readdirSync3 } = await import("node:fs");
1717
- const skillsCoreSrc = join10(staged.stagedPath, "core", "skills");
1718
- const skillsCoreDst = join10(paths.home, "skills", "core");
1719
- if (existsSync7(skillsCoreSrc)) {
1720
- rmSync2(skillsCoreDst, { recursive: true, force: true });
1721
- cpSync(skillsCoreSrc, skillsCoreDst, { recursive: true });
1722
- const files = readdirSync3(skillsCoreDst, { recursive: true });
1723
- const count = files.filter((f) => f.endsWith("SKILL.md")).length;
1724
- process.stdout.write(`\u2713 skills/core synced (${count} skills)
1640
+ const rollbackTs = Date.now();
1641
+ await restartBridge(paths);
1642
+ const rollbackHealth = await healthProbe(paths.home, rollbackTs, 3e4);
1643
+ if (rollbackHealth.healthy) {
1644
+ process.stderr.write(`\u26A0\uFE0F Rolled back to previous version. Investigate ${brokenDir} for the failure.
1725
1645
  `);
1646
+ rmSync2(brokenDir, { recursive: true, force: true });
1647
+ return 1;
1726
1648
  }
1727
- for (const d of ["custom", "downloaded", "self"]) {
1728
- await mkdir4(join10(paths.home, "skills", d), { recursive: true });
1649
+ process.stderr.write(`\u274C Rollback also failed. Manual intervention required.
1650
+ `);
1651
+ process.stderr.write(` Check: ~/.abtars/logs/bridge.log
1652
+ `);
1653
+ process.stderr.write(` Broken version preserved at: ${brokenDir}
1654
+ `);
1655
+ return 2;
1656
+ } finally {
1657
+ await release();
1658
+ }
1659
+ }
1660
+ async function copyAbmind(stagedPath, repoRoot) {
1661
+ const candidates = [
1662
+ process.env["ABMIND_REPO"],
1663
+ join10(repoRoot, "..", "abmind"),
1664
+ join10(process.env["HOME"] ?? "", "abmind")
1665
+ ].filter(Boolean);
1666
+ for (const src of candidates) {
1667
+ const distDir = join10(src, "dist");
1668
+ if (existsSync8(distDir)) {
1669
+ const dest = join10(stagedPath, "node_modules", "abmind");
1670
+ mkdirSync2(dest, { recursive: true });
1671
+ cpSync(distDir, join10(dest, "dist"), { recursive: true });
1672
+ if (existsSync8(join10(src, "package.json"))) cpSync(join10(src, "package.json"), join10(dest, "package.json"));
1673
+ if (existsSync8(join10(src, "prompts"))) cpSync(join10(src, "prompts"), join10(dest, "prompts"), { recursive: true });
1674
+ process.stdout.write(`\u2713 abmind copied from ${src}
1675
+ `);
1676
+ return;
1729
1677
  }
1730
- for (const stale of ["memory", "ops", "tools", "coding"]) {
1731
- const p = join10(paths.home, "skills", stale);
1732
- if (existsSync7(p)) {
1733
- rmSync2(p, { recursive: true, force: true });
1678
+ }
1679
+ process.stdout.write(`\u26A0 abmind source not found (checked: ${candidates.join(", ")}). Bridge may fail to start.
1680
+ `);
1681
+ }
1682
+ async function postSwapHousekeeping(paths, repoRoot, _staged) {
1683
+ const { loadManifest } = await import("./install-manifest-QRWID3KZ.js");
1684
+ const installManifest = loadManifest(paths.app);
1685
+ const repoScripts = join10(repoRoot, "scripts");
1686
+ const destScripts = join10(paths.home, "scripts");
1687
+ await mkdir4(destScripts, { recursive: true });
1688
+ const allScriptFiles = await readdir(repoScripts).catch(() => []);
1689
+ const matchesInclude = (name) => installManifest.scripts.include.some((pattern) => name.endsWith(pattern.replace("*", "")));
1690
+ const scriptFiles = allScriptFiles.filter(matchesInclude);
1691
+ const isExecutable = (name) => name.endsWith(installManifest.scripts.executable.replace("*", ""));
1692
+ for (const name of scriptFiles) {
1693
+ await copyFile2(join10(repoScripts, name), join10(destScripts, name));
1694
+ if (isExecutable(name)) await chmod(join10(destScripts, name), 493);
1695
+ }
1696
+ process.stdout.write(`\u2713 scripts refreshed (${scriptFiles.length} files)
1697
+ `);
1698
+ const { writeWrapper } = await import("./install-6H3APAIJ.js");
1699
+ await mkdir4(paths.bin, { recursive: true });
1700
+ for (const name of installManifest.cliWrappers) {
1701
+ await writeWrapper(paths.bin, name, paths.app, false);
1702
+ }
1703
+ process.stdout.write(`\u2713 wrappers refreshed (${installManifest.cliWrappers.length} files)
1704
+ `);
1705
+ const skillsCoreSrc = join10(paths.app, "core", "skills");
1706
+ const skillsCoreDst = join10(paths.home, "skills", "core");
1707
+ if (existsSync8(skillsCoreSrc)) {
1708
+ rmSync2(skillsCoreDst, { recursive: true, force: true });
1709
+ cpSync(skillsCoreSrc, skillsCoreDst, { recursive: true });
1710
+ const files = readdirSync3(skillsCoreDst, { recursive: true });
1711
+ const count = files.filter((f) => f.endsWith("SKILL.md")).length;
1712
+ process.stdout.write(`\u2713 skills/core synced (${count} skills)
1713
+ `);
1714
+ }
1715
+ for (const d of ["custom", "downloaded", "self"]) {
1716
+ await mkdir4(join10(paths.home, "skills", d), { recursive: true });
1717
+ }
1718
+ const releaseConfig = join10(paths.app, "config");
1719
+ const destConfig = join10(paths.home, "config");
1720
+ if (existsSync8(releaseConfig)) {
1721
+ for (const f of readdirSync3(releaseConfig)) {
1722
+ const src = join10(releaseConfig, f);
1723
+ if (f.endsWith(".example")) {
1724
+ cpSync(src, join10(destConfig, f));
1725
+ const target = join10(destConfig, f.replace(".example", ""));
1726
+ if (!existsSync8(target)) cpSync(src, target);
1734
1727
  }
1735
1728
  }
1736
- const releaseConfig = join10(staged.stagedPath, "config");
1737
- const destConfig = join10(paths.home, "config");
1738
- if (existsSync7(releaseConfig)) {
1739
- for (const f of readdirSync3(releaseConfig)) {
1740
- const src = join10(releaseConfig, f);
1741
- if (f.endsWith(".example")) {
1742
- cpSync(src, join10(destConfig, f));
1743
- const target = join10(destConfig, f.replace(".example", ""));
1744
- if (!existsSync7(target)) cpSync(src, target);
1729
+ const defaultTransport = join10(releaseConfig, "transport.default.json");
1730
+ if (existsSync8(defaultTransport)) cpSync(defaultTransport, join10(destConfig, "transport.default.json"));
1731
+ }
1732
+ const transportJson = join10(paths.home, "config", "transport.json");
1733
+ if (existsSync8(transportJson)) {
1734
+ try {
1735
+ const tc = JSON.parse(readFileSync6(transportJson, "utf-8"));
1736
+ let cleared = false;
1737
+ for (const agent of Object.values(tc.agents ?? {})) {
1738
+ if (agent.demoted) {
1739
+ delete agent.demoted;
1740
+ delete agent.demotedReason;
1741
+ delete agent.demotedModel;
1742
+ cleared = true;
1743
+ }
1744
+ for (const fb of agent.fallbacks ?? []) {
1745
+ if (fb.demoted) {
1746
+ delete fb.demoted;
1747
+ delete fb.demotedReason;
1748
+ delete fb.demotedModel;
1749
+ cleared = true;
1750
+ }
1745
1751
  }
1746
1752
  }
1747
- const defaultTransport = join10(releaseConfig, "transport.default.json");
1748
- if (existsSync7(defaultTransport)) cpSync(defaultTransport, join10(destConfig, "transport.default.json"));
1749
- }
1750
- const coreSrc = join10(staged.stagedPath, "core");
1751
- const coreDst = join10(paths.home, "core");
1752
- if (existsSync7(coreSrc)) {
1753
- await mkdir4(coreDst, { recursive: true });
1754
- cpSync(coreSrc, coreDst, { recursive: true });
1755
- }
1756
- if (serviceChanged) {
1757
- if (process.platform === "darwin") {
1758
- process.stdout.write(`\u26A0\uFE0F LaunchAgent plist updated \u2014 reload with: launchctl bootout gui/$(id -u)/com.abtars.watchdog && launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.abtars.watchdog.plist
1759
- `);
1760
- } else {
1761
- process.stdout.write(`\u26A0\uFE0F systemd service updated \u2014 reload with: systemctl --user daemon-reload && systemctl --user restart abtars-watchdog
1762
- `);
1753
+ if (cleared) {
1754
+ const { writeFileSync: writeFileSync3 } = await import("node:fs");
1755
+ writeFileSync3(transportJson, JSON.stringify(tc, null, 2) + "\n");
1763
1756
  }
1757
+ } catch {
1764
1758
  }
1765
- void hashFile;
1766
- const { ensureInstallInvariants } = await import("./ensure-invariants-BJIEOSJ2.js");
1767
- const invariantResults = await ensureInstallInvariants(process.cwd(), paths.home);
1768
- if (invariantResults.length > 0) {
1769
- process.stdout.write(`\u2713 invariants: ${invariantResults.join(", ")}
1759
+ }
1760
+ const doctorPath = join10(paths.home, "scripts", "doctor.sh");
1761
+ if (existsSync8(doctorPath)) {
1762
+ process.stdout.write("\n\u{1FA7A} Health check...\n");
1763
+ try {
1764
+ const { execSync } = await import("node:child_process");
1765
+ execSync(`bash "${doctorPath}" --fix`, { stdio: "inherit", timeout: 3e4 });
1766
+ } catch (err) {
1767
+ process.stderr.write(`\u26A0\uFE0F doctor --fix failed: ${err instanceof Error ? err.message : String(err)}
1770
1768
  `);
1771
1769
  }
1772
- const doctorPath = join10(paths.home, "scripts", "doctor.sh");
1773
- if (existsSync7(doctorPath)) {
1774
- process.stdout.write("\n\u{1FA7A} Health check...\n");
1770
+ }
1771
+ const { ensureInstallInvariants } = await import("./ensure-invariants-BJIEOSJ2.js");
1772
+ const invariantResults = await ensureInstallInvariants(process.cwd(), paths.home);
1773
+ if (invariantResults.length > 0) {
1774
+ process.stdout.write(`\u2713 invariants: ${invariantResults.join(", ")}
1775
+ `);
1776
+ }
1777
+ void hashFile;
1778
+ }
1779
+ async function restartBridge(paths) {
1780
+ const manifest = await readManifest(paths.manifest);
1781
+ const mode = manifest?.installMode;
1782
+ if (!mode) {
1783
+ process.stderr.write("\u274C installMode not set in manifest.json. Run 'abtars install' first.\n");
1784
+ return false;
1785
+ }
1786
+ if (mode === "supervised-daemon" || mode === "supervised") {
1787
+ process.stdout.write("\n\u267B\uFE0F Restarting bridge via watchdog...\n");
1788
+ const wdLock = join10(paths.home, "watchdog.lock");
1789
+ const wdPid = readJsonField2(wdLock, "pid");
1790
+ if (wdPid && wdPid > 0) {
1775
1791
  try {
1776
- const { execSync } = await import("node:child_process");
1777
- execSync(`bash "${doctorPath}" --fix`, { stdio: "inherit", timeout: 3e4 });
1778
- } catch (err) {
1779
- process.stderr.write(`\u26A0\uFE0F doctor --fix failed: ${err instanceof Error ? err.message : String(err)}
1792
+ process.kill(wdPid, "SIGUSR1");
1793
+ process.stdout.write(` USR1 sent to watchdog (PID ${wdPid})
1794
+ `);
1795
+ return true;
1796
+ } catch {
1797
+ process.stdout.write(`\u26A0\uFE0F Could not signal watchdog (PID ${wdPid}).
1780
1798
  `);
1781
1799
  }
1782
1800
  }
1783
- const manifestForRestart = await readManifest(paths.manifest);
1784
- const restartMode = manifestForRestart?.installMode;
1785
- if (!restartMode) {
1786
- process.stderr.write("\u274C installMode not set in manifest.json. Run 'abtars install' first.\n");
1787
- return 1;
1788
- }
1789
- if (restartMode === "supervised-daemon" || restartMode === "supervised") {
1790
- process.stdout.write("\nRestarting bridge via watchdog...\n");
1791
- const wdLock = join10(paths.home, "watchdog.lock");
1792
- const wdPid = readJsonField2(wdLock, "pid");
1793
- if (wdPid && wdPid > 0) {
1794
- try {
1795
- process.kill(wdPid, "SIGUSR1");
1796
- process.stdout.write(`\u267B\uFE0F USR1 sent to watchdog (PID ${wdPid}) \u2014 bridge will restart
1801
+ const { restart: restart3 } = await import("./abtars-restart.js");
1802
+ await restart3({ cold: true }).catch((err) => {
1803
+ process.stderr.write(`\u26A0\uFE0F Restart failed: ${err instanceof Error ? err.message : String(err)}
1797
1804
  `);
1798
- } catch {
1799
- process.stdout.write(`\u26A0\uFE0F Could not signal watchdog (PID ${wdPid}). Restart manually:
1800
- `);
1801
- if (process.platform === "darwin") {
1802
- process.stdout.write(` launchctl kickstart -k gui/$(id -u)/com.abtars.watchdog
1803
- `);
1804
- } else {
1805
- process.stdout.write(` systemctl --user restart abtars-watchdog
1805
+ });
1806
+ return true;
1807
+ }
1808
+ process.stdout.write("\n\u267B\uFE0F Restarting bridge...\n");
1809
+ const { restart: restart2 } = await import("./abtars-restart.js");
1810
+ await restart2({ cold: true }).catch((err) => {
1811
+ process.stderr.write(`\u26A0\uFE0F Restart failed: ${err instanceof Error ? err.message : String(err)}
1806
1812
  `);
1807
- }
1808
- }
1809
- } else {
1810
- process.stdout.write(`\u26A0\uFE0F Watchdog not running. Cold restart...
1813
+ });
1814
+ return true;
1815
+ }
1816
+ function printDryRun(paths, repoRoot, opts) {
1817
+ const { spawnSync: spawnSync4 } = __require("node:child_process");
1818
+ const commit = spawnSync4("git", ["rev-parse", "--short", "HEAD"], { cwd: repoRoot, encoding: "utf-8" }).stdout?.trim() ?? "?";
1819
+ const branch = spawnSync4("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: repoRoot, encoding: "utf-8" }).stdout?.trim() ?? "?";
1820
+ const wdLock = join10(paths.home, "watchdog.lock");
1821
+ const wdPid = readJsonField2(wdLock, "pid");
1822
+ process.stdout.write(`
1823
+ Dry run \u2014 no changes will be made.
1824
+ Source: ${opts.source} (commit ${commit} on ${branch})
1825
+ Staging to: ${paths.appStaging}
1826
+ Swap: app/ \u2192 app.prev/, app.staging/ \u2192 app/
1827
+ Config snap: config/ \u2192 config/.pre-update/ (3-slot rotation)
1828
+ Restart: ${wdPid ? `USR1 to watchdog (PID ${wdPid})` : "cold restart"}
1829
+ Health: poll bridge.lock for 60s
1830
+ On failure: auto-rollback (swap app/ \u2194 app.prev/)
1811
1831
  `);
1812
- const { restart: restart2 } = await import("./abtars-restart.js");
1813
- await restart2({ cold: true }).catch((err) => {
1814
- process.stderr.write(`\u26A0\uFE0F Restart failed: ${err instanceof Error ? err.message : String(err)}
1832
+ return 0;
1833
+ }
1834
+ async function checkForUpdates(home, opts) {
1835
+ const repoRoot = opts.repoRoot ?? process.cwd();
1836
+ if (!existsSync8(join10(repoRoot, ".git"))) {
1837
+ process.stderr.write("Not a git repository. --check requires a git checkout.\n");
1838
+ return 2;
1839
+ }
1840
+ const { spawnSync: spawnSync4 } = await import("node:child_process");
1841
+ spawnSync4("git", ["fetch", "--quiet"], { cwd: repoRoot });
1842
+ const result = spawnSync4("git", ["rev-list", "--count", "HEAD..origin/dev"], { cwd: repoRoot, encoding: "utf-8" });
1843
+ const ahead = parseInt(result.stdout?.trim() ?? "0", 10);
1844
+ const manifest = await readManifest(join10(home, "manifest.json"));
1845
+ process.stdout.write(`Current: ${manifest?.version ?? "unknown"} (deployed ${manifest?.activatedAt ?? "never"})
1815
1846
  `);
1816
- });
1817
- }
1818
- } else {
1819
- process.stdout.write("\nRestarting bridge...\n");
1820
- const { restart: restart2 } = await import("./abtars-restart.js");
1821
- await restart2({ cold: true }).catch((err) => {
1822
- process.stderr.write(`\u26A0\uFE0F Restart failed: ${err instanceof Error ? err.message : String(err)}
1847
+ if (ahead === 0) {
1848
+ process.stdout.write(`Remote: up to date
1823
1849
  `);
1824
- });
1825
- }
1826
- const { printHealthSummary } = await import("./health-check-RJ2SUJYL.js");
1827
- printHealthSummary(paths.home);
1828
1850
  return 0;
1829
- } finally {
1830
- await release();
1831
1851
  }
1852
+ process.stdout.write(`Remote: dev is ${ahead} commit${ahead === 1 ? "" : "s"} ahead
1853
+ `);
1854
+ process.stdout.write(`Action: run 'abtars update' to apply
1855
+ `);
1856
+ return 2;
1832
1857
  }
1833
1858
 
1834
1859
  // src/cli/abtars.ts
@@ -1865,8 +1890,8 @@ Usage:
1865
1890
  abtars uninstall [--yes]
1866
1891
  abtars update [--source local|npm|github] [--from-local]
1867
1892
  abtars rollback [--to <version>]
1868
- abtars backup
1869
- abtars restore <file.zip|.7z>
1893
+ abtars backup [--config] [--encrypt] [--output <dir>] [--prune-days N]
1894
+ abtars restore <file.zip|.7z|.abm|.enc> [--config] [--passphrase <p>]
1870
1895
  abtars doctor [<args passed to doctor.sh>...]
1871
1896
  abtars onboard [--non-interactive --accept-risk --telegram-token ... --telegram-chat-id ...]
1872
1897
  abtars restart [--cold]
@@ -1879,6 +1904,20 @@ Usage:
1879
1904
  );
1880
1905
  }
1881
1906
  async function main(argv) {
1907
+ if (argv[0] === "--version" || argv[0] === "-v" || argv[0] === "version") {
1908
+ const { readFileSync: readFileSync7 } = await import("node:fs");
1909
+ const { join: join11, dirname: dirname3 } = await import("node:path");
1910
+ const { fileURLToPath: fileURLToPath2 } = await import("node:url");
1911
+ const here = dirname3(fileURLToPath2(import.meta.url));
1912
+ try {
1913
+ const pkg = JSON.parse(readFileSync7(join11(here, "..", "package.json"), "utf-8"));
1914
+ process.stdout.write(`${pkg.version}
1915
+ `);
1916
+ } catch {
1917
+ process.stdout.write("unknown\n");
1918
+ }
1919
+ return 0;
1920
+ }
1882
1921
  const { command, flags } = parseArgs(argv);
1883
1922
  try {
1884
1923
  switch (command) {
@@ -1898,14 +1937,20 @@ async function main(argv) {
1898
1937
  allowAbmindMismatch: flags.get("allow-abmind-mismatch") === true
1899
1938
  });
1900
1939
  case "rollback":
1901
- return await rollback({
1902
- to: typeof flags.get("to") === "string" ? flags.get("to") : void 0
1903
- });
1940
+ return await rollback({ to: typeof flags.get("to") === "string" ? Number(flags.get("to")) : void 0 });
1904
1941
  case "backup":
1905
- return await backup(typeof flags.get("output") === "string" ? flags.get("output") : void 0);
1942
+ return await backup({
1943
+ config: flags.get("config") === true,
1944
+ encrypt: flags.get("encrypt") === true,
1945
+ outputDir: typeof flags.get("output") === "string" ? flags.get("output") : void 0,
1946
+ pruneDays: typeof flags.get("prune-days") === "string" ? Number(flags.get("prune-days")) : void 0
1947
+ });
1906
1948
  case "restore": {
1907
- const { restore } = await import("./restore-ROJF22R2.js");
1908
- return await restore(argv[1] ?? "");
1949
+ const { restore } = await import("./restore-UH2UE2K5.js");
1950
+ return await restore(argv[1] ?? "", {
1951
+ config: flags.get("config") === true,
1952
+ passphrase: typeof flags.get("passphrase") === "string" ? flags.get("passphrase") : void 0
1953
+ });
1909
1954
  }
1910
1955
  case "doctor":
1911
1956
  return await doctor(argv.slice(1).filter((a) => a !== ""));
@@ -1918,6 +1963,7 @@ async function main(argv) {
1918
1963
  defaultProvider: typeof flags.get("default-provider") === "string" ? flags.get("default-provider") : void 0,
1919
1964
  defaultModel: typeof flags.get("default-model") === "string" ? flags.get("default-model") : void 0,
1920
1965
  discordA2aChannel: typeof flags.get("discord-a2a-channel") === "string" ? flags.get("discord-a2a-channel") : void 0,
1966
+ userName: typeof flags.get("user-name") === "string" ? flags.get("user-name") : void 0,
1921
1967
  force: flags.get("force") === true
1922
1968
  });
1923
1969
  case "status":
@@ -1927,7 +1973,7 @@ async function main(argv) {
1927
1973
  case "stop":
1928
1974
  return await stop({ force: flags.get("force") === true });
1929
1975
  case "start": {
1930
- const { start: startCmd } = await import("./start-4IWBKLWO.js");
1976
+ const { start: startCmd } = await import("./start-CBVKNEAT.js");
1931
1977
  return await startCmd();
1932
1978
  }
1933
1979
  case "daemon": {
@@ -1946,10 +1992,6 @@ async function main(argv) {
1946
1992
  const { configShow } = await import("./config-show-ZTXX27FW.js");
1947
1993
  return await configShow();
1948
1994
  }
1949
- case "passwd": {
1950
- const { passwd } = await import("./passwd-QSHZJ2CG.js");
1951
- return await passwd();
1952
- }
1953
1995
  case "":
1954
1996
  case "help":
1955
1997
  case "--help":