@integrity-labs/agt-cli 0.27.65 → 0.27.67

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
@@ -27,7 +27,7 @@ import {
27
27
  success,
28
28
  table,
29
29
  warn
30
- } from "../chunk-UMFTYZO7.js";
30
+ } from "../chunk-EYWW624B.js";
31
31
  import {
32
32
  CHANNEL_REGISTRY,
33
33
  DEPLOYMENT_TEMPLATES,
@@ -53,11 +53,11 @@ import {
53
53
  renderTemplate,
54
54
  resolveChannels,
55
55
  serializeManifestForSlackCli
56
- } from "../chunk-PM3CC2SJ.js";
56
+ } from "../chunk-KPD5KJY7.js";
57
57
 
58
58
  // src/bin/agt.ts
59
- import { join as join18 } from "path";
60
- import { homedir as homedir8 } from "os";
59
+ import { join as join20 } from "path";
60
+ import { homedir as homedir9 } from "os";
61
61
  import { Command } from "commander";
62
62
 
63
63
  // src/commands/whoami.ts
@@ -551,11 +551,11 @@ async function lintCommand(path) {
551
551
  process.exitCode = 1;
552
552
  return;
553
553
  }
554
- const { readdirSync: readdirSync6, statSync: statSync4 } = await import("fs");
555
- const entries = readdirSync6(augmentedDir);
554
+ const { readdirSync: readdirSync7, statSync: statSync5 } = await import("fs");
555
+ const entries = readdirSync7(augmentedDir);
556
556
  for (const entry of entries) {
557
557
  const entryPath = join2(augmentedDir, entry);
558
- if (statSync4(entryPath).isDirectory()) {
558
+ if (statSync5(entryPath).isDirectory()) {
559
559
  dirs.push({ name: entry, dir: entryPath });
560
560
  }
561
561
  }
@@ -4929,7 +4929,7 @@ import { execFileSync, execSync } from "child_process";
4929
4929
  import { existsSync as existsSync10, realpathSync as realpathSync2 } from "fs";
4930
4930
  import chalk18 from "chalk";
4931
4931
  import ora16 from "ora";
4932
- var cliVersion = true ? "0.27.65" : "dev";
4932
+ var cliVersion = true ? "0.27.67" : "dev";
4933
4933
  async function fetchLatestVersion() {
4934
4934
  const host2 = getHost();
4935
4935
  if (!host2) return null;
@@ -5406,6 +5406,176 @@ async function auditMcpRenderCommand(options = {}) {
5406
5406
  }
5407
5407
  }
5408
5408
 
5409
+ // src/commands/audit-secrets.ts
5410
+ import { homedir as homedir8 } from "os";
5411
+ import { join as join19 } from "path";
5412
+
5413
+ // src/lib/secret-leak-audit.ts
5414
+ import { readdirSync as readdirSync6, readFileSync as readFileSync11, statSync as statSync4 } from "fs";
5415
+ import { join as join18 } from "path";
5416
+
5417
+ // ../../packages/core/dist/provisioning/mcp-secret-lint.js
5418
+ var LITERAL_SECRET_PATTERNS = [
5419
+ // Slack bot token — `xoxb-<workspace>-<...>`
5420
+ { name: "slack_bot_token", re: /^xoxb-/ },
5421
+ // Slack app-level token (Socket Mode) — `xapp-<...>`
5422
+ { name: "slack_app_token", re: /^xapp-/ },
5423
+ // AGT host API key — `tlk_<...>` (see claudecode-plugin-augmented README).
5424
+ { name: "agt_host_api_key", re: /^tlk_/ },
5425
+ // Composio / generic api-key prefix — `ak_<...>`
5426
+ { name: "composio_api_key", re: /^ak_/ },
5427
+ // Telegram bot token — `<10-digit bot id>:AAE<...>` (BotFather format).
5428
+ { name: "telegram_bot_token", re: /^\d{10}:AAE/ },
5429
+ // ENG-5901 extension beyond the original AC's five patterns: a literal
5430
+ // JWT (`eyJ...`) is the shape of a leaked AGT_API_KEY, which the
5431
+ // value-prefix patterns above would otherwise miss. Header values often
5432
+ // carry it behind `Bearer ` (or a copy-pasted `Authorization: Bearer `),
5433
+ // so those prefixes are optionally consumed (CodeRabbit #1731).
5434
+ // Templates (`Bearer ${AGT_API_KEY}`) and concrete non-secret values
5435
+ // (UUIDs, hosts) never put `eyJ` after the prefix, so this stays
5436
+ // false-positive-safe inside .mcp.json.
5437
+ { name: "jwt_agt_api_key", re: /^(?:authorization:\s*)?(?:bearer\s+)?eyJ[A-Za-z0-9_-]+\./i }
5438
+ ];
5439
+
5440
+ // src/lib/secret-leak-audit.ts
5441
+ var SECRET_KEY_NAME_RE = /TOKEN|KEY|SECRET|BEARER|PASSWORD/i;
5442
+ function isTemplated(value) {
5443
+ return value.includes("${");
5444
+ }
5445
+ function matchesKnownSecretShape(value) {
5446
+ return LITERAL_SECRET_PATTERNS.some(({ re }) => re.test(value));
5447
+ }
5448
+ function isWorldReadable(mode) {
5449
+ return (mode & 63) !== 0;
5450
+ }
5451
+ function octal(mode) {
5452
+ return `0${(mode & 511).toString(8)}`;
5453
+ }
5454
+ function scanMcpFile(file, agent2, findings) {
5455
+ let mode = null;
5456
+ try {
5457
+ mode = statSync4(file).mode;
5458
+ } catch {
5459
+ return;
5460
+ }
5461
+ if (mode !== null && isWorldReadable(mode)) {
5462
+ findings.push({ kind: "world-readable-secret-file", file, agent: agent2, mode: octal(mode) });
5463
+ }
5464
+ let parsed;
5465
+ try {
5466
+ parsed = JSON.parse(readFileSync11(file, "utf-8"));
5467
+ } catch {
5468
+ return;
5469
+ }
5470
+ const servers = parsed?.mcpServers;
5471
+ if (typeof servers !== "object" || servers === null) return;
5472
+ for (const [server, raw] of Object.entries(servers)) {
5473
+ if (typeof raw !== "object" || raw === null) continue;
5474
+ const entry = raw;
5475
+ const blocks = [
5476
+ [entry.env, "env"],
5477
+ [entry.headers, "header"]
5478
+ ];
5479
+ for (const [block, location] of blocks) {
5480
+ if (!block) continue;
5481
+ for (const [field, value] of Object.entries(block)) {
5482
+ if (typeof value !== "string" || value.length === 0) continue;
5483
+ if (isTemplated(value)) continue;
5484
+ if (!SECRET_KEY_NAME_RE.test(field) && !matchesKnownSecretShape(value)) continue;
5485
+ findings.push({ kind: "literal-secret-in-mcp", file, agent: agent2, server, field, location });
5486
+ }
5487
+ }
5488
+ }
5489
+ }
5490
+ function scanEnvIntegrations(file, agent2, findings) {
5491
+ let mode;
5492
+ try {
5493
+ mode = statSync4(file).mode;
5494
+ } catch {
5495
+ return;
5496
+ }
5497
+ if (isWorldReadable(mode)) {
5498
+ findings.push({ kind: "world-readable-secret-file", file, agent: agent2, mode: octal(mode) });
5499
+ }
5500
+ }
5501
+ function auditSecretLeaks(augmentedRoot) {
5502
+ const findings = [];
5503
+ let agents;
5504
+ try {
5505
+ agents = readdirSync6(augmentedRoot);
5506
+ } catch {
5507
+ return findings;
5508
+ }
5509
+ for (const agent2 of agents) {
5510
+ const agentDir = join18(augmentedRoot, agent2);
5511
+ try {
5512
+ if (!statSync4(agentDir).isDirectory()) continue;
5513
+ } catch {
5514
+ continue;
5515
+ }
5516
+ scanMcpFile(join18(agentDir, "provision", ".mcp.json"), agent2, findings);
5517
+ scanMcpFile(join18(agentDir, "project", ".mcp.json"), agent2, findings);
5518
+ scanEnvIntegrations(join18(agentDir, ".env.integrations"), agent2, findings);
5519
+ }
5520
+ return findings;
5521
+ }
5522
+ function formatSecretFinding(f) {
5523
+ const parts = [
5524
+ "[audit-secrets]",
5525
+ `kind=${f.kind}`,
5526
+ `agent=${f.agent}`,
5527
+ `file=${f.file}`
5528
+ ];
5529
+ if (f.server) parts.push(`server=${f.server}`);
5530
+ if (f.field) parts.push(`field=${f.field}`);
5531
+ if (f.location) parts.push(`location=${f.location}`);
5532
+ if (f.mode) parts.push(`mode=${f.mode}`);
5533
+ return parts.join(" ");
5534
+ }
5535
+
5536
+ // src/commands/audit-secrets.ts
5537
+ function describe(f) {
5538
+ if (f.kind === "literal-secret-in-mcp") {
5539
+ return `literal secret in ${f.location} field '${f.field}' of server '${f.server}'`;
5540
+ }
5541
+ return `secret file is mode ${f.mode} (expected 0600)`;
5542
+ }
5543
+ async function auditSecretsCommand(options = {}) {
5544
+ const root = options.root ?? join19(homedir8(), ".augmented");
5545
+ const findings = auditSecretLeaks(root);
5546
+ if (options.json) {
5547
+ process.stdout.write(`${JSON.stringify({ findings, root }, null, 2)}
5548
+ `);
5549
+ } else if (findings.length === 0) {
5550
+ process.stdout.write(
5551
+ `No literal secrets or world-readable secret files found under ${root}.
5552
+ `
5553
+ );
5554
+ } else {
5555
+ process.stdout.write(
5556
+ `Found ${findings.length} secret-handling issue${findings.length === 1 ? "" : "s"} under ${root}:
5557
+
5558
+ `
5559
+ );
5560
+ for (const f of findings) {
5561
+ process.stdout.write(` \u2022 [${f.agent}] ${describe(f)}
5562
+ `);
5563
+ process.stdout.write(` ${f.file}
5564
+ `);
5565
+ }
5566
+ process.stdout.write(
5567
+ "\nLiteral secrets in .mcp.json should be `${VAR}` placeholders with the raw value in .env.integrations (mode 0600). See docs/operator/credential-migration-eng5898.md.\n"
5568
+ );
5569
+ for (const f of findings) {
5570
+ process.stderr.write(`${formatSecretFinding(f)}
5571
+ `);
5572
+ }
5573
+ }
5574
+ if (options.strict && findings.length > 0) {
5575
+ process.exit(2);
5576
+ }
5577
+ }
5578
+
5409
5579
  // src/commands/integration.ts
5410
5580
  import chalk19 from "chalk";
5411
5581
  import ora17 from "ora";
@@ -5707,7 +5877,7 @@ function handleError(err) {
5707
5877
  }
5708
5878
 
5709
5879
  // src/bin/agt.ts
5710
- var cliVersion2 = true ? "0.27.65" : "dev";
5880
+ var cliVersion2 = true ? "0.27.67" : "dev";
5711
5881
  var program = new Command();
5712
5882
  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");
5713
5883
  program.hook("preAction", async (thisCommand, actionCommand) => {
@@ -5800,16 +5970,16 @@ host.command("pair <host-name>").description("Start an SSM port-forward + shell
5800
5970
  })
5801
5971
  );
5802
5972
  var manager = program.command("manager").description("Host config sync daemon \u2014 keeps local agent files in sync with API");
5803
- 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", join18(homedir8(), ".augmented")).option("--supervise", "Wrap the manager in a respawn-on-clean-exit loop so auto-upgrades can restart it transparently (ENG-4488)", false).action(managerStartCommand);
5804
- manager.command("stop").description("Stop the running manager daemon").option("--config-dir <dir>", "Config directory for agent files", join18(homedir8(), ".augmented")).action(managerStopCommand);
5805
- manager.command("status").description("Show the current manager daemon status and discovered agents").option("--config-dir <dir>", "Config directory for agent files", join18(homedir8(), ".augmented")).action(managerStatusCommand);
5806
- manager.command("watch").description("Live TUI dashboard \u2014 per-agent boxes, drill-in, log tail. Read-only (ENG-4555).").option("--config-dir <dir>", "Config directory for agent files", join18(homedir8(), ".augmented")).option("--no-tui", "Skip the TUI and stream the manager log to stdout instead (CI / scripts)").action(managerWatchCommand);
5807
- manager.command("install").description("Install OS-level supervisor (launchd LaunchAgent on macOS) so the manager auto-restarts after crash, reboot, or self-update (ENG-4593)").option("--interval <seconds>", "Poll interval in seconds (min 5)", "10").option("--config-dir <dir>", "Config directory for agent files", join18(homedir8(), ".augmented")).action(managerInstallCommand);
5973
+ 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", join20(homedir9(), ".augmented")).option("--supervise", "Wrap the manager in a respawn-on-clean-exit loop so auto-upgrades can restart it transparently (ENG-4488)", false).action(managerStartCommand);
5974
+ manager.command("stop").description("Stop the running manager daemon").option("--config-dir <dir>", "Config directory for agent files", join20(homedir9(), ".augmented")).action(managerStopCommand);
5975
+ manager.command("status").description("Show the current manager daemon status and discovered agents").option("--config-dir <dir>", "Config directory for agent files", join20(homedir9(), ".augmented")).action(managerStatusCommand);
5976
+ manager.command("watch").description("Live TUI dashboard \u2014 per-agent boxes, drill-in, log tail. Read-only (ENG-4555).").option("--config-dir <dir>", "Config directory for agent files", join20(homedir9(), ".augmented")).option("--no-tui", "Skip the TUI and stream the manager log to stdout instead (CI / scripts)").action(managerWatchCommand);
5977
+ manager.command("install").description("Install OS-level supervisor (launchd LaunchAgent on macOS) so the manager auto-restarts after crash, reboot, or self-update (ENG-4593)").option("--interval <seconds>", "Poll interval in seconds (min 5)", "10").option("--config-dir <dir>", "Config directory for agent files", join20(homedir9(), ".augmented")).action(managerInstallCommand);
5808
5978
  manager.command("uninstall").description("Remove the OS-level supervisor (launchctl unload + delete plist on macOS). Idempotent.").action(managerUninstallCommand);
5809
5979
  manager.command("install-system-unit").description("Install a system-level systemd unit (Linux, root) so the manager auto-starts on every boot. For headless EC2 hosts \u2014 survives reboot without `loginctl enable-linger`. (ENG-4706)").option("--interval <seconds>", "Poll interval in seconds (min 5)", "10").option("--config-dir <dir>", "Config directory for agent files (defaults to /root/.augmented or /home/<user>/.augmented)").option("--user <name>", "Unix user the manager runs as", "root").action(managerInstallSystemUnitCommand);
5810
5980
  manager.command("uninstall-system-unit").description("Remove the system-level systemd unit (Linux, root). Idempotent. (ENG-4706)").action(managerUninstallSystemUnitCommand);
5811
5981
  var agent = program.command("agent").description("Inspect and manage agents");
5812
- agent.command("show <code-name>").description("Display an agent's provisioned OpenClaw configuration").option("--config-dir <dir>", "Config directory", join18(homedir8(), ".augmented")).option("--all-channels", "Show all channels (including disabled)").action(agentShowCommand);
5982
+ agent.command("show <code-name>").description("Display an agent's provisioned OpenClaw configuration").option("--config-dir <dir>", "Config directory", join20(homedir9(), ".augmented")).option("--all-channels", "Show all channels (including disabled)").action(agentShowCommand);
5813
5983
  var kanban = program.command("kanban").description("Manage agent kanban boards");
5814
5984
  kanban.command("list").description("List kanban board items for an agent").requiredOption("--agent <code-name>", "Agent code name").action(kanbanListCommand);
5815
5985
  kanban.command("add <title>").description("Add a new item to an agent kanban board").requiredOption("--agent <code-name>", "Agent code name").option("--priority <1|2|3>", "Priority: 1=high, 2=medium, 3=low", "2").option("--status <status>", "Initial status: backlog | todo | in_progress", "todo").option("--description <text>", "Item description").option("--estimate <minutes>", "Estimated time in minutes").option("--deliverable <text>", "Expected output/deliverable").action(kanbanAddCommand);
@@ -5840,6 +6010,7 @@ program.command("update").description("Check for and install CLI updates").optio
5840
6010
  var audit = program.command("audit").description("Operator audits \u2014 sub-agent MCP bindings, etc.");
5841
6011
  audit.command("subagents").description("Find sub-agents whose tools allowlist would block mcp__* calls on dispatch (ENG-5897)").option("--strict", "Exit with code 2 if any findings (suitable for CI gating)").option("--json", "Emit findings as JSON to stdout").action(auditSubagentsCommand);
5842
6012
  audit.command("mcp-render").description("Find managed agents whose subagent .md tools allowlist is stale vs .mcp.json (ENG-5922)").option("--strict", "Exit with code 2 if any findings (suitable for CI gating)").option("--json", "Emit findings as JSON to stdout").action(auditMcpRenderCommand);
6013
+ audit.command("secrets").description("Find literal secrets in .mcp.json and world-readable secret files (ENG-5901)").option("--strict", "Exit with code 2 if any findings (suitable for CI gating)").option("--json", "Emit findings as JSON to stdout").action(auditSecretsCommand);
5843
6014
  var skipUpdateCheck = process.argv.includes("--skip-update-check") || process.argv.includes("--json") || process.argv[2] === "update";
5844
6015
  if (!skipUpdateCheck) {
5845
6016
  checkForUpdateOnStartup().catch(() => {