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.
- package/README.md +15 -18
- package/bundle/{_registry.generated-6GO2PAMD.js → _registry.generated-4P6KTBTH.js} +8 -8
- package/bundle/_registry.generated-G2FSGHNY.js +36 -0
- package/bundle/_registry.generated-G2FSGHNY.js.map +7 -0
- package/bundle/_registry.generated-N74SMTUJ.js +36 -0
- package/bundle/_registry.generated-N74SMTUJ.js.map +7 -0
- package/bundle/_registry.generated-Y4VT5MBX.js +36 -0
- package/bundle/_registry.generated-Y4VT5MBX.js.map +7 -0
- package/bundle/abtars-browser.js +0 -1
- package/bundle/abtars-browser.js.map +2 -2
- package/bundle/abtars-cli.js +589 -547
- package/bundle/abtars-cli.js.map +4 -4
- package/bundle/abtars-restart.js +1 -1
- package/bundle/abtars-rss.js +0 -1
- package/bundle/abtars-rss.js.map +2 -2
- package/bundle/abtars-task.js +1 -2
- package/bundle/abtars-task.js.map +2 -2
- package/bundle/abtars.js +58 -33
- package/bundle/abtars.js.map +2 -2
- package/bundle/{agent-registry-SYUFNSVB.js → agent-registry-TM7EISOV.js} +2 -2
- package/bundle/{browse-delivery-64GQIUHG.js → browse-delivery-VTLEAVYA.js} +4 -4
- package/bundle/{browser-5H2Q7GEC.js → browser-JQGYFZAG.js} +5 -5
- package/bundle/browser-M4KDLN75.js +19 -0
- package/bundle/browser-REIXOJ6S.js +19 -0
- package/bundle/browser-VG2GTEND.js +19 -0
- package/bundle/{chunk-KFENC7BM.js → chunk-4YOTNYAE.js} +2 -2
- package/bundle/{chunk-KWBGYWDO.js → chunk-7I5C2U3C.js} +1 -1
- package/bundle/chunk-7I5C2U3C.js.map +7 -0
- package/bundle/{chunk-7STAQF5O.js → chunk-CNWXVXGH.js} +4 -4
- package/bundle/{chunk-7STAQF5O.js.map → chunk-CNWXVXGH.js.map} +2 -2
- package/bundle/{chunk-JX3ZZU3O.js → chunk-CU3O53U3.js} +28 -14
- package/bundle/chunk-CU3O53U3.js.map +7 -0
- package/bundle/chunk-EZZBO2QK.js +85 -0
- package/bundle/chunk-EZZBO2QK.js.map +7 -0
- package/bundle/{chunk-XLLSPBBT.js → chunk-GPL57SRN.js} +2 -2
- package/bundle/chunk-GPL57SRN.js.map +7 -0
- package/bundle/{chunk-PQ62LZNA.js → chunk-HEHD3GG5.js} +2 -2
- package/bundle/{chunk-A5OJYQNU.js → chunk-HTGVYNPE.js} +4 -4
- package/bundle/{chunk-WUTCGJDZ.js → chunk-HZRUBJ4Q.js} +148 -9
- package/bundle/chunk-HZRUBJ4Q.js.map +7 -0
- package/bundle/{chunk-ZVDVNSXK.js → chunk-J7CFCY4U.js} +2 -2
- package/bundle/{chunk-HXJRZWKA.js → chunk-JRK3UFBS.js} +106 -58
- package/bundle/chunk-JRK3UFBS.js.map +7 -0
- package/bundle/{chunk-GZUQQWBZ.js → chunk-KJOCXWJ5.js} +3 -3
- package/bundle/chunk-KJOCXWJ5.js.map +7 -0
- package/bundle/{chunk-K7P74UNQ.js → chunk-LDKSCXGL.js} +3 -3
- package/bundle/{chunk-K7P74UNQ.js.map → chunk-LDKSCXGL.js.map} +2 -2
- package/bundle/chunk-MNKLPRGY.js +138 -0
- package/bundle/chunk-MNKLPRGY.js.map +7 -0
- package/bundle/chunk-O73EZGKR.js +135 -0
- package/bundle/chunk-O73EZGKR.js.map +7 -0
- package/bundle/{chunk-OZ4IZFV4.js → chunk-Q7F5R2LL.js} +1 -1
- package/bundle/{chunk-OZ4IZFV4.js.map → chunk-Q7F5R2LL.js.map} +2 -2
- package/bundle/{chunk-XOCP5BMO.js → chunk-URAQLQ2U.js} +2 -2
- package/bundle/chunk-URAQLQ2U.js.map +7 -0
- package/bundle/{chunk-PPSB2ZIN.js → chunk-VREI2GUO.js} +18 -7
- package/bundle/chunk-VREI2GUO.js.map +7 -0
- package/bundle/chunk-VYR25H37.js +135 -0
- package/bundle/chunk-VYR25H37.js.map +7 -0
- package/bundle/{commands-E3Y4QUNF.js → commands-J6JAZ7D2.js} +5 -5
- package/bundle/{deploy-lib-import-ODLDL2DB.js → deploy-lib-import-PJXUADPS.js} +18 -16
- package/bundle/{direct-api-transport-TRV45NO6.js → direct-api-transport-ZSJHKYBX.js} +1 -1
- package/bundle/direct-api-transport-ZSJHKYBX.js.map +7 -0
- package/bundle/{discord-adapter-5DS7UTBE.js → discord-adapter-OCWSJTJU.js} +8 -8
- package/bundle/{doctor-R54GZPKL.js → doctor-XOGVHA4W.js} +13 -2
- package/bundle/{doctor-R54GZPKL.js.map → doctor-XOGVHA4W.js.map} +2 -2
- package/bundle/{install-RSLOS3Q5.js → install-6H3APAIJ.js} +3 -3
- package/bundle/{irc-adapter-RKRUSZXB.js → irc-adapter-AIEP6OX6.js} +6 -2
- package/bundle/irc-adapter-AIEP6OX6.js.map +7 -0
- package/bundle/{message-pipeline-CFVO73CK.js → message-pipeline-TCPHRBAK.js} +5 -5
- package/bundle/meta.json +4005 -3868
- package/bundle/{phase-transport-FEZ4SIJJ.js → phase-transport-ME2XDRR2.js} +3 -3
- package/bundle/restore-UH2UE2K5.js +151 -0
- package/bundle/restore-UH2UE2K5.js.map +7 -0
- package/bundle/rollback-D7AYTC7Z.js +11 -0
- package/bundle/{sleep-BPWX3FCN.js → sleep-IOWYTSMK.js} +2 -2
- package/bundle/{soul-bundle-BRIUDEQ2.js → soul-bundle-AH5S3MVI.js} +2 -2
- package/bundle/{soul-loader-GBXJ7EBH.js → soul-loader-N5FA4I6C.js} +3 -3
- package/bundle/soul-loader-N5FA4I6C.js.map +7 -0
- package/bundle/{start-4IWBKLWO.js → start-CBVKNEAT.js} +1 -1
- package/bundle/{subagent-runtime-MDBO6LHS.js → subagent-runtime-OOJXCKJF.js} +2 -2
- package/bundle/subagent-runtime-OOJXCKJF.js.map +7 -0
- package/bundle/{system-status-WAY63BYC.js → system-status-4JP36OKC.js} +12 -5
- package/bundle/{system-status-WAY63BYC.js.map → system-status-4JP36OKC.js.map} +2 -2
- package/bundle/{task-store-KIBFZL5A.js → task-store-LC7ZMS72.js} +2 -2
- package/bundle/task-store-LC7ZMS72.js.map +7 -0
- package/bundle/{telegram-adapter-MVA33IO2.js → telegram-adapter-QXHJQLLX.js} +5 -5
- package/bundle/{update-check-O5MS6B3L.js → update-check-XVY2IK2L.js} +2 -2
- package/bundle/update-check-XVY2IK2L.js.map +7 -0
- package/core/skills/tools/rss/SKILL.md +51 -0
- package/install-manifest.json +4 -3
- package/package.json +3 -4
- package/scripts/abtars-daemon.service +1 -1
- package/scripts/abtars-watchdog.service +1 -1
- package/scripts/abtars.sh +3 -5
- package/scripts/abtars@.service +1 -1
- package/scripts/build-and-deploy.sh +6 -0
- package/scripts/com.abtars.daemon.plist +1 -1
- package/scripts/com.abtars.watchdog.plist +1 -1
- package/scripts/daily-backup.sh +1 -61
- package/scripts/doctor.sh +48 -33
- package/scripts/watchdog.sh +53 -8
- package/bundle/chunk-GZUQQWBZ.js.map +0 -7
- package/bundle/chunk-HXJRZWKA.js.map +0 -7
- package/bundle/chunk-JX3ZZU3O.js.map +0 -7
- package/bundle/chunk-KWBGYWDO.js.map +0 -7
- package/bundle/chunk-PPSB2ZIN.js.map +0 -7
- package/bundle/chunk-WUTCGJDZ.js.map +0 -7
- package/bundle/chunk-XLLSPBBT.js.map +0 -7
- package/bundle/chunk-XOCP5BMO.js.map +0 -7
- package/bundle/direct-api-transport-TRV45NO6.js.map +0 -7
- package/bundle/irc-adapter-RKRUSZXB.js.map +0 -7
- package/bundle/passwd-QSHZJ2CG.js +0 -134
- package/bundle/passwd-QSHZJ2CG.js.map +0 -7
- package/bundle/restore-ROJF22R2.js +0 -47
- package/bundle/restore-ROJF22R2.js.map +0 -7
- package/core/core_templates/SOUL.md +0 -24
- package/core/core_templates/agent_notes.md +0 -7
- package/core/core_templates/core_facts.md +0 -10
- package/core/core_templates/user_profile.md +0 -7
- package/core/skills/tools/fxtwitter/SKILL.md +0 -52
- package/core/skills/tools/twitterX/SKILL.md +0 -52
- package/core/skills/tools/twitterX/scripts/abtars-tweet.js +0 -532
- package/core/skills/tools/twitterX/scripts/package.json +0 -1
- /package/bundle/{_registry.generated-6GO2PAMD.js.map → _registry.generated-4P6KTBTH.js.map} +0 -0
- /package/bundle/{agent-registry-SYUFNSVB.js.map → agent-registry-TM7EISOV.js.map} +0 -0
- /package/bundle/{browse-delivery-64GQIUHG.js.map → browse-delivery-VTLEAVYA.js.map} +0 -0
- /package/bundle/{browser-5H2Q7GEC.js.map → browser-JQGYFZAG.js.map} +0 -0
- /package/bundle/{commands-E3Y4QUNF.js.map → browser-M4KDLN75.js.map} +0 -0
- /package/bundle/{deploy-lib-import-ODLDL2DB.js.map → browser-REIXOJ6S.js.map} +0 -0
- /package/bundle/{install-RSLOS3Q5.js.map → browser-VG2GTEND.js.map} +0 -0
- /package/bundle/{chunk-KFENC7BM.js.map → chunk-4YOTNYAE.js.map} +0 -0
- /package/bundle/{chunk-PQ62LZNA.js.map → chunk-HEHD3GG5.js.map} +0 -0
- /package/bundle/{chunk-A5OJYQNU.js.map → chunk-HTGVYNPE.js.map} +0 -0
- /package/bundle/{chunk-ZVDVNSXK.js.map → chunk-J7CFCY4U.js.map} +0 -0
- /package/bundle/{message-pipeline-CFVO73CK.js.map → commands-J6JAZ7D2.js.map} +0 -0
- /package/bundle/{phase-transport-FEZ4SIJJ.js.map → deploy-lib-import-PJXUADPS.js.map} +0 -0
- /package/bundle/{discord-adapter-5DS7UTBE.js.map → discord-adapter-OCWSJTJU.js.map} +0 -0
- /package/bundle/{sleep-BPWX3FCN.js.map → install-6H3APAIJ.js.map} +0 -0
- /package/bundle/{soul-bundle-BRIUDEQ2.js.map → message-pipeline-TCPHRBAK.js.map} +0 -0
- /package/bundle/{soul-loader-GBXJ7EBH.js.map → phase-transport-ME2XDRR2.js.map} +0 -0
- /package/bundle/{subagent-runtime-MDBO6LHS.js.map → rollback-D7AYTC7Z.js.map} +0 -0
- /package/bundle/{task-store-KIBFZL5A.js.map → sleep-IOWYTSMK.js.map} +0 -0
- /package/bundle/{update-check-O5MS6B3L.js.map → soul-bundle-AH5S3MVI.js.map} +0 -0
- /package/bundle/{start-4IWBKLWO.js.map → start-CBVKNEAT.js.map} +0 -0
- /package/bundle/{telegram-adapter-MVA33IO2.js.map → telegram-adapter-QXHJQLLX.js.map} +0 -0
package/bundle/abtars-cli.js
CHANGED
|
@@ -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-
|
|
5
|
+
} from "./chunk-VREI2GUO.js";
|
|
6
|
+
import {
|
|
7
|
+
rollback
|
|
8
|
+
} from "./chunk-EZZBO2QK.js";
|
|
6
9
|
import {
|
|
7
|
-
RETENTION,
|
|
8
10
|
acquireLock,
|
|
9
|
-
|
|
11
|
+
atomicSwap,
|
|
12
|
+
cleanStaleStaging,
|
|
13
|
+
configSnapshot,
|
|
10
14
|
emptyManifest,
|
|
11
15
|
hashFile,
|
|
12
|
-
|
|
16
|
+
healthProbe,
|
|
13
17
|
packagePaths,
|
|
14
|
-
pruneReleases,
|
|
15
|
-
readCurrent,
|
|
16
18
|
readManifest,
|
|
17
|
-
|
|
18
|
-
writeManifest
|
|
19
|
-
|
|
19
|
+
readSentinel,
|
|
20
|
+
writeManifest,
|
|
21
|
+
writeSentinel
|
|
22
|
+
} from "./chunk-JRK3UFBS.js";
|
|
20
23
|
import {
|
|
21
24
|
restart
|
|
22
|
-
} from "./chunk-
|
|
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,
|
|
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
|
-
|
|
310
|
+
import { createCipheriv, hkdfSync, randomBytes } from "node:crypto";
|
|
308
311
|
var DEFAULT_PRUNE_DAYS = 7;
|
|
309
|
-
|
|
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
|
|
315
|
-
|
|
316
|
-
|
|
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
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
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 (
|
|
367
|
-
const
|
|
368
|
-
|
|
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
|
-
|
|
371
|
-
|
|
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(
|
|
376
|
-
`);
|
|
411
|
+
process.stderr.write("Backup zip failed\n");
|
|
377
412
|
return 1;
|
|
378
413
|
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
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
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
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
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
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-
|
|
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-
|
|
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:
|
|
1043
|
-
const hasRelease =
|
|
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:
|
|
1064
|
-
const tasksJson = join6(abtarsHome4, "
|
|
1065
|
-
if (
|
|
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 (
|
|
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 {
|
|
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
|
-
`
|
|
1241
|
-
`
|
|
1242
|
-
`
|
|
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
|
-
|
|
1245
|
-
const
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
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
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
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
|
-
|
|
1292
|
-
import {
|
|
1293
|
-
import {
|
|
1294
|
-
import {
|
|
1295
|
-
import {
|
|
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
|
|
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 = !
|
|
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 (!
|
|
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 =
|
|
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 (
|
|
1407
|
+
if (existsSync6(coreDir)) await cp(coreDir, join8(stagedPath2, "core"), { recursive: true });
|
|
1388
1408
|
const scriptsSrc = join8(repoRoot, "scripts");
|
|
1389
|
-
if (
|
|
1409
|
+
if (existsSync6(scriptsSrc)) await cp(scriptsSrc, join8(stagedPath2, "scripts"), { recursive: true });
|
|
1390
1410
|
const configSrc = join8(repoRoot, "config");
|
|
1391
|
-
if (
|
|
1411
|
+
if (existsSync6(configSrc)) await cp(configSrc, join8(stagedPath2, "config"), { recursive: true });
|
|
1392
1412
|
const manifestSrc = join8(repoRoot, "install-manifest.json");
|
|
1393
|
-
if (
|
|
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
|
-
|
|
1406
|
-
|
|
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, "
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
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
|
|
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(
|
|
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 =
|
|
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 (
|
|
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(
|
|
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
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
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 && !
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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}
|
|
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
|
-
|
|
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
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
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
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
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",
|
|
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:
|
|
1598
|
+
activatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1613
1599
|
source: "local",
|
|
1614
|
-
|
|
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
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
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
|
|
1632
|
-
if (
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
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.
|
|
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
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
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
|
-
|
|
1715
|
-
|
|
1716
|
-
const
|
|
1717
|
-
|
|
1718
|
-
|
|
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
|
-
|
|
1728
|
-
|
|
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
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
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
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
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
|
-
|
|
1748
|
-
|
|
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
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
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
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
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
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
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
|
|
1784
|
-
|
|
1785
|
-
|
|
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
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
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
|
-
|
|
1810
|
-
|
|
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
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
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(
|
|
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-
|
|
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-
|
|
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":
|