abtars 0.2.1-alpha.5 → 0.2.1-alpha.6
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/bundle/abtars-cli.js +415 -431
- package/bundle/abtars-cli.js.map +3 -3
- package/bundle/abtars.js +1 -1
- package/bundle/abtars.js.map +2 -2
- package/bundle/chunk-4BUOO6WI.js +411 -0
- package/bundle/chunk-4BUOO6WI.js.map +7 -0
- package/bundle/chunk-RSWUPUNA.js +314 -0
- package/bundle/chunk-RSWUPUNA.js.map +7 -0
- package/bundle/deploy-lib-import-SBKXDD3F.js +52 -0
- package/bundle/deploy-lib-import-SBKXDD3F.js.map +7 -0
- package/bundle/install-SH4UVUXQ.js +13 -0
- package/bundle/install-SH4UVUXQ.js.map +7 -0
- package/bundle/meta.json +163 -127
- package/package.json +1 -1
- 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/abtars-cli.js
CHANGED
|
@@ -2,21 +2,21 @@
|
|
|
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-4BUOO6WI.js";
|
|
6
6
|
import {
|
|
7
|
-
RETENTION,
|
|
8
7
|
acquireLock,
|
|
9
|
-
|
|
8
|
+
atomicSwap,
|
|
9
|
+
cleanStaleStaging,
|
|
10
|
+
configSnapshot,
|
|
10
11
|
emptyManifest,
|
|
11
12
|
hashFile,
|
|
12
|
-
|
|
13
|
+
healthProbe,
|
|
13
14
|
packagePaths,
|
|
14
|
-
pruneReleases,
|
|
15
|
-
readCurrent,
|
|
16
15
|
readManifest,
|
|
17
|
-
|
|
18
|
-
writeManifest
|
|
19
|
-
|
|
16
|
+
readSentinel,
|
|
17
|
+
writeManifest,
|
|
18
|
+
writeSentinel
|
|
19
|
+
} from "./chunk-RSWUPUNA.js";
|
|
20
20
|
import {
|
|
21
21
|
restart
|
|
22
22
|
} from "./chunk-URAQLQ2U.js";
|
|
@@ -488,7 +488,7 @@ async function runInteractive(existing) {
|
|
|
488
488
|
const { intro, outro, text, select, confirm: confirm2, isCancel, cancel } = await import("./dist-J3T4XVKX.js");
|
|
489
489
|
intro("abtars onboard \u2014 first-time setup");
|
|
490
490
|
const noteEmpty = "press Enter to skip";
|
|
491
|
-
const { packagePaths: pp, readManifest: rm3 } = await import("./deploy-lib-import-
|
|
491
|
+
const { packagePaths: pp, readManifest: rm3 } = await import("./deploy-lib-import-SBKXDD3F.js");
|
|
492
492
|
const mfPaths = pp("abtars");
|
|
493
493
|
const mf = await rm3(mfPaths.manifest);
|
|
494
494
|
const installMode = mf?.installMode ?? "supervised";
|
|
@@ -924,7 +924,7 @@ async function onboard(opts) {
|
|
|
924
924
|
}
|
|
925
925
|
if (secrets.length > 0) process.stdout.write(`\u2713 ${secrets.length} secrets \u2192 ${secretDirPath}
|
|
926
926
|
`);
|
|
927
|
-
const { writeManifest: writeManifest2 } = await import("./deploy-lib-import-
|
|
927
|
+
const { writeManifest: writeManifest2 } = await import("./deploy-lib-import-SBKXDD3F.js");
|
|
928
928
|
const mf = await readManifest(paths.manifest);
|
|
929
929
|
if (mf) await writeManifest2(paths.manifest, { ...mf, installMode: answers.installMode });
|
|
930
930
|
process.stdout.write(`\u2713 install mode: ${answers.installMode}
|
|
@@ -1055,8 +1055,8 @@ Next: 'abtars update' to build, then start the bridge.
|
|
|
1055
1055
|
`);
|
|
1056
1056
|
return 0;
|
|
1057
1057
|
}
|
|
1058
|
-
const { existsSync:
|
|
1059
|
-
const hasRelease =
|
|
1058
|
+
const { existsSync: existsSync10 } = await import("node:fs");
|
|
1059
|
+
const hasRelease = existsSync10(join6(paths.home, "current"));
|
|
1060
1060
|
if (!hasRelease) {
|
|
1061
1061
|
process.stdout.write(`
|
|
1062
1062
|
Next: run 'abtars update' to stage the release, then start the bridge.
|
|
@@ -1076,9 +1076,9 @@ Next: start the bridge
|
|
|
1076
1076
|
return 0;
|
|
1077
1077
|
}
|
|
1078
1078
|
async function seedDefaultTasks(chatId, abtarsHome4) {
|
|
1079
|
-
const { existsSync:
|
|
1079
|
+
const { existsSync: existsSync10 } = await import("node:fs");
|
|
1080
1080
|
const tasksJson = join6(abtarsHome4, "tasks", "tasks.json");
|
|
1081
|
-
if (
|
|
1081
|
+
if (existsSync10(tasksJson)) {
|
|
1082
1082
|
process.stdout.write(`\u2022 tasks.json exists \u2014 skipping default-task seed
|
|
1083
1083
|
`);
|
|
1084
1084
|
return;
|
|
@@ -1091,7 +1091,7 @@ async function seedDefaultTasks(chatId, abtarsHome4) {
|
|
|
1091
1091
|
];
|
|
1092
1092
|
let templatePath = null;
|
|
1093
1093
|
for (const p of candidates) {
|
|
1094
|
-
if (
|
|
1094
|
+
if (existsSync10(p)) {
|
|
1095
1095
|
templatePath = p;
|
|
1096
1096
|
break;
|
|
1097
1097
|
}
|
|
@@ -1148,80 +1148,59 @@ async function seedDefaultTasks(chatId, abtarsHome4) {
|
|
|
1148
1148
|
}
|
|
1149
1149
|
|
|
1150
1150
|
// src/cli/commands/rollback.ts
|
|
1151
|
-
import {
|
|
1152
|
-
|
|
1151
|
+
import { existsSync as existsSync5, renameSync as renameSync3, rmSync as rmSync2 } from "node:fs";
|
|
1152
|
+
import { join as join7 } from "node:path";
|
|
1153
|
+
async function rollback() {
|
|
1153
1154
|
const paths = packagePaths("abtars");
|
|
1154
1155
|
const manifest = await readManifest(paths.manifest);
|
|
1155
|
-
if (!
|
|
1156
|
-
process.stderr.write(`
|
|
1156
|
+
if (!existsSync5(paths.appPrev)) {
|
|
1157
|
+
process.stderr.write(`Nothing to roll back to (no app.prev/ found).
|
|
1157
1158
|
`);
|
|
1158
1159
|
return 2;
|
|
1159
1160
|
}
|
|
1160
|
-
if (manifest
|
|
1161
|
-
process.stderr.write(`No
|
|
1161
|
+
if (!manifest?.version) {
|
|
1162
|
+
process.stderr.write(`No active release in manifest; nothing to roll back.
|
|
1162
1163
|
`);
|
|
1163
1164
|
return 2;
|
|
1164
1165
|
}
|
|
1165
|
-
const
|
|
1166
|
-
if (target === manifest.version) {
|
|
1167
|
-
process.stdout.write(`Already at ${target}; no-op.
|
|
1168
|
-
`);
|
|
1169
|
-
return 0;
|
|
1170
|
-
}
|
|
1171
|
-
if (!await releaseExists(paths.releases, target)) {
|
|
1172
|
-
const available = [manifest.version, ...manifest.priorReleases.map((r) => r.version)];
|
|
1173
|
-
process.stderr.write(
|
|
1174
|
-
`Target release '${target}' does not exist in ${paths.releases}.
|
|
1175
|
-
Available: ${available.join(", ")}
|
|
1176
|
-
If pruned, rebuild from the target's git SHA with 'abtars update' instead.
|
|
1177
|
-
`
|
|
1178
|
-
);
|
|
1179
|
-
return 2;
|
|
1180
|
-
}
|
|
1181
|
-
const targetRecord = target === manifest.version ? {
|
|
1182
|
-
version: manifest.version,
|
|
1183
|
-
commit: manifest.commit,
|
|
1184
|
-
activatedAt: manifest.activatedAt,
|
|
1185
|
-
packageLockHash: manifest.packageLockHash
|
|
1186
|
-
} : manifest.priorReleases.find((r) => r.version === target) ?? null;
|
|
1187
|
-
if (manifest.packageLockHash && targetRecord?.packageLockHash && manifest.packageLockHash !== targetRecord.packageLockHash) {
|
|
1188
|
-
process.stderr.write(
|
|
1189
|
-
`v${manifest.version} pinned different deps than v${target} (package-lock hashes differ).
|
|
1190
|
-
Rollback via symlink is unsafe. Instead:
|
|
1191
|
-
git checkout ${targetRecord.commit ?? "<commit-from-manifest>"}
|
|
1192
|
-
abtars update --from-local
|
|
1193
|
-
This rebuilds node_modules/ to match the target release.
|
|
1194
|
-
`
|
|
1195
|
-
);
|
|
1196
|
-
return 3;
|
|
1197
|
-
}
|
|
1198
|
-
const release = await acquireLock(paths.lock, `rollback --to ${target}`);
|
|
1166
|
+
const release = await acquireLock(paths.lock, "rollback");
|
|
1199
1167
|
try {
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1168
|
+
const brokenDir = join7(paths.home, "app.broken");
|
|
1169
|
+
rmSync2(brokenDir, { recursive: true, force: true });
|
|
1170
|
+
renameSync3(paths.app, brokenDir);
|
|
1171
|
+
renameSync3(paths.appPrev, paths.app);
|
|
1172
|
+
rmSync2(brokenDir, { recursive: true, force: true });
|
|
1173
|
+
process.stdout.write(`\u2713 rolled back: app.prev/ \u2192 app/
|
|
1174
|
+
`);
|
|
1175
|
+
if (manifest.previousVersion) {
|
|
1176
|
+
await writeManifest(paths.manifest, {
|
|
1177
|
+
...manifest,
|
|
1178
|
+
version: manifest.previousVersion,
|
|
1179
|
+
commit: manifest.previousCommit,
|
|
1180
|
+
activatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1181
|
+
previousVersion: manifest.version,
|
|
1182
|
+
previousCommit: manifest.commit
|
|
1183
|
+
});
|
|
1184
|
+
process.stdout.write(`\u2713 manifest: ${manifest.version} \u2192 ${manifest.previousVersion}
|
|
1185
|
+
`);
|
|
1186
|
+
}
|
|
1187
|
+
const restartTs = Date.now();
|
|
1188
|
+
const { restart: restart2 } = await import("./abtars-restart.js");
|
|
1189
|
+
process.stdout.write(`\u267B\uFE0F Restarting bridge...
|
|
1190
|
+
`);
|
|
1191
|
+
await restart2({ cold: true }).catch(() => {
|
|
1217
1192
|
});
|
|
1218
|
-
|
|
1193
|
+
const health = await healthProbe(paths.home, restartTs, 6e4);
|
|
1194
|
+
if (health.healthy) {
|
|
1195
|
+
process.stdout.write(`\u2713 Bridge healthy (PID ${health.pid})
|
|
1219
1196
|
`);
|
|
1197
|
+
} else {
|
|
1198
|
+
process.stderr.write(`\u26A0\uFE0F Bridge may not have started. Check logs.
|
|
1199
|
+
`);
|
|
1200
|
+
}
|
|
1220
1201
|
process.stdout.write(`
|
|
1221
|
-
Rollback complete
|
|
1202
|
+
Rollback complete.
|
|
1222
1203
|
`);
|
|
1223
|
-
void emptyManifest;
|
|
1224
|
-
void hostname;
|
|
1225
1204
|
return 0;
|
|
1226
1205
|
} finally {
|
|
1227
1206
|
await release();
|
|
@@ -1229,13 +1208,12 @@ Rollback complete: ${target}
|
|
|
1229
1208
|
}
|
|
1230
1209
|
|
|
1231
1210
|
// src/cli/commands/status.ts
|
|
1211
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
1232
1212
|
import { readFileSync as readFileSync3 } from "node:fs";
|
|
1233
|
-
import { join as
|
|
1213
|
+
import { join as join8 } from "node:path";
|
|
1234
1214
|
async function status() {
|
|
1235
1215
|
const paths = packagePaths("abtars");
|
|
1236
1216
|
const manifest = await readManifest(paths.manifest);
|
|
1237
|
-
const current = await readCurrent(paths.current);
|
|
1238
|
-
const lock = await inspectLock(paths.lock);
|
|
1239
1217
|
if (!manifest) {
|
|
1240
1218
|
process.stdout.write(
|
|
1241
1219
|
`abtars: not installed (no manifest at ${paths.manifest})
|
|
@@ -1244,6 +1222,8 @@ Run 'abtars install' to set up.
|
|
|
1244
1222
|
);
|
|
1245
1223
|
return 1;
|
|
1246
1224
|
}
|
|
1225
|
+
const appExists = existsSync6(paths.app);
|
|
1226
|
+
const appPrevExists = existsSync6(paths.appPrev);
|
|
1247
1227
|
const lines = [
|
|
1248
1228
|
`abtars status`,
|
|
1249
1229
|
` home: ${paths.home}`,
|
|
@@ -1253,68 +1233,62 @@ Run 'abtars install' to set up.
|
|
|
1253
1233
|
` source: ${manifest.source}`,
|
|
1254
1234
|
` mode: ${manifest.installMode ?? "supervised"}`,
|
|
1255
1235
|
` activated: ${manifest.activatedAt}`,
|
|
1256
|
-
`
|
|
1257
|
-
`
|
|
1258
|
-
`
|
|
1236
|
+
` app/: ${appExists ? "\u2713 present" : "\u2717 missing"}`,
|
|
1237
|
+
` app.prev/: ${appPrevExists ? "\u2713 present" : "\u25CB none"}`,
|
|
1238
|
+
` previous: ${manifest.previousVersion ?? "(none)"}`,
|
|
1239
|
+
` host: ${manifest.host}`
|
|
1259
1240
|
];
|
|
1260
|
-
|
|
1261
|
-
const
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
try {
|
|
1274
|
-
const bridgeLock = JSON.parse(readFileSync3(join7(paths.home, "bridge.lock"), "utf-8"));
|
|
1275
|
-
if (bridgeLock.pid) {
|
|
1276
|
-
const alive = (() => {
|
|
1277
|
-
try {
|
|
1278
|
-
process.kill(bridgeLock.pid, 0);
|
|
1279
|
-
return true;
|
|
1280
|
-
} catch {
|
|
1281
|
-
return false;
|
|
1282
|
-
}
|
|
1283
|
-
})();
|
|
1284
|
-
lines.push(` bridge: ${alive ? "\u25CF running" : "\u2717 dead"} (pid ${bridgeLock.pid})`);
|
|
1285
|
-
} else {
|
|
1286
|
-
lines.push(` bridge: \u25CB stopped`);
|
|
1287
|
-
}
|
|
1288
|
-
} catch {
|
|
1241
|
+
try {
|
|
1242
|
+
const bridgeLock = JSON.parse(readFileSync3(join8(paths.home, "bridge.lock"), "utf-8"));
|
|
1243
|
+
if (bridgeLock.pid) {
|
|
1244
|
+
const alive = (() => {
|
|
1245
|
+
try {
|
|
1246
|
+
process.kill(bridgeLock.pid, 0);
|
|
1247
|
+
return true;
|
|
1248
|
+
} catch {
|
|
1249
|
+
return false;
|
|
1250
|
+
}
|
|
1251
|
+
})();
|
|
1252
|
+
lines.push(` bridge: ${alive ? "\u25CF running" : "\u2717 dead"} (pid ${bridgeLock.pid})`);
|
|
1253
|
+
} else {
|
|
1289
1254
|
lines.push(` bridge: \u25CB stopped`);
|
|
1290
1255
|
}
|
|
1256
|
+
} catch {
|
|
1257
|
+
lines.push(` bridge: \u25CB stopped`);
|
|
1291
1258
|
}
|
|
1292
1259
|
process.stdout.write(`${lines.join("\n")}
|
|
1293
1260
|
`);
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1261
|
+
const sentinel = readSentinel(paths.home);
|
|
1262
|
+
if (sentinel?.status === "pending") {
|
|
1263
|
+
const age = Date.now() - new Date(sentinel.startedAt).getTime();
|
|
1264
|
+
if (age > 5 * 6e4) {
|
|
1265
|
+
process.stderr.write(`
|
|
1266
|
+
\u26A0\uFE0F Last update (${sentinel.version}) may have failed \u2014 bridge never confirmed boot.
|
|
1267
|
+
`);
|
|
1268
|
+
return 1;
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
if (!appExists) {
|
|
1272
|
+
process.stderr.write(`
|
|
1273
|
+
\u26A0\uFE0F app/ directory missing. Run 'abtars update' to deploy.
|
|
1274
|
+
`);
|
|
1301
1275
|
return 1;
|
|
1302
1276
|
}
|
|
1303
1277
|
return 0;
|
|
1304
1278
|
}
|
|
1305
1279
|
|
|
1306
1280
|
// src/cli/commands/update.ts
|
|
1307
|
-
|
|
1308
|
-
import {
|
|
1309
|
-
import {
|
|
1310
|
-
import {
|
|
1311
|
-
import {
|
|
1281
|
+
import { hostname } from "node:os";
|
|
1282
|
+
import { join as join11 } from "node:path";
|
|
1283
|
+
import { readFileSync as readFileSync5, existsSync as existsSync9 } from "node:fs";
|
|
1284
|
+
import { copyFile as copyFile2, mkdir as mkdir4, chmod, readdir } from "node:fs/promises";
|
|
1285
|
+
import { rmSync as rmSync3, cpSync, readdirSync as readdirSync3, mkdirSync as mkdirSync2 } from "node:fs";
|
|
1312
1286
|
|
|
1313
1287
|
// src/cli/update-sources/local.ts
|
|
1314
1288
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
1315
|
-
import { existsSync as
|
|
1289
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
1316
1290
|
import { copyFile, cp, mkdir as mkdir2, readFile as readFile2, rm, writeFile as writeFile2 } from "node:fs/promises";
|
|
1317
|
-
import { join as
|
|
1291
|
+
import { join as join9 } from "node:path";
|
|
1318
1292
|
var LocalBuildError = class extends Error {
|
|
1319
1293
|
constructor(message, hint) {
|
|
1320
1294
|
super(hint ? `${message}
|
|
@@ -1376,7 +1350,7 @@ ${upstream} is ahead by ${behind} commit${behind === 1 ? "" : "s"}.`,
|
|
|
1376
1350
|
return { commit, branch };
|
|
1377
1351
|
}
|
|
1378
1352
|
async function readPackageVersion(repoRoot) {
|
|
1379
|
-
const pkg = JSON.parse(await readFile2(
|
|
1353
|
+
const pkg = JSON.parse(await readFile2(join9(repoRoot, "package.json"), "utf-8"));
|
|
1380
1354
|
if (typeof pkg.version !== "string") {
|
|
1381
1355
|
throw new LocalBuildError("package.json has no version field.");
|
|
1382
1356
|
}
|
|
@@ -1384,30 +1358,30 @@ async function readPackageVersion(repoRoot) {
|
|
|
1384
1358
|
}
|
|
1385
1359
|
function makeLocalBuildSource(opts = {}) {
|
|
1386
1360
|
const repoRoot = opts.repoRoot ?? process.cwd();
|
|
1387
|
-
const isNpmPackage = !
|
|
1361
|
+
const isNpmPackage = !existsSync7(join9(repoRoot, ".git"));
|
|
1388
1362
|
return {
|
|
1389
1363
|
name: "local",
|
|
1390
1364
|
async prepare(ctx) {
|
|
1391
1365
|
if (isNpmPackage) {
|
|
1392
|
-
const bundleDir =
|
|
1393
|
-
if (!
|
|
1366
|
+
const bundleDir = join9(repoRoot, "bundle");
|
|
1367
|
+
if (!existsSync7(bundleDir)) {
|
|
1394
1368
|
throw new Error(`No bundle/ found at ${repoRoot}. Run from the abtars npm package or a git checkout.`);
|
|
1395
1369
|
}
|
|
1396
1370
|
const pkgVersion2 = await readPackageVersion(repoRoot);
|
|
1397
1371
|
const version2 = pkgVersion2;
|
|
1398
|
-
const stagedPath2 =
|
|
1372
|
+
const stagedPath2 = ctx.stagingDir;
|
|
1399
1373
|
await rm(stagedPath2, { recursive: true, force: true });
|
|
1400
1374
|
await mkdir2(stagedPath2, { recursive: true });
|
|
1401
|
-
await cp(bundleDir,
|
|
1402
|
-
const coreDir =
|
|
1403
|
-
if (
|
|
1404
|
-
const scriptsSrc =
|
|
1405
|
-
if (
|
|
1406
|
-
const configSrc =
|
|
1407
|
-
if (
|
|
1408
|
-
const manifestSrc =
|
|
1409
|
-
if (
|
|
1410
|
-
await writeFile2(
|
|
1375
|
+
await cp(bundleDir, join9(stagedPath2, "bundle"), { recursive: true });
|
|
1376
|
+
const coreDir = join9(repoRoot, "core");
|
|
1377
|
+
if (existsSync7(coreDir)) await cp(coreDir, join9(stagedPath2, "core"), { recursive: true });
|
|
1378
|
+
const scriptsSrc = join9(repoRoot, "scripts");
|
|
1379
|
+
if (existsSync7(scriptsSrc)) await cp(scriptsSrc, join9(stagedPath2, "scripts"), { recursive: true });
|
|
1380
|
+
const configSrc = join9(repoRoot, "config");
|
|
1381
|
+
if (existsSync7(configSrc)) await cp(configSrc, join9(stagedPath2, "config"), { recursive: true });
|
|
1382
|
+
const manifestSrc = join9(repoRoot, "install-manifest.json");
|
|
1383
|
+
if (existsSync7(manifestSrc)) await copyFile(manifestSrc, join9(stagedPath2, "install-manifest.json"));
|
|
1384
|
+
await writeFile2(join9(stagedPath2, "package.json"), JSON.stringify({ type: "module", name: "abtars", version: version2 }, null, 2) + "\n");
|
|
1411
1385
|
process.stdout.write(`\u2713 staged ${version2} (from npm package)
|
|
1412
1386
|
`);
|
|
1413
1387
|
return { version: version2, stagedPath: stagedPath2, commit: null, branch: null, packageLockHash: null, source: "local" };
|
|
@@ -1418,42 +1392,18 @@ function makeLocalBuildSource(opts = {}) {
|
|
|
1418
1392
|
if (opts.skipInstall !== true) {
|
|
1419
1393
|
runCmd("npm", ["install", "--no-audit", "--no-fund"], repoRoot);
|
|
1420
1394
|
}
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
runCmd("npm", ["run", "bundle"], repoRoot);
|
|
1424
|
-
const stagedPath2 = join8(ctx.releasesDir, version);
|
|
1425
|
-
await rm(stagedPath2, { recursive: true, force: true });
|
|
1426
|
-
await mkdir2(stagedPath2, { recursive: true });
|
|
1427
|
-
await cp(join8(repoRoot, "bundle"), join8(stagedPath2, "bundle"), { recursive: true });
|
|
1428
|
-
const coreSkillsSrc = join8(repoRoot, "core", "skills");
|
|
1429
|
-
if (existsSync5(coreSkillsSrc)) {
|
|
1430
|
-
await cp(coreSkillsSrc, join8(stagedPath2, "core", "skills"), { recursive: true });
|
|
1431
|
-
}
|
|
1432
|
-
await writeFile2(join8(stagedPath2, "package.json"), JSON.stringify({ type: "module", name: "abtars", version }, null, 2) + "\n");
|
|
1433
|
-
await copyFile(join8(repoRoot, "install-manifest.json"), join8(stagedPath2, "install-manifest.json"));
|
|
1434
|
-
const packageLockHash2 = await hashFile(join8(repoRoot, "package-lock.json"));
|
|
1435
|
-
return { version, stagedPath: stagedPath2, commit, branch, packageLockHash: packageLockHash2, source: "local" };
|
|
1436
|
-
}
|
|
1437
|
-
runCmd("npm", ["run", "build"], repoRoot);
|
|
1438
|
-
const stagedPath = join8(ctx.releasesDir, version);
|
|
1395
|
+
runCmd("npm", ["run", "bundle"], repoRoot);
|
|
1396
|
+
const stagedPath = ctx.stagingDir;
|
|
1439
1397
|
await rm(stagedPath, { recursive: true, force: true });
|
|
1440
1398
|
await mkdir2(stagedPath, { recursive: true });
|
|
1441
|
-
await cp(
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
const rsyncResult = spawnSync2(
|
|
1446
|
-
"rsync",
|
|
1447
|
-
["-aL", "--quiet", `${join8(repoRoot, "node_modules")}/`, `${ctx.nodeModulesDir}/`],
|
|
1448
|
-
{ stdio: "inherit" }
|
|
1449
|
-
);
|
|
1450
|
-
if (rsyncResult.status !== 0) {
|
|
1451
|
-
throw new LocalBuildError(
|
|
1452
|
-
`rsync of node_modules failed (status ${rsyncResult.status ?? -1})`,
|
|
1453
|
-
`Ensure rsync is installed. Falling back to node cp would re-create symlinks.`
|
|
1454
|
-
);
|
|
1399
|
+
await cp(join9(repoRoot, "bundle"), join9(stagedPath, "bundle"), { recursive: true });
|
|
1400
|
+
const coreSkillsSrc = join9(repoRoot, "core", "skills");
|
|
1401
|
+
if (existsSync7(coreSkillsSrc)) {
|
|
1402
|
+
await cp(coreSkillsSrc, join9(stagedPath, "core", "skills"), { recursive: true });
|
|
1455
1403
|
}
|
|
1456
|
-
|
|
1404
|
+
await writeFile2(join9(stagedPath, "package.json"), JSON.stringify({ type: "module", name: "abtars", version }, null, 2) + "\n");
|
|
1405
|
+
await copyFile(join9(repoRoot, "install-manifest.json"), join9(stagedPath, "install-manifest.json"));
|
|
1406
|
+
const packageLockHash = await hashFile(join9(repoRoot, "package-lock.json"));
|
|
1457
1407
|
return { version, stagedPath, commit, branch, packageLockHash, source: "local" };
|
|
1458
1408
|
}
|
|
1459
1409
|
};
|
|
@@ -1461,9 +1411,9 @@ function makeLocalBuildSource(opts = {}) {
|
|
|
1461
1411
|
|
|
1462
1412
|
// src/cli/update-sources/npm.ts
|
|
1463
1413
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
1464
|
-
import { existsSync as
|
|
1414
|
+
import { existsSync as existsSync8, readFileSync as readFileSync4, unlinkSync as unlinkSync4 } from "node:fs";
|
|
1465
1415
|
import { mkdir as mkdir3, rm as rm2 } from "node:fs/promises";
|
|
1466
|
-
import { join as
|
|
1416
|
+
import { join as join10 } from "node:path";
|
|
1467
1417
|
var TIMEOUT_MS = 6e4;
|
|
1468
1418
|
function run(cmd, args, cwd) {
|
|
1469
1419
|
const r = spawnSync3(cmd, args, { cwd, encoding: "utf-8", timeout: TIMEOUT_MS });
|
|
@@ -1473,7 +1423,7 @@ function run(cmd, args, cwd) {
|
|
|
1473
1423
|
}
|
|
1474
1424
|
function readLocalVersion(home) {
|
|
1475
1425
|
try {
|
|
1476
|
-
const pkg = JSON.parse(readFileSync4(
|
|
1426
|
+
const pkg = JSON.parse(readFileSync4(join10(home, "app", "package.json"), "utf-8"));
|
|
1477
1427
|
return pkg.version ?? null;
|
|
1478
1428
|
} catch {
|
|
1479
1429
|
return null;
|
|
@@ -1488,14 +1438,14 @@ function makeNpmSource(packageName) {
|
|
|
1488
1438
|
if (latest === current) {
|
|
1489
1439
|
throw new Error(`Already at latest version (${latest}). Nothing to update.`);
|
|
1490
1440
|
}
|
|
1491
|
-
const stagedPath =
|
|
1441
|
+
const stagedPath = ctx.stagingDir;
|
|
1492
1442
|
await rm2(stagedPath, { recursive: true, force: true });
|
|
1493
1443
|
await mkdir3(stagedPath, { recursive: true });
|
|
1494
1444
|
run("npm", ["pack", `${packageName}@${latest}`, "--pack-destination", stagedPath], stagedPath);
|
|
1495
1445
|
const tgzName = `${packageName}-${latest}.tgz`.replace("@", "").replace("/", "-");
|
|
1496
|
-
const tgzPath =
|
|
1446
|
+
const tgzPath = join10(stagedPath, tgzName);
|
|
1497
1447
|
run("tar", ["-xzf", tgzPath, "--strip-components=1"], stagedPath);
|
|
1498
|
-
if (
|
|
1448
|
+
if (existsSync8(tgzPath)) unlinkSync4(tgzPath);
|
|
1499
1449
|
run("npm", ["install", "--omit=dev", "--no-audit", "--no-fund"], stagedPath);
|
|
1500
1450
|
return { version: latest, stagedPath, commit: null, branch: null, packageLockHash: null, source: "npm" };
|
|
1501
1451
|
}
|
|
@@ -1518,37 +1468,65 @@ Use --source local (default) or --source npm.
|
|
|
1518
1468
|
return 2;
|
|
1519
1469
|
}
|
|
1520
1470
|
const paths = packagePaths("abtars");
|
|
1471
|
+
const sentinel = readSentinel(paths.home);
|
|
1472
|
+
if (sentinel?.status === "pending") {
|
|
1473
|
+
const age = Date.now() - new Date(sentinel.startedAt).getTime();
|
|
1474
|
+
if (age > 5 * 6e4) {
|
|
1475
|
+
process.stderr.write(`\u26A0\uFE0F Previous update (${sentinel.version}) never completed successfully. Proceeding...
|
|
1476
|
+
`);
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
if (opts.check) {
|
|
1480
|
+
return checkForUpdates(paths.home, opts);
|
|
1481
|
+
}
|
|
1521
1482
|
const release = await acquireLock(paths.lock, `update --source ${opts.source}`);
|
|
1522
1483
|
try {
|
|
1484
|
+
cleanStaleStaging(paths.appStaging);
|
|
1485
|
+
const cleanupHandler = () => {
|
|
1486
|
+
if (existsSync9(paths.appStaging) && existsSync9(paths.app)) {
|
|
1487
|
+
rmSync3(paths.appStaging, { recursive: true, force: true });
|
|
1488
|
+
}
|
|
1489
|
+
};
|
|
1490
|
+
process.on("SIGHUP", () => {
|
|
1491
|
+
cleanupHandler();
|
|
1492
|
+
process.exit(130);
|
|
1493
|
+
});
|
|
1494
|
+
process.on("SIGTERM", () => {
|
|
1495
|
+
cleanupHandler();
|
|
1496
|
+
process.exit(143);
|
|
1497
|
+
});
|
|
1523
1498
|
let repoRoot = opts.repoRoot ?? process.cwd();
|
|
1524
|
-
if (!opts.repoRoot && !
|
|
1499
|
+
if (!opts.repoRoot && !existsSync9(join11(repoRoot, ".git"))) {
|
|
1525
1500
|
const { realpathSync } = await import("node:fs");
|
|
1526
1501
|
const scriptPath = realpathSync(process.argv[1] ?? "");
|
|
1527
|
-
const
|
|
1528
|
-
|
|
1502
|
+
const { dirname: dirname3 } = await import("node:path");
|
|
1503
|
+
const candidate = join11(dirname3(scriptPath), "..");
|
|
1504
|
+
if (existsSync9(join11(candidate, "bundle"))) repoRoot = candidate;
|
|
1529
1505
|
}
|
|
1530
1506
|
const source = opts.source === "npm" ? makeNpmSource("abtars") : makeLocalBuildSource({ repoRoot, allowStale: opts.fromLocal });
|
|
1531
1507
|
if (opts.fromLocal) {
|
|
1532
1508
|
showHintOnce("update-from-local", "Building from working copy (--from-local). To sync with remote first: git pull && abtars update");
|
|
1533
1509
|
}
|
|
1534
|
-
|
|
1510
|
+
if (opts.dryRun) {
|
|
1511
|
+
return printDryRun(paths, repoRoot, opts);
|
|
1512
|
+
}
|
|
1513
|
+
process.stdout.write(`Building from local checkout (${repoRoot})...
|
|
1535
1514
|
`);
|
|
1536
1515
|
const staged = await source.prepare({
|
|
1537
|
-
|
|
1538
|
-
nodeModulesDir: paths.nodeModules,
|
|
1516
|
+
stagingDir: paths.appStaging,
|
|
1539
1517
|
home: paths.home,
|
|
1540
1518
|
allowStale: opts.fromLocal
|
|
1541
1519
|
});
|
|
1542
|
-
process.stdout.write(`\u2713 staged ${staged.version}
|
|
1520
|
+
process.stdout.write(`\u2713 staged ${staged.version}
|
|
1543
1521
|
`);
|
|
1544
1522
|
{
|
|
1545
|
-
const pkgPath =
|
|
1546
|
-
const { readFileSync: readFileSync6, writeFileSync: writeFileSync2 } = await import("node:fs");
|
|
1523
|
+
const pkgPath = join11(staged.stagedPath, "package.json");
|
|
1547
1524
|
try {
|
|
1548
|
-
const pkg = JSON.parse(
|
|
1525
|
+
const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
|
|
1549
1526
|
const externals = { patchright: "^1.59.4", "rettiwt-api": "^4.1.3" };
|
|
1550
1527
|
pkg.dependencies = { ...pkg.dependencies, ...externals };
|
|
1551
1528
|
if (pkg.dependencies?.abmind?.startsWith("file:")) delete pkg.dependencies.abmind;
|
|
1529
|
+
const { writeFileSync: writeFileSync2 } = await import("node:fs");
|
|
1552
1530
|
writeFileSync2(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
1553
1531
|
const { execSync } = await import("node:child_process");
|
|
1554
1532
|
execSync("npm install --omit=dev --ignore-scripts 2>/dev/null", { cwd: staged.stagedPath, timeout: 6e4 });
|
|
@@ -1559,283 +1537,291 @@ Use --source local (default) or --source npm.
|
|
|
1559
1537
|
`);
|
|
1560
1538
|
}
|
|
1561
1539
|
}
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
logAndSwallow("update", "op", err);
|
|
1569
|
-
}
|
|
1570
|
-
const entry = existsSync8(join10(staged.stagedPath, "bundle", "abtars.js")) ? "bundle/abtars.js" : "dist/main.js";
|
|
1571
|
-
symlinkSync(entry, mainLink);
|
|
1572
|
-
}
|
|
1573
|
-
const { existsSync: ex2, symlinkSync: sl2, mkdirSync: mk2, unlinkSync: ul2 } = await import("node:fs");
|
|
1574
|
-
const { dirname: dn2 } = await import("node:path");
|
|
1575
|
-
await activate(paths.current, staged.version);
|
|
1576
|
-
process.stdout.write(`\u2713 current -> releases/${staged.version}
|
|
1577
|
-
`);
|
|
1578
|
-
const globalModules = join10(dn2(process.execPath), "..", "lib", "node_modules");
|
|
1579
|
-
const abmindHome = process.env["ABMIND_HOME"] ?? join10(process.env["HOME"] ?? "", ".abmind");
|
|
1580
|
-
const links = [];
|
|
1581
|
-
const globalAbmind = join10(globalModules, "abmind");
|
|
1582
|
-
const homeAbmind = join10(process.env["HOME"] ?? "", "abmind");
|
|
1583
|
-
const localAbmind = join10(paths.home, "current", "node_modules", "abmind");
|
|
1584
|
-
if (ex2(globalAbmind)) links.push({ name: "abmind", target: globalAbmind });
|
|
1585
|
-
else if (ex2(homeAbmind)) links.push({ name: "abmind", target: homeAbmind });
|
|
1586
|
-
else if (ex2(localAbmind)) links.push({ name: "abmind", target: localAbmind });
|
|
1587
|
-
const bsq3 = join10(abmindHome, "lib", "node_modules", "better-sqlite3");
|
|
1588
|
-
if (ex2(bsq3)) links.push({ name: "better-sqlite3", target: bsq3 });
|
|
1589
|
-
if (links.length > 0) {
|
|
1590
|
-
const bundleNm = join10(paths.home, "current", "bundle", "node_modules");
|
|
1591
|
-
const rootNm = join10(paths.home, "current", "node_modules");
|
|
1592
|
-
for (const dir of [bundleNm, rootNm]) {
|
|
1593
|
-
mk2(dir, { recursive: true });
|
|
1594
|
-
for (const { name, target } of links) {
|
|
1595
|
-
const dest = join10(dir, name);
|
|
1596
|
-
try {
|
|
1597
|
-
if (ex2(dest)) ul2(dest);
|
|
1598
|
-
} catch {
|
|
1599
|
-
}
|
|
1600
|
-
try {
|
|
1601
|
-
sl2(target, dest);
|
|
1602
|
-
} catch {
|
|
1603
|
-
}
|
|
1604
|
-
}
|
|
1605
|
-
}
|
|
1540
|
+
await copyAbmind(staged.stagedPath, repoRoot);
|
|
1541
|
+
const entryPoint = join11(staged.stagedPath, "bundle", "abtars.js");
|
|
1542
|
+
if (!existsSync9(entryPoint)) {
|
|
1543
|
+
process.stderr.write(`\u274C Entry point not found: ${entryPoint}
|
|
1544
|
+
`);
|
|
1545
|
+
return 1;
|
|
1606
1546
|
}
|
|
1547
|
+
configSnapshot(paths.config);
|
|
1548
|
+
process.stdout.write(`\u2713 config snapshot (3-slot rotation)
|
|
1549
|
+
`);
|
|
1550
|
+
atomicSwap(paths.app, paths.appPrev, paths.appStaging);
|
|
1551
|
+
process.stdout.write(`\u2713 atomic swap: app.staging/ \u2192 app/
|
|
1552
|
+
`);
|
|
1553
|
+
await postSwapHousekeeping(paths, repoRoot, staged);
|
|
1607
1554
|
const prior = await readManifest(paths.manifest);
|
|
1608
|
-
const
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
},
|
|
1616
|
-
...prior.priorReleases ?? []
|
|
1617
|
-
].slice(0, RETENTION - 1) : prior?.priorReleases ?? [];
|
|
1555
|
+
const sentinelData = {
|
|
1556
|
+
version: staged.version,
|
|
1557
|
+
previousVersion: prior?.version ?? null,
|
|
1558
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1559
|
+
status: "pending"
|
|
1560
|
+
};
|
|
1561
|
+
writeSentinel(paths.home, sentinelData);
|
|
1618
1562
|
await writeManifest(paths.manifest, {
|
|
1619
|
-
...prior ?? emptyManifest("abtars",
|
|
1563
|
+
...prior ?? emptyManifest("abtars", hostname()),
|
|
1620
1564
|
version: staged.version,
|
|
1621
1565
|
commit: staged.commit,
|
|
1622
1566
|
branch: staged.branch,
|
|
1623
1567
|
packageLockHash: staged.packageLockHash,
|
|
1624
|
-
activatedAt:
|
|
1568
|
+
activatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1625
1569
|
source: "local",
|
|
1626
|
-
|
|
1570
|
+
previousVersion: prior?.version ?? null,
|
|
1571
|
+
previousCommit: prior?.commit ?? null
|
|
1627
1572
|
});
|
|
1628
1573
|
process.stdout.write(`\u2713 manifest updated
|
|
1629
1574
|
`);
|
|
1630
|
-
const
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
RETENTION
|
|
1635
|
-
);
|
|
1636
|
-
if (pruned.length > 0) {
|
|
1637
|
-
process.stdout.write(`\u2713 pruned ${pruned.length} old release${pruned.length === 1 ? "" : "s"}: ${pruned.join(", ")}
|
|
1575
|
+
const restartTimestamp = Date.now();
|
|
1576
|
+
const restarted = await restartBridge(paths);
|
|
1577
|
+
if (!restarted) {
|
|
1578
|
+
process.stdout.write(`\u26A0\uFE0F Could not restart bridge. Start manually.
|
|
1638
1579
|
`);
|
|
1580
|
+
return 0;
|
|
1639
1581
|
}
|
|
1640
|
-
process.stdout.write(`
|
|
1641
|
-
Update complete: ${staged.version}
|
|
1582
|
+
process.stdout.write(`Waiting for bridge health...
|
|
1642
1583
|
`);
|
|
1643
|
-
const
|
|
1644
|
-
if (
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
if (agent.demoted) {
|
|
1650
|
-
delete agent.demoted;
|
|
1651
|
-
delete agent.demotedReason;
|
|
1652
|
-
delete agent.demotedModel;
|
|
1653
|
-
cleared = true;
|
|
1654
|
-
}
|
|
1655
|
-
for (const fb of agent.fallbacks ?? []) {
|
|
1656
|
-
if (fb.demoted) {
|
|
1657
|
-
delete fb.demoted;
|
|
1658
|
-
delete fb.demotedReason;
|
|
1659
|
-
delete fb.demotedModel;
|
|
1660
|
-
cleared = true;
|
|
1661
|
-
}
|
|
1662
|
-
}
|
|
1663
|
-
}
|
|
1664
|
-
if (cleared) {
|
|
1665
|
-
const { writeFileSync: wfs } = await import("node:fs");
|
|
1666
|
-
wfs(transportJson, JSON.stringify(tc, null, 2) + "\n");
|
|
1667
|
-
}
|
|
1668
|
-
} catch {
|
|
1669
|
-
}
|
|
1670
|
-
}
|
|
1671
|
-
const installManifestPath = join10(repoRoot, "install-manifest.json");
|
|
1672
|
-
const installManifest = JSON.parse(await readFile3(installManifestPath, "utf-8"));
|
|
1673
|
-
const repoScripts = join10(repoRoot, "scripts");
|
|
1674
|
-
const destScripts = join10(paths.home, "scripts");
|
|
1675
|
-
await mkdir4(destScripts, { recursive: true });
|
|
1676
|
-
const allScriptFiles = await readdir(repoScripts).catch(() => []);
|
|
1677
|
-
const matchesInclude = (name) => installManifest.scripts.include.some((pattern) => {
|
|
1678
|
-
const ext = pattern.replace("*", "");
|
|
1679
|
-
return name.endsWith(ext);
|
|
1680
|
-
});
|
|
1681
|
-
const scriptFiles = allScriptFiles.filter(matchesInclude);
|
|
1682
|
-
const home = process.env["HOME"] ?? "";
|
|
1683
|
-
let serviceChanged = false;
|
|
1684
|
-
const installMode = (await readManifest(paths.manifest))?.installMode ?? "supervised";
|
|
1685
|
-
const isExecutable = (name) => {
|
|
1686
|
-
const ext = installManifest.scripts.executable.replace("*", "");
|
|
1687
|
-
return name.endsWith(ext);
|
|
1688
|
-
};
|
|
1689
|
-
for (const name of scriptFiles) {
|
|
1690
|
-
await copyFile2(join10(repoScripts, name), join10(destScripts, name));
|
|
1691
|
-
if (isExecutable(name)) await chmod(join10(destScripts, name), 493);
|
|
1692
|
-
const macService = installManifest.services.supervised.macos;
|
|
1693
|
-
if (macService && name === macService.plist && process.platform === "darwin" && home && installMode === "supervised") {
|
|
1694
|
-
const launchAgentsDir = join10(home, "Library", "LaunchAgents");
|
|
1695
|
-
await mkdir4(launchAgentsDir, { recursive: true });
|
|
1696
|
-
const dst = join10(launchAgentsDir, name);
|
|
1697
|
-
const oldContent = await readFile3(dst, "utf-8").catch(() => "");
|
|
1698
|
-
let templated = await readFile3(join10(repoScripts, name), "utf-8");
|
|
1699
|
-
for (const ph of macService.placeholders) templated = templated.replaceAll(ph, home);
|
|
1700
|
-
await writeFile3(dst, templated);
|
|
1701
|
-
if (oldContent !== templated) serviceChanged = true;
|
|
1702
|
-
}
|
|
1703
|
-
const linuxService = installManifest.services.supervised.linux;
|
|
1704
|
-
if (linuxService?.units.includes(name) && process.platform === "linux" && home && installMode === "supervised") {
|
|
1705
|
-
const systemdDir = join10(home, ".config", "systemd", "user");
|
|
1706
|
-
await mkdir4(systemdDir, { recursive: true });
|
|
1707
|
-
const dst = join10(systemdDir, name);
|
|
1708
|
-
const oldContent = await readFile3(dst, "utf-8").catch(() => "");
|
|
1709
|
-
await copyFile2(join10(repoScripts, name), dst);
|
|
1710
|
-
const newContent = await readFile3(dst, "utf-8").catch(() => "");
|
|
1711
|
-
if (oldContent !== newContent) serviceChanged = true;
|
|
1712
|
-
}
|
|
1584
|
+
const health = await healthProbe(paths.home, restartTimestamp, 6e4);
|
|
1585
|
+
if (health.healthy) {
|
|
1586
|
+
writeSentinel(paths.home, { ...sentinelData, status: "success" });
|
|
1587
|
+
process.stdout.write(`\u2713 Bridge healthy (PID ${health.pid}, tick at ${new Date(health.heartbeat).toISOString()})
|
|
1588
|
+
`);
|
|
1589
|
+
return 0;
|
|
1713
1590
|
}
|
|
1714
|
-
process.
|
|
1591
|
+
process.stderr.write(`\u274C Bridge unhealthy after 60s. Auto-rolling back...
|
|
1592
|
+
`);
|
|
1593
|
+
if (!existsSync9(paths.appPrev)) {
|
|
1594
|
+
process.stderr.write(`\u274C No app.prev/ to roll back to. Manual intervention required.
|
|
1595
|
+
`);
|
|
1596
|
+
process.stderr.write(` Check: ~/.abtars/logs/bridge.log
|
|
1715
1597
|
`);
|
|
1716
|
-
|
|
1717
|
-
await mkdir4(paths.bin, { recursive: true });
|
|
1718
|
-
for (const name of installManifest.cliWrappers) {
|
|
1719
|
-
await writeWrapper(paths.bin, name, paths.current, false);
|
|
1598
|
+
return 2;
|
|
1720
1599
|
}
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
const {
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
if (
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1600
|
+
const brokenDir = join11(paths.home, "app.broken");
|
|
1601
|
+
rmSync3(brokenDir, { recursive: true, force: true });
|
|
1602
|
+
const { renameSync: renameSync4 } = await import("node:fs");
|
|
1603
|
+
renameSync4(paths.app, brokenDir);
|
|
1604
|
+
renameSync4(paths.appPrev, paths.app);
|
|
1605
|
+
if (prior) {
|
|
1606
|
+
await writeManifest(paths.manifest, prior);
|
|
1607
|
+
}
|
|
1608
|
+
const rollbackTs = Date.now();
|
|
1609
|
+
await restartBridge(paths);
|
|
1610
|
+
const rollbackHealth = await healthProbe(paths.home, rollbackTs, 3e4);
|
|
1611
|
+
if (rollbackHealth.healthy) {
|
|
1612
|
+
process.stderr.write(`\u26A0\uFE0F Rolled back to previous version. Investigate ${brokenDir} for the failure.
|
|
1732
1613
|
`);
|
|
1614
|
+
rmSync3(brokenDir, { recursive: true, force: true });
|
|
1615
|
+
return 1;
|
|
1733
1616
|
}
|
|
1734
|
-
|
|
1735
|
-
|
|
1617
|
+
process.stderr.write(`\u274C Rollback also failed. Manual intervention required.
|
|
1618
|
+
`);
|
|
1619
|
+
process.stderr.write(` Check: ~/.abtars/logs/bridge.log
|
|
1620
|
+
`);
|
|
1621
|
+
process.stderr.write(` Broken version preserved at: ${brokenDir}
|
|
1622
|
+
`);
|
|
1623
|
+
return 2;
|
|
1624
|
+
} finally {
|
|
1625
|
+
await release();
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
async function copyAbmind(stagedPath, repoRoot) {
|
|
1629
|
+
const candidates = [
|
|
1630
|
+
process.env["ABMIND_REPO"],
|
|
1631
|
+
join11(repoRoot, "..", "abmind"),
|
|
1632
|
+
join11(process.env["HOME"] ?? "", "abmind")
|
|
1633
|
+
].filter(Boolean);
|
|
1634
|
+
for (const src of candidates) {
|
|
1635
|
+
const distDir = join11(src, "dist");
|
|
1636
|
+
if (existsSync9(distDir)) {
|
|
1637
|
+
const dest = join11(stagedPath, "node_modules", "abmind");
|
|
1638
|
+
mkdirSync2(dest, { recursive: true });
|
|
1639
|
+
cpSync(distDir, join11(dest, "dist"), { recursive: true });
|
|
1640
|
+
if (existsSync9(join11(src, "package.json"))) cpSync(join11(src, "package.json"), join11(dest, "package.json"));
|
|
1641
|
+
if (existsSync9(join11(src, "prompts"))) cpSync(join11(src, "prompts"), join11(dest, "prompts"), { recursive: true });
|
|
1642
|
+
process.stdout.write(`\u2713 abmind copied from ${src}
|
|
1643
|
+
`);
|
|
1644
|
+
return;
|
|
1736
1645
|
}
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1646
|
+
}
|
|
1647
|
+
process.stdout.write(`\u26A0 abmind source not found (checked: ${candidates.join(", ")}). Bridge may fail to start.
|
|
1648
|
+
`);
|
|
1649
|
+
}
|
|
1650
|
+
async function postSwapHousekeeping(paths, repoRoot, _staged) {
|
|
1651
|
+
const { loadManifest } = await import("./install-manifest-QRWID3KZ.js");
|
|
1652
|
+
const installManifest = loadManifest(paths.app);
|
|
1653
|
+
const repoScripts = join11(repoRoot, "scripts");
|
|
1654
|
+
const destScripts = join11(paths.home, "scripts");
|
|
1655
|
+
await mkdir4(destScripts, { recursive: true });
|
|
1656
|
+
const allScriptFiles = await readdir(repoScripts).catch(() => []);
|
|
1657
|
+
const matchesInclude = (name) => installManifest.scripts.include.some((pattern) => name.endsWith(pattern.replace("*", "")));
|
|
1658
|
+
const scriptFiles = allScriptFiles.filter(matchesInclude);
|
|
1659
|
+
const isExecutable = (name) => name.endsWith(installManifest.scripts.executable.replace("*", ""));
|
|
1660
|
+
for (const name of scriptFiles) {
|
|
1661
|
+
await copyFile2(join11(repoScripts, name), join11(destScripts, name));
|
|
1662
|
+
if (isExecutable(name)) await chmod(join11(destScripts, name), 493);
|
|
1663
|
+
}
|
|
1664
|
+
process.stdout.write(`\u2713 scripts refreshed (${scriptFiles.length} files)
|
|
1665
|
+
`);
|
|
1666
|
+
const { writeWrapper } = await import("./install-SH4UVUXQ.js");
|
|
1667
|
+
await mkdir4(paths.bin, { recursive: true });
|
|
1668
|
+
for (const name of installManifest.cliWrappers) {
|
|
1669
|
+
await writeWrapper(paths.bin, name, paths.app, false);
|
|
1670
|
+
}
|
|
1671
|
+
process.stdout.write(`\u2713 wrappers refreshed (${installManifest.cliWrappers.length} files)
|
|
1672
|
+
`);
|
|
1673
|
+
const skillsCoreSrc = join11(paths.app, "core", "skills");
|
|
1674
|
+
const skillsCoreDst = join11(paths.home, "skills", "core");
|
|
1675
|
+
if (existsSync9(skillsCoreSrc)) {
|
|
1676
|
+
rmSync3(skillsCoreDst, { recursive: true, force: true });
|
|
1677
|
+
cpSync(skillsCoreSrc, skillsCoreDst, { recursive: true });
|
|
1678
|
+
const files = readdirSync3(skillsCoreDst, { recursive: true });
|
|
1679
|
+
const count = files.filter((f) => f.endsWith("SKILL.md")).length;
|
|
1680
|
+
process.stdout.write(`\u2713 skills/core synced (${count} skills)
|
|
1681
|
+
`);
|
|
1682
|
+
}
|
|
1683
|
+
for (const d of ["custom", "downloaded", "self"]) {
|
|
1684
|
+
await mkdir4(join11(paths.home, "skills", d), { recursive: true });
|
|
1685
|
+
}
|
|
1686
|
+
const releaseConfig = join11(paths.app, "config");
|
|
1687
|
+
const destConfig = join11(paths.home, "config");
|
|
1688
|
+
if (existsSync9(releaseConfig)) {
|
|
1689
|
+
for (const f of readdirSync3(releaseConfig)) {
|
|
1690
|
+
const src = join11(releaseConfig, f);
|
|
1691
|
+
if (f.endsWith(".example")) {
|
|
1692
|
+
cpSync(src, join11(destConfig, f));
|
|
1693
|
+
const target = join11(destConfig, f.replace(".example", ""));
|
|
1694
|
+
if (!existsSync9(target)) cpSync(src, target);
|
|
1741
1695
|
}
|
|
1742
1696
|
}
|
|
1743
|
-
const
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1697
|
+
const defaultTransport = join11(releaseConfig, "transport.default.json");
|
|
1698
|
+
if (existsSync9(defaultTransport)) cpSync(defaultTransport, join11(destConfig, "transport.default.json"));
|
|
1699
|
+
}
|
|
1700
|
+
const transportJson = join11(paths.home, "config", "transport.json");
|
|
1701
|
+
if (existsSync9(transportJson)) {
|
|
1702
|
+
try {
|
|
1703
|
+
const tc = JSON.parse(readFileSync5(transportJson, "utf-8"));
|
|
1704
|
+
let cleared = false;
|
|
1705
|
+
for (const agent of Object.values(tc.agents ?? {})) {
|
|
1706
|
+
if (agent.demoted) {
|
|
1707
|
+
delete agent.demoted;
|
|
1708
|
+
delete agent.demotedReason;
|
|
1709
|
+
delete agent.demotedModel;
|
|
1710
|
+
cleared = true;
|
|
1711
|
+
}
|
|
1712
|
+
for (const fb of agent.fallbacks ?? []) {
|
|
1713
|
+
if (fb.demoted) {
|
|
1714
|
+
delete fb.demoted;
|
|
1715
|
+
delete fb.demotedReason;
|
|
1716
|
+
delete fb.demotedModel;
|
|
1717
|
+
cleared = true;
|
|
1718
|
+
}
|
|
1752
1719
|
}
|
|
1753
1720
|
}
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
const coreSrc = join10(staged.stagedPath, "core");
|
|
1758
|
-
const coreDst = join10(paths.home, "core");
|
|
1759
|
-
if (existsSync7(coreSrc)) {
|
|
1760
|
-
await mkdir4(coreDst, { recursive: true });
|
|
1761
|
-
cpSync(coreSrc, coreDst, { recursive: true });
|
|
1762
|
-
}
|
|
1763
|
-
if (serviceChanged) {
|
|
1764
|
-
if (process.platform === "darwin") {
|
|
1765
|
-
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
|
|
1766
|
-
`);
|
|
1767
|
-
} else {
|
|
1768
|
-
process.stdout.write(`\u26A0\uFE0F systemd service updated \u2014 reload with: systemctl --user daemon-reload && systemctl --user restart abtars-watchdog
|
|
1769
|
-
`);
|
|
1721
|
+
if (cleared) {
|
|
1722
|
+
const { writeFileSync: writeFileSync2 } = await import("node:fs");
|
|
1723
|
+
writeFileSync2(transportJson, JSON.stringify(tc, null, 2) + "\n");
|
|
1770
1724
|
}
|
|
1725
|
+
} catch {
|
|
1771
1726
|
}
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1727
|
+
}
|
|
1728
|
+
const doctorPath = join11(paths.home, "scripts", "doctor.sh");
|
|
1729
|
+
if (existsSync9(doctorPath)) {
|
|
1730
|
+
process.stdout.write("\n\u{1FA7A} Health check...\n");
|
|
1731
|
+
try {
|
|
1732
|
+
const { execSync } = await import("node:child_process");
|
|
1733
|
+
execSync(`bash "${doctorPath}" --fix`, { stdio: "inherit", timeout: 3e4 });
|
|
1734
|
+
} catch (err) {
|
|
1735
|
+
process.stderr.write(`\u26A0\uFE0F doctor --fix failed: ${err instanceof Error ? err.message : String(err)}
|
|
1777
1736
|
`);
|
|
1778
1737
|
}
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1738
|
+
}
|
|
1739
|
+
const { ensureInstallInvariants } = await import("./ensure-invariants-BJIEOSJ2.js");
|
|
1740
|
+
const invariantResults = await ensureInstallInvariants(process.cwd(), paths.home);
|
|
1741
|
+
if (invariantResults.length > 0) {
|
|
1742
|
+
process.stdout.write(`\u2713 invariants: ${invariantResults.join(", ")}
|
|
1743
|
+
`);
|
|
1744
|
+
}
|
|
1745
|
+
void hashFile;
|
|
1746
|
+
}
|
|
1747
|
+
async function restartBridge(paths) {
|
|
1748
|
+
const manifest = await readManifest(paths.manifest);
|
|
1749
|
+
const mode = manifest?.installMode;
|
|
1750
|
+
if (!mode) {
|
|
1751
|
+
process.stderr.write("\u274C installMode not set in manifest.json. Run 'abtars install' first.\n");
|
|
1752
|
+
return false;
|
|
1753
|
+
}
|
|
1754
|
+
if (mode === "supervised-daemon" || mode === "supervised") {
|
|
1755
|
+
process.stdout.write("\n\u267B\uFE0F Restarting bridge via watchdog...\n");
|
|
1756
|
+
const wdLock = join11(paths.home, "watchdog.lock");
|
|
1757
|
+
const wdPid = readJsonField2(wdLock, "pid");
|
|
1758
|
+
if (wdPid && wdPid > 0) {
|
|
1782
1759
|
try {
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1760
|
+
process.kill(wdPid, "SIGUSR1");
|
|
1761
|
+
process.stdout.write(` USR1 sent to watchdog (PID ${wdPid})
|
|
1762
|
+
`);
|
|
1763
|
+
return true;
|
|
1764
|
+
} catch {
|
|
1765
|
+
process.stdout.write(`\u26A0\uFE0F Could not signal watchdog (PID ${wdPid}).
|
|
1787
1766
|
`);
|
|
1788
1767
|
}
|
|
1789
1768
|
}
|
|
1790
|
-
const
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
process.stderr.write("\u274C installMode not set in manifest.json. Run 'abtars install' first.\n");
|
|
1794
|
-
return 1;
|
|
1795
|
-
}
|
|
1796
|
-
if (restartMode === "supervised-daemon" || restartMode === "supervised") {
|
|
1797
|
-
process.stdout.write("\nRestarting bridge via watchdog...\n");
|
|
1798
|
-
const wdLock = join10(paths.home, "watchdog.lock");
|
|
1799
|
-
const wdPid = readJsonField2(wdLock, "pid");
|
|
1800
|
-
if (wdPid && wdPid > 0) {
|
|
1801
|
-
try {
|
|
1802
|
-
process.kill(wdPid, "SIGUSR1");
|
|
1803
|
-
process.stdout.write(`\u267B\uFE0F USR1 sent to watchdog (PID ${wdPid}) \u2014 bridge will restart
|
|
1769
|
+
const { restart: restart3 } = await import("./abtars-restart.js");
|
|
1770
|
+
await restart3({ cold: true }).catch((err) => {
|
|
1771
|
+
process.stderr.write(`\u26A0\uFE0F Restart failed: ${err instanceof Error ? err.message : String(err)}
|
|
1804
1772
|
`);
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
process.stdout.write(` systemctl --user restart abtars-watchdog
|
|
1773
|
+
});
|
|
1774
|
+
return true;
|
|
1775
|
+
}
|
|
1776
|
+
process.stdout.write("\n\u267B\uFE0F Restarting bridge...\n");
|
|
1777
|
+
const { restart: restart2 } = await import("./abtars-restart.js");
|
|
1778
|
+
await restart2({ cold: true }).catch((err) => {
|
|
1779
|
+
process.stderr.write(`\u26A0\uFE0F Restart failed: ${err instanceof Error ? err.message : String(err)}
|
|
1813
1780
|
`);
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1781
|
+
});
|
|
1782
|
+
return true;
|
|
1783
|
+
}
|
|
1784
|
+
function printDryRun(paths, repoRoot, opts) {
|
|
1785
|
+
const { spawnSync: spawnSync4 } = __require("node:child_process");
|
|
1786
|
+
const commit = spawnSync4("git", ["rev-parse", "--short", "HEAD"], { cwd: repoRoot, encoding: "utf-8" }).stdout?.trim() ?? "?";
|
|
1787
|
+
const branch = spawnSync4("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: repoRoot, encoding: "utf-8" }).stdout?.trim() ?? "?";
|
|
1788
|
+
const wdLock = join11(paths.home, "watchdog.lock");
|
|
1789
|
+
const wdPid = readJsonField2(wdLock, "pid");
|
|
1790
|
+
process.stdout.write(`
|
|
1791
|
+
Dry run \u2014 no changes will be made.
|
|
1792
|
+
Source: ${opts.source} (commit ${commit} on ${branch})
|
|
1793
|
+
Staging to: ${paths.appStaging}
|
|
1794
|
+
Swap: app/ \u2192 app.prev/, app.staging/ \u2192 app/
|
|
1795
|
+
Config snap: config/ \u2192 config/.pre-update/ (3-slot rotation)
|
|
1796
|
+
Restart: ${wdPid ? `USR1 to watchdog (PID ${wdPid})` : "cold restart"}
|
|
1797
|
+
Health: poll bridge.lock for 60s
|
|
1798
|
+
On failure: auto-rollback (swap app/ \u2194 app.prev/)
|
|
1818
1799
|
`);
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1800
|
+
return 0;
|
|
1801
|
+
}
|
|
1802
|
+
async function checkForUpdates(home, opts) {
|
|
1803
|
+
const repoRoot = opts.repoRoot ?? process.cwd();
|
|
1804
|
+
if (!existsSync9(join11(repoRoot, ".git"))) {
|
|
1805
|
+
process.stderr.write("Not a git repository. --check requires a git checkout.\n");
|
|
1806
|
+
return 2;
|
|
1807
|
+
}
|
|
1808
|
+
const { spawnSync: spawnSync4 } = await import("node:child_process");
|
|
1809
|
+
spawnSync4("git", ["fetch", "--quiet"], { cwd: repoRoot });
|
|
1810
|
+
const result = spawnSync4("git", ["rev-list", "--count", "HEAD..origin/dev"], { cwd: repoRoot, encoding: "utf-8" });
|
|
1811
|
+
const ahead = parseInt(result.stdout?.trim() ?? "0", 10);
|
|
1812
|
+
const manifest = await readManifest(join11(home, "manifest.json"));
|
|
1813
|
+
process.stdout.write(`Current: ${manifest?.version ?? "unknown"} (deployed ${manifest?.activatedAt ?? "never"})
|
|
1822
1814
|
`);
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
} else {
|
|
1826
|
-
process.stdout.write("\nRestarting bridge...\n");
|
|
1827
|
-
const { restart: restart2 } = await import("./abtars-restart.js");
|
|
1828
|
-
await restart2({ cold: true }).catch((err) => {
|
|
1829
|
-
process.stderr.write(`\u26A0\uFE0F Restart failed: ${err instanceof Error ? err.message : String(err)}
|
|
1815
|
+
if (ahead === 0) {
|
|
1816
|
+
process.stdout.write(`Remote: up to date
|
|
1830
1817
|
`);
|
|
1831
|
-
});
|
|
1832
|
-
}
|
|
1833
|
-
const { printHealthSummary } = await import("./health-check-RJ2SUJYL.js");
|
|
1834
|
-
printHealthSummary(paths.home);
|
|
1835
1818
|
return 0;
|
|
1836
|
-
} finally {
|
|
1837
|
-
await release();
|
|
1838
1819
|
}
|
|
1820
|
+
process.stdout.write(`Remote: dev is ${ahead} commit${ahead === 1 ? "" : "s"} ahead
|
|
1821
|
+
`);
|
|
1822
|
+
process.stdout.write(`Action: run 'abtars update' to apply
|
|
1823
|
+
`);
|
|
1824
|
+
return 2;
|
|
1839
1825
|
}
|
|
1840
1826
|
|
|
1841
1827
|
// src/cli/abtars.ts
|
|
@@ -1905,9 +1891,7 @@ async function main(argv) {
|
|
|
1905
1891
|
allowAbmindMismatch: flags.get("allow-abmind-mismatch") === true
|
|
1906
1892
|
});
|
|
1907
1893
|
case "rollback":
|
|
1908
|
-
return await rollback(
|
|
1909
|
-
to: typeof flags.get("to") === "string" ? flags.get("to") : void 0
|
|
1910
|
-
});
|
|
1894
|
+
return await rollback();
|
|
1911
1895
|
case "backup":
|
|
1912
1896
|
return await backup(typeof flags.get("output") === "string" ? flags.get("output") : void 0);
|
|
1913
1897
|
case "restore": {
|