abtars 0.1.0-alpha.23 → 0.1.0-alpha.24

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.
@@ -2,7 +2,7 @@
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-7CHLS36W.js";
5
+ } from "./chunk-XMJ76XLE.js";
6
6
  import {
7
7
  RETENTION,
8
8
  acquireLock,
@@ -1676,7 +1676,7 @@ Update complete: ${staged.version}
1676
1676
  } catch {
1677
1677
  }
1678
1678
  }
1679
- const { loadManifest } = await import("./install-manifest-ZETY4AFS.js");
1679
+ const { loadManifest } = await import("./install-manifest-QRWID3KZ.js");
1680
1680
  const sourceRoot = staged.stagedPath;
1681
1681
  const installManifest = loadManifest(sourceRoot);
1682
1682
  const repoScripts = join9(sourceRoot, "scripts");
@@ -1726,7 +1726,7 @@ Update complete: ${staged.version}
1726
1726
  }
1727
1727
  process.stdout.write(`\u2713 scripts refreshed (${scriptFiles.length} files)
1728
1728
  `);
1729
- const { writeWrapper } = await import("./install-24XR5FO5.js");
1729
+ const { writeWrapper } = await import("./install-K67U3WLX.js");
1730
1730
  await mkdir4(paths.bin, { recursive: true });
1731
1731
  for (const name of installManifest.cliWrappers) {
1732
1732
  await writeWrapper(paths.bin, name, paths.current, false);
@@ -1783,7 +1783,7 @@ Update complete: ${staged.version}
1783
1783
  }
1784
1784
  }
1785
1785
  void hashFile;
1786
- const { ensureInstallInvariants } = await import("./ensure-invariants-KUXIW73S.js");
1786
+ const { ensureInstallInvariants } = await import("./ensure-invariants-BJIEOSJ2.js");
1787
1787
  const invariantResults = await ensureInstallInvariants(process.cwd(), paths.home);
