abtars 0.1.0-alpha.16 → 0.1.0-alpha.17

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.
@@ -1,18 +1,17 @@
1
1
  #!/usr/bin/env node
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
- import {
4
- showHintOnce,
5
- update
6
- } from "./chunk-TQG56KI3.js";
7
3
  import {
8
4
  install
9
5
  } from "./chunk-RB3X66KM.js";
10
6
  import {
7
+ RETENTION,
11
8
  acquireLock,
12
9
  activate,
13
10
  emptyManifest,
11
+ hashFile,
14
12
  inspectLock,
15
13
  packagePaths,
14
+ pruneReleases,
16
15
  readCurrent,
17
16
  readManifest,
18
17
  releaseExists,
@@ -136,11 +135,11 @@ function removeLock(path) {
136
135
  }
137
136
  async function stop(opts) {
138
137
  const home = abtarsHome2();
139
- const manifestPath = join2(home, "manifest.json");
138
+ const manifestPath2 = join2(home, "manifest.json");
140
139
  const watchdogLock = join2(home, "watchdog.lock");
141
140
  const bridgeLock = join2(home, "bridge.lock");
142
141
  const force = opts.force ?? false;
143
- const installMode = readJsonField(manifestPath, "installMode");
142
+ const installMode = readJsonField(manifestPath2, "installMode");
144
143
  if (installMode === "supervised-daemon" && !force) {
145
144
  process.stderr.write(`Bridge runs under supervised-daemon \u2014 use supervisor stop (supervisor will respawn if you kill the process directly).
146
145
  `);
@@ -413,8 +412,45 @@ function collectDir(dir, prefix, out) {
413
412
  // src/cli/commands/onboard.ts
414
413
  init_log_and_swallow();
415
414
  import { mkdir, readFile, writeFile } from "node:fs/promises";
416
- import { dirname as dirname2, join as join5 } from "node:path";
415
+ import { dirname as dirname2, join as join6 } from "node:path";
417
416
  import { fileURLToPath } from "node:url";
417
+
418
+ // src/components/hints.ts
419
+ import { existsSync as existsSync4, readFileSync as readFileSync2, writeFileSync, renameSync as renameSync2 } from "node:fs";
420
+ import { join as join5 } from "node:path";
421
+ function manifestPath() {
422
+ const home = process.env["ABTARS_HOME"] ?? join5(process.env["HOME"] ?? "", ".abtars");
423
+ return join5(home, "manifest.json");
424
+ }
425
+ function readManifest2() {
426
+ const path = manifestPath();
427
+ if (!existsSync4(path)) return {};
428
+ try {
429
+ return JSON.parse(readFileSync2(path, "utf-8"));
430
+ } catch {
431
+ return {};
432
+ }
433
+ }
434
+ function writeManifestAtomic(data) {
435
+ const path = manifestPath();
436
+ const tmp = path + ".tmp";
437
+ writeFileSync(tmp, JSON.stringify(data, null, 2) + "\n");
438
+ renameSync2(tmp, path);
439
+ }
440
+ function showHintOnce(id, text) {
441
+ const manifest = readManifest2();
442
+ const seen = manifest["hintsSeen"] ?? {};
443
+ if (seen[id]) return;
444
+ process.stderr.write(`
445
+ \u{1F4A1} ${text}
446
+
447
+ `);
448
+ seen[id] = (/* @__PURE__ */ new Date()).toISOString();
449
+ manifest["hintsSeen"] = seen;
450
+ writeManifestAtomic(manifest);
451
+ }
452
+
453
+ // src/cli/commands/onboard.ts
418
454
  var VALID_PROVIDERS = ["openrouter", "anthropic", "openai", "ollama", "kiro", "gemini"];
419
455
  var PROVIDER_TRANSPORT_NAME = {
420
456
  openrouter: "openrouter",
@@ -451,9 +487,9 @@ async function runInteractive(existing) {
451
487
  const { intro, outro, text, select, confirm: confirm2, isCancel, cancel } = await import("./dist-J3T4XVKX.js");
452
488
  intro("abtars onboard \u2014 first-time setup");
453
489
  const noteEmpty = "press Enter to skip";
454
- const { packagePaths: pp, readManifest: rm } = await import("./deploy-lib-import-ODLDL2DB.js");
490
+ const { packagePaths: pp, readManifest: rm3 } = await import("./deploy-lib-import-ODLDL2DB.js");
455
491
  const mfPaths = pp("abtars");
456
- const mf = await rm(mfPaths.manifest);
492
+ const mf = await rm3(mfPaths.manifest);
457
493
  const installMode = mf?.installMode ?? "supervised";
458
494
  const userName = await text({
459
495
  message: "Your name",
@@ -850,11 +886,11 @@ async function onboard(opts) {
850
886
  );
851
887
  return 2;
852
888
  }
853
- const envPath = join5(paths.config, ".env");
889
+ const envPath = join6(paths.config, ".env");
854
890
  const existing = await readExisting(envPath);
855
- const secretDir = join5(paths.home, "secret");
891
+ const secretDir = join6(paths.home, "secret");
856
892
  const { existsSync: secretExists } = await import("node:fs");
857
- const hasSecretToken = secretExists(join5(secretDir, "TELEGRAM_BOT_TOKEN")) || secretExists(join5(secretDir, "DISCORD_BOT_TOKEN"));
893
+ const hasSecretToken = secretExists(join6(secretDir, "TELEGRAM_BOT_TOKEN")) || secretExists(join6(secretDir, "DISCORD_BOT_TOKEN"));
858
894
  const hasUserConfig = existing !== null && (existing.telegramToken || existing.discordBotToken || hasSecretToken);
859
895
  if (hasUserConfig && !opts.force) {
860
896
  showHintOnce("onboard-reoffer", "Re-running onboard overwrites config. Use --force to confirm, or edit ~/.abtars/config/.env directly.");
@@ -888,7 +924,7 @@ async function onboard(opts) {
888
924
  process.stdout.write(`
889
925
  \u2713 Wrote ${envPath}
890
926
  `);
891
- const secretDirPath = join5(paths.home, "secret");
927
+ const secretDirPath = join6(paths.home, "secret");
892
928
  await mkdir(secretDirPath, { recursive: true });
893
929
  const secrets = [];
894
930
  if (answers.telegramToken) secrets.push(["TELEGRAM_BOT_TOKEN", answers.telegramToken]);
@@ -899,7 +935,7 @@ async function onboard(opts) {
899
935
  }
900
936
  if (answers.groqApiKey) secrets.push(["GROQ_API_KEY", answers.groqApiKey]);
901
937
  for (const [name, value] of secrets) {
902
- await writeFile(join5(secretDirPath, name), value, { mode: 384 });
938
+ await writeFile(join6(secretDirPath, name), value, { mode: 384 });
903
939
  }
904
940
  if (secrets.length > 0) process.stdout.write(`\u2713 ${secrets.length} secrets \u2192 ${secretDirPath}
905
941
  `);
@@ -909,7 +945,7 @@ async function onboard(opts) {
909
945
  process.stdout.write(`\u2713 install mode: ${answers.installMode}
910
946
  `);
911
947
  {
912
- const transportPath = join5(paths.config, "transport.json");
948
+ const transportPath = join6(paths.config, "transport.json");
913
949
  let tc = {};
914
950
  try {
915
951
  tc = JSON.parse(await readFile(transportPath, "utf-8"));
@@ -941,7 +977,7 @@ async function onboard(opts) {
941
977
  `);
942
978
  }
943
979
  {
944
- const usersPath = join5(paths.config, "users.json");
980
+ const usersPath = join6(paths.config, "users.json");
945
981
  const userId = answers.userName.toLowerCase().replace(/[^a-z0-9]/g, "") || "user";
946
982
  const platforms = {};
947
983
  if (answers.telegramChatId) platforms["telegram"] = parseInt(answers.telegramChatId, 10) || answers.telegramChatId;
@@ -962,11 +998,11 @@ async function onboard(opts) {
962
998
  `);
963
999
  }
964
1000
  if (answers.userName) {
965
- const abmindHome = process.env["ABMIND_HOME"] ?? join5(dirname2(paths.home), ".abmind");
966
- const profileDir = join5(abmindHome, "memory", "core");
1001
+ const abmindHome = process.env["ABMIND_HOME"] ?? join6(dirname2(paths.home), ".abmind");
1002
+ const profileDir = join6(abmindHome, "memory", "core");
967
1003
  const { existsSync: profileExists } = await import("node:fs");
968
1004
  if (profileExists(profileDir)) {
969
- const profilePath = join5(profileDir, "user_profile.md");
1005
+ const profilePath = join6(profileDir, "user_profile.md");
970
1006
  if (!profileExists(profilePath)) {
971
1007
  await writeFile(profilePath, `# User Profile
972
1008
 
@@ -994,13 +1030,13 @@ Name: ${answers.userName}
994
1030
  if (answers.telegramChatId) {
995
1031
  await seedDefaultTasks(answers.telegramChatId, paths.home);
996
1032
  }
997
- const agentsDir = join5(paths.home, "agents");
998
- const agentRulesPath = join5(agentsDir, "default.md");
1033
+ const agentsDir = join6(paths.home, "agents");
1034
+ const agentRulesPath = join6(agentsDir, "default.md");
999
1035
  const { existsSync: agentRulesExists } = await import("node:fs");
1000
1036
  if (!agentRulesExists(agentRulesPath)) {
1001
1037
  await mkdir(agentsDir, { recursive: true });
1002
- const bundledPath = join5(dirname2(fileURLToPath(import.meta.url)), "agents", "default.md");
1003
- const fallbackPath = join5(dirname2(fileURLToPath(import.meta.url)), "..", "..", "agents", "default.md");
1038
+ const bundledPath = join6(dirname2(fileURLToPath(import.meta.url)), "agents", "default.md");
1039
+ const fallbackPath = join6(dirname2(fileURLToPath(import.meta.url)), "..", "..", "agents", "default.md");
1004
1040
  let content = "# Agent-to-Agent API\n\n<!-- See docs for configuration -->\n";
1005
1041
  try {
1006
1042
  content = await readFile(bundledPath, "utf-8");
@@ -1016,7 +1052,7 @@ Name: ${answers.userName}
1016
1052
  }
1017
1053
  process.stdout.write(`
1018
1054
  \u{1F4A1} To edit providers, agents, hailMary, fallback chains \u2014 edit:
1019
- ${join5(paths.config, "transport.json")}
1055
+ ${join6(paths.config, "transport.json")}
1020
1056
  Docs: https://aksika.github.io/abtars/
1021
1057
  `);
1022
1058
  if (opts.nonInteractive) {
@@ -1025,33 +1061,22 @@ Next: 'abtars update' to build, then start the bridge.
1025
1061
  `);
1026
1062
  return 0;
1027
1063
  }
1028
- const { existsSync: existsSync4 } = await import("node:fs");
1029
- const inRepo = existsSync4(join5(process.cwd(), ".git")) && existsSync4(join5(process.cwd(), "package.json"));
1030
- if (!inRepo) {
1064
+ const { existsSync: existsSync8 } = await import("node:fs");
1065
+ const hasRelease = existsSync8(join6(paths.home, "current"));
1066
+ if (!hasRelease) {
1031
1067
  process.stdout.write(`
1032
- Next: cd into the abtars repo and run 'abtars update' to build and activate.
1068
+ Next: run 'abtars update' to stage the release, then start the bridge.
1033
1069
  `);
1034
1070
  return 0;
1035
1071
  }
1036
- process.stdout.write(`
1037
- \u2500\u2500 Running 'abtars update' \u2500\u2500
1038
- `);
1039
- const { update: update2 } = await import("./update-XLWLAPLP.js");
1040
- const updRc = await update2({ source: "local", fromLocal: true, allowAbmindMismatch: false });
1041
- if (updRc !== 0) {
1042
- process.stderr.write(`
1043
- \u26A0\uFE0F 'abtars update' exited with code ${updRc}. Fix and re-run manually.
1044
- `);
1045
- return updRc;
1046
- }
1047
1072
  try {
1048
- const { spawnSync: spawnSync2 } = await import("node:child_process");
1049
- const which = spawnSync2("which", ["abmind"], { encoding: "utf-8" });
1073
+ const { spawnSync: spawnSync4 } = await import("node:child_process");
1074
+ const which = spawnSync4("which", ["abmind"], { encoding: "utf-8" });
1050
1075
  if (which.status === 0) {
1051
1076
  process.stdout.write(`
1052
1077
  \u2500\u2500 Running 'abmind install' \u2500\u2500
1053
1078
  `);
1054
- const r = spawnSync2("abmind", ["install"], { stdio: "inherit" });
1079
+ const r = spawnSync4("abmind", ["install"], { stdio: "inherit" });
1055
1080
  if (r.status !== 0) process.stdout.write(`\u26A0 abmind install exited with code ${r.status}
1056
1081
  `);
1057
1082
  } else {
@@ -1080,8 +1105,8 @@ Next: cd into the abtars repo and run 'abtars update' to build and activate.
1080
1105
  if (startCmds.length > 0) {
1081
1106
  for (const cmd of startCmds) process.stdout.write(` \u2192 ${cmd}
1082
1107
  `);
1083
- const run = await confirm2({ message: `Enable and start the watchdog service?`, initialValue: true });
1084
- if (run === true) {
1108
+ const run2 = await confirm2({ message: `Enable and start the watchdog service?`, initialValue: true });
1109
+ if (run2 === true) {
1085
1110
  for (const cmd of startCmds) {
1086
1111
  await new Promise((resolve) => {
1087
1112
  const child = spawn2("bash", ["-lc", cmd], { stdio: "inherit" });
@@ -1099,9 +1124,9 @@ Next: cd into the abtars repo and run 'abtars update' to build and activate.
1099
1124
  return 0;
1100
1125
  }
1101
1126
  async function seedDefaultTasks(chatId, abtarsHome4) {
1102
- const { existsSync: existsSync4 } = await import("node:fs");
1103
- const tasksJson = join5(abtarsHome4, "state", "tasks.json");
1104
- if (existsSync4(tasksJson)) {
1127
+ const { existsSync: existsSync8 } = await import("node:fs");
1128
+ const tasksJson = join6(abtarsHome4, "state", "tasks.json");
1129
+ if (existsSync8(tasksJson)) {
1105
1130
  process.stdout.write(`\u2022 tasks.json exists \u2014 skipping default-task seed
1106
1131
  `);
1107
1132
  return;
@@ -1109,12 +1134,12 @@ async function seedDefaultTasks(chatId, abtarsHome4) {
1109
1134
  const { fileURLToPath: fileURLToPath2 } = await import("node:url");
1110
1135
  const here = dirname2(fileURLToPath2(import.meta.url));
1111
1136
  const candidates = [
1112
- join5(here, "..", "..", "..", "tasks.default.json"),
1113
- join5(here, "..", "..", "tasks.default.json")
1137
+ join6(here, "..", "..", "..", "tasks.default.json"),
1138
+ join6(here, "..", "..", "tasks.default.json")
1114
1139
  ];
1115
1140
  let templatePath = null;
1116
1141
  for (const p of candidates) {
1117
- if (existsSync4(p)) {
1142
+ if (existsSync8(p)) {
1118
1143
  templatePath = p;
1119
1144
  break;
1120
1145
  }
@@ -1133,7 +1158,7 @@ async function seedDefaultTasks(chatId, abtarsHome4) {
1133
1158
  `);
1134
1159
  return;
1135
1160
  }
1136
- const { spawnSync: spawnSync2 } = await import("node:child_process");
1161
+ const { spawnSync: spawnSync4 } = await import("node:child_process");
1137
1162
  let seeded = 0;
1138
1163
  for (const entry of template) {
1139
1164
  const args = [
@@ -1152,7 +1177,7 @@ async function seedDefaultTasks(chatId, abtarsHome4) {
1152
1177
  if (entry.id) args.push("--id", entry.id);
1153
1178
  if (entry.title) args.push("--title", entry.title);
1154
1179
  if (entry.maxRunsPerDay) args.push("--max-runs-per-day", String(entry.maxRunsPerDay));
1155
- const result = spawnSync2("abtars-task", args, {
1180
+ const result = spawnSync4("abtars-task", args, {
1156
1181
  encoding: "utf-8",
1157
1182
  env: { ...process.env, ABTARS_HOME: abtarsHome4 }
1158
1183
  });
@@ -1298,6 +1323,511 @@ Re-run 'abtars update' or 'abtars rollback' to reconcile.
1298
1323
  return 0;
1299
1324
  }
1300
1325
 
1326
+ // src/cli/commands/update.ts
1327
+ init_log_and_swallow();
1328
+ import { hostname as hostname2 } from "node:os";
1329
+ import { join as join9, dirname as dirname3 } from "node:path";
1330
+ import { readFileSync as readFileSync4, existsSync as existsSync7, mkdirSync as mkdirSync2 } from "node:fs";
1331
+ import { copyFile as copyFile2, mkdir as mkdir4, chmod, readdir, readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
1332
+
1333
+ // src/cli/update-sources/local.ts
1334
+ import { spawnSync as spawnSync2 } from "node:child_process";
1335
+ import { existsSync as existsSync5 } from "node:fs";
1336
+ import { copyFile, cp, mkdir as mkdir2, readFile as readFile2, rm, writeFile as writeFile2 } from "node:fs/promises";
1337
+ import { join as join7 } from "node:path";
1338
+ var LocalBuildError = class extends Error {
1339
+ constructor(message, hint) {
1340
+ super(hint ? `${message}
1341
+
1342
+ ${hint}` : message);
1343
+ this.hint = hint;
1344
+ this.name = "LocalBuildError";
1345
+ }
1346
+ };
1347
+ function runCmd(cmd, args, cwd) {
1348
+ const r = spawnSync2(cmd, args, { cwd, encoding: "utf-8" });
1349
+ if (r.error) throw new LocalBuildError(`${cmd} ${args.join(" ")} failed: ${r.error.message}`);
1350
+ if (r.status !== 0) {
1351
+ throw new LocalBuildError(
1352
+ `${cmd} ${args.join(" ")} exited with status ${r.status}`,
1353
+ r.stderr?.trim() || void 0
1354
+ );
1355
+ }
1356
+ return r.stdout.trim();
1357
+ }
1358
+ function tryCmd(cmd, args, cwd) {
1359
+ const r = spawnSync2(cmd, args, { cwd, encoding: "utf-8" });
1360
+ if (r.status !== 0) return null;
1361
+ return r.stdout.trim();
1362
+ }
1363
+ function checkStaleness(repoRoot, allowStale) {
1364
+ const commit = runCmd("git", ["rev-parse", "--short", "HEAD"], repoRoot);
1365
+ const branch = tryCmd("git", ["rev-parse", "--abbrev-ref", "HEAD"], repoRoot);
1366
+ if (allowStale) return { commit, branch: branch === "HEAD" ? null : branch };
1367
+ if (branch === "HEAD" || branch === null) {
1368
+ throw new LocalBuildError(
1369
+ "Working tree is in detached HEAD (no current branch).",
1370
+ "Cannot check for staleness. Pass --from-local to proceed with the current tree."
1371
+ );
1372
+ }
1373
+ runCmd("git", ["fetch", "--quiet"], repoRoot);
1374
+ const upstream = tryCmd("git", ["rev-parse", "--abbrev-ref", `${branch}@{upstream}`], repoRoot);
1375
+ if (upstream === null) {
1376
+ throw new LocalBuildError(
1377
+ `Branch '${branch}' has no upstream configured.`,
1378
+ "Cannot check for staleness. Push the branch, or pass --from-local to proceed with the current tree."
1379
+ );
1380
+ }
1381
+ const behindStr = tryCmd("git", ["rev-list", "--count", `HEAD..${upstream}`], repoRoot);
1382
+ const behind = behindStr === null ? null : Number(behindStr);
1383
+ if (behind === null || !Number.isFinite(behind)) {
1384
+ throw new LocalBuildError(
1385
+ `Could not determine how far HEAD is behind ${upstream}.`,
1386
+ "Pass --from-local to proceed anyway."
1387
+ );
1388
+ }
1389
+ if (behind > 0) {
1390
+ throw new LocalBuildError(
1391
+ `Current branch: ${branch} (${commit})
1392
+ ${upstream} is ahead by ${behind} commit${behind === 1 ? "" : "s"}.`,
1393
+ `Run 'git pull' first, or pass --from-local to build from the current tree.`
1394
+ );
1395
+ }
1396
+ return { commit, branch };
1397
+ }
1398
+ async function readPackageVersion(repoRoot) {
1399
+ const pkg = JSON.parse(await readFile2(join7(repoRoot, "package.json"), "utf-8"));
1400
+ if (typeof pkg.version !== "string") {
1401
+ throw new LocalBuildError("package.json has no version field.");
1402
+ }
1403
+ return pkg.version;
1404
+ }
1405
+ function makeLocalBuildSource(opts = {}) {
1406
+ const repoRoot = opts.repoRoot ?? process.cwd();
1407
+ const isNpmPackage = !existsSync5(join7(repoRoot, ".git"));
1408
+ return {
1409
+ name: "local",
1410
+ async prepare(ctx) {
1411
+ if (isNpmPackage) {
1412
+ const bundleDir = join7(repoRoot, "bundle");
1413
+ if (!existsSync5(bundleDir)) {
1414
+ throw new Error(`No bundle/ found at ${repoRoot}. Run from the abtars npm package or a git checkout.`);
1415
+ }
1416
+ const pkgVersion2 = await readPackageVersion(repoRoot);
1417
+ const version2 = pkgVersion2;
1418
+ const stagedPath2 = join7(ctx.releasesDir, version2);
1419
+ await rm(stagedPath2, { recursive: true, force: true });
1420
+ await mkdir2(stagedPath2, { recursive: true });
1421
+ await cp(bundleDir, join7(stagedPath2, "bundle"), { recursive: true });
1422
+ const coreDir = join7(repoRoot, "core");
1423
+ if (existsSync5(coreDir)) await cp(coreDir, join7(stagedPath2, "core"), { recursive: true });
1424
+ const scriptsSrc = join7(repoRoot, "scripts");
1425
+ if (existsSync5(scriptsSrc)) await cp(scriptsSrc, join7(stagedPath2, "scripts"), { recursive: true });
1426
+ const configSrc = join7(repoRoot, "config");
1427
+ if (existsSync5(configSrc)) await cp(configSrc, join7(stagedPath2, "config"), { recursive: true });
1428
+ const manifestSrc = join7(repoRoot, "install-manifest.json");
1429
+ if (existsSync5(manifestSrc)) await copyFile(manifestSrc, join7(stagedPath2, "install-manifest.json"));
1430
+ await writeFile2(join7(stagedPath2, "package.json"), JSON.stringify({ type: "module", name: "abtars", version: version2 }, null, 2) + "\n");
1431
+ process.stdout.write(`\u2713 staged ${version2} (from npm package)
1432
+ `);
1433
+ return { version: version2, stagedPath: stagedPath2, commit: null, branch: null, packageLockHash: null, source: "local" };
1434
+ }
1435
+ const { commit, branch } = checkStaleness(repoRoot, opts.allowStale === true || ctx.allowStale);
1436
+ const pkgVersion = await readPackageVersion(repoRoot);
1437
+ const version = `${pkgVersion}-${commit}`;
1438
+ if (opts.skipInstall !== true) {
1439
+ runCmd("npm", ["install", "--no-audit", "--no-fund"], repoRoot);
1440
+ }
1441
+ const useBundle = process.env["AGENTBRIDGE_BUILD_MODE"] !== "tsc";
1442
+ if (useBundle) {
1443
+ runCmd("npm", ["run", "bundle"], repoRoot);
1444
+ const stagedPath2 = join7(ctx.releasesDir, version);
1445
+ await rm(stagedPath2, { recursive: true, force: true });
1446
+ await mkdir2(stagedPath2, { recursive: true });
1447
+ await cp(join7(repoRoot, "bundle"), join7(stagedPath2, "bundle"), { recursive: true });
1448
+ const coreSkillsSrc = join7(repoRoot, "core", "skills");
1449
+ if (existsSync5(coreSkillsSrc)) {
1450
+ await cp(coreSkillsSrc, join7(stagedPath2, "core", "skills"), { recursive: true });
1451
+ }
1452
+ await writeFile2(join7(stagedPath2, "package.json"), JSON.stringify({ type: "module", name: "abtars", version }, null, 2) + "\n");
1453
+ await copyFile(join7(repoRoot, "install-manifest.json"), join7(stagedPath2, "install-manifest.json"));
1454
+ const packageLockHash2 = await hashFile(join7(repoRoot, "package-lock.json"));
1455
+ return { version, stagedPath: stagedPath2, commit, branch, packageLockHash: packageLockHash2, source: "local" };
1456
+ }
1457
+ runCmd("npm", ["run", "build"], repoRoot);
1458
+ const stagedPath = join7(ctx.releasesDir, version);
1459
+ await rm(stagedPath, { recursive: true, force: true });
1460
+ await mkdir2(stagedPath, { recursive: true });
1461
+ await cp(join7(repoRoot, "dist"), join7(stagedPath, "dist"), { recursive: true });
1462
+ await copyFile(join7(repoRoot, "install-manifest.json"), join7(stagedPath, "install-manifest.json"));
1463
+ await rm(ctx.nodeModulesDir, { recursive: true, force: true });
1464
+ await mkdir2(ctx.nodeModulesDir, { recursive: true });
1465
+ const rsyncResult = spawnSync2(
1466
+ "rsync",
1467
+ ["-aL", "--quiet", `${join7(repoRoot, "node_modules")}/`, `${ctx.nodeModulesDir}/`],
1468
+ { stdio: "inherit" }
1469
+ );
1470
+ if (rsyncResult.status !== 0) {
1471
+ throw new LocalBuildError(
1472
+ `rsync of node_modules failed (status ${rsyncResult.status ?? -1})`,
1473
+ `Ensure rsync is installed. Falling back to node cp would re-create symlinks.`
1474
+ );
1475
+ }
1476
+ const packageLockHash = await hashFile(join7(repoRoot, "package-lock.json"));
1477
+ return { version, stagedPath, commit, branch, packageLockHash, source: "local" };
1478
+ }
1479
+ };
1480
+ }
1481
+
1482
+ // src/cli/update-sources/npm.ts
1483
+ import { spawnSync as spawnSync3 } from "node:child_process";
1484
+ import { existsSync as existsSync6, readFileSync as readFileSync3, unlinkSync as unlinkSync4 } from "node:fs";
1485
+ import { mkdir as mkdir3, rm as rm2 } from "node:fs/promises";
1486
+ import { join as join8 } from "node:path";
1487
+ var TIMEOUT_MS = 6e4;
1488
+ function run(cmd, args, cwd) {
1489
+ const r = spawnSync3(cmd, args, { cwd, encoding: "utf-8", timeout: TIMEOUT_MS });
1490
+ if (r.error) throw new Error(`${cmd} ${args.join(" ")}: ${r.error.message}`);
1491
+ if (r.status !== 0) throw new Error(`${cmd} ${args.join(" ")} exited ${r.status}: ${r.stderr?.trim()}`);
1492
+ return r.stdout.trim();
1493
+ }
1494
+ function readLocalVersion(home) {
1495
+ try {
1496
+ const pkg = JSON.parse(readFileSync3(join8(home, "current", "package.json"), "utf-8"));
1497
+ return pkg.version ?? null;
1498
+ } catch {
1499
+ return null;
1500
+ }
1501
+ }
1502
+ function makeNpmSource(packageName) {
1503
+ return {
1504
+ name: "npm",
1505
+ async prepare(ctx) {
1506
+ const latest = run("npm", ["view", packageName, "version"], ctx.home);
1507
+ const current = readLocalVersion(ctx.home);
1508
+ if (latest === current) {
1509
+ throw new Error(`Already at latest version (${latest}). Nothing to update.`);
1510
+ }
1511
+ const stagedPath = join8(ctx.releasesDir, latest);
1512
+ await rm2(stagedPath, { recursive: true, force: true });
1513
+ await mkdir3(stagedPath, { recursive: true });
1514
+ run("npm", ["pack", `${packageName}@${latest}`, "--pack-destination", stagedPath], stagedPath);
1515
+ const tgzName = `${packageName}-${latest}.tgz`.replace("@", "").replace("/", "-");
1516
+ const tgzPath = join8(stagedPath, tgzName);
1517
+ run("tar", ["-xzf", tgzPath, "--strip-components=1"], stagedPath);
1518
+ if (existsSync6(tgzPath)) unlinkSync4(tgzPath);
1519
+ run("npm", ["install", "--omit=dev", "--no-audit", "--no-fund"], stagedPath);
1520
+ return { version: latest, stagedPath, commit: null, branch: null, packageLockHash: null, source: "npm" };
1521
+ }
1522
+ };
1523
+ }
1524
+
1525
+ // src/cli/commands/update.ts
1526
+ function readJsonField2(file, field) {
1527
+ try {
1528
+ return JSON.parse(readFileSync4(file, "utf-8"))[field];
1529
+ } catch {
1530
+ return void 0;
1531
+ }
1532
+ }
1533
+ async function update(opts) {
1534
+ if (opts.source !== "local" && opts.source !== "npm") {
1535
+ process.stderr.write(`--source ${opts.source} is not yet supported.
1536
+ Use --source local (default) or --source npm.
1537
+ `);
1538
+ return 2;
1539
+ }
1540
+ const paths = packagePaths("abtars");
1541
+ if (!existsSync7(paths.releases) && existsSync7(join9(paths.home, "current"))) {
1542
+ process.stdout.write("Migrating old layout \u2192 releases/...\n");
1543
+ mkdirSync2(paths.releases, { recursive: true });
1544
+ }
1545
+ const release = await acquireLock(paths.lock, `update --source ${opts.source}`);
1546
+ try {
1547
+ let repoRoot = opts.repoRoot ?? process.cwd();
1548
+ if (!opts.repoRoot && !existsSync7(join9(repoRoot, ".git"))) {
1549
+ const { realpathSync } = await import("node:fs");
1550
+ const scriptPath = realpathSync(process.argv[1] ?? "");
1551
+ const candidate = join9(dirname3(scriptPath), "..");
1552
+ if (existsSync7(join9(candidate, "bundle"))) repoRoot = candidate;
1553
+ }
1554
+ const source = opts.source === "npm" ? makeNpmSource("abtars") : makeLocalBuildSource({ repoRoot, allowStale: opts.fromLocal });
1555
+ if (opts.fromLocal) {
1556
+ showHintOnce("update-from-local", "Building from working copy (--from-local). To sync with remote first: git pull && abtars update");
1557
+ }
1558
+ process.stdout.write(`Building from local checkout (${process.cwd()})...
1559
+ `);
1560
+ const staged = await source.prepare({
1561
+ releasesDir: paths.releases,
1562
+ nodeModulesDir: paths.nodeModules,
1563
+ home: paths.home,
1564
+ allowStale: opts.fromLocal
1565
+ });
1566
+ process.stdout.write(`\u2713 staged ${staged.version} at ${staged.stagedPath}
1567
+ `);
1568
+ {
1569
+ const pkgPath = join9(staged.stagedPath, "package.json");
1570
+ const { readFileSync: readFileSync5, writeFileSync: writeFileSync2 } = await import("node:fs");
1571
+ try {
1572
+ const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
1573
+ const externals = { patchright: "^1.59.4", "rettiwt-api": "^4.1.3" };
1574
+ pkg.dependencies = { ...pkg.dependencies, ...externals };
1575
+ writeFileSync2(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
1576
+ const { execSync } = await import("node:child_process");
1577
+ execSync("npm install --omit=dev --ignore-scripts 2>/dev/null", { cwd: staged.stagedPath, timeout: 6e4 });
1578
+ process.stdout.write(`\u2713 external deps installed
1579
+ `);
1580
+ } catch (err) {
1581
+ process.stdout.write(`\u26A0 external deps install failed: ${err instanceof Error ? err.message : String(err)}
1582
+ `);
1583
+ }
1584
+ }
1585
+ {
1586
+ const { existsSync: existsSync8, unlinkSync: unlinkSync5, symlinkSync } = await import("node:fs");
1587
+ const mainLink = join9(staged.stagedPath, "main.js");
1588
+ try {
1589
+ unlinkSync5(mainLink);
1590
+ } catch (err) {
1591
+ logAndSwallow("update", "op", err);
1592
+ }
1593
+ const entry = existsSync8(join9(staged.stagedPath, "bundle", "abtars.js")) ? "bundle/abtars.js" : "dist/main.js";
1594
+ symlinkSync(entry, mainLink);
1595
+ }
1596
+ const { existsSync: ex2, lstatSync, readlinkSync: readlinkSync2, symlinkSync: sl2, mkdirSync: mk2 } = await import("node:fs");
1597
+ const oldNm = join9(paths.home, "current", "node_modules");
1598
+ const preservedLinks = [];
1599
+ if (ex2(oldNm)) {
1600
+ for (const name of ["abmind", "better-sqlite3"]) {
1601
+ const p = join9(oldNm, name);
1602
+ try {
1603
+ if (ex2(p) && lstatSync(p).isSymbolicLink()) preservedLinks.push({ name, target: readlinkSync2(p) });
1604
+ } catch {
1605
+ }
1606
+ }
1607
+ }
1608
+ await activate(paths.current, staged.version);
1609
+ process.stdout.write(`\u2713 current -> releases/${staged.version}
1610
+ `);
1611
+ if (preservedLinks.length > 0) {
1612
+ const newNm = join9(paths.home, "current", "node_modules");
1613
+ mk2(newNm, { recursive: true });
1614
+ for (const { name, target } of preservedLinks) {
1615
+ try {
1616
+ sl2(target, join9(newNm, name));
1617
+ } catch {
1618
+ }
1619
+ }
1620
+ }
1621
+ const prior = await readManifest(paths.manifest);
1622
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1623
+ const newPriorReleases = prior?.version ? [
1624
+ {
1625
+ version: prior.version,
1626
+ commit: prior.commit,
1627
+ activatedAt: prior.activatedAt,
1628
+ packageLockHash: prior.packageLockHash
1629
+ },
1630
+ ...prior.priorReleases ?? []
1631
+ ].slice(0, RETENTION - 1) : prior?.priorReleases ?? [];
1632
+ await writeManifest(paths.manifest, {
1633
+ ...prior ?? emptyManifest("abtars", hostname2()),
1634
+ version: staged.version,
1635
+ commit: staged.commit,
1636
+ branch: staged.branch,
1637
+ packageLockHash: staged.packageLockHash,
1638
+ activatedAt: now,
1639
+ source: "local",
1640
+ priorReleases: newPriorReleases
1641
+ });
1642
+ process.stdout.write(`\u2713 manifest updated
1643
+ `);
1644
+ const pruned = await pruneReleases(
1645
+ paths.releases,
1646
+ [staged.version, ...newPriorReleases.map((r) => r.version)],
1647
+ staged.version,
1648
+ RETENTION
1649
+ );
1650
+ if (pruned.length > 0) {
1651
+ process.stdout.write(`\u2713 pruned ${pruned.length} old release${pruned.length === 1 ? "" : "s"}: ${pruned.join(", ")}
1652
+ `);
1653
+ }
1654
+ process.stdout.write(`
1655
+ Update complete: ${staged.version}
1656
+ `);
1657
+ const { loadManifest } = await import("./install-manifest-MCJCAYSR.js");
1658
+ const sourceRoot = staged.stagedPath;
1659
+ const installManifest = loadManifest(sourceRoot);
1660
+ const repoScripts = join9(sourceRoot, "scripts");
1661
+ const destScripts = join9(paths.home, "scripts");
1662
+ await mkdir4(destScripts, { recursive: true });
1663
+ const allScriptFiles = await readdir(repoScripts).catch(() => []);
1664
+ const matchesInclude = (name) => installManifest.scripts.include.some((pattern) => {
1665
+ const ext = pattern.replace("*", "");
1666
+ return name.endsWith(ext);
1667
+ });
1668
+ const scriptFiles = allScriptFiles.filter(matchesInclude);
1669
+ const home = process.env["HOME"] ?? "";
1670
+ let serviceChanged = false;
1671
+ const installMode = (await readManifest(paths.manifest))?.installMode ?? "supervised";
1672
+ const isExecutable = (name) => {
1673
+ const ext = installManifest.scripts.executable.replace("*", "");
1674
+ return name.endsWith(ext);
1675
+ };
1676
+ for (const name of scriptFiles) {
1677
+ await copyFile2(join9(repoScripts, name), join9(destScripts, name));
1678
+ if (isExecutable(name)) await chmod(join9(destScripts, name), 493);
1679
+ if (isExecutable(name)) {
1680
+ await copyFile2(join9(repoScripts, name), join9(paths.home, name));
1681
+ await chmod(join9(paths.home, name), 493);
1682
+ }
1683
+ const macService = installManifest.services.supervised.macos;
1684
+ if (macService && name === macService.plist && process.platform === "darwin" && home && installMode === "supervised") {
1685
+ const launchAgentsDir = join9(home, "Library", "LaunchAgents");
1686
+ await mkdir4(launchAgentsDir, { recursive: true });
1687
+ const dst = join9(launchAgentsDir, name);
1688
+ const oldContent = await readFile3(dst, "utf-8").catch(() => "");
1689
+ let templated = await readFile3(join9(repoScripts, name), "utf-8");
1690
+ for (const ph of macService.placeholders) templated = templated.replaceAll(ph, home);
1691
+ await writeFile3(dst, templated);
1692
+ if (oldContent !== templated) serviceChanged = true;
1693
+ }
1694
+ const linuxService = installManifest.services.supervised.linux;
1695
+ if (linuxService?.units.includes(name) && process.platform === "linux" && home && installMode === "supervised") {
1696
+ const systemdDir = join9(home, ".config", "systemd", "user");
1697
+ await mkdir4(systemdDir, { recursive: true });
1698
+ const dst = join9(systemdDir, name);
1699
+ const oldContent = await readFile3(dst, "utf-8").catch(() => "");
1700
+ await copyFile2(join9(repoScripts, name), dst);
1701
+ const newContent = await readFile3(dst, "utf-8").catch(() => "");
1702
+ if (oldContent !== newContent) serviceChanged = true;
1703
+ }
1704
+ }
1705
+ process.stdout.write(`\u2713 scripts refreshed (${scriptFiles.length} files)
1706
+ `);
1707
+ const { writeWrapper } = await import("./install-AJ7VW76P.js");
1708
+ await mkdir4(paths.bin, { recursive: true });
1709
+ for (const name of installManifest.cliWrappers) {
1710
+ await writeWrapper(paths.bin, name, paths.current, false);
1711
+ }
1712
+ process.stdout.write(`\u2713 wrappers refreshed (${installManifest.cliWrappers.length} files)
1713
+ `);
1714
+ const { rmSync: rmSync2, cpSync, readdirSync: readdirSync3 } = await import("node:fs");
1715
+ const skillsCoreSrc = join9(staged.stagedPath, "core", "skills");
1716
+ const skillsCoreDst = join9(paths.home, "skills", "core");
1717
+ if (existsSync7(skillsCoreSrc)) {
1718
+ rmSync2(skillsCoreDst, { recursive: true, force: true });
1719
+ cpSync(skillsCoreSrc, skillsCoreDst, { recursive: true });
1720
+ const files = readdirSync3(skillsCoreDst, { recursive: true });
1721
+ const count = files.filter((f) => f.endsWith("SKILL.md")).length;
1722
+ process.stdout.write(`\u2713 skills/core synced (${count} skills)
1723
+ `);
1724
+ }
1725
+ for (const d of ["custom", "downloaded", "self"]) {
1726
+ await mkdir4(join9(paths.home, "skills", d), { recursive: true });
1727
+ }
1728
+ for (const stale of ["memory", "ops", "tools", "coding"]) {
1729
+ const p = join9(paths.home, "skills", stale);
1730
+ if (existsSync7(p)) {
1731
+ rmSync2(p, { recursive: true, force: true });
1732
+ }
1733
+ }
1734
+ const releaseConfig = join9(staged.stagedPath, "config");
1735
+ const destConfig = join9(paths.home, "config");
1736
+ if (existsSync7(releaseConfig)) {
1737
+ for (const f of readdirSync3(releaseConfig)) {
1738
+ if (!f.endsWith(".example")) continue;
1739
+ const target = join9(destConfig, f.replace(".example", ""));
1740
+ if (!existsSync7(target)) {
1741
+ cpSync(join9(releaseConfig, f), target);
1742
+ }
1743
+ }
1744
+ const defaultTransport = join9(releaseConfig, "transport.default.json");
1745
+ if (existsSync7(defaultTransport)) cpSync(defaultTransport, join9(destConfig, "transport.default.json"));
1746
+ }
1747
+ const coreSrc = join9(staged.stagedPath, "core");
1748
+ const coreDst = join9(paths.home, "core");
1749
+ if (existsSync7(coreSrc)) {
1750
+ await mkdir4(coreDst, { recursive: true });
1751
+ cpSync(coreSrc, coreDst, { recursive: true });
1752
+ }
1753
+ if (serviceChanged) {
1754
+ if (process.platform === "darwin") {
1755
+ 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
1756
+ `);
1757
+ } else {
1758
+ process.stdout.write(`\u26A0\uFE0F systemd service updated \u2014 reload with: systemctl --user daemon-reload && systemctl --user restart abtars-watchdog
1759
+ `);
1760
+ }
1761
+ }
1762
+ void hashFile;
1763
+ const { ensureInstallInvariants } = await import("./ensure-invariants-K2ZUZ6NR.js");
1764
+ const invariantResults = await ensureInstallInvariants(process.cwd(), paths.home);
1765
+ if (invariantResults.length > 0) {
1766
+ process.stdout.write(`\u2713 invariants: ${invariantResults.join(", ")}
1767
+ `);
1768
+ }
1769
+ const doctorPath = join9(paths.home, "scripts", "doctor.sh");
1770
+ if (existsSync7(doctorPath)) {
1771
+ process.stdout.write("\n\u{1FA7A} Health check...\n");
1772
+ try {
1773
+ const { execSync } = await import("node:child_process");
1774
+ execSync(`bash "${doctorPath}" --fix`, { stdio: "inherit", timeout: 3e4 });
1775
+ } catch (err) {
1776
+ process.stderr.write(`\u26A0\uFE0F doctor --fix failed: ${err instanceof Error ? err.message : String(err)}
1777
+ `);
1778
+ }
1779
+ }
1780
+ const manifestForRestart = await readManifest(paths.manifest);
1781
+ const restartMode = manifestForRestart?.installMode;
1782
+ if (!restartMode) {
1783
+ process.stderr.write("\u274C installMode not set in manifest.json. Run 'abtars install' first.\n");
1784
+ return 1;
1785
+ }
1786
+ if (restartMode === "supervised-daemon" || restartMode === "supervised") {
1787
+ process.stdout.write("\nRestarting bridge via watchdog...\n");
1788
+ const wdLock = join9(paths.home, "watchdog.lock");
1789
+ const wdPid = readJsonField2(wdLock, "pid");
1790
+ if (wdPid && wdPid > 0) {
1791
+ try {
1792
+ process.kill(wdPid, "SIGUSR1");
1793
+ process.stdout.write(`\u267B\uFE0F USR1 sent to watchdog (PID ${wdPid}) \u2014 bridge will restart
1794
+ `);
1795
+ } catch {
1796
+ process.stdout.write(`\u26A0\uFE0F Could not signal watchdog (PID ${wdPid}). Restart manually:
1797
+ `);
1798
+ if (process.platform === "darwin") {
1799
+ process.stdout.write(` launchctl kickstart -k gui/$(id -u)/com.abtars.watchdog
1800
+ `);
1801
+ } else {
1802
+ process.stdout.write(` systemctl --user restart abtars-watchdog
1803
+ `);
1804
+ }
1805
+ }
1806
+ } else {
1807
+ process.stdout.write(`\u26A0\uFE0F Watchdog not running. Cold restart...
1808
+ `);
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)}
1812
+ `);
1813
+ });
1814
+ }
1815
+ } else {
1816
+ process.stdout.write("\nRestarting bridge...\n");
1817
+ const { restart: restart2 } = await import("./abtars-restart.js");
1818
+ await restart2({ cold: true }).catch((err) => {
1819
+ process.stderr.write(`\u26A0\uFE0F Restart failed: ${err instanceof Error ? err.message : String(err)}
1820
+ `);
1821
+ });
1822
+ }
1823
+ const { printHealthSummary } = await import("./health-check-RJ2SUJYL.js");
1824
+ printHealthSummary(paths.home);
1825
+ return 0;
1826
+ } finally {
1827
+ await release();
1828
+ }
1829
+ }
1830
+
1301
1831
  // src/cli/abtars.ts
1302
1832
  process.umask(63);
1303
1833
  function parseArgs(argv) {