agentradar-cli 0.3.5 → 0.3.7
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/admin.js +39 -0
- package/dist/agent-guard/hooks/hooks.json +8 -8
- package/dist/agent-guard/scripts/compaction-report.mjs +76 -0
- package/dist/agent-guard/skills/agentradar-discipline/SKILL.md +6 -0
- package/dist/agentradar-vendor/SKILL.md +12 -10
- package/dist/channel.js +57 -32
- package/dist/index.js +67 -103
- package/dist/runner.js +16 -11
- package/dist/secrets.js +3 -1
- package/dist/setup.js +124 -58
- package/dist/skill-install.js +14 -78
- package/dist/tool-run.js +167 -172
- package/dist/update.js +27 -2
- package/dist/vendor-runner/feeder.mjs +183 -23
- package/dist/vendor-runner/session-frame.mjs +97 -36
- package/dist/vendor-runner/skill-zip.mjs +76 -0
- package/dist/vendor-runner/supervisor/supervise-agent.sh +81 -15
- package/dist/vendor-runner/terminal-bridge.mjs +5 -0
- package/package.json +3 -3
- package/dist/commands.js +0 -74
package/dist/index.js
CHANGED
|
@@ -1,44 +1,63 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
|
+
import { homedir } from "node:os";
|
|
3
4
|
import { installVendorSkill } from "./skill-install.js";
|
|
4
5
|
import { startChannel } from "./channel.js";
|
|
5
6
|
import { runRestartSelftest, startVendorRunner } from "./runner.js";
|
|
6
|
-
import { cancelJoin, CODEX_GUARD_REFUSAL, ensureCodexGuard, joinOrganisation, leaveOrganisation, setupVendorAgent } from "./setup.js";
|
|
7
|
-
import { callAwpTool, callToolOverCli,
|
|
7
|
+
import { cancelJoin, CODEX_GUARD_REFUSAL, dropStaleMcp, ensureCodexGuard, joinOrganisation, leaveOrganisation, setupVendorAgent } from "./setup.js";
|
|
8
|
+
import { callAwpTool, callToolOverCli, EXIT, parseCallArgs } from "./tool-run.js";
|
|
8
9
|
import { installSyncTimer, syncAgentDir } from "./sync.js";
|
|
9
10
|
import { importVaultOverCli } from "./vault-import.js";
|
|
10
11
|
import { cloneVaultOverCli } from "./vault-clone.js";
|
|
11
12
|
import { secretsOverCli } from "./secrets.js";
|
|
13
|
+
import { adminOverCli } from "./admin.js";
|
|
12
14
|
// `run` is the one prefix every agent-facing command shares, so one harness "always allow agentradar run"
|
|
13
15
|
// covers a whole session: `run <link>` (or `run join <link>`) joins, `run <runner-id> <tool>` calls a tool.
|
|
14
16
|
export const runArgs = (raw) => raw[0] !== "run" ? raw
|
|
15
17
|
: raw[1] === "join" ? raw.slice(1)
|
|
16
18
|
: /^https?:\/\//.test(raw[1] ?? "") ? ["join", ...raw.slice(1)]
|
|
17
19
|
: ["call", ...raw.slice(1)];
|
|
20
|
+
const USAGE = [
|
|
21
|
+
"Usage: agentradar <noun> [<sub-noun>] <verb> [--<flag> <value> | --<flag>-file <path> ...] [--dry-run] [--json] [--runner <id>]",
|
|
22
|
+
" Every agent command comes from the server. agentradar --help lists the groups (as $AR_CLI_RUNNER_ID),",
|
|
23
|
+
" agentradar <group> --help a group's commands, agentradar <command> --help its flags.",
|
|
24
|
+
" Examples:",
|
|
25
|
+
" agentradar inbox next",
|
|
26
|
+
" agentradar channels reply --thread-id <id> --text \"On it\"",
|
|
27
|
+
" agentradar tasks create --title \"Draft the brief\" --dry-run",
|
|
28
|
+
" The runner is $AR_CLI_RUNNER_ID, or --runner <id>. --dry-run prints the exact call and sends nothing.",
|
|
29
|
+
" Exit codes: 0 done; 1 refused or unreachable (the reason is on stderr); 2 the command was wrong.",
|
|
30
|
+
"Local commands:",
|
|
31
|
+
" agentradar join <join-link> [--name <n>] [--model <m>] [--trust] [--attach-tmux <pane> | --attach-current] [--session <id>] [--no-service | --service]",
|
|
32
|
+
" agentradar join cancel <join-link|request-id>",
|
|
33
|
+
" agentradar setup [--trust] [--harness claude|codex]",
|
|
34
|
+
" agentradar leave <runner-id>",
|
|
35
|
+
" agentradar channel <runner-id>",
|
|
36
|
+
" agentradar selftest <runner-id> [--status|--restart]",
|
|
37
|
+
" agentradar runner start <runner-id>",
|
|
38
|
+
" agentradar call <runner-id> <tool | command> [--<flag> <value> ...] [--args '<json>']",
|
|
39
|
+
" agentradar vault import <runner-id> <dir> [--vault <vault-id>] [--source <name>] [--write]",
|
|
40
|
+
" agentradar vault clone <runner-id> <vault-id> [dir]",
|
|
41
|
+
" agentradar secrets pull|renew|env <runner-id> | agentradar secrets exec <runner-id> -- <command>",
|
|
42
|
+
" agentradar secrets upload|rotate <runner-id> --env-file <path> | --from-env <NAME,...> | --prompt <NAME> [--replace]",
|
|
43
|
+
" agentradar skill install | agentradar sync [dir] | agentradar sync install [dir] [--interval <s>]",
|
|
44
|
+
" agentradar admin broadcast --dry-run|--send --target all --text <text> --cta <next step>",
|
|
45
|
+
" agentradar upgrade | agentradar --version",
|
|
46
|
+
].join("\n");
|
|
18
47
|
async function main() {
|
|
19
48
|
const args = runArgs(process.argv.slice(2));
|
|
20
49
|
if (args.length === 1 && (args[0] === "--help" || args[0] === "-h")) {
|
|
21
|
-
console.log(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
" agentradar run <runner-id> thread --help a group's verbs",
|
|
27
|
-
" agentradar run <runner-id> thread reply --help one command's arguments",
|
|
28
|
-
" Examples:",
|
|
29
|
-
" agentradar run r1 inbox next",
|
|
30
|
-
" agentradar run r1 thread reply --thread-id <id> --text \"On it\"",
|
|
31
|
-
" agentradar run r1 inbox ack --message-ids <id> --message-ids <id>",
|
|
32
|
-
" agentradar run r1 task create --title \"Draft the brief\" --dry-run",
|
|
33
|
-
" --dry-run prints the exact call and sends nothing. Output is JSON. Exit codes: 0 done; 1 refused or",
|
|
34
|
-
" unreachable (reason on stderr); 2 the command was wrong.",
|
|
35
|
-
"agentradar upgrade install the latest agentradar-cli",
|
|
36
|
-
].join("\n"));
|
|
50
|
+
console.log(USAGE);
|
|
51
|
+
// The agent's own groups, from its server, when this shell is a runner's.
|
|
52
|
+
const runner = process.env.AR_CLI_RUNNER_ID;
|
|
53
|
+
if (runner)
|
|
54
|
+
process.exitCode = await callToolOverCli(runner, "", undefined, {}, [["help", ""]]);
|
|
37
55
|
return;
|
|
38
56
|
}
|
|
39
57
|
if (args.length === 1 && args[0] === "upgrade") {
|
|
40
58
|
const { selfUpdate, runningVersion } = await import("./update.js");
|
|
41
59
|
const installed = await selfUpdate();
|
|
60
|
+
await dropStaleMcp(process.cwd(), homedir());
|
|
42
61
|
console.log(installed ? `agentradar upgraded to ${installed}` : `agentradar ${await runningVersion()} is the latest`);
|
|
43
62
|
return;
|
|
44
63
|
}
|
|
@@ -67,7 +86,7 @@ async function main() {
|
|
|
67
86
|
await startChannel(args[1]);
|
|
68
87
|
return;
|
|
69
88
|
}
|
|
70
|
-
if (args[0] === "selftest" && args.length === 3 && args[2] === "--restart") {
|
|
89
|
+
if (args[0] === "selftest" && args[1] !== "run" && args.length === 3 && args[2] === "--restart") {
|
|
71
90
|
// A native-channel session resumes in this terminal; print the result after it closes, not over its screen.
|
|
72
91
|
const report = [];
|
|
73
92
|
process.once("exit", () => { if (report.length)
|
|
@@ -75,7 +94,7 @@ async function main() {
|
|
|
75
94
|
await runRestartSelftest(args[1], { log: (text) => report.push(text) });
|
|
76
95
|
return;
|
|
77
96
|
}
|
|
78
|
-
if (args[0] === "selftest" && (args.length === 2 || (args.length === 3 && args[2] === "--status"))) {
|
|
97
|
+
if (args[0] === "selftest" && args[1] !== "run" && (args.length === 2 || (args.length === 3 && args[2] === "--status"))) {
|
|
79
98
|
process.exitCode = await callAwpTool(args[1], "run_selftest", { statusOnly: args[2] === "--status" }, {});
|
|
80
99
|
return;
|
|
81
100
|
}
|
|
@@ -107,18 +126,18 @@ async function main() {
|
|
|
107
126
|
for (let i = 1; i < args.length; i++) {
|
|
108
127
|
if (["--name", "--model", "--attach-tmux", "--session"].includes(args[i]))
|
|
109
128
|
named[args[i].slice(2)] = args[++i] ?? "-";
|
|
110
|
-
else if (!["--trust", "--attach-current", "--allow-bypass", "--revoke-bypass", "--feeder"].includes(args[i]))
|
|
129
|
+
else if (!["--trust", "--attach-current", "--allow-bypass", "--revoke-bypass", "--feeder", "--service", "--no-service"].includes(args[i]))
|
|
111
130
|
urls.push(args[i]);
|
|
112
131
|
}
|
|
113
|
-
if (urls.length !== 1 || [urls[0], ...Object.values(named)].some((value) => value.startsWith("-")) || (named["attach-tmux"] && args.includes("--attach-current"))) {
|
|
114
|
-
console.error("Usage: agentradar join <join-link> [--name <your name>] [--model <your model id>] [--trust] [--attach-tmux <session[:window.pane]> | --attach-current] [--session <claude session id>] [--revoke-bypass] [--
|
|
132
|
+
if (urls.length !== 1 || [urls[0], ...Object.values(named)].some((value) => value.startsWith("-")) || (named["attach-tmux"] && args.includes("--attach-current")) || (args.includes("--service") && args.includes("--no-service"))) {
|
|
133
|
+
console.error("Usage: agentradar join <join-link> [--name <your name>] [--model <your model id>] [--trust] [--attach-tmux <session[:window.pane]> | --attach-current (Codex and other CLIs)] [--session <claude session id>] [--revoke-bypass] [--no-service | --service]");
|
|
115
134
|
process.exitCode = 1;
|
|
116
135
|
return;
|
|
117
136
|
}
|
|
118
137
|
process.exitCode = await joinOrganisation(urls[0], {
|
|
119
138
|
trust: args.includes("--trust"), agentName: named.name, model: named.model, session: named.session,
|
|
120
139
|
attach: args.includes("--attach-current") ? "current" : named["attach-tmux"],
|
|
121
|
-
revokeBypass: args.includes("--revoke-bypass"), feeder: args.includes("--feeder"),
|
|
140
|
+
revokeBypass: args.includes("--revoke-bypass"), feeder: args.includes("--feeder"), service: args.includes("--service"),
|
|
122
141
|
});
|
|
123
142
|
return;
|
|
124
143
|
}
|
|
@@ -131,21 +150,16 @@ async function main() {
|
|
|
131
150
|
process.exitCode = await startVendorRunner(args[2]);
|
|
132
151
|
return;
|
|
133
152
|
}
|
|
134
|
-
if (args
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if (!parsed || (!call && parsed.flags.length)) {
|
|
142
|
-
console.error(call ? "Usage: agentradar run <runner-id> <tool> [--<argument> <value> ...] [--args '<json>'] | agentradar run <runner-id> [<tool>] --help" : "Usage: agentradar tool run <runner-id> <tool-key> [--args '<json>']");
|
|
143
|
-
process.exitCode = 1;
|
|
153
|
+
if (args[0] === "call") {
|
|
154
|
+
// `call <runner-id> <tool | command>`: the runner named in place, for a shell that has no AR_CLI_RUNNER_ID.
|
|
155
|
+
const parsed = parseCallArgs(args.slice(1));
|
|
156
|
+
const [runnerId, ...words] = parsed?.words ?? [];
|
|
157
|
+
if (!parsed || !runnerId || words.length > 3 || (!words.length && !parsed.flags.some(([flag]) => flag === "help"))) {
|
|
158
|
+
console.error("Usage: agentradar call <runner-id> <tool | command> [--<flag> <value> ...] [--args '<json>'] | agentradar call <runner-id> [<command>] --help");
|
|
159
|
+
process.exitCode = EXIT.usage;
|
|
144
160
|
return;
|
|
145
161
|
}
|
|
146
|
-
process.exitCode =
|
|
147
|
-
? await callToolOverCli(parsed.runnerId, parsed.tool, parsed.json, {}, parsed.flags)
|
|
148
|
-
: await runServiceToolOverCli(parsed.runnerId, parsed.tool, parsed.json);
|
|
162
|
+
process.exitCode = await callToolOverCli(runnerId, words.join(" "), parsed.json, {}, parsed.flags);
|
|
149
163
|
return;
|
|
150
164
|
}
|
|
151
165
|
if (args[0] === "vault" && args[1] === "import") {
|
|
@@ -168,71 +182,13 @@ async function main() {
|
|
|
168
182
|
process.exitCode = await cloneVaultOverCli(args[2], args[3], args[4]);
|
|
169
183
|
return;
|
|
170
184
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if (!parsed || parsed.tool !== "inbox_search") {
|
|
174
|
-
console.error(INBOX_SEARCH_USAGE);
|
|
175
|
-
process.exitCode = 1;
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
process.exitCode = await inboxSearchOverCli(parsed.runnerId, parsed.flags, args.includes("--json"));
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
if (args[0] === "metric" && args[1] === "report") {
|
|
182
|
-
// report_metric over the same endpoint and key: an agent's own metric, a subordinate's, or a person's below it.
|
|
183
|
-
const parsed = parseCallArgs([...args.slice(2), "report_metric"]);
|
|
184
|
-
if (!parsed || !parsed.flags.some(([flag]) => flag === "metric-id")) {
|
|
185
|
-
console.error(METRIC_REPORT_USAGE);
|
|
186
|
-
process.exitCode = 1;
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
process.exitCode = await callToolOverCli(parsed.runnerId, parsed.tool, undefined, {}, parsed.flags);
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
if (args[0] === "propose-human") {
|
|
193
|
-
// R013: the Chief invites a human it found into a vacant role, the MCP `propose_human` over the same endpoint and key.
|
|
194
|
-
const parsed = parseCallArgs([...args.slice(1), "propose_human"]);
|
|
195
|
-
if (!parsed || !["name", "email", "role-id"].every((k) => parsed.flags.some(([flag]) => flag === k))) {
|
|
196
|
-
console.error(PROPOSE_HUMAN_USAGE);
|
|
197
|
-
process.exitCode = 1;
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
process.exitCode = await callToolOverCli(parsed.runnerId, parsed.tool, undefined, {}, parsed.flags);
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
if (args[0] === "listing" && args[1] === "describe") {
|
|
204
|
-
// UAT U1: the agent's public catalog description, the MCP `set_listing_description` over the same endpoint and key.
|
|
205
|
-
const parsed = parseCallArgs([...args.slice(2), "set_listing_description"]);
|
|
206
|
-
if (!parsed || !parsed.flags.some(([flag]) => flag === "capabilities")) {
|
|
207
|
-
console.error(LISTING_DESCRIBE_USAGE);
|
|
208
|
-
process.exitCode = 1;
|
|
209
|
-
return;
|
|
210
|
-
}
|
|
211
|
-
process.exitCode = await callToolOverCli(parsed.runnerId, parsed.tool, undefined, {}, parsed.flags);
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
if (args[0] === "permissions" && args[1] && Object.hasOwn(PERMISSION_VERB_TOOLS, args[1])) {
|
|
215
|
-
const tool = PERMISSION_VERB_TOOLS[args[1]];
|
|
216
|
-
const parsed = parseCallArgs([...args.slice(2), tool]);
|
|
217
|
-
if (!parsed || parsed.tool !== tool) {
|
|
218
|
-
console.error("Usage: agentradar permissions mine|roles|set|request|grant-tool <runner-id> [--<argument> <value> ...]");
|
|
219
|
-
process.exitCode = 1;
|
|
220
|
-
return;
|
|
221
|
-
}
|
|
222
|
-
process.exitCode = await callToolOverCli(parsed.runnerId, tool, parsed.json, {}, parsed.flags);
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
if (args[0] === "secrets") {
|
|
185
|
+
// The local secrets verbs take the runner in place; `secrets list|upload|grant|revoke|rotate --<flag>` are the server's.
|
|
186
|
+
if (args[0] === "secrets" && ["pull", "renew", "env", "exec", "upload", "rotate"].includes(args[1] ?? "") && args[2] && !args[2].startsWith("-")) {
|
|
226
187
|
process.exitCode = await secretsOverCli(args.slice(1));
|
|
227
188
|
return;
|
|
228
189
|
}
|
|
229
|
-
if (args[0] === "
|
|
230
|
-
|
|
231
|
-
console.error("Usage: agentradar task create|repeat|make-recurring <runner-id> --args '<json>'");
|
|
232
|
-
process.exitCode = 1;
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
process.exitCode = await createTaskOverCli(args[2], args[1], args[4]);
|
|
190
|
+
if (args[0] === "admin") {
|
|
191
|
+
process.exitCode = await adminOverCli(args.slice(1));
|
|
236
192
|
return;
|
|
237
193
|
}
|
|
238
194
|
if (args.length === 2 && args[0] === "skill" && args[1] === "install") {
|
|
@@ -253,13 +209,21 @@ async function main() {
|
|
|
253
209
|
return;
|
|
254
210
|
}
|
|
255
211
|
if (args[0] === "sync" && args.length <= 2) {
|
|
212
|
+
await dropStaleMcp(args[1] ?? process.cwd(), homedir());
|
|
256
213
|
process.exitCode = await syncAgentDir(args[1]);
|
|
257
214
|
return;
|
|
258
215
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
216
|
+
// #1871: every other command is the server's, `<noun> [<sub-noun>] <verb>`, run as this shell's runner.
|
|
217
|
+
const parsed = parseCallArgs(args);
|
|
218
|
+
const runner = parsed?.flags.find(([flag]) => flag === "runner")?.[1] ?? process.env.AR_CLI_RUNNER_ID;
|
|
219
|
+
if (!parsed || !runner || parsed.words.length > 3 || (!parsed.words.length && !parsed.flags.some(([flag]) => flag === "help"))) {
|
|
220
|
+
console.error(USAGE);
|
|
221
|
+
if (parsed && !runner)
|
|
222
|
+
console.error("Agent commands run as a runner: set AR_CLI_RUNNER_ID or pass --runner <id>.");
|
|
223
|
+
process.exitCode = EXIT.usage;
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
process.exitCode = await callToolOverCli(runner, parsed.words.join(" "), parsed.json, {}, parsed.flags.filter(([flag]) => flag !== "runner"));
|
|
263
227
|
}
|
|
264
228
|
/**
|
|
265
229
|
* 🔴 `error.message` ALONE IS TWO WORDS ON THE FAILURE A VENDOR IS MOST LIKELY TO HIT. Node's `fetch`
|
package/dist/runner.js
CHANGED
|
@@ -6,14 +6,14 @@ import { fileURLToPath } from "node:url";
|
|
|
6
6
|
import { promisify } from "node:util";
|
|
7
7
|
import { shellValue, trustedMcpEndpoint, writePrivateFile } from "./setup.js";
|
|
8
8
|
import { callAwpTool, envValue } from "./tool-run.js";
|
|
9
|
-
import { installedVersion, revertCli, runningVersion, selfUpdate } from "./update.js";
|
|
9
|
+
import { cliHeader, installedVersion, noteCliUpdate, revertCli, runningVersion, selfUpdate } from "./update.js";
|
|
10
10
|
const RUNNER_ID = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;
|
|
11
11
|
const ENGAGEMENT_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
12
12
|
// Per-hire bearers are ordinary agent keys (`pcp_…`); the runner's own is an AWP key (`ar_…`).
|
|
13
13
|
const HIRE_BEARER = /^(ar|pcp)_[A-Za-z0-9_-]+$/;
|
|
14
14
|
// What a hire's env file sets for itself; the catalog agent's values for these never reach it.
|
|
15
15
|
// A hire always runs Claude, whose MCP config it owns; a Codex home would be the catalog agent's.
|
|
16
|
-
const HIRE_OWN_KEYS = /^(export\s+)?(AGENT_NAME|AR_BASE|AWP_MCP_URL|AWP_BEARER|AWP_BEARER_FILE|MCP_CONFIG|TMUX_SESSION|AGENT_PID_PATTERN|AR_AGENT_HARNESS|CODEX_HOME)=/;
|
|
16
|
+
const HIRE_OWN_KEYS = /^(export\s+)?(AGENT_NAME|AR_CLI_RUNNER_ID|CLAUDE_CONFIG_DIR|AR_BASE|AWP_MCP_URL|AWP_BEARER|AWP_BEARER_FILE|MCP_CONFIG|TMUX_SESSION|AGENT_PID_PATTERN|AR_AGENT_HARNESS|CODEX_HOME)=/;
|
|
17
17
|
const CODEX_HARNESS = /^(export\s+)?AR_AGENT_HARNESS=\s*["']?codex\b/m;
|
|
18
18
|
// While a pane shows this, its agent is mid-turn (the feeder's in-progress hint, same for Claude and Codex).
|
|
19
19
|
const MID_TURN = /esc to interrupt|\besc to cancel\b/i;
|
|
@@ -285,12 +285,16 @@ export async function startVendorRunner(runnerId, home = homedir(), deps = {}) {
|
|
|
285
285
|
const killSession = (name) => spawnChild("bash", [
|
|
286
286
|
"-c", 'PATH="/opt/homebrew/bin:/usr/local/bin:$PATH"; exec "${TMUX_BIN:-tmux}" kill-session -t "$1"', "tmux", `=_${name}`,
|
|
287
287
|
]).once("error", () => { });
|
|
288
|
-
const post = (path, body, token = bearer) =>
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
288
|
+
const post = async (path, body, token = bearer) => {
|
|
289
|
+
const response = await (deps.fetch ?? fetch)(new URL(path, mcpUrl), {
|
|
290
|
+
method: "POST",
|
|
291
|
+
headers: { ...(await cliHeader("feeder")), authorization: `Bearer ${token}`, accept: "application/json", "content-type": "application/json", "cache-control": "no-store" },
|
|
292
|
+
body: JSON.stringify(body ?? {}),
|
|
293
|
+
signal: AbortSignal.timeout(30_000),
|
|
294
|
+
});
|
|
295
|
+
await noteCliUpdate(response.headers);
|
|
296
|
+
return response;
|
|
297
|
+
};
|
|
294
298
|
const launchAll = async () => {
|
|
295
299
|
for (const file of await readdir(agentDir)) {
|
|
296
300
|
const engagementId = file.startsWith(prefix) && file.endsWith(".env") ? file.slice(prefix.length, -".env".length) : "";
|
|
@@ -332,9 +336,9 @@ export async function startVendorRunner(runnerId, home = homedir(), deps = {}) {
|
|
|
332
336
|
const configDir = join(home, ".agentradar", "config", name);
|
|
333
337
|
await mkdir(configDir, { recursive: true, mode: 0o700 });
|
|
334
338
|
const mcpConfig = join(configDir, "awp-only.json");
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
}, null, 2)}\n`);
|
|
339
|
+
// Vadym 12915: no MCP entry for AgentRadar. The hire acts with `agentradar run $AR_CLI_RUNNER_ID <tool>`,
|
|
340
|
+
// which reads this hire's own env file; --strict-mcp-config still confines the pane to this empty list.
|
|
341
|
+
await writePrivateFile(mcpConfig, `${JSON.stringify({ mcpServers: {} }, null, 2)}\n`);
|
|
338
342
|
// Every setting the vendor put in the catalog agent's env (tool env, harness flags) is copied, as
|
|
339
343
|
// read when the runner started, except the ones this hire sets for itself — the shared bearer among
|
|
340
344
|
// them. Copied rather than sourced: sourcing would put the shared bearer in the hire's supervisor.
|
|
@@ -343,6 +347,7 @@ export async function startVendorRunner(runnerId, home = homedir(), deps = {}) {
|
|
|
343
347
|
// ponytail: line by line, so a multi-line quoted value is copied only in part; parse it when one appears.
|
|
344
348
|
const own = [
|
|
345
349
|
`AGENT_NAME=${shellValue(name)}`,
|
|
350
|
+
`AR_CLI_RUNNER_ID=${shellValue(name)}`,
|
|
346
351
|
`AR_BASE=${shellValue(folderOf(hire.engagementId))}`,
|
|
347
352
|
`AWP_MCP_URL=${shellValue(mcpUrl.toString())}`,
|
|
348
353
|
`AWP_BEARER=${shellValue(hire.bearer)}`,
|
package/dist/secrets.js
CHANGED
|
@@ -15,6 +15,7 @@ import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
|
15
15
|
import { homedir } from "node:os";
|
|
16
16
|
import { join } from "node:path";
|
|
17
17
|
import { awpRequest, envValue } from "./tool-run.js";
|
|
18
|
+
import { cliHeader, noteCliUpdate } from "./update.js";
|
|
18
19
|
export const SECRETS_FILE = join(".agentradar", "secrets.enc");
|
|
19
20
|
export const SECRETS_USAGE = "Usage: agentradar secrets pull|renew|env <runner-id> | agentradar secrets exec <runner-id> -- <command> [args...]"
|
|
20
21
|
+ " | agentradar secrets upload <runner-id> --env-file <path> | --from-env <NAME,...> | --prompt <NAME> [--only <NAME,...>] [--replace --approval-details '<json>'] [--token <token>]"
|
|
@@ -34,8 +35,9 @@ async function credentials(runnerId, deps) {
|
|
|
34
35
|
async function post(runnerId, path, body, deps) {
|
|
35
36
|
const { origin, bearer } = await credentials(runnerId, deps);
|
|
36
37
|
const res = await (deps.fetch ?? fetch)(`${origin}/api/agent/${path}`, {
|
|
37
|
-
method: "POST", headers: { authorization: `Bearer ${bearer}`, "content-type": "application/json" }, body: JSON.stringify(body ?? {}),
|
|
38
|
+
method: "POST", headers: { ...(await cliHeader()), authorization: `Bearer ${bearer}`, "content-type": "application/json" }, body: JSON.stringify(body ?? {}),
|
|
38
39
|
});
|
|
40
|
+
await noteCliUpdate(res.headers, deps.logError);
|
|
39
41
|
const json = await res.json().catch(() => ({}));
|
|
40
42
|
if (!res.ok)
|
|
41
43
|
throw new Error(json.error ?? `AgentRadar answered ${res.status}`);
|