1788
1788
  if (invariantResults.length > 0) {
1789
1789
  process.stdout.write(`\u2713 invariants: ${invariantResults.join(", ")}
@@ -0,0 +1,386 @@
1
+ #!/usr/bin/env node
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
+ emptyManifest,
5
+ packagePaths,
6
+ readManifest,
7
+ resolveUserBinDir,
8
+ writeManifest
9
+ } from "./chunk-HXJRZWKA.js";
10
+
11
+ // src/cli/commands/install.ts
12
+ import { mkdir, readFile, stat, symlink, writeFile } from "node:fs/promises";
13
+ import { existsSync, readFileSync, readdirSync, copyFileSync, mkdirSync } from "node:fs";
14
+ import { hostname, homedir } from "node:os";
15
+ import { basename, dirname, join } from "node:path";
16
+ async function exists(p) {
17
+ try {
18
+ await stat(p);
19
+ return true;
20
+ } catch {
21
+ return false;
22
+ }
23
+ }
24
+ async function existsAny(p) {
25
+ try {
26
+ const { lstat } = await import("node:fs/promises");
27
+ await lstat(p);
28
+ return true;
29
+ } catch {
30
+ return false;
31
+ }
32
+ }
33
+ async function isSymlink(p) {
34
+ try {
35
+ const { lstat } = await import("node:fs/promises");
36
+ const s = await lstat(p);
37
+ return s.isSymbolicLink();
38
+ } catch {
39
+ return false;
40
+ }
41
+ }
42
+ async function createSkeleton(home, dryRun) {
43
+ const { loadManifest } = await import("./install-manifest-QRWID3KZ.js");
44
+ const manifest = loadManifest();
45
+ const dirs = manifest.directories.map((d) => join(home, d.path));
46
+ if (dryRun) {
47
+ process.stdout.write(`[dry-run] mkdir -p:
48
+ ${dirs.join("\n ")}
49
+ `);
50
+ return;
51
+ }
52
+ for (const d of manifest.directories) {
53
+ await mkdir(join(home, d.path), { recursive: true, mode: d.mode ? parseInt(d.mode, 8) : void 0 });
54
+ }
55
+ }
56
+ async function seedConfig(repoRoot, _configDir, dryRun, home) {
57
+ const { loadManifest } = await import("./install-manifest-QRWID3KZ.js");
58
+ const manifest = loadManifest(repoRoot);
59
+ const seeded = [];
60
+ for (const seed of manifest.configSeeds) {
61
+ const src = join(repoRoot, seed.source);
62
+ const dst = join(home, seed.dest);
63
+ if (!await exists(src)) continue;
64
+ if (await exists(dst)) continue;
65
+ if (dryRun) {
66
+ seeded.push(`[dry-run] cp ${src} ${dst}`);
67
+ continue;
68
+ }
69
+ const content = await readFile(src, "utf-8");
70
+ await mkdir(dirname(dst), { recursive: true });
71
+ await writeFile(dst, content, { mode: seed.mode ? parseInt(seed.mode, 8) : 420 });
72
+ seeded.push(basename(dst));
73
+ }
74
+ return seeded;
75
+ }
76
+ async function reconcilePathLink(binDir, userBinDir, name, force, dryRun) {
77
+ const linkPath = join(userBinDir, name);
78
+ const targetPath = join(binDir, name);
79
+ const linkExists = await existsAny(linkPath);
80
+ if (!linkExists) {
81
+ if (dryRun) return { action: `[dry-run] ln -s ${targetPath} ${linkPath}` };
82
+ await symlink(targetPath, linkPath);
83
+ return { action: `created ${linkPath}` };
84
+ }
85
+ if (await isSymlink(linkPath)) {
86
+ const { readlink, unlink } = await import("node:fs/promises");
87
+ const current = await readlink(linkPath);
88
+ const ownsIt = current === targetPath;
89
+ if (ownsIt) {
90
+ if (dryRun) return { action: `[dry-run] overwrite ${linkPath} (we own it)` };
91
+ await unlink(linkPath);
92
+ await symlink(targetPath, linkPath);
93
+ return { action: `updated ${linkPath}` };
94
+ }
95
+ if (force) {
96
+ if (dryRun) return { action: `[dry-run] --force overwrite ${linkPath} (currently -> ${current})` };
97
+ await unlink(linkPath);
98
+ await symlink(targetPath, linkPath);
99
+ return { action: `forced overwrite ${linkPath} (was -> ${current})` };
100
+ }
101
+ return {
102
+ action: "refused",
103
+ message: `${linkPath} is a symlink to ${current} (not ours). Pass --force to overwrite.`
104
+ };
105
+ }
106
+ if (force) {
107
+ if (dryRun) return { action: `[dry-run] --force overwrite ${linkPath} (regular file)` };
108
+ const { unlink } = await import("node:fs/promises");
109
+ await unlink(linkPath);
110
+ await symlink(targetPath, linkPath);
111
+ return { action: `forced overwrite ${linkPath} (was regular file)` };
112
+ }
113
+ return {
114
+ action: "refused",
115
+ message: `${linkPath} exists as a regular file (not our symlink). Pass --force to overwrite.`
116
+ };
117
+ }
118
+ async function writeWrapper(binDir, name, currentLink, dryRun) {
119
+ const bundleFile = name === "abtars" ? "abtars-cli.js" : `${name}.js`;
120
+ const target = join(currentLink, "bundle", bundleFile);
121
+ const distFile = name === "abtars" ? "abtars.js" : `${name}.js`;
122
+ const fallback = join(currentLink, "dist", "cli", distFile);
123
+ const content = `#!/usr/bin/env bash
124
+ if [ -f "${target}" ]; then
125
+ exec node "${target}" "$@"
126
+ elif [ -f "${fallback}" ]; then
127
+ exec node "${fallback}" "$@"
128
+ else
129
+ # No local release \u2014 fall through to npm global binary
130
+ GLOBAL_BIN="$(npm root -g 2>/dev/null)/abtars/bundle/${bundleFile}"
131
+ if [ -f "$GLOBAL_BIN" ]; then
132
+ exec node "$GLOBAL_BIN" "$@"
133
+ fi
134
+ echo "abtars: no release staged. Run 'abtars install' first." >&2
135
+ exit 1
136
+ fi
137
+ `;
138
+ const path = join(binDir, name);
139
+ if (dryRun) {
140
+ process.stdout.write(`[dry-run] write wrapper ${path} -> node ${target} (fallback: ${fallback})
141
+ `);
142
+ return;
143
+ }
144
+ await writeFile(path, content, { mode: 493 });
145
+ }
146
+ function isPathOnPATH(userBinDir) {
147
+ const PATH = process.env["PATH"] ?? "";
148
+ return PATH.split(":").some((p) => p === userBinDir);
149
+ }
150
+ async function install(opts) {
151
+ const paths = packagePaths("abtars");
152
+ const home = paths.home;
153
+ const userBinDir = resolveUserBinDir();
154
+ const repoRoot = process.cwd();
155
+ const { initInstallLog, logInstall, logInstallHeader } = await import("./install-log-Q6RUHKWC.js");
156
+ initInstallLog(home);
157
+ logInstallHeader("install");
158
+ const _origWrite = process.stdout.write.bind(process.stdout);
159
+ process.stdout.write = (chunk, ...args) => {
160
+ if (typeof chunk === "string" && (chunk.startsWith("\u2713") || chunk.startsWith("\u26A0"))) logInstall(chunk.trimEnd());
161
+ return _origWrite(chunk, ...args);
162
+ };
163
+ const homeExists = await exists(home);
164
+ const manifest = homeExists ? await readManifest(paths.manifest) : null;
165
+ if (homeExists && manifest && !opts.force && !opts.restore) {
166
+ process.stderr.write(
167
+ `~/.abtars already installed at version ${manifest.version || "(unset)"}.
168
+ Use 'abtars update' to upgrade, or --force to re-seed missing config.
169
+ `
170
+ );
171
+ return 2;
172
+ }
173
+ await createSkeleton(home, opts.dryRun);
174
+ process.stdout.write(`\u2713 skeleton at ${home}
175
+ `);
176
+ const kiroAgentsDir = join(homedir(), ".kiro", "agents");
177
+ const professorJson = join(kiroAgentsDir, "professor.json");
178
+ if (!opts.dryRun) {
179
+ await mkdir(kiroAgentsDir, { recursive: true });
180
+ if (!await exists(professorJson)) {
181
+ await writeFile(professorJson, JSON.stringify({
182
+ name: "professor",
183
+ description: "Abtars bridge agent",
184
+ tools: ["*"],
185
+ allowedTools: ["@builtin"],
186
+ toolsSettings: { shell: { autoAllowReadonly: true } },
187
+ includeMcpJson: true
188
+ }, null, 2) + "\n");
189
+ process.stdout.write(`\u2713 kiro agent: ${professorJson}
190
+ `);
191
+ }
192
+ } else {
193
+ process.stdout.write(`[dry-run] create ${professorJson}
194
+ `);
195
+ }
196
+ const identityKey = join(paths.config, "identity.key");
197
+ const identityPub = join(paths.config, "identity.pub");
198
+ if (!opts.dryRun && !await exists(identityKey)) {
199
+ const { generateKeyPairSync } = await import("node:crypto");
200
+ const { privateKey, publicKey } = generateKeyPairSync("ed25519");
201
+ await writeFile(identityKey, privateKey.export({ format: "der", type: "pkcs8" }).toString("base64"));
202
+ await writeFile(identityPub, publicKey.export({ format: "der", type: "spki" }).toString("base64"));
203
+ const { chmodSync } = await import("node:fs");
204
+ chmodSync(identityKey, 384);
205
+ process.stdout.write(`\u2713 identity keypair generated
206
+ `);
207
+ }
208
+ const identityCrt = join(paths.config, "identity.crt");
209
+ const identityTlsKey = join(paths.config, "identity.tls.key");
210
+ if (!opts.dryRun && !await exists(identityCrt)) {
211
+ const { execSync } = await import("node:child_process");
212
+ const { chmodSync } = await import("node:fs");
213
+ const agentName = hostname();
214
+ try {
215
+ execSync(`openssl req -x509 -newkey ed25519 -keyout identity.tls.key -out identity.crt -days 3650 -nodes -subj "/CN=${agentName}"`, { cwd: paths.config, stdio: "ignore" });
216
+ chmodSync(identityTlsKey, 384);
217
+ process.stdout.write(`\u2713 TLS certificate generated (Ed25519, 10yr)
218
+ `);
219
+ } catch (err) {
220
+ process.stderr.write(`\u26A0 TLS cert generation failed (openssl not found?). Agent-api will start without TLS.
221
+ `);
222
+ }
223
+ }
224
+ const seeded = await seedConfig(repoRoot, paths.config, opts.dryRun, home);
225
+ if (seeded.length > 0) {
226
+ process.stdout.write(`\u2713 seeded config: ${seeded.join(", ")}
227
+ `);
228
+ }
229
+ const { loadManifest: loadInstallManifest } = await import("./install-manifest-QRWID3KZ.js");
230
+ const installManifest = loadInstallManifest(repoRoot);
231
+ if (!opts.dryRun) {
232
+ await mkdir(paths.bin, { recursive: true });
233
+ }
234
+ for (const name of installManifest.cliWrappers) {
235
+ await writeWrapper(paths.bin, name, paths.current, opts.dryRun);
236
+ }
237
+ process.stdout.write(`\u2713 wrappers in ${paths.bin}
238
+ `);
239
+ if (!opts.dryRun) await mkdir(userBinDir, { recursive: true });
240
+ const refused = [];
241
+ for (const name of installManifest.cliWrappers) {
242
+ const r = await reconcilePathLink(paths.bin, userBinDir, name, opts.force, opts.dryRun);
243
+ if (r.action === "refused") {
244
+ refused.push(r.message ?? name);
245
+ }
246
+ }
247
+ if (refused.length > 0) {
248
+ process.stderr.write(`
249
+ PATH symlink conflicts:
250
+ ${refused.join("\n ")}
251
+ `);
252
+ return 4;
253
+ }
254
+ process.stdout.write(`\u2713 PATH symlinks in ${userBinDir}
255
+ `);
256
+ if (!isPathOnPATH(userBinDir)) {
257
+ process.stderr.write(
258
+ `
259
+ Warning: ${userBinDir} is not on $PATH. Add to your shell config:
260
+ export PATH="${userBinDir}:$PATH"
261
+ `
262
+ );
263
+ }
264
+ const manifestAfter = await readManifest(paths.manifest);
265
+ if (manifestAfter === null && !opts.dryRun) {
266
+ await writeManifest(paths.manifest, {
267
+ ...emptyManifest("abtars", hostname()),
268
+ version: "",
269
+ preMigrationBackup: null
270
+ });
271
+ process.stdout.write(`\u2713 manifest initialized at ${paths.manifest}
272
+ `);
273
+ }
274
+ const manifestForMode = await readManifest(paths.manifest);
275
+ const existingMode = manifestForMode?.installMode;
276
+ const mode = opts.mode ?? existingMode ?? "supervised";
277
+ if (manifestForMode) {
278
+ await writeManifest(paths.manifest, { ...manifestForMode, installMode: mode });
279
+ }
280
+ process.stdout.write(`\u2713 install mode: ${mode}
281
+ `);
282
+ if (opts.restore) {
283
+ const { spawnSync } = await import("node:child_process");
284
+ const { existsSync: fileExists } = await import("node:fs");
285
+ const zipPath = opts.restore;
286
+ if (!fileExists(zipPath)) {
287
+ process.stderr.write(`error: backup file not found: ${zipPath}
288
+ `);
289
+ return 1;
290
+ }
291
+ const tmpDir = join(process.env["TMPDIR"] ?? "/tmp", `abtars-restore-${Date.now()}`);
292
+ const unzip = spawnSync("unzip", ["-o", zipPath, "-d", tmpDir], { encoding: "utf-8" });
293
+ if (unzip.status !== 0) {
294
+ process.stderr.write(`error: unzip failed: ${unzip.stderr}
295
+ `);
296
+ return 1;
297
+ }
298
+ const abSrc = join(tmpDir, "abtars");
299
+ if (fileExists(abSrc)) {
300
+ spawnSync("cp", ["-r", ...readdirSync(abSrc).map((f) => join(abSrc, f)), home], { stdio: "inherit" });
301
+ process.stdout.write(`\u2713 restored abtars config
302
+ `);
303
+ }
304
+ const abmindHome = process.env["ABMIND_HOME"] ?? join(dirname(home), ".abmind");
305
+ const abmindSrc = join(tmpDir, "abmind");
306
+ if (fileExists(abmindSrc)) {
307
+ spawnSync("cp", ["-r", ...readdirSync(abmindSrc).map((f) => join(abmindSrc, f)), abmindHome], { stdio: "inherit" });
308
+ process.stdout.write(`\u2713 restored abmind data
309
+ `);
310
+ }
311
+ spawnSync("rm", ["-rf", tmpDir]);
312
+ process.stdout.write(`
313
+ Restore complete.
314
+ `);
315
+ process.stdout.write(`Next: 'abtars update' to build and activate.
316
+ `);
317
+ return 0;
318
+ }
319
+ if (mode === "supervised") {
320
+ const { execSync } = await import("node:child_process");
321
+ if (process.platform === "darwin") {
322
+ const plistSrc = join(home, "scripts", "com.abtars.watchdog.plist");
323
+ const plistDst = join(homedir(), "Library", "LaunchAgents", "com.abtars.watchdog.plist");
324
+ if (existsSync(plistSrc)) {
325
+ const content = readFileSync(plistSrc, "utf-8").replaceAll("{{HOME}}", homedir());
326
+ const { writeFileSync } = await import("node:fs");
327
+ writeFileSync(plistDst, content);
328
+ try {
329
+ execSync(`launchctl load "${plistDst}"`, { stdio: "ignore" });
330
+ } catch {
331
+ }
332
+ process.stdout.write(`\u2713 watchdog LaunchAgent loaded
333
+ `);
334
+ }
335
+ } else if (process.platform === "linux") {
336
+ const unitSrc = join(home, "scripts", "abtars-watchdog.service");
337
+ const unitDir = join(homedir(), ".config", "systemd", "user");
338
+ if (existsSync(unitSrc)) {
339
+ mkdirSync(unitDir, { recursive: true });
340
+ copyFileSync(unitSrc, join(unitDir, "abtars-watchdog.service"));
341
+ try {
342
+ execSync("systemctl --user daemon-reload && systemctl --user enable --now abtars-watchdog", { stdio: "ignore" });
343
+ } catch {
344
+ }
345
+ process.stdout.write(`\u2713 watchdog systemd user service enabled
346
+ `);
347
+ }
348
+ }
349
+ }
350
+ process.stdout.write(`
351
+ Install complete.
352
+ `);
353
+ if (!manifestAfter || manifestAfter.version === "") {
354
+ process.stdout.write(`Next: 'abtars update' to build and activate the first release.
355
+ `);
356
+ } else {
357
+ process.stdout.write(`
358
+ \u2500\u2500 Next steps \u2500\u2500
359
+ `);
360
+ process.stdout.write(` 1. Run 'abtars onboard' to configure Telegram token + model provider
361
+ `);
362
+ process.stdout.write(` 2. (Optional) Install Ollama for memory embeddings: curl -fsSL https://ollama.com/install.sh | sh
363
+ `);
364
+ process.stdout.write(` 3. Start the bridge: 'abtars restart' or use the watchdog
365
+
366
+ `);
367
+ }
368
+ const { printHealthSummary } = await import("./health-check-RJ2SUJYL.js");
369
+ printHealthSummary(paths.home);
370
+ if (!opts.dryRun && opts.restore) {
371
+ const { validateMinimumViability, formatValidationError } = await import("./install-validate-H74LUCE2.js");
372
+ const validation = validateMinimumViability(paths.config);
373
+ if (!validation.ok) {
374
+ const invocation = `abtars install --restore ${opts.restore}`;
375
+ process.stderr.write("\n" + formatValidationError(validation, invocation) + "\n");
376
+ return 1;
377
+ }
378
+ }
379
+ return 0;
380
+ }
381
+
382
+ export {
383
+ writeWrapper,
384
+ install
385
+ };
386
+ //# sourceMappingURL=chunk-XMJ76XLE.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/cli/commands/install.ts"],
4
+ "sourcesContent": ["/**\n * `abtars install [--upgrade]` \u2014 first-time setup.\n *\n * Phase 1 behavior:\n * - No existing ~/.abtars: create dirs, seed config/ from .env.example,\n * create PATH symlinks. Does NOT run onboard (Phase 3).\n * - Existing ~/.abtars with flat layout (pre-158): refuse unless\n * --upgrade, then run migration 003-flat-to-releases (Phase 1c).\n * - Existing ~/.abtars with new layout: refuse unless --force (which\n * re-seeds missing config and reconciles symlinks, no code changes).\n */\n\nimport { mkdir, readFile, stat, symlink, writeFile } from 'node:fs/promises';\nimport { existsSync, readFileSync, readdirSync, copyFileSync, mkdirSync } from 'node:fs';\nimport { hostname, homedir } from 'node:os';\nimport { basename, dirname, join } from 'node:path';\nimport { emptyManifest, packagePaths, readManifest, resolveUserBinDir, writeManifest } from '../deploy-lib-import.js';\n\nexport interface InstallOptions {\n readonly restore?: string;\n readonly force: boolean;\n readonly dryRun: boolean;\n readonly mode?: \"simple\" | \"supervised\";\n}\n\n// CLI wrappers are read from install-manifest.json at runtime.\n// Each is a thin wrapper that invokes `node current/dist/cli/<name>.js \"$@\"`.\n// Regenerated on every install / flat-to-releases migration.\n\nasync function exists(p: string): Promise<boolean> {\n try {\n await stat(p);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * True if `p` exists as any filesystem entry \u2014 regular file, directory,\n * symlink (including dangling). Use this for collision checks in\n * reconcilePathLink, where a dangling symlink still occupies the inode\n * and would cause EEXIST on symlink(). `exists()` above uses stat() which\n * follows symlinks and returns false on dangling ones; that's wrong for\n * collision detection.\n */\nasync function existsAny(p: string): Promise<boolean> {\n try {\n const { lstat } = await import('node:fs/promises');\n await lstat(p);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function isSymlink(p: string): Promise<boolean> {\n try {\n const { lstat } = await import('node:fs/promises');\n const s = await lstat(p);\n return s.isSymbolicLink();\n } catch {\n return false;\n }\n}\n\nasync function createSkeleton(home: string, dryRun: boolean): Promise<void> {\n const { loadManifest } = await import('../install-manifest.js');\n const manifest = loadManifest();\n const dirs = manifest.directories.map(d => join(home, d.path));\n if (dryRun) {\n process.stdout.write(`[dry-run] mkdir -p:\\n ${dirs.join('\\n ')}\\n`);\n return;\n }\n for (const d of manifest.directories) {\n await mkdir(join(home, d.path), { recursive: true, mode: d.mode ? parseInt(d.mode, 8) : undefined });\n }\n}\n\nasync function seedConfig(repoRoot: string, _configDir: string, dryRun: boolean, home: string): Promise<readonly string[]> {\n const { loadManifest } = await import('../install-manifest.js');\n const manifest = loadManifest(repoRoot);\n const seeded: string[] = [];\n for (const seed of manifest.configSeeds) {\n const src = join(repoRoot, seed.source);\n const dst = join(home, seed.dest);\n if (!(await exists(src))) continue;\n if (await exists(dst)) continue;\n if (dryRun) {\n seeded.push(`[dry-run] cp ${src} ${dst}`);\n continue;\n }\n const content = await readFile(src, 'utf-8');\n await mkdir(dirname(dst), { recursive: true });\n await writeFile(dst, content, { mode: seed.mode ? parseInt(seed.mode, 8) : 0o644 });\n seeded.push(basename(dst));\n }\n return seeded;\n}\n\n/**\n * Reconcile a single PATH symlink at ~/.local/bin/<name>.\n * Policy (plan \u00A7\"PATH symlink collision\"):\n * - Missing \u2192 create\n * - Symlink pointing into our own ~/.abtars/bin/ \u2192 overwrite\n * - Anything else \u2192 refuse with message, unless force\n */\nasync function reconcilePathLink(\n binDir: string,\n userBinDir: string,\n name: string,\n force: boolean,\n dryRun: boolean,\n): Promise<{ action: string; message?: string }> {\n const linkPath = join(userBinDir, name);\n const targetPath = join(binDir, name);\n const linkExists = await existsAny(linkPath);\n if (!linkExists) {\n if (dryRun) return { action: `[dry-run] ln -s ${targetPath} ${linkPath}` };\n await symlink(targetPath, linkPath);\n return { action: `created ${linkPath}` };\n }\n if (await isSymlink(linkPath)) {\n const { readlink, unlink } = await import('node:fs/promises');\n const current = await readlink(linkPath);\n // \"We own it\" means: points at THIS install's bin dir, not a fuzzy match\n // on any path containing /.abtars/bin/. A smoke-test install at\n // ABTARS_HOME=~/.cache/ab-smoke-.../ must not clobber the real\n // ~/.abtars symlinks. Compare absolute paths directly.\n const ownsIt = current === targetPath;\n if (ownsIt) {\n if (dryRun) return { action: `[dry-run] overwrite ${linkPath} (we own it)` };\n await unlink(linkPath);\n await symlink(targetPath, linkPath);\n return { action: `updated ${linkPath}` };\n }\n if (force) {\n if (dryRun) return { action: `[dry-run] --force overwrite ${linkPath} (currently -> ${current})` };\n await unlink(linkPath);\n await symlink(targetPath, linkPath);\n return { action: `forced overwrite ${linkPath} (was -> ${current})` };\n }\n return {\n action: 'refused',\n message: `${linkPath} is a symlink to ${current} (not ours). Pass --force to overwrite.`,\n };\n }\n if (force) {\n if (dryRun) return { action: `[dry-run] --force overwrite ${linkPath} (regular file)` };\n const { unlink } = await import('node:fs/promises');\n await unlink(linkPath);\n await symlink(targetPath, linkPath);\n return { action: `forced overwrite ${linkPath} (was regular file)` };\n }\n return {\n action: 'refused',\n message: `${linkPath} exists as a regular file (not our symlink). Pass --force to overwrite.`,\n };\n}\n\nexport async function writeWrapper(binDir: string, name: string, currentLink: string, dryRun: boolean): Promise<void> {\n const bundleFile = name === 'abtars' ? 'abtars-cli.js' : `${name}.js`;\n const target = join(currentLink, 'bundle', bundleFile);\n // Fallback: pre-bundle dist/ layout (tsc build) for installs that haven't migrated yet.\n const distFile = name === 'abtars' ? 'abtars.js' : `${name}.js`;\n const fallback = join(currentLink, 'dist', 'cli', distFile);\n const content = `#!/usr/bin/env bash\nif [ -f \"${target}\" ]; then\n exec node \"${target}\" \"$@\"\nelif [ -f \"${fallback}\" ]; then\n exec node \"${fallback}\" \"$@\"\nelse\n # No local release \u2014 fall through to npm global binary\n GLOBAL_BIN=\"$(npm root -g 2>/dev/null)/abtars/bundle/${bundleFile}\"\n if [ -f \"$GLOBAL_BIN\" ]; then\n exec node \"$GLOBAL_BIN\" \"$@\"\n fi\n echo \"abtars: no release staged. Run 'abtars install' first.\" >&2\n exit 1\nfi\n`;\n const path = join(binDir, name);\n if (dryRun) {\n process.stdout.write(`[dry-run] write wrapper ${path} -> node ${target} (fallback: ${fallback})\\n`);\n return;\n }\n await writeFile(path, content, { mode: 0o755 });\n}\n\nfunction isPathOnPATH(userBinDir: string): boolean {\n const PATH = process.env['PATH'] ?? '';\n return PATH.split(':').some((p) => p === userBinDir);\n}\n\nexport async function install(opts: InstallOptions): Promise<number> {\n const paths = packagePaths('abtars');\n const home = paths.home;\n const userBinDir = resolveUserBinDir();\n const repoRoot = process.cwd();\n\n // Install log (#718)\n const { initInstallLog, logInstall, logInstallHeader } = await import(\"../install-log.js\");\n initInstallLog(home);\n logInstallHeader(\"install\");\n const _origWrite = process.stdout.write.bind(process.stdout);\n process.stdout.write = ((chunk: any, ...args: any[]) => {\n if (typeof chunk === \"string\" && (chunk.startsWith(\"\u2713\") || chunk.startsWith(\"\u26A0\"))) logInstall(chunk.trimEnd());\n return _origWrite(chunk, ...args);\n }) as typeof process.stdout.write;\n\n const homeExists = await exists(home);\n const manifest = homeExists ? await readManifest(paths.manifest) : null;\n\n if (homeExists && manifest && !opts.force && !opts.restore) {\n process.stderr.write(\n `~/.abtars already installed at version ${manifest.version || '(unset)'}.\\nUse 'abtars update' to upgrade, or --force to re-seed missing config.\\n`,\n );\n return 2;\n }\n\n // Create skeleton (idempotent)\n await createSkeleton(home, opts.dryRun);\n process.stdout.write(`\u2713 skeleton at ${home}\\n`);\n\n // Core templates: abmind seeds its own on first boot (#427 ensureInitialized).\n // No longer seeded by abtars install.\n\n // Create kiro-cli agent config \u2014 ACP transport needs ~/.kiro/agents/professor.json\n const kiroAgentsDir = join(homedir(), '.kiro', 'agents');\n const professorJson = join(kiroAgentsDir, 'professor.json');\n if (!opts.dryRun) {\n await mkdir(kiroAgentsDir, { recursive: true });\n if (!(await exists(professorJson))) {\n await writeFile(professorJson, JSON.stringify({\n name: \"professor\",\n description: \"Abtars bridge agent\",\n tools: [\"*\"],\n allowedTools: [\"@builtin\"],\n toolsSettings: { shell: { autoAllowReadonly: true } },\n includeMcpJson: true,\n }, null, 2) + '\\n');\n process.stdout.write(`\u2713 kiro agent: ${professorJson}\\n`);\n }\n } else {\n process.stdout.write(`[dry-run] create ${professorJson}\\n`);\n }\n\n // Generate Ed25519 identity keypair (skip if already exists)\n const identityKey = join(paths.config, 'identity.key');\n const identityPub = join(paths.config, 'identity.pub');\n if (!opts.dryRun && !(await exists(identityKey))) {\n const { generateKeyPairSync } = await import('node:crypto');\n const { privateKey, publicKey } = generateKeyPairSync('ed25519');\n await writeFile(identityKey, privateKey.export({ format: 'der', type: 'pkcs8' }).toString('base64'));\n await writeFile(identityPub, publicKey.export({ format: 'der', type: 'spki' }).toString('base64'));\n const { chmodSync } = await import('node:fs');\n chmodSync(identityKey, 0o600);\n process.stdout.write(`\u2713 identity keypair generated\\n`);\n }\n\n // Generate self-signed Ed25519 TLS certificate (skip if already exists)\n const identityCrt = join(paths.config, 'identity.crt');\n const identityTlsKey = join(paths.config, 'identity.tls.key');\n if (!opts.dryRun && !(await exists(identityCrt))) {\n const { execSync } = await import('node:child_process');\n const { chmodSync } = await import('node:fs');\n const agentName = hostname();\n try {\n execSync(`openssl req -x509 -newkey ed25519 -keyout identity.tls.key -out identity.crt -days 3650 -nodes -subj \"/CN=${agentName}\"`, { cwd: paths.config, stdio: 'ignore' });\n chmodSync(identityTlsKey, 0o600);\n process.stdout.write(`\u2713 TLS certificate generated (Ed25519, 10yr)\\n`);\n } catch (err) {\n process.stderr.write(`\u26A0 TLS cert generation failed (openssl not found?). Agent-api will start without TLS.\\n`);\n }\n }\n\n // Seed config from examples (only missing ones)\n const seeded = await seedConfig(repoRoot, paths.config, opts.dryRun, home);\n if (seeded.length > 0) {\n process.stdout.write(`\u2713 seeded config: ${seeded.join(', ')}\\n`);\n }\n\n // Write wrappers (always overwrite \u2014 they're regenerable thin shims)\n const { loadManifest: loadInstallManifest } = await import('../install-manifest.js');\n const installManifest = loadInstallManifest(repoRoot);\n if (!opts.dryRun) {\n await mkdir(paths.bin, { recursive: true });\n }\n for (const name of installManifest.cliWrappers) {\n await writeWrapper(paths.bin, name, paths.current, opts.dryRun);\n }\n process.stdout.write(`\u2713 wrappers in ${paths.bin}\\n`);\n\n // Reconcile PATH symlinks\n if (!opts.dryRun) await mkdir(userBinDir, { recursive: true });\n const refused: string[] = [];\n for (const name of installManifest.cliWrappers) {\n const r = await reconcilePathLink(paths.bin, userBinDir, name, opts.force, opts.dryRun);\n if (r.action === 'refused') {\n refused.push(r.message ?? name);\n }\n }\n if (refused.length > 0) {\n process.stderr.write(`\\nPATH symlink conflicts:\\n ${refused.join('\\n ')}\\n`);\n return 4;\n }\n process.stdout.write(`\u2713 PATH symlinks in ${userBinDir}\\n`);\n\n // Warn if ~/.local/bin not on PATH\n if (!isPathOnPATH(userBinDir)) {\n process.stderr.write(\n `\\nWarning: ${userBinDir} is not on $PATH. Add to your shell config:\\n export PATH=\"${userBinDir}:$PATH\"\\n`,\n );\n }\n\n // Initialize manifest if brand-new install AND migration didn't write one.\n // (Migration 003 writes a manifest mid-flow with version + migration record;\n // we must not clobber it here.)\n const manifestAfter = await readManifest(paths.manifest);\n if (manifestAfter === null && !opts.dryRun) {\n await writeManifest(paths.manifest, {\n ...emptyManifest('abtars', hostname()),\n version: '',\n preMigrationBackup: null,\n });\n process.stdout.write(`\u2713 manifest initialized at ${paths.manifest}\\n`);\n }\n\n // Write install mode to manifest. Priority:\n // 1. --mode flag (explicit) \u2014 always wins\n // 2. existing manifest installMode \u2014 preserved (don't clobber on --force)\n // 3. default: supervised\n const manifestForMode = await readManifest(paths.manifest);\n const existingMode = manifestForMode?.installMode;\n const mode = opts.mode ?? existingMode ?? \"supervised\";\n if (manifestForMode) {\n await writeManifest(paths.manifest, { ...manifestForMode, installMode: mode });\n }\n process.stdout.write(`\u2713 install mode: ${mode}\\n`);\n\n // Release staging is handled by `abtars update` (auto-detects npm vs git source)\n\n // Restore from backup zip\n if (opts.restore) {\n const { spawnSync } = await import('node:child_process');\n const { existsSync: fileExists } = await import('node:fs');\n const zipPath = opts.restore;\n if (!fileExists(zipPath)) {\n process.stderr.write(`error: backup file not found: ${zipPath}\\n`);\n return 1;\n }\n // Extract to temp dir\n const tmpDir = join(process.env['TMPDIR'] ?? '/tmp', `abtars-restore-${Date.now()}`);\n const unzip = spawnSync('unzip', ['-o', zipPath, '-d', tmpDir], { encoding: 'utf-8' });\n if (unzip.status !== 0) {\n process.stderr.write(`error: unzip failed: ${unzip.stderr}\\n`);\n return 1;\n }\n // Copy abtars files\n const abSrc = join(tmpDir, 'abtars');\n if (fileExists(abSrc)) {\n spawnSync('cp', ['-r', ...readdirSync(abSrc).map(f => join(abSrc, f)), home], { stdio: 'inherit' });\n process.stdout.write(`\u2713 restored abtars config\\n`);\n }\n // Copy abmind files\n const abmindHome = process.env['ABMIND_HOME'] ?? join(dirname(home), '.abmind');\n const abmindSrc = join(tmpDir, 'abmind');\n if (fileExists(abmindSrc)) {\n spawnSync('cp', ['-r', ...readdirSync(abmindSrc).map(f => join(abmindSrc, f)), abmindHome], { stdio: 'inherit' });\n process.stdout.write(`\u2713 restored abmind data\\n`);\n }\n // Cleanup\n spawnSync('rm', ['-rf', tmpDir]);\n process.stdout.write(`\\nRestore complete.\\n`);\n process.stdout.write(`Next: 'abtars update' to build and activate.\\n`);\n return 0;\n }\n\n // --- supervised: load user-scope watchdog (LaunchAgent / systemd user) ---\n if (mode === 'supervised') {\n const { execSync } = await import('node:child_process');\n if (process.platform === 'darwin') {\n const plistSrc = join(home, 'scripts', 'com.abtars.watchdog.plist');\n const plistDst = join(homedir(), 'Library', 'LaunchAgents', 'com.abtars.watchdog.plist');\n if (existsSync(plistSrc)) {\n const content = readFileSync(plistSrc, 'utf-8').replaceAll('{{HOME}}', homedir());\n const { writeFileSync } = await import('node:fs');\n writeFileSync(plistDst, content);\n try { execSync(`launchctl load \"${plistDst}\"`, { stdio: 'ignore' }); } catch { /* already loaded */ }\n process.stdout.write(`\u2713 watchdog LaunchAgent loaded\\n`);\n }\n } else if (process.platform === 'linux') {\n const unitSrc = join(home, 'scripts', 'abtars-watchdog.service');\n const unitDir = join(homedir(), '.config', 'systemd', 'user');\n if (existsSync(unitSrc)) {\n mkdirSync(unitDir, { recursive: true });\n copyFileSync(unitSrc, join(unitDir, 'abtars-watchdog.service'));\n try { execSync('systemctl --user daemon-reload && systemctl --user enable --now abtars-watchdog', { stdio: 'ignore' }); } catch { /* may fail in chroot */ }\n process.stdout.write(`\u2713 watchdog systemd user service enabled\\n`);\n }\n }\n }\n\n process.stdout.write(`\\nInstall complete.\\n`);\n if (!manifestAfter || manifestAfter.version === '') {\n process.stdout.write(`Next: 'abtars update' to build and activate the first release.\\n`);\n } else {\n process.stdout.write(`\\n\u2500\u2500 Next steps \u2500\u2500\\n`);\n process.stdout.write(` 1. Run 'abtars onboard' to configure Telegram token + model provider\\n`);\n process.stdout.write(` 2. (Optional) Install Ollama for memory embeddings: curl -fsSL https://ollama.com/install.sh | sh\\n`);\n process.stdout.write(` 3. Start the bridge: 'abtars restart' or use the watchdog\\n\\n`);\n }\n\n const { printHealthSummary } = await import('./health-check.js');\n printHealthSummary(paths.home);\n\n // #334: Post-install healthcheck \u2014 validate operator channel exists (only on --restore)\n if (!opts.dryRun && opts.restore) {\n const { validateMinimumViability, formatValidationError } = await import('./install-validate.js');\n const validation = validateMinimumViability(paths.config);\n if (!validation.ok) {\n const invocation = `abtars install --restore ${opts.restore}`;\n process.stderr.write(\"\\n\" + formatValidationError(validation, invocation) + \"\\n\");\n return 1;\n }\n }\n\n return 0;\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env node
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
+ init_logger,
5
+ logInfo
6
+ } from "./chunk-2BY6I4P5.js";
7
+ import "./chunk-MJ6PHMOK.js";
8
+ import "./chunk-7K2YZTLD.js";
9
+
10
+ // src/cli/ensure-invariants.ts
11
+ init_logger();
12
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
13
+ import { join, dirname } from "node:path";
14
+ var TAG = "ensure-invariants";
15
+ var MIGRATIONS = [
16
+ {
17
+ id: "irc-secure-to-signed",
18
+ file: "config/irc.json",
19
+ applies: (c) => c.includes('"secure"'),
20
+ apply: (c) => c.replace(/"secure"/g, '"signed"')
21
+ }
22
+ ];
23
+ async function ensureInstallInvariants(repoRoot, home) {
24
+ const { loadManifest } = await import("./install-manifest-QRWID3KZ.js");
25
+ const manifest = loadManifest(repoRoot);
26
+ const created = [];
27
+ for (const seed of manifest.configSeeds) {
28
+ const src = join(repoRoot, seed.source);
29
+ const dst = join(home, seed.dest);
30
+ if (!existsSync(src) || existsSync(dst)) continue;
31
+ mkdirSync(dirname(dst), { recursive: true });
32
+ writeFileSync(dst, readFileSync(src, "utf-8"), { mode: seed.mode ? parseInt(seed.mode, 8) : 420 });
33
+ created.push(seed.dest);
34
+ logInfo(TAG, `Seeded missing: ${seed.dest}`);
35
+ }
36
+ for (const m of MIGRATIONS) {
37
+ const path = join(home, m.file);
38
+ if (!existsSync(path)) continue;
39
+ const content = readFileSync(path, "utf-8");
40
+ if (!m.applies(content)) continue;
41
+ writeFileSync(path, m.apply(content));
42
+ logInfo(TAG, `Migrated: ${m.id} (${m.file})`);
43
+ created.push(`[migrated] ${m.file}`);
44
+ }
45
+ return created;
46
+ }
47
+ export {
48
+ ensureInstallInvariants
49
+ };
50
+ //# sourceMappingURL=ensure-invariants-BJIEOSJ2.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/cli/ensure-invariants.ts"],
4
+ "sourcesContent": ["/**\n * ensure-invariants.ts \u2014 seed missing config files + run config migrations on update.\n * Called from both `abtars install` and `abtars update`. Idempotent.\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { logInfo } from \"../components/logger.js\";\n\nconst TAG = \"ensure-invariants\";\n\ninterface ConfigMigration {\n id: string;\n file: string; // relative to home (e.g. \"config/irc.json\")\n applies: (content: string) => boolean;\n apply: (content: string) => string;\n}\n\nconst MIGRATIONS: ConfigMigration[] = [\n {\n id: \"irc-secure-to-signed\",\n file: \"config/irc.json\",\n applies: (c) => c.includes('\"secure\"'),\n apply: (c) => c.replace(/\"secure\"/g, '\"signed\"'),\n },\n];\n\n/** Seed missing config files from install-manifest. Returns list of created files. */\nexport async function ensureInstallInvariants(repoRoot: string, home: string): Promise<string[]> {\n const { loadManifest } = await import(\"./install-manifest.js\");\n const manifest = loadManifest(repoRoot);\n const created: string[] = [];\n\n for (const seed of manifest.configSeeds) {\n const src = join(repoRoot, seed.source);\n const dst = join(home, seed.dest);\n if (!existsSync(src) || existsSync(dst)) continue;\n mkdirSync(dirname(dst), { recursive: true });\n writeFileSync(dst, readFileSync(src, \"utf-8\"), { mode: seed.mode ? parseInt(seed.mode, 8) : 0o644 });\n created.push(seed.dest);\n logInfo(TAG, `Seeded missing: ${seed.dest}`);\n }\n\n // Run config migrations\n for (const m of MIGRATIONS) {\n const path = join(home, m.file);\n if (!existsSync(path)) continue;\n const content = readFileSync(path, \"utf-8\");\n if (!m.applies(content)) continue;\n writeFileSync(path, m.apply(content));\n logInfo(TAG, `Migrated: ${m.id} (${m.file})`);\n created.push(`[migrated] ${m.file}`);\n }\n\n return created;\n}\n"],
5
+ "mappings": ";;;;;;;;;;AAOA;AAFA,SAAS,YAAY,cAAc,eAAe,iBAAiB;AACnE,SAAS,MAAM,eAAe;AAG9B,IAAM,MAAM;AASZ,IAAM,aAAgC;AAAA,EACpC;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,SAAS,CAAC,MAAM,EAAE,SAAS,UAAU;AAAA,IACrC,OAAO,CAAC,MAAM,EAAE,QAAQ,aAAa,UAAU;AAAA,EACjD;AACF;AAGA,eAAsB,wBAAwB,UAAkB,MAAiC;AAC/F,QAAM,EAAE,aAAa,IAAI,MAAM,OAAO,gCAAuB;AAC7D,QAAM,WAAW,aAAa,QAAQ;AACtC,QAAM,UAAoB,CAAC;AAE3B,aAAW,QAAQ,SAAS,aAAa;AACvC,UAAM,MAAM,KAAK,UAAU,KAAK,MAAM;AACtC,UAAM,MAAM,KAAK,MAAM,KAAK,IAAI;AAChC,QAAI,CAAC,WAAW,GAAG,KAAK,WAAW,GAAG,EAAG;AACzC,cAAU,QAAQ,GAAG,GAAG,EAAE,WAAW,KAAK,CAAC;AAC3C,kBAAc,KAAK,aAAa,KAAK,OAAO,GAAG,EAAE,MAAM,KAAK,OAAO,SAAS,KAAK,MAAM,CAAC,IAAI,IAAM,CAAC;AACnG,YAAQ,KAAK,KAAK,IAAI;AACtB,YAAQ,KAAK,mBAAmB,KAAK,IAAI,EAAE;AAAA,EAC7C;AAGA,aAAW,KAAK,YAAY;AAC1B,UAAM,OAAO,KAAK,MAAM,EAAE,IAAI;AAC9B,QAAI,CAAC,WAAW,IAAI,EAAG;AACvB,UAAM,UAAU,aAAa,MAAM,OAAO;AAC1C,QAAI,CAAC,EAAE,QAAQ,OAAO,EAAG;AACzB,kBAAc,MAAM,EAAE,MAAM,OAAO,CAAC;AACpC,YAAQ,KAAK,aAAa,EAAE,EAAE,KAAK,EAAE,IAAI,GAAG;AAC5C,YAAQ,KAAK,cAAc,EAAE,IAAI,EAAE;AAAA,EACrC;AAEA,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
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
+ install,
5
+ writeWrapper
6
+ } from "./chunk-XMJ76XLE.js";
7
+ import "./chunk-HXJRZWKA.js";
8
+ import "./chunk-7K2YZTLD.js";
9
+ export {
10
+ install,
11
+ writeWrapper
12
+ };
13
+ //# sourceMappingURL=install-K67U3WLX.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [],
5
+ "mappings": "",
6
+ "names": []
7
+ }
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env node
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
+ init_logger,
5
+ logWarn
6
+ } from "./chunk-2BY6I4P5.js";
7
+ import "./chunk-MJ6PHMOK.js";
8
+ import "./chunk-7K2YZTLD.js";
9
+
10
+ // src/cli/install-manifest.ts
11
+ init_logger();
12
+ import { readFileSync, existsSync, mkdirSync, copyFileSync, chmodSync, statSync, realpathSync } from "node:fs";
13
+ import { join, dirname } from "node:path";
14
+ import { fileURLToPath } from "node:url";
15
+ var TAG = "manifest";
16
+ var SUPPORTED_VERSION = 2;
17
+ var cached = null;
18
+ function loadManifest(repoRoot) {
19
+ if (cached) return cached;
20
+ const root = repoRoot ?? join(dirname(realpathSync(process.argv[1] ?? fileURLToPath(import.meta.url))), "..");
21
+ const p = join(root, "install-manifest.json");
22
+ let raw;
23
+ try {
24
+ raw = JSON.parse(readFileSync(p, "utf-8"));
25
+ } catch (err) {
26
+ throw new Error(`Invalid JSON in install-manifest.json: ${err instanceof Error ? err.message : String(err)}`);
27
+ }
28
+ if (raw.manifestVersion > SUPPORTED_VERSION) {
29
+ logWarn(TAG, `manifest version ${raw.manifestVersion} > supported ${SUPPORTED_VERSION} \u2014 some features may not be applied`);
30
+ }
31
+ cached = raw;
32
+ return raw;
33
+ }
34
+ function _resetManifestCache() {
35
+ cached = null;
36
+ }
37
+ function isLazyRootAllowed(manifest, relPath) {
38
+ return manifest.lazyRoots.some((root) => relPath === root || relPath.startsWith(root + "/"));
39
+ }
40
+ function reconcileManifest(manifest, home, repoRoot, fix) {
41
+ const ok = [];
42
+ const fixed = [];
43
+ const warnings = [];
44
+ for (const dir of manifest.directories) {
45
+ const abs = join(home, dir.path);
46
+ if (existsSync(abs)) {
47
+ if (dir.mode) {
48
+ const actual = (statSync(abs).mode & 511).toString(8);
49
+ const expected = dir.mode.replace(/^0/, "");
50
+ if (actual !== expected) {
51
+ if (fix) {
52
+ chmodSync(abs, parseInt(dir.mode, 8));
53
+ fixed.push(`${dir.path}/ permissions ${actual} \u2192 ${expected}`);
54
+ } else {
55
+ warnings.push(`${dir.path}/ permissions ${actual}, expected ${expected}`);
56
+ }
57
+ } else {
58
+ ok.push(`${dir.path}/`);
59
+ }
60
+ } else {
61
+ ok.push(`${dir.path}/`);
62
+ }
63
+ } else if (fix) {
64
+ mkdirSync(abs, { recursive: true, mode: dir.mode ? parseInt(dir.mode, 8) : void 0 });
65
+ fixed.push(`created ${dir.path}/`);
66
+ } else {
67
+ warnings.push(`${dir.path}/ MISSING`);
68
+ }
69
+ }
70
+ for (const seed of manifest.configSeeds) {
71
+ const dest = join(home, seed.dest);
72
+ if (existsSync(dest)) {
73
+ ok.push(seed.dest);
74
+ } else {
75
+ const src = join(repoRoot, seed.source);
76
+ if (fix && existsSync(src)) {
77
+ mkdirSync(join(home, seed.dest, ".."), { recursive: true });
78
+ copyFileSync(src, dest);
79
+ if (seed.mode) chmodSync(dest, parseInt(seed.mode, 8));
80
+ fixed.push(`seeded ${seed.dest} from ${seed.source}`);
81
+ } else if (!existsSync(src)) {
82
+ warnings.push(`${seed.dest} MISSING (source ${seed.source} not found)`);
83
+ } else {
84
+ warnings.push(`${seed.dest} MISSING (seed from ${seed.source})`);
85
+ }
86
+ }
87
+ }
88
+ for (const req of manifest.requiredConfigs) {
89
+ const abs = join(home, req.path);
90
+ if (existsSync(abs)) {
91
+ ok.push(req.path);
92
+ } else {
93
+ warnings.push(`${req.path} MISSING \u2014 ${req.remediation}`);
94
+ }
95
+ }
96
+ return { ok, fixed, warnings };
97
+ }
98
+ export {
99
+ _resetManifestCache,
100
+ isLazyRootAllowed,
101
+ loadManifest,
102
+ reconcileManifest
103
+ };
104
+ //# sourceMappingURL=install-manifest-QRWID3KZ.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/cli/install-manifest.ts"],
4
+ "sourcesContent": ["/**\n * install-manifest.ts \u2014 Load and validate install-manifest.json.\n * Single source of truth for install-time requirements.\n */\n\nimport { readFileSync, existsSync, mkdirSync, copyFileSync, chmodSync, statSync, realpathSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { logWarn } from \"../components/logger.js\";\n\nconst TAG = \"manifest\";\nconst SUPPORTED_VERSION = 2;\n\n// \u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface ManifestDirectory {\n path: string;\n mode?: string;\n}\n\nexport interface ManifestConfigSeed {\n source: string;\n dest: string;\n mode?: string;\n}\n\nexport interface ManifestRequiredConfig {\n path: string;\n remediation: string;\n}\n\nexport interface ManifestScripts {\n include: string[];\n executable: string;\n}\n\nexport interface ManifestServices {\n supervised: {\n macos?: { plist: string; placeholders: string[] };\n linux?: { units: string[] };\n };\n}\n\n\nexport interface InstallManifest {\n manifestVersion: number;\n directories: ManifestDirectory[];\n lazyRoots: string[];\n configSeeds: ManifestConfigSeed[];\n requiredConfigs: ManifestRequiredConfig[];\n scripts: ManifestScripts;\n services: ManifestServices;\n cliWrappers: string[];\n}\n\n// \u2500\u2500 Loader \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nlet cached: InstallManifest | null = null;\n\nexport function loadManifest(repoRoot?: string): InstallManifest {\n if (cached) return cached;\n const root = repoRoot ?? join(dirname(realpathSync(process.argv[1] ?? fileURLToPath(import.meta.url))), \"..\");\n const p = join(root, \"install-manifest.json\");\n let raw: InstallManifest;\n try { raw = JSON.parse(readFileSync(p, \"utf-8\")) as InstallManifest; }\n catch (err) { throw new Error(`Invalid JSON in install-manifest.json: ${err instanceof Error ? err.message : String(err)}`); }\n if (raw.manifestVersion > SUPPORTED_VERSION) {\n logWarn(TAG, `manifest version ${raw.manifestVersion} > supported ${SUPPORTED_VERSION} \u2014 some features may not be applied`);\n }\n cached = raw;\n return raw;\n}\n\n/** Clear cache (for tests). */\nexport function _resetManifestCache(): void { cached = null; }\n\n/** Check if a path is under a declared lazyRoot. */\nexport function isLazyRootAllowed(manifest: InstallManifest, relPath: string): boolean {\n return manifest.lazyRoots.some(root => relPath === root || relPath.startsWith(root + \"/\"));\n}\n\n// \u2500\u2500 Reconciliation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface ReconcileResult {\n ok: string[];\n fixed: string[];\n warnings: string[];\n}\n\n/**\n * Reconcile install state against manifest. If fix=true, creates missing dirs\n * and seeds missing configs. Returns a report.\n */\nexport function reconcileManifest(\n manifest: InstallManifest,\n home: string,\n repoRoot: string,\n fix: boolean,\n): ReconcileResult {\n const ok: string[] = [];\n const fixed: string[] = [];\n const warnings: string[] = [];\n\n // Eager directories\n for (const dir of manifest.directories) {\n const abs = join(home, dir.path);\n if (existsSync(abs)) {\n if (dir.mode) {\n const actual = (statSync(abs).mode & 0o777).toString(8);\n const expected = dir.mode.replace(/^0/, \"\");\n if (actual !== expected) {\n if (fix) {\n chmodSync(abs, parseInt(dir.mode, 8));\n fixed.push(`${dir.path}/ permissions ${actual} \u2192 ${expected}`);\n } else {\n warnings.push(`${dir.path}/ permissions ${actual}, expected ${expected}`);\n }\n } else {\n ok.push(`${dir.path}/`);\n }\n } else {\n ok.push(`${dir.path}/`);\n }\n } else if (fix) {\n mkdirSync(abs, { recursive: true, mode: dir.mode ? parseInt(dir.mode, 8) : undefined });\n fixed.push(`created ${dir.path}/`);\n } else {\n warnings.push(`${dir.path}/ MISSING`);\n }\n }\n\n // Config seeds\n for (const seed of manifest.configSeeds) {\n const dest = join(home, seed.dest);\n if (existsSync(dest)) {\n ok.push(seed.dest);\n } else {\n const src = join(repoRoot, seed.source);\n if (fix && existsSync(src)) {\n mkdirSync(join(home, seed.dest, \"..\"), { recursive: true });\n copyFileSync(src, dest);\n if (seed.mode) chmodSync(dest, parseInt(seed.mode, 8));\n fixed.push(`seeded ${seed.dest} from ${seed.source}`);\n } else if (!existsSync(src)) {\n warnings.push(`${seed.dest} MISSING (source ${seed.source} not found)`);\n } else {\n warnings.push(`${seed.dest} MISSING (seed from ${seed.source})`);\n }\n }\n }\n\n // Required configs (report only, never auto-fix)\n for (const req of manifest.requiredConfigs) {\n const abs = join(home, req.path);\n if (existsSync(abs)) {\n ok.push(req.path);\n } else {\n warnings.push(`${req.path} MISSING \u2014 ${req.remediation}`);\n }\n }\n\n return { ok, fixed, warnings };\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
package/bundle/meta.json CHANGED
@@ -22623,7 +22623,7 @@
22623
22623
  "format": "esm"
22624
22624
  },
