@integrity-labs/agt-cli 0.27.8 → 0.27.9-test.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/impersonate-statusline.sh +7 -2
- package/dist/bin/agt.js +946 -129
- package/dist/bin/agt.js.map +1 -1
- package/dist/{chunk-YSBGIXJG.js → chunk-I2OTQJH3.js} +361 -26
- package/dist/chunk-I2OTQJH3.js.map +1 -0
- package/dist/{chunk-GN4XPQWJ.js → chunk-MQJ5DMPT.js} +535 -94
- package/dist/chunk-MQJ5DMPT.js.map +1 -0
- package/dist/{chunk-IB655E5U.js → chunk-SXBZDUKN.js} +1086 -285
- package/dist/chunk-SXBZDUKN.js.map +1 -0
- package/dist/{claude-pair-runtime-ZBQKBBMT.js → claude-pair-runtime-F6USL3EW.js} +2 -2
- package/dist/lib/manager-worker.js +2071 -517
- package/dist/lib/manager-worker.js.map +1 -1
- package/dist/mcp/direct-chat-channel.js +49 -455
- package/dist/mcp/index.js +232 -137
- package/dist/mcp/slack-channel.js +1379 -856
- package/dist/mcp/teams-channel.js +16018 -0
- package/dist/mcp/telegram-channel.js +1264 -618
- package/dist/{persistent-session-ICYFLUAM.js → persistent-session-OL5EDYB4.js} +5 -3
- package/dist/responsiveness-probe-B6LJJRUD.js +74 -0
- package/dist/responsiveness-probe-B6LJJRUD.js.map +1 -0
- package/package.json +2 -2
- package/dist/chunk-GN4XPQWJ.js.map +0 -1
- package/dist/chunk-IB655E5U.js.map +0 -1
- package/dist/chunk-YSBGIXJG.js.map +0 -1
- package/dist/responsiveness-probe-WZNQ2762.js +0 -33
- package/dist/responsiveness-probe-WZNQ2762.js.map +0 -1
- /package/dist/{claude-pair-runtime-ZBQKBBMT.js.map → claude-pair-runtime-F6USL3EW.js.map} +0 -0
- /package/dist/{persistent-session-ICYFLUAM.js.map → persistent-session-OL5EDYB4.js.map} +0 -0
package/dist/bin/agt.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
ApiError,
|
|
4
|
-
|
|
4
|
+
LITERAL_SECRET_PATTERNS,
|
|
5
|
+
PROD_AGT_HOST,
|
|
5
6
|
api,
|
|
6
7
|
error,
|
|
7
8
|
exchangeApiKey,
|
|
@@ -27,7 +28,7 @@ import {
|
|
|
27
28
|
success,
|
|
28
29
|
table,
|
|
29
30
|
warn
|
|
30
|
-
} from "../chunk-
|
|
31
|
+
} from "../chunk-SXBZDUKN.js";
|
|
31
32
|
import {
|
|
32
33
|
CHANNEL_REGISTRY,
|
|
33
34
|
DEPLOYMENT_TEMPLATES,
|
|
@@ -53,11 +54,11 @@ import {
|
|
|
53
54
|
renderTemplate,
|
|
54
55
|
resolveChannels,
|
|
55
56
|
serializeManifestForSlackCli
|
|
56
|
-
} from "../chunk-
|
|
57
|
+
} from "../chunk-I2OTQJH3.js";
|
|
57
58
|
|
|
58
59
|
// src/bin/agt.ts
|
|
59
|
-
import { join as
|
|
60
|
-
import { homedir as
|
|
60
|
+
import { join as join20 } from "path";
|
|
61
|
+
import { homedir as homedir9 } from "os";
|
|
61
62
|
import { Command } from "commander";
|
|
62
63
|
|
|
63
64
|
// src/commands/whoami.ts
|
|
@@ -551,11 +552,11 @@ async function lintCommand(path) {
|
|
|
551
552
|
process.exitCode = 1;
|
|
552
553
|
return;
|
|
553
554
|
}
|
|
554
|
-
const { readdirSync:
|
|
555
|
-
const entries =
|
|
555
|
+
const { readdirSync: readdirSync7, statSync: statSync5 } = await import("fs");
|
|
556
|
+
const entries = readdirSync7(augmentedDir);
|
|
556
557
|
for (const entry of entries) {
|
|
557
558
|
const entryPath = join2(augmentedDir, entry);
|
|
558
|
-
if (
|
|
559
|
+
if (statSync5(entryPath).isDirectory()) {
|
|
559
560
|
dirs.push({ name: entry, dir: entryPath });
|
|
560
561
|
}
|
|
561
562
|
}
|
|
@@ -828,7 +829,11 @@ async function channelSlackSetupCommand(agentCodeName, options) {
|
|
|
828
829
|
const manifest = generateSlackAppManifest({
|
|
829
830
|
agent_name: agent2.display_name ?? agentCodeName,
|
|
830
831
|
description: `Augmented-managed Slack bot for agent ${agentCodeName}`,
|
|
831
|
-
scopes: selectedScopes
|
|
832
|
+
scopes: selectedScopes,
|
|
833
|
+
// ENG-6044: per-agent slash commands carry the code-name suffix.
|
|
834
|
+
// No-op until a slash_command_url is supplied, but keeps this
|
|
835
|
+
// manifest consistent with the API-generated ones.
|
|
836
|
+
agent_code_name: agent2.code_name ?? agentCodeName
|
|
832
837
|
});
|
|
833
838
|
const manifestObj = serializeManifestForSlackCli(manifest);
|
|
834
839
|
const manifestJson = JSON.stringify(manifestObj, null, 2);
|
|
@@ -1435,6 +1440,23 @@ async function provisionCommand(codeName, options) {
|
|
|
1435
1440
|
};
|
|
1436
1441
|
}
|
|
1437
1442
|
const resolvedChannels = resolveChannels(agentChannelPolicy, orgChannelPolicy);
|
|
1443
|
+
spinner.text = "Fetching integrations\u2026";
|
|
1444
|
+
const agentId = agentData.agent_id;
|
|
1445
|
+
let integrations = [];
|
|
1446
|
+
let integrationsError = null;
|
|
1447
|
+
try {
|
|
1448
|
+
const integrationsData = await api.post("/host/agent-integrations", { agent_id: agentId });
|
|
1449
|
+
integrations = integrationsData.integrations ?? [];
|
|
1450
|
+
} catch (err) {
|
|
1451
|
+
integrationsError = err.message;
|
|
1452
|
+
if (!json) {
|
|
1453
|
+
spinner.stop();
|
|
1454
|
+
warn(
|
|
1455
|
+
`Could not fetch integrations: ${integrationsError}. .mcp.json may be missing integration servers.`
|
|
1456
|
+
);
|
|
1457
|
+
spinner.start("Provisioning\u2026");
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1438
1460
|
const frameworkId = agentData.framework ?? "openclaw";
|
|
1439
1461
|
const adapter = getFramework(frameworkId);
|
|
1440
1462
|
spinner.text = `Building ${adapter.label} config\u2026`;
|
|
@@ -1445,6 +1467,7 @@ async function provisionCommand(codeName, options) {
|
|
|
1445
1467
|
toolsFrontmatter,
|
|
1446
1468
|
toolsContent,
|
|
1447
1469
|
resolvedChannels,
|
|
1470
|
+
integrations,
|
|
1448
1471
|
deploymentTarget: target,
|
|
1449
1472
|
gatewayPort: 9e3
|
|
1450
1473
|
};
|
|
@@ -1478,6 +1501,8 @@ async function provisionCommand(codeName, options) {
|
|
|
1478
1501
|
charter_hash: provisionOutput.charterHash,
|
|
1479
1502
|
tools_hash: provisionOutput.toolsHash,
|
|
1480
1503
|
channels: resolvedChannels.length,
|
|
1504
|
+
integrations: integrations.length,
|
|
1505
|
+
integrations_error: integrationsError,
|
|
1481
1506
|
artifacts: provisionOutput.artifacts.map((a) => ({
|
|
1482
1507
|
path: a.relativePath,
|
|
1483
1508
|
size: a.content.length
|
|
@@ -1534,6 +1559,8 @@ async function provisionCommand(codeName, options) {
|
|
|
1534
1559
|
charter_hash: provisionOutput.charterHash,
|
|
1535
1560
|
tools_hash: provisionOutput.toolsHash,
|
|
1536
1561
|
channels: resolvedChannels.length,
|
|
1562
|
+
integrations: integrations.length,
|
|
1563
|
+
integrations_error: integrationsError,
|
|
1537
1564
|
artifacts: provisionOutput.artifacts.map((a) => a.relativePath)
|
|
1538
1565
|
});
|
|
1539
1566
|
return;
|
|
@@ -1546,6 +1573,7 @@ async function provisionCommand(codeName, options) {
|
|
|
1546
1573
|
info(`CHARTER hash: ${provisionOutput.charterHash.slice(0, 12)}\u2026`);
|
|
1547
1574
|
info(`TOOLS hash: ${provisionOutput.toolsHash.slice(0, 12)}\u2026`);
|
|
1548
1575
|
info(`Channels: ${resolvedChannels.length} active`);
|
|
1576
|
+
info(`Integrations: ${integrations.length}${integrationsError ? " (fetch failed)" : ""}`);
|
|
1549
1577
|
info(`Artifacts: ${provisionOutput.artifacts.map((a) => a.relativePath).join(", ")}`);
|
|
1550
1578
|
console.log();
|
|
1551
1579
|
info("Next steps:");
|
|
@@ -1557,8 +1585,9 @@ async function provisionCommand(codeName, options) {
|
|
|
1557
1585
|
import chalk10 from "chalk";
|
|
1558
1586
|
import ora10 from "ora";
|
|
1559
1587
|
import { spawn } from "child_process";
|
|
1560
|
-
import { readFileSync as readFileSync5, writeFileSync as writeFileSync7 } from "fs";
|
|
1561
|
-
import { join as join10 } from "path";
|
|
1588
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync7 } from "fs";
|
|
1589
|
+
import { delimiter, dirname as dirname3, join as join10 } from "path";
|
|
1590
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1562
1591
|
|
|
1563
1592
|
// src/lib/impersonate-flag.ts
|
|
1564
1593
|
var ENV_VAR = "AGT_IMPERSONATE_ENABLED";
|
|
@@ -1590,10 +1619,12 @@ production.
|
|
|
1590
1619
|
}
|
|
1591
1620
|
|
|
1592
1621
|
// src/lib/impersonate-state.ts
|
|
1622
|
+
import { createHash } from "crypto";
|
|
1593
1623
|
import {
|
|
1594
1624
|
chmodSync,
|
|
1595
1625
|
existsSync as existsSync2,
|
|
1596
1626
|
mkdirSync as mkdirSync4,
|
|
1627
|
+
readdirSync,
|
|
1597
1628
|
readFileSync as readFileSync2,
|
|
1598
1629
|
renameSync,
|
|
1599
1630
|
rmSync,
|
|
@@ -1604,6 +1635,12 @@ import { join as join6 } from "path";
|
|
|
1604
1635
|
var IMPERSONATE_ROOT = join6(homedir(), ".augmented-impersonate");
|
|
1605
1636
|
var ACTIVE_DIR = join6(IMPERSONATE_ROOT, "active");
|
|
1606
1637
|
var ACTIVE_MANIFEST_PATH = join6(ACTIVE_DIR, "manifest.json");
|
|
1638
|
+
var SESSION_FILE_PREFIX = "session-";
|
|
1639
|
+
var SESSION_FILE_SUFFIX = ".json";
|
|
1640
|
+
function sessionManifestPath(projectCwd) {
|
|
1641
|
+
const hash = createHash("sha256").update(projectCwd).digest("hex").slice(0, 16);
|
|
1642
|
+
return join6(ACTIVE_DIR, `${SESSION_FILE_PREFIX}${hash}${SESSION_FILE_SUFFIX}`);
|
|
1643
|
+
}
|
|
1607
1644
|
var CODE_NAME_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
1608
1645
|
function assertValidCodeName(codeName) {
|
|
1609
1646
|
if (!CODE_NAME_RE.test(codeName)) {
|
|
@@ -1627,36 +1664,84 @@ function ensureImpersonateWorkdir(codeName) {
|
|
|
1627
1664
|
mkdirSync4(dir, { recursive: true });
|
|
1628
1665
|
return dir;
|
|
1629
1666
|
}
|
|
1630
|
-
function
|
|
1631
|
-
if (
|
|
1667
|
+
function validateManifest(parsed) {
|
|
1668
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
1669
|
+
const m = parsed;
|
|
1670
|
+
const backupsOk = typeof m.backups === "object" && m.backups !== null && Object.values(m.backups).every(
|
|
1671
|
+
(v) => v === "had-file" || v === "was-symlink" || v === "didnt-exist"
|
|
1672
|
+
);
|
|
1673
|
+
if (typeof m.code_name !== "string" || typeof m.agent_id !== "string" || typeof m.team_id !== "string" || typeof m.token !== "string" || typeof m.expires_at !== "number" || typeof m.session_id !== "string" || typeof m.minted_at !== "string" || typeof m.project_cwd !== "string" || // ENG-5688 (redesigned): host required so `exit` calls /stop against
|
|
1674
|
+
// the right environment. Required, not optional, because writing the
|
|
1675
|
+
// wrong host on an existing session would point exit at a server
|
|
1676
|
+
// that has no record of the session — silently misleading.
|
|
1677
|
+
typeof m.host !== "string" || m.host.length === 0 || !backupsOk) {
|
|
1678
|
+
return null;
|
|
1679
|
+
}
|
|
1680
|
+
return parsed;
|
|
1681
|
+
}
|
|
1682
|
+
function readManifestFile(path) {
|
|
1683
|
+
if (!existsSync2(path)) return null;
|
|
1632
1684
|
try {
|
|
1633
|
-
|
|
1634
|
-
const parsed = JSON.parse(raw);
|
|
1635
|
-
const backupsOk = typeof parsed.backups === "object" && parsed.backups !== null && Object.values(parsed.backups).every(
|
|
1636
|
-
(v) => v === "had-file" || v === "was-symlink" || v === "didnt-exist"
|
|
1637
|
-
);
|
|
1638
|
-
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
|
|
1639
|
-
// the right environment. Required, not optional, because writing the
|
|
1640
|
-
// wrong host on an existing session would point exit at a server
|
|
1641
|
-
// that has no record of the session — silently misleading.
|
|
1642
|
-
typeof parsed.host !== "string" || parsed.host.length === 0 || !backupsOk) {
|
|
1643
|
-
return null;
|
|
1644
|
-
}
|
|
1645
|
-
return parsed;
|
|
1685
|
+
return validateManifest(JSON.parse(readFileSync2(path, "utf-8")));
|
|
1646
1686
|
} catch {
|
|
1647
1687
|
return null;
|
|
1648
1688
|
}
|
|
1649
1689
|
}
|
|
1690
|
+
function migrateLegacyManifest() {
|
|
1691
|
+
if (!existsSync2(ACTIVE_MANIFEST_PATH)) return;
|
|
1692
|
+
const legacy = readManifestFile(ACTIVE_MANIFEST_PATH);
|
|
1693
|
+
try {
|
|
1694
|
+
if (!legacy) {
|
|
1695
|
+
rmSync(ACTIVE_MANIFEST_PATH, { force: true });
|
|
1696
|
+
return;
|
|
1697
|
+
}
|
|
1698
|
+
const target = sessionManifestPath(legacy.project_cwd);
|
|
1699
|
+
if (!existsSync2(target)) {
|
|
1700
|
+
mkdirSync4(ACTIVE_DIR, { recursive: true, mode: 448 });
|
|
1701
|
+
renameSync(ACTIVE_MANIFEST_PATH, target);
|
|
1702
|
+
chmodSync(target, 384);
|
|
1703
|
+
} else {
|
|
1704
|
+
rmSync(ACTIVE_MANIFEST_PATH, { force: true });
|
|
1705
|
+
}
|
|
1706
|
+
} catch {
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
function readActiveManifest(projectCwd) {
|
|
1710
|
+
migrateLegacyManifest();
|
|
1711
|
+
return readManifestFile(sessionManifestPath(projectCwd));
|
|
1712
|
+
}
|
|
1713
|
+
function listActiveManifests() {
|
|
1714
|
+
migrateLegacyManifest();
|
|
1715
|
+
let entries;
|
|
1716
|
+
try {
|
|
1717
|
+
entries = readdirSync(ACTIVE_DIR);
|
|
1718
|
+
} catch {
|
|
1719
|
+
return [];
|
|
1720
|
+
}
|
|
1721
|
+
const out = [];
|
|
1722
|
+
for (const entry of entries) {
|
|
1723
|
+
if (!entry.startsWith(SESSION_FILE_PREFIX) || !entry.endsWith(SESSION_FILE_SUFFIX)) {
|
|
1724
|
+
continue;
|
|
1725
|
+
}
|
|
1726
|
+
const manifest = readManifestFile(join6(ACTIVE_DIR, entry));
|
|
1727
|
+
if (manifest) out.push(manifest);
|
|
1728
|
+
}
|
|
1729
|
+
return out;
|
|
1730
|
+
}
|
|
1650
1731
|
function writeActiveManifest(manifest) {
|
|
1732
|
+
migrateLegacyManifest();
|
|
1651
1733
|
mkdirSync4(ACTIVE_DIR, { recursive: true, mode: 448 });
|
|
1652
|
-
const
|
|
1734
|
+
const path = sessionManifestPath(manifest.project_cwd);
|
|
1735
|
+
const tmp = path + ".tmp";
|
|
1653
1736
|
writeFileSync4(tmp, JSON.stringify(manifest, null, 2), { mode: 384 });
|
|
1654
1737
|
chmodSync(tmp, 384);
|
|
1655
|
-
renameSync(tmp,
|
|
1738
|
+
renameSync(tmp, path);
|
|
1656
1739
|
}
|
|
1657
|
-
function clearActiveManifest() {
|
|
1658
|
-
|
|
1659
|
-
|
|
1740
|
+
function clearActiveManifest(projectCwd) {
|
|
1741
|
+
migrateLegacyManifest();
|
|
1742
|
+
const path = sessionManifestPath(projectCwd);
|
|
1743
|
+
if (existsSync2(path)) {
|
|
1744
|
+
rmSync(path);
|
|
1660
1745
|
}
|
|
1661
1746
|
}
|
|
1662
1747
|
function isExpired(manifest, now = Date.now()) {
|
|
@@ -1756,7 +1841,7 @@ import {
|
|
|
1756
1841
|
copyFileSync,
|
|
1757
1842
|
existsSync as existsSync4,
|
|
1758
1843
|
mkdirSync as mkdirSync5,
|
|
1759
|
-
readdirSync,
|
|
1844
|
+
readdirSync as readdirSync2,
|
|
1760
1845
|
readFileSync as readFileSync3,
|
|
1761
1846
|
renameSync as renameSync3,
|
|
1762
1847
|
rmdirSync,
|
|
@@ -1814,7 +1899,7 @@ function unregisterIntroduceHook(backup) {
|
|
|
1814
1899
|
if (created_claude_dir) {
|
|
1815
1900
|
const claudeDir = dirOf(settings_path);
|
|
1816
1901
|
try {
|
|
1817
|
-
if (existsSync4(claudeDir) &&
|
|
1902
|
+
if (existsSync4(claudeDir) && readdirSync2(claudeDir).length === 0) {
|
|
1818
1903
|
rmdirSync(claudeDir);
|
|
1819
1904
|
}
|
|
1820
1905
|
} catch {
|
|
@@ -1850,7 +1935,7 @@ import {
|
|
|
1850
1935
|
copyFileSync as copyFileSync2,
|
|
1851
1936
|
existsSync as existsSync5,
|
|
1852
1937
|
mkdirSync as mkdirSync6,
|
|
1853
|
-
readdirSync as
|
|
1938
|
+
readdirSync as readdirSync3,
|
|
1854
1939
|
readFileSync as readFileSync4,
|
|
1855
1940
|
renameSync as renameSync4,
|
|
1856
1941
|
rmdirSync as rmdirSync2,
|
|
@@ -1920,7 +2005,7 @@ function unregisterStatusLine(backup) {
|
|
|
1920
2005
|
if (created_claude_dir) {
|
|
1921
2006
|
const claudeDir = dirname2(settings_path);
|
|
1922
2007
|
try {
|
|
1923
|
-
if (existsSync5(claudeDir) &&
|
|
2008
|
+
if (existsSync5(claudeDir) && readdirSync3(claudeDir).length === 0) {
|
|
1924
2009
|
rmdirSync2(claudeDir);
|
|
1925
2010
|
}
|
|
1926
2011
|
} catch {
|
|
@@ -1996,12 +2081,86 @@ function buildIntroduction(input4) {
|
|
|
1996
2081
|
const lines = [`I'm ${spokenName}${codeSuffix}${roleClause}.`, ""];
|
|
1997
2082
|
if (integrations.length > 0) {
|
|
1998
2083
|
lines.push(`Connected integrations: ${integrations.join(", ")}.`);
|
|
2084
|
+
lines.push("");
|
|
2085
|
+
lines.push(
|
|
2086
|
+
"If the operator asks what you can do, call each MCP server's `tools/list` and report the actual tool inventory. Do not narrate capabilities from CHARTER.md \u2014 it describes intent, not runtime state. Tools that fail to enumerate (server failed to spawn, env missing, etc.) should be reported as such, not silently omitted."
|
|
2087
|
+
);
|
|
1999
2088
|
} else {
|
|
2000
2089
|
lines.push("No integrations are connected.");
|
|
2001
2090
|
}
|
|
2002
2091
|
return lines.join("\n");
|
|
2003
2092
|
}
|
|
2004
2093
|
|
|
2094
|
+
// src/lib/impersonate-mcp-rewrite.ts
|
|
2095
|
+
var SERVER_HOME_MCP_PATH_RE = /^\/home\/[^/]+\/\.augmented\/_mcp\/([^/]+)$/;
|
|
2096
|
+
var CHANNEL_SERVER_KEYS = /* @__PURE__ */ new Set([
|
|
2097
|
+
"slack",
|
|
2098
|
+
"telegram",
|
|
2099
|
+
"msteams",
|
|
2100
|
+
"direct-chat",
|
|
2101
|
+
"discord"
|
|
2102
|
+
]);
|
|
2103
|
+
function rewriteMcpJsonForImpersonation(mcpJson, ctx) {
|
|
2104
|
+
const parsed = JSON.parse(mcpJson);
|
|
2105
|
+
if (!isPlainObject(parsed)) {
|
|
2106
|
+
throw new Error(
|
|
2107
|
+
`rewriteMcpJsonForImpersonation: expected an object at the JSON root, got ${typeof parsed}`
|
|
2108
|
+
);
|
|
2109
|
+
}
|
|
2110
|
+
const servers = parsed["mcpServers"];
|
|
2111
|
+
if (!isPlainObject(servers)) {
|
|
2112
|
+
throw new Error(
|
|
2113
|
+
"rewriteMcpJsonForImpersonation: expected an object-typed `mcpServers` field"
|
|
2114
|
+
);
|
|
2115
|
+
}
|
|
2116
|
+
const rewrittenServers = {};
|
|
2117
|
+
for (const [serverName, raw] of Object.entries(servers)) {
|
|
2118
|
+
rewrittenServers[serverName] = rewriteServerEntry(serverName, raw, ctx);
|
|
2119
|
+
}
|
|
2120
|
+
const out = { ...parsed, mcpServers: rewrittenServers };
|
|
2121
|
+
return JSON.stringify(out, null, 2);
|
|
2122
|
+
}
|
|
2123
|
+
function rewriteServerEntry(serverName, raw, ctx) {
|
|
2124
|
+
if (!isPlainObject(raw)) return raw;
|
|
2125
|
+
let args = raw["args"];
|
|
2126
|
+
if (Array.isArray(args)) {
|
|
2127
|
+
args = args.map((arg) => {
|
|
2128
|
+
if (typeof arg !== "string") return arg;
|
|
2129
|
+
const m = SERVER_HOME_MCP_PATH_RE.exec(arg);
|
|
2130
|
+
if (!m) return arg;
|
|
2131
|
+
return `${ctx.cliMcpBundleDir}/${m[1]}`;
|
|
2132
|
+
});
|
|
2133
|
+
}
|
|
2134
|
+
const env = isPlainObject(raw["env"]) ? { ...raw["env"] } : {};
|
|
2135
|
+
fillIfEmpty(env, "AGT_HOST", ctx.apiHost);
|
|
2136
|
+
fillIfEmpty(env, "AGT_API_KEY", ctx.impersonationToken);
|
|
2137
|
+
if (ctx.agentSessionToken) {
|
|
2138
|
+
fillIfEmpty(env, "AGT_AGENT_SESSION_TOKEN", ctx.agentSessionToken);
|
|
2139
|
+
}
|
|
2140
|
+
fillIfEmpty(env, "HOME", ctx.operatorHome);
|
|
2141
|
+
fillIfEmpty(env, "AGT_AGENT_ID", ctx.agentId);
|
|
2142
|
+
fillIfEmpty(env, "AGT_AGENT_CODE_NAME", ctx.agentCodeName);
|
|
2143
|
+
if ("PATH" in env) {
|
|
2144
|
+
env["PATH"] = ctx.operatorPath;
|
|
2145
|
+
}
|
|
2146
|
+
if (env["AGT_RUN_ID"] === "${AGT_RUN_ID}") {
|
|
2147
|
+
delete env["AGT_RUN_ID"];
|
|
2148
|
+
}
|
|
2149
|
+
if (CHANNEL_SERVER_KEYS.has(serverName)) {
|
|
2150
|
+
env["AGT_ACT_AS_AGENT_ID"] = ctx.agentId;
|
|
2151
|
+
}
|
|
2152
|
+
return { ...raw, args, env };
|
|
2153
|
+
}
|
|
2154
|
+
function fillIfEmpty(env, key, value) {
|
|
2155
|
+
const current = env[key];
|
|
2156
|
+
if (typeof current !== "string" || current.length === 0 || current === `\${${key}}`) {
|
|
2157
|
+
env[key] = value;
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
function isPlainObject(v) {
|
|
2161
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2005
2164
|
// src/commands/impersonate.ts
|
|
2006
2165
|
var SWAP_FILES = ["CLAUDE.md", ".mcp.json"];
|
|
2007
2166
|
var AGENT_IMPERSONATION_HEADER = "X-Agent-Impersonation";
|
|
@@ -2017,13 +2176,15 @@ async function impersonateConnectCommand(token, options = {}) {
|
|
|
2017
2176
|
process.exitCode = 1;
|
|
2018
2177
|
return;
|
|
2019
2178
|
}
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2179
|
+
if (!options.workdir) {
|
|
2180
|
+
const existing = readActiveManifest(process.cwd());
|
|
2181
|
+
if (existing) {
|
|
2182
|
+
const msg = `An impersonation session is already active in this directory (${existing.code_name}). Run \`agt impersonate exit\` here first, or connect from a different directory.`;
|
|
2183
|
+
if (json) jsonOutput({ ok: false, error: msg });
|
|
2184
|
+
else error(msg);
|
|
2185
|
+
process.exitCode = 1;
|
|
2186
|
+
return;
|
|
2187
|
+
}
|
|
2027
2188
|
}
|
|
2028
2189
|
if (!options.workdir) {
|
|
2029
2190
|
const workTreeRoot = findGitWorkTreeRoot(process.cwd());
|
|
@@ -2041,7 +2202,7 @@ async function impersonateConnectCommand(token, options = {}) {
|
|
|
2041
2202
|
isSilent: json
|
|
2042
2203
|
});
|
|
2043
2204
|
spinner.start();
|
|
2044
|
-
const host2 = (options.apiHost ??
|
|
2205
|
+
const host2 = (options.apiHost ?? PROD_AGT_HOST).replace(/\/+$/, "");
|
|
2045
2206
|
let bundle;
|
|
2046
2207
|
try {
|
|
2047
2208
|
const res = await fetch(`${host2}/impersonate/agent/redeem`, {
|
|
@@ -2071,10 +2232,28 @@ async function impersonateConnectCommand(token, options = {}) {
|
|
|
2071
2232
|
);
|
|
2072
2233
|
writeFileSync7(
|
|
2073
2234
|
join10(personaDir, ".mcp.json"),
|
|
2074
|
-
bundle.artifacts[".mcp.json"]
|
|
2235
|
+
rewriteMcpJsonForImpersonation(bundle.artifacts[".mcp.json"], {
|
|
2236
|
+
operatorHome: process.env.HOME ?? "",
|
|
2237
|
+
operatorPath: resolveOperatorPath(),
|
|
2238
|
+
cliMcpBundleDir: resolveCliMcpBundleDir(),
|
|
2239
|
+
impersonationToken: bundle.token,
|
|
2240
|
+
agentSessionToken: bundle.agent_session_token ?? null,
|
|
2241
|
+
apiHost: host2,
|
|
2242
|
+
agentId: bundle.agent.agent_id,
|
|
2243
|
+
agentCodeName: bundle.agent.code_name
|
|
2244
|
+
})
|
|
2075
2245
|
);
|
|
2076
2246
|
spinner.text = "Swapping persona files\u2026";
|
|
2077
2247
|
const projectCwd = options.workdir ? ensureImpersonateWorkdir(bundle.agent.code_name) : process.cwd();
|
|
2248
|
+
const existingForTarget = readActiveManifest(projectCwd);
|
|
2249
|
+
if (existingForTarget) {
|
|
2250
|
+
spinner.fail("Impersonation already active for this agent.");
|
|
2251
|
+
const msg = `An impersonation session is already active in ${projectCwd} (${existingForTarget.code_name}). Run \`agt impersonate exit\` there first.`;
|
|
2252
|
+
if (json) jsonOutput({ ok: false, error: msg });
|
|
2253
|
+
else error(msg);
|
|
2254
|
+
process.exitCode = 1;
|
|
2255
|
+
return;
|
|
2256
|
+
}
|
|
2078
2257
|
const backups = {};
|
|
2079
2258
|
const swapped = [];
|
|
2080
2259
|
let hookBackup;
|
|
@@ -2164,10 +2343,16 @@ async function impersonateConnectCommand(token, options = {}) {
|
|
|
2164
2343
|
}
|
|
2165
2344
|
info("Launching Claude Code\u2026 (use `--no-launch` next time to skip)");
|
|
2166
2345
|
console.log();
|
|
2346
|
+
const launch = buildImpersonateClaudeLaunch(
|
|
2347
|
+
projectCwd,
|
|
2348
|
+
process.env,
|
|
2349
|
+
bundle.agent.agent_id
|
|
2350
|
+
);
|
|
2167
2351
|
await new Promise((resolve2) => {
|
|
2168
|
-
const child = spawn("claude", {
|
|
2352
|
+
const child = spawn("claude", launch.args, {
|
|
2169
2353
|
stdio: "inherit",
|
|
2170
|
-
cwd: projectCwd
|
|
2354
|
+
cwd: projectCwd,
|
|
2355
|
+
env: launch.env
|
|
2171
2356
|
});
|
|
2172
2357
|
child.on("error", (err) => {
|
|
2173
2358
|
if (err.code === "ENOENT") {
|
|
@@ -2190,33 +2375,8 @@ async function impersonateConnectCommand(token, options = {}) {
|
|
|
2190
2375
|
console.log();
|
|
2191
2376
|
info("When done: `agt impersonate exit` (still active).");
|
|
2192
2377
|
}
|
|
2193
|
-
|
|
2194
|
-
requireImpersonateEnabled();
|
|
2195
|
-
const json = isJsonMode();
|
|
2196
|
-
const manifest = readActiveManifest();
|
|
2197
|
-
if (!manifest) {
|
|
2198
|
-
if (json) jsonOutput({ ok: true, active: null });
|
|
2199
|
-
else info("No active impersonation.");
|
|
2200
|
-
return;
|
|
2201
|
-
}
|
|
2378
|
+
function printManifestText(manifest) {
|
|
2202
2379
|
const expired = isExpired(manifest);
|
|
2203
|
-
if (json) {
|
|
2204
|
-
jsonOutput({
|
|
2205
|
-
ok: true,
|
|
2206
|
-
active: {
|
|
2207
|
-
code_name: manifest.code_name,
|
|
2208
|
-
agent_id: manifest.agent_id,
|
|
2209
|
-
team_id: manifest.team_id,
|
|
2210
|
-
session_id: manifest.session_id,
|
|
2211
|
-
minted_at: manifest.minted_at,
|
|
2212
|
-
expires_at: manifest.expires_at,
|
|
2213
|
-
expired,
|
|
2214
|
-
project_cwd: manifest.project_cwd,
|
|
2215
|
-
host: manifest.host
|
|
2216
|
-
}
|
|
2217
|
-
});
|
|
2218
|
-
return;
|
|
2219
|
-
}
|
|
2220
2380
|
console.log();
|
|
2221
2381
|
console.log(chalk10.bold(`Impersonating: ${manifest.code_name}`));
|
|
2222
2382
|
console.log(` Agent ID: ${chalk10.dim(manifest.agent_id)}`);
|
|
@@ -2230,15 +2390,92 @@ async function impersonateCurrentCommand() {
|
|
|
2230
2390
|
console.log(` Host: ${chalk10.dim(manifest.host)}`);
|
|
2231
2391
|
console.log();
|
|
2232
2392
|
}
|
|
2233
|
-
|
|
2393
|
+
function manifestToJson(manifest) {
|
|
2394
|
+
return {
|
|
2395
|
+
code_name: manifest.code_name,
|
|
2396
|
+
agent_id: manifest.agent_id,
|
|
2397
|
+
team_id: manifest.team_id,
|
|
2398
|
+
session_id: manifest.session_id,
|
|
2399
|
+
minted_at: manifest.minted_at,
|
|
2400
|
+
expires_at: manifest.expires_at,
|
|
2401
|
+
expired: isExpired(manifest),
|
|
2402
|
+
project_cwd: manifest.project_cwd,
|
|
2403
|
+
host: manifest.host
|
|
2404
|
+
};
|
|
2405
|
+
}
|
|
2406
|
+
async function impersonateCurrentCommand(opts = {}) {
|
|
2407
|
+
requireImpersonateEnabled();
|
|
2408
|
+
const json = isJsonMode();
|
|
2409
|
+
if (opts.all) {
|
|
2410
|
+
const manifests = listActiveManifests();
|
|
2411
|
+
if (json) {
|
|
2412
|
+
jsonOutput({ ok: true, active: manifests.map(manifestToJson) });
|
|
2413
|
+
return;
|
|
2414
|
+
}
|
|
2415
|
+
if (manifests.length === 0) {
|
|
2416
|
+
info("No active impersonations.");
|
|
2417
|
+
return;
|
|
2418
|
+
}
|
|
2419
|
+
console.log();
|
|
2420
|
+
console.log(chalk10.bold(`Active impersonations (${manifests.length}):`));
|
|
2421
|
+
for (const m of manifests) printManifestText(m);
|
|
2422
|
+
return;
|
|
2423
|
+
}
|
|
2424
|
+
const manifest = readActiveManifest(process.cwd());
|
|
2425
|
+
if (!manifest) {
|
|
2426
|
+
const others = listActiveManifests();
|
|
2427
|
+
if (json) {
|
|
2428
|
+
jsonOutput({ ok: true, active: null, others_active: others.length });
|
|
2429
|
+
return;
|
|
2430
|
+
}
|
|
2431
|
+
if (others.length > 0) {
|
|
2432
|
+
info(
|
|
2433
|
+
`No active impersonation in this directory \u2014 ${others.length} active elsewhere. Run \`agt impersonate current --all\` to list them.`
|
|
2434
|
+
);
|
|
2435
|
+
} else {
|
|
2436
|
+
info("No active impersonation.");
|
|
2437
|
+
}
|
|
2438
|
+
return;
|
|
2439
|
+
}
|
|
2440
|
+
if (json) {
|
|
2441
|
+
jsonOutput({ ok: true, active: manifestToJson(manifest) });
|
|
2442
|
+
return;
|
|
2443
|
+
}
|
|
2444
|
+
printManifestText(manifest);
|
|
2445
|
+
}
|
|
2446
|
+
async function impersonateExitCommand(opts = {}) {
|
|
2234
2447
|
requireImpersonateEnabled();
|
|
2235
2448
|
const json = isJsonMode();
|
|
2236
|
-
|
|
2449
|
+
if (opts.all) {
|
|
2450
|
+
const manifests = listActiveManifests();
|
|
2451
|
+
if (manifests.length === 0) {
|
|
2452
|
+
if (json) jsonOutput({ ok: true, was_active: false, exited: [] });
|
|
2453
|
+
else info("No active impersonation to exit.");
|
|
2454
|
+
return;
|
|
2455
|
+
}
|
|
2456
|
+
const exited = [];
|
|
2457
|
+
for (const manifest2 of manifests) {
|
|
2458
|
+
await performImpersonateExit(manifest2, { json, silent: true });
|
|
2459
|
+
exited.push(manifest2.code_name);
|
|
2460
|
+
}
|
|
2461
|
+
if (json) {
|
|
2462
|
+
jsonOutput({ ok: true, was_active: true, exited });
|
|
2463
|
+
} else {
|
|
2464
|
+
success(`Exited ${exited.length} impersonation session(s): ${exited.join(", ")}`);
|
|
2465
|
+
info("Restart Claude Code to pick up your original persona.");
|
|
2466
|
+
}
|
|
2467
|
+
return;
|
|
2468
|
+
}
|
|
2469
|
+
const manifest = readActiveManifest(process.cwd());
|
|
2237
2470
|
if (!manifest) {
|
|
2238
2471
|
if (json) jsonOutput({ ok: true, was_active: false });
|
|
2239
2472
|
else info("No active impersonation to exit.");
|
|
2240
2473
|
return;
|
|
2241
2474
|
}
|
|
2475
|
+
await performImpersonateExit(manifest, { json, silent: false });
|
|
2476
|
+
}
|
|
2477
|
+
async function performImpersonateExit(manifest, opts) {
|
|
2478
|
+
const { json, silent } = opts;
|
|
2242
2479
|
let stopOk = true;
|
|
2243
2480
|
let stopDetail = null;
|
|
2244
2481
|
try {
|
|
@@ -2295,7 +2532,8 @@ async function impersonateExitCommand() {
|
|
|
2295
2532
|
if (!json) error(`Failed to remove introduce hook: ${msg}`);
|
|
2296
2533
|
}
|
|
2297
2534
|
}
|
|
2298
|
-
clearActiveManifest();
|
|
2535
|
+
clearActiveManifest(manifest.project_cwd);
|
|
2536
|
+
if (silent) return;
|
|
2299
2537
|
if (json) {
|
|
2300
2538
|
jsonOutput({
|
|
2301
2539
|
ok: true,
|
|
@@ -2316,9 +2554,32 @@ async function impersonateExitCommand() {
|
|
|
2316
2554
|
}
|
|
2317
2555
|
info("Restart Claude Code to pick up your original persona.");
|
|
2318
2556
|
}
|
|
2557
|
+
async function autoExitExpiredImpersonation() {
|
|
2558
|
+
let manifests;
|
|
2559
|
+
try {
|
|
2560
|
+
manifests = listActiveManifests();
|
|
2561
|
+
} catch {
|
|
2562
|
+
return;
|
|
2563
|
+
}
|
|
2564
|
+
const expired = manifests.filter((m) => isExpired(m));
|
|
2565
|
+
if (expired.length === 0) return;
|
|
2566
|
+
for (const manifest of expired) {
|
|
2567
|
+
try {
|
|
2568
|
+
if (!isJsonMode()) {
|
|
2569
|
+
console.error(
|
|
2570
|
+
chalk10.cyan(
|
|
2571
|
+
`\u2139 Impersonation session for ${manifest.code_name} expired \u2014 auto-exiting and restoring your files.`
|
|
2572
|
+
)
|
|
2573
|
+
);
|
|
2574
|
+
}
|
|
2575
|
+
await performImpersonateExit(manifest, { json: isJsonMode(), silent: true });
|
|
2576
|
+
} catch {
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2579
|
+
}
|
|
2319
2580
|
async function impersonateIntroduceCommand() {
|
|
2320
2581
|
try {
|
|
2321
|
-
const manifest = readActiveManifest();
|
|
2582
|
+
const manifest = readActiveManifest(process.cwd());
|
|
2322
2583
|
if (!manifest || isExpired(manifest)) return;
|
|
2323
2584
|
const cwd = manifest.project_cwd;
|
|
2324
2585
|
const identity = readClaudeMdIdentity(join10(cwd, "CLAUDE.md"));
|
|
@@ -2347,6 +2608,43 @@ function readMcpIntegrations(path) {
|
|
|
2347
2608
|
return [];
|
|
2348
2609
|
}
|
|
2349
2610
|
}
|
|
2611
|
+
function resolveCliMcpBundleDir() {
|
|
2612
|
+
const moduleDir = dirname3(fileURLToPath2(import.meta.url));
|
|
2613
|
+
const candidates = [
|
|
2614
|
+
join10(moduleDir, "mcp"),
|
|
2615
|
+
join10(moduleDir, "..", "mcp"),
|
|
2616
|
+
join10(moduleDir, "..", "..", "mcp")
|
|
2617
|
+
];
|
|
2618
|
+
for (const candidate of candidates) {
|
|
2619
|
+
if (existsSync6(join10(candidate, "index.js"))) return candidate;
|
|
2620
|
+
}
|
|
2621
|
+
return candidates[candidates.length - 1];
|
|
2622
|
+
}
|
|
2623
|
+
function resolveOperatorPath(execPath = process.execPath, inheritedPath = process.env.PATH ?? "") {
|
|
2624
|
+
const nodeBinDir = dirname3(execPath);
|
|
2625
|
+
const segments = inheritedPath.split(delimiter).filter((s) => s.length > 0);
|
|
2626
|
+
if (!segments.includes(nodeBinDir)) {
|
|
2627
|
+
segments.unshift(nodeBinDir);
|
|
2628
|
+
}
|
|
2629
|
+
return segments.join(delimiter);
|
|
2630
|
+
}
|
|
2631
|
+
function buildImpersonateClaudeLaunch(projectCwd, inheritEnv = process.env, agentId = null) {
|
|
2632
|
+
const env = {
|
|
2633
|
+
...inheritEnv,
|
|
2634
|
+
ENABLE_CLAUDEAI_MCP_SERVERS: "false"
|
|
2635
|
+
};
|
|
2636
|
+
if (agentId !== null && agentId.length > 0) {
|
|
2637
|
+
env.AGT_ACT_AS_AGENT_ID = agentId;
|
|
2638
|
+
}
|
|
2639
|
+
return {
|
|
2640
|
+
args: [
|
|
2641
|
+
"--strict-mcp-config",
|
|
2642
|
+
"--mcp-config",
|
|
2643
|
+
join10(projectCwd, ".mcp.json")
|
|
2644
|
+
],
|
|
2645
|
+
env
|
|
2646
|
+
};
|
|
2647
|
+
}
|
|
2350
2648
|
|
|
2351
2649
|
// src/commands/drift.ts
|
|
2352
2650
|
import chalk11 from "chalk";
|
|
@@ -2354,13 +2652,13 @@ import ora11 from "ora";
|
|
|
2354
2652
|
|
|
2355
2653
|
// ../../packages/core/dist/drift/live-state-reader.js
|
|
2356
2654
|
import { readFile } from "fs/promises";
|
|
2357
|
-
import { createHash } from "crypto";
|
|
2655
|
+
import { createHash as createHash2 } from "crypto";
|
|
2358
2656
|
import { join as join11 } from "path";
|
|
2359
2657
|
import JSON5 from "json5";
|
|
2360
2658
|
async function hashFile(filePath) {
|
|
2361
2659
|
try {
|
|
2362
2660
|
const content = await readFile(filePath);
|
|
2363
|
-
return
|
|
2661
|
+
return createHash2("sha256").update(content).digest("hex");
|
|
2364
2662
|
} catch {
|
|
2365
2663
|
return null;
|
|
2366
2664
|
}
|
|
@@ -2802,6 +3100,97 @@ async function hostRotateKeyCommand(hostName) {
|
|
|
2802
3100
|
process.exitCode = 1;
|
|
2803
3101
|
}
|
|
2804
3102
|
}
|
|
3103
|
+
async function hostMaintenanceWindowCommand(hostName, opts) {
|
|
3104
|
+
const teamSlug = requireTeam();
|
|
3105
|
+
if (!teamSlug) return;
|
|
3106
|
+
const json = isJsonMode();
|
|
3107
|
+
const path = `/hosts/${encodeURIComponent(hostName)}/maintenance-window`;
|
|
3108
|
+
const setting = opts.clear || opts.start !== void 0 || opts.end !== void 0 || opts.tz !== void 0;
|
|
3109
|
+
if (!setting) {
|
|
3110
|
+
const spinner2 = ora12({ text: "Fetching maintenance window\u2026", isSilent: json });
|
|
3111
|
+
spinner2.start();
|
|
3112
|
+
try {
|
|
3113
|
+
const data = await api.get(path);
|
|
3114
|
+
spinner2.stop();
|
|
3115
|
+
if (json) {
|
|
3116
|
+
jsonOutput({ ok: true, ...data });
|
|
3117
|
+
return;
|
|
3118
|
+
}
|
|
3119
|
+
const ov = data.override;
|
|
3120
|
+
const hasOverride = ov.maintenance_window_start !== null || ov.maintenance_window_end !== null || ov.maintenance_timezone !== null;
|
|
3121
|
+
info(`Host: ${chalk12.bold(hostName)}`);
|
|
3122
|
+
info(
|
|
3123
|
+
`Effective: ${chalk12.green(`${data.effective.start}\u2013${data.effective.end}`)} ${data.effective.timezone}`
|
|
3124
|
+
);
|
|
3125
|
+
info(
|
|
3126
|
+
`Override: ${hasOverride ? chalk12.cyan("set") : chalk12.dim("none (default 01:00\u201302:00)")}`
|
|
3127
|
+
);
|
|
3128
|
+
info("Set with --start/--end/--tz, or --clear to follow the default.");
|
|
3129
|
+
} catch (err) {
|
|
3130
|
+
spinner2.fail("Failed to fetch maintenance window.");
|
|
3131
|
+
if (json) jsonOutput({ ok: false, error: err.message });
|
|
3132
|
+
else error(err.message);
|
|
3133
|
+
process.exitCode = 1;
|
|
3134
|
+
}
|
|
3135
|
+
return;
|
|
3136
|
+
}
|
|
3137
|
+
if (opts.clear && (opts.start !== void 0 || opts.end !== void 0 || opts.tz !== void 0)) {
|
|
3138
|
+
const msg = "Use --clear on its own \u2014 it cannot be combined with --start/--end/--tz.";
|
|
3139
|
+
if (json) jsonOutput({ ok: false, error: msg });
|
|
3140
|
+
else error(msg);
|
|
3141
|
+
process.exitCode = 1;
|
|
3142
|
+
return;
|
|
3143
|
+
}
|
|
3144
|
+
if (!opts.clear && opts.start === void 0 !== (opts.end === void 0)) {
|
|
3145
|
+
const msg = "Provide both --start and --end (or use --clear).";
|
|
3146
|
+
if (json) jsonOutput({ ok: false, error: msg });
|
|
3147
|
+
else error(msg);
|
|
3148
|
+
process.exitCode = 1;
|
|
3149
|
+
return;
|
|
3150
|
+
}
|
|
3151
|
+
if (!opts.clear && opts.tz !== void 0 && opts.start === void 0) {
|
|
3152
|
+
const msg = "Provide --start and --end when setting --tz (or use --clear).";
|
|
3153
|
+
if (json) jsonOutput({ ok: false, error: msg });
|
|
3154
|
+
else error(msg);
|
|
3155
|
+
process.exitCode = 1;
|
|
3156
|
+
return;
|
|
3157
|
+
}
|
|
3158
|
+
let resolvedTz = opts.tz ?? null;
|
|
3159
|
+
if (!opts.clear && opts.tz === void 0) {
|
|
3160
|
+
try {
|
|
3161
|
+
const cur = await api.get(path);
|
|
3162
|
+
resolvedTz = cur.override.maintenance_timezone;
|
|
3163
|
+
} catch (err) {
|
|
3164
|
+
const spinner0 = ora12({ isSilent: json });
|
|
3165
|
+
spinner0.fail("Failed to read current maintenance window.");
|
|
3166
|
+
if (json) jsonOutput({ ok: false, error: err.message });
|
|
3167
|
+
else error(err.message);
|
|
3168
|
+
process.exitCode = 1;
|
|
3169
|
+
return;
|
|
3170
|
+
}
|
|
3171
|
+
}
|
|
3172
|
+
const payload = opts.clear ? { maintenance_window_start: null, maintenance_window_end: null, maintenance_timezone: null } : {
|
|
3173
|
+
maintenance_window_start: opts.start ?? null,
|
|
3174
|
+
maintenance_window_end: opts.end ?? null,
|
|
3175
|
+
maintenance_timezone: resolvedTz
|
|
3176
|
+
};
|
|
3177
|
+
const spinner = ora12({ text: "Updating maintenance window\u2026", isSilent: json });
|
|
3178
|
+
spinner.start();
|
|
3179
|
+
try {
|
|
3180
|
+
const data = await api.post(path, payload);
|
|
3181
|
+
spinner.succeed(`Maintenance window updated for ${chalk12.bold(hostName)}.`);
|
|
3182
|
+
if (json) {
|
|
3183
|
+
jsonOutput({ ok: true, ...data });
|
|
3184
|
+
return;
|
|
3185
|
+
}
|
|
3186
|
+
if (data.note) info(data.note);
|
|
3187
|
+
} catch (err) {
|
|
3188
|
+
spinner.fail("Failed to update maintenance window.");
|
|
3189
|
+
if (json) jsonOutput({ ok: false, error: err.message });
|
|
3190
|
+
else error(err.message);
|
|
3191
|
+
process.exitCode = 1;
|
|
3192
|
+
}
|
|
3193
|
+
}
|
|
2805
3194
|
async function hostDecommissionCommand(hostName) {
|
|
2806
3195
|
const teamSlug = requireTeam();
|
|
2807
3196
|
if (!teamSlug) return;
|
|
@@ -2989,7 +3378,7 @@ function terminate(child) {
|
|
|
2989
3378
|
// src/commands/manager-watch.tsx
|
|
2990
3379
|
import { useEffect, useState, useMemo } from "react";
|
|
2991
3380
|
import { render, Box, Text, useApp, useInput } from "ink";
|
|
2992
|
-
import { existsSync as
|
|
3381
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6, statSync, openSync, readSync, closeSync } from "fs";
|
|
2993
3382
|
import { homedir as homedir4 } from "os";
|
|
2994
3383
|
import { join as join12 } from "path";
|
|
2995
3384
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -3021,7 +3410,7 @@ function managerWatchCommand(opts = {}) {
|
|
|
3021
3410
|
}
|
|
3022
3411
|
function readState(stateFile) {
|
|
3023
3412
|
try {
|
|
3024
|
-
if (!
|
|
3413
|
+
if (!existsSync7(stateFile)) return null;
|
|
3025
3414
|
const raw = readFileSync6(stateFile, "utf-8");
|
|
3026
3415
|
return JSON.parse(raw);
|
|
3027
3416
|
} catch {
|
|
@@ -3029,7 +3418,7 @@ function readState(stateFile) {
|
|
|
3029
3418
|
}
|
|
3030
3419
|
}
|
|
3031
3420
|
function tailLogFile(logFile, lines) {
|
|
3032
|
-
if (!
|
|
3421
|
+
if (!existsSync7(logFile)) return [];
|
|
3033
3422
|
try {
|
|
3034
3423
|
const fileSize = statSync(logFile).size;
|
|
3035
3424
|
if (fileSize === 0) return [];
|
|
@@ -3270,7 +3659,7 @@ function truncate(s, max) {
|
|
|
3270
3659
|
return s.length > max ? `${s.slice(0, Math.max(0, max - 1))}\u2026` : s;
|
|
3271
3660
|
}
|
|
3272
3661
|
function streamLogFile(logFile) {
|
|
3273
|
-
if (!
|
|
3662
|
+
if (!existsSync7(logFile)) {
|
|
3274
3663
|
process.stderr.write(`No manager log found at ${logFile}.
|
|
3275
3664
|
Start the manager first: agt manager start
|
|
3276
3665
|
`);
|
|
@@ -3316,14 +3705,14 @@ Start the manager first: agt manager start
|
|
|
3316
3705
|
// src/commands/agent.ts
|
|
3317
3706
|
import chalk14 from "chalk";
|
|
3318
3707
|
import JSON52 from "json5";
|
|
3319
|
-
import { readFileSync as readFileSync7, existsSync as
|
|
3708
|
+
import { readFileSync as readFileSync7, existsSync as existsSync8 } from "fs";
|
|
3320
3709
|
import { join as join13 } from "path";
|
|
3321
3710
|
async function agentShowCommand(codeName, opts) {
|
|
3322
3711
|
const json = isJsonMode();
|
|
3323
3712
|
const unifiedDir = join13(opts.configDir, codeName, "provision");
|
|
3324
3713
|
const legacyDir = join13(opts.configDir, codeName, "claudecode", "provision");
|
|
3325
|
-
const agentDir =
|
|
3326
|
-
const hasLocalConfig =
|
|
3714
|
+
const agentDir = existsSync8(unifiedDir) ? unifiedDir : legacyDir;
|
|
3715
|
+
const hasLocalConfig = existsSync8(agentDir);
|
|
3327
3716
|
let apiChannels = null;
|
|
3328
3717
|
let apiAgent = null;
|
|
3329
3718
|
if (getApiKey()) {
|
|
@@ -3357,7 +3746,7 @@ async function agentShowCommand(codeName, opts) {
|
|
|
3357
3746
|
let agentState = null;
|
|
3358
3747
|
if (hasLocalConfig) {
|
|
3359
3748
|
const charterPath = join13(agentDir, "CHARTER.md");
|
|
3360
|
-
if (
|
|
3749
|
+
if (existsSync8(charterPath)) {
|
|
3361
3750
|
const raw = readFileSync7(charterPath, "utf-8");
|
|
3362
3751
|
const parsed = extractFrontmatter(raw);
|
|
3363
3752
|
if (parsed.frontmatter) {
|
|
@@ -3365,7 +3754,7 @@ async function agentShowCommand(codeName, opts) {
|
|
|
3365
3754
|
}
|
|
3366
3755
|
}
|
|
3367
3756
|
const toolsPath = join13(agentDir, "TOOLS.md");
|
|
3368
|
-
if (
|
|
3757
|
+
if (existsSync8(toolsPath)) {
|
|
3369
3758
|
const raw = readFileSync7(toolsPath, "utf-8");
|
|
3370
3759
|
const parsed = extractFrontmatter(raw);
|
|
3371
3760
|
if (parsed.frontmatter) {
|
|
@@ -3373,7 +3762,7 @@ async function agentShowCommand(codeName, opts) {
|
|
|
3373
3762
|
}
|
|
3374
3763
|
}
|
|
3375
3764
|
const openclawPath = join13(agentDir, "openclaw.json5");
|
|
3376
|
-
if (
|
|
3765
|
+
if (existsSync8(openclawPath)) {
|
|
3377
3766
|
try {
|
|
3378
3767
|
const raw = readFileSync7(openclawPath, "utf-8");
|
|
3379
3768
|
openclawConfig = JSON52.parse(raw);
|
|
@@ -3381,7 +3770,7 @@ async function agentShowCommand(codeName, opts) {
|
|
|
3381
3770
|
}
|
|
3382
3771
|
}
|
|
3383
3772
|
const statePath = join13(opts.configDir, "manager-state.json");
|
|
3384
|
-
if (
|
|
3773
|
+
if (existsSync8(statePath)) {
|
|
3385
3774
|
try {
|
|
3386
3775
|
const raw = readFileSync7(statePath, "utf-8");
|
|
3387
3776
|
const state = JSON.parse(raw);
|
|
@@ -3720,8 +4109,8 @@ async function kanbanRecurringDisableCommand(titleOrId, opts) {
|
|
|
3720
4109
|
}
|
|
3721
4110
|
|
|
3722
4111
|
// src/commands/setup.ts
|
|
3723
|
-
import { existsSync as
|
|
3724
|
-
import { join as join14, dirname as
|
|
4112
|
+
import { existsSync as existsSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync8, mkdirSync as mkdirSync7, accessSync, constants as fsConstants } from "fs";
|
|
4113
|
+
import { join as join14, dirname as dirname4 } from "path";
|
|
3725
4114
|
import { homedir as homedir5 } from "os";
|
|
3726
4115
|
import chalk16 from "chalk";
|
|
3727
4116
|
import ora14 from "ora";
|
|
@@ -3736,7 +4125,7 @@ function detectShellProfile() {
|
|
|
3736
4125
|
return fishConfig;
|
|
3737
4126
|
}
|
|
3738
4127
|
const bashrc = join14(home, ".bashrc");
|
|
3739
|
-
if (
|
|
4128
|
+
if (existsSync9(bashrc)) return bashrc;
|
|
3740
4129
|
return join14(home, ".bash_profile");
|
|
3741
4130
|
}
|
|
3742
4131
|
function maybeWriteSystemWideEnv(apiUrl, apiKey, consoleUrl) {
|
|
@@ -3763,7 +4152,7 @@ function quoteForFishShell(value) {
|
|
|
3763
4152
|
}
|
|
3764
4153
|
function writeEtcEnvironment(apiUrl, apiKey, consoleUrl) {
|
|
3765
4154
|
const envPath = "/etc/environment";
|
|
3766
|
-
if (!
|
|
4155
|
+
if (!existsSync9(envPath)) return false;
|
|
3767
4156
|
try {
|
|
3768
4157
|
accessSync(envPath, fsConstants.W_OK);
|
|
3769
4158
|
} catch {
|
|
@@ -3789,7 +4178,7 @@ function writeEtcEnvironment(apiUrl, apiKey, consoleUrl) {
|
|
|
3789
4178
|
}
|
|
3790
4179
|
function writeProfileDScript(apiUrl, apiKey, consoleUrl) {
|
|
3791
4180
|
const profileD = "/etc/profile.d";
|
|
3792
|
-
if (!
|
|
4181
|
+
if (!existsSync9(profileD)) return false;
|
|
3793
4182
|
try {
|
|
3794
4183
|
accessSync(profileD, fsConstants.W_OK);
|
|
3795
4184
|
} catch {
|
|
@@ -3812,7 +4201,7 @@ function writeProfileDScript(apiUrl, apiKey, consoleUrl) {
|
|
|
3812
4201
|
}
|
|
3813
4202
|
function ensureBashrcSourcesProfileD() {
|
|
3814
4203
|
const bashrc = "/etc/bashrc";
|
|
3815
|
-
if (!
|
|
4204
|
+
if (!existsSync9(bashrc)) return false;
|
|
3816
4205
|
try {
|
|
3817
4206
|
accessSync(bashrc, fsConstants.W_OK);
|
|
3818
4207
|
} catch {
|
|
@@ -3858,7 +4247,7 @@ function buildExportLines(shell, apiUrl, apiKey, consoleUrl) {
|
|
|
3858
4247
|
lines.push("");
|
|
3859
4248
|
return lines.join("\n");
|
|
3860
4249
|
}
|
|
3861
|
-
async function setupCommand(token) {
|
|
4250
|
+
async function setupCommand(token, options = {}) {
|
|
3862
4251
|
const json = isJsonMode();
|
|
3863
4252
|
const shortTokenPattern = /^[A-HJ-NP-Z2-9]{4}-[A-HJ-NP-Z2-9]{4}$/i;
|
|
3864
4253
|
const legacyTokenPattern = /^prov_[a-f0-9]{64}$/i;
|
|
@@ -3873,11 +4262,24 @@ async function setupCommand(token) {
|
|
|
3873
4262
|
return;
|
|
3874
4263
|
}
|
|
3875
4264
|
const normalizedToken = shortTokenPattern.test(token) ? token.toUpperCase() : token.toLowerCase();
|
|
3876
|
-
|
|
4265
|
+
const apiHostFlag = options.apiHost?.trim();
|
|
4266
|
+
const envHost = process.env["AGT_HOST"]?.trim();
|
|
4267
|
+
const apiUrl = apiHostFlag || envHost;
|
|
3877
4268
|
if (!apiUrl) {
|
|
3878
|
-
|
|
3879
|
-
if (
|
|
3880
|
-
|
|
4269
|
+
const msg = "Cannot determine API host for token exchange. Pass --api-host <url> or set AGT_HOST before running `agt setup`.\n Production: --api-host https://api.augmented.team\n Non-prod stage: --api-host https://<stage>.api.staging.augmented.team\n Local development: --api-host http://api.agt.localhost:1355";
|
|
4270
|
+
if (json) {
|
|
4271
|
+
jsonOutput({ ok: false, error: msg });
|
|
4272
|
+
} else {
|
|
4273
|
+
error(msg);
|
|
4274
|
+
}
|
|
4275
|
+
process.exitCode = 1;
|
|
4276
|
+
return;
|
|
4277
|
+
}
|
|
4278
|
+
if (!json) {
|
|
4279
|
+
if (apiHostFlag) {
|
|
4280
|
+
info(`Exchanging token against ${chalk16.bold(apiUrl)} (from --api-host)`);
|
|
4281
|
+
} else {
|
|
4282
|
+
info(`Exchanging token against ${chalk16.bold(apiUrl)} (from AGT_HOST)`);
|
|
3881
4283
|
}
|
|
3882
4284
|
}
|
|
3883
4285
|
const spinner = ora14({ text: "Exchanging provisioning token\u2026", isSilent: json });
|
|
@@ -3945,12 +4347,12 @@ async function setupCommand(token) {
|
|
|
3945
4347
|
);
|
|
3946
4348
|
}
|
|
3947
4349
|
const exportLines = buildExportLines(shell, finalApiUrl, setupResult.api_key, consoleUrl);
|
|
3948
|
-
const profileDir =
|
|
3949
|
-
if (!
|
|
4350
|
+
const profileDir = dirname4(profilePath);
|
|
4351
|
+
if (!existsSync9(profileDir)) {
|
|
3950
4352
|
mkdirSync7(profileDir, { recursive: true });
|
|
3951
4353
|
}
|
|
3952
4354
|
const marker = "# Augmented (agt) host configuration";
|
|
3953
|
-
const current =
|
|
4355
|
+
const current = existsSync9(profilePath) ? readFileSync8(profilePath, "utf-8") : "";
|
|
3954
4356
|
let updated;
|
|
3955
4357
|
if (current.includes(marker)) {
|
|
3956
4358
|
updated = current.replace(
|
|
@@ -4529,10 +4931,10 @@ async function acpxCloseCommand(agent2, _opts, cmd) {
|
|
|
4529
4931
|
|
|
4530
4932
|
// src/commands/update.ts
|
|
4531
4933
|
import { execFileSync, execSync } from "child_process";
|
|
4532
|
-
import { existsSync as
|
|
4934
|
+
import { existsSync as existsSync10, realpathSync as realpathSync2 } from "fs";
|
|
4533
4935
|
import chalk18 from "chalk";
|
|
4534
4936
|
import ora16 from "ora";
|
|
4535
|
-
var cliVersion = true ? "0.27.
|
|
4937
|
+
var cliVersion = true ? "0.27.9-test.12" : "dev";
|
|
4536
4938
|
async function fetchLatestVersion() {
|
|
4537
4939
|
const host2 = getHost();
|
|
4538
4940
|
if (!host2) return null;
|
|
@@ -4636,7 +5038,7 @@ function performUpdate(version) {
|
|
|
4636
5038
|
function detectBrewOwner() {
|
|
4637
5039
|
for (const prefix of ["/home/linuxbrew/.linuxbrew", "/opt/homebrew", "/usr/local"]) {
|
|
4638
5040
|
const cellar = `${prefix}/Cellar`;
|
|
4639
|
-
if (!
|
|
5041
|
+
if (!existsSync10(cellar)) continue;
|
|
4640
5042
|
try {
|
|
4641
5043
|
return execSync(`stat -c %U "${cellar}" 2>/dev/null || stat -f %Su "${cellar}"`, { encoding: "utf-8" }).trim() || null;
|
|
4642
5044
|
} catch {
|
|
@@ -4763,6 +5165,397 @@ async function checkForUpdateOnStartup() {
|
|
|
4763
5165
|
}
|
|
4764
5166
|
}
|
|
4765
5167
|
|
|
5168
|
+
// src/commands/audit-subagents.ts
|
|
5169
|
+
import { homedir as homedir6 } from "os";
|
|
5170
|
+
import { join as join16 } from "path";
|
|
5171
|
+
|
|
5172
|
+
// src/lib/subagent-mcp-audit.ts
|
|
5173
|
+
import { readdirSync as readdirSync4, readFileSync as readFileSync9, statSync as statSync2 } from "fs";
|
|
5174
|
+
import { join as join15 } from "path";
|
|
5175
|
+
function parseFrontmatter(content) {
|
|
5176
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
5177
|
+
if (!match) return null;
|
|
5178
|
+
const out = {};
|
|
5179
|
+
const lines = match[1].split(/\r?\n/);
|
|
5180
|
+
for (let i = 0; i < lines.length; i++) {
|
|
5181
|
+
const line = lines[i];
|
|
5182
|
+
const m = line.match(/^(name|tools|background):\s*(.*)$/);
|
|
5183
|
+
if (!m) continue;
|
|
5184
|
+
const [, key, rawValue] = m;
|
|
5185
|
+
const trimmed = rawValue.trim();
|
|
5186
|
+
if (key === "tools" && trimmed === "") {
|
|
5187
|
+
const items = [];
|
|
5188
|
+
let j = i + 1;
|
|
5189
|
+
while (j < lines.length) {
|
|
5190
|
+
const nextRaw = lines[j];
|
|
5191
|
+
const itemMatch = nextRaw.match(/^(\s+)-\s*(.+?)\s*$/);
|
|
5192
|
+
if (!itemMatch) break;
|
|
5193
|
+
items.push(itemMatch[2].replace(/^['"](.*)['"]$/, "$1"));
|
|
5194
|
+
j++;
|
|
5195
|
+
}
|
|
5196
|
+
if (items.length > 0) {
|
|
5197
|
+
out.tools = items.join(", ");
|
|
5198
|
+
i = j - 1;
|
|
5199
|
+
continue;
|
|
5200
|
+
}
|
|
5201
|
+
}
|
|
5202
|
+
out[key] = trimmed;
|
|
5203
|
+
}
|
|
5204
|
+
return out;
|
|
5205
|
+
}
|
|
5206
|
+
function toolsExcludeMcp(tools) {
|
|
5207
|
+
if (tools.length === 0) return false;
|
|
5208
|
+
if (/\bmcp__/.test(tools)) return false;
|
|
5209
|
+
if (/\*/.test(tools)) return false;
|
|
5210
|
+
if (/\bAll tools\b/i.test(tools)) return false;
|
|
5211
|
+
return true;
|
|
5212
|
+
}
|
|
5213
|
+
function findSubagentFiles(dir, depth = 0) {
|
|
5214
|
+
if (depth > 6) return [];
|
|
5215
|
+
let entries;
|
|
5216
|
+
try {
|
|
5217
|
+
entries = readdirSync4(dir);
|
|
5218
|
+
} catch {
|
|
5219
|
+
return [];
|
|
5220
|
+
}
|
|
5221
|
+
const out = [];
|
|
5222
|
+
const isAgentsDir = dir.endsWith("/agents") || dir.endsWith("\\agents");
|
|
5223
|
+
for (const entry of entries) {
|
|
5224
|
+
const full = join15(dir, entry);
|
|
5225
|
+
let s;
|
|
5226
|
+
try {
|
|
5227
|
+
s = statSync2(full);
|
|
5228
|
+
} catch {
|
|
5229
|
+
continue;
|
|
5230
|
+
}
|
|
5231
|
+
if (s.isDirectory()) {
|
|
5232
|
+
out.push(...findSubagentFiles(full, depth + 1));
|
|
5233
|
+
} else if (s.isFile() && isAgentsDir && entry.endsWith(".md")) {
|
|
5234
|
+
out.push(full);
|
|
5235
|
+
}
|
|
5236
|
+
}
|
|
5237
|
+
return out;
|
|
5238
|
+
}
|
|
5239
|
+
function auditSubagentMcpBindings(rootDirs) {
|
|
5240
|
+
const findings = [];
|
|
5241
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5242
|
+
for (const root of rootDirs) {
|
|
5243
|
+
for (const file of findSubagentFiles(root)) {
|
|
5244
|
+
if (seen.has(file)) continue;
|
|
5245
|
+
seen.add(file);
|
|
5246
|
+
let content;
|
|
5247
|
+
try {
|
|
5248
|
+
content = readFileSync9(file, "utf-8");
|
|
5249
|
+
} catch {
|
|
5250
|
+
continue;
|
|
5251
|
+
}
|
|
5252
|
+
const fm = parseFrontmatter(content);
|
|
5253
|
+
if (!fm || typeof fm.tools !== "string") continue;
|
|
5254
|
+
if (!toolsExcludeMcp(fm.tools)) continue;
|
|
5255
|
+
findings.push({
|
|
5256
|
+
file,
|
|
5257
|
+
name: fm.name ?? "<unknown>",
|
|
5258
|
+
tools: fm.tools,
|
|
5259
|
+
reason: "restrictive-tools-no-mcp"
|
|
5260
|
+
});
|
|
5261
|
+
}
|
|
5262
|
+
}
|
|
5263
|
+
return findings;
|
|
5264
|
+
}
|
|
5265
|
+
function formatFinding(f) {
|
|
5266
|
+
return `[manager-worker] [subagent-mcp-audit] file=${f.file} name=${f.name} reason=${f.reason} tools=${JSON.stringify(f.tools)}`;
|
|
5267
|
+
}
|
|
5268
|
+
|
|
5269
|
+
// src/commands/audit-subagents.ts
|
|
5270
|
+
function defaultRoots() {
|
|
5271
|
+
const home = homedir6();
|
|
5272
|
+
return [
|
|
5273
|
+
join16(home, ".claude", "agents"),
|
|
5274
|
+
join16(home, ".claude", "plugins"),
|
|
5275
|
+
join16(process.cwd(), ".claude", "agents")
|
|
5276
|
+
];
|
|
5277
|
+
}
|
|
5278
|
+
async function auditSubagentsCommand(options = {}) {
|
|
5279
|
+
const roots = defaultRoots();
|
|
5280
|
+
const findings = auditSubagentMcpBindings(roots);
|
|
5281
|
+
if (options.json) {
|
|
5282
|
+
process.stdout.write(`${JSON.stringify({ findings, roots }, null, 2)}
|
|
5283
|
+
`);
|
|
5284
|
+
} else if (findings.length === 0) {
|
|
5285
|
+
process.stdout.write("No restrictive sub-agents found \u2014 every sub-agent under the scanned roots either inherits the parent toolset or explicitly includes mcp__* wildcards.\n");
|
|
5286
|
+
} else {
|
|
5287
|
+
process.stdout.write(`Found ${findings.length} sub-agent${findings.length === 1 ? "" : "s"} whose tools allowlist excludes mcp__*:
|
|
5288
|
+
|
|
5289
|
+
`);
|
|
5290
|
+
for (const f of findings) {
|
|
5291
|
+
process.stdout.write(` \u2022 ${f.name} (${f.file})
|
|
5292
|
+
`);
|
|
5293
|
+
process.stdout.write(` tools: ${f.tools}
|
|
5294
|
+
`);
|
|
5295
|
+
}
|
|
5296
|
+
process.stdout.write(
|
|
5297
|
+
"\nThese sub-agents will return \"No such tool available.\" for every mcp__* call when dispatched from a managed agent. If the parent needs to do MCP-tool work, dispatch the Augmented plugin's `augmented-worker` sub-agent instead \u2014 it omits `tools:` so it inherits the parent's full toolset including every MCP server. See ENG-5897.\n"
|
|
5298
|
+
);
|
|
5299
|
+
for (const f of findings) {
|
|
5300
|
+
process.stderr.write(`${formatFinding(f)}
|
|
5301
|
+
`);
|
|
5302
|
+
}
|
|
5303
|
+
}
|
|
5304
|
+
if (options.strict && findings.length > 0) {
|
|
5305
|
+
process.exit(2);
|
|
5306
|
+
}
|
|
5307
|
+
}
|
|
5308
|
+
|
|
5309
|
+
// src/lib/mcp-render-allowlist-audit.ts
|
|
5310
|
+
import { readdirSync as readdirSync5, readFileSync as readFileSync10, statSync as statSync3 } from "fs";
|
|
5311
|
+
import { homedir as homedir7 } from "os";
|
|
5312
|
+
import { join as join17 } from "path";
|
|
5313
|
+
function expectedWildcard(serverKey) {
|
|
5314
|
+
return `mcp__${serverKey.replace(/-/g, "_")}__*`;
|
|
5315
|
+
}
|
|
5316
|
+
function parseMcpWildcardsFromToolsLine(content) {
|
|
5317
|
+
const toolsLine = content.split("\n").find((l) => l.startsWith("tools:"));
|
|
5318
|
+
if (!toolsLine) return null;
|
|
5319
|
+
const value = toolsLine.replace(/^tools:\s*/, "").trim();
|
|
5320
|
+
if (value.length === 0) return [];
|
|
5321
|
+
return value.split(/\s*,\s*/).map((t) => t.trim()).filter((t) => t.startsWith("mcp__"));
|
|
5322
|
+
}
|
|
5323
|
+
function readMcpServerKeys(mcpJsonPath) {
|
|
5324
|
+
try {
|
|
5325
|
+
const config = JSON.parse(readFileSync10(mcpJsonPath, "utf-8"));
|
|
5326
|
+
return Object.keys(config.mcpServers ?? {});
|
|
5327
|
+
} catch {
|
|
5328
|
+
return null;
|
|
5329
|
+
}
|
|
5330
|
+
}
|
|
5331
|
+
function auditMcpRenderAllowlist(rootDir = join17(homedir7(), ".augmented")) {
|
|
5332
|
+
const findings = [];
|
|
5333
|
+
let entries;
|
|
5334
|
+
try {
|
|
5335
|
+
entries = readdirSync5(rootDir);
|
|
5336
|
+
} catch {
|
|
5337
|
+
return findings;
|
|
5338
|
+
}
|
|
5339
|
+
for (const entry of entries) {
|
|
5340
|
+
const agentDir = join17(rootDir, entry);
|
|
5341
|
+
let s;
|
|
5342
|
+
try {
|
|
5343
|
+
s = statSync3(agentDir);
|
|
5344
|
+
} catch {
|
|
5345
|
+
continue;
|
|
5346
|
+
}
|
|
5347
|
+
if (!s.isDirectory() || entry.startsWith("_") || entry.startsWith(".")) continue;
|
|
5348
|
+
for (const scope of ["provision", "project"]) {
|
|
5349
|
+
const mcpJsonPath = scope === "provision" ? join17(agentDir, "provision", ".mcp.json") : join17(agentDir, "project", ".mcp.json");
|
|
5350
|
+
const keys = readMcpServerKeys(mcpJsonPath);
|
|
5351
|
+
if (keys === null || keys.length === 0) continue;
|
|
5352
|
+
const expected = new Set(keys.map(expectedWildcard));
|
|
5353
|
+
for (const subagent of ["channel-message-handler", "augmented-worker"]) {
|
|
5354
|
+
const mdPath = scope === "provision" ? join17(agentDir, ".claude", "agents", `${subagent}.md`) : join17(agentDir, "project", ".claude", "agents", `${subagent}.md`);
|
|
5355
|
+
let content;
|
|
5356
|
+
try {
|
|
5357
|
+
content = readFileSync10(mdPath, "utf-8");
|
|
5358
|
+
} catch {
|
|
5359
|
+
continue;
|
|
5360
|
+
}
|
|
5361
|
+
const declared = parseMcpWildcardsFromToolsLine(content);
|
|
5362
|
+
if (declared === null) continue;
|
|
5363
|
+
const declaredSet = new Set(declared);
|
|
5364
|
+
const missing = [...expected].filter((w) => !declaredSet.has(w));
|
|
5365
|
+
if (missing.length > 0) {
|
|
5366
|
+
findings.push({ agent: entry, subagent, scope, missing, file: mdPath });
|
|
5367
|
+
}
|
|
5368
|
+
}
|
|
5369
|
+
}
|
|
5370
|
+
}
|
|
5371
|
+
return findings;
|
|
5372
|
+
}
|
|
5373
|
+
function formatRenderDriftFinding(f) {
|
|
5374
|
+
return `[manager-worker] [mcp-render-drift] agent=${f.agent} subagent=${f.subagent} scope=${f.scope} missing=${JSON.stringify(f.missing)} file=${f.file}`;
|
|
5375
|
+
}
|
|
5376
|
+
|
|
5377
|
+
// src/commands/audit-mcp-render.ts
|
|
5378
|
+
async function auditMcpRenderCommand(options = {}) {
|
|
5379
|
+
const findings = auditMcpRenderAllowlist();
|
|
5380
|
+
if (options.json) {
|
|
5381
|
+
process.stdout.write(`${JSON.stringify({ findings }, null, 2)}
|
|
5382
|
+
`);
|
|
5383
|
+
} else if (findings.length === 0) {
|
|
5384
|
+
process.stdout.write(
|
|
5385
|
+
"No render drift found. Every managed agent's sub-agent .md `tools:` allowlist covers every server in its `.mcp.json`.\n"
|
|
5386
|
+
);
|
|
5387
|
+
} else {
|
|
5388
|
+
process.stdout.write(
|
|
5389
|
+
`Found ${findings.length} sub-agent .md file${findings.length === 1 ? "" : "s"} with a stale tools allowlist:
|
|
5390
|
+
|
|
5391
|
+
`
|
|
5392
|
+
);
|
|
5393
|
+
for (const f of findings) {
|
|
5394
|
+
process.stdout.write(` \u2022 ${f.agent}/${f.scope}/${f.subagent}.md
|
|
5395
|
+
`);
|
|
5396
|
+
process.stdout.write(` missing: ${f.missing.join(", ")}
|
|
5397
|
+
`);
|
|
5398
|
+
process.stdout.write(` file: ${f.file}
|
|
5399
|
+
`);
|
|
5400
|
+
}
|
|
5401
|
+
process.stdout.write(
|
|
5402
|
+
'\nA missing wildcard means the sub-agent will get "No such tool available." on any call to that MCP server. The fix is to force a re-render (e.g. by adding/removing any MCP server, which routes through syncMcpToProject), OR restart the manager so the next poll picks up the live `.mcp.json` state. The root-cause write path that bypassed the render chokepoint is tracked in ENG-5922.\n'
|
|
5403
|
+
);
|
|
5404
|
+
for (const f of findings) {
|
|
5405
|
+
process.stderr.write(`${formatRenderDriftFinding(f)}
|
|
5406
|
+
`);
|
|
5407
|
+
}
|
|
5408
|
+
}
|
|
5409
|
+
if (options.strict && findings.length > 0) {
|
|
5410
|
+
process.exit(2);
|
|
5411
|
+
}
|
|
5412
|
+
}
|
|
5413
|
+
|
|
5414
|
+
// src/commands/audit-secrets.ts
|
|
5415
|
+
import { homedir as homedir8 } from "os";
|
|
5416
|
+
import { join as join19 } from "path";
|
|
5417
|
+
|
|
5418
|
+
// src/lib/secret-leak-audit.ts
|
|
5419
|
+
import { readdirSync as readdirSync6, readFileSync as readFileSync11, statSync as statSync4 } from "fs";
|
|
5420
|
+
import { join as join18 } from "path";
|
|
5421
|
+
var SECRET_KEY_NAME_RE = /TOKEN|KEY|SECRET|BEARER|PASSWORD/i;
|
|
5422
|
+
function isTemplated(value) {
|
|
5423
|
+
return value.includes("${");
|
|
5424
|
+
}
|
|
5425
|
+
function matchesKnownSecretShape(value) {
|
|
5426
|
+
return LITERAL_SECRET_PATTERNS.some(({ re }) => re.test(value));
|
|
5427
|
+
}
|
|
5428
|
+
function isWorldReadable(mode) {
|
|
5429
|
+
return (mode & 63) !== 0;
|
|
5430
|
+
}
|
|
5431
|
+
function octal(mode) {
|
|
5432
|
+
return `0${(mode & 511).toString(8)}`;
|
|
5433
|
+
}
|
|
5434
|
+
function scanMcpFile(file, agent2, findings) {
|
|
5435
|
+
let mode = null;
|
|
5436
|
+
try {
|
|
5437
|
+
mode = statSync4(file).mode;
|
|
5438
|
+
} catch {
|
|
5439
|
+
return;
|
|
5440
|
+
}
|
|
5441
|
+
if (mode !== null && isWorldReadable(mode)) {
|
|
5442
|
+
findings.push({ kind: "world-readable-secret-file", file, agent: agent2, mode: octal(mode) });
|
|
5443
|
+
}
|
|
5444
|
+
let parsed;
|
|
5445
|
+
try {
|
|
5446
|
+
parsed = JSON.parse(readFileSync11(file, "utf-8"));
|
|
5447
|
+
} catch {
|
|
5448
|
+
return;
|
|
5449
|
+
}
|
|
5450
|
+
const servers = parsed?.mcpServers;
|
|
5451
|
+
if (typeof servers !== "object" || servers === null) return;
|
|
5452
|
+
for (const [server, raw] of Object.entries(servers)) {
|
|
5453
|
+
if (typeof raw !== "object" || raw === null) continue;
|
|
5454
|
+
const entry = raw;
|
|
5455
|
+
const blocks = [
|
|
5456
|
+
[entry.env, "env"],
|
|
5457
|
+
[entry.headers, "header"]
|
|
5458
|
+
];
|
|
5459
|
+
for (const [block, location] of blocks) {
|
|
5460
|
+
if (!block) continue;
|
|
5461
|
+
for (const [field, value] of Object.entries(block)) {
|
|
5462
|
+
if (typeof value !== "string" || value.length === 0) continue;
|
|
5463
|
+
if (isTemplated(value)) continue;
|
|
5464
|
+
if (!SECRET_KEY_NAME_RE.test(field) && !matchesKnownSecretShape(value)) continue;
|
|
5465
|
+
findings.push({ kind: "literal-secret-in-mcp", file, agent: agent2, server, field, location });
|
|
5466
|
+
}
|
|
5467
|
+
}
|
|
5468
|
+
}
|
|
5469
|
+
}
|
|
5470
|
+
function scanEnvIntegrations(file, agent2, findings) {
|
|
5471
|
+
let mode;
|
|
5472
|
+
try {
|
|
5473
|
+
mode = statSync4(file).mode;
|
|
5474
|
+
} catch {
|
|
5475
|
+
return;
|
|
5476
|
+
}
|
|
5477
|
+
if (isWorldReadable(mode)) {
|
|
5478
|
+
findings.push({ kind: "world-readable-secret-file", file, agent: agent2, mode: octal(mode) });
|
|
5479
|
+
}
|
|
5480
|
+
}
|
|
5481
|
+
function auditSecretLeaks(augmentedRoot) {
|
|
5482
|
+
const findings = [];
|
|
5483
|
+
let agents;
|
|
5484
|
+
try {
|
|
5485
|
+
agents = readdirSync6(augmentedRoot);
|
|
5486
|
+
} catch {
|
|
5487
|
+
return findings;
|
|
5488
|
+
}
|
|
5489
|
+
for (const agent2 of agents) {
|
|
5490
|
+
const agentDir = join18(augmentedRoot, agent2);
|
|
5491
|
+
try {
|
|
5492
|
+
if (!statSync4(agentDir).isDirectory()) continue;
|
|
5493
|
+
} catch {
|
|
5494
|
+
continue;
|
|
5495
|
+
}
|
|
5496
|
+
scanMcpFile(join18(agentDir, "provision", ".mcp.json"), agent2, findings);
|
|
5497
|
+
scanMcpFile(join18(agentDir, "project", ".mcp.json"), agent2, findings);
|
|
5498
|
+
scanEnvIntegrations(join18(agentDir, ".env.integrations"), agent2, findings);
|
|
5499
|
+
}
|
|
5500
|
+
return findings;
|
|
5501
|
+
}
|
|
5502
|
+
function formatSecretFinding(f) {
|
|
5503
|
+
const parts = [
|
|
5504
|
+
"[audit-secrets]",
|
|
5505
|
+
`kind=${f.kind}`,
|
|
5506
|
+
`agent=${f.agent}`,
|
|
5507
|
+
`file=${f.file}`
|
|
5508
|
+
];
|
|
5509
|
+
if (f.server) parts.push(`server=${f.server}`);
|
|
5510
|
+
if (f.field) parts.push(`field=${f.field}`);
|
|
5511
|
+
if (f.location) parts.push(`location=${f.location}`);
|
|
5512
|
+
if (f.mode) parts.push(`mode=${f.mode}`);
|
|
5513
|
+
return parts.join(" ");
|
|
5514
|
+
}
|
|
5515
|
+
|
|
5516
|
+
// src/commands/audit-secrets.ts
|
|
5517
|
+
function describe(f) {
|
|
5518
|
+
if (f.kind === "literal-secret-in-mcp") {
|
|
5519
|
+
return `literal secret in ${f.location} field '${f.field}' of server '${f.server}'`;
|
|
5520
|
+
}
|
|
5521
|
+
return `secret file is mode ${f.mode} (expected 0600)`;
|
|
5522
|
+
}
|
|
5523
|
+
async function auditSecretsCommand(options = {}) {
|
|
5524
|
+
const root = options.root ?? join19(homedir8(), ".augmented");
|
|
5525
|
+
const findings = auditSecretLeaks(root);
|
|
5526
|
+
if (options.json) {
|
|
5527
|
+
process.stdout.write(`${JSON.stringify({ findings, root }, null, 2)}
|
|
5528
|
+
`);
|
|
5529
|
+
} else if (findings.length === 0) {
|
|
5530
|
+
process.stdout.write(
|
|
5531
|
+
`No literal secrets or world-readable secret files found under ${root}.
|
|
5532
|
+
`
|
|
5533
|
+
);
|
|
5534
|
+
} else {
|
|
5535
|
+
process.stdout.write(
|
|
5536
|
+
`Found ${findings.length} secret-handling issue${findings.length === 1 ? "" : "s"} under ${root}:
|
|
5537
|
+
|
|
5538
|
+
`
|
|
5539
|
+
);
|
|
5540
|
+
for (const f of findings) {
|
|
5541
|
+
process.stdout.write(` \u2022 [${f.agent}] ${describe(f)}
|
|
5542
|
+
`);
|
|
5543
|
+
process.stdout.write(` ${f.file}
|
|
5544
|
+
`);
|
|
5545
|
+
}
|
|
5546
|
+
process.stdout.write(
|
|
5547
|
+
"\nLiteral secrets in .mcp.json should be `${VAR}` placeholders with the raw value in .env.integrations (mode 0600). See docs/operator/credential-migration-eng5898.md.\n"
|
|
5548
|
+
);
|
|
5549
|
+
for (const f of findings) {
|
|
5550
|
+
process.stderr.write(`${formatSecretFinding(f)}
|
|
5551
|
+
`);
|
|
5552
|
+
}
|
|
5553
|
+
}
|
|
5554
|
+
if (options.strict && findings.length > 0) {
|
|
5555
|
+
process.exit(2);
|
|
5556
|
+
}
|
|
5557
|
+
}
|
|
5558
|
+
|
|
4766
5559
|
// src/commands/integration.ts
|
|
4767
5560
|
import chalk19 from "chalk";
|
|
4768
5561
|
import ora17 from "ora";
|
|
@@ -4770,7 +5563,7 @@ async function integrationListCommand() {
|
|
|
4770
5563
|
const json = isJsonMode();
|
|
4771
5564
|
const spinner = ora17({ text: "Fetching integrations\u2026", isSilent: json }).start();
|
|
4772
5565
|
try {
|
|
4773
|
-
const data = await api.get("/integrations/
|
|
5566
|
+
const data = await api.get("/integrations/installs");
|
|
4774
5567
|
spinner.stop();
|
|
4775
5568
|
if (json) {
|
|
4776
5569
|
jsonOutput({ integrations: data });
|
|
@@ -4797,7 +5590,7 @@ async function integrationShowCommand(slug) {
|
|
|
4797
5590
|
const json = isJsonMode();
|
|
4798
5591
|
const spinner = ora17({ text: "Fetching integration\u2026", isSilent: json }).start();
|
|
4799
5592
|
try {
|
|
4800
|
-
const all = await api.get("/integrations/
|
|
5593
|
+
const all = await api.get("/integrations/installs");
|
|
4801
5594
|
const integration2 = all.find((p) => p.slug === slug);
|
|
4802
5595
|
if (!integration2) {
|
|
4803
5596
|
spinner.stop();
|
|
@@ -4805,7 +5598,7 @@ async function integrationShowCommand(slug) {
|
|
|
4805
5598
|
process.exitCode = 1;
|
|
4806
5599
|
return;
|
|
4807
5600
|
}
|
|
4808
|
-
const scopes = await api.get(`/integrations/
|
|
5601
|
+
const scopes = await api.get(`/integrations/installs/${integration2.id}/scopes`);
|
|
4809
5602
|
spinner.stop();
|
|
4810
5603
|
if (json) {
|
|
4811
5604
|
jsonOutput({ integration: integration2, scopes });
|
|
@@ -4839,7 +5632,7 @@ async function integrationInstallCommand(slug, options) {
|
|
|
4839
5632
|
const json = isJsonMode();
|
|
4840
5633
|
const spinner = ora17({ text: "Installing integration\u2026", isSilent: json }).start();
|
|
4841
5634
|
try {
|
|
4842
|
-
const all = await api.get("/integrations/
|
|
5635
|
+
const all = await api.get("/integrations/installs");
|
|
4843
5636
|
const integration2 = all.find((p) => p.slug === slug);
|
|
4844
5637
|
if (!integration2) {
|
|
4845
5638
|
spinner.stop();
|
|
@@ -4856,7 +5649,7 @@ async function integrationInstallCommand(slug, options) {
|
|
|
4856
5649
|
return;
|
|
4857
5650
|
}
|
|
4858
5651
|
const scopes = options.scopes?.split(",").map((s) => s.trim()).filter(Boolean) ?? [];
|
|
4859
|
-
const result = await api.post("/integrations/
|
|
5652
|
+
const result = await api.post("/integrations/installs/install", {
|
|
4860
5653
|
agent_id: agent2.agent_id,
|
|
4861
5654
|
plugin_id: integration2.id,
|
|
4862
5655
|
scopes
|
|
@@ -4889,7 +5682,7 @@ async function integrationUninstallCommand(slug, options) {
|
|
|
4889
5682
|
const json = isJsonMode();
|
|
4890
5683
|
const spinner = ora17({ text: "Uninstalling integration\u2026", isSilent: json }).start();
|
|
4891
5684
|
try {
|
|
4892
|
-
const all = await api.get("/integrations/
|
|
5685
|
+
const all = await api.get("/integrations/installs");
|
|
4893
5686
|
const integration2 = all.find((p) => p.slug === slug);
|
|
4894
5687
|
if (!integration2) {
|
|
4895
5688
|
spinner.stop();
|
|
@@ -4905,7 +5698,7 @@ async function integrationUninstallCommand(slug, options) {
|
|
|
4905
5698
|
process.exitCode = 1;
|
|
4906
5699
|
return;
|
|
4907
5700
|
}
|
|
4908
|
-
await api.post("/integrations/
|
|
5701
|
+
await api.post("/integrations/installs/uninstall", {
|
|
4909
5702
|
agent_id: agent2.agent_id,
|
|
4910
5703
|
plugin_id: integration2.id
|
|
4911
5704
|
});
|
|
@@ -4920,7 +5713,7 @@ async function integrationRequestsCommand() {
|
|
|
4920
5713
|
const json = isJsonMode();
|
|
4921
5714
|
const spinner = ora17({ text: "Fetching scope requests\u2026", isSilent: json }).start();
|
|
4922
5715
|
try {
|
|
4923
|
-
const requests = await api.get("/integrations/
|
|
5716
|
+
const requests = await api.get("/integrations/installs/scope-requests");
|
|
4924
5717
|
spinner.stop();
|
|
4925
5718
|
if (json) {
|
|
4926
5719
|
jsonOutput({ requests });
|
|
@@ -4946,7 +5739,7 @@ async function integrationApproveCommand(requestId, options) {
|
|
|
4946
5739
|
const json = isJsonMode();
|
|
4947
5740
|
const spinner = ora17({ text: "Approving request\u2026", isSilent: json }).start();
|
|
4948
5741
|
try {
|
|
4949
|
-
const result = await api.put(`/integrations/
|
|
5742
|
+
const result = await api.put(`/integrations/installs/scope-requests/${requestId}`, {
|
|
4950
5743
|
status: "approved",
|
|
4951
5744
|
review_notes: options.notes
|
|
4952
5745
|
});
|
|
@@ -4965,7 +5758,7 @@ async function integrationDenyCommand(requestId, options) {
|
|
|
4965
5758
|
const json = isJsonMode();
|
|
4966
5759
|
const spinner = ora17({ text: "Denying request\u2026", isSilent: json }).start();
|
|
4967
5760
|
try {
|
|
4968
|
-
const result = await api.put(`/integrations/
|
|
5761
|
+
const result = await api.put(`/integrations/installs/scope-requests/${requestId}`, {
|
|
4969
5762
|
status: "denied",
|
|
4970
5763
|
review_notes: options.reason
|
|
4971
5764
|
});
|
|
@@ -5027,7 +5820,7 @@ async function integrationEnrolCommand(options) {
|
|
|
5027
5820
|
}
|
|
5028
5821
|
let displayName = options.displayName;
|
|
5029
5822
|
if (!displayName) {
|
|
5030
|
-
const defs = await api.get("/integrations/
|
|
5823
|
+
const defs = await api.get("/integrations/installs");
|
|
5031
5824
|
const def = defs.find((d) => d.slug === options.def);
|
|
5032
5825
|
displayName = def?.name ?? options.def;
|
|
5033
5826
|
}
|
|
@@ -5064,17 +5857,29 @@ function handleError(err) {
|
|
|
5064
5857
|
}
|
|
5065
5858
|
|
|
5066
5859
|
// src/bin/agt.ts
|
|
5067
|
-
var cliVersion2 = true ? "0.27.
|
|
5860
|
+
var cliVersion2 = true ? "0.27.9-test.12" : "dev";
|
|
5068
5861
|
var program = new Command();
|
|
5069
5862
|
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");
|
|
5070
|
-
program.hook("preAction", (thisCommand) => {
|
|
5863
|
+
program.hook("preAction", async (thisCommand, actionCommand) => {
|
|
5071
5864
|
const root = thisCommand.optsWithGlobals();
|
|
5072
5865
|
if (root.json) {
|
|
5073
5866
|
setJsonMode(true);
|
|
5074
5867
|
}
|
|
5868
|
+
const segs = [];
|
|
5869
|
+
for (let c = actionCommand; c && c.parent; c = c.parent) segs.unshift(c.name());
|
|
5870
|
+
const cmdPath = segs.join(" ");
|
|
5871
|
+
if (cmdPath !== "impersonate introduce" && cmdPath !== "impersonate exit") {
|
|
5872
|
+
await autoExitExpiredImpersonation();
|
|
5873
|
+
}
|
|
5075
5874
|
});
|
|
5076
5875
|
program.command("whoami").description("Show the authenticated host, team, and user from AGT_API_KEY").action(whoamiCommand);
|
|
5077
|
-
program.command("setup <token>").description("One-command host setup: exchange provisioning token, configure env vars, verify, and start manager").
|
|
5876
|
+
program.command("setup <token>").description("One-command host setup: exchange provisioning token, configure env vars, verify, and start manager").option(
|
|
5877
|
+
"--api-host <url>",
|
|
5878
|
+
// ENG-5831: required when AGT_HOST is not set in the shell — the setup
|
|
5879
|
+
// command no longer silently defaults to prod. Mirrors `agt impersonate
|
|
5880
|
+
// connect --api-host` (ENG-5773).
|
|
5881
|
+
"API host to exchange the provisioning token against. Takes precedence over AGT_HOST. Required when AGT_HOST is unset (e.g. https://test.api.staging.augmented.team for the test stage; https://api.augmented.team for prod)."
|
|
5882
|
+
).action(setupCommand);
|
|
5078
5883
|
var team = program.command("team").description("Manage teams");
|
|
5079
5884
|
team.command("list").description("List teams you belong to").action(teamListCommand);
|
|
5080
5885
|
team.command("create <name>").description("Create a new team and set it as active").action(teamCreateCommand);
|
|
@@ -5105,8 +5910,12 @@ impersonate.command("connect <token>").description(
|
|
|
5105
5910
|
})
|
|
5106
5911
|
)
|
|
5107
5912
|
);
|
|
5108
|
-
impersonate.command("current").description('Print the active impersonation (or "none")').action(
|
|
5109
|
-
|
|
5913
|
+
impersonate.command("current").description('Print the active impersonation for this directory (or "none")').option("--all", "List every active impersonation session on this machine").action(
|
|
5914
|
+
(opts) => impersonateCurrentCommand({ all: opts.all === true })
|
|
5915
|
+
);
|
|
5916
|
+
impersonate.command("exit").description("End this directory's impersonation, restore project files, and notify the server").option("--all", "Exit every active impersonation session on this machine").action(
|
|
5917
|
+
(opts) => impersonateExitCommand({ all: opts.all === true })
|
|
5918
|
+
);
|
|
5110
5919
|
impersonate.command("introduce", { hidden: true }).description("Print the impersonated agent's self-introduction (SessionStart hook target)").action(impersonateIntroduceCommand);
|
|
5111
5920
|
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);
|
|
5112
5921
|
program.command("lint").argument("[path]", "Path to agent directory (default: all agents in .augmented/)").description("Lint CHARTER.md and TOOLS.md files").action(lintCommand);
|
|
@@ -5133,6 +5942,7 @@ host.command("unassign <host-name> <agent-code-names...>").description("Unassign
|
|
|
5133
5942
|
host.command("agents [host-name]").description("List agents assigned to a host (omit name to auto-resolve from AGT_API_KEY)").action(hostAgentsCommand);
|
|
5134
5943
|
host.command("rotate-key <host-name>").description("Rotate the API key for a host (revokes the old key)").action(hostRotateKeyCommand);
|
|
5135
5944
|
host.command("decommission <host-name>").description("Decommission a host (revokes key, marks inactive)").action(hostDecommissionCommand);
|
|
5945
|
+
host.command("maintenance-window <host-name>").description("View or set the host maintenance window for restart-causing updates").option("--start <HH:MM>", "Window start, 24h local time (e.g. 01:00)").option("--end <HH:MM>", "Window end, 24h local time (e.g. 02:00)").option("--tz <IANA>", "Window timezone (e.g. Australia/Sydney)").option("--clear", "Clear the override and follow the default 01:00\u201302:00").action(hostMaintenanceWindowCommand);
|
|
5136
5946
|
host.command("pair <host-name>").description("Start an SSM port-forward + shell to re-authenticate Claude Code on an EC2 host").option("--port <port>", "Local port to forward for the OAuth callback", "54545").option("--no-shell", "Start the tunnel only; do not open an interactive shell").action(
|
|
5137
5947
|
(hostName, opts) => hostPairCommand(hostName, {
|
|
5138
5948
|
port: opts.port,
|
|
@@ -5140,16 +5950,16 @@ host.command("pair <host-name>").description("Start an SSM port-forward + shell
|
|
|
5140
5950
|
})
|
|
5141
5951
|
);
|
|
5142
5952
|
var manager = program.command("manager").description("Host config sync daemon \u2014 keeps local agent files in sync with API");
|
|
5143
|
-
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",
|
|
5144
|
-
manager.command("stop").description("Stop the running manager daemon").option("--config-dir <dir>", "Config directory for agent files",
|
|
5145
|
-
manager.command("status").description("Show the current manager daemon status and discovered agents").option("--config-dir <dir>", "Config directory for agent files",
|
|
5146
|
-
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",
|
|
5147
|
-
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",
|
|
5953
|
+
manager.command("start").description("Start the manager daemon (polls API for config changes and detects local drift)").option("--interval <seconds>", "Poll interval in seconds (min 5)", "10").option("--config-dir <dir>", "Config directory for agent files", join20(homedir9(), ".augmented")).option("--supervise", "Wrap the manager in a respawn-on-clean-exit loop so auto-upgrades can restart it transparently (ENG-4488)", false).action(managerStartCommand);
|
|
5954
|
+
manager.command("stop").description("Stop the running manager daemon").option("--config-dir <dir>", "Config directory for agent files", join20(homedir9(), ".augmented")).action(managerStopCommand);
|
|
5955
|
+
manager.command("status").description("Show the current manager daemon status and discovered agents").option("--config-dir <dir>", "Config directory for agent files", join20(homedir9(), ".augmented")).action(managerStatusCommand);
|
|
5956
|
+
manager.command("watch").description("Live TUI dashboard \u2014 per-agent boxes, drill-in, log tail. Read-only (ENG-4555).").option("--config-dir <dir>", "Config directory for agent files", join20(homedir9(), ".augmented")).option("--no-tui", "Skip the TUI and stream the manager log to stdout instead (CI / scripts)").action(managerWatchCommand);
|
|
5957
|
+
manager.command("install").description("Install OS-level supervisor (launchd LaunchAgent on macOS) so the manager auto-restarts after crash, reboot, or self-update (ENG-4593)").option("--interval <seconds>", "Poll interval in seconds (min 5)", "10").option("--config-dir <dir>", "Config directory for agent files", join20(homedir9(), ".augmented")).action(managerInstallCommand);
|
|
5148
5958
|
manager.command("uninstall").description("Remove the OS-level supervisor (launchctl unload + delete plist on macOS). Idempotent.").action(managerUninstallCommand);
|
|
5149
5959
|
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);
|
|
5150
5960
|
manager.command("uninstall-system-unit").description("Remove the system-level systemd unit (Linux, root). Idempotent. (ENG-4706)").action(managerUninstallSystemUnitCommand);
|
|
5151
5961
|
var agent = program.command("agent").description("Inspect and manage agents");
|
|
5152
|
-
agent.command("show <code-name>").description("Display an agent's provisioned OpenClaw configuration").option("--config-dir <dir>", "Config directory",
|
|
5962
|
+
agent.command("show <code-name>").description("Display an agent's provisioned OpenClaw configuration").option("--config-dir <dir>", "Config directory", join20(homedir9(), ".augmented")).option("--all-channels", "Show all channels (including disabled)").action(agentShowCommand);
|
|
5153
5963
|
var kanban = program.command("kanban").description("Manage agent kanban boards");
|
|
5154
5964
|
kanban.command("list").description("List kanban board items for an agent").requiredOption("--agent <code-name>", "Agent code name").action(kanbanListCommand);
|
|
5155
5965
|
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);
|
|
@@ -5177,10 +5987,17 @@ integration.command("approve <request-id>").description("Approve a scope request
|
|
|
5177
5987
|
integration.command("deny <request-id>").description("Deny a scope request").option("--reason <text>", "Denial reason").action(integrationDenyCommand);
|
|
5178
5988
|
integration.command("enrol").description("Enrol an integration with an API key or webhook secret (no OAuth flow)").requiredOption("--def <code-name>", "Integration definition code_name (e.g. resend, xero)").requiredOption("--scope <org|team|agent>", "Install scope").option("--api-key <value>", "API key (sets auth_type=api_key)").option("--access-token <value>", "Bearer access token (sets auth_type=oauth2)").option("--webhook-secret <value>", "Webhook signing secret (sets auth_type=webhook)").option("--display-name <name>", "Override display name (defaults to the definition\u2019s display_name)").option("--agent <code-name>", "Agent code name (required for agent scope)").action(integrationEnrolCommand);
|
|
5179
5989
|
program.command("update").description("Check for and install CLI updates").option("--force", "Update even if manager or agent sessions are running").action(updateCommand);
|
|
5990
|
+
var audit = program.command("audit").description("Operator audits \u2014 sub-agent MCP bindings, etc.");
|
|
5991
|
+
audit.command("subagents").description("Find sub-agents whose tools allowlist would block mcp__* calls on dispatch (ENG-5897)").option("--strict", "Exit with code 2 if any findings (suitable for CI gating)").option("--json", "Emit findings as JSON to stdout").action(auditSubagentsCommand);
|
|
5992
|
+
audit.command("mcp-render").description("Find managed agents whose subagent .md tools allowlist is stale vs .mcp.json (ENG-5922)").option("--strict", "Exit with code 2 if any findings (suitable for CI gating)").option("--json", "Emit findings as JSON to stdout").action(auditMcpRenderCommand);
|
|
5993
|
+
audit.command("secrets").description("Find literal secrets in .mcp.json and world-readable secret files (ENG-5901)").option("--strict", "Exit with code 2 if any findings (suitable for CI gating)").option("--json", "Emit findings as JSON to stdout").action(auditSecretsCommand);
|
|
5180
5994
|
var skipUpdateCheck = process.argv.includes("--skip-update-check") || process.argv.includes("--json") || process.argv[2] === "update";
|
|
5181
5995
|
if (!skipUpdateCheck) {
|
|
5182
5996
|
checkForUpdateOnStartup().catch(() => {
|
|
5183
5997
|
});
|
|
5184
5998
|
}
|
|
5185
|
-
program.
|
|
5999
|
+
program.parseAsync().catch((err) => {
|
|
6000
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
6001
|
+
process.exitCode = 1;
|
|
6002
|
+
});
|
|
5186
6003
|
//# sourceMappingURL=agt.js.map
|