@integrity-labs/agt-cli 0.26.2-eng5706.1 → 0.27.0
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 +651 -186
- package/dist/bin/agt.js.map +1 -1
- package/dist/{chunk-U3HCB23E.js → chunk-BKLEFKUZ.js} +10 -8
- package/dist/chunk-BKLEFKUZ.js.map +1 -0
- package/dist/{chunk-QSDDP7JN.js → chunk-LJZK5RL3.js} +2 -2
- package/dist/{chunk-LBYU24PW.js → chunk-YWXPSVI5.js} +22 -8
- package/dist/{chunk-LBYU24PW.js.map → chunk-YWXPSVI5.js.map} +1 -1
- package/dist/{claude-pair-runtime-2NNAUBP3.js → claude-pair-runtime-3UQKYKMO.js} +2 -2
- package/dist/lib/manager-worker.js +7 -7
- package/dist/lib/manager-worker.js.map +1 -1
- package/dist/mcp/direct-chat-channel.js +168 -0
- package/dist/mcp/slack-channel.js +101 -16
- package/dist/mcp/telegram-channel.js +212 -40
- package/dist/{persistent-session-K4X76LLB.js → persistent-session-SE3E72ET.js} +3 -3
- package/dist/{responsiveness-probe-QBV43SJ7.js → responsiveness-probe-MTMEIXEL.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-U3HCB23E.js.map +0 -1
- /package/dist/{chunk-QSDDP7JN.js.map → chunk-LJZK5RL3.js.map} +0 -0
- /package/dist/{claude-pair-runtime-2NNAUBP3.js.map → claude-pair-runtime-3UQKYKMO.js.map} +0 -0
- /package/dist/{persistent-session-K4X76LLB.js.map → persistent-session-SE3E72ET.js.map} +0 -0
- /package/dist/{responsiveness-probe-QBV43SJ7.js.map → responsiveness-probe-MTMEIXEL.js.map} +0 -0
package/dist/bin/agt.js
CHANGED
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
success,
|
|
27
27
|
table,
|
|
28
28
|
warn
|
|
29
|
-
} from "../chunk-
|
|
29
|
+
} from "../chunk-YWXPSVI5.js";
|
|
30
30
|
import {
|
|
31
31
|
CHANNEL_REGISTRY,
|
|
32
32
|
DEPLOYMENT_TEMPLATES,
|
|
@@ -52,11 +52,11 @@ import {
|
|
|
52
52
|
renderTemplate,
|
|
53
53
|
resolveChannels,
|
|
54
54
|
serializeManifestForSlackCli
|
|
55
|
-
} from "../chunk-
|
|
55
|
+
} from "../chunk-BKLEFKUZ.js";
|
|
56
56
|
|
|
57
57
|
// src/bin/agt.ts
|
|
58
|
-
import { join as
|
|
59
|
-
import { homedir as
|
|
58
|
+
import { join as join12 } from "path";
|
|
59
|
+
import { homedir as homedir5 } from "os";
|
|
60
60
|
import { Command } from "commander";
|
|
61
61
|
|
|
62
62
|
// src/commands/whoami.ts
|
|
@@ -1552,14 +1552,461 @@ async function provisionCommand(codeName, options) {
|
|
|
1552
1552
|
info(` agt drift check ${agentData.code_name}`);
|
|
1553
1553
|
}
|
|
1554
1554
|
|
|
1555
|
-
// src/commands/
|
|
1555
|
+
// src/commands/impersonate.ts
|
|
1556
1556
|
import chalk10 from "chalk";
|
|
1557
1557
|
import ora10 from "ora";
|
|
1558
|
+
import { spawn } from "child_process";
|
|
1559
|
+
import { writeFileSync as writeFileSync5 } from "fs";
|
|
1560
|
+
import { join as join7 } from "path";
|
|
1561
|
+
|
|
1562
|
+
// src/lib/impersonate-flag.ts
|
|
1563
|
+
var ENV_VAR = "AGT_IMPERSONATE_ENABLED";
|
|
1564
|
+
function impersonateEnabled() {
|
|
1565
|
+
const v = process.env[ENV_VAR];
|
|
1566
|
+
if (!v) return false;
|
|
1567
|
+
const trimmed = v.trim().toLowerCase();
|
|
1568
|
+
return !(trimmed === "" || trimmed === "0" || trimmed === "false" || trimmed === "no");
|
|
1569
|
+
}
|
|
1570
|
+
function requireImpersonateEnabled() {
|
|
1571
|
+
if (impersonateEnabled()) return;
|
|
1572
|
+
process.stderr.write(
|
|
1573
|
+
`
|
|
1574
|
+
The \`agt impersonate\` commands are gated behind a feature flag.
|
|
1575
|
+
|
|
1576
|
+
To enable them, set ${ENV_VAR}=1 in your environment:
|
|
1577
|
+
|
|
1578
|
+
export ${ENV_VAR}=1
|
|
1579
|
+
|
|
1580
|
+
These commands are part of the operator-impersonates-agent v0 flow
|
|
1581
|
+
(ENG-5687 / ENG-5688). The gating will be removed once the
|
|
1582
|
+
surrounding pieces (ENG-5689 channel-MCP refusal, ENG-5690 slash
|
|
1583
|
+
commands) land and the end-to-end smoke test has been run against
|
|
1584
|
+
production.
|
|
1585
|
+
|
|
1586
|
+
`
|
|
1587
|
+
);
|
|
1588
|
+
process.exit(2);
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
// src/lib/impersonate-state.ts
|
|
1592
|
+
import {
|
|
1593
|
+
chmodSync,
|
|
1594
|
+
existsSync as existsSync2,
|
|
1595
|
+
mkdirSync as mkdirSync4,
|
|
1596
|
+
readFileSync as readFileSync2,
|
|
1597
|
+
renameSync,
|
|
1598
|
+
rmSync,
|
|
1599
|
+
writeFileSync as writeFileSync4
|
|
1600
|
+
} from "fs";
|
|
1601
|
+
import { homedir } from "os";
|
|
1602
|
+
import { join as join6 } from "path";
|
|
1603
|
+
var IMPERSONATE_ROOT = join6(homedir(), ".augmented-impersonate");
|
|
1604
|
+
var ACTIVE_DIR = join6(IMPERSONATE_ROOT, "active");
|
|
1605
|
+
var ACTIVE_MANIFEST_PATH = join6(ACTIVE_DIR, "manifest.json");
|
|
1606
|
+
var CODE_NAME_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
1607
|
+
function assertValidCodeName(codeName) {
|
|
1608
|
+
if (!CODE_NAME_RE.test(codeName)) {
|
|
1609
|
+
throw new Error(
|
|
1610
|
+
`Invalid agent code_name "${codeName}" \u2014 must be lowercase kebab-case (a-z, 0-9, hyphens; no leading/trailing/consecutive hyphens).`
|
|
1611
|
+
);
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
function getImpersonateCodeNameDir(codeName) {
|
|
1615
|
+
assertValidCodeName(codeName);
|
|
1616
|
+
return join6(IMPERSONATE_ROOT, codeName);
|
|
1617
|
+
}
|
|
1618
|
+
function ensureCodeNameDir(codeName) {
|
|
1619
|
+
assertValidCodeName(codeName);
|
|
1620
|
+
const dir = getImpersonateCodeNameDir(codeName);
|
|
1621
|
+
mkdirSync4(dir, { recursive: true });
|
|
1622
|
+
return dir;
|
|
1623
|
+
}
|
|
1624
|
+
function readActiveManifest() {
|
|
1625
|
+
if (!existsSync2(ACTIVE_MANIFEST_PATH)) return null;
|
|
1626
|
+
try {
|
|
1627
|
+
const raw = readFileSync2(ACTIVE_MANIFEST_PATH, "utf-8");
|
|
1628
|
+
const parsed = JSON.parse(raw);
|
|
1629
|
+
const backupsOk = typeof parsed.backups === "object" && parsed.backups !== null && Object.values(parsed.backups).every(
|
|
1630
|
+
(v) => v === "had-file" || v === "was-symlink" || v === "didnt-exist"
|
|
1631
|
+
);
|
|
1632
|
+
if (typeof parsed.code_name !== "string" || typeof parsed.agent_id !== "string" || typeof parsed.team_id !== "string" || typeof parsed.token !== "string" || typeof parsed.expires_at !== "number" || typeof parsed.session_id !== "string" || typeof parsed.minted_at !== "string" || typeof parsed.project_cwd !== "string" || // ENG-5688 (redesigned): host required so `exit` calls /stop against
|
|
1633
|
+
// the right environment. Required, not optional, because writing the
|
|
1634
|
+
// wrong host on an existing session would point exit at a server
|
|
1635
|
+
// that has no record of the session — silently misleading.
|
|
1636
|
+
typeof parsed.host !== "string" || parsed.host.length === 0 || !backupsOk) {
|
|
1637
|
+
return null;
|
|
1638
|
+
}
|
|
1639
|
+
return parsed;
|
|
1640
|
+
} catch {
|
|
1641
|
+
return null;
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
function writeActiveManifest(manifest) {
|
|
1645
|
+
mkdirSync4(ACTIVE_DIR, { recursive: true, mode: 448 });
|
|
1646
|
+
const tmp = ACTIVE_MANIFEST_PATH + ".tmp";
|
|
1647
|
+
writeFileSync4(tmp, JSON.stringify(manifest, null, 2), { mode: 384 });
|
|
1648
|
+
chmodSync(tmp, 384);
|
|
1649
|
+
renameSync(tmp, ACTIVE_MANIFEST_PATH);
|
|
1650
|
+
}
|
|
1651
|
+
function clearActiveManifest() {
|
|
1652
|
+
if (existsSync2(ACTIVE_MANIFEST_PATH)) {
|
|
1653
|
+
rmSync(ACTIVE_MANIFEST_PATH);
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
function isExpired(manifest, now = Date.now()) {
|
|
1657
|
+
return manifest.expires_at * 1e3 <= now;
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
// src/lib/impersonate-fs-ops.ts
|
|
1661
|
+
import {
|
|
1662
|
+
existsSync as existsSync3,
|
|
1663
|
+
lstatSync,
|
|
1664
|
+
readlinkSync,
|
|
1665
|
+
realpathSync,
|
|
1666
|
+
renameSync as renameSync2,
|
|
1667
|
+
symlinkSync,
|
|
1668
|
+
unlinkSync
|
|
1669
|
+
} from "fs";
|
|
1670
|
+
import { homedir as homedir2 } from "os";
|
|
1671
|
+
import { sep } from "path";
|
|
1672
|
+
var BACKUP_SUFFIX = ".pre-impersonate-backup";
|
|
1673
|
+
function swapInPersonaFile(projectPath, personaPath) {
|
|
1674
|
+
let kind;
|
|
1675
|
+
if (!lstatSafe(projectPath)) {
|
|
1676
|
+
kind = "didnt-exist";
|
|
1677
|
+
} else {
|
|
1678
|
+
const stat = lstatSync(projectPath);
|
|
1679
|
+
if (stat.isSymbolicLink()) {
|
|
1680
|
+
const target = readlinkSync(projectPath);
|
|
1681
|
+
const ourRoot = realpathSync(homedir2()) + sep + ".augmented-impersonate" + sep;
|
|
1682
|
+
let resolvedTarget;
|
|
1683
|
+
try {
|
|
1684
|
+
resolvedTarget = realpathSync(projectPath);
|
|
1685
|
+
} catch {
|
|
1686
|
+
throw new Error(
|
|
1687
|
+
`${projectPath} is a broken symlink (target ${target}). Resolve manually and retry.`
|
|
1688
|
+
);
|
|
1689
|
+
}
|
|
1690
|
+
if (resolvedTarget.startsWith(ourRoot)) {
|
|
1691
|
+
unlinkSync(projectPath);
|
|
1692
|
+
kind = "was-symlink";
|
|
1693
|
+
} else {
|
|
1694
|
+
throw new Error(
|
|
1695
|
+
`${projectPath} is a symlink to ${target} (resolved: ${resolvedTarget}). Refusing to replace it \u2014 it doesn't live under ${ourRoot}. Resolve manually and retry.`
|
|
1696
|
+
);
|
|
1697
|
+
}
|
|
1698
|
+
} else {
|
|
1699
|
+
const backupPath = projectPath + BACKUP_SUFFIX;
|
|
1700
|
+
if (existsSync3(backupPath)) {
|
|
1701
|
+
throw new Error(
|
|
1702
|
+
`${backupPath} already exists from a previous run. Remove it manually if you're sure it's stale, then retry.`
|
|
1703
|
+
);
|
|
1704
|
+
}
|
|
1705
|
+
renameSync2(projectPath, backupPath);
|
|
1706
|
+
kind = "had-file";
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
symlinkSync(personaPath, projectPath, "file");
|
|
1710
|
+
return kind;
|
|
1711
|
+
}
|
|
1712
|
+
function restoreSwapped(projectPath, kind) {
|
|
1713
|
+
if (lstatSafe(projectPath)) {
|
|
1714
|
+
const stat = lstatSync(projectPath);
|
|
1715
|
+
if (stat.isSymbolicLink()) {
|
|
1716
|
+
unlinkSync(projectPath);
|
|
1717
|
+
} else {
|
|
1718
|
+
return;
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
if (kind === "had-file") {
|
|
1722
|
+
const backupPath = projectPath + BACKUP_SUFFIX;
|
|
1723
|
+
if (existsSync3(backupPath)) {
|
|
1724
|
+
renameSync2(backupPath, projectPath);
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
function lstatSafe(path) {
|
|
1729
|
+
try {
|
|
1730
|
+
lstatSync(path);
|
|
1731
|
+
return true;
|
|
1732
|
+
} catch {
|
|
1733
|
+
return false;
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1737
|
+
// src/commands/impersonate.ts
|
|
1738
|
+
var SWAP_FILES = ["CLAUDE.md", ".mcp.json"];
|
|
1739
|
+
var AGENT_IMPERSONATION_HEADER = "X-Agent-Impersonation";
|
|
1740
|
+
var REDEEM_TOKEN_RE = /^[A-HJ-NP-Z2-9]{4}-[A-HJ-NP-Z2-9]{4}$/i;
|
|
1741
|
+
async function impersonateConnectCommand(token, options = {}) {
|
|
1742
|
+
requireImpersonateEnabled();
|
|
1743
|
+
const json = isJsonMode();
|
|
1744
|
+
const shouldLaunchClaude = !options.noLaunch && !json;
|
|
1745
|
+
if (!REDEEM_TOKEN_RE.test(token)) {
|
|
1746
|
+
const msg = "Token is not in the expected XXXX-XXXX format. Copy the full `agt impersonate connect <token>` command from the webapp.";
|
|
1747
|
+
if (json) jsonOutput({ ok: false, error: msg });
|
|
1748
|
+
else error(msg);
|
|
1749
|
+
process.exitCode = 1;
|
|
1750
|
+
return;
|
|
1751
|
+
}
|
|
1752
|
+
const existing = readActiveManifest();
|
|
1753
|
+
if (existing) {
|
|
1754
|
+
const msg = `An impersonation session is already active (${existing.code_name}). Run \`agt impersonate exit\` first.`;
|
|
1755
|
+
if (json) jsonOutput({ ok: false, error: msg });
|
|
1756
|
+
else error(msg);
|
|
1757
|
+
process.exitCode = 1;
|
|
1758
|
+
return;
|
|
1759
|
+
}
|
|
1760
|
+
if (!json) console.log(chalk10.bold("\nAugmented \u2014 Impersonate\n"));
|
|
1761
|
+
const spinner = ora10({
|
|
1762
|
+
text: "Redeeming token\u2026",
|
|
1763
|
+
isSilent: json
|
|
1764
|
+
});
|
|
1765
|
+
spinner.start();
|
|
1766
|
+
const host2 = getHost();
|
|
1767
|
+
let bundle;
|
|
1768
|
+
try {
|
|
1769
|
+
const res = await fetch(`${host2}/impersonate/agent/redeem`, {
|
|
1770
|
+
method: "POST",
|
|
1771
|
+
headers: { "Content-Type": "application/json" },
|
|
1772
|
+
body: JSON.stringify({ redeem_token: token })
|
|
1773
|
+
});
|
|
1774
|
+
if (!res.ok) {
|
|
1775
|
+
const body = await res.json().catch(() => ({}));
|
|
1776
|
+
const detail = body["error"] ?? `HTTP ${res.status}`;
|
|
1777
|
+
throw new Error(`${detail} (HTTP ${res.status})`);
|
|
1778
|
+
}
|
|
1779
|
+
bundle = await res.json();
|
|
1780
|
+
} catch (err) {
|
|
1781
|
+
spinner.fail("Failed to redeem token.");
|
|
1782
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1783
|
+
if (json) jsonOutput({ ok: false, error: msg });
|
|
1784
|
+
else error(msg);
|
|
1785
|
+
process.exitCode = 1;
|
|
1786
|
+
return;
|
|
1787
|
+
}
|
|
1788
|
+
spinner.text = "Writing persona files\u2026";
|
|
1789
|
+
const personaDir = ensureCodeNameDir(bundle.agent.code_name);
|
|
1790
|
+
writeFileSync5(
|
|
1791
|
+
join7(personaDir, "CLAUDE.md"),
|
|
1792
|
+
bundle.artifacts["CLAUDE.md"]
|
|
1793
|
+
);
|
|
1794
|
+
writeFileSync5(
|
|
1795
|
+
join7(personaDir, ".mcp.json"),
|
|
1796
|
+
bundle.artifacts[".mcp.json"]
|
|
1797
|
+
);
|
|
1798
|
+
spinner.text = "Swapping persona files\u2026";
|
|
1799
|
+
const projectCwd = process.cwd();
|
|
1800
|
+
const backups = {};
|
|
1801
|
+
const swapped = [];
|
|
1802
|
+
try {
|
|
1803
|
+
for (const file of SWAP_FILES) {
|
|
1804
|
+
const projectPath = join7(projectCwd, file);
|
|
1805
|
+
const kind = swapInPersonaFile(projectPath, join7(personaDir, file));
|
|
1806
|
+
backups[file] = kind;
|
|
1807
|
+
swapped.push({ file, kind });
|
|
1808
|
+
}
|
|
1809
|
+
const manifest = {
|
|
1810
|
+
code_name: bundle.agent.code_name,
|
|
1811
|
+
agent_id: bundle.agent.agent_id,
|
|
1812
|
+
team_id: bundle.agent.team_id,
|
|
1813
|
+
token: bundle.token,
|
|
1814
|
+
expires_at: bundle.expires_at,
|
|
1815
|
+
session_id: bundle.session_id,
|
|
1816
|
+
minted_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1817
|
+
project_cwd: projectCwd,
|
|
1818
|
+
host: host2,
|
|
1819
|
+
backups
|
|
1820
|
+
};
|
|
1821
|
+
writeActiveManifest(manifest);
|
|
1822
|
+
} catch (err) {
|
|
1823
|
+
for (const { file, kind } of [...swapped].reverse()) {
|
|
1824
|
+
try {
|
|
1825
|
+
restoreSwapped(join7(projectCwd, file), kind);
|
|
1826
|
+
} catch {
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
spinner.fail("Failed to enter impersonation; rolled back changes.");
|
|
1830
|
+
throw err;
|
|
1831
|
+
}
|
|
1832
|
+
spinner.stop();
|
|
1833
|
+
if (json) {
|
|
1834
|
+
jsonOutput({
|
|
1835
|
+
ok: true,
|
|
1836
|
+
code_name: bundle.agent.code_name,
|
|
1837
|
+
agent_id: bundle.agent.agent_id,
|
|
1838
|
+
session_id: bundle.session_id,
|
|
1839
|
+
expires_at: bundle.expires_at,
|
|
1840
|
+
project_cwd: projectCwd,
|
|
1841
|
+
host: host2,
|
|
1842
|
+
swapped_files: SWAP_FILES
|
|
1843
|
+
});
|
|
1844
|
+
return;
|
|
1845
|
+
}
|
|
1846
|
+
success(`Impersonating ${chalk10.bold(bundle.agent.code_name)}`);
|
|
1847
|
+
console.log();
|
|
1848
|
+
console.log(` Agent ID: ${chalk10.dim(bundle.agent.agent_id)}`);
|
|
1849
|
+
console.log(` Team: ${chalk10.dim(bundle.agent.team_id)}`);
|
|
1850
|
+
console.log(` Session: ${chalk10.dim(bundle.session_id)}`);
|
|
1851
|
+
console.log(
|
|
1852
|
+
` Expires: ${chalk10.dim(new Date(bundle.expires_at * 1e3).toISOString())}`
|
|
1853
|
+
);
|
|
1854
|
+
console.log(` Project: ${chalk10.dim(projectCwd)}`);
|
|
1855
|
+
console.log(` Host: ${chalk10.dim(host2)}`);
|
|
1856
|
+
console.log(` Swapped: ${chalk10.dim(SWAP_FILES.join(", "))}`);
|
|
1857
|
+
console.log();
|
|
1858
|
+
if (!shouldLaunchClaude) {
|
|
1859
|
+
info(
|
|
1860
|
+
"Restart Claude Code to pick up the agent's persona + MCP server set. Claude Code 2.1.152 does not honour `tools/list_changed` in-session (spike PoC, ENG-4733)."
|
|
1861
|
+
);
|
|
1862
|
+
console.log();
|
|
1863
|
+
info("When done: `agt impersonate exit`");
|
|
1864
|
+
return;
|
|
1865
|
+
}
|
|
1866
|
+
info("Launching Claude Code\u2026 (use `--no-launch` next time to skip)");
|
|
1867
|
+
console.log();
|
|
1868
|
+
await new Promise((resolve2) => {
|
|
1869
|
+
const child = spawn("claude", {
|
|
1870
|
+
stdio: "inherit",
|
|
1871
|
+
cwd: projectCwd
|
|
1872
|
+
});
|
|
1873
|
+
child.on("error", (err) => {
|
|
1874
|
+
if (err.code === "ENOENT") {
|
|
1875
|
+
error(
|
|
1876
|
+
`\`claude\` binary not found on PATH. Start Claude Code yourself from ${projectCwd}, or install the CLI from claude.ai/code.`
|
|
1877
|
+
);
|
|
1878
|
+
process.exitCode = 1;
|
|
1879
|
+
} else {
|
|
1880
|
+
error(`Failed to launch Claude Code: ${err.message}`);
|
|
1881
|
+
process.exitCode = 1;
|
|
1882
|
+
}
|
|
1883
|
+
resolve2();
|
|
1884
|
+
});
|
|
1885
|
+
child.on("exit", (code, signal) => {
|
|
1886
|
+
if (code !== null && code !== 0) process.exitCode = code;
|
|
1887
|
+
if (signal) process.exitCode = 130;
|
|
1888
|
+
resolve2();
|
|
1889
|
+
});
|
|
1890
|
+
});
|
|
1891
|
+
console.log();
|
|
1892
|
+
info("When done: `agt impersonate exit` (still active).");
|
|
1893
|
+
}
|
|
1894
|
+
async function impersonateCurrentCommand() {
|
|
1895
|
+
requireImpersonateEnabled();
|
|
1896
|
+
const json = isJsonMode();
|
|
1897
|
+
const manifest = readActiveManifest();
|
|
1898
|
+
if (!manifest) {
|
|
1899
|
+
if (json) jsonOutput({ ok: true, active: null });
|
|
1900
|
+
else info("No active impersonation.");
|
|
1901
|
+
return;
|
|
1902
|
+
}
|
|
1903
|
+
const expired = isExpired(manifest);
|
|
1904
|
+
if (json) {
|
|
1905
|
+
jsonOutput({
|
|
1906
|
+
ok: true,
|
|
1907
|
+
active: {
|
|
1908
|
+
code_name: manifest.code_name,
|
|
1909
|
+
agent_id: manifest.agent_id,
|
|
1910
|
+
team_id: manifest.team_id,
|
|
1911
|
+
session_id: manifest.session_id,
|
|
1912
|
+
minted_at: manifest.minted_at,
|
|
1913
|
+
expires_at: manifest.expires_at,
|
|
1914
|
+
expired,
|
|
1915
|
+
project_cwd: manifest.project_cwd,
|
|
1916
|
+
host: manifest.host
|
|
1917
|
+
}
|
|
1918
|
+
});
|
|
1919
|
+
return;
|
|
1920
|
+
}
|
|
1921
|
+
console.log();
|
|
1922
|
+
console.log(chalk10.bold(`Impersonating: ${manifest.code_name}`));
|
|
1923
|
+
console.log(` Agent ID: ${chalk10.dim(manifest.agent_id)}`);
|
|
1924
|
+
console.log(` Team: ${chalk10.dim(manifest.team_id)}`);
|
|
1925
|
+
console.log(` Session: ${chalk10.dim(manifest.session_id)}`);
|
|
1926
|
+
console.log(` Minted at: ${chalk10.dim(manifest.minted_at)}`);
|
|
1927
|
+
console.log(
|
|
1928
|
+
` Expires at: ${chalk10.dim(new Date(manifest.expires_at * 1e3).toISOString())} ` + (expired ? chalk10.red("(EXPIRED \u2014 run `agt impersonate exit`)") : "")
|
|
1929
|
+
);
|
|
1930
|
+
console.log(` Project: ${chalk10.dim(manifest.project_cwd)}`);
|
|
1931
|
+
console.log(` Host: ${chalk10.dim(manifest.host)}`);
|
|
1932
|
+
console.log();
|
|
1933
|
+
}
|
|
1934
|
+
async function impersonateExitCommand() {
|
|
1935
|
+
requireImpersonateEnabled();
|
|
1936
|
+
const json = isJsonMode();
|
|
1937
|
+
const manifest = readActiveManifest();
|
|
1938
|
+
if (!manifest) {
|
|
1939
|
+
if (json) jsonOutput({ ok: true, was_active: false });
|
|
1940
|
+
else info("No active impersonation to exit.");
|
|
1941
|
+
return;
|
|
1942
|
+
}
|
|
1943
|
+
let stopOk = true;
|
|
1944
|
+
let stopDetail = null;
|
|
1945
|
+
try {
|
|
1946
|
+
if (process.env["AGT_API_KEY"]) {
|
|
1947
|
+
await api.post("/impersonate/agent/stop", void 0, {
|
|
1948
|
+
[AGENT_IMPERSONATION_HEADER]: manifest.token
|
|
1949
|
+
});
|
|
1950
|
+
} else {
|
|
1951
|
+
const res = await fetch(`${manifest.host}/impersonate/agent/stop`, {
|
|
1952
|
+
method: "POST",
|
|
1953
|
+
headers: {
|
|
1954
|
+
"Content-Type": "application/json",
|
|
1955
|
+
[AGENT_IMPERSONATION_HEADER]: manifest.token
|
|
1956
|
+
}
|
|
1957
|
+
});
|
|
1958
|
+
if (!res.ok) {
|
|
1959
|
+
const body = await res.json().catch(() => ({}));
|
|
1960
|
+
const detail = body["error"] ?? `HTTP ${res.status}`;
|
|
1961
|
+
stopOk = false;
|
|
1962
|
+
stopDetail = `${detail} (HTTP ${res.status})`;
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
} catch (err) {
|
|
1966
|
+
stopOk = false;
|
|
1967
|
+
stopDetail = err instanceof ApiError ? `${err.message} (HTTP ${err.status})` : err instanceof Error ? err.message : String(err);
|
|
1968
|
+
}
|
|
1969
|
+
const restored = [];
|
|
1970
|
+
for (const [file, kind] of Object.entries(manifest.backups)) {
|
|
1971
|
+
const projectPath = join7(manifest.project_cwd, file);
|
|
1972
|
+
try {
|
|
1973
|
+
restoreSwapped(projectPath, kind);
|
|
1974
|
+
restored.push(file);
|
|
1975
|
+
} catch (err) {
|
|
1976
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1977
|
+
if (!json) error(`Failed to restore ${file}: ${msg}`);
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
clearActiveManifest();
|
|
1981
|
+
if (json) {
|
|
1982
|
+
jsonOutput({
|
|
1983
|
+
ok: true,
|
|
1984
|
+
was_active: true,
|
|
1985
|
+
session_id: manifest.session_id,
|
|
1986
|
+
stop_acknowledged: stopOk,
|
|
1987
|
+
stop_detail: stopDetail,
|
|
1988
|
+
restored
|
|
1989
|
+
});
|
|
1990
|
+
return;
|
|
1991
|
+
}
|
|
1992
|
+
success(`Exited impersonation: ${manifest.code_name}`);
|
|
1993
|
+
if (restored.length > 0) {
|
|
1994
|
+
info(`Restored: ${restored.join(", ")}`);
|
|
1995
|
+
}
|
|
1996
|
+
if (!stopOk) {
|
|
1997
|
+
info(`Note: server-side stop not acknowledged (${stopDetail}). Token will expire naturally.`);
|
|
1998
|
+
}
|
|
1999
|
+
info("Restart Claude Code to pick up your original persona.");
|
|
2000
|
+
}
|
|
2001
|
+
|
|
2002
|
+
// src/commands/drift.ts
|
|
2003
|
+
import chalk11 from "chalk";
|
|
2004
|
+
import ora11 from "ora";
|
|
1558
2005
|
|
|
1559
2006
|
// ../../packages/core/dist/drift/live-state-reader.js
|
|
1560
2007
|
import { readFile } from "fs/promises";
|
|
1561
2008
|
import { createHash } from "crypto";
|
|
1562
|
-
import { join as
|
|
2009
|
+
import { join as join8 } from "path";
|
|
1563
2010
|
import JSON5 from "json5";
|
|
1564
2011
|
async function hashFile(filePath) {
|
|
1565
2012
|
try {
|
|
@@ -1583,8 +2030,8 @@ async function readLiveState(options) {
|
|
|
1583
2030
|
const toolsFile = trackedFiles.find((f) => f.includes("TOOLS")) ?? "TOOLS.md";
|
|
1584
2031
|
const [frameworkConfig, charterHash, toolsHash] = await Promise.all([
|
|
1585
2032
|
readJsonFile(options.configPath),
|
|
1586
|
-
hashFile(
|
|
1587
|
-
hashFile(
|
|
2033
|
+
hashFile(join8(options.teamDir, charterFile)),
|
|
2034
|
+
hashFile(join8(options.teamDir, toolsFile))
|
|
1588
2035
|
]);
|
|
1589
2036
|
return {
|
|
1590
2037
|
frameworkConfig,
|
|
@@ -1599,15 +2046,15 @@ var KEBAB_CASE_RE = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;
|
|
|
1599
2046
|
function severityColor(severity) {
|
|
1600
2047
|
switch (severity) {
|
|
1601
2048
|
case "critical":
|
|
1602
|
-
return
|
|
2049
|
+
return chalk11.red(severity);
|
|
1603
2050
|
case "warning":
|
|
1604
|
-
return
|
|
2051
|
+
return chalk11.yellow(severity);
|
|
1605
2052
|
default:
|
|
1606
|
-
return
|
|
2053
|
+
return chalk11.dim(severity);
|
|
1607
2054
|
}
|
|
1608
2055
|
}
|
|
1609
2056
|
function printDriftReport(report) {
|
|
1610
|
-
console.log(
|
|
2057
|
+
console.log(chalk11.bold(`
|
|
1611
2058
|
Drift Report: ${report.codeName}
|
|
1612
2059
|
`));
|
|
1613
2060
|
info(`Agent ID: ${report.agentId}`);
|
|
@@ -1636,7 +2083,7 @@ async function driftCheckCommand(codeName, opts) {
|
|
|
1636
2083
|
return;
|
|
1637
2084
|
}
|
|
1638
2085
|
const useJson = opts.json || isJsonMode();
|
|
1639
|
-
const spinner =
|
|
2086
|
+
const spinner = ora11({ text: `Checking drift for "${codeName}"\u2026`, isSilent: useJson });
|
|
1640
2087
|
spinner.start();
|
|
1641
2088
|
try {
|
|
1642
2089
|
const driftData = await api.get(`/agents/${encodeURIComponent(codeName)}/drift-data`);
|
|
@@ -1718,14 +2165,14 @@ async function driftWatchCommand(codeName, opts) {
|
|
|
1718
2165
|
const trackedFiles = adapter.driftTrackedFiles();
|
|
1719
2166
|
if (!useJson) {
|
|
1720
2167
|
console.log(
|
|
1721
|
-
|
|
2168
|
+
chalk11.bold(`
|
|
1722
2169
|
Watching drift for "${codeName}" every ${intervalSec}s. Press Ctrl+C to stop.
|
|
1723
2170
|
`)
|
|
1724
2171
|
);
|
|
1725
2172
|
}
|
|
1726
2173
|
let previousFindingCount = -1;
|
|
1727
2174
|
const runCheck = async () => {
|
|
1728
|
-
const spinner =
|
|
2175
|
+
const spinner = ora11({ text: `Checking drift for "${codeName}"\u2026`, isSilent: useJson });
|
|
1729
2176
|
spinner.start();
|
|
1730
2177
|
try {
|
|
1731
2178
|
const driftData = await api.get(`/agents/${encodeURIComponent(codeName)}/drift-data`);
|
|
@@ -1789,19 +2236,19 @@ Watching drift for "${codeName}" every ${intervalSec}s. Press Ctrl+C to stop.
|
|
|
1789
2236
|
}, intervalSec * 1e3);
|
|
1790
2237
|
process.on("SIGINT", () => {
|
|
1791
2238
|
clearInterval(timer);
|
|
1792
|
-
if (!useJson) console.log(
|
|
2239
|
+
if (!useJson) console.log(chalk11.dim("\nStopped watching."));
|
|
1793
2240
|
process.exit(0);
|
|
1794
2241
|
});
|
|
1795
2242
|
}
|
|
1796
2243
|
|
|
1797
2244
|
// src/commands/host.ts
|
|
1798
|
-
import
|
|
1799
|
-
import
|
|
2245
|
+
import chalk12 from "chalk";
|
|
2246
|
+
import ora12 from "ora";
|
|
1800
2247
|
async function hostListCommand() {
|
|
1801
2248
|
const teamSlug = requireTeam();
|
|
1802
2249
|
if (!teamSlug) return;
|
|
1803
2250
|
const json = isJsonMode();
|
|
1804
|
-
const spinner =
|
|
2251
|
+
const spinner = ora12({ text: "Fetching hosts\u2026", isSilent: json });
|
|
1805
2252
|
spinner.start();
|
|
1806
2253
|
try {
|
|
1807
2254
|
const data = await api.get("/hosts");
|
|
@@ -1819,10 +2266,10 @@ async function hostListCommand() {
|
|
|
1819
2266
|
return;
|
|
1820
2267
|
}
|
|
1821
2268
|
const rows = data.hosts.map((h) => {
|
|
1822
|
-
const status = h.status === "active" ?
|
|
2269
|
+
const status = h.status === "active" ? chalk12.green("active") : chalk12.red("decommissioned");
|
|
1823
2270
|
const agents = String(h.agents);
|
|
1824
|
-
const lastSeen = h.last_seen_at ? new Date(h.last_seen_at).toLocaleDateString() :
|
|
1825
|
-
const prefix = h.key_prefix ? `tlk_${h.key_prefix}\u2026` :
|
|
2271
|
+
const lastSeen = h.last_seen_at ? new Date(h.last_seen_at).toLocaleDateString() : chalk12.dim("never");
|
|
2272
|
+
const prefix = h.key_prefix ? `tlk_${h.key_prefix}\u2026` : chalk12.dim("none");
|
|
1826
2273
|
return [h.name, status, agents, lastSeen, prefix];
|
|
1827
2274
|
});
|
|
1828
2275
|
table(["Name", "Status", "Agents", "Last Seen", "Key"], rows);
|
|
@@ -1849,14 +2296,14 @@ async function hostAssignCommand(hostName, agentCodeNames, opts) {
|
|
|
1849
2296
|
process.exitCode = 1;
|
|
1850
2297
|
return;
|
|
1851
2298
|
}
|
|
1852
|
-
const spinner =
|
|
2299
|
+
const spinner = ora12({ text: "Assigning agents\u2026", isSilent: json });
|
|
1853
2300
|
spinner.start();
|
|
1854
2301
|
try {
|
|
1855
2302
|
await api.post(`/hosts/${encodeURIComponent(hostName)}/assign`, {
|
|
1856
2303
|
agents: agentCodeNames,
|
|
1857
2304
|
force: opts.force
|
|
1858
2305
|
});
|
|
1859
|
-
spinner.succeed(`Agents assigned to ${
|
|
2306
|
+
spinner.succeed(`Agents assigned to ${chalk12.bold(hostName)}.`);
|
|
1860
2307
|
if (json) {
|
|
1861
2308
|
jsonOutput({ ok: true, host: hostName, assigned: agentCodeNames });
|
|
1862
2309
|
return;
|
|
@@ -1887,13 +2334,13 @@ async function hostUnassignCommand(hostName, agentCodeNames) {
|
|
|
1887
2334
|
process.exitCode = 1;
|
|
1888
2335
|
return;
|
|
1889
2336
|
}
|
|
1890
|
-
const spinner =
|
|
2337
|
+
const spinner = ora12({ text: "Unassigning agents\u2026", isSilent: json });
|
|
1891
2338
|
spinner.start();
|
|
1892
2339
|
try {
|
|
1893
2340
|
await api.post(`/hosts/${encodeURIComponent(hostName)}/unassign`, {
|
|
1894
2341
|
agents: agentCodeNames
|
|
1895
2342
|
});
|
|
1896
|
-
spinner.succeed(`Agents unassigned from ${
|
|
2343
|
+
spinner.succeed(`Agents unassigned from ${chalk12.bold(hostName)}.`);
|
|
1897
2344
|
if (json) {
|
|
1898
2345
|
jsonOutput({ ok: true, host: hostName, unassigned: agentCodeNames });
|
|
1899
2346
|
return;
|
|
@@ -1913,7 +2360,7 @@ async function hostUnassignCommand(hostName, agentCodeNames) {
|
|
|
1913
2360
|
}
|
|
1914
2361
|
async function hostAgentsCommand(hostName) {
|
|
1915
2362
|
const json = isJsonMode();
|
|
1916
|
-
const spinner =
|
|
2363
|
+
const spinner = ora12({ text: "Fetching host agents\u2026", isSilent: json });
|
|
1917
2364
|
spinner.start();
|
|
1918
2365
|
try {
|
|
1919
2366
|
const hostId = await getHostId();
|
|
@@ -1952,7 +2399,7 @@ async function hostAgentsCommand(hostName) {
|
|
|
1952
2399
|
return;
|
|
1953
2400
|
}
|
|
1954
2401
|
const rows = agents.map((a) => {
|
|
1955
|
-
const statusColor = a.status === "active" ?
|
|
2402
|
+
const statusColor = a.status === "active" ? chalk12.green : chalk12.yellow;
|
|
1956
2403
|
return [
|
|
1957
2404
|
a.code_name,
|
|
1958
2405
|
a.display_name,
|
|
@@ -1975,11 +2422,11 @@ async function hostRotateKeyCommand(hostName) {
|
|
|
1975
2422
|
const teamSlug = requireTeam();
|
|
1976
2423
|
if (!teamSlug) return;
|
|
1977
2424
|
const json = isJsonMode();
|
|
1978
|
-
const spinner =
|
|
2425
|
+
const spinner = ora12({ text: "Rotating host key\u2026", isSilent: json });
|
|
1979
2426
|
spinner.start();
|
|
1980
2427
|
try {
|
|
1981
2428
|
const data = await api.post(`/hosts/${encodeURIComponent(hostName)}/rotate-key`);
|
|
1982
|
-
spinner.succeed(`Key rotated for ${
|
|
2429
|
+
spinner.succeed(`Key rotated for ${chalk12.bold(hostName)}.`);
|
|
1983
2430
|
if (json) {
|
|
1984
2431
|
jsonOutput({
|
|
1985
2432
|
ok: true,
|
|
@@ -1989,12 +2436,12 @@ async function hostRotateKeyCommand(hostName) {
|
|
|
1989
2436
|
return;
|
|
1990
2437
|
}
|
|
1991
2438
|
console.log();
|
|
1992
|
-
info(`Host: ${
|
|
2439
|
+
info(`Host: ${chalk12.bold(hostName)}`);
|
|
1993
2440
|
info(`Prefix: ${data.key.prefix}`);
|
|
1994
2441
|
console.log();
|
|
1995
|
-
console.log(
|
|
2442
|
+
console.log(chalk12.yellow.bold(" Save this key now \u2014 it will not be shown again:"));
|
|
1996
2443
|
console.log();
|
|
1997
|
-
console.log(` ${
|
|
2444
|
+
console.log(` ${chalk12.green.bold(data.key.raw_key)}`);
|
|
1998
2445
|
console.log();
|
|
1999
2446
|
} catch (err) {
|
|
2000
2447
|
spinner.fail("Failed to rotate key.");
|
|
@@ -2010,11 +2457,11 @@ async function hostDecommissionCommand(hostName) {
|
|
|
2010
2457
|
const teamSlug = requireTeam();
|
|
2011
2458
|
if (!teamSlug) return;
|
|
2012
2459
|
const json = isJsonMode();
|
|
2013
|
-
const spinner =
|
|
2460
|
+
const spinner = ora12({ text: "Decommissioning host\u2026", isSilent: json });
|
|
2014
2461
|
spinner.start();
|
|
2015
2462
|
try {
|
|
2016
2463
|
await api.post(`/hosts/${encodeURIComponent(hostName)}/decommission`);
|
|
2017
|
-
spinner.succeed(`Host "${
|
|
2464
|
+
spinner.succeed(`Host "${chalk12.bold(hostName)}" decommissioned.`);
|
|
2018
2465
|
if (json) {
|
|
2019
2466
|
jsonOutput({
|
|
2020
2467
|
ok: true,
|
|
@@ -2024,7 +2471,7 @@ async function hostDecommissionCommand(hostName) {
|
|
|
2024
2471
|
return;
|
|
2025
2472
|
}
|
|
2026
2473
|
info(`Host: ${hostName}`);
|
|
2027
|
-
info(`Status: ${
|
|
2474
|
+
info(`Status: ${chalk12.red("decommissioned")}`);
|
|
2028
2475
|
info("API key revoked. Agents remain assigned for audit visibility.");
|
|
2029
2476
|
info("Reassign agents to another host with `agt host assign --force`.");
|
|
2030
2477
|
} catch (err) {
|
|
@@ -2039,8 +2486,8 @@ async function hostDecommissionCommand(hostName) {
|
|
|
2039
2486
|
}
|
|
2040
2487
|
|
|
2041
2488
|
// src/commands/host-pair.ts
|
|
2042
|
-
import { spawn, spawnSync } from "child_process";
|
|
2043
|
-
import
|
|
2489
|
+
import { spawn as spawn2, spawnSync } from "child_process";
|
|
2490
|
+
import chalk13 from "chalk";
|
|
2044
2491
|
async function hostPairCommand(name, opts) {
|
|
2045
2492
|
const teamSlug = requireTeam();
|
|
2046
2493
|
if (!teamSlug) return;
|
|
@@ -2110,17 +2557,17 @@ async function hostPairCommand(name, opts) {
|
|
|
2110
2557
|
});
|
|
2111
2558
|
return;
|
|
2112
2559
|
}
|
|
2113
|
-
info(`Pairing Claude Code on ${
|
|
2114
|
-
info(`Local port ${
|
|
2560
|
+
info(`Pairing Claude Code on ${chalk13.bold(name)} (${instanceId}, ${region})`);
|
|
2561
|
+
info(`Local port ${chalk13.cyan(String(localPort))} will be forwarded to the host.`);
|
|
2115
2562
|
console.log();
|
|
2116
|
-
console.log(
|
|
2117
|
-
console.log(` ${
|
|
2118
|
-
console.log(
|
|
2119
|
-
console.log(
|
|
2563
|
+
console.log(chalk13.dim("In the shell that opens, run:"));
|
|
2564
|
+
console.log(` ${chalk13.cyan(`CLAUDE_CODE_OAUTH_PORT=${localPort} claude /login`)}`);
|
|
2565
|
+
console.log(chalk13.dim("Then click the printed URL on this machine. The OAuth callback"));
|
|
2566
|
+
console.log(chalk13.dim(`will reach Claude Code via the port-forward tunnel.`));
|
|
2120
2567
|
console.log();
|
|
2121
|
-
console.log(
|
|
2568
|
+
console.log(chalk13.dim("Exit the shell (Ctrl+D) when Claude Code reports successful login."));
|
|
2122
2569
|
console.log();
|
|
2123
|
-
const tunnel =
|
|
2570
|
+
const tunnel = spawn2(
|
|
2124
2571
|
"aws",
|
|
2125
2572
|
[
|
|
2126
2573
|
"ssm",
|
|
@@ -2139,7 +2586,7 @@ async function hostPairCommand(name, opts) {
|
|
|
2139
2586
|
tunnel.stderr?.on("data", (buf) => {
|
|
2140
2587
|
const line = buf.toString().trim();
|
|
2141
2588
|
if (line.startsWith("An error occurred")) {
|
|
2142
|
-
console.error(
|
|
2589
|
+
console.error(chalk13.red(`[tunnel] ${line}`));
|
|
2143
2590
|
}
|
|
2144
2591
|
});
|
|
2145
2592
|
await new Promise((r) => setTimeout(r, 1500));
|
|
@@ -2149,7 +2596,7 @@ async function hostPairCommand(name, opts) {
|
|
|
2149
2596
|
return;
|
|
2150
2597
|
}
|
|
2151
2598
|
if (opts.noShell) {
|
|
2152
|
-
console.log(
|
|
2599
|
+
console.log(chalk13.dim("--no-shell set; tunnel is running. Press Ctrl+C to exit."));
|
|
2153
2600
|
await new Promise((resolve2) => {
|
|
2154
2601
|
const onSignal = () => {
|
|
2155
2602
|
terminate(tunnel);
|
|
@@ -2177,7 +2624,7 @@ function preflightAws() {
|
|
|
2177
2624
|
}
|
|
2178
2625
|
function runInteractive(cmd, args) {
|
|
2179
2626
|
return new Promise((resolve2) => {
|
|
2180
|
-
const child =
|
|
2627
|
+
const child = spawn2(cmd, args, { stdio: "inherit" });
|
|
2181
2628
|
child.on("exit", (code) => resolve2(code ?? 0));
|
|
2182
2629
|
child.on("error", () => resolve2(1));
|
|
2183
2630
|
});
|
|
@@ -2193,15 +2640,15 @@ function terminate(child) {
|
|
|
2193
2640
|
// src/commands/manager-watch.tsx
|
|
2194
2641
|
import { useEffect, useState, useMemo } from "react";
|
|
2195
2642
|
import { render, Box, Text, useApp, useInput } from "ink";
|
|
2196
|
-
import { existsSync as
|
|
2197
|
-
import { homedir } from "os";
|
|
2198
|
-
import { join as
|
|
2643
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3, statSync, openSync, readSync, closeSync } from "fs";
|
|
2644
|
+
import { homedir as homedir3 } from "os";
|
|
2645
|
+
import { join as join9 } from "path";
|
|
2199
2646
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2200
2647
|
var REFRESH_MS = 1e3;
|
|
2201
2648
|
var LOG_TAIL_LINES = 200;
|
|
2202
2649
|
var DETAIL_RECENT_LINES = 50;
|
|
2203
2650
|
function managerWatchCommand(opts = {}) {
|
|
2204
|
-
const configDir = opts.configDir ??
|
|
2651
|
+
const configDir = opts.configDir ?? join9(homedir3(), ".augmented");
|
|
2205
2652
|
const paths = getManagerPaths(configDir);
|
|
2206
2653
|
const isTty = process.stdout.isTTY === true && process.stdin.isTTY === true;
|
|
2207
2654
|
if (opts.noTui || !isTty) {
|
|
@@ -2225,15 +2672,15 @@ function managerWatchCommand(opts = {}) {
|
|
|
2225
2672
|
}
|
|
2226
2673
|
function readState(stateFile) {
|
|
2227
2674
|
try {
|
|
2228
|
-
if (!
|
|
2229
|
-
const raw =
|
|
2675
|
+
if (!existsSync4(stateFile)) return null;
|
|
2676
|
+
const raw = readFileSync3(stateFile, "utf-8");
|
|
2230
2677
|
return JSON.parse(raw);
|
|
2231
2678
|
} catch {
|
|
2232
2679
|
return null;
|
|
2233
2680
|
}
|
|
2234
2681
|
}
|
|
2235
2682
|
function tailLogFile(logFile, lines) {
|
|
2236
|
-
if (!
|
|
2683
|
+
if (!existsSync4(logFile)) return [];
|
|
2237
2684
|
try {
|
|
2238
2685
|
const fileSize = statSync(logFile).size;
|
|
2239
2686
|
if (fileSize === 0) return [];
|
|
@@ -2474,7 +2921,7 @@ function truncate(s, max) {
|
|
|
2474
2921
|
return s.length > max ? `${s.slice(0, Math.max(0, max - 1))}\u2026` : s;
|
|
2475
2922
|
}
|
|
2476
2923
|
function streamLogFile(logFile) {
|
|
2477
|
-
if (!
|
|
2924
|
+
if (!existsSync4(logFile)) {
|
|
2478
2925
|
process.stderr.write(`No manager log found at ${logFile}.
|
|
2479
2926
|
Start the manager first: agt manager start
|
|
2480
2927
|
`);
|
|
@@ -2483,7 +2930,7 @@ Start the manager first: agt manager start
|
|
|
2483
2930
|
}
|
|
2484
2931
|
let lastSize = 0;
|
|
2485
2932
|
try {
|
|
2486
|
-
const initial =
|
|
2933
|
+
const initial = readFileSync3(logFile, "utf-8");
|
|
2487
2934
|
process.stdout.write(initial);
|
|
2488
2935
|
lastSize = statSync(logFile).size;
|
|
2489
2936
|
} catch (err) {
|
|
@@ -2518,16 +2965,16 @@ Start the manager first: agt manager start
|
|
|
2518
2965
|
}
|
|
2519
2966
|
|
|
2520
2967
|
// src/commands/agent.ts
|
|
2521
|
-
import
|
|
2968
|
+
import chalk14 from "chalk";
|
|
2522
2969
|
import JSON52 from "json5";
|
|
2523
|
-
import { readFileSync as
|
|
2524
|
-
import { join as
|
|
2970
|
+
import { readFileSync as readFileSync4, existsSync as existsSync5 } from "fs";
|
|
2971
|
+
import { join as join10 } from "path";
|
|
2525
2972
|
async function agentShowCommand(codeName, opts) {
|
|
2526
2973
|
const json = isJsonMode();
|
|
2527
|
-
const unifiedDir =
|
|
2528
|
-
const legacyDir =
|
|
2529
|
-
const agentDir =
|
|
2530
|
-
const hasLocalConfig =
|
|
2974
|
+
const unifiedDir = join10(opts.configDir, codeName, "provision");
|
|
2975
|
+
const legacyDir = join10(opts.configDir, codeName, "claudecode", "provision");
|
|
2976
|
+
const agentDir = existsSync5(unifiedDir) ? unifiedDir : legacyDir;
|
|
2977
|
+
const hasLocalConfig = existsSync5(agentDir);
|
|
2531
2978
|
let apiChannels = null;
|
|
2532
2979
|
let apiAgent = null;
|
|
2533
2980
|
if (getApiKey()) {
|
|
@@ -2560,34 +3007,34 @@ async function agentShowCommand(codeName, opts) {
|
|
|
2560
3007
|
let openclawConfig = null;
|
|
2561
3008
|
let agentState = null;
|
|
2562
3009
|
if (hasLocalConfig) {
|
|
2563
|
-
const charterPath =
|
|
2564
|
-
if (
|
|
2565
|
-
const raw =
|
|
3010
|
+
const charterPath = join10(agentDir, "CHARTER.md");
|
|
3011
|
+
if (existsSync5(charterPath)) {
|
|
3012
|
+
const raw = readFileSync4(charterPath, "utf-8");
|
|
2566
3013
|
const parsed = extractFrontmatter(raw);
|
|
2567
3014
|
if (parsed.frontmatter) {
|
|
2568
3015
|
charter = parsed.frontmatter;
|
|
2569
3016
|
}
|
|
2570
3017
|
}
|
|
2571
|
-
const toolsPath =
|
|
2572
|
-
if (
|
|
2573
|
-
const raw =
|
|
3018
|
+
const toolsPath = join10(agentDir, "TOOLS.md");
|
|
3019
|
+
if (existsSync5(toolsPath)) {
|
|
3020
|
+
const raw = readFileSync4(toolsPath, "utf-8");
|
|
2574
3021
|
const parsed = extractFrontmatter(raw);
|
|
2575
3022
|
if (parsed.frontmatter) {
|
|
2576
3023
|
tools = parsed.frontmatter;
|
|
2577
3024
|
}
|
|
2578
3025
|
}
|
|
2579
|
-
const openclawPath =
|
|
2580
|
-
if (
|
|
3026
|
+
const openclawPath = join10(agentDir, "openclaw.json5");
|
|
3027
|
+
if (existsSync5(openclawPath)) {
|
|
2581
3028
|
try {
|
|
2582
|
-
const raw =
|
|
3029
|
+
const raw = readFileSync4(openclawPath, "utf-8");
|
|
2583
3030
|
openclawConfig = JSON52.parse(raw);
|
|
2584
3031
|
} catch {
|
|
2585
3032
|
}
|
|
2586
3033
|
}
|
|
2587
|
-
const statePath =
|
|
2588
|
-
if (
|
|
3034
|
+
const statePath = join10(opts.configDir, "manager-state.json");
|
|
3035
|
+
if (existsSync5(statePath)) {
|
|
2589
3036
|
try {
|
|
2590
|
-
const raw =
|
|
3037
|
+
const raw = readFileSync4(statePath, "utf-8");
|
|
2591
3038
|
const state = JSON.parse(raw);
|
|
2592
3039
|
agentState = state.agents?.find((a) => a.codeName === codeName) ?? null;
|
|
2593
3040
|
} catch {
|
|
@@ -2654,7 +3101,7 @@ async function agentShowCommand(codeName, opts) {
|
|
|
2654
3101
|
return;
|
|
2655
3102
|
}
|
|
2656
3103
|
const displayName = apiAgent?.display_name ?? charter?.display_name ?? codeName;
|
|
2657
|
-
console.log(
|
|
3104
|
+
console.log(chalk14.bold(`
|
|
2658
3105
|
Agent: ${codeName}`) + (displayName !== codeName ? ` (${displayName})` : "") + "\n");
|
|
2659
3106
|
if (charter || apiAgent) {
|
|
2660
3107
|
const agentId = apiAgent?.agent_id ?? charter?.agent_id;
|
|
@@ -2686,7 +3133,7 @@ Agent: ${codeName}`) + (displayName !== codeName ? ` (${displayName})` : "") + "
|
|
|
2686
3133
|
info("No agent metadata available");
|
|
2687
3134
|
}
|
|
2688
3135
|
const filtered = opts.allChannels ? channelRows : channelRows.filter((c) => c.enabled);
|
|
2689
|
-
console.log(
|
|
3136
|
+
console.log(chalk14.bold("\nChannels:"));
|
|
2690
3137
|
if (filtered.length === 0) {
|
|
2691
3138
|
info("(none enabled)");
|
|
2692
3139
|
} else {
|
|
@@ -2702,7 +3149,7 @@ Agent: ${codeName}`) + (displayName !== codeName ? ` (${displayName})` : "") + "
|
|
|
2702
3149
|
);
|
|
2703
3150
|
}
|
|
2704
3151
|
if (tools) {
|
|
2705
|
-
console.log(
|
|
3152
|
+
console.log(chalk14.bold("\nTools:"));
|
|
2706
3153
|
if (tools.tools.length === 0) {
|
|
2707
3154
|
info("(none configured)");
|
|
2708
3155
|
} else {
|
|
@@ -2713,7 +3160,7 @@ Agent: ${codeName}`) + (displayName !== codeName ? ` (${displayName})` : "") + "
|
|
|
2713
3160
|
}
|
|
2714
3161
|
if (tools.global_controls) {
|
|
2715
3162
|
const gc = tools.global_controls;
|
|
2716
|
-
console.log(
|
|
3163
|
+
console.log(chalk14.bold("\nGlobal Controls:"));
|
|
2717
3164
|
info(`Network: ${gc.default_network_policy === "deny" ? "deny-by-default" : "allow-by-default"}`);
|
|
2718
3165
|
info(`Timeout: ${gc.default_timeout_ms}ms`);
|
|
2719
3166
|
info(`Rate: ${gc.default_rate_limit_rpm} rpm`);
|
|
@@ -2729,8 +3176,8 @@ function formatTimestamp(iso) {
|
|
|
2729
3176
|
}
|
|
2730
3177
|
|
|
2731
3178
|
// src/commands/kanban-recurring.ts
|
|
2732
|
-
import
|
|
2733
|
-
import
|
|
3179
|
+
import chalk15 from "chalk";
|
|
3180
|
+
import ora13 from "ora";
|
|
2734
3181
|
async function resolveAgentId(codeName) {
|
|
2735
3182
|
try {
|
|
2736
3183
|
const data = await api.get(`/agents/${codeName}`);
|
|
@@ -2741,10 +3188,10 @@ async function resolveAgentId(codeName) {
|
|
|
2741
3188
|
}
|
|
2742
3189
|
}
|
|
2743
3190
|
function priorityLabel(p) {
|
|
2744
|
-
return p === 1 ?
|
|
3191
|
+
return p === 1 ? chalk15.red("HIGH") : p === 3 ? chalk15.dim("LOW") : chalk15.yellow("MED");
|
|
2745
3192
|
}
|
|
2746
3193
|
function formatSpawnTime(isoDate, timezone) {
|
|
2747
|
-
if (!isoDate) return
|
|
3194
|
+
if (!isoDate) return chalk15.dim("\u2014");
|
|
2748
3195
|
try {
|
|
2749
3196
|
return new Intl.DateTimeFormat("en-AU", {
|
|
2750
3197
|
dateStyle: "medium",
|
|
@@ -2785,7 +3232,7 @@ async function kanbanRecurringAddCommand(title, opts) {
|
|
|
2785
3232
|
return;
|
|
2786
3233
|
}
|
|
2787
3234
|
}
|
|
2788
|
-
const spinner =
|
|
3235
|
+
const spinner = ora13({ text: "Creating recurring template\u2026", isSilent: json });
|
|
2789
3236
|
spinner.start();
|
|
2790
3237
|
const agentId = await resolveAgentId(opts.agent);
|
|
2791
3238
|
if (!agentId) {
|
|
@@ -2809,8 +3256,8 @@ async function kanbanRecurringAddCommand(title, opts) {
|
|
|
2809
3256
|
jsonOutput({ ok: true, template: data.template });
|
|
2810
3257
|
return;
|
|
2811
3258
|
}
|
|
2812
|
-
success(`Recurring task created: ${
|
|
2813
|
-
info(`Schedule: ${
|
|
3259
|
+
success(`Recurring task created: ${chalk15.bold(title)}`);
|
|
3260
|
+
info(`Schedule: ${chalk15.cyan(data.template.natural_language ?? data.template.expression ?? data.template.every_interval ?? "")}`);
|
|
2814
3261
|
info(`Next spawn: ${formatSpawnTime(data.template.next_spawn_at, data.template.timezone)}`);
|
|
2815
3262
|
info(`Timezone: ${data.template.timezone}`);
|
|
2816
3263
|
} catch (err) {
|
|
@@ -2827,7 +3274,7 @@ async function kanbanRecurringListCommand(opts) {
|
|
|
2827
3274
|
const teamSlug = requireTeam();
|
|
2828
3275
|
if (!teamSlug) return;
|
|
2829
3276
|
const json = isJsonMode();
|
|
2830
|
-
const spinner =
|
|
3277
|
+
const spinner = ora13({ text: "Fetching recurring templates\u2026", isSilent: json });
|
|
2831
3278
|
spinner.start();
|
|
2832
3279
|
const agentId = await resolveAgentId(opts.agent);
|
|
2833
3280
|
if (!agentId) {
|
|
@@ -2853,7 +3300,7 @@ async function kanbanRecurringListCommand(opts) {
|
|
|
2853
3300
|
t.title,
|
|
2854
3301
|
t.natural_language ?? t.expression ?? t.every_interval ?? "\u2014",
|
|
2855
3302
|
priorityLabel(t.priority),
|
|
2856
|
-
t.enabled ?
|
|
3303
|
+
t.enabled ? chalk15.green("Active") : chalk15.dim("Disabled"),
|
|
2857
3304
|
formatSpawnTime(t.next_spawn_at, t.timezone),
|
|
2858
3305
|
String(t.spawn_count)
|
|
2859
3306
|
]);
|
|
@@ -2875,7 +3322,7 @@ async function kanbanRecurringDisableCommand(titleOrId, opts) {
|
|
|
2875
3322
|
const teamSlug = requireTeam();
|
|
2876
3323
|
if (!teamSlug) return;
|
|
2877
3324
|
const json = isJsonMode();
|
|
2878
|
-
const spinner =
|
|
3325
|
+
const spinner = ora13({ text: "Disabling recurring template\u2026", isSilent: json });
|
|
2879
3326
|
spinner.start();
|
|
2880
3327
|
const agentId = await resolveAgentId(opts.agent);
|
|
2881
3328
|
if (!agentId) {
|
|
@@ -2910,7 +3357,7 @@ async function kanbanRecurringDisableCommand(titleOrId, opts) {
|
|
|
2910
3357
|
if (json) {
|
|
2911
3358
|
jsonOutput({ ok: true, id: match.id, title: match.title, enabled: false });
|
|
2912
3359
|
} else {
|
|
2913
|
-
success(`Disabled: ${
|
|
3360
|
+
success(`Disabled: ${chalk15.bold(match.title)}`);
|
|
2914
3361
|
}
|
|
2915
3362
|
} catch (err) {
|
|
2916
3363
|
spinner.fail("Failed to disable recurring template");
|
|
@@ -2924,24 +3371,24 @@ async function kanbanRecurringDisableCommand(titleOrId, opts) {
|
|
|
2924
3371
|
}
|
|
2925
3372
|
|
|
2926
3373
|
// src/commands/setup.ts
|
|
2927
|
-
import { existsSync as
|
|
2928
|
-
import { join as
|
|
2929
|
-
import { homedir as
|
|
2930
|
-
import
|
|
2931
|
-
import
|
|
3374
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, accessSync, constants as fsConstants } from "fs";
|
|
3375
|
+
import { join as join11, dirname } from "path";
|
|
3376
|
+
import { homedir as homedir4 } from "os";
|
|
3377
|
+
import chalk16 from "chalk";
|
|
3378
|
+
import ora14 from "ora";
|
|
2932
3379
|
function detectShellProfile() {
|
|
2933
3380
|
const shell = process.env["SHELL"] ?? "";
|
|
2934
|
-
const home =
|
|
3381
|
+
const home = homedir4();
|
|
2935
3382
|
if (shell.includes("zsh")) {
|
|
2936
|
-
return
|
|
3383
|
+
return join11(home, ".zshrc");
|
|
2937
3384
|
}
|
|
2938
3385
|
if (shell.includes("fish")) {
|
|
2939
|
-
const fishConfig =
|
|
3386
|
+
const fishConfig = join11(home, ".config", "fish", "config.fish");
|
|
2940
3387
|
return fishConfig;
|
|
2941
3388
|
}
|
|
2942
|
-
const bashrc =
|
|
2943
|
-
if (
|
|
2944
|
-
return
|
|
3389
|
+
const bashrc = join11(home, ".bashrc");
|
|
3390
|
+
if (existsSync6(bashrc)) return bashrc;
|
|
3391
|
+
return join11(home, ".bash_profile");
|
|
2945
3392
|
}
|
|
2946
3393
|
function maybeWriteSystemWideEnv(apiUrl, apiKey, consoleUrl) {
|
|
2947
3394
|
const empty = { etcEnvironment: false, profileD: false, bashrc: false };
|
|
@@ -2967,14 +3414,14 @@ function quoteForFishShell(value) {
|
|
|
2967
3414
|
}
|
|
2968
3415
|
function writeEtcEnvironment(apiUrl, apiKey, consoleUrl) {
|
|
2969
3416
|
const envPath = "/etc/environment";
|
|
2970
|
-
if (!
|
|
3417
|
+
if (!existsSync6(envPath)) return false;
|
|
2971
3418
|
try {
|
|
2972
3419
|
accessSync(envPath, fsConstants.W_OK);
|
|
2973
3420
|
} catch {
|
|
2974
3421
|
return false;
|
|
2975
3422
|
}
|
|
2976
3423
|
try {
|
|
2977
|
-
const current =
|
|
3424
|
+
const current = readFileSync5(envPath, "utf-8");
|
|
2978
3425
|
const stripped = current.split(/\r?\n/).filter((line) => !/^\s*(?:export\s+)?AGT_(?:HOST|API_KEY|CONSOLE_URL)=/.test(line)).join("\n");
|
|
2979
3426
|
const base = stripped.endsWith("\n") || stripped.length === 0 ? stripped : `${stripped}
|
|
2980
3427
|
`;
|
|
@@ -2985,7 +3432,7 @@ function writeEtcEnvironment(apiUrl, apiKey, consoleUrl) {
|
|
|
2985
3432
|
if (consoleUrl) lines.push(`AGT_CONSOLE_URL=${quoteForEtcEnvironment(consoleUrl)}`);
|
|
2986
3433
|
const appended = `${base}${lines.join("\n")}
|
|
2987
3434
|
`;
|
|
2988
|
-
|
|
3435
|
+
writeFileSync6(envPath, appended, { mode: 420 });
|
|
2989
3436
|
return true;
|
|
2990
3437
|
} catch {
|
|
2991
3438
|
return false;
|
|
@@ -2993,7 +3440,7 @@ function writeEtcEnvironment(apiUrl, apiKey, consoleUrl) {
|
|
|
2993
3440
|
}
|
|
2994
3441
|
function writeProfileDScript(apiUrl, apiKey, consoleUrl) {
|
|
2995
3442
|
const profileD = "/etc/profile.d";
|
|
2996
|
-
if (!
|
|
3443
|
+
if (!existsSync6(profileD)) return false;
|
|
2997
3444
|
try {
|
|
2998
3445
|
accessSync(profileD, fsConstants.W_OK);
|
|
2999
3446
|
} catch {
|
|
@@ -3008,7 +3455,7 @@ function writeProfileDScript(apiUrl, apiKey, consoleUrl) {
|
|
|
3008
3455
|
];
|
|
3009
3456
|
if (consoleUrl) lines.push(`export AGT_CONSOLE_URL=${quoteForPosixShell(consoleUrl)}`);
|
|
3010
3457
|
lines.push("");
|
|
3011
|
-
|
|
3458
|
+
writeFileSync6(scriptPath, lines.join("\n"), { mode: 420 });
|
|
3012
3459
|
return true;
|
|
3013
3460
|
} catch {
|
|
3014
3461
|
return false;
|
|
@@ -3016,14 +3463,14 @@ function writeProfileDScript(apiUrl, apiKey, consoleUrl) {
|
|
|
3016
3463
|
}
|
|
3017
3464
|
function ensureBashrcSourcesProfileD() {
|
|
3018
3465
|
const bashrc = "/etc/bashrc";
|
|
3019
|
-
if (!
|
|
3466
|
+
if (!existsSync6(bashrc)) return false;
|
|
3020
3467
|
try {
|
|
3021
3468
|
accessSync(bashrc, fsConstants.W_OK);
|
|
3022
3469
|
} catch {
|
|
3023
3470
|
return false;
|
|
3024
3471
|
}
|
|
3025
3472
|
try {
|
|
3026
|
-
const current =
|
|
3473
|
+
const current = readFileSync5(bashrc, "utf-8");
|
|
3027
3474
|
const marker = "# Augmented (agt) \u2014 source system-wide AGT env";
|
|
3028
3475
|
if (current.includes(marker)) return true;
|
|
3029
3476
|
const snippet = [
|
|
@@ -3034,7 +3481,7 @@ function ensureBashrcSourcesProfileD() {
|
|
|
3034
3481
|
"fi",
|
|
3035
3482
|
""
|
|
3036
3483
|
].join("\n");
|
|
3037
|
-
|
|
3484
|
+
writeFileSync6(bashrc, current + snippet);
|
|
3038
3485
|
return true;
|
|
3039
3486
|
} catch {
|
|
3040
3487
|
return false;
|
|
@@ -3081,10 +3528,10 @@ async function setupCommand(token) {
|
|
|
3081
3528
|
if (!apiUrl) {
|
|
3082
3529
|
apiUrl = "https://api.augmented.team";
|
|
3083
3530
|
if (!json) {
|
|
3084
|
-
info(`No AGT_HOST set \u2014 using default: ${
|
|
3531
|
+
info(`No AGT_HOST set \u2014 using default: ${chalk16.bold(apiUrl)}`);
|
|
3085
3532
|
}
|
|
3086
3533
|
}
|
|
3087
|
-
const spinner =
|
|
3534
|
+
const spinner = ora14({ text: "Exchanging provisioning token\u2026", isSilent: json });
|
|
3088
3535
|
spinner.start();
|
|
3089
3536
|
let setupResult;
|
|
3090
3537
|
try {
|
|
@@ -3112,17 +3559,17 @@ async function setupCommand(token) {
|
|
|
3112
3559
|
const finalApiUrl = setupResult.api_url || apiUrl;
|
|
3113
3560
|
process.env["AGT_HOST"] = finalApiUrl;
|
|
3114
3561
|
process.env["AGT_API_KEY"] = setupResult.api_key;
|
|
3115
|
-
const verifySpinner =
|
|
3562
|
+
const verifySpinner = ora14({ text: "Verifying connection\u2026", isSilent: json });
|
|
3116
3563
|
verifySpinner.start();
|
|
3117
3564
|
try {
|
|
3118
3565
|
const exchange = await exchangeApiKey(setupResult.api_key);
|
|
3119
3566
|
verifySpinner.succeed("Connection verified");
|
|
3120
3567
|
if (!json) {
|
|
3121
|
-
info(`Host: ${
|
|
3568
|
+
info(`Host: ${chalk16.bold(setupResult.host_name)}`);
|
|
3122
3569
|
info(`Host ID: ${exchange.hostId}`);
|
|
3123
|
-
info(`Team: ${
|
|
3570
|
+
info(`Team: ${chalk16.bold(exchange.teamSlug ?? setupResult.team_slug ?? "unknown")}`);
|
|
3124
3571
|
if (exchange.userEmail) {
|
|
3125
|
-
info(`User: ${
|
|
3572
|
+
info(`User: ${chalk16.bold(exchange.userEmail)}`);
|
|
3126
3573
|
}
|
|
3127
3574
|
}
|
|
3128
3575
|
} catch (err) {
|
|
@@ -3150,11 +3597,11 @@ async function setupCommand(token) {
|
|
|
3150
3597
|
}
|
|
3151
3598
|
const exportLines = buildExportLines(shell, finalApiUrl, setupResult.api_key, consoleUrl);
|
|
3152
3599
|
const profileDir = dirname(profilePath);
|
|
3153
|
-
if (!
|
|
3154
|
-
|
|
3600
|
+
if (!existsSync6(profileDir)) {
|
|
3601
|
+
mkdirSync5(profileDir, { recursive: true });
|
|
3155
3602
|
}
|
|
3156
3603
|
const marker = "# Augmented (agt) host configuration";
|
|
3157
|
-
const current =
|
|
3604
|
+
const current = existsSync6(profilePath) ? readFileSync5(profilePath, "utf-8") : "";
|
|
3158
3605
|
let updated;
|
|
3159
3606
|
if (current.includes(marker)) {
|
|
3160
3607
|
updated = current.replace(
|
|
@@ -3167,10 +3614,10 @@ async function setupCommand(token) {
|
|
|
3167
3614
|
} else {
|
|
3168
3615
|
updated = current + exportLines;
|
|
3169
3616
|
}
|
|
3170
|
-
|
|
3617
|
+
writeFileSync6(profilePath, updated.endsWith("\n") ? updated : `${updated}
|
|
3171
3618
|
`);
|
|
3172
3619
|
if (!json) {
|
|
3173
|
-
success(`Environment variables written to ${
|
|
3620
|
+
success(`Environment variables written to ${chalk16.bold(profilePath)}`);
|
|
3174
3621
|
}
|
|
3175
3622
|
const sys = maybeWriteSystemWideEnv(finalApiUrl, setupResult.api_key, consoleUrl);
|
|
3176
3623
|
const valueChannelsWritten = sys.etcEnvironment || sys.profileD;
|
|
@@ -3182,10 +3629,10 @@ async function setupCommand(token) {
|
|
|
3182
3629
|
].filter(Boolean).join(" + ");
|
|
3183
3630
|
success(`System-wide env updated: ${channels2}`);
|
|
3184
3631
|
}
|
|
3185
|
-
const managerSpinner =
|
|
3632
|
+
const managerSpinner = ora14({ text: "Starting manager daemon\u2026", isSilent: json });
|
|
3186
3633
|
managerSpinner.start();
|
|
3187
3634
|
try {
|
|
3188
|
-
const configDir =
|
|
3635
|
+
const configDir = join11(homedir4(), ".augmented");
|
|
3189
3636
|
const { pid } = startWatchdog({ intervalMs: 1e4, configDir, detached: true });
|
|
3190
3637
|
managerSpinner.succeed(`Manager started (PID ${pid})`);
|
|
3191
3638
|
} catch (err) {
|
|
@@ -3210,21 +3657,21 @@ async function setupCommand(token) {
|
|
|
3210
3657
|
process.exit(0);
|
|
3211
3658
|
} else {
|
|
3212
3659
|
console.log();
|
|
3213
|
-
console.log(
|
|
3660
|
+
console.log(chalk16.green.bold(" Setup complete!"));
|
|
3214
3661
|
console.log();
|
|
3215
3662
|
if (setupResult.agents.length > 0) {
|
|
3216
|
-
info(`Agents: ${setupResult.agents.map((a) =>
|
|
3663
|
+
info(`Agents: ${setupResult.agents.map((a) => chalk16.cyan(a)).join(", ")}`);
|
|
3217
3664
|
} else {
|
|
3218
3665
|
info("No agents assigned yet. Assign agents in the dashboard or with: agt host assign");
|
|
3219
3666
|
}
|
|
3220
3667
|
console.log();
|
|
3221
|
-
info(`Restart your shell or run: ${
|
|
3668
|
+
info(`Restart your shell or run: ${chalk16.bold(`source ${profilePath}`)}`);
|
|
3222
3669
|
}
|
|
3223
3670
|
}
|
|
3224
3671
|
|
|
3225
3672
|
// src/commands/kanban.ts
|
|
3226
|
-
import
|
|
3227
|
-
import
|
|
3673
|
+
import chalk17 from "chalk";
|
|
3674
|
+
import ora15 from "ora";
|
|
3228
3675
|
async function resolveAgentId2(codeName) {
|
|
3229
3676
|
try {
|
|
3230
3677
|
const data = await api.get(`/agents/${codeName}`);
|
|
@@ -3234,14 +3681,14 @@ async function resolveAgentId2(codeName) {
|
|
|
3234
3681
|
}
|
|
3235
3682
|
}
|
|
3236
3683
|
function priorityLabel2(p) {
|
|
3237
|
-
return p === 1 ?
|
|
3684
|
+
return p === 1 ? chalk17.red("HIGH") : p === 3 ? chalk17.dim("LOW") : chalk17.yellow("MED");
|
|
3238
3685
|
}
|
|
3239
3686
|
function statusLabel(s) {
|
|
3240
3687
|
const map = {
|
|
3241
|
-
in_progress:
|
|
3242
|
-
todo:
|
|
3243
|
-
backlog:
|
|
3244
|
-
done:
|
|
3688
|
+
in_progress: chalk17.blue("In Progress"),
|
|
3689
|
+
todo: chalk17.green("To Do"),
|
|
3690
|
+
backlog: chalk17.dim("Backlog"),
|
|
3691
|
+
done: chalk17.gray("Done")
|
|
3245
3692
|
};
|
|
3246
3693
|
return map[s] ?? s;
|
|
3247
3694
|
}
|
|
@@ -3249,7 +3696,7 @@ async function kanbanListCommand(opts) {
|
|
|
3249
3696
|
const teamSlug = requireTeam();
|
|
3250
3697
|
if (!teamSlug) return;
|
|
3251
3698
|
const json = isJsonMode();
|
|
3252
|
-
const spinner =
|
|
3699
|
+
const spinner = ora15({ text: "Fetching kanban board\u2026", isSilent: json });
|
|
3253
3700
|
spinner.start();
|
|
3254
3701
|
const agentId = await resolveAgentId2(opts.agent);
|
|
3255
3702
|
if (!agentId) {
|
|
@@ -3277,7 +3724,7 @@ async function kanbanListCommand(opts) {
|
|
|
3277
3724
|
item.estimated_minutes ? `~${item.estimated_minutes}min` : "",
|
|
3278
3725
|
item.id.slice(0, 8)
|
|
3279
3726
|
]);
|
|
3280
|
-
console.log(
|
|
3727
|
+
console.log(chalk17.bold(`
|
|
3281
3728
|
Kanban: ${opts.agent}
|
|
3282
3729
|
`));
|
|
3283
3730
|
table(["Pri", "Status", "Title", "Est", "ID"], rows);
|
|
@@ -3517,7 +3964,7 @@ function getAcpAgent(name) {
|
|
|
3517
3964
|
}
|
|
3518
3965
|
|
|
3519
3966
|
// ../../packages/core/dist/acp/client.js
|
|
3520
|
-
import { spawn as
|
|
3967
|
+
import { spawn as spawn3 } from "child_process";
|
|
3521
3968
|
function resolveAgentCommand(agentName) {
|
|
3522
3969
|
const adapter = getAcpAgent(agentName);
|
|
3523
3970
|
if (adapter) {
|
|
@@ -3533,7 +3980,7 @@ function resolveAgentCommand(agentName) {
|
|
|
3533
3980
|
}
|
|
3534
3981
|
async function runAcpx(args, options = {}) {
|
|
3535
3982
|
return new Promise((resolve2) => {
|
|
3536
|
-
const child =
|
|
3983
|
+
const child = spawn3("acpx", args, {
|
|
3537
3984
|
cwd: options.cwd,
|
|
3538
3985
|
stdio: ["pipe", "pipe", "pipe"],
|
|
3539
3986
|
env: { ...process.env }
|
|
@@ -3733,10 +4180,10 @@ async function acpxCloseCommand(agent2, _opts, cmd) {
|
|
|
3733
4180
|
|
|
3734
4181
|
// src/commands/update.ts
|
|
3735
4182
|
import { execFileSync, execSync } from "child_process";
|
|
3736
|
-
import { existsSync as
|
|
3737
|
-
import
|
|
3738
|
-
import
|
|
3739
|
-
var cliVersion = true ? "0.
|
|
4183
|
+
import { existsSync as existsSync7, realpathSync as realpathSync2 } from "fs";
|
|
4184
|
+
import chalk18 from "chalk";
|
|
4185
|
+
import ora16 from "ora";
|
|
4186
|
+
var cliVersion = true ? "0.27.0" : "dev";
|
|
3740
4187
|
async function fetchLatestVersion() {
|
|
3741
4188
|
const host2 = getHost();
|
|
3742
4189
|
if (!host2) return null;
|
|
@@ -3800,7 +4247,7 @@ function detectActiveSessions() {
|
|
|
3800
4247
|
}
|
|
3801
4248
|
function detectInstallSource() {
|
|
3802
4249
|
try {
|
|
3803
|
-
const resolved =
|
|
4250
|
+
const resolved = realpathSync2(process.argv[1] ?? "");
|
|
3804
4251
|
if (/\/Cellar\/[^/]+\//.test(resolved)) {
|
|
3805
4252
|
return "brew";
|
|
3806
4253
|
}
|
|
@@ -3840,7 +4287,7 @@ function performUpdate(version) {
|
|
|
3840
4287
|
function detectBrewOwner() {
|
|
3841
4288
|
for (const prefix of ["/home/linuxbrew/.linuxbrew", "/opt/homebrew", "/usr/local"]) {
|
|
3842
4289
|
const cellar = `${prefix}/Cellar`;
|
|
3843
|
-
if (!
|
|
4290
|
+
if (!existsSync7(cellar)) continue;
|
|
3844
4291
|
try {
|
|
3845
4292
|
return execSync(`stat -c %U "${cellar}" 2>/dev/null || stat -f %Su "${cellar}"`, { encoding: "utf-8" }).trim() || null;
|
|
3846
4293
|
} catch {
|
|
@@ -3850,7 +4297,7 @@ function detectBrewOwner() {
|
|
|
3850
4297
|
}
|
|
3851
4298
|
async function updateCommand(opts = {}) {
|
|
3852
4299
|
const json = isJsonMode();
|
|
3853
|
-
const spinner =
|
|
4300
|
+
const spinner = ora16({ text: "Checking for updates\u2026", isSilent: json });
|
|
3854
4301
|
spinner.start();
|
|
3855
4302
|
const versionInfo = await fetchLatestVersion();
|
|
3856
4303
|
if (!versionInfo) {
|
|
@@ -3869,13 +4316,13 @@ async function updateCommand(opts = {}) {
|
|
|
3869
4316
|
if (json) {
|
|
3870
4317
|
jsonOutput({ ok: true, current: cliVersion, latest: versionInfo.latest, update_available: false });
|
|
3871
4318
|
} else {
|
|
3872
|
-
success(`You're on the latest version (${
|
|
4319
|
+
success(`You're on the latest version (${chalk18.bold(cliVersion)})`);
|
|
3873
4320
|
}
|
|
3874
4321
|
return;
|
|
3875
4322
|
}
|
|
3876
4323
|
spinner.stop();
|
|
3877
4324
|
if (!json) {
|
|
3878
|
-
info(`Update available: ${
|
|
4325
|
+
info(`Update available: ${chalk18.dim(cliVersion)} \u2192 ${chalk18.bold.green(versionInfo.latest)}`);
|
|
3879
4326
|
if (versionInfo.changelog_url) {
|
|
3880
4327
|
info(`Changelog: ${versionInfo.changelog_url}`);
|
|
3881
4328
|
}
|
|
@@ -3886,16 +4333,16 @@ async function updateCommand(opts = {}) {
|
|
|
3886
4333
|
if (!json) {
|
|
3887
4334
|
warn("Active processes detected:");
|
|
3888
4335
|
if (managerPid) {
|
|
3889
|
-
console.error(
|
|
4336
|
+
console.error(chalk18.yellow(` \u2022 Manager process running (PID ${managerPid})`));
|
|
3890
4337
|
}
|
|
3891
4338
|
if (tmuxSessions.length > 0) {
|
|
3892
|
-
console.error(
|
|
4339
|
+
console.error(chalk18.yellow(` \u2022 ${tmuxSessions.length} active agent session(s): ${tmuxSessions.join(", ")}`));
|
|
3893
4340
|
}
|
|
3894
4341
|
console.error();
|
|
3895
4342
|
warn("Updating while the manager is running will leave it on the old version until restarted.");
|
|
3896
4343
|
warn("Active agent sessions will continue with stale MCP servers.");
|
|
3897
4344
|
console.error();
|
|
3898
|
-
info(`Run ${
|
|
4345
|
+
info(`Run ${chalk18.bold("agt update --force")} to update anyway, then restart the manager.`);
|
|
3899
4346
|
} else {
|
|
3900
4347
|
jsonOutput({
|
|
3901
4348
|
ok: false,
|
|
@@ -3913,7 +4360,7 @@ async function updateCommand(opts = {}) {
|
|
|
3913
4360
|
return;
|
|
3914
4361
|
}
|
|
3915
4362
|
}
|
|
3916
|
-
const updateSpinner =
|
|
4363
|
+
const updateSpinner = ora16({ text: "Installing update\u2026", isSilent: json });
|
|
3917
4364
|
updateSpinner.start();
|
|
3918
4365
|
try {
|
|
3919
4366
|
performUpdate(versionInfo.latest);
|
|
@@ -3927,8 +4374,8 @@ async function updateCommand(opts = {}) {
|
|
|
3927
4374
|
updated: true
|
|
3928
4375
|
});
|
|
3929
4376
|
} else {
|
|
3930
|
-
success(`Updated to ${
|
|
3931
|
-
info(`If you have a running manager, restart it: ${
|
|
4377
|
+
success(`Updated to ${chalk18.bold(versionInfo.latest)}`);
|
|
4378
|
+
info(`If you have a running manager, restart it: ${chalk18.bold("agt manager stop && agt manager start")}`);
|
|
3932
4379
|
}
|
|
3933
4380
|
} catch (err) {
|
|
3934
4381
|
updateSpinner.fail("Update failed");
|
|
@@ -3959,8 +4406,8 @@ async function checkForUpdateOnStartup() {
|
|
|
3959
4406
|
if (!versionInfo) return;
|
|
3960
4407
|
if (isNewerVersion(cliVersion, versionInfo.latest)) {
|
|
3961
4408
|
console.error(
|
|
3962
|
-
|
|
3963
|
-
Update available: ${cliVersion} \u2192 ${versionInfo.latest}. Run ${
|
|
4409
|
+
chalk18.yellow(`
|
|
4410
|
+
Update available: ${cliVersion} \u2192 ${versionInfo.latest}. Run ${chalk18.bold("agt update")} to install.`) + chalk18.dim("\n Note: Restart the manager after updating.\n")
|
|
3964
4411
|
);
|
|
3965
4412
|
}
|
|
3966
4413
|
} catch {
|
|
@@ -3968,11 +4415,11 @@ async function checkForUpdateOnStartup() {
|
|
|
3968
4415
|
}
|
|
3969
4416
|
|
|
3970
4417
|
// src/commands/integration.ts
|
|
3971
|
-
import
|
|
3972
|
-
import
|
|
4418
|
+
import chalk19 from "chalk";
|
|
4419
|
+
import ora17 from "ora";
|
|
3973
4420
|
async function integrationListCommand() {
|
|
3974
4421
|
const json = isJsonMode();
|
|
3975
|
-
const spinner =
|
|
4422
|
+
const spinner = ora17({ text: "Fetching integrations\u2026", isSilent: json }).start();
|
|
3976
4423
|
try {
|
|
3977
4424
|
const data = await api.get("/integrations/bindings");
|
|
3978
4425
|
spinner.stop();
|
|
@@ -3984,11 +4431,11 @@ async function integrationListCommand() {
|
|
|
3984
4431
|
info("No integrations found for this team.");
|
|
3985
4432
|
return;
|
|
3986
4433
|
}
|
|
3987
|
-
console.log(
|
|
4434
|
+
console.log(chalk19.bold("\nAvailable Integrations:\n"));
|
|
3988
4435
|
for (const p of data) {
|
|
3989
|
-
const statusColor = p.status === "published" ?
|
|
4436
|
+
const statusColor = p.status === "published" ? chalk19.green : p.status === "archived" ? chalk19.gray : chalk19.yellow;
|
|
3990
4437
|
console.log(
|
|
3991
|
-
` ${
|
|
4438
|
+
` ${chalk19.bold(p.name)} ${chalk19.dim(`(${p.slug})`)} ${statusColor(p.status)} v${p.version} ${chalk19.dim(`${p.skills?.length ?? 0} skills, ${p.defined_scopes?.length ?? 0} scopes`)}`
|
|
3992
4439
|
);
|
|
3993
4440
|
}
|
|
3994
4441
|
console.log();
|
|
@@ -3999,7 +4446,7 @@ async function integrationListCommand() {
|
|
|
3999
4446
|
}
|
|
4000
4447
|
async function integrationShowCommand(slug) {
|
|
4001
4448
|
const json = isJsonMode();
|
|
4002
|
-
const spinner =
|
|
4449
|
+
const spinner = ora17({ text: "Fetching integration\u2026", isSilent: json }).start();
|
|
4003
4450
|
try {
|
|
4004
4451
|
const all = await api.get("/integrations/bindings");
|
|
4005
4452
|
const integration2 = all.find((p) => p.slug === slug);
|
|
@@ -4015,21 +4462,21 @@ async function integrationShowCommand(slug) {
|
|
|
4015
4462
|
jsonOutput({ integration: integration2, scopes });
|
|
4016
4463
|
return;
|
|
4017
4464
|
}
|
|
4018
|
-
console.log(
|
|
4019
|
-
${integration2.name}`),
|
|
4020
|
-
if (integration2.description) console.log(
|
|
4465
|
+
console.log(chalk19.bold(`
|
|
4466
|
+
${integration2.name}`), chalk19.dim(`(${integration2.slug})`));
|
|
4467
|
+
if (integration2.description) console.log(chalk19.dim(integration2.description));
|
|
4021
4468
|
console.log();
|
|
4022
4469
|
console.log(` Status: ${integration2.status} v${integration2.version}`);
|
|
4023
4470
|
console.log(` Category: ${integration2.category}`);
|
|
4024
4471
|
console.log(` Toolkits: ${integration2.required_toolkits.join(", ") || "none"}`);
|
|
4025
4472
|
console.log(` Skills: ${integration2.skills?.length ?? 0}`);
|
|
4026
4473
|
if (scopes.length > 0) {
|
|
4027
|
-
console.log(
|
|
4474
|
+
console.log(chalk19.bold("\n Permission Scopes:\n"));
|
|
4028
4475
|
for (const s of scopes) {
|
|
4029
|
-
const roleColor = s.can_grant ?
|
|
4030
|
-
const overrideTag = s.is_overridden ?
|
|
4476
|
+
const roleColor = s.can_grant ? chalk19.green : chalk19.red;
|
|
4477
|
+
const overrideTag = s.is_overridden ? chalk19.cyan(" [team override]") : "";
|
|
4031
4478
|
console.log(
|
|
4032
|
-
` ${
|
|
4479
|
+
` ${chalk19.bold(s.id)} ${s.name} min: ${roleColor(s.effective_min_role)}${overrideTag} ${s.can_grant ? chalk19.green("\u2713 grantable") : chalk19.red("\u2717 needs approval")}`
|
|
4033
4480
|
);
|
|
4034
4481
|
}
|
|
4035
4482
|
}
|
|
@@ -4041,7 +4488,7 @@ ${integration2.name}`), chalk18.dim(`(${integration2.slug})`));
|
|
|
4041
4488
|
}
|
|
4042
4489
|
async function integrationInstallCommand(slug, options) {
|
|
4043
4490
|
const json = isJsonMode();
|
|
4044
|
-
const spinner =
|
|
4491
|
+
const spinner = ora17({ text: "Installing integration\u2026", isSilent: json }).start();
|
|
4045
4492
|
try {
|
|
4046
4493
|
const all = await api.get("/integrations/bindings");
|
|
4047
4494
|
const integration2 = all.find((p) => p.slug === slug);
|
|
@@ -4080,7 +4527,7 @@ async function integrationInstallCommand(slug, options) {
|
|
|
4080
4527
|
const needsApproval = err.body["needs_approval"];
|
|
4081
4528
|
error("Some scopes exceed your role ceiling:");
|
|
4082
4529
|
for (const s of needsApproval) {
|
|
4083
|
-
console.log(
|
|
4530
|
+
console.log(chalk19.yellow(` - ${s}`));
|
|
4084
4531
|
}
|
|
4085
4532
|
info("To request elevated scopes, use the web dashboard or contact your team admin.");
|
|
4086
4533
|
process.exitCode = 1;
|
|
@@ -4091,7 +4538,7 @@ async function integrationInstallCommand(slug, options) {
|
|
|
4091
4538
|
}
|
|
4092
4539
|
async function integrationUninstallCommand(slug, options) {
|
|
4093
4540
|
const json = isJsonMode();
|
|
4094
|
-
const spinner =
|
|
4541
|
+
const spinner = ora17({ text: "Uninstalling integration\u2026", isSilent: json }).start();
|
|
4095
4542
|
try {
|
|
4096
4543
|
const all = await api.get("/integrations/bindings");
|
|
4097
4544
|
const integration2 = all.find((p) => p.slug === slug);
|
|
@@ -4122,7 +4569,7 @@ async function integrationUninstallCommand(slug, options) {
|
|
|
4122
4569
|
}
|
|
4123
4570
|
async function integrationRequestsCommand() {
|
|
4124
4571
|
const json = isJsonMode();
|
|
4125
|
-
const spinner =
|
|
4572
|
+
const spinner = ora17({ text: "Fetching scope requests\u2026", isSilent: json }).start();
|
|
4126
4573
|
try {
|
|
4127
4574
|
const requests = await api.get("/integrations/bindings/scope-requests");
|
|
4128
4575
|
spinner.stop();
|
|
@@ -4134,10 +4581,10 @@ async function integrationRequestsCommand() {
|
|
|
4134
4581
|
info("No pending scope requests.");
|
|
4135
4582
|
return;
|
|
4136
4583
|
}
|
|
4137
|
-
console.log(
|
|
4584
|
+
console.log(chalk19.bold("\nPending Scope Requests:\n"));
|
|
4138
4585
|
for (const r of requests) {
|
|
4139
|
-
console.log(` ${
|
|
4140
|
-
if (r.reason) console.log(` reason: ${
|
|
4586
|
+
console.log(` ${chalk19.bold(r.id)} scopes: ${r.requested_scopes.join(", ")}`);
|
|
4587
|
+
if (r.reason) console.log(` reason: ${chalk19.dim(r.reason)}`);
|
|
4141
4588
|
console.log(` status: ${r.status} requested: ${r.created_at}`);
|
|
4142
4589
|
console.log();
|
|
4143
4590
|
}
|
|
@@ -4148,7 +4595,7 @@ async function integrationRequestsCommand() {
|
|
|
4148
4595
|
}
|
|
4149
4596
|
async function integrationApproveCommand(requestId, options) {
|
|
4150
4597
|
const json = isJsonMode();
|
|
4151
|
-
const spinner =
|
|
4598
|
+
const spinner = ora17({ text: "Approving request\u2026", isSilent: json }).start();
|
|
4152
4599
|
try {
|
|
4153
4600
|
const result = await api.put(`/integrations/bindings/scope-requests/${requestId}`, {
|
|
4154
4601
|
status: "approved",
|
|
@@ -4167,7 +4614,7 @@ async function integrationApproveCommand(requestId, options) {
|
|
|
4167
4614
|
}
|
|
4168
4615
|
async function integrationDenyCommand(requestId, options) {
|
|
4169
4616
|
const json = isJsonMode();
|
|
4170
|
-
const spinner =
|
|
4617
|
+
const spinner = ora17({ text: "Denying request\u2026", isSilent: json }).start();
|
|
4171
4618
|
try {
|
|
4172
4619
|
const result = await api.put(`/integrations/bindings/scope-requests/${requestId}`, {
|
|
4173
4620
|
status: "denied",
|
|
@@ -4209,7 +4656,7 @@ async function integrationEnrolCommand(options) {
|
|
|
4209
4656
|
const [secretKey, secretValue] = secrets[0];
|
|
4210
4657
|
const credentials = { [secretKey]: secretValue };
|
|
4211
4658
|
const authType = options.authType ?? (options.webhookSecret ? "webhook" : options.apiKey ? "api_key" : "oauth2");
|
|
4212
|
-
const spinner =
|
|
4659
|
+
const spinner = ora17({ text: `Enrolling ${options.def} at ${apiScope} scope\u2026`, isSilent: json }).start();
|
|
4213
4660
|
try {
|
|
4214
4661
|
let agentId;
|
|
4215
4662
|
if (apiScope === "agent") {
|
|
@@ -4268,7 +4715,7 @@ function handleError(err) {
|
|
|
4268
4715
|
}
|
|
4269
4716
|
|
|
4270
4717
|
// src/bin/agt.ts
|
|
4271
|
-
var cliVersion2 = true ? "0.
|
|
4718
|
+
var cliVersion2 = true ? "0.27.0" : "dev";
|
|
4272
4719
|
var program = new Command();
|
|
4273
4720
|
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");
|
|
4274
4721
|
program.hook("preAction", (thisCommand) => {
|
|
@@ -4283,6 +4730,24 @@ var team = program.command("team").description("Manage teams");
|
|
|
4283
4730
|
team.command("list").description("List teams you belong to").action(teamListCommand);
|
|
4284
4731
|
team.command("create <name>").description("Create a new team and set it as active").action(teamCreateCommand);
|
|
4285
4732
|
team.command("switch <slug>").description("Switch the active team").action(teamSwitchCommand);
|
|
4733
|
+
var impersonate = program.command("impersonate").description(
|
|
4734
|
+
"Operate as a managed agent locally (experimental, gated by AGT_IMPERSONATE_ENABLED)"
|
|
4735
|
+
);
|
|
4736
|
+
impersonate.command("connect <token>").description(
|
|
4737
|
+
"Redeem a XXXX-XXXX token from the webapp, render the agent's CLAUDE.md + .mcp.json, swap them into the current project, and launch Claude Code (use --no-launch to skip the launch)"
|
|
4738
|
+
).option(
|
|
4739
|
+
"--no-launch",
|
|
4740
|
+
"Skip launching Claude Code after the swap (operator manages their own session)"
|
|
4741
|
+
).action(
|
|
4742
|
+
(token, opts) => (
|
|
4743
|
+
// Commander negates --no-FLAG into opts.flag, so --no-launch arrives
|
|
4744
|
+
// as opts.launch === false. Translate to the command's noLaunch
|
|
4745
|
+
// option, defaulting to launching when the flag isn't passed.
|
|
4746
|
+
impersonateConnectCommand(token, { noLaunch: opts.launch === false })
|
|
4747
|
+
)
|
|
4748
|
+
);
|
|
4749
|
+
impersonate.command("current").description('Print the active impersonation (or "none")').action(impersonateCurrentCommand);
|
|
4750
|
+
impersonate.command("exit").description("End the active impersonation, restore project files, and notify the server").action(impersonateExitCommand);
|
|
4286
4751
|
program.command("init").description("Create a new agent (interactive wizard, or non-interactive with --name)").option("--name <display-name>", "Agent display name (triggers non-interactive mode)").option("--code-name <code-name>", "Agent code name (kebab-case, derived from --name if omitted)").option("--description <text>", "Short agent description").option("--env <environment>", "Environment: dev | stage | prod", "dev").option("--risk-tier <tier>", "Risk tier: Low | Medium | High", "Low").option("--budget-type <type>", "Budget type: tokens | dollars | both", "tokens").option("--budget-tokens <number>", "Token budget limit", "10000").option("--budget-dollars <number>", "Dollar budget limit", "10").option("--budget-window <window>", "Budget window: daily | weekly | monthly", "daily").option("--budget-enforcement <mode>", "Budget enforcement: block | throttle | alert | degrade", "block").option("--channels <list>", "Comma-separated channel IDs").option("--logging <mode>", "Logging mode: redacted | hash-only | full-local", "redacted").action(initCommand);
|
|
4287
4752
|
program.command("lint").argument("[path]", "Path to agent directory (default: all agents in .augmented/)").description("Lint CHARTER.md and TOOLS.md files").action(lintCommand);
|
|
4288
4753
|
var channels = program.command("channels").description("Manage and inspect channel configuration");
|
|
@@ -4315,16 +4780,16 @@ host.command("pair <host-name>").description("Start an SSM port-forward + shell
|
|
|
4315
4780
|
})
|
|
4316
4781
|
);
|
|
4317
4782
|
var manager = program.command("manager").description("Host config sync daemon \u2014 keeps local agent files in sync with API");
|
|
4318
|
-
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",
|
|
4319
|
-
manager.command("stop").description("Stop the running manager daemon").option("--config-dir <dir>", "Config directory for agent files",
|
|
4320
|
-
manager.command("status").description("Show the current manager daemon status and discovered agents").option("--config-dir <dir>", "Config directory for agent files",
|
|
4321
|
-
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",
|
|
4322
|
-
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",
|
|
4783
|
+
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", join12(homedir5(), ".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);
|
|
4784
|
+
manager.command("stop").description("Stop the running manager daemon").option("--config-dir <dir>", "Config directory for agent files", join12(homedir5(), ".augmented")).action(managerStopCommand);
|
|
4785
|
+
manager.command("status").description("Show the current manager daemon status and discovered agents").option("--config-dir <dir>", "Config directory for agent files", join12(homedir5(), ".augmented")).action(managerStatusCommand);
|
|
4786
|
+
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", join12(homedir5(), ".augmented")).option("--no-tui", "Skip the TUI and stream the manager log to stdout instead (CI / scripts)").action(managerWatchCommand);
|
|
4787
|
+
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", join12(homedir5(), ".augmented")).action(managerInstallCommand);
|
|
4323
4788
|
manager.command("uninstall").description("Remove the OS-level supervisor (launchctl unload + delete plist on macOS). Idempotent.").action(managerUninstallCommand);
|
|
4324
4789
|
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);
|
|
4325
4790
|
manager.command("uninstall-system-unit").description("Remove the system-level systemd unit (Linux, root). Idempotent. (ENG-4706)").action(managerUninstallSystemUnitCommand);
|
|
4326
4791
|
var agent = program.command("agent").description("Inspect and manage agents");
|
|
4327
|
-
agent.command("show <code-name>").description("Display an agent's provisioned OpenClaw configuration").option("--config-dir <dir>", "Config directory",
|
|
4792
|
+
agent.command("show <code-name>").description("Display an agent's provisioned OpenClaw configuration").option("--config-dir <dir>", "Config directory", join12(homedir5(), ".augmented")).option("--all-channels", "Show all channels (including disabled)").action(agentShowCommand);
|
|
4328
4793
|
var kanban = program.command("kanban").description("Manage agent kanban boards");
|
|
4329
4794
|
kanban.command("list").description("List kanban board items for an agent").requiredOption("--agent <code-name>", "Agent code name").action(kanbanListCommand);
|
|
4330
4795
|
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);
|