troxy-cli 1.2.0 → 1.2.2
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 +65 -32
- package/package.json +8 -2
- package/src/api.js +5 -1
- package/src/auth.js +21 -6
- package/src/policies.js +7 -2
package/bin/troxy.js
CHANGED
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
import { runInit } from '../src/init.js';
|
|
3
3
|
import { runUninstall } from '../src/uninstall.js';
|
|
4
4
|
import { runMcp } from '../src/mcp-server.js';
|
|
5
|
-
import { runLogin, clearSession, requireKey } from '../src/auth.js';
|
|
6
|
-
import { runCards } from '../src/cards.js';
|
|
5
|
+
import { runLogin, clearSession, requireKey, getKeySource } from '../src/auth.js';
|
|
7
6
|
import { runPolicies } from '../src/policies.js';
|
|
8
7
|
import { runMcps } from '../src/mcps.js';
|
|
9
8
|
import { runActivity } from '../src/activity.js';
|
|
@@ -26,6 +25,26 @@ for (let i = 0; i < allArgs.length; i++) {
|
|
|
26
25
|
}
|
|
27
26
|
}
|
|
28
27
|
|
|
28
|
+
try { await _run(); } catch (err) { _handleError(err); }
|
|
29
|
+
|
|
30
|
+
function _handleError(err) {
|
|
31
|
+
if (err.code === 'UNAUTHORIZED') {
|
|
32
|
+
const source = getKeySource();
|
|
33
|
+
if (source === 'config') {
|
|
34
|
+
console.error('\n API key revoked or invalid.');
|
|
35
|
+
console.error(' Your saved key is no longer accepted by Troxy.');
|
|
36
|
+
console.error(' Run: npx troxy init --key <new-key> to reconnect.\n');
|
|
37
|
+
} else {
|
|
38
|
+
console.error('\n API key invalid or revoked.');
|
|
39
|
+
console.error(' Check the key in your Troxy dashboard → Connections.\n');
|
|
40
|
+
}
|
|
41
|
+
} else {
|
|
42
|
+
console.error(`\n Error: ${err.message}\n`);
|
|
43
|
+
}
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function _run() {
|
|
29
48
|
switch (command) {
|
|
30
49
|
// ── Setup ─────────────────────────────────────────────────────
|
|
31
50
|
case 'init':
|
|
@@ -37,6 +56,22 @@ switch (command) {
|
|
|
37
56
|
break;
|
|
38
57
|
|
|
39
58
|
// ── Auth ──────────────────────────────────────────────────────
|
|
59
|
+
case 'connect': {
|
|
60
|
+
const k = flags.key;
|
|
61
|
+
if (!k || !k.startsWith('txy-')) {
|
|
62
|
+
console.error('\n Usage: troxy connect --key txy-...\n');
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
// Validate key before saving
|
|
66
|
+
process.stdout.write('\n Validating key... ');
|
|
67
|
+
await api.agentStatus(k);
|
|
68
|
+
console.log('✓');
|
|
69
|
+
const { saveConfig } = await import('../src/config.js');
|
|
70
|
+
saveConfig({ apiKey: k });
|
|
71
|
+
console.log(' Key saved to ~/.troxy/config.json\n');
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
|
|
40
75
|
case 'login':
|
|
41
76
|
await runLogin(flags);
|
|
42
77
|
break;
|
|
@@ -52,10 +87,6 @@ switch (command) {
|
|
|
52
87
|
break;
|
|
53
88
|
|
|
54
89
|
// ── Resources (read-only: --key or saved config; write: login) ─
|
|
55
|
-
case 'cards':
|
|
56
|
-
await runCards(positional, flags);
|
|
57
|
-
break;
|
|
58
|
-
|
|
59
90
|
case 'policies':
|
|
60
91
|
await runPolicies(positional, flags);
|
|
61
92
|
break;
|
|
@@ -98,11 +129,10 @@ switch (command) {
|
|
|
98
129
|
|
|
99
130
|
// ── Shorthand: troxy list [resource] ──────────────────────────
|
|
100
131
|
case 'list':
|
|
101
|
-
if (!sub || sub === '
|
|
102
|
-
if (sub === 'policies') { await runPolicies(['list'], flags); break; }
|
|
132
|
+
if (!sub || sub === 'policies') { await runPolicies(['list'], flags); break; }
|
|
103
133
|
if (sub === 'mcps') { await runMcps(['list'], flags); break; }
|
|
104
134
|
if (sub === 'activity') { await runActivity(flags); break; }
|
|
105
|
-
console.error(` Unknown resource: ${sub}. Try:
|
|
135
|
+
console.error(` Unknown resource: ${sub}. Try: policies, mcps, activity\n`);
|
|
106
136
|
process.exit(1);
|
|
107
137
|
|
|
108
138
|
// ── Status ────────────────────────────────────────────────────
|
|
@@ -114,11 +144,15 @@ switch (command) {
|
|
|
114
144
|
|
|
115
145
|
// If we have a key, show enriched status
|
|
116
146
|
try {
|
|
117
|
-
const apiKey
|
|
118
|
-
const
|
|
147
|
+
const apiKey = requireKey(flags);
|
|
148
|
+
const source = getKeySource();
|
|
149
|
+
const data = await api.agentStatus(apiKey);
|
|
119
150
|
const { token, account } = data;
|
|
151
|
+
const keyNote = source === 'config'
|
|
152
|
+
? '(saved — run `troxy init` to change)'
|
|
153
|
+
: source === 'env' ? '(from TROXY_API_KEY env)' : '(passed via --key)';
|
|
120
154
|
console.log(`
|
|
121
|
-
|
|
155
|
+
Key: ${token.prefix} ${keyNote}
|
|
122
156
|
MCP: ${token.connected ? '● connected' : '○ offline'} last seen ${token.last_seen}
|
|
123
157
|
Fallback: ${token.default_action}
|
|
124
158
|
|
|
@@ -128,7 +162,7 @@ switch (command) {
|
|
|
128
162
|
Requests 24h: ${account.requests_24h}
|
|
129
163
|
Default action: ${account.default_action}
|
|
130
164
|
`);
|
|
131
|
-
} catch { console.log(); }
|
|
165
|
+
} catch (err) { if (err.code === 'UNAUTHORIZED') throw err; console.log(); }
|
|
132
166
|
|
|
133
167
|
// Version check
|
|
134
168
|
try {
|
|
@@ -152,31 +186,30 @@ switch (command) {
|
|
|
152
186
|
console.log(`
|
|
153
187
|
Troxy — AI payment control
|
|
154
188
|
|
|
155
|
-
|
|
156
|
-
|
|
189
|
+
First time on a machine? Run: npx troxy init --key <api-key>
|
|
190
|
+
This saves your key to ~/.troxy/config.json — no need to pass --key again.
|
|
157
191
|
|
|
158
192
|
Setup
|
|
159
|
-
troxy
|
|
160
|
-
troxy
|
|
161
|
-
troxy
|
|
162
|
-
troxy
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
troxy policies
|
|
167
|
-
troxy
|
|
168
|
-
troxy
|
|
169
|
-
troxy
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
Manage (requires login)
|
|
193
|
+
troxy connect --key <api-key> Save API key (CLI only — no MCP setup)
|
|
194
|
+
troxy init --key <api-key> Full setup: save key + configure MCP
|
|
195
|
+
troxy uninstall Remove Troxy from this machine
|
|
196
|
+
troxy status API health + which key is in use
|
|
197
|
+
|
|
198
|
+
Inspect (uses saved key — no flags needed after init)
|
|
199
|
+
troxy policies list
|
|
200
|
+
troxy policies describe --name "Block Amazon"
|
|
201
|
+
troxy mcps list
|
|
202
|
+
troxy activity [--limit 50] [--mine]
|
|
203
|
+
troxy insights [--period 7]
|
|
204
|
+
|
|
205
|
+
Manage (requires: npx troxy login)
|
|
174
206
|
troxy policies create --name "X" --action BLOCK --field amount --operator gte --value 500
|
|
175
207
|
troxy policies enable --name "X"
|
|
176
208
|
troxy policies disable --name "X"
|
|
177
209
|
troxy policies delete --name "X"
|
|
178
|
-
|
|
179
|
-
|
|
210
|
+
|
|
211
|
+
Override key for a single command: --key txy-...
|
|
180
212
|
`);
|
|
181
213
|
process.exit(command ? 1 : 0);
|
|
182
214
|
}
|
|
215
|
+
} // end _run
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "troxy-cli",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"description": "AI payment control — protect your agent's payments with policies",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -19,6 +19,12 @@
|
|
|
19
19
|
"bin/",
|
|
20
20
|
"src/"
|
|
21
21
|
],
|
|
22
|
-
"keywords": [
|
|
22
|
+
"keywords": [
|
|
23
|
+
"mcp",
|
|
24
|
+
"ai",
|
|
25
|
+
"payments",
|
|
26
|
+
"policy",
|
|
27
|
+
"agents"
|
|
28
|
+
],
|
|
23
29
|
"license": "MIT"
|
|
24
30
|
}
|
package/src/api.js
CHANGED
|
@@ -14,7 +14,11 @@ async function request(method, path, { apiKey, jwt, body } = {}) {
|
|
|
14
14
|
});
|
|
15
15
|
|
|
16
16
|
const data = await res.json();
|
|
17
|
-
if (!res.ok)
|
|
17
|
+
if (!res.ok) {
|
|
18
|
+
const err = new Error(data?.error || `HTTP ${res.status}`);
|
|
19
|
+
if (res.status === 401) err.code = 'UNAUTHORIZED';
|
|
20
|
+
throw err;
|
|
21
|
+
}
|
|
18
22
|
return data;
|
|
19
23
|
}
|
|
20
24
|
|
package/src/auth.js
CHANGED
|
@@ -33,17 +33,32 @@ export function requireJwt() {
|
|
|
33
33
|
return session.jwt;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
// Tracks how the last key was resolved — read by bin/troxy.js error handler
|
|
37
|
+
let _lastKeySource = null;
|
|
38
|
+
|
|
39
|
+
export function getKeySource() { return _lastKeySource; }
|
|
40
|
+
|
|
36
41
|
/**
|
|
37
|
-
* Resolve API key: --key flag → TROXY_API_KEY env → saved config.
|
|
42
|
+
* Resolve API key: --key flag → TROXY_API_KEY env → saved config (~/.troxy/config.json).
|
|
38
43
|
* Exits with a helpful message if nothing is found.
|
|
39
44
|
*/
|
|
40
45
|
export function requireKey(flags = {}) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
if (flags.key) {
|
|
47
|
+
_lastKeySource = 'flag';
|
|
48
|
+
return flags.key;
|
|
49
|
+
}
|
|
50
|
+
if (process.env.TROXY_API_KEY) {
|
|
51
|
+
_lastKeySource = 'env';
|
|
52
|
+
return process.env.TROXY_API_KEY;
|
|
53
|
+
}
|
|
54
|
+
const saved = loadConfig()?.apiKey;
|
|
55
|
+
if (saved) {
|
|
56
|
+
_lastKeySource = 'config';
|
|
57
|
+
return saved;
|
|
45
58
|
}
|
|
46
|
-
|
|
59
|
+
console.error('\n No API key found.');
|
|
60
|
+
console.error(' Run: npx troxy init --key txy-... to connect this machine.\n');
|
|
61
|
+
process.exit(1);
|
|
47
62
|
}
|
|
48
63
|
|
|
49
64
|
function loadConfig() {
|
package/src/policies.js
CHANGED
|
@@ -3,7 +3,12 @@ import { requireJwt, requireKey } from './auth.js';
|
|
|
3
3
|
import { table } from './print.js';
|
|
4
4
|
|
|
5
5
|
const DECISION_ICON = { ALLOW: '✓', BLOCK: '✗', ESCALATE: '⏳', NOTIFY: '~', TIERED: '⊕' };
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
function _scope(p) {
|
|
8
|
+
if (p.global !== false) return 'all MCPs';
|
|
9
|
+
if (p.mcps && p.mcps.length > 0) return p.mcps.map(m => m.name || m.token_prefix || 'MCP').join(', ');
|
|
10
|
+
return 'no MCPs applied';
|
|
11
|
+
}
|
|
7
12
|
|
|
8
13
|
export async function runPolicies([sub, ...args], flags) {
|
|
9
14
|
// Read-only subcommands work with just an API key
|
|
@@ -23,7 +28,7 @@ export async function runPolicies([sub, ...args], flags) {
|
|
|
23
28
|
p.priority,
|
|
24
29
|
p.name,
|
|
25
30
|
p.action,
|
|
26
|
-
|
|
31
|
+
_scope(p),
|
|
27
32
|
p.enabled ? 'enabled' : 'disabled',
|
|
28
33
|
_condSummary(p),
|
|
29
34
|
p.applies_to_me ? '✓' : '—',
|