@arbiterai/cli 0.1.0 → 0.1.1

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.
Files changed (2) hide show
  1. package/dist/index.js +194 -42
  2. package/package.json +8 -9
package/dist/index.js CHANGED
@@ -31,7 +31,7 @@ var require_package = __commonJS({
31
31
  "package.json"(exports2, module2) {
32
32
  module2.exports = {
33
33
  name: "@arbiterai/cli",
34
- version: "0.1.0",
34
+ version: "0.1.1",
35
35
  description: "Official CLI for Arbiter \u2014 the developer-first MCP gateway",
36
36
  license: "MIT",
37
37
  keywords: [
@@ -305,9 +305,6 @@ function printError(msg) {
305
305
  console.error(import_chalk.default.red("\u2717") + " " + msg);
306
306
  process.exit(1);
307
307
  }
308
- function printWarning(msg) {
309
- console.warn(import_chalk.default.yellow("\u26A0") + " " + msg);
310
- }
311
308
 
312
309
  // src/commands/login.ts
313
310
  function registerLogin(program2) {
@@ -502,6 +499,44 @@ function registerAgentCommands(program2) {
502
499
  registerAgentDelete(agentCmd);
503
500
  }
504
501
 
502
+ // src/commands/mcp-servers/list.ts
503
+ function registerMcpServersList(mcpServersCmd) {
504
+ mcpServersCmd.command("list").description("List registered MCP servers").option("--json", "Output raw JSON").action(async (opts) => {
505
+ requireAuth();
506
+ let page;
507
+ try {
508
+ page = await get("/api/v1/mcp-servers", { skip: 0, limit: 200 });
509
+ } catch (err) {
510
+ if (err instanceof ApiError) {
511
+ printError(`API error: ${err.detail}`);
512
+ }
513
+ throw err;
514
+ }
515
+ if (opts.json) {
516
+ console.log(JSON.stringify(page, null, 2));
517
+ return;
518
+ }
519
+ if (page.items.length === 0) {
520
+ printTable(["Name", "Base URL", "Cache", "Active"], []);
521
+ console.log("No MCP servers registered.");
522
+ return;
523
+ }
524
+ const rows = page.items.map((s) => [
525
+ s.name,
526
+ s.base_url,
527
+ s.cache_enabled ? "yes" : "no",
528
+ s.is_active ? "yes" : "no"
529
+ ]);
530
+ printTable(["Name", "Base URL", "Cache", "Active"], rows);
531
+ });
532
+ }
533
+
534
+ // src/commands/mcp-servers/index.ts
535
+ function registerMcpServersCommands(program2) {
536
+ const mcpServersCmd = program2.command("mcp-servers").description("Manage registered MCP servers");
537
+ registerMcpServersList(mcpServersCmd);
538
+ }
539
+
505
540
  // src/commands/permissions/grant.ts
506
541
  function registerPermissionsGrant(permissionsCmd) {
507
542
  permissionsCmd.command("grant").description("Grant a tool permission to an agent").requiredOption("--agent <id>", "Agent ID").requiredOption("--tool <tool>", "Tool name (e.g. read_file, or * for all tools)").requiredOption("--server <name>", "MCP server name").option("--json", "Output raw JSON").action(async (opts) => {
@@ -578,57 +613,116 @@ function formatDate2(iso) {
578
613
  return d.toISOString().replace("T", " ").slice(0, 16);
579
614
  }
580
615
 
616
+ // src/commands/permissions/revoke.ts
617
+ function registerPermissionsRevoke(permissionsCmd) {
618
+ permissionsCmd.command("revoke").description("Revoke a tool permission from an agent").requiredOption("--agent <id>", "Agent ID").requiredOption("--tool <tool>", "Tool name (e.g. read_file, or * for wildcard)").requiredOption("--server <name>", "MCP server name").action(async (opts) => {
619
+ requireAuth();
620
+ const serverPage = await get("/api/v1/mcp-servers", { skip: 0, limit: 200 });
621
+ const server = serverPage.items.find(
622
+ (s) => s.name.toLowerCase() === opts.server.toLowerCase()
623
+ );
624
+ if (!server) {
625
+ printError(`MCP server '${opts.server}' not found.`);
626
+ }
627
+ let page;
628
+ try {
629
+ page = await get(`/api/v1/agents/${opts.agent}/permissions`);
630
+ } catch (err) {
631
+ if (err instanceof ApiError && err.status === 404) {
632
+ printError("Agent not found.");
633
+ }
634
+ throw err;
635
+ }
636
+ const permission = page.items.find(
637
+ (p) => p.mcp_server_id === server.id && p.tool_name === opts.tool
638
+ );
639
+ if (!permission) {
640
+ printError(`No permission found for tool '${opts.tool}' on server '${opts.server}'.`);
641
+ }
642
+ try {
643
+ await del(`/api/v1/agents/${opts.agent}/permissions/${permission.id}`);
644
+ } catch (err) {
645
+ if (err instanceof ApiError) {
646
+ printError(`API error: ${err.detail}`);
647
+ }
648
+ throw err;
649
+ }
650
+ printSuccess(`Permission revoked: ${opts.agent} \u2192 ${opts.server}/${opts.tool}`);
651
+ });
652
+ }
653
+
581
654
  // src/commands/permissions/index.ts
582
655
  function registerPermissionsCommands(program2) {
583
656
  const permissionsCmd = program2.command("permissions").description("Manage agent tool permissions");
584
657
  registerPermissionsGrant(permissionsCmd);
585
658
  registerPermissionsList(permissionsCmd);
659
+ registerPermissionsRevoke(permissionsCmd);
586
660
  }
587
661
 
588
662
  // src/commands/vault/set.ts
663
+ var readline2 = __toESM(require("readline"));
589
664
  var SECRET_NAME_RE = /^[A-Za-z0-9_]+$/;
665
+ function promptSecret(prompt) {
666
+ return new Promise((resolve) => {
667
+ const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
668
+ process.stdout.write(prompt);
669
+ rl.output.write = () => true;
670
+ rl.question("", (answer) => {
671
+ rl.close();
672
+ process.stdout.write("\n");
673
+ resolve(answer);
674
+ });
675
+ });
676
+ }
590
677
  function registerVaultSet(vaultCmd) {
591
- vaultCmd.command("set").description("Store a secret in the vault").requiredOption("--agent <id>", "Agent ID to scope the secret to").requiredOption("--key <key>", "Secret key name (e.g. OPENAI_API_KEY)").requiredOption("--value <value>", "Secret value").option("--json", "Output raw JSON").action(async (opts) => {
592
- requireAuth();
593
- if (!SECRET_NAME_RE.test(opts.key)) {
594
- printError(
595
- "Secret key must contain only letters, numbers, and underscores (A-Za-z0-9_)."
596
- );
597
- }
598
- if (process.stdout.isTTY) {
599
- printWarning(
600
- "Secret value provided via --value flag will appear in shell history."
601
- );
602
- console.warn(
603
- ` Consider using: read -s VAL && arbiter vault set --agent ${opts.agent} --key ${opts.key} --value "$VAL"`
604
- );
605
- console.warn("");
606
- }
607
- let secret;
608
- try {
609
- secret = await post("/api/v1/vault/secrets", {
610
- name: opts.key,
611
- value: opts.value,
612
- agent_id: opts.agent
613
- });
614
- } catch (err) {
615
- if (err instanceof ApiError) {
616
- if (err.status === 404) {
617
- printError("Agent not found.");
618
- }
619
- if (err.status === 402) {
620
- printError("Plan limit reached. Upgrade at https://arbiterai.dev/pricing");
678
+ vaultCmd.command("set").description("Store a secret in the vault").option("--agent <id>", "Agent ID to scope the secret to (omit for org-level secrets)").requiredOption("--key <key>", "Secret key name (e.g. OPENAI_API_KEY)").option(
679
+ "--value <value>",
680
+ "Secret value (omit to be prompted with masked input \u2014 keeps value out of ps aux)"
681
+ ).option("--json", "Output raw JSON").action(
682
+ async (opts) => {
683
+ requireAuth();
684
+ if (!SECRET_NAME_RE.test(opts.key)) {
685
+ printError(
686
+ "Secret key must contain only letters, numbers, and underscores (A-Za-z0-9_)."
687
+ );
688
+ }
689
+ let secretValue;
690
+ if (opts.value !== void 0) {
691
+ secretValue = opts.value;
692
+ } else if (process.stdin.isTTY) {
693
+ secretValue = await promptSecret(`Secret value for ${opts.key}: `);
694
+ if (!secretValue) printError("Secret value cannot be empty.");
695
+ } else {
696
+ secretValue = await new Promise((resolve) => {
697
+ let data = "";
698
+ process.stdin.on("data", (chunk) => data += chunk);
699
+ process.stdin.on("end", () => resolve(data.trim()));
700
+ });
701
+ }
702
+ let secret;
703
+ try {
704
+ secret = await post("/api/v1/vault/secrets", {
705
+ name: opts.key,
706
+ value: secretValue,
707
+ agent_id: opts.agent ?? null
708
+ });
709
+ } catch (err) {
710
+ if (err instanceof ApiError) {
711
+ if (err.status === 404) printError("Agent not found.");
712
+ if (err.status === 402)
713
+ printError("Plan limit reached. Upgrade at https://arbiterai.dev/pricing");
714
+ printError(`API error: ${err.detail}`);
621
715
  }
622
- printError(`API error: ${err.detail}`);
716
+ throw err;
623
717
  }
624
- throw err;
625
- }
626
- if (opts.json) {
627
- console.log(JSON.stringify(secret, null, 2));
628
- return;
718
+ if (opts.json) {
719
+ console.log(JSON.stringify(secret, null, 2));
720
+ return;
721
+ }
722
+ const scope = opts.agent ? `agent ${opts.agent}` : "org-level";
723
+ printSuccess(`Secret '${opts.key}' set (${scope}).`);
629
724
  }
630
- printSuccess(`Secret '${opts.key}' set for agent ${opts.agent}.`);
631
- });
725
+ );
632
726
  }
633
727
 
634
728
  // src/commands/vault/index.ts
@@ -637,6 +731,62 @@ function registerVaultCommands(program2) {
637
731
  registerVaultSet(vaultCmd);
638
732
  }
639
733
 
734
+ // src/commands/test-call.ts
735
+ var import_axios3 = __toESM(require("axios"));
736
+ function registerTestCall(program2) {
737
+ program2.command("test-call").description("Fire a tool call through the gateway from the terminal").requiredOption("--server <name>", "MCP server name").requiredOption("--tool <tool>", "Tool name to invoke").option("--params <json>", "Tool arguments as a JSON string (default: {})", "{}").option("--agent-key <key>", "Agent API key (overrides config)").option("--json", "Output raw JSON response").action(
738
+ async (opts) => {
739
+ requireAuth();
740
+ const serverPage = await get("/api/v1/mcp-servers", { skip: 0, limit: 200 });
741
+ const server = serverPage.items.find(
742
+ (s) => s.name.toLowerCase() === opts.server.toLowerCase()
743
+ );
744
+ if (!server) {
745
+ printError(`MCP server '${opts.server}' not found.`);
746
+ }
747
+ let params;
748
+ try {
749
+ params = JSON.parse(opts.params);
750
+ } catch {
751
+ printError(`--params must be valid JSON, e.g. '{"path": "/tmp/test.txt"}'`);
752
+ }
753
+ const cfg = getConfig();
754
+ const apiKey = opts.agentKey;
755
+ if (!apiKey) {
756
+ printError(
757
+ "An agent API key is required for proxy calls.\nPass --agent-key <key> or create an agent with `arbiter agent create`."
758
+ );
759
+ }
760
+ const baseUrl = (process.env["ARBITER_API_URL"] ?? cfg?.api_url ?? "https://api.arbiterai.dev").replace(/\/$/, "");
761
+ let result;
762
+ try {
763
+ const res = await import_axios3.default.post(
764
+ `${baseUrl}/api/v1/proxy/tool-call`,
765
+ { server_name: opts.server, tool_name: opts.tool, params },
766
+ { headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" } }
767
+ );
768
+ result = res.data;
769
+ } catch (err) {
770
+ if (import_axios3.default.isAxiosError(err)) {
771
+ const detail = err.response?.data?.detail ?? err.message;
772
+ printError(`Tool call failed (HTTP ${err.response?.status ?? 0}): ${detail}`);
773
+ }
774
+ throw err;
775
+ }
776
+ if (opts.json) {
777
+ console.log(JSON.stringify(result, null, 2));
778
+ return;
779
+ }
780
+ const cacheTag = result.cache_hit ? " [cached]" : "";
781
+ console.log(`
782
+ \u2713 ${opts.tool} on ${opts.server}${cacheTag} \u2014 ${result.duration_ms}ms
783
+ `);
784
+ console.log(JSON.stringify(result.result, null, 2));
785
+ console.log();
786
+ }
787
+ );
788
+ }
789
+
640
790
  // src/index.ts
641
791
  var pkg = require_package();
642
792
  var program = new import_commander.Command();
@@ -648,8 +798,10 @@ registerLogin(program);
648
798
  registerLogout(program);
649
799
  registerStatus(program);
650
800
  registerAgentCommands(program);
801
+ registerMcpServersCommands(program);
651
802
  registerPermissionsCommands(program);
652
803
  registerVaultCommands(program);
804
+ registerTestCall(program);
653
805
  program.parseAsync(process.argv).catch((err) => {
654
806
  if (err instanceof Error) {
655
807
  console.error(err.message);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arbiterai/cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Official CLI for Arbiter — the developer-first MCP gateway",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -25,13 +25,6 @@
25
25
  "engines": {
26
26
  "node": ">=18.0.0"
27
27
  },
28
- "scripts": {
29
- "build": "tsup",
30
- "dev": "tsup --watch",
31
- "lint": "eslint src --max-warnings 0",
32
- "type-check": "tsc --noEmit",
33
- "prepublishOnly": "npm run build"
34
- },
35
28
  "dependencies": {
36
29
  "axios": "^1.7.7",
37
30
  "chalk": "^5.3.0",
@@ -47,5 +40,11 @@
47
40
  "tsup": "^8.3.0",
48
41
  "typescript": "^5.6.3",
49
42
  "typescript-eslint": "^8.57.2"
43
+ },
44
+ "scripts": {
45
+ "build": "tsup",
46
+ "dev": "tsup --watch",
47
+ "lint": "eslint src --max-warnings 0",
48
+ "type-check": "tsc --noEmit"
50
49
  }
51
- }
50
+ }