agentinbox-cli 0.0.0 → 0.1.0-nightly.10
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/LICENSE +21 -0
- package/README.md +337 -0
- package/dist/admin-CXkmdTYo.js +196 -0
- package/dist/admin-CXkmdTYo.js.map +1 -0
- package/dist/audit-bItbJYFP.js +65 -0
- package/dist/audit-bItbJYFP.js.map +1 -0
- package/dist/audit-gVdDzkAE.js +2 -0
- package/dist/auth-0NrMvHdE.js +550 -0
- package/dist/auth-0NrMvHdE.js.map +1 -0
- package/dist/auth-CoHvbWgO.js +2 -0
- package/dist/bus-Cz0flS37.js +12 -0
- package/dist/bus-Cz0flS37.js.map +1 -0
- package/dist/bus-OKIGut72.js +2 -0
- package/dist/cli.js +220 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands-DZ60j8_y.js +103 -0
- package/dist/commands-DZ60j8_y.js.map +1 -0
- package/dist/config-CIvgMqLx.js +2 -0
- package/dist/config-v975gxv4.js +44 -0
- package/dist/config-v975gxv4.js.map +1 -0
- package/dist/connect-BHC2trBi.js +292 -0
- package/dist/connect-BHC2trBi.js.map +1 -0
- package/dist/content-BXblC_Qs.js +1162 -0
- package/dist/content-BXblC_Qs.js.map +1 -0
- package/dist/content-worker.js +28 -0
- package/dist/content-worker.js.map +1 -0
- package/dist/crypto-DYZ4R7c2.js +74 -0
- package/dist/crypto-DYZ4R7c2.js.map +1 -0
- package/dist/db-B6gLQGBK.js +2 -0
- package/dist/db-D0gvshCc.js +282 -0
- package/dist/db-D0gvshCc.js.map +1 -0
- package/dist/errors-FbMS3QBo.js +39 -0
- package/dist/errors-FbMS3QBo.js.map +1 -0
- package/dist/filter-DGVz6-P1.js +140 -0
- package/dist/filter-DGVz6-P1.js.map +1 -0
- package/dist/instance-BBzEsYZO.js +175 -0
- package/dist/instance-BBzEsYZO.js.map +1 -0
- package/dist/instance-DLNTj9nm.js +2 -0
- package/dist/listen-D8DkEhqM.js +1724 -0
- package/dist/listen-D8DkEhqM.js.map +1 -0
- package/dist/listen-DT-omyD6.js +2 -0
- package/dist/mcpheader-By2dIyZq.js +2 -0
- package/dist/mcpheader-D9rrI-aa.js +65 -0
- package/dist/mcpheader-D9rrI-aa.js.map +1 -0
- package/dist/mcpsetup-7KxroOCp.js +2 -0
- package/dist/mcpsetup-DJ7ebFGJ.js +266 -0
- package/dist/mcpsetup-DJ7ebFGJ.js.map +1 -0
- package/dist/paths-BCBp8vrG.js +2 -0
- package/dist/paths-TLp8hiMt.js +40 -0
- package/dist/paths-TLp8hiMt.js.map +1 -0
- package/dist/profile-CgGgJ4Dm.js +181 -0
- package/dist/profile-CgGgJ4Dm.js.map +1 -0
- package/dist/profile-RbN2FRz7.js +2 -0
- package/dist/recipes-7d_Xws4M.js +2 -0
- package/dist/recipes-UalOc_LB.js +100 -0
- package/dist/recipes-UalOc_LB.js.map +1 -0
- package/dist/send-Dk-t8ia-.js +108 -0
- package/dist/send-Dk-t8ia-.js.map +1 -0
- package/dist/server-Hunjpt8n.js +8103 -0
- package/dist/server-Hunjpt8n.js.map +1 -0
- package/dist/service-BAgZDh22.js +2 -0
- package/dist/service-DYdD92CC.js +2 -0
- package/dist/service-D_ZEs3Zk.js +508 -0
- package/dist/service-D_ZEs3Zk.js.map +1 -0
- package/dist/service-whbugeQp.js +295 -0
- package/dist/service-whbugeQp.js.map +1 -0
- package/dist/settings-CeEsv4CE.js +2 -0
- package/dist/settings-DLUStIN7.js +109 -0
- package/dist/settings-DLUStIN7.js.map +1 -0
- package/dist/setup-token-BO89xnpb.js +48 -0
- package/dist/setup-token-BO89xnpb.js.map +1 -0
- package/dist/skill/SKILL.md +277 -0
- package/dist/skill-BJFn2bHK.js +2 -0
- package/dist/skill-WKbeyNct.js +43 -0
- package/dist/skill-WKbeyNct.js.map +1 -0
- package/dist/types-3eEsBFVj.js +37 -0
- package/dist/types-3eEsBFVj.js.map +1 -0
- package/dist/ui-B8Sd4Y7Y.js +81 -0
- package/dist/ui-B8Sd4Y7Y.js.map +1 -0
- package/dist/urls-Dp8pBGMU.js +2 -0
- package/dist/urls-U_LfYV61.js +27 -0
- package/dist/urls-U_LfYV61.js.map +1 -0
- package/dist/web/assets/_agentId-DAj08NDq.js +1 -0
- package/dist/web/assets/_app-B6q_HS9y.js +1 -0
- package/dist/web/assets/_app-CAQ6Mu1s.js +63 -0
- package/dist/web/assets/_webhookId-CMgBUN2Z.js +1 -0
- package/dist/web/assets/account-status-omoUlgBI.js +1 -0
- package/dist/web/assets/accounts-DyLfACj_.js +1 -0
- package/dist/web/assets/accounts-list-DiXACW0-.js +1 -0
- package/dist/web/assets/activity-zrckFhPk.js +1 -0
- package/dist/web/assets/agent-create-form-DKwaY1Dg.js +1 -0
- package/dist/web/assets/agents-Drvjccgt.js +1 -0
- package/dist/web/assets/approvals-4kd0_J8X.js +2 -0
- package/dist/web/assets/arrow-left-DVJV4Zx7.js +1 -0
- package/dist/web/assets/arrow-right-4PFX6MSS.js +1 -0
- package/dist/web/assets/audit-76H45orR.js +1 -0
- package/dist/web/assets/audit-bits-B1We-fQ3.js +1 -0
- package/dist/web/assets/bot-DVw15lBn.js +1 -0
- package/dist/web/assets/card-EQTpqntn.js +1 -0
- package/dist/web/assets/checkbox-DDZZurgK.js +1 -0
- package/dist/web/assets/chevron-down-892ylB4R.js +1 -0
- package/dist/web/assets/chevron-right-CbBKS6-T.js +1 -0
- package/dist/web/assets/chips-input--xgMZpg2.js +1 -0
- package/dist/web/assets/circle-x-Bi4IGwky.js +1 -0
- package/dist/web/assets/common-PG6weTqi.js +1 -0
- package/dist/web/assets/connect-host-DdkOJnMu.js +1 -0
- package/dist/web/assets/data-table-Ca9aQZKR.js +1 -0
- package/dist/web/assets/dialog-CxNRCZdE.js +1 -0
- package/dist/web/assets/dist-B0e3Ju3r.js +1 -0
- package/dist/web/assets/empty-CE_WZGEC.js +1 -0
- package/dist/web/assets/es2015-DPaldoxG.js +41 -0
- package/dist/web/assets/event-row-2xoyzgbP.js +1 -0
- package/dist/web/assets/events-Bizq3DPC.js +1 -0
- package/dist/web/assets/eye-off-DjpaihHf.js +1 -0
- package/dist/web/assets/field-CcyMn3ey.js +1 -0
- package/dist/web/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/dist/web/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/dist/web/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/dist/web/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/dist/web/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
- package/dist/web/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
- package/dist/web/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
- package/dist/web/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
- package/dist/web/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
- package/dist/web/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
- package/dist/web/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/dist/web/assets/get-started-DXUVmyNX.js +1 -0
- package/dist/web/assets/grants-BhXj1Pew.js +1 -0
- package/dist/web/assets/index-Bd4Y5wSp.js +10 -0
- package/dist/web/assets/index-akvt_1UI.css +2 -0
- package/dist/web/assets/input-CMhmmHiH.js +1 -0
- package/dist/web/assets/input-group-DRs3TNaT.js +1 -0
- package/dist/web/assets/key-round-CVIZRCLM.js +1 -0
- package/dist/web/assets/labels-C_Pwium9.js +1 -0
- package/dist/web/assets/login-aCr-cPqJ.js +1 -0
- package/dist/web/assets/mail-CAVmuESo.js +1 -0
- package/dist/web/assets/mutations-DVlbDx0M.js +1 -0
- package/dist/web/assets/paperclip-QFjM4y6n.js +1 -0
- package/dist/web/assets/password-input-_m9Ycphv.js +1 -0
- package/dist/web/assets/pencil-Bf1JhsG0.js +1 -0
- package/dist/web/assets/play-DiPk49JH.js +1 -0
- package/dist/web/assets/plus-CETmK0SB.js +1 -0
- package/dist/web/assets/preload-helper-BFuRUfdt.js +1 -0
- package/dist/web/assets/progress-3ZOsbLSK.js +6 -0
- package/dist/web/assets/queries-a3M5_loa.js +2 -0
- package/dist/web/assets/refresh-cw-DOn9kD7g.js +1 -0
- package/dist/web/assets/search-gcYjitzV.js +1 -0
- package/dist/web/assets/select-C8tJBHb5.js +1 -0
- package/dist/web/assets/send-BhQwhuJo.js +1 -0
- package/dist/web/assets/settings-H7ExHD9L.js +1 -0
- package/dist/web/assets/settings-form-CNUionsI.js +1 -0
- package/dist/web/assets/setup-DLbH5Dvl.js +1 -0
- package/dist/web/assets/sheet-BQKpA2xa.js +1 -0
- package/dist/web/assets/shield-alert-CbFgd06X.js +1 -0
- package/dist/web/assets/stamp-CH2OvjH2.js +1 -0
- package/dist/web/assets/stream-C2Tr2d5O.js +1 -0
- package/dist/web/assets/tabs-ySiz7Mni.js +1 -0
- package/dist/web/assets/tag-CP72Kk6Y.js +1 -0
- package/dist/web/assets/test-connection-Bd2Gac0O.js +1 -0
- package/dist/web/assets/textarea-CCokBZIw.js +1 -0
- package/dist/web/assets/toggle-group-ne0pVDg6.js +1 -0
- package/dist/web/assets/trash-DxdMNg8B.js +1 -0
- package/dist/web/assets/use-sign-out-BuzlTb0Q.js +1 -0
- package/dist/web/assets/useInfiniteQuery-DE6WChlb.js +1 -0
- package/dist/web/assets/useMatch-BKUnm54_.js +1 -0
- package/dist/web/assets/useMutation-DeWvLw1a.js +1 -0
- package/dist/web/assets/useQuery-DAhf1YDH.js +1 -0
- package/dist/web/assets/useSelector-D70eKNYs.js +1 -0
- package/dist/web/assets/webhook-BWT75M78.js +1 -0
- package/dist/web/assets/webhook-sheet-Cy_jHIqG.js +34 -0
- package/dist/web/assets/webhooks-CTAj5Ovp.js +1 -0
- package/dist/web/assets/with-selector-CkNyXYMW.js +1 -0
- package/dist/web/assets/x-BHjDLXNO.js +1 -0
- package/dist/web/favicon.svg +1 -0
- package/dist/web/index.html +31 -0
- package/package.json +95 -1
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { t as CAPABILITIES } from "./types-3eEsBFVj.js";
|
|
3
|
+
import { i as safeText, n as box, r as formatDuration, t as StatusLine } from "./ui-B8Sd4Y7Y.js";
|
|
4
|
+
import { d as NPX, i as saveProfile, n as loadProfile, r as profileExists, t as listProfiles } from "./profile-CgGgJ4Dm.js";
|
|
5
|
+
import { C as startPairing, S as pollPairing, b as clientInfo, c as defaultConsumer, d as validConsumer, g as normalizeDaemonUrl, h as insecureBanner, m as checkTransport, y as ApiError } from "./listen-D8DkEhqM.js";
|
|
6
|
+
import { f as validProfileName, s as profilePath } from "./paths-TLp8hiMt.js";
|
|
7
|
+
import { s as writeClaudeMcpConfig } from "./mcpheader-D9rrI-aa.js";
|
|
8
|
+
import { n as isServiceInstalled } from "./service-whbugeQp.js";
|
|
9
|
+
import { i as recipe, n as TOOL_LABELS, o as shellQuote, t as TOOLS } from "./recipes-UalOc_LB.js";
|
|
10
|
+
import { l as setupMcp, s as mcpTarget } from "./mcpsetup-DJ7ebFGJ.js";
|
|
11
|
+
import { closeSync, constants, fchmodSync, lstatSync, openSync, writeSync } from "node:fs";
|
|
12
|
+
import { hostname } from "node:os";
|
|
13
|
+
import pc from "picocolors";
|
|
14
|
+
//#region server/listen/connect.ts
|
|
15
|
+
/** Exit codes, so scripts (and agents setting themselves up) can tell outcomes apart. */
|
|
16
|
+
const CONNECT_EXIT = {
|
|
17
|
+
approved: 0,
|
|
18
|
+
error: 1,
|
|
19
|
+
denied: 2,
|
|
20
|
+
expired: 3,
|
|
21
|
+
consumed: 4,
|
|
22
|
+
cancelled: 130
|
|
23
|
+
};
|
|
24
|
+
function parseTool(value) {
|
|
25
|
+
if (value === void 0) return "other";
|
|
26
|
+
const v = value.trim().toLowerCase().replace(/^claude$/, "claude-code");
|
|
27
|
+
if (!TOOLS.includes(v)) throw new Error(`Unknown --tool "${value}". Choose one of: ${TOOLS.join(", ")}.`);
|
|
28
|
+
return v;
|
|
29
|
+
}
|
|
30
|
+
function parseCapabilities(value) {
|
|
31
|
+
if (value === void 0) return void 0;
|
|
32
|
+
const list = value.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
|
|
33
|
+
const bad = list.filter((c) => !CAPABILITIES.includes(c));
|
|
34
|
+
if (bad.length) throw new Error(`Unknown capabilit${bad.length > 1 ? "ies" : "y"} ${bad.join(", ")}. Choose from: ${CAPABILITIES.join(", ")}.`);
|
|
35
|
+
return [...new Set(list)];
|
|
36
|
+
}
|
|
37
|
+
const shortHost = () => hostname().replace(/\.(local|lan|home)$/i, "");
|
|
38
|
+
const sleep = (ms, signal) => new Promise((resolve) => {
|
|
39
|
+
const t = setTimeout(resolve, ms);
|
|
40
|
+
signal?.addEventListener("abort", () => {
|
|
41
|
+
clearTimeout(t);
|
|
42
|
+
resolve();
|
|
43
|
+
}, { once: true });
|
|
44
|
+
});
|
|
45
|
+
const stripAnsi = (s) => s.replace(/\x1b\[[0-9;]*m/g, "");
|
|
46
|
+
/** --log target (fd), written in parallel with the console. */
|
|
47
|
+
let logFd = null;
|
|
48
|
+
const toLog = (s) => {
|
|
49
|
+
if (logFd !== null) writeSync(logFd, stripAnsi(s) + "\n");
|
|
50
|
+
};
|
|
51
|
+
const out = (s = "") => {
|
|
52
|
+
process.stdout.write(`${s}\n`);
|
|
53
|
+
toLog(s);
|
|
54
|
+
};
|
|
55
|
+
const err = (s = "") => {
|
|
56
|
+
process.stderr.write(`${s}\n`);
|
|
57
|
+
toLog(s);
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Opens --log safely: a new file is created 0600 with O_EXCL; an existing one (e.g. from `mktemp`) is
|
|
61
|
+
* only reused if it is a regular file owned by us, opened without following symlinks. This is what
|
|
62
|
+
* keeps another local user from pre-creating the log and reading the pairing as it happens.
|
|
63
|
+
*/
|
|
64
|
+
function openConnectLog(path) {
|
|
65
|
+
try {
|
|
66
|
+
return openSync(path, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 384);
|
|
67
|
+
} catch (e) {
|
|
68
|
+
if (e.code !== "EEXIST") throw e;
|
|
69
|
+
}
|
|
70
|
+
const st = lstatSync(path);
|
|
71
|
+
const uid = process.getuid?.();
|
|
72
|
+
if (!st.isFile() || uid !== void 0 && st.uid !== uid) throw new Error(`Refusing --log ${path}: it exists and is not a regular file owned by you.`);
|
|
73
|
+
const fd = openSync(path, constants.O_WRONLY | constants.O_TRUNC | constants.O_NOFOLLOW);
|
|
74
|
+
fchmodSync(fd, 384);
|
|
75
|
+
return fd;
|
|
76
|
+
}
|
|
77
|
+
async function runConnect(rawUrl, flags, opts = {}) {
|
|
78
|
+
if (flags.log) logFd = openConnectLog(flags.log);
|
|
79
|
+
try {
|
|
80
|
+
return await connect(rawUrl, flags, opts);
|
|
81
|
+
} finally {
|
|
82
|
+
if (logFd !== null) closeSync(logFd);
|
|
83
|
+
logFd = null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
async function connect(rawUrl, flags, opts) {
|
|
87
|
+
const url = normalizeDaemonUrl(rawUrl);
|
|
88
|
+
const verdict = await checkTransport(url, { insecure: flags.insecure });
|
|
89
|
+
if (verdict.warning) err(insecureBanner(verdict.warning));
|
|
90
|
+
const profileName = validProfileName(flags.profile ?? "default");
|
|
91
|
+
let replacing = null;
|
|
92
|
+
if (profileExists(profileName) && flags.force) try {
|
|
93
|
+
replacing = loadProfile(profileName);
|
|
94
|
+
} catch {}
|
|
95
|
+
if (profileExists(profileName) && !flags.force) {
|
|
96
|
+
let who = "";
|
|
97
|
+
try {
|
|
98
|
+
const p = loadProfile(profileName);
|
|
99
|
+
who = ` (agent "${safeText(p.agentName)}" on ${p.url})`;
|
|
100
|
+
} catch {}
|
|
101
|
+
const others = listProfiles();
|
|
102
|
+
throw new Error(`Profile "${profileName}" already exists${who}.\n Pair a second agent with --profile <name>${others.length > 1 ? ` (existing: ${others.join(", ")})` : ""}, or replace this one with --force.`);
|
|
103
|
+
}
|
|
104
|
+
const tool = parseTool(flags.tool);
|
|
105
|
+
const capabilities = parseCapabilities(flags.capabilities);
|
|
106
|
+
const consumer = validConsumer(flags.consumer ?? replacing?.consumer ?? defaultConsumer(profileName));
|
|
107
|
+
const name = (flags.name?.trim() || `${tool === "other" ? "Agent" : TOOL_LABELS[tool]} on ${shortHost()}`).slice(0, 80);
|
|
108
|
+
const input = {
|
|
109
|
+
name,
|
|
110
|
+
requested: {
|
|
111
|
+
...flags.account?.length ? { accounts: flags.account } : {},
|
|
112
|
+
...capabilities ? { capabilities } : {},
|
|
113
|
+
...flags.reason ? { reason: flags.reason } : {}
|
|
114
|
+
},
|
|
115
|
+
client: {
|
|
116
|
+
...clientInfo(),
|
|
117
|
+
tool
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
let start;
|
|
121
|
+
try {
|
|
122
|
+
start = await startPairing(url, input);
|
|
123
|
+
} catch (e) {
|
|
124
|
+
if (e instanceof ApiError && e.status === 404) throw new Error(`${url} doesn't support pairing (no /agent/v1/pairings). Is this an agentinbox daemon, and is it up to date?`);
|
|
125
|
+
if (e instanceof ApiError) throw new Error(`${e.message}${e.hint ? `\n ${e.hint}` : ""}`);
|
|
126
|
+
throw e;
|
|
127
|
+
}
|
|
128
|
+
out();
|
|
129
|
+
out(box([
|
|
130
|
+
pc.bold("Approve this agent in the agentinbox console"),
|
|
131
|
+
"",
|
|
132
|
+
`Code: ${pc.bold(pc.yellow(safeText(start.userCode, 40)))}`,
|
|
133
|
+
`Open: ${pc.cyan(safeText(start.verificationUrl, 300))}`,
|
|
134
|
+
`Agent: ${safeText(name)}`,
|
|
135
|
+
"",
|
|
136
|
+
pc.gray("Only approve if the console shows this exact code.")
|
|
137
|
+
]));
|
|
138
|
+
out();
|
|
139
|
+
const ac = new AbortController();
|
|
140
|
+
const onAbort = () => ac.abort();
|
|
141
|
+
opts.signal?.addEventListener("abort", onAbort, { once: true });
|
|
142
|
+
const onSigint = () => ac.abort();
|
|
143
|
+
if (opts.handleSignals) process.on("SIGINT", onSigint);
|
|
144
|
+
const started = Date.now();
|
|
145
|
+
const expiresAt = Date.parse(start.expiresAt) || started + 6e5;
|
|
146
|
+
let interval = Math.max(1, start.interval || 5) * 1e3;
|
|
147
|
+
let note = "";
|
|
148
|
+
const status = new StatusLine(process.stderr);
|
|
149
|
+
status.start(() => `Waiting for approval · ${formatDuration(Date.now() - started)} elapsed · code expires in ${formatDuration(expiresAt - Date.now())}${note ? pc.yellow(` · ${note}`) : ""}`);
|
|
150
|
+
let result = null;
|
|
151
|
+
try {
|
|
152
|
+
while (!ac.signal.aborted) {
|
|
153
|
+
await sleep(opts.pollIntervalMs ?? interval, ac.signal);
|
|
154
|
+
if (ac.signal.aborted) break;
|
|
155
|
+
try {
|
|
156
|
+
const r = await pollPairing(url, start.deviceCode);
|
|
157
|
+
note = "";
|
|
158
|
+
if (r.status === "pending") continue;
|
|
159
|
+
if (r.status === "slow_down") {
|
|
160
|
+
interval = Math.max(interval + 1e3, (r.interval || 0) * 1e3);
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
result = r;
|
|
164
|
+
break;
|
|
165
|
+
} catch (e) {
|
|
166
|
+
if (e instanceof ApiError && e.status === 429) {
|
|
167
|
+
interval += 5e3;
|
|
168
|
+
note = "rate limited, slowing down";
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (e instanceof ApiError && e.status >= 400 && e.status < 500 && e.status !== 408) throw new Error(`${e.message}${e.hint ? `\n ${e.hint}` : ""}`);
|
|
172
|
+
note = "can't reach the daemon, retrying";
|
|
173
|
+
if (Date.now() > expiresAt + 3e4) {
|
|
174
|
+
result = { status: "expired" };
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
} finally {
|
|
180
|
+
status.stop();
|
|
181
|
+
if (opts.handleSignals) process.off("SIGINT", onSigint);
|
|
182
|
+
opts.signal?.removeEventListener("abort", onAbort);
|
|
183
|
+
}
|
|
184
|
+
if (!result) {
|
|
185
|
+
err(pc.yellow("Cancelled. Nothing was approved; the code expires on its own."));
|
|
186
|
+
return CONNECT_EXIT.cancelled;
|
|
187
|
+
}
|
|
188
|
+
switch (result.status) {
|
|
189
|
+
case "denied":
|
|
190
|
+
err(pc.red(`✖ The request was denied in the console${result.note ? `: ${safeText(result.note, 300)}` : "."}`));
|
|
191
|
+
return CONNECT_EXIT.denied;
|
|
192
|
+
case "expired":
|
|
193
|
+
err(pc.red("✖ The code expired before it was approved."));
|
|
194
|
+
err(` Run the same command again for a fresh code, and approve it within ${formatDuration(Date.parse(start.expiresAt) - started)}.`);
|
|
195
|
+
return CONNECT_EXIT.expired;
|
|
196
|
+
case "consumed":
|
|
197
|
+
err(pc.red("✖ This pairing was already completed and its key handed out once (perhaps by an earlier run of this command)."));
|
|
198
|
+
err(` For safety the key is never delivered twice. Start over with \`${NPX} connect\`, or rotate the agent's key in the console.`);
|
|
199
|
+
return CONNECT_EXIT.consumed;
|
|
200
|
+
case "approved": break;
|
|
201
|
+
default: throw new Error(`Unexpected pairing status ${JSON.stringify(result.status)}`);
|
|
202
|
+
}
|
|
203
|
+
const profile = {
|
|
204
|
+
...replacing ? {
|
|
205
|
+
types: replacing.types,
|
|
206
|
+
filter: replacing.filter,
|
|
207
|
+
includeBody: replacing.includeBody,
|
|
208
|
+
exec: replacing.exec,
|
|
209
|
+
forward: replacing.forward,
|
|
210
|
+
forwardSecret: replacing.forwardSecret,
|
|
211
|
+
concurrency: replacing.concurrency,
|
|
212
|
+
timeoutSeconds: replacing.timeoutSeconds,
|
|
213
|
+
maxAttempts: replacing.maxAttempts,
|
|
214
|
+
cwd: replacing.cwd
|
|
215
|
+
} : {},
|
|
216
|
+
url,
|
|
217
|
+
apiKey: result.apiKey,
|
|
218
|
+
agentId: result.agent.id,
|
|
219
|
+
agentName: result.agent.name,
|
|
220
|
+
consumer,
|
|
221
|
+
...verdict.kind === "insecure" ? { insecure: true } : {},
|
|
222
|
+
tool,
|
|
223
|
+
mcpUrl: `${url}/mcp`,
|
|
224
|
+
skillUrl: `${url}/skill.md`,
|
|
225
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
226
|
+
};
|
|
227
|
+
const path = saveProfile(profileName, profile);
|
|
228
|
+
writeClaudeMcpConfig(profileName, profile.mcpUrl);
|
|
229
|
+
if (typeof result.mcpUrl === "string" && result.mcpUrl !== profile.mcpUrl) err(pc.yellow(`! The daemon advertises its MCP endpoint as ${safeText(result.mcpUrl, 200)} (its "public URL" setting); using ${profile.mcpUrl}, the address you connected to. Set the public URL in the console if agents should use another address.`));
|
|
230
|
+
out(`${pc.green("✔")} Approved. Agent ${pc.bold(safeText(result.agent.name))} ${pc.gray(`(${result.agent.id})`)} is connected.`);
|
|
231
|
+
out(` Key saved to ${pc.cyan(path)} ${pc.gray("(mode 0600; never displayed. Rotate it in the console if it leaks.)")}`);
|
|
232
|
+
if (replacing && (replacing.exec || replacing.forward)) out(` Kept this profile's listener settings. Restart the listener to use the new key${isServiceInstalled(profileName) ? `: ${NPX} listen service install${profileName === "default" ? "" : ` --profile ${profileName}`}` : "."}`);
|
|
233
|
+
if (replacing && replacing.agentId !== profile.agentId) err(pc.yellow(`! Replaced profile "${profileName}" (agent "${safeText(replacing.agentName)}", ${replacing.agentId}). Its key still works until you rotate it or delete that agent in the console; config backups made by setup-mcp (*.agentinbox-backup-*) may contain it.`));
|
|
234
|
+
let mcp = null;
|
|
235
|
+
if (flags.setupMcp) {
|
|
236
|
+
mcp = setupMcp(tool, mcpTarget(profileName, profile), path);
|
|
237
|
+
printMcpResult(mcp);
|
|
238
|
+
}
|
|
239
|
+
printNextSteps(profileName, profile, tool, mcp);
|
|
240
|
+
return CONNECT_EXIT.approved;
|
|
241
|
+
}
|
|
242
|
+
function printMcpResult(r) {
|
|
243
|
+
if (r.ok) out(`${pc.green("✔")} ${r.message}`);
|
|
244
|
+
else err(`${pc.yellow("!")} ${r.message}`);
|
|
245
|
+
if (r.backup) out(pc.gray(` Backup: ${r.backup}`));
|
|
246
|
+
if (!r.ok && r.manual?.length) {
|
|
247
|
+
out(" Do it by hand:");
|
|
248
|
+
for (const l of r.manual) out(` ${l}`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
function skillCommand(tool, url) {
|
|
252
|
+
switch (tool) {
|
|
253
|
+
case "claude-code": return `${NPX} skill install --target claude --url ${url}`;
|
|
254
|
+
case "codex": return `${NPX} skill install --target codex --url ${url}`;
|
|
255
|
+
case "hermes": return `${NPX} skill install --target ~/.hermes/skills/email --url ${url}`;
|
|
256
|
+
case "cursor": return `${NPX} skill install --target agents --url ${url}`;
|
|
257
|
+
default: return `${NPX} skill print --url ${url} > SKILL.md`;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
function printNextSteps(profileName, profile, tool, mcp) {
|
|
261
|
+
const p = profileName === "default" ? "" : ` --profile ${profileName}`;
|
|
262
|
+
const label = tool === "other" ? "your agent" : TOOL_LABELS[tool];
|
|
263
|
+
const r = recipe(tool, profileName);
|
|
264
|
+
const execArg = shellQuote(r.exec);
|
|
265
|
+
out();
|
|
266
|
+
out(pc.bold("Next steps"));
|
|
267
|
+
let n = 1;
|
|
268
|
+
if (mcp?.ok) out(` ${n++}. ${label} has the agentinbox MCP tools ${pc.gray(`(restart it if it's running)`)}.`);
|
|
269
|
+
else if (tool === "other") {
|
|
270
|
+
out(` ${n++}. Point your agent's MCP client at ${pc.cyan(profile.mcpUrl ?? `${profile.url}/mcp`)} with header Authorization: Bearer <key>`);
|
|
271
|
+
out(pc.gray(` The key is the "apiKey" field in ${profilePath(profileName)}.`));
|
|
272
|
+
} else {
|
|
273
|
+
out(` ${n++}. Give ${label} the agentinbox MCP tools without ever seeing the key:`);
|
|
274
|
+
out(` ${pc.cyan(`${NPX} setup-mcp --tool ${tool}${p}`)}`);
|
|
275
|
+
}
|
|
276
|
+
out(` ${n++}. Install the agentinbox skill so ${label} knows the rules:`);
|
|
277
|
+
out(` ${pc.cyan(skillCommand(tool, profile.url))}`);
|
|
278
|
+
out(` ${n++}. Wake ${label} when mail arrives. Try it in the foreground (--save keeps the command in the profile):`);
|
|
279
|
+
out(` ${pc.cyan(`${NPX} listen${p} --save --exec ${execArg}`)}`);
|
|
280
|
+
out(` then keep it running in the background (systemd / launchd, starts at login):`);
|
|
281
|
+
out(` ${pc.cyan(`${NPX} listen service install${p}`)}`);
|
|
282
|
+
out(pc.gray(` What the woken agent can use: ${r.confinement}.`));
|
|
283
|
+
for (const note of r.notes) out(pc.gray(` ${note}`));
|
|
284
|
+
out();
|
|
285
|
+
out(pc.gray(" What this agent may do (mailboxes, capabilities, send mode) was set by whoever approved it. New grants are draft-only:"));
|
|
286
|
+
out(pc.gray(" sends become Gmail drafts until the operator changes that in the console."));
|
|
287
|
+
out();
|
|
288
|
+
}
|
|
289
|
+
//#endregion
|
|
290
|
+
export { parseTool, printMcpResult, runConnect };
|
|
291
|
+
|
|
292
|
+
//# sourceMappingURL=connect-BHC2trBi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connect-BHC2trBi.js","names":[],"sources":["../server/listen/connect.ts"],"sourcesContent":["import { closeSync, constants, fchmodSync, lstatSync, openSync, writeSync } from \"node:fs\";\nimport { hostname } from \"node:os\";\nimport pc from \"picocolors\";\nimport type { AgentTool, PairingPoll, PairingStart, PairingStartInput } from \"../../shared/agent-api.ts\";\nimport { CAPABILITIES, type Capability } from \"../../shared/types.ts\";\nimport { ApiError, clientInfo, pollPairing, startPairing } from \"./api.ts\";\nimport { writeClaudeMcpConfig } from \"./mcpheader.ts\";\nimport { isServiceInstalled } from \"./service.ts\";\nimport { setupMcp, mcpTarget, type McpSetupResult } from \"./mcpsetup.ts\";\nimport { defaultConsumer, validConsumer } from \"./options.ts\";\nimport { DEFAULT_PROFILE, profilePath, validProfileName } from \"./paths.ts\";\nimport { listProfiles, loadProfile, type Profile, profileExists, saveProfile } from \"./profile.ts\";\nimport { recipe, shellQuote, TOOL_LABELS, TOOLS } from \"./recipes.ts\";\nimport { checkTransport, insecureBanner, normalizeDaemonUrl } from \"./transport.ts\";\nimport { box, formatDuration, safeText, StatusLine } from \"./ui.ts\";\nimport { NPX } from \"./version.ts\";\n\nexport interface ConnectFlags {\n name?: string;\n tool?: string;\n account?: string[];\n capabilities?: string;\n reason?: string;\n profile?: string;\n consumer?: string;\n insecure?: boolean;\n force?: boolean;\n setupMcp?: boolean;\n /** Also write the output (plain text) to this file: created 0600, never through a symlink or someone else's file. */\n log?: string;\n}\n\n/** Exit codes, so scripts (and agents setting themselves up) can tell outcomes apart. */\nexport const CONNECT_EXIT = { approved: 0, error: 1, denied: 2, expired: 3, consumed: 4, cancelled: 130 } as const;\n\nexport interface ConnectOptions {\n /** Tests: poll faster than the server's interval. */\n pollIntervalMs?: number;\n signal?: AbortSignal;\n handleSignals?: boolean;\n}\n\nexport function parseTool(value: string | undefined): AgentTool {\n if (value === undefined) return \"other\";\n const v = value.trim().toLowerCase().replace(/^claude$/, \"claude-code\");\n if (!(TOOLS as readonly string[]).includes(v)) throw new Error(`Unknown --tool \"${value}\". Choose one of: ${TOOLS.join(\", \")}.`);\n return v as AgentTool;\n}\n\nexport function parseCapabilities(value: string | undefined): Capability[] | undefined {\n if (value === undefined) return undefined;\n const list = value\n .split(\",\")\n .map((s) => s.trim().toLowerCase())\n .filter(Boolean);\n const bad = list.filter((c) => !(CAPABILITIES as readonly string[]).includes(c));\n if (bad.length) throw new Error(`Unknown capabilit${bad.length > 1 ? \"ies\" : \"y\"} ${bad.join(\", \")}. Choose from: ${CAPABILITIES.join(\", \")}.`);\n return [...new Set(list)] as Capability[];\n}\n\nconst shortHost = () => hostname().replace(/\\.(local|lan|home)$/i, \"\");\n\nconst sleep = (ms: number, signal?: AbortSignal) =>\n new Promise<void>((resolve) => {\n const t = setTimeout(resolve, ms);\n signal?.addEventListener(\n \"abort\",\n () => {\n clearTimeout(t);\n resolve();\n },\n { once: true },\n );\n });\n\n// eslint-disable-next-line no-control-regex\nconst stripAnsi = (s: string) => s.replace(/\\x1b\\[[0-9;]*m/g, \"\");\n/** --log target (fd), written in parallel with the console. */\nlet logFd: number | null = null;\nconst toLog = (s: string) => {\n if (logFd !== null) writeSync(logFd, stripAnsi(s) + \"\\n\");\n};\nconst out = (s = \"\") => {\n process.stdout.write(`${s}\\n`);\n toLog(s);\n};\nconst err = (s = \"\") => {\n process.stderr.write(`${s}\\n`);\n toLog(s);\n};\n\n/**\n * Opens --log safely: a new file is created 0600 with O_EXCL; an existing one (e.g. from `mktemp`) is\n * only reused if it is a regular file owned by us, opened without following symlinks. This is what\n * keeps another local user from pre-creating the log and reading the pairing as it happens.\n */\nexport function openConnectLog(path: string): number {\n try {\n return openSync(path, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600);\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code !== \"EEXIST\") throw e;\n }\n const st = lstatSync(path);\n const uid = process.getuid?.();\n if (!st.isFile() || (uid !== undefined && st.uid !== uid)) throw new Error(`Refusing --log ${path}: it exists and is not a regular file owned by you.`);\n const fd = openSync(path, constants.O_WRONLY | constants.O_TRUNC | constants.O_NOFOLLOW);\n fchmodSync(fd, 0o600);\n return fd;\n}\n\nexport async function runConnect(rawUrl: string, flags: ConnectFlags, opts: ConnectOptions = {}): Promise<number> {\n if (flags.log) logFd = openConnectLog(flags.log);\n try {\n return await connect(rawUrl, flags, opts);\n } finally {\n if (logFd !== null) closeSync(logFd);\n logFd = null;\n }\n}\n\nasync function connect(rawUrl: string, flags: ConnectFlags, opts: ConnectOptions): Promise<number> {\n const url = normalizeDaemonUrl(rawUrl);\n const verdict = await checkTransport(url, { insecure: flags.insecure });\n if (verdict.warning) err(insecureBanner(verdict.warning));\n\n const profileName = validProfileName(flags.profile ?? DEFAULT_PROFILE);\n let replacing: Profile | null = null;\n if (profileExists(profileName) && flags.force) {\n try {\n replacing = loadProfile(profileName);\n } catch {\n // Unreadable: it's being replaced anyway.\n }\n }\n if (profileExists(profileName) && !flags.force) {\n let who = \"\";\n try {\n const p = loadProfile(profileName);\n who = ` (agent \"${safeText(p.agentName)}\" on ${p.url})`;\n } catch {\n // Unreadable: still don't overwrite silently.\n }\n const others = listProfiles();\n throw new Error(\n `Profile \"${profileName}\" already exists${who}.\\n` +\n ` Pair a second agent with --profile <name>${others.length > 1 ? ` (existing: ${others.join(\", \")})` : \"\"}, or replace this one with --force.`,\n );\n }\n const tool = parseTool(flags.tool);\n const capabilities = parseCapabilities(flags.capabilities);\n // --force keeps the listener's consumer, so the server-side cursor carries on (no replay, no gap).\n const consumer = validConsumer(flags.consumer ?? replacing?.consumer ?? defaultConsumer(profileName));\n const name = (flags.name?.trim() || `${tool === \"other\" ? \"Agent\" : TOOL_LABELS[tool]} on ${shortHost()}`).slice(0, 80);\n\n const input: PairingStartInput = {\n name,\n requested: {\n ...(flags.account?.length ? { accounts: flags.account } : {}),\n ...(capabilities ? { capabilities } : {}),\n ...(flags.reason ? { reason: flags.reason } : {}),\n },\n client: { ...clientInfo(), tool },\n };\n\n let start: PairingStart;\n try {\n start = await startPairing(url, input);\n } catch (e) {\n if (e instanceof ApiError && e.status === 404) {\n throw new Error(`${url} doesn't support pairing (no /agent/v1/pairings). Is this an agentinbox daemon, and is it up to date?`);\n }\n if (e instanceof ApiError) throw new Error(`${e.message}${e.hint ? `\\n ${e.hint}` : \"\"}`);\n throw e;\n }\n\n out();\n out(\n box([\n pc.bold(\"Approve this agent in the agentinbox console\"),\n \"\",\n `Code: ${pc.bold(pc.yellow(safeText(start.userCode, 40)))}`,\n `Open: ${pc.cyan(safeText(start.verificationUrl, 300))}`,\n `Agent: ${safeText(name)}`,\n \"\",\n pc.gray(\"Only approve if the console shows this exact code.\"),\n ]),\n );\n out();\n\n // Ctrl-C: stop cleanly. Nothing was approved; the request just expires.\n const ac = new AbortController();\n const onAbort = () => ac.abort();\n opts.signal?.addEventListener(\"abort\", onAbort, { once: true });\n const onSigint = () => ac.abort();\n if (opts.handleSignals) process.on(\"SIGINT\", onSigint);\n\n const started = Date.now();\n const expiresAt = Date.parse(start.expiresAt) || started + 10 * 60_000;\n let interval = Math.max(1, start.interval || 5) * 1000;\n let note = \"\";\n const status = new StatusLine(process.stderr);\n status.start(\n () =>\n `Waiting for approval · ${formatDuration(Date.now() - started)} elapsed · code expires in ${formatDuration(expiresAt - Date.now())}${note ? pc.yellow(` · ${note}`) : \"\"}`,\n );\n\n let result: PairingPoll | null = null;\n try {\n while (!ac.signal.aborted) {\n await sleep(opts.pollIntervalMs ?? interval, ac.signal);\n if (ac.signal.aborted) break;\n try {\n const r = await pollPairing(url, start.deviceCode);\n note = \"\";\n if (r.status === \"pending\") continue;\n if (r.status === \"slow_down\") {\n // The server asks for more room between polls.\n interval = Math.max(interval + 1000, (r.interval || 0) * 1000);\n continue;\n }\n result = r;\n break;\n } catch (e) {\n if (e instanceof ApiError && e.status === 429) {\n interval += 5000;\n note = \"rate limited, slowing down\";\n continue;\n }\n if (e instanceof ApiError && e.status >= 400 && e.status < 500 && e.status !== 408) {\n throw new Error(`${e.message}${e.hint ? `\\n ${e.hint}` : \"\"}`);\n }\n // Network trouble or a 5xx: keep trying until the code expires.\n note = \"can't reach the daemon, retrying\";\n if (Date.now() > expiresAt + 30_000) {\n result = { status: \"expired\" };\n break;\n }\n }\n }\n } finally {\n status.stop();\n if (opts.handleSignals) process.off(\"SIGINT\", onSigint);\n opts.signal?.removeEventListener(\"abort\", onAbort);\n }\n\n if (!result) {\n err(pc.yellow(\"Cancelled. Nothing was approved; the code expires on its own.\"));\n return CONNECT_EXIT.cancelled;\n }\n\n switch (result.status) {\n case \"denied\":\n err(pc.red(`✖ The request was denied in the console${result.note ? `: ${safeText(result.note, 300)}` : \".\"}`));\n return CONNECT_EXIT.denied;\n case \"expired\":\n err(pc.red(\"✖ The code expired before it was approved.\"));\n err(` Run the same command again for a fresh code, and approve it within ${formatDuration(Date.parse(start.expiresAt) - started)}.`);\n return CONNECT_EXIT.expired;\n case \"consumed\":\n err(pc.red(\"✖ This pairing was already completed and its key handed out once (perhaps by an earlier run of this command).\"));\n err(` For safety the key is never delivered twice. Start over with \\`${NPX} connect\\`, or rotate the agent's key in the console.`);\n return CONNECT_EXIT.consumed;\n case \"approved\":\n break;\n default:\n throw new Error(`Unexpected pairing status ${JSON.stringify((result as { status: unknown }).status)}`);\n }\n\n // --force keeps everything about the listener (targets, filters, limits, forward secret, cwd); only\n // the pairing (daemon, key, agent) is replaced.\n const kept: Partial<Profile> = replacing\n ? {\n types: replacing.types,\n filter: replacing.filter,\n includeBody: replacing.includeBody,\n exec: replacing.exec,\n forward: replacing.forward,\n forwardSecret: replacing.forwardSecret,\n concurrency: replacing.concurrency,\n timeoutSeconds: replacing.timeoutSeconds,\n maxAttempts: replacing.maxAttempts,\n cwd: replacing.cwd,\n }\n : {};\n const profile: Profile = {\n ...kept,\n url,\n apiKey: result.apiKey,\n agentId: result.agent.id,\n agentName: result.agent.name,\n consumer,\n ...(verdict.kind === \"insecure\" ? { insecure: true } : {}),\n tool,\n // Derived from the URL the user chose and transport-checked; the daemon's advertised URLs come\n // from its publicUrl setting and are never trusted with the key.\n mcpUrl: `${url}/mcp`,\n skillUrl: `${url}/skill.md`,\n createdAt: new Date().toISOString(),\n };\n const path = saveProfile(profileName, profile);\n writeClaudeMcpConfig(profileName, profile.mcpUrl!);\n if (typeof result.mcpUrl === \"string\" && result.mcpUrl !== profile.mcpUrl) {\n err(\n pc.yellow(\n `! The daemon advertises its MCP endpoint as ${safeText(result.mcpUrl, 200)} (its \"public URL\" setting); using ${profile.mcpUrl}, the address you connected to. Set the public URL in the console if agents should use another address.`,\n ),\n );\n }\n\n out(`${pc.green(\"✔\")} Approved. Agent ${pc.bold(safeText(result.agent.name))} ${pc.gray(`(${result.agent.id})`)} is connected.`);\n out(` Key saved to ${pc.cyan(path)} ${pc.gray(\"(mode 0600; never displayed. Rotate it in the console if it leaks.)\")}`);\n if (replacing && (replacing.exec || replacing.forward)) {\n out(` Kept this profile's listener settings. Restart the listener to use the new key${isServiceInstalled(profileName) ? `: ${NPX} listen service install${profileName === DEFAULT_PROFILE ? \"\" : ` --profile ${profileName}`}` : \".\"}`);\n }\n if (replacing && replacing.agentId !== profile.agentId) {\n err(\n pc.yellow(\n `! Replaced profile \"${profileName}\" (agent \"${safeText(replacing.agentName)}\", ${replacing.agentId}). Its key still works until you rotate it or delete that agent in the console; config backups made by setup-mcp (*.agentinbox-backup-*) may contain it.`,\n ),\n );\n }\n\n let mcp: McpSetupResult | null = null;\n if (flags.setupMcp) {\n mcp = setupMcp(tool, mcpTarget(profileName, profile), path);\n printMcpResult(mcp);\n }\n printNextSteps(profileName, profile, tool, mcp);\n return CONNECT_EXIT.approved;\n}\n\nexport function printMcpResult(r: McpSetupResult): void {\n if (r.ok) out(`${pc.green(\"✔\")} ${r.message}`);\n else err(`${pc.yellow(\"!\")} ${r.message}`);\n if (r.backup) out(pc.gray(` Backup: ${r.backup}`));\n if (!r.ok && r.manual?.length) {\n out(\" Do it by hand:\");\n for (const l of r.manual) out(` ${l}`);\n }\n}\n\nfunction skillCommand(tool: AgentTool, url: string): string {\n switch (tool) {\n case \"claude-code\":\n return `${NPX} skill install --target claude --url ${url}`;\n case \"codex\":\n return `${NPX} skill install --target codex --url ${url}`;\n case \"hermes\":\n return `${NPX} skill install --target ~/.hermes/skills/email --url ${url}`;\n case \"cursor\":\n return `${NPX} skill install --target agents --url ${url}`;\n default:\n return `${NPX} skill print --url ${url} > SKILL.md`;\n }\n}\n\nexport function printNextSteps(profileName: string, profile: Profile, tool: AgentTool, mcp: McpSetupResult | null): void {\n const p = profileName === DEFAULT_PROFILE ? \"\" : ` --profile ${profileName}`;\n const label = tool === \"other\" ? \"your agent\" : TOOL_LABELS[tool];\n const r = recipe(tool, profileName);\n const execArg = shellQuote(r.exec);\n out();\n out(pc.bold(\"Next steps\"));\n let n = 1;\n if (mcp?.ok) {\n out(` ${n++}. ${label} has the agentinbox MCP tools ${pc.gray(`(restart it if it's running)`)}.`);\n } else if (tool === \"other\") {\n out(` ${n++}. Point your agent's MCP client at ${pc.cyan(profile.mcpUrl ?? `${profile.url}/mcp`)} with header Authorization: Bearer <key>`);\n out(pc.gray(` The key is the \"apiKey\" field in ${profilePath(profileName)}.`));\n } else {\n out(` ${n++}. Give ${label} the agentinbox MCP tools without ever seeing the key:`);\n out(` ${pc.cyan(`${NPX} setup-mcp --tool ${tool}${p}`)}`);\n }\n out(` ${n++}. Install the agentinbox skill so ${label} knows the rules:`);\n out(` ${pc.cyan(skillCommand(tool, profile.url))}`);\n out(` ${n++}. Wake ${label} when mail arrives. Try it in the foreground (--save keeps the command in the profile):`);\n out(` ${pc.cyan(`${NPX} listen${p} --save --exec ${execArg}`)}`);\n out(` then keep it running in the background (systemd / launchd, starts at login):`);\n out(` ${pc.cyan(`${NPX} listen service install${p}`)}`);\n out(pc.gray(` What the woken agent can use: ${r.confinement}.`));\n for (const note of r.notes) out(pc.gray(` ${note}`));\n out();\n out(pc.gray(\" What this agent may do (mailboxes, capabilities, send mode) was set by whoever approved it. New grants are draft-only:\"));\n out(pc.gray(\" sends become Gmail drafts until the operator changes that in the console.\"));\n out();\n}\n"],"mappings":"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"}
|