troxy-cli 1.8.4 → 1.8.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "troxy-cli",
3
- "version": "1.8.4",
3
+ "version": "1.8.6",
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
@@ -71,6 +71,8 @@ export const api = {
71
71
 
72
72
  // Evaluate + confirm (agent API key)
73
73
  evaluate: (body, apiKey) => request('POST', '/evaluate', { apiKey, body }),
74
+ evaluateEmail: (body, apiKey) => request('POST', '/evaluate/email', { apiKey, body }),
75
+ selfRevoke: (apiKey) => request('POST', '/mcp/self-revoke', { apiKey }),
74
76
  confirmPayment: (auditId, body, apiKey) => request('POST', `/payments/${auditId}/confirm`, { apiKey, body }),
75
77
  waitApprovalStatus: (token) => request('GET', `/approvals/${encodeURIComponent(token)}/wait`),
76
78
 
package/src/mcp-server.js CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  import { loadConfig } from './config.js';
8
8
  import { evaluatePayment, api } from './api.js';
9
9
  const confirmPayment = (auditId, body, apiKey) => api.confirmPayment(auditId, body, apiKey);
10
+ const evaluateEmail = (body, apiKey) => api.evaluateEmail(body, apiKey);
10
11
 
11
12
  export async function runMcp() {
12
13
  const config = loadConfig();
@@ -38,7 +39,8 @@ export async function runMcp() {
38
39
  'If the decision is ESCALATE, the response includes an approval_token. Immediately call wait_for_payment_approval with that token; it polls automatically until the payment is approved or declined (up to 10 minutes). Once it returns approved, call evaluate_payment again with the same payment details PLUS the approval_token field. The second call will return ALLOW without re-escalating. ' +
39
40
  'If the decision is NOTIFY, proceed but the user will be notified. ' +
40
41
  'When calling evaluate_payment, use the merchant\'s domain name as merchant_name when available (e.g. "amazon.com", "indigobloom.co.il"); this ensures consistent matching against policies. ' +
41
- 'After every ALLOW or NOTIFY decision, attempt the payment, then call confirm_payment with the audit_id and the outcome (success/failed/cancelled). Always include a reason when the charge fails.',
42
+ 'After every ALLOW or NOTIFY decision, attempt the payment, then call confirm_payment with the audit_id and the outcome (success/failed/cancelled). Always include a reason when the charge fails. ' +
43
+ 'You MUST also call the evaluate_email tool before sending any email on the user\'s behalf. Never send an email without first receiving an ALLOW or NOTIFY decision. If BLOCK, do not send it. If ESCALATE, call wait_for_payment_approval with the returned approval_token (same wait mechanism payments use), then call evaluate_email again with the approval_token once approved.',
42
44
  },
43
45
  { capabilities: { tools: {} } },
44
46
  );
@@ -133,6 +135,34 @@ export async function runMcp() {
133
135
  },
134
136
  },
135
137
  },
138
+ {
139
+ name: 'evaluate_email',
140
+ description:
141
+ 'Evaluate whether sending an email should be allowed, blocked, or escalated ' +
142
+ 'based on your Troxy policies. Call this before sending any email on the user\'s behalf.',
143
+ inputSchema: {
144
+ type: 'object',
145
+ required: ['recipient'],
146
+ properties: {
147
+ recipient: {
148
+ type: 'string',
149
+ description: 'Email address of the recipient',
150
+ },
151
+ subject: {
152
+ type: 'string',
153
+ description: 'Subject line of the email (optional)',
154
+ },
155
+ agent: {
156
+ type: 'string',
157
+ description: 'Name of the agent sending the email (optional)',
158
+ },
159
+ approval_token: {
160
+ type: 'string',
161
+ description: 'Approval token from a previous ESCALATE response. Include this to proceed after the user has approved.',
162
+ },
163
+ },
164
+ },
165
+ },
136
166
  ],
137
167
  }));
138
168
 
@@ -203,6 +233,45 @@ export async function runMcp() {
203
233
  }
204
234
  }
205
235
 
236
+ if (toolName === 'evaluate_email') {
237
+ if (agentName && !args.agent) args.agent = agentName;
238
+ let result;
239
+ try {
240
+ result = await evaluateEmail(args, apiKey);
241
+ } catch (err) {
242
+ return {
243
+ content: [{ type: 'text', text: `Troxy error: ${err.message}` }],
244
+ isError: true,
245
+ };
246
+ }
247
+ if (result.error) {
248
+ return {
249
+ content: [{ type: 'text', text: `Troxy error: ${result.error}` }],
250
+ isError: true,
251
+ };
252
+ }
253
+ const { decision, reason, audit_id, approval_token } = result;
254
+ let emailText;
255
+ switch (decision) {
256
+ case 'ALLOW':
257
+ case 'NOTIFY':
258
+ emailText = `✓ Email approved.${reason ? ` ${reason}` : ''} You may send it now. (audit: ${audit_id})`;
259
+ break;
260
+ case 'BLOCK':
261
+ emailText = `✗ Email blocked.${reason ? ` ${reason}` : ''} Do not send it. (audit: ${audit_id})`;
262
+ break;
263
+ case 'ESCALATE':
264
+ emailText = `⏳ Email requires human approval; a request has been sent to the account owner.\n\nApproval token: ${approval_token}\n\nNow call wait_for_payment_approval(approval_token="${approval_token}") to automatically detect approval, then call evaluate_email again with the same recipient/subject PLUS this approval_token. Do not send until it returns approved.`;
265
+ break;
266
+ default:
267
+ emailText = JSON.stringify(result);
268
+ }
269
+ return {
270
+ content: [{ type: 'text', text: emailText }],
271
+ isError: decision === 'BLOCK',
272
+ };
273
+ }
274
+
206
275
  if (toolName !== 'evaluate_payment') {
207
276
  throw new Error(`Unknown tool: ${toolName}`);
208
277
  }
package/src/uninstall.js CHANGED
@@ -4,6 +4,7 @@ import path from 'path';
4
4
  import { execSync } from 'child_process';
5
5
  import readline from 'readline';
6
6
  import { loadConfig } from './config.js';
7
+ import { api } from './api.js';
7
8
 
8
9
  const MCP_CLIENTS = [
9
10
  {
@@ -107,12 +108,21 @@ export async function runUninstall() {
107
108
  console.log('none found');
108
109
  }
109
110
 
110
- // 3. Warn about API key (requires login to revoke user must do it from dashboard)
111
+ // 3. Revoke the API key. /mcp/self-revoke only lets a key revoke the exact
112
+ // token it authenticates as — it can't touch or list any other token — so
113
+ // this doesn't need a login/JWT, the key on disk is enough.
111
114
  const cfg = loadConfig();
112
115
  if (cfg?.apiKey) {
113
116
  const prefix = cfg.apiKey.slice(0, 12) + '...';
114
- console.log(`\n API key ${prefix} was NOT revoked automatically.`);
115
- console.log(' Go to dash.troxy.io → Settings → API Keys to revoke it.\n');
117
+ process.stdout.write(` Revoking API key ${prefix}... `);
118
+ try {
119
+ await api.selfRevoke(cfg.apiKey);
120
+ console.log('✓');
121
+ } catch (err) {
122
+ console.log('✗');
123
+ console.log(` Couldn't revoke automatically (${err.message}).`);
124
+ console.log(' Go to dash.troxy.io → Settings → API Keys to revoke it.\n');
125
+ }
116
126
  }
117
127
 
118
128
  // 4. Delete ~/.troxy config