@michael-joseph-miller/ant-bot 0.1.4 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import { createRequire as __antbotCreateRequire } from 'node:module';
3
3
  const require = __antbotCreateRequire(import.meta.url);
4
4
  import {
5
5
  SettingsSchema
6
- } from "./chunk-4XML2GPY.js";
6
+ } from "./chunk-RMDMUCVS.js";
7
7
 
8
8
  // cli/src/index.ts
9
9
  import fs10 from "node:fs";
@@ -19,6 +19,7 @@ var KNOWN_COMMANDS = [
19
19
  "doctor",
20
20
  "open",
21
21
  "skill",
22
+ "connector",
22
23
  "backup",
23
24
  "restore",
24
25
  "update"
@@ -39,6 +40,7 @@ var COMMAND_FLAGS = {
39
40
  doctor: {},
40
41
  open: {},
41
42
  skill: {},
43
+ connector: {},
42
44
  backup: {
43
45
  out: { type: "string" }
44
46
  },
@@ -50,6 +52,7 @@ var COMMAND_FLAGS = {
50
52
  yes: { type: "boolean", default: false }
51
53
  }
52
54
  };
55
+ var PASSTHROUGH_COMMANDS = /* @__PURE__ */ new Set(["connector"]);
53
56
  function isKnownCommand(value) {
54
57
  return KNOWN_COMMANDS.includes(value);
55
58
  }
@@ -87,6 +90,10 @@ Run "antbot --help" for usage.`);
87
90
  help = true;
88
91
  continue;
89
92
  }
93
+ if (PASSTHROUGH_COMMANDS.has(command)) {
94
+ positionals.push(tok);
95
+ continue;
96
+ }
90
97
  if (tok.startsWith("--")) {
91
98
  let name = tok.slice(2);
92
99
  let inlineValue;
@@ -142,6 +149,7 @@ Daemon:
142
149
 
143
150
  Data:
144
151
  skill <subcommand> Manage the skills your bots can use
152
+ connector <subcmd> Manage the MCP servers your bots can use
145
153
  backup [--out PATH] Archive the database, config, skills, and bot memory
146
154
  restore <path> Restore from a backup archive
147
155
 
@@ -256,6 +264,27 @@ live database handle and may be mid-turn.
256
264
 
257
265
  In a git checkout there is nothing for a package manager to update \u2014 use
258
266
  \`git pull && pnpm install\` instead.`,
267
+ connector: `antbot connector \u2014 manage the MCP servers your bots can use
268
+
269
+ Usage: antbot connector <subcommand>
270
+
271
+ list Show every connector, with a warning for any missing secret
272
+ add <name> \u2026 Add one: --stdio "<cmd>" or --url <url>
273
+ enable|disable <n> Turn one on or off for every bot at once
274
+ remove <name> Delete it, and every bot's assignment to it
275
+ test <name> Connect and list the tools it offers
276
+
277
+ A connector is an MCP server registered once for the account and then assigned to
278
+ individual bots in Bot settings \u2014 a bot with no assignment cannot see its tools at
279
+ all. Its tools reach bots as \`mcp__<name>__<tool>\` and pass the permission gateway
280
+ like any other tool, so the first call asks you for approval.
281
+
282
+ Credentials stay in the keychain: write {{secret:NAME}} in any --env or --header
283
+ value and the daemon substitutes it when the connector is mounted.
284
+
285
+ antbot connector add fs --stdio "npx -y @modelcontextprotocol/server-filesystem /tmp"
286
+ antbot connector add gh --url https://api.example.com/mcp \\
287
+ --header "Authorization=Bearer {{secret:GH_TOKEN}}"`,
259
288
  restore: `antbot restore \u2014 restore from a backup archive
260
289
 
261
290
  Usage: antbot restore <path> [--yes]
@@ -536,6 +565,16 @@ async function postJson(port, path12, body) {
536
565
  })
537
566
  );
538
567
  }
568
+ async function patchJson(port, path12, body) {
569
+ return unwrap(
570
+ await fetch(`${BASE(port)}${path12}`, {
571
+ method: "PATCH",
572
+ headers: { "content-type": "application/json" },
573
+ body: JSON.stringify(body),
574
+ signal: AbortSignal.timeout(1e4)
575
+ })
576
+ );
577
+ }
539
578
  async function deleteJson(port, path12) {
540
579
  return unwrap(
541
580
  await fetch(`${BASE(port)}${path12}`, { method: "DELETE", signal: AbortSignal.timeout(1e4) })
@@ -1160,6 +1199,190 @@ async function lintSkills(args) {
1160
1199
  return 0;
1161
1200
  }
1162
1201
 
1202
+ // cli/src/connector.ts
1203
+ function collectPairs(argv, flag) {
1204
+ const out = {};
1205
+ for (let i = 0; i < argv.length; i++) {
1206
+ if (argv[i] !== flag) continue;
1207
+ const raw = argv[i + 1] ?? "";
1208
+ const eq = raw.indexOf("=");
1209
+ if (eq > 0) out[raw.slice(0, eq)] = raw.slice(eq + 1);
1210
+ i++;
1211
+ }
1212
+ return out;
1213
+ }
1214
+ function flagValue(argv, flag) {
1215
+ const i = argv.indexOf(flag);
1216
+ return i >= 0 ? argv[i + 1] : void 0;
1217
+ }
1218
+ function splitCommand(input) {
1219
+ const parts = input.match(/"[^"]*"|'[^']*'|\S+/g) ?? [];
1220
+ const clean = parts.map(
1221
+ (p) => p.startsWith('"') && p.endsWith('"') || p.startsWith("'") && p.endsWith("'") ? p.slice(1, -1) : p
1222
+ );
1223
+ return { command: clean[0] ?? "", args: clean.slice(1) };
1224
+ }
1225
+ function buildConfigFromFlags(argv) {
1226
+ const stdio = flagValue(argv, "--stdio");
1227
+ const url = flagValue(argv, "--url");
1228
+ if (stdio && url) return { error: "Use either --stdio or --url, not both." };
1229
+ if (stdio) {
1230
+ const { command, args } = splitCommand(stdio);
1231
+ if (!command) return { error: '--stdio needs a command, e.g. --stdio "npx -y @scope/server"' };
1232
+ return { config: { transport: "stdio", command, args, env: collectPairs(argv, "--env") } };
1233
+ }
1234
+ if (url) {
1235
+ const transport = flagValue(argv, "--transport") ?? "http";
1236
+ if (transport !== "http" && transport !== "sse") return { error: "--transport must be http or sse" };
1237
+ const tools = flagValue(argv, "--tools");
1238
+ return {
1239
+ config: {
1240
+ transport,
1241
+ url,
1242
+ headers: collectPairs(argv, "--header"),
1243
+ ...tools ? { tools: tools.split(",").map((t) => t.trim()).filter(Boolean) } : {}
1244
+ }
1245
+ };
1246
+ }
1247
+ return { error: 'Give either --stdio "<command>" or --url <url>.' };
1248
+ }
1249
+ var transportOf = (c) => String(c.config.transport ?? "?");
1250
+ async function findByName(port, name) {
1251
+ return (await getJson(port, "/api/connectors")).find((c) => c.name === name);
1252
+ }
1253
+ async function runConnectorCommand(argv, port) {
1254
+ const sub = argv[0];
1255
+ const hint = () => {
1256
+ console.error(dim("Subcommands: list, add, enable, disable, remove, test."));
1257
+ console.error(dim("Run `antbot connector --help` for the full usage."));
1258
+ };
1259
+ if (!sub) {
1260
+ console.error(red("antbot connector needs a subcommand."));
1261
+ hint();
1262
+ return 2;
1263
+ }
1264
+ let rows;
1265
+ const load = async () => {
1266
+ try {
1267
+ rows = await getJson(port, "/api/connectors");
1268
+ return true;
1269
+ } catch (err) {
1270
+ console.error(red(`Could not reach the daemon on port ${port}: ${err.message}`));
1271
+ console.error(dim("Start it with `antbot start`."));
1272
+ return false;
1273
+ }
1274
+ };
1275
+ switch (sub) {
1276
+ case "list": {
1277
+ if (!await load()) return 1;
1278
+ if (!rows.length) {
1279
+ console.log(dim("No connectors yet. Add one with `antbot connector add`."));
1280
+ return 0;
1281
+ }
1282
+ for (const c of rows) {
1283
+ const state = c.enabled ? "" : dim(" (disabled)");
1284
+ console.log(`${bold(c.name)} ${dim(transportOf(c))}${state}`);
1285
+ if (c.description) console.log(` ${c.description}`);
1286
+ if (c.missingSecrets.length) {
1287
+ console.log(yellow(` missing secret(s): ${c.missingSecrets.join(", ")} \u2014 this connector will not mount`));
1288
+ }
1289
+ }
1290
+ return 0;
1291
+ }
1292
+ case "add": {
1293
+ const name = argv[1];
1294
+ if (!name || name.startsWith("--")) {
1295
+ console.error(red('Usage: antbot connector add <name> (--stdio "<cmd>" | --url <url>)'));
1296
+ return 2;
1297
+ }
1298
+ const built = buildConfigFromFlags(argv);
1299
+ if ("error" in built) {
1300
+ console.error(red(built.error));
1301
+ return 2;
1302
+ }
1303
+ try {
1304
+ const created = await postJson(port, "/api/connectors", {
1305
+ name,
1306
+ description: flagValue(argv, "--desc") ?? "",
1307
+ config: built.config
1308
+ });
1309
+ console.log(green(`Added connector "${created.name}".`));
1310
+ console.log(dim("Assign it to a bot in Bot settings, then `antbot connector test` to check it."));
1311
+ return 0;
1312
+ } catch (err) {
1313
+ console.error(red(err.message));
1314
+ return 1;
1315
+ }
1316
+ }
1317
+ case "enable":
1318
+ case "disable": {
1319
+ const name = argv[1];
1320
+ if (!name) {
1321
+ console.error(red(`Usage: antbot connector ${sub} <name>`));
1322
+ return 2;
1323
+ }
1324
+ if (!await load()) return 1;
1325
+ const match = rows.find((c) => c.name === name);
1326
+ if (!match) {
1327
+ console.error(red(`No connector named "${name}".`));
1328
+ return 1;
1329
+ }
1330
+ await patchJson(port, `/api/connectors/${match.id}`, { enabled: sub === "enable" });
1331
+ console.log(green(`Connector "${name}" ${sub}d.`));
1332
+ return 0;
1333
+ }
1334
+ case "remove": {
1335
+ const name = argv[1];
1336
+ if (!name) {
1337
+ console.error(red("Usage: antbot connector remove <name>"));
1338
+ return 2;
1339
+ }
1340
+ if (!await load()) return 1;
1341
+ const match = rows.find((c) => c.name === name);
1342
+ if (!match) {
1343
+ console.error(red(`No connector named "${name}".`));
1344
+ return 1;
1345
+ }
1346
+ await deleteJson(port, `/api/connectors/${match.id}`);
1347
+ console.log(green(`Removed connector "${name}". Bot assignments for it are gone too.`));
1348
+ return 0;
1349
+ }
1350
+ case "test": {
1351
+ const name = argv[1];
1352
+ if (!name) {
1353
+ console.error(red("Usage: antbot connector test <name>"));
1354
+ return 2;
1355
+ }
1356
+ let match;
1357
+ try {
1358
+ match = await findByName(port, name);
1359
+ } catch (err) {
1360
+ console.error(red(`Could not reach the daemon on port ${port}: ${err.message}`));
1361
+ return 1;
1362
+ }
1363
+ if (!match) {
1364
+ console.error(red(`No connector named "${name}".`));
1365
+ return 1;
1366
+ }
1367
+ const result = await postJson(port, `/api/connectors/${match.id}/test`, {});
1368
+ if (!result.ok) {
1369
+ console.error(red(`${name}: ${result.error ?? "could not connect"}`));
1370
+ return 1;
1371
+ }
1372
+ console.log(green(`${name}: connected, ${result.tools.length} tool(s)`));
1373
+ for (const t of result.tools) {
1374
+ console.log(` ${bold(`mcp__${name}__${t.name}`)}`);
1375
+ if (t.description) console.log(` ${dim(t.description)}`);
1376
+ }
1377
+ return 0;
1378
+ }
1379
+ default:
1380
+ console.error(red(`Unknown subcommand "${sub}".`));
1381
+ hint();
1382
+ return 2;
1383
+ }
1384
+ }
1385
+
1163
1386
  // cli/src/updateCommand.ts
1164
1387
  import fs9 from "node:fs";
1165
1388
  import path10 from "node:path";
@@ -1538,6 +1761,11 @@ async function main() {
1538
1761
  process.exit(await runSkillCommand(parsed.positionals, cfg.port));
1539
1762
  return;
1540
1763
  }
1764
+ case "connector": {
1765
+ const cfg = loadConfig();
1766
+ process.exit(await runConnectorCommand(parsed.positionals, cfg.port));
1767
+ return;
1768
+ }
1541
1769
  case "backup":
1542
1770
  process.exit(await runBackupCommand(typeof parsed.flags.out === "string" ? parsed.flags.out : void 0));
1543
1771
  return;