@integrity-labs/agt-cli 0.10.10 → 0.10.12

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/dist/bin/agt.js CHANGED
@@ -32,7 +32,7 @@ import {
32
32
  resolveChannels,
33
33
  serializeManifestForSlackCli,
34
34
  setActiveTeam
35
- } from "../chunk-PZG4XPJV.js";
35
+ } from "../chunk-KZ7Y55HJ.js";
36
36
 
37
37
  // src/bin/agt.ts
38
38
  import { join as join11 } from "path";
@@ -2211,32 +2211,38 @@ import { join as join8 } from "path";
2211
2211
  import { homedir } from "os";
2212
2212
 
2213
2213
  // src/lib/watchdog.ts
2214
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync4, unlinkSync, existsSync as existsSync2, mkdirSync as mkdirSync4 } from "fs";
2214
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync4, unlinkSync, existsSync as existsSync2, mkdirSync as mkdirSync4, openSync, closeSync, chmodSync } from "fs";
2215
2215
  import { join as join7 } from "path";
2216
- var AUGMENTED_DIR = join7(process.env["HOME"] ?? "/tmp", ".augmented");
2217
- var PID_FILE = join7(AUGMENTED_DIR, "manager.pid");
2218
- var STATE_FILE = join7(AUGMENTED_DIR, "manager-state.json");
2219
- function ensureDir() {
2220
- if (!existsSync2(AUGMENTED_DIR)) {
2221
- mkdirSync4(AUGMENTED_DIR, { recursive: true });
2216
+ import { spawn as spawn2 } from "child_process";
2217
+ var DEFAULT_CONFIG_DIR = join7(process.env["HOME"] ?? "/tmp", ".augmented");
2218
+ function getManagerPaths(configDir) {
2219
+ return {
2220
+ pidFile: join7(configDir, "manager.pid"),
2221
+ stateFile: join7(configDir, "manager-state.json"),
2222
+ logFile: join7(configDir, "manager.log")
2223
+ };
2224
+ }
2225
+ function ensureDir(configDir) {
2226
+ if (!existsSync2(configDir)) {
2227
+ mkdirSync4(configDir, { recursive: true });
2222
2228
  }
2223
2229
  }
2224
- function writePidFile(pid) {
2225
- ensureDir();
2226
- writeFileSync4(PID_FILE, String(pid), { mode: 384 });
2230
+ function writePidFile(configDir, pid) {
2231
+ ensureDir(configDir);
2232
+ writeFileSync4(getManagerPaths(configDir).pidFile, String(pid), { mode: 384 });
2227
2233
  }
2228
- function readPidFile() {
2234
+ function readPidFile(configDir) {
2229
2235
  try {
2230
- const raw = readFileSync2(PID_FILE, "utf-8").trim();
2236
+ const raw = readFileSync2(getManagerPaths(configDir).pidFile, "utf-8").trim();
2231
2237
  const pid = parseInt(raw, 10);
2232
2238
  return isNaN(pid) ? null : pid;
2233
2239
  } catch {
2234
2240
  return null;
2235
2241
  }
2236
2242
  }
2237
- function removePidFile() {
2243
+ function removePidFile(configDir) {
2238
2244
  try {
2239
- unlinkSync(PID_FILE);
2245
+ unlinkSync(getManagerPaths(configDir).pidFile);
2240
2246
  } catch {
2241
2247
  }
2242
2248
  }
@@ -2248,49 +2254,91 @@ function isProcessAlive(pid) {
2248
2254
  return false;
2249
2255
  }
2250
2256
  }
2251
- function readStateFile() {
2257
+ function readStateFile(configDir) {
2252
2258
  try {
2253
- const raw = readFileSync2(STATE_FILE, "utf-8");
2259
+ const raw = readFileSync2(getManagerPaths(configDir).stateFile, "utf-8");
2254
2260
  return JSON.parse(raw);
2255
2261
  } catch {
2256
2262
  return null;
2257
2263
  }
2258
2264
  }
2259
- function removeStateFile() {
2265
+ function removeStateFile(configDir) {
2260
2266
  try {
2261
- unlinkSync(STATE_FILE);
2267
+ unlinkSync(getManagerPaths(configDir).stateFile);
2262
2268
  } catch {
2263
2269
  }
2264
2270
  }
2265
2271
  function startWatchdog(opts) {
2266
- const existingPid = readPidFile();
2272
+ const { configDir } = opts;
2273
+ const existingPid = readPidFile(configDir);
2267
2274
  if (existingPid !== null) {
2268
2275
  if (isProcessAlive(existingPid)) {
2269
2276
  throw new Error(`Manager already running (PID ${existingPid}). Use \`agt manager stop\` first.`);
2270
2277
  }
2271
- removePidFile();
2272
- removeStateFile();
2278
+ removePidFile(configDir);
2279
+ removeStateFile(configDir);
2280
+ }
2281
+ if (opts.detached) {
2282
+ ensureDir(configDir);
2283
+ const { logFile } = getManagerPaths(configDir);
2284
+ const logFd = openSync(logFile, "a", 384);
2285
+ try {
2286
+ chmodSync(logFile, 384);
2287
+ } catch {
2288
+ }
2289
+ const intervalSec = String(Math.max(Math.floor(opts.intervalMs / 1e3), 5));
2290
+ const child = spawn2(
2291
+ process.execPath,
2292
+ [process.argv[1], "manager", "start", "--interval", intervalSec, "--config-dir", configDir],
2293
+ {
2294
+ detached: true,
2295
+ stdio: ["ignore", logFd, logFd],
2296
+ env: process.env
2297
+ }
2298
+ );
2299
+ child.unref();
2300
+ closeSync(logFd);
2301
+ if (!child.pid) {
2302
+ throw new Error("Failed to spawn detached manager process");
2303
+ }
2304
+ const { pidFile } = getManagerPaths(configDir);
2305
+ const deadline = Date.now() + 5e3;
2306
+ const sleepBuf = new Int32Array(new SharedArrayBuffer(4));
2307
+ while (Date.now() < deadline) {
2308
+ if (existsSync2(pidFile)) {
2309
+ return { pid: child.pid };
2310
+ }
2311
+ if (child.exitCode !== null) {
2312
+ throw new Error(
2313
+ `Manager exited during startup (code ${child.exitCode}). See ${logFile} for details.`
2314
+ );
2315
+ }
2316
+ Atomics.wait(sleepBuf, 0, 0, 100);
2317
+ }
2318
+ throw new Error(
2319
+ `Manager did not become ready within 5s. See ${logFile} for details.`
2320
+ );
2273
2321
  }
2274
- writePidFile(process.pid);
2322
+ writePidFile(configDir, process.pid);
2275
2323
  void import("../lib/manager-worker.js").then(({ startManager }) => {
2276
2324
  startManager({
2277
2325
  intervalMs: opts.intervalMs,
2278
- configDir: opts.configDir
2326
+ configDir
2279
2327
  });
2280
2328
  });
2281
2329
  process.on("exit", () => {
2282
- removePidFile();
2330
+ removePidFile(configDir);
2283
2331
  });
2284
2332
  return { pid: process.pid };
2285
2333
  }
2286
- async function stopWatchdog() {
2287
- const pid = readPidFile();
2334
+ async function stopWatchdog(configDir = DEFAULT_CONFIG_DIR) {
2335
+ const pid = readPidFile(configDir);
2288
2336
  if (pid === null) {
2289
2337
  return { stopped: false };
2290
2338
  }
2291
2339
  if (!isProcessAlive(pid)) {
2292
- removePidFile();
2293
- removeStateFile();
2340
+ removePidFile(configDir);
2341
+ removeStateFile(configDir);
2294
2342
  return { stopped: true, pid };
2295
2343
  }
2296
2344
  process.kill(pid, "SIGTERM");
@@ -2298,7 +2346,7 @@ async function stopWatchdog() {
2298
2346
  while (Date.now() < deadline) {
2299
2347
  await new Promise((r) => setTimeout(r, 200));
2300
2348
  if (!isProcessAlive(pid)) {
2301
- removePidFile();
2349
+ removePidFile(configDir);
2302
2350
  return { stopped: true, pid };
2303
2351
  }
2304
2352
  }
@@ -2306,19 +2354,19 @@ async function stopWatchdog() {
2306
2354
  process.kill(pid, "SIGKILL");
2307
2355
  } catch {
2308
2356
  }
2309
- removePidFile();
2310
- removeStateFile();
2357
+ removePidFile(configDir);
2358
+ removeStateFile(configDir);
2311
2359
  return { stopped: true, pid };
2312
2360
  }
2313
- function getManagerStatus() {
2314
- const pid = readPidFile();
2361
+ function getManagerStatus(configDir = DEFAULT_CONFIG_DIR) {
2362
+ const pid = readPidFile(configDir);
2315
2363
  if (pid === null) return null;
2316
2364
  if (!isProcessAlive(pid)) {
2317
- removePidFile();
2318
- removeStateFile();
2365
+ removePidFile(configDir);
2366
+ removeStateFile(configDir);
2319
2367
  return null;
2320
2368
  }
2321
- return readStateFile();
2369
+ return readStateFile(configDir);
2322
2370
  }
2323
2371
 
2324
2372
  // src/commands/manager.ts
@@ -2367,10 +2415,11 @@ function managerStartCommand(opts) {
2367
2415
  process.exitCode = 1;
2368
2416
  }
2369
2417
  }
2370
- async function managerStopCommand() {
2418
+ async function managerStopCommand(opts = {}) {
2371
2419
  const json = isJsonMode();
2420
+ const configDir = opts.configDir ?? join8(homedir(), ".augmented");
2372
2421
  try {
2373
- const result = await stopWatchdog();
2422
+ const result = await stopWatchdog(configDir);
2374
2423
  if (!result.stopped && !result.pid) {
2375
2424
  if (json) {
2376
2425
  jsonOutput({ ok: false, error: "Manager is not running" });
@@ -2394,9 +2443,10 @@ async function managerStopCommand() {
2394
2443
  process.exitCode = 1;
2395
2444
  }
2396
2445
  }
2397
- function managerStatusCommand() {
2446
+ function managerStatusCommand(opts = {}) {
2398
2447
  const json = isJsonMode();
2399
- const status = getManagerStatus();
2448
+ const configDir = opts.configDir ?? join8(homedir(), ".augmented");
2449
+ const status = getManagerStatus(configDir);
2400
2450
  if (!status) {
2401
2451
  if (json) {
2402
2452
  jsonOutput({ ok: true, running: false });
@@ -2865,7 +2915,7 @@ async function kanbanRecurringDisableCommand(titleOrId, opts) {
2865
2915
  }
2866
2916
 
2867
2917
  // src/commands/setup.ts
2868
- import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5 } from "fs";
2918
+ import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, accessSync, constants as fsConstants } from "fs";
2869
2919
  import { join as join10, dirname } from "path";
2870
2920
  import { homedir as homedir2 } from "os";
2871
2921
  import chalk18 from "chalk";
@@ -2884,6 +2934,30 @@ function detectShellProfile() {
2884
2934
  if (existsSync4(bashrc)) return bashrc;
2885
2935
  return join10(home, ".bash_profile");
2886
2936
  }
2937
+ function maybeWriteEtcEnvironment(apiUrl, apiKey) {
2938
+ if (process.platform === "win32") return false;
2939
+ if (typeof process.getuid !== "function" || process.getuid() !== 0) return false;
2940
+ const envPath = "/etc/environment";
2941
+ if (!existsSync4(envPath)) return false;
2942
+ try {
2943
+ accessSync(envPath, fsConstants.W_OK);
2944
+ } catch {
2945
+ return false;
2946
+ }
2947
+ try {
2948
+ const current = readFileSync4(envPath, "utf-8");
2949
+ const stripped = current.split(/\r?\n/).filter((line) => !/^\s*(?:export\s+)?AGT_(?:HOST|API_KEY)=/.test(line)).join("\n");
2950
+ const base = stripped.endsWith("\n") || stripped.length === 0 ? stripped : `${stripped}
2951
+ `;
2952
+ const appended = `${base}AGT_HOST="${apiUrl}"
2953
+ AGT_API_KEY="${apiKey}"
2954
+ `;
2955
+ writeFileSync5(envPath, appended, { mode: 420 });
2956
+ return true;
2957
+ } catch {
2958
+ return false;
2959
+ }
2960
+ }
2887
2961
  function buildExportLines(shell, apiUrl, apiKey) {
2888
2962
  if (shell.includes("fish")) {
2889
2963
  return [
@@ -3003,11 +3077,15 @@ async function setupCommand(token) {
3003
3077
  if (!json) {
3004
3078
  success(`Environment variables written to ${chalk18.bold(profilePath)}`);
3005
3079
  }
3080
+ const envWritten = maybeWriteEtcEnvironment(finalApiUrl, setupResult.api_key);
3081
+ if (!json && envWritten) {
3082
+ success(`System-wide env written to ${chalk18.bold("/etc/environment")}`);
3083
+ }
3006
3084
  const managerSpinner = ora13({ text: "Starting manager daemon\u2026", isSilent: json });
3007
3085
  managerSpinner.start();
3008
3086
  try {
3009
3087
  const configDir = join10(homedir2(), ".augmented");
3010
- const { pid } = startWatchdog({ intervalMs: 1e4, configDir });
3088
+ const { pid } = startWatchdog({ intervalMs: 1e4, configDir, detached: true });
3011
3089
  managerSpinner.succeed(`Manager started (PID ${pid})`);
3012
3090
  } catch (err) {
3013
3091
  managerSpinner.warn("Could not start manager daemon");
@@ -3016,6 +3094,7 @@ async function setupCommand(token) {
3016
3094
  info("Start it manually with: agt manager start");
3017
3095
  }
3018
3096
  }
3097
+ const includeApiKey = process.env["AGT_SETUP_INCLUDE_API_KEY"] === "1";
3019
3098
  if (json) {
3020
3099
  jsonOutput({
3021
3100
  ok: true,
@@ -3023,14 +3102,11 @@ async function setupCommand(token) {
3023
3102
  host_id: setupResult.host_id,
3024
3103
  team_slug: setupResult.team_slug,
3025
3104
  api_url: finalApiUrl,
3026
- // Host API key exposed only in JSON mode so bootstrap/automation can
3027
- // persist it system-wide. The token the caller just supplied grants
3028
- // access to this key anyway; re-emitting it here just saves the caller
3029
- // from having to parse the shell profile we wrote.
3030
- api_key: setupResult.api_key,
3105
+ ...includeApiKey ? { api_key: setupResult.api_key } : {},
3031
3106
  agents: setupResult.agents,
3032
3107
  profile: profilePath
3033
3108
  });
3109
+ process.exit(0);
3034
3110
  } else {
3035
3111
  console.log();
3036
3112
  console.log(chalk18.green.bold(" Setup complete!"));
@@ -3340,7 +3416,7 @@ function getAcpAgent(name) {
3340
3416
  }
3341
3417
 
3342
3418
  // ../../packages/core/dist/acp/client.js
3343
- import { spawn as spawn2 } from "child_process";
3419
+ import { spawn as spawn3 } from "child_process";
3344
3420
  function resolveAgentCommand(agentName) {
3345
3421
  const adapter = getAcpAgent(agentName);
3346
3422
  if (adapter) {
@@ -3356,7 +3432,7 @@ function resolveAgentCommand(agentName) {
3356
3432
  }
3357
3433
  async function runAcpx(args, options = {}) {
3358
3434
  return new Promise((resolve2) => {
3359
- const child = spawn2("acpx", args, {
3435
+ const child = spawn3("acpx", args, {
3360
3436
  cwd: options.cwd,
3361
3437
  stdio: ["pipe", "pipe", "pipe"],
3362
3438
  env: { ...process.env }
@@ -3558,7 +3634,7 @@ async function acpxCloseCommand(agent2, _opts, cmd) {
3558
3634
  import { execSync } from "child_process";
3559
3635
  import chalk20 from "chalk";
3560
3636
  import ora15 from "ora";
3561
- var cliVersion = true ? "0.10.10" : "dev";
3637
+ var cliVersion = true ? "0.10.12" : "dev";
3562
3638
  async function fetchLatestVersion() {
3563
3639
  const host2 = getHost();
3564
3640
  if (!host2) return null;
@@ -3957,7 +4033,7 @@ function handleError(err) {
3957
4033
  }
3958
4034
 
3959
4035
  // src/bin/agt.ts
3960
- var cliVersion2 = true ? "0.10.10" : "dev";
4036
+ var cliVersion2 = true ? "0.10.12" : "dev";
3961
4037
  var program = new Command();
3962
4038
  program.name("agt").description("Augmented CLI \u2014 agent provisioning and management").version(cliVersion2).option("--json", "Emit machine-readable JSON output (suppress spinners and colors)").option("--skip-update-check", "Skip the automatic update check on startup");
3963
4039
  program.hook("preAction", (thisCommand) => {
@@ -4005,8 +4081,8 @@ host.command("pair <host-name>").description("Start an SSM port-forward + shell
4005
4081
  );
4006
4082
  var manager = program.command("manager").description("Host config sync daemon \u2014 keeps local agent files in sync with API");
4007
4083
  manager.command("start").description("Start the manager daemon (polls API for config changes and detects local drift)").option("--interval <seconds>", "Poll interval in seconds (min 5)", "10").option("--config-dir <dir>", "Config directory for agent files", join11(homedir3(), ".augmented")).action(managerStartCommand);
4008
- manager.command("stop").description("Stop the running manager daemon").action(managerStopCommand);
4009
- manager.command("status").description("Show the current manager daemon status and discovered agents").action(managerStatusCommand);
4084
+ manager.command("stop").description("Stop the running manager daemon").option("--config-dir <dir>", "Config directory for agent files", join11(homedir3(), ".augmented")).action(managerStopCommand);
4085
+ manager.command("status").description("Show the current manager daemon status and discovered agents").option("--config-dir <dir>", "Config directory for agent files", join11(homedir3(), ".augmented")).action(managerStatusCommand);
4010
4086
  var agent = program.command("agent").description("Inspect and manage agents");
4011
4087
  agent.command("show <code-name>").description("Display an agent's provisioned OpenClaw configuration").option("--config-dir <dir>", "Config directory", join11(homedir3(), ".augmented")).option("--all-channels", "Show all channels (including disabled)").action(agentShowCommand);
4012
4088
  var kanban = program.command("kanban").description("Manage agent kanban boards");