@integrity-labs/agt-cli 0.26.2-eng5706.1 → 0.26.2
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 +603 -180
- package/dist/bin/agt.js.map +1 -1
- package/dist/{chunk-LBYU24PW.js → chunk-4CESBZPM.js} +21 -7
- package/dist/{chunk-LBYU24PW.js.map → chunk-4CESBZPM.js.map} +1 -1
- package/dist/lib/manager-worker.js +2 -2
- package/dist/lib/manager-worker.js.map +1 -1
- package/dist/mcp/direct-chat-channel.js +92 -0
- package/dist/mcp/slack-channel.js +15 -12
- package/dist/mcp/telegram-channel.js +130 -40
- package/package.json +1 -1
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-4CESBZPM.js";
|
|
30
30
|
import {
|
|
31
31
|
CHANNEL_REGISTRY,
|
|
32
32
|
DEPLOYMENT_TEMPLATES,
|
|
@@ -55,8 +55,8 @@ import {
|
|
|
55
55
|
} from "../chunk-U3HCB23E.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,429 @@ 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 { writeFileSync as writeFileSync5 } from "fs";
|
|
1559
|
+
import { join as join7 } from "path";
|
|
1560
|
+
|
|
1561
|
+
// src/lib/impersonate-flag.ts
|
|
1562
|
+
var ENV_VAR = "AGT_IMPERSONATE_ENABLED";
|
|
1563
|
+
function impersonateEnabled() {
|
|
1564
|
+
const v = process.env[ENV_VAR];
|
|
1565
|
+
if (!v) return false;
|
|
1566
|
+
const trimmed = v.trim().toLowerCase();
|
|
1567
|
+
return !(trimmed === "" || trimmed === "0" || trimmed === "false" || trimmed === "no");
|
|
1568
|
+
}
|
|
1569
|
+
function requireImpersonateEnabled() {
|
|
1570
|
+
if (impersonateEnabled()) return;
|
|
1571
|
+
process.stderr.write(
|
|
1572
|
+
`
|
|
1573
|
+
The \`agt impersonate\` commands are gated behind a feature flag.
|
|
1574
|
+
|
|
1575
|
+
To enable them, set ${ENV_VAR}=1 in your environment:
|
|
1576
|
+
|
|
1577
|
+
export ${ENV_VAR}=1
|
|
1578
|
+
|
|
1579
|
+
These commands are part of the operator-impersonates-agent v0 flow
|
|
1580
|
+
(ENG-5687 / ENG-5688). The gating will be removed once the
|
|
1581
|
+
surrounding pieces (ENG-5689 channel-MCP refusal, ENG-5690 slash
|
|
1582
|
+
commands) land and the end-to-end smoke test has been run against
|
|
1583
|
+
production.
|
|
1584
|
+
|
|
1585
|
+
`
|
|
1586
|
+
);
|
|
1587
|
+
process.exit(2);
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
// src/lib/impersonate-state.ts
|
|
1591
|
+
import {
|
|
1592
|
+
chmodSync,
|
|
1593
|
+
existsSync as existsSync2,
|
|
1594
|
+
mkdirSync as mkdirSync4,
|
|
1595
|
+
readFileSync as readFileSync2,
|
|
1596
|
+
renameSync,
|
|
1597
|
+
rmSync,
|
|
1598
|
+
writeFileSync as writeFileSync4
|
|
1599
|
+
} from "fs";
|
|
1600
|
+
import { homedir } from "os";
|
|
1601
|
+
import { join as join6 } from "path";
|
|
1602
|
+
var IMPERSONATE_ROOT = join6(homedir(), ".augmented-impersonate");
|
|
1603
|
+
var ACTIVE_DIR = join6(IMPERSONATE_ROOT, "active");
|
|
1604
|
+
var ACTIVE_MANIFEST_PATH = join6(ACTIVE_DIR, "manifest.json");
|
|
1605
|
+
var CODE_NAME_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
1606
|
+
function assertValidCodeName(codeName) {
|
|
1607
|
+
if (!CODE_NAME_RE.test(codeName)) {
|
|
1608
|
+
throw new Error(
|
|
1609
|
+
`Invalid agent code_name "${codeName}" \u2014 must be lowercase kebab-case (a-z, 0-9, hyphens; no leading/trailing/consecutive hyphens).`
|
|
1610
|
+
);
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
function getImpersonateCodeNameDir(codeName) {
|
|
1614
|
+
assertValidCodeName(codeName);
|
|
1615
|
+
return join6(IMPERSONATE_ROOT, codeName);
|
|
1616
|
+
}
|
|
1617
|
+
function ensureCodeNameDir(codeName) {
|
|
1618
|
+
assertValidCodeName(codeName);
|
|
1619
|
+
const dir = getImpersonateCodeNameDir(codeName);
|
|
1620
|
+
mkdirSync4(dir, { recursive: true });
|
|
1621
|
+
return dir;
|
|
1622
|
+
}
|
|
1623
|
+
function readActiveManifest() {
|
|
1624
|
+
if (!existsSync2(ACTIVE_MANIFEST_PATH)) return null;
|
|
1625
|
+
try {
|
|
1626
|
+
const raw = readFileSync2(ACTIVE_MANIFEST_PATH, "utf-8");
|
|
1627
|
+
const parsed = JSON.parse(raw);
|
|
1628
|
+
const backupsOk = typeof parsed.backups === "object" && parsed.backups !== null && Object.values(parsed.backups).every(
|
|
1629
|
+
(v) => v === "had-file" || v === "was-symlink" || v === "didnt-exist"
|
|
1630
|
+
);
|
|
1631
|
+
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
|
|
1632
|
+
// the right environment. Required, not optional, because writing the
|
|
1633
|
+
// wrong host on an existing session would point exit at a server
|
|
1634
|
+
// that has no record of the session — silently misleading.
|
|
1635
|
+
typeof parsed.host !== "string" || parsed.host.length === 0 || !backupsOk) {
|
|
1636
|
+
return null;
|
|
1637
|
+
}
|
|
1638
|
+
return parsed;
|
|
1639
|
+
} catch {
|
|
1640
|
+
return null;
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
function writeActiveManifest(manifest) {
|
|
1644
|
+
mkdirSync4(ACTIVE_DIR, { recursive: true, mode: 448 });
|
|
1645
|
+
const tmp = ACTIVE_MANIFEST_PATH + ".tmp";
|
|
1646
|
+
writeFileSync4(tmp, JSON.stringify(manifest, null, 2), { mode: 384 });
|
|
1647
|
+
chmodSync(tmp, 384);
|
|
1648
|
+
renameSync(tmp, ACTIVE_MANIFEST_PATH);
|
|
1649
|
+
}
|
|
1650
|
+
function clearActiveManifest() {
|
|
1651
|
+
if (existsSync2(ACTIVE_MANIFEST_PATH)) {
|
|
1652
|
+
rmSync(ACTIVE_MANIFEST_PATH);
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
function isExpired(manifest, now = Date.now()) {
|
|
1656
|
+
return manifest.expires_at * 1e3 <= now;
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
// src/lib/impersonate-fs-ops.ts
|
|
1660
|
+
import {
|
|
1661
|
+
existsSync as existsSync3,
|
|
1662
|
+
lstatSync,
|
|
1663
|
+
readlinkSync,
|
|
1664
|
+
realpathSync,
|
|
1665
|
+
renameSync as renameSync2,
|
|
1666
|
+
symlinkSync,
|
|
1667
|
+
unlinkSync
|
|
1668
|
+
} from "fs";
|
|
1669
|
+
import { homedir as homedir2 } from "os";
|
|
1670
|
+
import { sep } from "path";
|
|
1671
|
+
var BACKUP_SUFFIX = ".pre-impersonate-backup";
|
|
1672
|
+
function swapInPersonaFile(projectPath, personaPath) {
|
|
1673
|
+
let kind;
|
|
1674
|
+
if (!lstatSafe(projectPath)) {
|
|
1675
|
+
kind = "didnt-exist";
|
|
1676
|
+
} else {
|
|
1677
|
+
const stat = lstatSync(projectPath);
|
|
1678
|
+
if (stat.isSymbolicLink()) {
|
|
1679
|
+
const target = readlinkSync(projectPath);
|
|
1680
|
+
const ourRoot = realpathSync(homedir2()) + sep + ".augmented-impersonate" + sep;
|
|
1681
|
+
let resolvedTarget;
|
|
1682
|
+
try {
|
|
1683
|
+
resolvedTarget = realpathSync(projectPath);
|
|
1684
|
+
} catch {
|
|
1685
|
+
throw new Error(
|
|
1686
|
+
`${projectPath} is a broken symlink (target ${target}). Resolve manually and retry.`
|
|
1687
|
+
);
|
|
1688
|
+
}
|
|
1689
|
+
if (resolvedTarget.startsWith(ourRoot)) {
|
|
1690
|
+
unlinkSync(projectPath);
|
|
1691
|
+
kind = "was-symlink";
|
|
1692
|
+
} else {
|
|
1693
|
+
throw new Error(
|
|
1694
|
+
`${projectPath} is a symlink to ${target} (resolved: ${resolvedTarget}). Refusing to replace it \u2014 it doesn't live under ${ourRoot}. Resolve manually and retry.`
|
|
1695
|
+
);
|
|
1696
|
+
}
|
|
1697
|
+
} else {
|
|
1698
|
+
const backupPath = projectPath + BACKUP_SUFFIX;
|
|
1699
|
+
if (existsSync3(backupPath)) {
|
|
1700
|
+
throw new Error(
|
|
1701
|
+
`${backupPath} already exists from a previous run. Remove it manually if you're sure it's stale, then retry.`
|
|
1702
|
+
);
|
|
1703
|
+
}
|
|
1704
|
+
renameSync2(projectPath, backupPath);
|
|
1705
|
+
kind = "had-file";
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
symlinkSync(personaPath, projectPath, "file");
|
|
1709
|
+
return kind;
|
|
1710
|
+
}
|
|
1711
|
+
function restoreSwapped(projectPath, kind) {
|
|
1712
|
+
if (lstatSafe(projectPath)) {
|
|
1713
|
+
const stat = lstatSync(projectPath);
|
|
1714
|
+
if (stat.isSymbolicLink()) {
|
|
1715
|
+
unlinkSync(projectPath);
|
|
1716
|
+
} else {
|
|
1717
|
+
return;
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
if (kind === "had-file") {
|
|
1721
|
+
const backupPath = projectPath + BACKUP_SUFFIX;
|
|
1722
|
+
if (existsSync3(backupPath)) {
|
|
1723
|
+
renameSync2(backupPath, projectPath);
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
function lstatSafe(path) {
|
|
1728
|
+
try {
|
|
1729
|
+
lstatSync(path);
|
|
1730
|
+
return true;
|
|
1731
|
+
} catch {
|
|
1732
|
+
return false;
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
// src/commands/impersonate.ts
|
|
1737
|
+
var SWAP_FILES = ["CLAUDE.md", ".mcp.json"];
|
|
1738
|
+
var AGENT_IMPERSONATION_HEADER = "X-Agent-Impersonation";
|
|
1739
|
+
var REDEEM_TOKEN_RE = /^[A-HJ-NP-Z2-9]{4}-[A-HJ-NP-Z2-9]{4}$/i;
|
|
1740
|
+
async function impersonateConnectCommand(token) {
|
|
1741
|
+
requireImpersonateEnabled();
|
|
1742
|
+
const json = isJsonMode();
|
|
1743
|
+
if (!REDEEM_TOKEN_RE.test(token)) {
|
|
1744
|
+
const msg = "Token is not in the expected XXXX-XXXX format. Copy the full `agt impersonate connect <token>` command from the webapp.";
|
|
1745
|
+
if (json) jsonOutput({ ok: false, error: msg });
|
|
1746
|
+
else error(msg);
|
|
1747
|
+
process.exitCode = 1;
|
|
1748
|
+
return;
|
|
1749
|
+
}
|
|
1750
|
+
const existing = readActiveManifest();
|
|
1751
|
+
if (existing) {
|
|
1752
|
+
const msg = `An impersonation session is already active (${existing.code_name}). Run \`agt impersonate exit\` first.`;
|
|
1753
|
+
if (json) jsonOutput({ ok: false, error: msg });
|
|
1754
|
+
else error(msg);
|
|
1755
|
+
process.exitCode = 1;
|
|
1756
|
+
return;
|
|
1757
|
+
}
|
|
1758
|
+
if (!json) console.log(chalk10.bold("\nAugmented \u2014 Impersonate\n"));
|
|
1759
|
+
const spinner = ora10({
|
|
1760
|
+
text: "Redeeming token\u2026",
|
|
1761
|
+
isSilent: json
|
|
1762
|
+
});
|
|
1763
|
+
spinner.start();
|
|
1764
|
+
const host2 = getHost();
|
|
1765
|
+
let bundle;
|
|
1766
|
+
try {
|
|
1767
|
+
const res = await fetch(`${host2}/impersonate/agent/redeem`, {
|
|
1768
|
+
method: "POST",
|
|
1769
|
+
headers: { "Content-Type": "application/json" },
|
|
1770
|
+
body: JSON.stringify({ redeem_token: token })
|
|
1771
|
+
});
|
|
1772
|
+
if (!res.ok) {
|
|
1773
|
+
const body = await res.json().catch(() => ({}));
|
|
1774
|
+
const detail = body["error"] ?? `HTTP ${res.status}`;
|
|
1775
|
+
throw new Error(`${detail} (HTTP ${res.status})`);
|
|
1776
|
+
}
|
|
1777
|
+
bundle = await res.json();
|
|
1778
|
+
} catch (err) {
|
|
1779
|
+
spinner.fail("Failed to redeem token.");
|
|
1780
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1781
|
+
if (json) jsonOutput({ ok: false, error: msg });
|
|
1782
|
+
else error(msg);
|
|
1783
|
+
process.exitCode = 1;
|
|
1784
|
+
return;
|
|
1785
|
+
}
|
|
1786
|
+
spinner.text = "Writing persona files\u2026";
|
|
1787
|
+
const personaDir = ensureCodeNameDir(bundle.agent.code_name);
|
|
1788
|
+
writeFileSync5(
|
|
1789
|
+
join7(personaDir, "CLAUDE.md"),
|
|
1790
|
+
bundle.artifacts["CLAUDE.md"]
|
|
1791
|
+
);
|
|
1792
|
+
writeFileSync5(
|
|
1793
|
+
join7(personaDir, ".mcp.json"),
|
|
1794
|
+
bundle.artifacts[".mcp.json"]
|
|
1795
|
+
);
|
|
1796
|
+
spinner.text = "Swapping persona files\u2026";
|
|
1797
|
+
const projectCwd = process.cwd();
|
|
1798
|
+
const backups = {};
|
|
1799
|
+
const swapped = [];
|
|
1800
|
+
try {
|
|
1801
|
+
for (const file of SWAP_FILES) {
|
|
1802
|
+
const projectPath = join7(projectCwd, file);
|
|
1803
|
+
const kind = swapInPersonaFile(projectPath, join7(personaDir, file));
|
|
1804
|
+
backups[file] = kind;
|
|
1805
|
+
swapped.push({ file, kind });
|
|
1806
|
+
}
|
|
1807
|
+
const manifest = {
|
|
1808
|
+
code_name: bundle.agent.code_name,
|
|
1809
|
+
agent_id: bundle.agent.agent_id,
|
|
1810
|
+
team_id: bundle.agent.team_id,
|
|
1811
|
+
token: bundle.token,
|
|
1812
|
+
expires_at: bundle.expires_at,
|
|
1813
|
+
session_id: bundle.session_id,
|
|
1814
|
+
minted_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1815
|
+
project_cwd: projectCwd,
|
|
1816
|
+
host: host2,
|
|
1817
|
+
backups
|
|
1818
|
+
};
|
|
1819
|
+
writeActiveManifest(manifest);
|
|
1820
|
+
} catch (err) {
|
|
1821
|
+
for (const { file, kind } of [...swapped].reverse()) {
|
|
1822
|
+
try {
|
|
1823
|
+
restoreSwapped(join7(projectCwd, file), kind);
|
|
1824
|
+
} catch {
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
spinner.fail("Failed to enter impersonation; rolled back changes.");
|
|
1828
|
+
throw err;
|
|
1829
|
+
}
|
|
1830
|
+
spinner.stop();
|
|
1831
|
+
if (json) {
|
|
1832
|
+
jsonOutput({
|
|
1833
|
+
ok: true,
|
|
1834
|
+
code_name: bundle.agent.code_name,
|
|
1835
|
+
agent_id: bundle.agent.agent_id,
|
|
1836
|
+
session_id: bundle.session_id,
|
|
1837
|
+
expires_at: bundle.expires_at,
|
|
1838
|
+
project_cwd: projectCwd,
|
|
1839
|
+
host: host2,
|
|
1840
|
+
swapped_files: SWAP_FILES
|
|
1841
|
+
});
|
|
1842
|
+
return;
|
|
1843
|
+
}
|
|
1844
|
+
success(`Impersonating ${chalk10.bold(bundle.agent.code_name)}`);
|
|
1845
|
+
console.log();
|
|
1846
|
+
console.log(` Agent ID: ${chalk10.dim(bundle.agent.agent_id)}`);
|
|
1847
|
+
console.log(` Team: ${chalk10.dim(bundle.agent.team_id)}`);
|
|
1848
|
+
console.log(` Session: ${chalk10.dim(bundle.session_id)}`);
|
|
1849
|
+
console.log(
|
|
1850
|
+
` Expires: ${chalk10.dim(new Date(bundle.expires_at * 1e3).toISOString())}`
|
|
1851
|
+
);
|
|
1852
|
+
console.log(` Project: ${chalk10.dim(projectCwd)}`);
|
|
1853
|
+
console.log(` Host: ${chalk10.dim(host2)}`);
|
|
1854
|
+
console.log(` Swapped: ${chalk10.dim(SWAP_FILES.join(", "))}`);
|
|
1855
|
+
console.log();
|
|
1856
|
+
info(
|
|
1857
|
+
"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)."
|
|
1858
|
+
);
|
|
1859
|
+
console.log();
|
|
1860
|
+
info("When done: `agt impersonate exit`");
|
|
1861
|
+
}
|
|
1862
|
+
async function impersonateCurrentCommand() {
|
|
1863
|
+
requireImpersonateEnabled();
|
|
1864
|
+
const json = isJsonMode();
|
|
1865
|
+
const manifest = readActiveManifest();
|
|
1866
|
+
if (!manifest) {
|
|
1867
|
+
if (json) jsonOutput({ ok: true, active: null });
|
|
1868
|
+
else info("No active impersonation.");
|
|
1869
|
+
return;
|
|
1870
|
+
}
|
|
1871
|
+
const expired = isExpired(manifest);
|
|
1872
|
+
if (json) {
|
|
1873
|
+
jsonOutput({
|
|
1874
|
+
ok: true,
|
|
1875
|
+
active: {
|
|
1876
|
+
code_name: manifest.code_name,
|
|
1877
|
+
agent_id: manifest.agent_id,
|
|
1878
|
+
team_id: manifest.team_id,
|
|
1879
|
+
session_id: manifest.session_id,
|
|
1880
|
+
minted_at: manifest.minted_at,
|
|
1881
|
+
expires_at: manifest.expires_at,
|
|
1882
|
+
expired,
|
|
1883
|
+
project_cwd: manifest.project_cwd,
|
|
1884
|
+
host: manifest.host
|
|
1885
|
+
}
|
|
1886
|
+
});
|
|
1887
|
+
return;
|
|
1888
|
+
}
|
|
1889
|
+
console.log();
|
|
1890
|
+
console.log(chalk10.bold(`Impersonating: ${manifest.code_name}`));
|
|
1891
|
+
console.log(` Agent ID: ${chalk10.dim(manifest.agent_id)}`);
|
|
1892
|
+
console.log(` Team: ${chalk10.dim(manifest.team_id)}`);
|
|
1893
|
+
console.log(` Session: ${chalk10.dim(manifest.session_id)}`);
|
|
1894
|
+
console.log(` Minted at: ${chalk10.dim(manifest.minted_at)}`);
|
|
1895
|
+
console.log(
|
|
1896
|
+
` Expires at: ${chalk10.dim(new Date(manifest.expires_at * 1e3).toISOString())} ` + (expired ? chalk10.red("(EXPIRED \u2014 run `agt impersonate exit`)") : "")
|
|
1897
|
+
);
|
|
1898
|
+
console.log(` Project: ${chalk10.dim(manifest.project_cwd)}`);
|
|
1899
|
+
console.log(` Host: ${chalk10.dim(manifest.host)}`);
|
|
1900
|
+
console.log();
|
|
1901
|
+
}
|
|
1902
|
+
async function impersonateExitCommand() {
|
|
1903
|
+
requireImpersonateEnabled();
|
|
1904
|
+
const json = isJsonMode();
|
|
1905
|
+
const manifest = readActiveManifest();
|
|
1906
|
+
if (!manifest) {
|
|
1907
|
+
if (json) jsonOutput({ ok: true, was_active: false });
|
|
1908
|
+
else info("No active impersonation to exit.");
|
|
1909
|
+
return;
|
|
1910
|
+
}
|
|
1911
|
+
let stopOk = true;
|
|
1912
|
+
let stopDetail = null;
|
|
1913
|
+
try {
|
|
1914
|
+
if (process.env["AGT_API_KEY"]) {
|
|
1915
|
+
await api.post("/impersonate/agent/stop", void 0, {
|
|
1916
|
+
[AGENT_IMPERSONATION_HEADER]: manifest.token
|
|
1917
|
+
});
|
|
1918
|
+
} else {
|
|
1919
|
+
const res = await fetch(`${manifest.host}/impersonate/agent/stop`, {
|
|
1920
|
+
method: "POST",
|
|
1921
|
+
headers: {
|
|
1922
|
+
"Content-Type": "application/json",
|
|
1923
|
+
[AGENT_IMPERSONATION_HEADER]: manifest.token
|
|
1924
|
+
}
|
|
1925
|
+
});
|
|
1926
|
+
if (!res.ok) {
|
|
1927
|
+
const body = await res.json().catch(() => ({}));
|
|
1928
|
+
const detail = body["error"] ?? `HTTP ${res.status}`;
|
|
1929
|
+
stopOk = false;
|
|
1930
|
+
stopDetail = `${detail} (HTTP ${res.status})`;
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
} catch (err) {
|
|
1934
|
+
stopOk = false;
|
|
1935
|
+
stopDetail = err instanceof ApiError ? `${err.message} (HTTP ${err.status})` : err instanceof Error ? err.message : String(err);
|
|
1936
|
+
}
|
|
1937
|
+
const restored = [];
|
|
1938
|
+
for (const [file, kind] of Object.entries(manifest.backups)) {
|
|
1939
|
+
const projectPath = join7(manifest.project_cwd, file);
|
|
1940
|
+
try {
|
|
1941
|
+
restoreSwapped(projectPath, kind);
|
|
1942
|
+
restored.push(file);
|
|
1943
|
+
} catch (err) {
|
|
1944
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1945
|
+
if (!json) error(`Failed to restore ${file}: ${msg}`);
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
clearActiveManifest();
|
|
1949
|
+
if (json) {
|
|
1950
|
+
jsonOutput({
|
|
1951
|
+
ok: true,
|
|
1952
|
+
was_active: true,
|
|
1953
|
+
session_id: manifest.session_id,
|
|
1954
|
+
stop_acknowledged: stopOk,
|
|
1955
|
+
stop_detail: stopDetail,
|
|
1956
|
+
restored
|
|
1957
|
+
});
|
|
1958
|
+
return;
|
|
1959
|
+
}
|
|
1960
|
+
success(`Exited impersonation: ${manifest.code_name}`);
|
|
1961
|
+
if (restored.length > 0) {
|
|
1962
|
+
info(`Restored: ${restored.join(", ")}`);
|
|
1963
|
+
}
|
|
1964
|
+
if (!stopOk) {
|
|
1965
|
+
info(`Note: server-side stop not acknowledged (${stopDetail}). Token will expire naturally.`);
|
|
1966
|
+
}
|
|
1967
|
+
info("Restart Claude Code to pick up your original persona.");
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1970
|
+
// src/commands/drift.ts
|
|
1971
|
+
import chalk11 from "chalk";
|
|
1972
|
+
import ora11 from "ora";
|
|
1558
1973
|
|
|
1559
1974
|
// ../../packages/core/dist/drift/live-state-reader.js
|
|
1560
1975
|
import { readFile } from "fs/promises";
|
|
1561
1976
|
import { createHash } from "crypto";
|
|
1562
|
-
import { join as
|
|
1977
|
+
import { join as join8 } from "path";
|
|
1563
1978
|
import JSON5 from "json5";
|
|
1564
1979
|
async function hashFile(filePath) {
|
|
1565
1980
|
try {
|
|
@@ -1583,8 +1998,8 @@ async function readLiveState(options) {
|
|
|
1583
1998
|
const toolsFile = trackedFiles.find((f) => f.includes("TOOLS")) ?? "TOOLS.md";
|
|
1584
1999
|
const [frameworkConfig, charterHash, toolsHash] = await Promise.all([
|
|
1585
2000
|
readJsonFile(options.configPath),
|
|
1586
|
-
hashFile(
|
|
1587
|
-
hashFile(
|
|
2001
|
+
hashFile(join8(options.teamDir, charterFile)),
|
|
2002
|
+
hashFile(join8(options.teamDir, toolsFile))
|
|
1588
2003
|
]);
|
|
1589
2004
|
return {
|
|
1590
2005
|
frameworkConfig,
|
|
@@ -1599,15 +2014,15 @@ var KEBAB_CASE_RE = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;
|
|
|
1599
2014
|
function severityColor(severity) {
|
|
1600
2015
|
switch (severity) {
|
|
1601
2016
|
case "critical":
|
|
1602
|
-
return
|
|
2017
|
+
return chalk11.red(severity);
|
|
1603
2018
|
case "warning":
|
|
1604
|
-
return
|
|
2019
|
+
return chalk11.yellow(severity);
|
|
1605
2020
|
default:
|
|
1606
|
-
return
|
|
2021
|
+
return chalk11.dim(severity);
|
|
1607
2022
|
}
|
|
1608
2023
|
}
|
|
1609
2024
|
function printDriftReport(report) {
|
|
1610
|
-
console.log(
|
|
2025
|
+
console.log(chalk11.bold(`
|
|
1611
2026
|
Drift Report: ${report.codeName}
|
|
1612
2027
|
`));
|
|
1613
2028
|
info(`Agent ID: ${report.agentId}`);
|
|
@@ -1636,7 +2051,7 @@ async function driftCheckCommand(codeName, opts) {
|
|
|
1636
2051
|
return;
|
|
1637
2052
|
}
|
|
1638
2053
|
const useJson = opts.json || isJsonMode();
|
|
1639
|
-
const spinner =
|
|
2054
|
+
const spinner = ora11({ text: `Checking drift for "${codeName}"\u2026`, isSilent: useJson });
|
|
1640
2055
|
spinner.start();
|
|
1641
2056
|
try {
|
|
1642
2057
|
const driftData = await api.get(`/agents/${encodeURIComponent(codeName)}/drift-data`);
|
|
@@ -1718,14 +2133,14 @@ async function driftWatchCommand(codeName, opts) {
|
|
|
1718
2133
|
const trackedFiles = adapter.driftTrackedFiles();
|
|
1719
2134
|
if (!useJson) {
|
|
1720
2135
|
console.log(
|
|
1721
|
-
|
|
2136
|
+
chalk11.bold(`
|
|
1722
2137
|
Watching drift for "${codeName}" every ${intervalSec}s. Press Ctrl+C to stop.
|
|
1723
2138
|
`)
|
|
1724
2139
|
);
|
|
1725
2140
|
}
|
|
1726
2141
|
let previousFindingCount = -1;
|
|
1727
2142
|
const runCheck = async () => {
|
|
1728
|
-
const spinner =
|
|
2143
|
+
const spinner = ora11({ text: `Checking drift for "${codeName}"\u2026`, isSilent: useJson });
|
|
1729
2144
|
spinner.start();
|
|
1730
2145
|
try {
|
|
1731
2146
|
const driftData = await api.get(`/agents/${encodeURIComponent(codeName)}/drift-data`);
|
|
@@ -1789,19 +2204,19 @@ Watching drift for "${codeName}" every ${intervalSec}s. Press Ctrl+C to stop.
|
|
|
1789
2204
|
}, intervalSec * 1e3);
|
|
1790
2205
|
process.on("SIGINT", () => {
|
|
1791
2206
|
clearInterval(timer);
|
|
1792
|
-
if (!useJson) console.log(
|
|
2207
|
+
if (!useJson) console.log(chalk11.dim("\nStopped watching."));
|
|
1793
2208
|
process.exit(0);
|
|
1794
2209
|
});
|
|
1795
2210
|
}
|
|
1796
2211
|
|
|
1797
2212
|
// src/commands/host.ts
|
|
1798
|
-
import
|
|
1799
|
-
import
|
|
2213
|
+
import chalk12 from "chalk";
|
|
2214
|
+
import ora12 from "ora";
|
|
1800
2215
|
async function hostListCommand() {
|
|
1801
2216
|
const teamSlug = requireTeam();
|
|
1802
2217
|
if (!teamSlug) return;
|
|
1803
2218
|
const json = isJsonMode();
|
|
1804
|
-
const spinner =
|
|
2219
|
+
const spinner = ora12({ text: "Fetching hosts\u2026", isSilent: json });
|
|
1805
2220
|
spinner.start();
|
|
1806
2221
|
try {
|
|
1807
2222
|
const data = await api.get("/hosts");
|
|
@@ -1819,10 +2234,10 @@ async function hostListCommand() {
|
|
|
1819
2234
|
return;
|
|
1820
2235
|
}
|
|
1821
2236
|
const rows = data.hosts.map((h) => {
|
|
1822
|
-
const status = h.status === "active" ?
|
|
2237
|
+
const status = h.status === "active" ? chalk12.green("active") : chalk12.red("decommissioned");
|
|
1823
2238
|
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` :
|
|
2239
|
+
const lastSeen = h.last_seen_at ? new Date(h.last_seen_at).toLocaleDateString() : chalk12.dim("never");
|
|
2240
|
+
const prefix = h.key_prefix ? `tlk_${h.key_prefix}\u2026` : chalk12.dim("none");
|
|
1826
2241
|
return [h.name, status, agents, lastSeen, prefix];
|
|
1827
2242
|
});
|
|
1828
2243
|
table(["Name", "Status", "Agents", "Last Seen", "Key"], rows);
|
|
@@ -1849,14 +2264,14 @@ async function hostAssignCommand(hostName, agentCodeNames, opts) {
|
|
|
1849
2264
|
process.exitCode = 1;
|
|
1850
2265
|
return;
|
|
1851
2266
|
}
|
|
1852
|
-
const spinner =
|
|
2267
|
+
const spinner = ora12({ text: "Assigning agents\u2026", isSilent: json });
|
|
1853
2268
|
spinner.start();
|
|
1854
2269
|
try {
|
|
1855
2270
|
await api.post(`/hosts/${encodeURIComponent(hostName)}/assign`, {
|
|
1856
2271
|
agents: agentCodeNames,
|
|
1857
2272
|
force: opts.force
|
|
1858
2273
|
});
|
|
1859
|
-
spinner.succeed(`Agents assigned to ${
|
|
2274
|
+
spinner.succeed(`Agents assigned to ${chalk12.bold(hostName)}.`);
|
|
1860
2275
|
if (json) {
|
|
1861
2276
|
jsonOutput({ ok: true, host: hostName, assigned: agentCodeNames });
|
|
1862
2277
|
return;
|
|
@@ -1887,13 +2302,13 @@ async function hostUnassignCommand(hostName, agentCodeNames) {
|
|
|
1887
2302
|
process.exitCode = 1;
|
|
1888
2303
|
return;
|
|
1889
2304
|
}
|
|
1890
|
-
const spinner =
|
|
2305
|
+
const spinner = ora12({ text: "Unassigning agents\u2026", isSilent: json });
|
|
1891
2306
|
spinner.start();
|
|
1892
2307
|
try {
|
|
1893
2308
|
await api.post(`/hosts/${encodeURIComponent(hostName)}/unassign`, {
|
|
1894
2309
|
agents: agentCodeNames
|
|
1895
2310
|
});
|
|
1896
|
-
spinner.succeed(`Agents unassigned from ${
|
|
2311
|
+
spinner.succeed(`Agents unassigned from ${chalk12.bold(hostName)}.`);
|
|
1897
2312
|
if (json) {
|
|
1898
2313
|
jsonOutput({ ok: true, host: hostName, unassigned: agentCodeNames });
|
|
1899
2314
|
return;
|
|
@@ -1913,7 +2328,7 @@ async function hostUnassignCommand(hostName, agentCodeNames) {
|
|
|
1913
2328
|
}
|
|
1914
2329
|
async function hostAgentsCommand(hostName) {
|
|
1915
2330
|
const json = isJsonMode();
|
|
1916
|
-
const spinner =
|
|
2331
|
+
const spinner = ora12({ text: "Fetching host agents\u2026", isSilent: json });
|
|
1917
2332
|
spinner.start();
|
|
1918
2333
|
try {
|
|
1919
2334
|
const hostId = await getHostId();
|
|
@@ -1952,7 +2367,7 @@ async function hostAgentsCommand(hostName) {
|
|
|
1952
2367
|
return;
|
|
1953
2368
|
}
|
|
1954
2369
|
const rows = agents.map((a) => {
|
|
1955
|
-
const statusColor = a.status === "active" ?
|
|
2370
|
+
const statusColor = a.status === "active" ? chalk12.green : chalk12.yellow;
|
|
1956
2371
|
return [
|
|
1957
2372
|
a.code_name,
|
|
1958
2373
|
a.display_name,
|
|
@@ -1975,11 +2390,11 @@ async function hostRotateKeyCommand(hostName) {
|
|
|
1975
2390
|
const teamSlug = requireTeam();
|
|
1976
2391
|
if (!teamSlug) return;
|
|
1977
2392
|
const json = isJsonMode();
|
|
1978
|
-
const spinner =
|
|
2393
|
+
const spinner = ora12({ text: "Rotating host key\u2026", isSilent: json });
|
|
1979
2394
|
spinner.start();
|
|
1980
2395
|
try {
|
|
1981
2396
|
const data = await api.post(`/hosts/${encodeURIComponent(hostName)}/rotate-key`);
|
|
1982
|
-
spinner.succeed(`Key rotated for ${
|
|
2397
|
+
spinner.succeed(`Key rotated for ${chalk12.bold(hostName)}.`);
|
|
1983
2398
|
if (json) {
|
|
1984
2399
|
jsonOutput({
|
|
1985
2400
|
ok: true,
|
|
@@ -1989,12 +2404,12 @@ async function hostRotateKeyCommand(hostName) {
|
|
|
1989
2404
|
return;
|
|
1990
2405
|
}
|
|
1991
2406
|
console.log();
|
|
1992
|
-
info(`Host: ${
|
|
2407
|
+
info(`Host: ${chalk12.bold(hostName)}`);
|
|
1993
2408
|
info(`Prefix: ${data.key.prefix}`);
|
|
1994
2409
|
console.log();
|
|
1995
|
-
console.log(
|
|
2410
|
+
console.log(chalk12.yellow.bold(" Save this key now \u2014 it will not be shown again:"));
|
|
1996
2411
|
console.log();
|
|
1997
|
-
console.log(` ${
|
|
2412
|
+
console.log(` ${chalk12.green.bold(data.key.raw_key)}`);
|
|
1998
2413
|
console.log();
|
|
1999
2414
|
} catch (err) {
|
|
2000
2415
|
spinner.fail("Failed to rotate key.");
|
|
@@ -2010,11 +2425,11 @@ async function hostDecommissionCommand(hostName) {
|
|
|
2010
2425
|
const teamSlug = requireTeam();
|
|
2011
2426
|
if (!teamSlug) return;
|
|
2012
2427
|
const json = isJsonMode();
|
|
2013
|
-
const spinner =
|
|
2428
|
+
const spinner = ora12({ text: "Decommissioning host\u2026", isSilent: json });
|
|
2014
2429
|
spinner.start();
|
|
2015
2430
|
try {
|
|
2016
2431
|
await api.post(`/hosts/${encodeURIComponent(hostName)}/decommission`);
|
|
2017
|
-
spinner.succeed(`Host "${
|
|
2432
|
+
spinner.succeed(`Host "${chalk12.bold(hostName)}" decommissioned.`);
|
|
2018
2433
|
if (json) {
|
|
2019
2434
|
jsonOutput({
|
|
2020
2435
|
ok: true,
|
|
@@ -2024,7 +2439,7 @@ async function hostDecommissionCommand(hostName) {
|
|
|
2024
2439
|
return;
|
|
2025
2440
|
}
|
|
2026
2441
|
info(`Host: ${hostName}`);
|
|
2027
|
-
info(`Status: ${
|
|
2442
|
+
info(`Status: ${chalk12.red("decommissioned")}`);
|
|
2028
2443
|
info("API key revoked. Agents remain assigned for audit visibility.");
|
|
2029
2444
|
info("Reassign agents to another host with `agt host assign --force`.");
|
|
2030
2445
|
} catch (err) {
|
|
@@ -2040,7 +2455,7 @@ async function hostDecommissionCommand(hostName) {
|
|
|
2040
2455
|
|
|
2041
2456
|
// src/commands/host-pair.ts
|
|
2042
2457
|
import { spawn, spawnSync } from "child_process";
|
|
2043
|
-
import
|
|
2458
|
+
import chalk13 from "chalk";
|
|
2044
2459
|
async function hostPairCommand(name, opts) {
|
|
2045
2460
|
const teamSlug = requireTeam();
|
|
2046
2461
|
if (!teamSlug) return;
|
|
@@ -2110,15 +2525,15 @@ async function hostPairCommand(name, opts) {
|
|
|
2110
2525
|
});
|
|
2111
2526
|
return;
|
|
2112
2527
|
}
|
|
2113
|
-
info(`Pairing Claude Code on ${
|
|
2114
|
-
info(`Local port ${
|
|
2528
|
+
info(`Pairing Claude Code on ${chalk13.bold(name)} (${instanceId}, ${region})`);
|
|
2529
|
+
info(`Local port ${chalk13.cyan(String(localPort))} will be forwarded to the host.`);
|
|
2115
2530
|
console.log();
|
|
2116
|
-
console.log(
|
|
2117
|
-
console.log(` ${
|
|
2118
|
-
console.log(
|
|
2119
|
-
console.log(
|
|
2531
|
+
console.log(chalk13.dim("In the shell that opens, run:"));
|
|
2532
|
+
console.log(` ${chalk13.cyan(`CLAUDE_CODE_OAUTH_PORT=${localPort} claude /login`)}`);
|
|
2533
|
+
console.log(chalk13.dim("Then click the printed URL on this machine. The OAuth callback"));
|
|
2534
|
+
console.log(chalk13.dim(`will reach Claude Code via the port-forward tunnel.`));
|
|
2120
2535
|
console.log();
|
|
2121
|
-
console.log(
|
|
2536
|
+
console.log(chalk13.dim("Exit the shell (Ctrl+D) when Claude Code reports successful login."));
|
|
2122
2537
|
console.log();
|
|
2123
2538
|
const tunnel = spawn(
|
|
2124
2539
|
"aws",
|
|
@@ -2139,7 +2554,7 @@ async function hostPairCommand(name, opts) {
|
|
|
2139
2554
|
tunnel.stderr?.on("data", (buf) => {
|
|
2140
2555
|
const line = buf.toString().trim();
|
|
2141
2556
|
if (line.startsWith("An error occurred")) {
|
|
2142
|
-
console.error(
|
|
2557
|
+
console.error(chalk13.red(`[tunnel] ${line}`));
|
|
2143
2558
|
}
|
|
2144
2559
|
});
|
|
2145
2560
|
await new Promise((r) => setTimeout(r, 1500));
|
|
@@ -2149,7 +2564,7 @@ async function hostPairCommand(name, opts) {
|
|
|
2149
2564
|
return;
|
|
2150
2565
|
}
|
|
2151
2566
|
if (opts.noShell) {
|
|
2152
|
-
console.log(
|
|
2567
|
+
console.log(chalk13.dim("--no-shell set; tunnel is running. Press Ctrl+C to exit."));
|
|
2153
2568
|
await new Promise((resolve2) => {
|
|
2154
2569
|
const onSignal = () => {
|
|
2155
2570
|
terminate(tunnel);
|
|
@@ -2193,15 +2608,15 @@ function terminate(child) {
|
|
|
2193
2608
|
// src/commands/manager-watch.tsx
|
|
2194
2609
|
import { useEffect, useState, useMemo } from "react";
|
|
2195
2610
|
import { render, Box, Text, useApp, useInput } from "ink";
|
|
2196
|
-
import { existsSync as
|
|
2197
|
-
import { homedir } from "os";
|
|
2198
|
-
import { join as
|
|
2611
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3, statSync, openSync, readSync, closeSync } from "fs";
|
|
2612
|
+
import { homedir as homedir3 } from "os";
|
|
2613
|
+
import { join as join9 } from "path";
|
|
2199
2614
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2200
2615
|
var REFRESH_MS = 1e3;
|
|
2201
2616
|
var LOG_TAIL_LINES = 200;
|
|
2202
2617
|
var DETAIL_RECENT_LINES = 50;
|
|
2203
2618
|
function managerWatchCommand(opts = {}) {
|
|
2204
|
-
const configDir = opts.configDir ??
|
|
2619
|
+
const configDir = opts.configDir ?? join9(homedir3(), ".augmented");
|
|
2205
2620
|
const paths = getManagerPaths(configDir);
|
|
2206
2621
|
const isTty = process.stdout.isTTY === true && process.stdin.isTTY === true;
|
|
2207
2622
|
if (opts.noTui || !isTty) {
|
|
@@ -2225,15 +2640,15 @@ function managerWatchCommand(opts = {}) {
|
|
|
2225
2640
|
}
|
|
2226
2641
|
function readState(stateFile) {
|
|
2227
2642
|
try {
|
|
2228
|
-
if (!
|
|
2229
|
-
const raw =
|
|
2643
|
+
if (!existsSync4(stateFile)) return null;
|
|
2644
|
+
const raw = readFileSync3(stateFile, "utf-8");
|
|
2230
2645
|
return JSON.parse(raw);
|
|
2231
2646
|
} catch {
|
|
2232
2647
|
return null;
|
|
2233
2648
|
}
|
|
2234
2649
|
}
|
|
2235
2650
|
function tailLogFile(logFile, lines) {
|
|
2236
|
-
if (!
|
|
2651
|
+
if (!existsSync4(logFile)) return [];
|
|
2237
2652
|
try {
|
|
2238
2653
|
const fileSize = statSync(logFile).size;
|
|
2239
2654
|
if (fileSize === 0) return [];
|
|
@@ -2474,7 +2889,7 @@ function truncate(s, max) {
|
|
|
2474
2889
|
return s.length > max ? `${s.slice(0, Math.max(0, max - 1))}\u2026` : s;
|
|
2475
2890
|
}
|
|
2476
2891
|
function streamLogFile(logFile) {
|
|
2477
|
-
if (!
|
|
2892
|
+
if (!existsSync4(logFile)) {
|
|
2478
2893
|
process.stderr.write(`No manager log found at ${logFile}.
|
|
2479
2894
|
Start the manager first: agt manager start
|
|
2480
2895
|
`);
|
|
@@ -2483,7 +2898,7 @@ Start the manager first: agt manager start
|
|
|
2483
2898
|
}
|
|
2484
2899
|
let lastSize = 0;
|
|
2485
2900
|
try {
|
|
2486
|
-
const initial =
|
|
2901
|
+
const initial = readFileSync3(logFile, "utf-8");
|
|
2487
2902
|
process.stdout.write(initial);
|
|
2488
2903
|
lastSize = statSync(logFile).size;
|
|
2489
2904
|
} catch (err) {
|
|
@@ -2518,16 +2933,16 @@ Start the manager first: agt manager start
|
|
|
2518
2933
|
}
|
|
2519
2934
|
|
|
2520
2935
|
// src/commands/agent.ts
|
|
2521
|
-
import
|
|
2936
|
+
import chalk14 from "chalk";
|
|
2522
2937
|
import JSON52 from "json5";
|
|
2523
|
-
import { readFileSync as
|
|
2524
|
-
import { join as
|
|
2938
|
+
import { readFileSync as readFileSync4, existsSync as existsSync5 } from "fs";
|
|
2939
|
+
import { join as join10 } from "path";
|
|
2525
2940
|
async function agentShowCommand(codeName, opts) {
|
|
2526
2941
|
const json = isJsonMode();
|
|
2527
|
-
const unifiedDir =
|
|
2528
|
-
const legacyDir =
|
|
2529
|
-
const agentDir =
|
|
2530
|
-
const hasLocalConfig =
|
|
2942
|
+
const unifiedDir = join10(opts.configDir, codeName, "provision");
|
|
2943
|
+
const legacyDir = join10(opts.configDir, codeName, "claudecode", "provision");
|
|
2944
|
+
const agentDir = existsSync5(unifiedDir) ? unifiedDir : legacyDir;
|
|
2945
|
+
const hasLocalConfig = existsSync5(agentDir);
|
|
2531
2946
|
let apiChannels = null;
|
|
2532
2947
|
let apiAgent = null;
|
|
2533
2948
|
if (getApiKey()) {
|
|
@@ -2560,34 +2975,34 @@ async function agentShowCommand(codeName, opts) {
|
|
|
2560
2975
|
let openclawConfig = null;
|
|
2561
2976
|
let agentState = null;
|
|
2562
2977
|
if (hasLocalConfig) {
|
|
2563
|
-
const charterPath =
|
|
2564
|
-
if (
|
|
2565
|
-
const raw =
|
|
2978
|
+
const charterPath = join10(agentDir, "CHARTER.md");
|
|
2979
|
+
if (existsSync5(charterPath)) {
|
|
2980
|
+
const raw = readFileSync4(charterPath, "utf-8");
|
|
2566
2981
|
const parsed = extractFrontmatter(raw);
|
|
2567
2982
|
if (parsed.frontmatter) {
|
|
2568
2983
|
charter = parsed.frontmatter;
|
|
2569
2984
|
}
|
|
2570
2985
|
}
|
|
2571
|
-
const toolsPath =
|
|
2572
|
-
if (
|
|
2573
|
-
const raw =
|
|
2986
|
+
const toolsPath = join10(agentDir, "TOOLS.md");
|
|
2987
|
+
if (existsSync5(toolsPath)) {
|
|
2988
|
+
const raw = readFileSync4(toolsPath, "utf-8");
|
|
2574
2989
|
const parsed = extractFrontmatter(raw);
|
|
2575
2990
|
if (parsed.frontmatter) {
|
|
2576
2991
|
tools = parsed.frontmatter;
|
|
2577
2992
|
}
|
|
2578
2993
|
}
|
|
2579
|
-
const openclawPath =
|
|
2580
|
-
if (
|
|
2994
|
+
const openclawPath = join10(agentDir, "openclaw.json5");
|
|
2995
|
+
if (existsSync5(openclawPath)) {
|
|
2581
2996
|
try {
|
|
2582
|
-
const raw =
|
|
2997
|
+
const raw = readFileSync4(openclawPath, "utf-8");
|
|
2583
2998
|
openclawConfig = JSON52.parse(raw);
|
|
2584
2999
|
} catch {
|
|
2585
3000
|
}
|
|
2586
3001
|
}
|
|
2587
|
-
const statePath =
|
|
2588
|
-
if (
|
|
3002
|
+
const statePath = join10(opts.configDir, "manager-state.json");
|
|
3003
|
+
if (existsSync5(statePath)) {
|
|
2589
3004
|
try {
|
|
2590
|
-
const raw =
|
|
3005
|
+
const raw = readFileSync4(statePath, "utf-8");
|
|
2591
3006
|
const state = JSON.parse(raw);
|
|
2592
3007
|
agentState = state.agents?.find((a) => a.codeName === codeName) ?? null;
|
|
2593
3008
|
} catch {
|
|
@@ -2654,7 +3069,7 @@ async function agentShowCommand(codeName, opts) {
|
|
|
2654
3069
|
return;
|
|
2655
3070
|
}
|
|
2656
3071
|
const displayName = apiAgent?.display_name ?? charter?.display_name ?? codeName;
|
|
2657
|
-
console.log(
|
|
3072
|
+
console.log(chalk14.bold(`
|
|
2658
3073
|
Agent: ${codeName}`) + (displayName !== codeName ? ` (${displayName})` : "") + "\n");
|
|
2659
3074
|
if (charter || apiAgent) {
|
|
2660
3075
|
const agentId = apiAgent?.agent_id ?? charter?.agent_id;
|
|
@@ -2686,7 +3101,7 @@ Agent: ${codeName}`) + (displayName !== codeName ? ` (${displayName})` : "") + "
|
|
|
2686
3101
|
info("No agent metadata available");
|
|
2687
3102
|
}
|
|
2688
3103
|
const filtered = opts.allChannels ? channelRows : channelRows.filter((c) => c.enabled);
|
|
2689
|
-
console.log(
|
|
3104
|
+
console.log(chalk14.bold("\nChannels:"));
|
|
2690
3105
|
if (filtered.length === 0) {
|
|
2691
3106
|
info("(none enabled)");
|
|
2692
3107
|
} else {
|
|
@@ -2702,7 +3117,7 @@ Agent: ${codeName}`) + (displayName !== codeName ? ` (${displayName})` : "") + "
|
|
|
2702
3117
|
);
|
|
2703
3118
|
}
|
|
2704
3119
|
if (tools) {
|
|
2705
|
-
console.log(
|
|
3120
|
+
console.log(chalk14.bold("\nTools:"));
|
|
2706
3121
|
if (tools.tools.length === 0) {
|
|
2707
3122
|
info("(none configured)");
|
|
2708
3123
|
} else {
|
|
@@ -2713,7 +3128,7 @@ Agent: ${codeName}`) + (displayName !== codeName ? ` (${displayName})` : "") + "
|
|
|
2713
3128
|
}
|
|
2714
3129
|
if (tools.global_controls) {
|
|
2715
3130
|
const gc = tools.global_controls;
|
|
2716
|
-
console.log(
|
|
3131
|
+
console.log(chalk14.bold("\nGlobal Controls:"));
|
|
2717
3132
|
info(`Network: ${gc.default_network_policy === "deny" ? "deny-by-default" : "allow-by-default"}`);
|
|
2718
3133
|
info(`Timeout: ${gc.default_timeout_ms}ms`);
|
|
2719
3134
|
info(`Rate: ${gc.default_rate_limit_rpm} rpm`);
|
|
@@ -2729,8 +3144,8 @@ function formatTimestamp(iso) {
|
|
|
2729
3144
|
}
|
|
2730
3145
|
|
|
2731
3146
|
// src/commands/kanban-recurring.ts
|
|
2732
|
-
import
|
|
2733
|
-
import
|
|
3147
|
+
import chalk15 from "chalk";
|
|
3148
|
+
import ora13 from "ora";
|
|
2734
3149
|
async function resolveAgentId(codeName) {
|
|
2735
3150
|
try {
|
|
2736
3151
|
const data = await api.get(`/agents/${codeName}`);
|
|
@@ -2741,10 +3156,10 @@ async function resolveAgentId(codeName) {
|
|
|
2741
3156
|
}
|
|
2742
3157
|
}
|
|
2743
3158
|
function priorityLabel(p) {
|
|
2744
|
-
return p === 1 ?
|
|
3159
|
+
return p === 1 ? chalk15.red("HIGH") : p === 3 ? chalk15.dim("LOW") : chalk15.yellow("MED");
|
|
2745
3160
|
}
|
|
2746
3161
|
function formatSpawnTime(isoDate, timezone) {
|
|
2747
|
-
if (!isoDate) return
|
|
3162
|
+
if (!isoDate) return chalk15.dim("\u2014");
|
|
2748
3163
|
try {
|
|
2749
3164
|
return new Intl.DateTimeFormat("en-AU", {
|
|
2750
3165
|
dateStyle: "medium",
|
|
@@ -2785,7 +3200,7 @@ async function kanbanRecurringAddCommand(title, opts) {
|
|
|
2785
3200
|
return;
|
|
2786
3201
|
}
|
|
2787
3202
|
}
|
|
2788
|
-
const spinner =
|
|
3203
|
+
const spinner = ora13({ text: "Creating recurring template\u2026", isSilent: json });
|
|
2789
3204
|
spinner.start();
|
|
2790
3205
|
const agentId = await resolveAgentId(opts.agent);
|
|
2791
3206
|
if (!agentId) {
|
|
@@ -2809,8 +3224,8 @@ async function kanbanRecurringAddCommand(title, opts) {
|
|
|
2809
3224
|
jsonOutput({ ok: true, template: data.template });
|
|
2810
3225
|
return;
|
|
2811
3226
|
}
|
|
2812
|
-
success(`Recurring task created: ${
|
|
2813
|
-
info(`Schedule: ${
|
|
3227
|
+
success(`Recurring task created: ${chalk15.bold(title)}`);
|
|
3228
|
+
info(`Schedule: ${chalk15.cyan(data.template.natural_language ?? data.template.expression ?? data.template.every_interval ?? "")}`);
|
|
2814
3229
|
info(`Next spawn: ${formatSpawnTime(data.template.next_spawn_at, data.template.timezone)}`);
|
|
2815
3230
|
info(`Timezone: ${data.template.timezone}`);
|
|
2816
3231
|
} catch (err) {
|
|
@@ -2827,7 +3242,7 @@ async function kanbanRecurringListCommand(opts) {
|
|
|
2827
3242
|
const teamSlug = requireTeam();
|
|
2828
3243
|
if (!teamSlug) return;
|
|
2829
3244
|
const json = isJsonMode();
|
|
2830
|
-
const spinner =
|
|
3245
|
+
const spinner = ora13({ text: "Fetching recurring templates\u2026", isSilent: json });
|
|
2831
3246
|
spinner.start();
|
|
2832
3247
|
const agentId = await resolveAgentId(opts.agent);
|
|
2833
3248
|
if (!agentId) {
|
|
@@ -2853,7 +3268,7 @@ async function kanbanRecurringListCommand(opts) {
|
|
|
2853
3268
|
t.title,
|
|
2854
3269
|
t.natural_language ?? t.expression ?? t.every_interval ?? "\u2014",
|
|
2855
3270
|
priorityLabel(t.priority),
|
|
2856
|
-
t.enabled ?
|
|
3271
|
+
t.enabled ? chalk15.green("Active") : chalk15.dim("Disabled"),
|
|
2857
3272
|
formatSpawnTime(t.next_spawn_at, t.timezone),
|
|
2858
3273
|
String(t.spawn_count)
|
|
2859
3274
|
]);
|
|
@@ -2875,7 +3290,7 @@ async function kanbanRecurringDisableCommand(titleOrId, opts) {
|
|
|
2875
3290
|
const teamSlug = requireTeam();
|
|
2876
3291
|
if (!teamSlug) return;
|
|
2877
3292
|
const json = isJsonMode();
|
|
2878
|
-
const spinner =
|
|
3293
|
+
const spinner = ora13({ text: "Disabling recurring template\u2026", isSilent: json });
|
|
2879
3294
|
spinner.start();
|
|
2880
3295
|
const agentId = await resolveAgentId(opts.agent);
|
|
2881
3296
|
if (!agentId) {
|
|
@@ -2910,7 +3325,7 @@ async function kanbanRecurringDisableCommand(titleOrId, opts) {
|
|
|
2910
3325
|
if (json) {
|
|
2911
3326
|
jsonOutput({ ok: true, id: match.id, title: match.title, enabled: false });
|
|
2912
3327
|
} else {
|
|
2913
|
-
success(`Disabled: ${
|
|
3328
|
+
success(`Disabled: ${chalk15.bold(match.title)}`);
|
|
2914
3329
|
}
|
|
2915
3330
|
} catch (err) {
|
|
2916
3331
|
spinner.fail("Failed to disable recurring template");
|
|
@@ -2924,24 +3339,24 @@ async function kanbanRecurringDisableCommand(titleOrId, opts) {
|
|
|
2924
3339
|
}
|
|
2925
3340
|
|
|
2926
3341
|
// src/commands/setup.ts
|
|
2927
|
-
import { existsSync as
|
|
2928
|
-
import { join as
|
|
2929
|
-
import { homedir as
|
|
2930
|
-
import
|
|
2931
|
-
import
|
|
3342
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, accessSync, constants as fsConstants } from "fs";
|
|
3343
|
+
import { join as join11, dirname } from "path";
|
|
3344
|
+
import { homedir as homedir4 } from "os";
|
|
3345
|
+
import chalk16 from "chalk";
|
|
3346
|
+
import ora14 from "ora";
|
|
2932
3347
|
function detectShellProfile() {
|
|
2933
3348
|
const shell = process.env["SHELL"] ?? "";
|
|
2934
|
-
const home =
|
|
3349
|
+
const home = homedir4();
|
|
2935
3350
|
if (shell.includes("zsh")) {
|
|
2936
|
-
return
|
|
3351
|
+
return join11(home, ".zshrc");
|
|
2937
3352
|
}
|
|
2938
3353
|
if (shell.includes("fish")) {
|
|
2939
|
-
const fishConfig =
|
|
3354
|
+
const fishConfig = join11(home, ".config", "fish", "config.fish");
|
|
2940
3355
|
return fishConfig;
|
|
2941
3356
|
}
|
|
2942
|
-
const bashrc =
|
|
2943
|
-
if (
|
|
2944
|
-
return
|
|
3357
|
+
const bashrc = join11(home, ".bashrc");
|
|
3358
|
+
if (existsSync6(bashrc)) return bashrc;
|
|
3359
|
+
return join11(home, ".bash_profile");
|
|
2945
3360
|
}
|
|
2946
3361
|
function maybeWriteSystemWideEnv(apiUrl, apiKey, consoleUrl) {
|
|
2947
3362
|
const empty = { etcEnvironment: false, profileD: false, bashrc: false };
|
|
@@ -2967,14 +3382,14 @@ function quoteForFishShell(value) {
|
|
|
2967
3382
|
}
|
|
2968
3383
|
function writeEtcEnvironment(apiUrl, apiKey, consoleUrl) {
|
|
2969
3384
|
const envPath = "/etc/environment";
|
|
2970
|
-
if (!
|
|
3385
|
+
if (!existsSync6(envPath)) return false;
|
|
2971
3386
|
try {
|
|
2972
3387
|
accessSync(envPath, fsConstants.W_OK);
|
|
2973
3388
|
} catch {
|
|
2974
3389
|
return false;
|
|
2975
3390
|
}
|
|
2976
3391
|
try {
|
|
2977
|
-
const current =
|
|
3392
|
+
const current = readFileSync5(envPath, "utf-8");
|
|
2978
3393
|
const stripped = current.split(/\r?\n/).filter((line) => !/^\s*(?:export\s+)?AGT_(?:HOST|API_KEY|CONSOLE_URL)=/.test(line)).join("\n");
|
|
2979
3394
|
const base = stripped.endsWith("\n") || stripped.length === 0 ? stripped : `${stripped}
|
|
2980
3395
|
`;
|
|
@@ -2985,7 +3400,7 @@ function writeEtcEnvironment(apiUrl, apiKey, consoleUrl) {
|
|
|
2985
3400
|
if (consoleUrl) lines.push(`AGT_CONSOLE_URL=${quoteForEtcEnvironment(consoleUrl)}`);
|
|
2986
3401
|
const appended = `${base}${lines.join("\n")}
|
|
2987
3402
|
`;
|
|
2988
|
-
|
|
3403
|
+
writeFileSync6(envPath, appended, { mode: 420 });
|
|
2989
3404
|
return true;
|
|
2990
3405
|
} catch {
|
|
2991
3406
|
return false;
|
|
@@ -2993,7 +3408,7 @@ function writeEtcEnvironment(apiUrl, apiKey, consoleUrl) {
|
|
|
2993
3408
|
}
|
|
2994
3409
|
function writeProfileDScript(apiUrl, apiKey, consoleUrl) {
|
|
2995
3410
|
const profileD = "/etc/profile.d";
|
|
2996
|
-
if (!
|
|
3411
|
+
if (!existsSync6(profileD)) return false;
|
|
2997
3412
|
try {
|
|
2998
3413
|
accessSync(profileD, fsConstants.W_OK);
|
|
2999
3414
|
} catch {
|
|
@@ -3008,7 +3423,7 @@ function writeProfileDScript(apiUrl, apiKey, consoleUrl) {
|
|
|
3008
3423
|
];
|
|
3009
3424
|
if (consoleUrl) lines.push(`export AGT_CONSOLE_URL=${quoteForPosixShell(consoleUrl)}`);
|
|
3010
3425
|
lines.push("");
|
|
3011
|
-
|
|
3426
|
+
writeFileSync6(scriptPath, lines.join("\n"), { mode: 420 });
|
|
3012
3427
|
return true;
|
|
3013
3428
|
} catch {
|
|
3014
3429
|
return false;
|
|
@@ -3016,14 +3431,14 @@ function writeProfileDScript(apiUrl, apiKey, consoleUrl) {
|
|
|
3016
3431
|
}
|
|
3017
3432
|
function ensureBashrcSourcesProfileD() {
|
|
3018
3433
|
const bashrc = "/etc/bashrc";
|
|
3019
|
-
if (!
|
|
3434
|
+
if (!existsSync6(bashrc)) return false;
|
|
3020
3435
|
try {
|
|
3021
3436
|
accessSync(bashrc, fsConstants.W_OK);
|
|
3022
3437
|
} catch {
|
|
3023
3438
|
return false;
|
|
3024
3439
|
}
|
|
3025
3440
|
try {
|
|
3026
|
-
const current =
|
|
3441
|
+
const current = readFileSync5(bashrc, "utf-8");
|
|
3027
3442
|
const marker = "# Augmented (agt) \u2014 source system-wide AGT env";
|
|
3028
3443
|
if (current.includes(marker)) return true;
|
|
3029
3444
|
const snippet = [
|
|
@@ -3034,7 +3449,7 @@ function ensureBashrcSourcesProfileD() {
|
|
|
3034
3449
|
"fi",
|
|
3035
3450
|
""
|
|
3036
3451
|
].join("\n");
|
|
3037
|
-
|
|
3452
|
+
writeFileSync6(bashrc, current + snippet);
|
|
3038
3453
|
return true;
|
|
3039
3454
|
} catch {
|
|
3040
3455
|
return false;
|
|
@@ -3081,10 +3496,10 @@ async function setupCommand(token) {
|
|
|
3081
3496
|
if (!apiUrl) {
|
|
3082
3497
|
apiUrl = "https://api.augmented.team";
|
|
3083
3498
|
if (!json) {
|
|
3084
|
-
info(`No AGT_HOST set \u2014 using default: ${
|
|
3499
|
+
info(`No AGT_HOST set \u2014 using default: ${chalk16.bold(apiUrl)}`);
|
|
3085
3500
|
}
|
|
3086
3501
|
}
|
|
3087
|
-
const spinner =
|
|
3502
|
+
const spinner = ora14({ text: "Exchanging provisioning token\u2026", isSilent: json });
|
|
3088
3503
|
spinner.start();
|
|
3089
3504
|
let setupResult;
|
|
3090
3505
|
try {
|
|
@@ -3112,17 +3527,17 @@ async function setupCommand(token) {
|
|
|
3112
3527
|
const finalApiUrl = setupResult.api_url || apiUrl;
|
|
3113
3528
|
process.env["AGT_HOST"] = finalApiUrl;
|
|
3114
3529
|
process.env["AGT_API_KEY"] = setupResult.api_key;
|
|
3115
|
-
const verifySpinner =
|
|
3530
|
+
const verifySpinner = ora14({ text: "Verifying connection\u2026", isSilent: json });
|
|
3116
3531
|
verifySpinner.start();
|
|
3117
3532
|
try {
|
|
3118
3533
|
const exchange = await exchangeApiKey(setupResult.api_key);
|
|
3119
3534
|
verifySpinner.succeed("Connection verified");
|
|
3120
3535
|
if (!json) {
|
|
3121
|
-
info(`Host: ${
|
|
3536
|
+
info(`Host: ${chalk16.bold(setupResult.host_name)}`);
|
|
3122
3537
|
info(`Host ID: ${exchange.hostId}`);
|
|
3123
|
-
info(`Team: ${
|
|
3538
|
+
info(`Team: ${chalk16.bold(exchange.teamSlug ?? setupResult.team_slug ?? "unknown")}`);
|
|
3124
3539
|
if (exchange.userEmail) {
|
|
3125
|
-
info(`User: ${
|
|
3540
|
+
info(`User: ${chalk16.bold(exchange.userEmail)}`);
|
|
3126
3541
|
}
|
|
3127
3542
|
}
|
|
3128
3543
|
} catch (err) {
|
|
@@ -3150,11 +3565,11 @@ async function setupCommand(token) {
|
|
|
3150
3565
|
}
|
|
3151
3566
|
const exportLines = buildExportLines(shell, finalApiUrl, setupResult.api_key, consoleUrl);
|
|
3152
3567
|
const profileDir = dirname(profilePath);
|
|
3153
|
-
if (!
|
|
3154
|
-
|
|
3568
|
+
if (!existsSync6(profileDir)) {
|
|
3569
|
+
mkdirSync5(profileDir, { recursive: true });
|
|
3155
3570
|
}
|
|
3156
3571
|
const marker = "# Augmented (agt) host configuration";
|
|
3157
|
-
const current =
|
|
3572
|
+
const current = existsSync6(profilePath) ? readFileSync5(profilePath, "utf-8") : "";
|
|
3158
3573
|
let updated;
|
|
3159
3574
|
if (current.includes(marker)) {
|
|
3160
3575
|
updated = current.replace(
|
|
@@ -3167,10 +3582,10 @@ async function setupCommand(token) {
|
|
|
3167
3582
|
} else {
|
|
3168
3583
|
updated = current + exportLines;
|
|
3169
3584
|
}
|
|
3170
|
-
|
|
3585
|
+
writeFileSync6(profilePath, updated.endsWith("\n") ? updated : `${updated}
|
|
3171
3586
|
`);
|
|
3172
3587
|
if (!json) {
|
|
3173
|
-
success(`Environment variables written to ${
|
|
3588
|
+
success(`Environment variables written to ${chalk16.bold(profilePath)}`);
|
|
3174
3589
|
}
|
|
3175
3590
|
const sys = maybeWriteSystemWideEnv(finalApiUrl, setupResult.api_key, consoleUrl);
|
|
3176
3591
|
const valueChannelsWritten = sys.etcEnvironment || sys.profileD;
|
|
@@ -3182,10 +3597,10 @@ async function setupCommand(token) {
|
|
|
3182
3597
|
].filter(Boolean).join(" + ");
|
|
3183
3598
|
success(`System-wide env updated: ${channels2}`);
|
|
3184
3599
|
}
|
|
3185
|
-
const managerSpinner =
|
|
3600
|
+
const managerSpinner = ora14({ text: "Starting manager daemon\u2026", isSilent: json });
|
|
3186
3601
|
managerSpinner.start();
|
|
3187
3602
|
try {
|
|
3188
|
-
const configDir =
|
|
3603
|
+
const configDir = join11(homedir4(), ".augmented");
|
|
3189
3604
|
const { pid } = startWatchdog({ intervalMs: 1e4, configDir, detached: true });
|
|
3190
3605
|
managerSpinner.succeed(`Manager started (PID ${pid})`);
|
|
3191
3606
|
} catch (err) {
|
|
@@ -3210,21 +3625,21 @@ async function setupCommand(token) {
|
|
|
3210
3625
|
process.exit(0);
|
|
3211
3626
|
} else {
|
|
3212
3627
|
console.log();
|
|
3213
|
-
console.log(
|
|
3628
|
+
console.log(chalk16.green.bold(" Setup complete!"));
|
|
3214
3629
|
console.log();
|
|
3215
3630
|
if (setupResult.agents.length > 0) {
|
|
3216
|
-
info(`Agents: ${setupResult.agents.map((a) =>
|
|
3631
|
+
info(`Agents: ${setupResult.agents.map((a) => chalk16.cyan(a)).join(", ")}`);
|
|
3217
3632
|
} else {
|
|
3218
3633
|
info("No agents assigned yet. Assign agents in the dashboard or with: agt host assign");
|
|
3219
3634
|
}
|
|
3220
3635
|
console.log();
|
|
3221
|
-
info(`Restart your shell or run: ${
|
|
3636
|
+
info(`Restart your shell or run: ${chalk16.bold(`source ${profilePath}`)}`);
|
|
3222
3637
|
}
|
|
3223
3638
|
}
|
|
3224
3639
|
|
|
3225
3640
|
// src/commands/kanban.ts
|
|
3226
|
-
import
|
|
3227
|
-
import
|
|
3641
|
+
import chalk17 from "chalk";
|
|
3642
|
+
import ora15 from "ora";
|
|
3228
3643
|
async function resolveAgentId2(codeName) {
|
|
3229
3644
|
try {
|
|
3230
3645
|
const data = await api.get(`/agents/${codeName}`);
|
|
@@ -3234,14 +3649,14 @@ async function resolveAgentId2(codeName) {
|
|
|
3234
3649
|
}
|
|
3235
3650
|
}
|
|
3236
3651
|
function priorityLabel2(p) {
|
|
3237
|
-
return p === 1 ?
|
|
3652
|
+
return p === 1 ? chalk17.red("HIGH") : p === 3 ? chalk17.dim("LOW") : chalk17.yellow("MED");
|
|
3238
3653
|
}
|
|
3239
3654
|
function statusLabel(s) {
|
|
3240
3655
|
const map = {
|
|
3241
|
-
in_progress:
|
|
3242
|
-
todo:
|
|
3243
|
-
backlog:
|
|
3244
|
-
done:
|
|
3656
|
+
in_progress: chalk17.blue("In Progress"),
|
|
3657
|
+
todo: chalk17.green("To Do"),
|
|
3658
|
+
backlog: chalk17.dim("Backlog"),
|
|
3659
|
+
done: chalk17.gray("Done")
|
|
3245
3660
|
};
|
|
3246
3661
|
return map[s] ?? s;
|
|
3247
3662
|
}
|
|
@@ -3249,7 +3664,7 @@ async function kanbanListCommand(opts) {
|
|
|
3249
3664
|
const teamSlug = requireTeam();
|
|
3250
3665
|
if (!teamSlug) return;
|
|
3251
3666
|
const json = isJsonMode();
|
|
3252
|
-
const spinner =
|
|
3667
|
+
const spinner = ora15({ text: "Fetching kanban board\u2026", isSilent: json });
|
|
3253
3668
|
spinner.start();
|
|
3254
3669
|
const agentId = await resolveAgentId2(opts.agent);
|
|
3255
3670
|
if (!agentId) {
|
|
@@ -3277,7 +3692,7 @@ async function kanbanListCommand(opts) {
|
|
|
3277
3692
|
item.estimated_minutes ? `~${item.estimated_minutes}min` : "",
|
|
3278
3693
|
item.id.slice(0, 8)
|
|
3279
3694
|
]);
|
|
3280
|
-
console.log(
|
|
3695
|
+
console.log(chalk17.bold(`
|
|
3281
3696
|
Kanban: ${opts.agent}
|
|
3282
3697
|
`));
|
|
3283
3698
|
table(["Pri", "Status", "Title", "Est", "ID"], rows);
|
|
@@ -3733,10 +4148,10 @@ async function acpxCloseCommand(agent2, _opts, cmd) {
|
|
|
3733
4148
|
|
|
3734
4149
|
// src/commands/update.ts
|
|
3735
4150
|
import { execFileSync, execSync } from "child_process";
|
|
3736
|
-
import { existsSync as
|
|
3737
|
-
import
|
|
3738
|
-
import
|
|
3739
|
-
var cliVersion = true ? "0.26.2
|
|
4151
|
+
import { existsSync as existsSync7, realpathSync as realpathSync2 } from "fs";
|
|
4152
|
+
import chalk18 from "chalk";
|
|
4153
|
+
import ora16 from "ora";
|
|
4154
|
+
var cliVersion = true ? "0.26.2" : "dev";
|
|
3740
4155
|
async function fetchLatestVersion() {
|
|
3741
4156
|
const host2 = getHost();
|
|
3742
4157
|
if (!host2) return null;
|
|
@@ -3800,7 +4215,7 @@ function detectActiveSessions() {
|
|
|
3800
4215
|
}
|
|
3801
4216
|
function detectInstallSource() {
|
|
3802
4217
|
try {
|
|
3803
|
-
const resolved =
|
|
4218
|
+
const resolved = realpathSync2(process.argv[1] ?? "");
|
|
3804
4219
|
if (/\/Cellar\/[^/]+\//.test(resolved)) {
|
|
3805
4220
|
return "brew";
|
|
3806
4221
|
}
|
|
@@ -3840,7 +4255,7 @@ function performUpdate(version) {
|
|
|
3840
4255
|
function detectBrewOwner() {
|
|
3841
4256
|
for (const prefix of ["/home/linuxbrew/.linuxbrew", "/opt/homebrew", "/usr/local"]) {
|
|
3842
4257
|
const cellar = `${prefix}/Cellar`;
|
|
3843
|
-
if (!
|
|
4258
|
+
if (!existsSync7(cellar)) continue;
|
|
3844
4259
|
try {
|
|
3845
4260
|
return execSync(`stat -c %U "${cellar}" 2>/dev/null || stat -f %Su "${cellar}"`, { encoding: "utf-8" }).trim() || null;
|
|
3846
4261
|
} catch {
|
|
@@ -3850,7 +4265,7 @@ function detectBrewOwner() {
|
|
|
3850
4265
|
}
|
|
3851
4266
|
async function updateCommand(opts = {}) {
|
|
3852
4267
|
const json = isJsonMode();
|
|
3853
|
-
const spinner =
|
|
4268
|
+
const spinner = ora16({ text: "Checking for updates\u2026", isSilent: json });
|
|
3854
4269
|
spinner.start();
|
|
3855
4270
|
const versionInfo = await fetchLatestVersion();
|
|
3856
4271
|
if (!versionInfo) {
|
|
@@ -3869,13 +4284,13 @@ async function updateCommand(opts = {}) {
|
|
|
3869
4284
|
if (json) {
|
|
3870
4285
|
jsonOutput({ ok: true, current: cliVersion, latest: versionInfo.latest, update_available: false });
|
|
3871
4286
|
} else {
|
|
3872
|
-
success(`You're on the latest version (${
|
|
4287
|
+
success(`You're on the latest version (${chalk18.bold(cliVersion)})`);
|
|
3873
4288
|
}
|
|
3874
4289
|
return;
|
|
3875
4290
|
}
|
|
3876
4291
|
spinner.stop();
|
|
3877
4292
|
if (!json) {
|
|
3878
|
-
info(`Update available: ${
|
|
4293
|
+
info(`Update available: ${chalk18.dim(cliVersion)} \u2192 ${chalk18.bold.green(versionInfo.latest)}`);
|
|
3879
4294
|
if (versionInfo.changelog_url) {
|
|
3880
4295
|
info(`Changelog: ${versionInfo.changelog_url}`);
|
|
3881
4296
|
}
|
|
@@ -3886,16 +4301,16 @@ async function updateCommand(opts = {}) {
|
|
|
3886
4301
|
if (!json) {
|
|
3887
4302
|
warn("Active processes detected:");
|
|
3888
4303
|
if (managerPid) {
|
|
3889
|
-
console.error(
|
|
4304
|
+
console.error(chalk18.yellow(` \u2022 Manager process running (PID ${managerPid})`));
|
|
3890
4305
|
}
|
|
3891
4306
|
if (tmuxSessions.length > 0) {
|
|
3892
|
-
console.error(
|
|
4307
|
+
console.error(chalk18.yellow(` \u2022 ${tmuxSessions.length} active agent session(s): ${tmuxSessions.join(", ")}`));
|
|
3893
4308
|
}
|
|
3894
4309
|
console.error();
|
|
3895
4310
|
warn("Updating while the manager is running will leave it on the old version until restarted.");
|
|
3896
4311
|
warn("Active agent sessions will continue with stale MCP servers.");
|
|
3897
4312
|
console.error();
|
|
3898
|
-
info(`Run ${
|
|
4313
|
+
info(`Run ${chalk18.bold("agt update --force")} to update anyway, then restart the manager.`);
|
|
3899
4314
|
} else {
|
|
3900
4315
|
jsonOutput({
|
|
3901
4316
|
ok: false,
|
|
@@ -3913,7 +4328,7 @@ async function updateCommand(opts = {}) {
|
|
|
3913
4328
|
return;
|
|
3914
4329
|
}
|
|
3915
4330
|
}
|
|
3916
|
-
const updateSpinner =
|
|
4331
|
+
const updateSpinner = ora16({ text: "Installing update\u2026", isSilent: json });
|
|
3917
4332
|
updateSpinner.start();
|
|
3918
4333
|
try {
|
|
3919
4334
|
performUpdate(versionInfo.latest);
|
|
@@ -3927,8 +4342,8 @@ async function updateCommand(opts = {}) {
|
|
|
3927
4342
|
updated: true
|
|
3928
4343
|
});
|
|
3929
4344
|
} else {
|
|
3930
|
-
success(`Updated to ${
|
|
3931
|
-
info(`If you have a running manager, restart it: ${
|
|
4345
|
+
success(`Updated to ${chalk18.bold(versionInfo.latest)}`);
|
|
4346
|
+
info(`If you have a running manager, restart it: ${chalk18.bold("agt manager stop && agt manager start")}`);
|
|
3932
4347
|
}
|
|
3933
4348
|
} catch (err) {
|
|
3934
4349
|
updateSpinner.fail("Update failed");
|
|
@@ -3959,8 +4374,8 @@ async function checkForUpdateOnStartup() {
|
|
|
3959
4374
|
if (!versionInfo) return;
|
|
3960
4375
|
if (isNewerVersion(cliVersion, versionInfo.latest)) {
|
|
3961
4376
|
console.error(
|
|
3962
|
-
|
|
3963
|
-
Update available: ${cliVersion} \u2192 ${versionInfo.latest}. Run ${
|
|
4377
|
+
chalk18.yellow(`
|
|
4378
|
+
Update available: ${cliVersion} \u2192 ${versionInfo.latest}. Run ${chalk18.bold("agt update")} to install.`) + chalk18.dim("\n Note: Restart the manager after updating.\n")
|
|
3964
4379
|
);
|
|
3965
4380
|
}
|
|
3966
4381
|
} catch {
|
|
@@ -3968,11 +4383,11 @@ async function checkForUpdateOnStartup() {
|
|
|
3968
4383
|
}
|
|
3969
4384
|
|
|
3970
4385
|
// src/commands/integration.ts
|
|
3971
|
-
import
|
|
3972
|
-
import
|
|
4386
|
+
import chalk19 from "chalk";
|
|
4387
|
+
import ora17 from "ora";
|
|
3973
4388
|
async function integrationListCommand() {
|
|
3974
4389
|
const json = isJsonMode();
|
|
3975
|
-
const spinner =
|
|
4390
|
+
const spinner = ora17({ text: "Fetching integrations\u2026", isSilent: json }).start();
|
|
3976
4391
|
try {
|
|
3977
4392
|
const data = await api.get("/integrations/bindings");
|
|
3978
4393
|
spinner.stop();
|
|
@@ -3984,11 +4399,11 @@ async function integrationListCommand() {
|
|
|
3984
4399
|
info("No integrations found for this team.");
|
|
3985
4400
|
return;
|
|
3986
4401
|
}
|
|
3987
|
-
console.log(
|
|
4402
|
+
console.log(chalk19.bold("\nAvailable Integrations:\n"));
|
|
3988
4403
|
for (const p of data) {
|
|
3989
|
-
const statusColor = p.status === "published" ?
|
|
4404
|
+
const statusColor = p.status === "published" ? chalk19.green : p.status === "archived" ? chalk19.gray : chalk19.yellow;
|
|
3990
4405
|
console.log(
|
|
3991
|
-
` ${
|
|
4406
|
+
` ${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
4407
|
);
|
|
3993
4408
|
}
|
|
3994
4409
|
console.log();
|
|
@@ -3999,7 +4414,7 @@ async function integrationListCommand() {
|
|
|
3999
4414
|
}
|
|
4000
4415
|
async function integrationShowCommand(slug) {
|
|
4001
4416
|
const json = isJsonMode();
|
|
4002
|
-
const spinner =
|
|
4417
|
+
const spinner = ora17({ text: "Fetching integration\u2026", isSilent: json }).start();
|
|
4003
4418
|
try {
|
|
4004
4419
|
const all = await api.get("/integrations/bindings");
|
|
4005
4420
|
const integration2 = all.find((p) => p.slug === slug);
|
|
@@ -4015,21 +4430,21 @@ async function integrationShowCommand(slug) {
|
|
|
4015
4430
|
jsonOutput({ integration: integration2, scopes });
|
|
4016
4431
|
return;
|
|
4017
4432
|
}
|
|
4018
|
-
console.log(
|
|
4019
|
-
${integration2.name}`),
|
|
4020
|
-
if (integration2.description) console.log(
|
|
4433
|
+
console.log(chalk19.bold(`
|
|
4434
|
+
${integration2.name}`), chalk19.dim(`(${integration2.slug})`));
|
|
4435
|
+
if (integration2.description) console.log(chalk19.dim(integration2.description));
|
|
4021
4436
|
console.log();
|
|
4022
4437
|
console.log(` Status: ${integration2.status} v${integration2.version}`);
|
|
4023
4438
|
console.log(` Category: ${integration2.category}`);
|
|
4024
4439
|
console.log(` Toolkits: ${integration2.required_toolkits.join(", ") || "none"}`);
|
|
4025
4440
|
console.log(` Skills: ${integration2.skills?.length ?? 0}`);
|
|
4026
4441
|
if (scopes.length > 0) {
|
|
4027
|
-
console.log(
|
|
4442
|
+
console.log(chalk19.bold("\n Permission Scopes:\n"));
|
|
4028
4443
|
for (const s of scopes) {
|
|
4029
|
-
const roleColor = s.can_grant ?
|
|
4030
|
-
const overrideTag = s.is_overridden ?
|
|
4444
|
+
const roleColor = s.can_grant ? chalk19.green : chalk19.red;
|
|
4445
|
+
const overrideTag = s.is_overridden ? chalk19.cyan(" [team override]") : "";
|
|
4031
4446
|
console.log(
|
|
4032
|
-
` ${
|
|
4447
|
+
` ${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
4448
|
);
|
|
4034
4449
|
}
|
|
4035
4450
|
}
|
|
@@ -4041,7 +4456,7 @@ ${integration2.name}`), chalk18.dim(`(${integration2.slug})`));
|
|
|
4041
4456
|
}
|
|
4042
4457
|
async function integrationInstallCommand(slug, options) {
|
|
4043
4458
|
const json = isJsonMode();
|
|
4044
|
-
const spinner =
|
|
4459
|
+
const spinner = ora17({ text: "Installing integration\u2026", isSilent: json }).start();
|
|
4045
4460
|
try {
|
|
4046
4461
|
const all = await api.get("/integrations/bindings");
|
|
4047
4462
|
const integration2 = all.find((p) => p.slug === slug);
|
|
@@ -4080,7 +4495,7 @@ async function integrationInstallCommand(slug, options) {
|
|
|
4080
4495
|
const needsApproval = err.body["needs_approval"];
|
|
4081
4496
|
error("Some scopes exceed your role ceiling:");
|
|
4082
4497
|
for (const s of needsApproval) {
|
|
4083
|
-
console.log(
|
|
4498
|
+
console.log(chalk19.yellow(` - ${s}`));
|
|
4084
4499
|
}
|
|
4085
4500
|
info("To request elevated scopes, use the web dashboard or contact your team admin.");
|
|
4086
4501
|
process.exitCode = 1;
|
|
@@ -4091,7 +4506,7 @@ async function integrationInstallCommand(slug, options) {
|
|
|
4091
4506
|
}
|
|
4092
4507
|
async function integrationUninstallCommand(slug, options) {
|
|
4093
4508
|
const json = isJsonMode();
|
|
4094
|
-
const spinner =
|
|
4509
|
+
const spinner = ora17({ text: "Uninstalling integration\u2026", isSilent: json }).start();
|
|
4095
4510
|
try {
|
|
4096
4511
|
const all = await api.get("/integrations/bindings");
|
|
4097
4512
|
const integration2 = all.find((p) => p.slug === slug);
|
|
@@ -4122,7 +4537,7 @@ async function integrationUninstallCommand(slug, options) {
|
|
|
4122
4537
|
}
|
|
4123
4538
|
async function integrationRequestsCommand() {
|
|
4124
4539
|
const json = isJsonMode();
|
|
4125
|
-
const spinner =
|
|
4540
|
+
const spinner = ora17({ text: "Fetching scope requests\u2026", isSilent: json }).start();
|
|
4126
4541
|
try {
|
|
4127
4542
|
const requests = await api.get("/integrations/bindings/scope-requests");
|
|
4128
4543
|
spinner.stop();
|
|
@@ -4134,10 +4549,10 @@ async function integrationRequestsCommand() {
|
|
|
4134
4549
|
info("No pending scope requests.");
|
|
4135
4550
|
return;
|
|
4136
4551
|
}
|
|
4137
|
-
console.log(
|
|
4552
|
+
console.log(chalk19.bold("\nPending Scope Requests:\n"));
|
|
4138
4553
|
for (const r of requests) {
|
|
4139
|
-
console.log(` ${
|
|
4140
|
-
if (r.reason) console.log(` reason: ${
|
|
4554
|
+
console.log(` ${chalk19.bold(r.id)} scopes: ${r.requested_scopes.join(", ")}`);
|
|
4555
|
+
if (r.reason) console.log(` reason: ${chalk19.dim(r.reason)}`);
|
|
4141
4556
|
console.log(` status: ${r.status} requested: ${r.created_at}`);
|
|
4142
4557
|
console.log();
|
|
4143
4558
|
}
|
|
@@ -4148,7 +4563,7 @@ async function integrationRequestsCommand() {
|
|
|
4148
4563
|
}
|
|
4149
4564
|
async function integrationApproveCommand(requestId, options) {
|
|
4150
4565
|
const json = isJsonMode();
|
|
4151
|
-
const spinner =
|
|
4566
|
+
const spinner = ora17({ text: "Approving request\u2026", isSilent: json }).start();
|
|
4152
4567
|
try {
|
|
4153
4568
|
const result = await api.put(`/integrations/bindings/scope-requests/${requestId}`, {
|
|
4154
4569
|
status: "approved",
|
|
@@ -4167,7 +4582,7 @@ async function integrationApproveCommand(requestId, options) {
|
|
|
4167
4582
|
}
|
|
4168
4583
|
async function integrationDenyCommand(requestId, options) {
|
|
4169
4584
|
const json = isJsonMode();
|
|
4170
|
-
const spinner =
|
|
4585
|
+
const spinner = ora17({ text: "Denying request\u2026", isSilent: json }).start();
|
|
4171
4586
|
try {
|
|
4172
4587
|
const result = await api.put(`/integrations/bindings/scope-requests/${requestId}`, {
|
|
4173
4588
|
status: "denied",
|
|
@@ -4209,7 +4624,7 @@ async function integrationEnrolCommand(options) {
|
|
|
4209
4624
|
const [secretKey, secretValue] = secrets[0];
|
|
4210
4625
|
const credentials = { [secretKey]: secretValue };
|
|
4211
4626
|
const authType = options.authType ?? (options.webhookSecret ? "webhook" : options.apiKey ? "api_key" : "oauth2");
|
|
4212
|
-
const spinner =
|
|
4627
|
+
const spinner = ora17({ text: `Enrolling ${options.def} at ${apiScope} scope\u2026`, isSilent: json }).start();
|
|
4213
4628
|
try {
|
|
4214
4629
|
let agentId;
|
|
4215
4630
|
if (apiScope === "agent") {
|
|
@@ -4268,7 +4683,7 @@ function handleError(err) {
|
|
|
4268
4683
|
}
|
|
4269
4684
|
|
|
4270
4685
|
// src/bin/agt.ts
|
|
4271
|
-
var cliVersion2 = true ? "0.26.2
|
|
4686
|
+
var cliVersion2 = true ? "0.26.2" : "dev";
|
|
4272
4687
|
var program = new Command();
|
|
4273
4688
|
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
4689
|
program.hook("preAction", (thisCommand) => {
|
|
@@ -4283,6 +4698,14 @@ var team = program.command("team").description("Manage teams");
|
|
|
4283
4698
|
team.command("list").description("List teams you belong to").action(teamListCommand);
|
|
4284
4699
|
team.command("create <name>").description("Create a new team and set it as active").action(teamCreateCommand);
|
|
4285
4700
|
team.command("switch <slug>").description("Switch the active team").action(teamSwitchCommand);
|
|
4701
|
+
var impersonate = program.command("impersonate").description(
|
|
4702
|
+
"Operate as a managed agent locally (experimental, gated by AGT_IMPERSONATE_ENABLED)"
|
|
4703
|
+
);
|
|
4704
|
+
impersonate.command("connect <token>").description(
|
|
4705
|
+
"Redeem a XXXX-XXXX token from the webapp, render the agent's CLAUDE.md + .mcp.json, and symlink them into the current project"
|
|
4706
|
+
).action(impersonateConnectCommand);
|
|
4707
|
+
impersonate.command("current").description('Print the active impersonation (or "none")').action(impersonateCurrentCommand);
|
|
4708
|
+
impersonate.command("exit").description("End the active impersonation, restore project files, and notify the server").action(impersonateExitCommand);
|
|
4286
4709
|
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
4710
|
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
4711
|
var channels = program.command("channels").description("Manage and inspect channel configuration");
|
|
@@ -4315,16 +4738,16 @@ host.command("pair <host-name>").description("Start an SSM port-forward + shell
|
|
|
4315
4738
|
})
|
|
4316
4739
|
);
|
|
4317
4740
|
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",
|
|
4741
|
+
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);
|
|
4742
|
+
manager.command("stop").description("Stop the running manager daemon").option("--config-dir <dir>", "Config directory for agent files", join12(homedir5(), ".augmented")).action(managerStopCommand);
|
|
4743
|
+
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);
|
|
4744
|
+
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);
|
|
4745
|
+
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
4746
|
manager.command("uninstall").description("Remove the OS-level supervisor (launchctl unload + delete plist on macOS). Idempotent.").action(managerUninstallCommand);
|
|
4324
4747
|
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
4748
|
manager.command("uninstall-system-unit").description("Remove the system-level systemd unit (Linux, root). Idempotent. (ENG-4706)").action(managerUninstallSystemUnitCommand);
|
|
4326
4749
|
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",
|
|
4750
|
+
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
4751
|
var kanban = program.command("kanban").description("Manage agent kanban boards");
|
|
4329
4752
|
kanban.command("list").description("List kanban board items for an agent").requiredOption("--agent <code-name>", "Agent code name").action(kanbanListCommand);
|
|
4330
4753
|
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);
|