22625
22625
  "src/cli/install-manifest.ts": {
22626
- "bytes": 5289,
22626
+ "bytes": 5330,
22627
22627
  "imports": [
22628
22628
  {
22629
22629
  "path": "node:fs",
@@ -23867,13 +23867,13 @@
23867
23867
  }
23868
23868
  },
23869
23869
  "outputs": {
23870
- "bundle/ensure-invariants-KUXIW73S.js.map": {
23870
+ "bundle/ensure-invariants-BJIEOSJ2.js.map": {
23871
23871
  "imports": [],
23872
23872
  "exports": [],
23873
23873
  "inputs": {},
23874
23874
  "bytes": 3144
23875
23875
  },
23876
- "bundle/ensure-invariants-KUXIW73S.js": {
23876
+ "bundle/ensure-invariants-BJIEOSJ2.js": {
23877
23877
  "imports": [
23878
23878
  {
23879
23879
  "path": "bundle/chunk-2BY6I4P5.js",
@@ -23898,7 +23898,7 @@
23898
23898
  "external": true
23899
23899
  },
23900
23900
  {
23901
- "path": "bundle/install-manifest-ZETY4AFS.js",
23901
+ "path": "bundle/install-manifest-QRWID3KZ.js",
23902
23902
  "kind": "dynamic-import"
23903
23903
  }
23904
23904
  ],
@@ -24358,13 +24358,13 @@
24358
24358
  "inputs": {},
24359
24359
  "bytes": 1224
24360
24360
  },
24361
- "bundle/install-manifest-ZETY4AFS.js.map": {
24361
+ "bundle/install-manifest-QRWID3KZ.js.map": {
24362
24362
  "imports": [],
24363
24363
  "exports": [],
24364
24364
  "inputs": {},
24365
- "bytes": 8579
24365
+ "bytes": 8648
24366
24366
  },
24367
- "bundle/install-manifest-ZETY4AFS.js": {
24367
+ "bundle/install-manifest-QRWID3KZ.js": {
24368
24368
  "imports": [
24369
24369
  {
24370
24370
  "path": "bundle/chunk-2BY6I4P5.js",
@@ -24403,10 +24403,10 @@
24403
24403
  "entryPoint": "src/cli/install-manifest.ts",
24404
24404
  "inputs": {
24405
24405
  "src/cli/install-manifest.ts": {
24406
- "bytesInOutput": 3056
24406
+ "bytesInOutput": 3097
24407
24407
  }
24408
24408
  },
24409
- "bytes": 3752
24409
+ "bytes": 3793
24410
24410
  },
24411
24411
  "bundle/install-log-Q6RUHKWC.js.map": {
24412
24412
  "imports": [],
@@ -24523,16 +24523,16 @@
24523
24523
  },
24524
24524
  "bytes": 2345
24525
24525
  },
24526
- "bundle/install-24XR5FO5.js.map": {
24526
+ "bundle/install-K67U3WLX.js.map": {
24527
24527
  "imports": [],
24528
24528
  "exports": [],
24529
24529
  "inputs": {},
24530
24530
  "bytes": 93
24531
24531
  },
24532
- "bundle/install-24XR5FO5.js": {
24532
+ "bundle/install-K67U3WLX.js": {
24533
24533
  "imports": [
24534
24534
  {
24535
- "path": "bundle/chunk-7CHLS36W.js",
24535
+ "path": "bundle/chunk-XMJ76XLE.js",
24536
24536
  "kind": "import-statement"
24537
24537
  },
24538
24538
  {
@@ -33806,7 +33806,7 @@
33806
33806
  "bundle/abtars-cli.js": {
33807
33807
  "imports": [
33808
33808
  {
33809
- "path": "bundle/chunk-7CHLS36W.js",
33809
+ "path": "bundle/chunk-XMJ76XLE.js",
33810
33810
  "kind": "import-statement"
33811
33811
  },
33812
33812
  {
@@ -34099,11 +34099,11 @@
34099
34099
  "external": true
34100
34100
  },
34101
34101
  {
34102
- "path": "bundle/install-manifest-ZETY4AFS.js",
34102
+ "path": "bundle/install-manifest-QRWID3KZ.js",
34103
34103
  "kind": "dynamic-import"
34104
34104
  },
34105
34105
  {
34106
- "path": "bundle/install-24XR5FO5.js",
34106
+ "path": "bundle/install-K67U3WLX.js",
34107
34107
  "kind": "dynamic-import"
34108
34108
  },
34109
34109
  {
@@ -34112,7 +34112,7 @@
34112
34112
  "external": true
34113
34113
  },
34114
34114
  {
34115
- "path": "bundle/ensure-invariants-KUXIW73S.js",
34115
+ "path": "bundle/ensure-invariants-BJIEOSJ2.js",
34116
34116
  "kind": "dynamic-import"
34117
34117
  },
34118
34118
  {
@@ -34205,13 +34205,13 @@
34205
34205
  },
34206
34206
  "bytes": 77079
34207
34207
  },
34208
- "bundle/chunk-7CHLS36W.js.map": {
34208
+ "bundle/chunk-XMJ76XLE.js.map": {
34209
34209
  "imports": [],
34210
34210
  "exports": [],
34211
34211
  "inputs": {},
34212
34212
  "bytes": 27771
34213
34213
  },
34214
- "bundle/chunk-7CHLS36W.js": {
34214
+ "bundle/chunk-XMJ76XLE.js": {
34215
34215
  "imports": [
34216
34216
  {
34217
34217
  "path": "bundle/chunk-HXJRZWKA.js",
@@ -34248,11 +34248,11 @@
34248
34248
  "external": true
34249
34249
  },
34250
34250
  {
34251
- "path": "bundle/install-manifest-ZETY4AFS.js",
34251
+ "path": "bundle/install-manifest-QRWID3KZ.js",
34252
34252
  "kind": "dynamic-import"
34253
34253
  },
34254
34254
  {
34255
- "path": "bundle/install-manifest-ZETY4AFS.js",
34255
+ "path": "bundle/install-manifest-QRWID3KZ.js",
34256
34256
  "kind": "dynamic-import"
34257
34257
  },
34258
34258
  {
@@ -34290,7 +34290,7 @@
34290
34290
  "external": true
34291
34291
  },
34292
34292
  {
34293
- "path": "bundle/install-manifest-ZETY4AFS.js",
34293
+ "path": "bundle/install-manifest-QRWID3KZ.js",
34294
34294
  "kind": "dynamic-import"
34295
34295
  },
34296
34296
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "abtars",
3
- "version": "0.1.0-alpha.23",
3
+ "version": "0.1.0-alpha.24",
4
4
  "description": "Standalone agent bridging Telegram to Kiro CLI via tmux/ACP",
5
5
  "type": "module",
6
6
  "main": "bundle/abtars.js",