troxy-cli 1.4.3 → 1.4.5

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/bin/troxy.js CHANGED
@@ -143,7 +143,9 @@ switch (command) {
143
143
 
144
144
  // ── Simulate a payment evaluation ────────────────────────────
145
145
  case 'pay': {
146
- const apiKey = requireKey(flags);
146
+ requireJwt();
147
+ const apiKey = loadConfig()?.apiKey || process.env.TROXY_API_KEY;
148
+ if (!apiKey) { console.error(' No API key. Run: troxy init --key txy-...\n'); process.exit(1); }
147
149
  const merchant = flags.merchant;
148
150
  const amount = parseFloat(flags.amount);
149
151
  const card = flags.card || 'Work';
@@ -286,30 +288,37 @@ switch (command) {
286
288
  MCP setup (once per machine): troxy init --key <api-key>
287
289
  Login for CLI commands (12h): troxy login
288
290
 
289
- MCP Setup
291
+ Setup (one time, API key required)
290
292
  troxy init --key <api-key> Connect this machine as an MCP + save key
291
- troxy rotate-key Create new MCP key + save it
292
- troxy rotate-key --revoke-old Same + revoke the old key immediately
293
293
  troxy uninstall Remove Troxy from this machine
294
- troxy status API health + account overview
295
- troxy pause Pause this MCP (blocks all payments until resumed)
296
- troxy resume Resume this MCP after a pause
294
+ troxy status API health + MCP status (no login needed)
295
+
296
+ Everything else requires: troxy login
297
+
298
+ MCP
299
+ troxy pause Pause this MCP (blocks all payments)
300
+ troxy resume Resume this MCP
301
+ troxy mcps list All MCPs in your account
302
+ troxy mcps rename --name "x" Rename this machine's MCP
297
303
 
298
- Inspect (requires: troxy login)
304
+ Keys
305
+ troxy rotate-key Create new MCP key + save it locally
306
+ troxy rotate-key --revoke-old Same + revoke the old key immediately
307
+
308
+ Policies
299
309
  troxy policies list
300
310
  troxy policies describe --name "Block Amazon"
301
- troxy mcps list
302
- troxy mcps rename --name "new-name"
303
- troxy activity [--limit 50] [--mine]
304
- troxy insights [--period 7]
305
-
306
- Manage (requires: troxy login)
307
311
  troxy policies create --name "X" --action BLOCK --field amount --operator gte --value 500
312
+ troxy policies create --name "X" --action BLOCK --mcp "My Laptop" (scoped to one MCP)
308
313
  troxy policies enable --name "X"
309
314
  troxy policies disable --name "X"
310
315
  troxy policies delete --name "X"
311
316
 
312
- Simulate (requires MCP key — run troxy init first)
317
+ Activity & insights
318
+ troxy activity [--limit 50] [--mine]
319
+ troxy insights [--period 7]
320
+
321
+ Simulate
313
322
  troxy pay --merchant "Amazon" --amount 50 --card "Work"
314
323
  troxy pay --merchant "Google" --amount 300 --card "Work" --category software
315
324
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "troxy-cli",
3
- "version": "1.4.3",
3
+ "version": "1.4.5",
4
4
  "description": "AI payment control — protect your agent's payments with policies",
5
5
  "type": "module",
6
6
  "bin": {
package/src/api.js CHANGED
@@ -58,11 +58,18 @@ export const api = {
58
58
  // MCP heartbeat (agent API key)
59
59
  mcpHeartbeat: (apiKey, agentName) => request('POST', '/mcp/heartbeat', { apiKey, body: agentName ? { agent_name: agentName } : undefined }),
60
60
 
61
- // MCP status / rename / pause / resume (agent API key)
62
- mcpStatus: (apiKey) => request('GET', '/mcp/status', { apiKey }),
63
- mcpRename: (apiKey, name) => request('PATCH', '/mcp/name', { apiKey, body: { name } }),
64
- mcpPause: (apiKey) => request('POST', '/mcp/pause', { apiKey }),
65
- mcpResume: (apiKey) => request('POST', '/mcp/resume', { apiKey }),
61
+ // MCP status (agent API key no login needed)
62
+ mcpStatus: (apiKey) => request('GET', '/mcp/status', { apiKey }),
63
+
64
+ // MCP pause / resume / rename via JWT (requires troxy login)
65
+ pauseToken: (jwt, id) => request('POST', `/tokens/${id}/pause`, { jwt }),
66
+ resumeToken: (jwt, id) => request('POST', `/tokens/${id}/resume`, { jwt }),
67
+ renameToken: (jwt, id, name) => request('PATCH', `/tokens/${id}/name`, { jwt, body: { name } }),
68
+
69
+ // MCP daemon endpoints (agent API key — used by MCP server only)
70
+ mcpPause: (apiKey) => request('POST', '/mcp/pause', { apiKey }),
71
+ mcpResume: (apiKey) => request('POST', '/mcp/resume', { apiKey }),
72
+ mcpRename: (apiKey, name) => request('PATCH', '/mcp/name', { apiKey, body: { name } }),
66
73
 
67
74
  // Agent read-only API (JWT session auth — run: troxy login)
68
75
  agentStatus: (jwt) => request('GET', '/agent/status', { jwt }),
package/src/mcps.js CHANGED
@@ -28,18 +28,16 @@ export async function runMcps([sub], flags) {
28
28
 
29
29
  case 'rename': {
30
30
  const name = flags.name;
31
- if (!name) {
32
- console.error('\n Usage: troxy mcps rename --name "new-name"\n');
33
- process.exit(1);
34
- }
31
+ if (!name) { console.error('\n Usage: troxy mcps rename --name "new-name"\n'); process.exit(1); }
32
+ const jwt = requireJwt();
35
33
  const config = loadConfig();
36
- const apiKey = process.env.TROXY_API_KEY || config?.apiKey;
37
- if (!apiKey) {
38
- console.error('\n No API key found. Run: troxy init --key txy-...\n');
39
- process.exit(1);
40
- }
34
+ const prefix = (config?.apiKey || '').substring(0, 11);
35
+ if (!prefix) { console.error('\n No API key found. Run: troxy init --key txy-...\n'); process.exit(1); }
41
36
  process.stdout.write(`\n Renaming MCP to "${name}"... `);
42
- await api.mcpRename(apiKey, name);
37
+ const { tokens = [] } = await api.listTokens(jwt);
38
+ const tok = tokens.find(t => t.prefix === prefix);
39
+ if (!tok) { console.error('\nCould not find this machine\'s MCP. Run: troxy init --key txy-...\n'); process.exit(1); }
40
+ await api.renameToken(jwt, tok.id, name);
43
41
  saveConfig({ ...config, agentName: name });
44
42
  console.log('✓');
45
43
  console.log(' Dashboard and future heartbeats will use the new name.\n');
package/src/pause.js CHANGED
@@ -1,25 +1,33 @@
1
1
  import { loadConfig } from './config.js';
2
+ import { requireJwt } from './auth.js';
2
3
  import { api } from './api.js';
3
4
 
5
+ async function _localTokenId(jwt, config) {
6
+ const prefix = (config?.apiKey || '').substring(0, 11);
7
+ if (!prefix) throw new Error('No API key found. Run: troxy init --key txy-...');
8
+ const { tokens = [] } = await api.listTokens(jwt);
9
+ const tok = tokens.find(t => t.prefix === prefix);
10
+ if (!tok) throw new Error('This machine\'s MCP key was not found in your account. Run: troxy init --key txy-...');
11
+ return tok.id;
12
+ }
13
+
4
14
  export async function runPause() {
15
+ const jwt = requireJwt();
5
16
  const config = loadConfig();
6
- const apiKey = process.env.TROXY_API_KEY || config?.apiKey;
7
- if (!apiKey) {
8
- console.error('No API key found. Run: npx troxy init --key txy-...');
9
- process.exit(1);
10
- }
11
- await api.mcpPause(apiKey);
12
- console.log('⏸ MCP paused. All payment evaluations will be blocked.');
13
- console.log(' Run "troxy resume" to resume.');
17
+ process.stdout.write('\n Pausing MCP... ');
18
+ const id = await _localTokenId(jwt, config);
19
+ await api.pauseToken(jwt, id);
20
+ console.log('✓');
21
+ console.log(' ⏸ All payment evaluations are now blocked.');
22
+ console.log(' Run "troxy resume" to resume.\n');
14
23
  }
15
24
 
16
25
  export async function runResume() {
26
+ const jwt = requireJwt();
17
27
  const config = loadConfig();
18
- const apiKey = process.env.TROXY_API_KEY || config?.apiKey;
19
- if (!apiKey) {
20
- console.error('No API key found. Run: npx troxy init --key txy-...');
21
- process.exit(1);
22
- }
23
- await api.mcpResume(apiKey);
24
- console.log('▶ MCP resumed. Payment evaluations are active again.');
28
+ process.stdout.write('\n Resuming MCP... ');
29
+ const id = await _localTokenId(jwt, config);
30
+ await api.resumeToken(jwt, id);
31
+ console.log('✓');
32
+ console.log(' ▶ Payment evaluations are active again.\n');
25
33
  }
package/src/policies.js CHANGED
@@ -82,8 +82,30 @@ export async function runPolicies([sub, ...args], flags) {
82
82
  if (flags.value2) cond.value2 = flags.value2;
83
83
  conditions.push(cond);
84
84
  }
85
- const policy = await api.createPolicy(jwt, { name, action, conditions, enabled: true });
86
- console.log(`\n Policy "${policy.name}" created ✓ (priority: ${policy.priority})\n`);
85
+
86
+ // --mcp <name>: scope policy to a specific MCP instead of all
87
+ let isGlobal = true;
88
+ let mcpIds = [];
89
+ if (flags.mcp) {
90
+ const { tokens = [] } = await api.listTokens(jwt);
91
+ const needle = flags.mcp.toLowerCase();
92
+ const match = tokens.find(t =>
93
+ (t.name && t.name.toLowerCase() === needle) ||
94
+ (t.agent_name && t.agent_name.toLowerCase() === needle) ||
95
+ (t.prefix && t.prefix.toLowerCase().startsWith(needle))
96
+ );
97
+ if (!match) {
98
+ console.error(`\n MCP "${flags.mcp}" not found. Run: troxy mcps list\n`);
99
+ process.exit(1);
100
+ }
101
+ isGlobal = false;
102
+ mcpIds = [match.id];
103
+ console.log(`\n Scoping to MCP: ${match.name || match.agent_name || match.prefix}`);
104
+ }
105
+
106
+ const policy = await api.createPolicy(jwt, { name, action, conditions, enabled: true, global: isGlobal, mcp_ids: mcpIds });
107
+ const scope = isGlobal ? 'all MCPs' : (policy.mcps?.[0]?.name || flags.mcp);
108
+ console.log(`\n Policy "${policy.name}" created ✓ (priority: ${policy.priority}, scope: ${scope})\n`);
87
109
  break;
88
110
  }
89
111