troxy-cli 1.30.12 → 1.30.14
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/README.md +1 -1
- package/bin/troxy.js +15 -99
- package/package.json +2 -2
- package/src/sandbox/posttooluse-hook.js +18 -1
- package/src/tests/posttooluse-hook.test.js +22 -0
- package/src/tests/proxy-wiring.test.js +0 -6
package/README.md
CHANGED
package/bin/troxy.js
CHANGED
|
@@ -570,14 +570,13 @@ switch (command) {
|
|
|
570
570
|
const { account } = data;
|
|
571
571
|
console.log(`
|
|
572
572
|
Account
|
|
573
|
-
Active policies: ${account.active_policies}
|
|
574
573
|
MCPs total: ${account.total_mcps} (${account.connected_mcps} connected)
|
|
575
574
|
Requests 24h: ${account.requests_24h}
|
|
576
575
|
Default action: ${account.default_action}
|
|
577
576
|
`);
|
|
578
577
|
} catch { console.log(); }
|
|
579
578
|
} else {
|
|
580
|
-
console.log(' Account: not signed in. Run `troxy login` to
|
|
579
|
+
console.log(' Account: not signed in. Run `troxy login` to view usage and activity from the CLI.\n');
|
|
581
580
|
}
|
|
582
581
|
|
|
583
582
|
// Version check
|
|
@@ -771,106 +770,23 @@ switch (command) {
|
|
|
771
770
|
default:
|
|
772
771
|
if (command) console.error(` Unknown command: ${command}\n`);
|
|
773
772
|
console.log(`
|
|
774
|
-
Troxy:
|
|
775
|
-
|
|
776
|
-
This CLI has two parts:
|
|
777
|
-
- Commands below (policies, mcps, activity, ...) are for YOU to run and manage your account.
|
|
778
|
-
- "troxy mcp" starts the MCP server an AI agent connects to. You don't run it by hand;
|
|
779
|
-
MCP clients (Claude Desktop, etc.) launch it after "troxy init" configures them.
|
|
780
|
-
|
|
781
|
-
First time? Run these two commands in order:
|
|
782
|
-
1) npx troxy-cli init --key txy-... (get key from https://dash.troxy.io)
|
|
783
|
-
2) troxy login (start a 12h CLI session)
|
|
784
|
-
|
|
785
|
-
MCP setup (once per machine): troxy init --key <api-key>
|
|
786
|
-
Login for CLI commands (12h): troxy login
|
|
787
|
-
|
|
788
|
-
Setup (one time, API key required)
|
|
789
|
-
troxy init --key <api-key> Connect this machine as an MCP + save key
|
|
790
|
-
troxy init --key <api-key> --name "My Agent" Same, no interactive prompt
|
|
791
|
-
(for cloud/scripted agents that can't answer stdin)
|
|
792
|
-
troxy tools Scan for AI tools + report them to the dashboard
|
|
793
|
-
troxy restart Restart the MCP background service
|
|
794
|
-
troxy uninstall Remove Troxy from this machine
|
|
795
|
-
troxy status API health + MCP status (no login needed)
|
|
796
|
-
troxy update Update to the latest version
|
|
773
|
+
Troxy: make your Claude usage go further
|
|
797
774
|
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
connection above)
|
|
801
|
-
troxy proxy status Show whether it's on, and cert/interceptor health
|
|
802
|
-
troxy proxy enable --experimental Turn it on (installs a local CA cert)
|
|
803
|
-
troxy proxy disable Turn it off - removes the settings.json patch
|
|
804
|
-
|
|
805
|
-
Everything else requires: troxy login
|
|
806
|
-
|
|
807
|
-
MCP
|
|
808
|
-
troxy pause Pause this MCP (blocks all checkpoints)
|
|
809
|
-
troxy resume Resume this MCP
|
|
810
|
-
troxy mcps list All MCPs in your account
|
|
811
|
-
troxy mcps rename --name "x" Rename this machine's MCP
|
|
812
|
-
troxy mcps create --name "x" Create a key for another agent/machine
|
|
813
|
-
troxy mcps revoke --mcp "x" Revoke an MCP's key
|
|
814
|
-
troxy mcps pause/resume --mcp "x" Pause/resume any MCP (not just this one)
|
|
815
|
-
troxy mcps budget --mcp "x" --currency USD --limit 500
|
|
816
|
-
troxy mcps set-default-action --mcp "x" --action BLOCK
|
|
817
|
-
|
|
818
|
-
Keys
|
|
819
|
-
troxy rotate-key Rotate MCP key (revokes old, saves new locally)
|
|
820
|
-
|
|
821
|
-
Policies (all six checkpoints: payment, comms, access, destructive, model, secrets)
|
|
822
|
-
Admin-only: every write below requires --org. Personal and per-MCP policies
|
|
823
|
-
no longer exist - a policy is always org-wide or space-wide, and org acts as
|
|
824
|
-
a ceiling a space policy can never be looser than. list/describe/test need
|
|
825
|
-
no --org and show every org + space policy that applies to you.
|
|
826
|
-
troxy policies list / list --org
|
|
827
|
-
troxy policies describe --name "Block Amazon"
|
|
828
|
-
troxy policies templates (browse the built-in library)
|
|
829
|
-
troxy policies fields --domain secrets (see what each checkpoint supports)
|
|
830
|
-
troxy policies create --describe "block Amazon purchases over $200" --org (AI builder, payment/comms only)
|
|
831
|
-
troxy policies create --name "X" --template "Block a secret going somewhere public" --org
|
|
832
|
-
troxy policies create --name "X" --action BLOCK --domain payment --field amount --operator gte --value 500 --org
|
|
833
|
-
troxy policies create --name "X" --action BLOCK --domain access --field login_action --operator eq --value signup --org
|
|
834
|
-
troxy policies create --name "X" --action BLOCK --domain comms --or "field=recipient_domain,operator=contains,value=gmail.com" --org
|
|
835
|
-
troxy policies create --name "X" --action BLOCK --domain secrets --field has_api_key --operator eq --value true --org
|
|
836
|
-
troxy policies test --domain payment --field amount --operator gt --value 100 --example '{"amount": 150}'
|
|
837
|
-
troxy policies set-priority --name "X" --priority 10 --org
|
|
838
|
-
troxy policies pause --name "X" --org
|
|
839
|
-
troxy policies resume --name "X" --org
|
|
840
|
-
troxy policies delete --name "X" --org
|
|
841
|
-
(--org needs admin access; run 'troxy policies create --help' for the full condition syntax)
|
|
842
|
-
|
|
843
|
-
Approvals (ESCALATE holds)
|
|
844
|
-
troxy approvals list
|
|
845
|
-
troxy approvals approve --id <id>
|
|
846
|
-
troxy approvals decline --id <id>
|
|
847
|
-
|
|
848
|
-
Settings
|
|
849
|
-
troxy settings show
|
|
850
|
-
troxy settings set --default-action BLOCK --approval-timeout 4
|
|
851
|
-
|
|
852
|
-
LLM provider keys
|
|
853
|
-
troxy secrets list
|
|
854
|
-
troxy secrets set --name "OpenAI"
|
|
855
|
-
troxy secrets delete --name "OpenAI"
|
|
856
|
-
|
|
857
|
-
Account
|
|
858
|
-
troxy account clear-data Wipe activity/policies, keep account + keys
|
|
859
|
-
troxy account delete Permanently delete your account (irreversible)
|
|
775
|
+
First time? Create a key at https://dash.troxy.io, then run:
|
|
776
|
+
npx troxy-cli init --key txy-...
|
|
860
777
|
|
|
861
|
-
|
|
862
|
-
troxy
|
|
863
|
-
troxy
|
|
864
|
-
troxy
|
|
778
|
+
Commands
|
|
779
|
+
troxy init --key <api-key> Connect this machine (add --name "My Mac" to skip the prompt)
|
|
780
|
+
troxy status Health and connection status
|
|
781
|
+
troxy update Update to the latest version
|
|
782
|
+
troxy uninstall Remove Troxy from this machine
|
|
783
|
+
troxy login Start a 12h CLI session for the commands below
|
|
784
|
+
troxy usage-bar [--period 7] Your usage and what Troxy saved
|
|
785
|
+
troxy activity [--limit 50] Recent requests
|
|
786
|
+
troxy insights [--period 7] Trends
|
|
865
787
|
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
troxy pay --merchant "Google" --amount 300 --category software
|
|
869
|
-
troxy send --recipient jane@gmail.com --body "the password is hunter2"
|
|
870
|
-
troxy signin --site coinbase.com --type signup
|
|
871
|
-
troxy action --verb delete --resource "prod users table" --items 500
|
|
872
|
-
troxy model-call --model claude-opus-5 --provider anthropic --cost 4.50
|
|
873
|
-
troxy scan --content "AWS_SECRET_ACCESS_KEY=..." --destination public --do push
|
|
788
|
+
In Claude Code: /troxy status, /troxy off, /troxy on
|
|
789
|
+
Help: https://docs.troxy.io or support@troxy.io
|
|
874
790
|
`);
|
|
875
791
|
process.exit(command ? 1 : 0);
|
|
876
792
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "troxy-cli",
|
|
3
|
-
"version": "1.30.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.30.14",
|
|
4
|
+
"description": "Make your Claude Code usage go further: lighter requests, less usage, less money",
|
|
5
5
|
"homepage": "https://troxy.io",
|
|
6
6
|
"bugs": {
|
|
7
7
|
"email": "support@troxy.io"
|
|
@@ -107,6 +107,23 @@ function rebuildWithText(toolResponse, text) {
|
|
|
107
107
|
return { ...toolResponse, content: text };
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
// Only outputs Claude rarely fetches back get shortened. Measured 2026-10-23 on 1,435 shortened
|
|
111
|
+
// outputs: Grep 0% recalled; wc, ls, head, tail, test runs and logs 0-15%; Read of .js/.html
|
|
112
|
+
// 17-20%. Targeted reads (sed, cat, Bash grep, Read of .py/.md/.txt) came back 30-86%, so
|
|
113
|
+
// shortening them only adds a recall step.
|
|
114
|
+
const SHORTEN_COMMANDS = new Set(['wc', 'ls', 'head', 'tail', 'ps', 'gh', 'aws', 'pytest']);
|
|
115
|
+
const SHORTEN_READ_EXT = /\.(js|html)$/i;
|
|
116
|
+
|
|
117
|
+
export function isShortenable(toolName, toolInput) {
|
|
118
|
+
if (toolName === undefined) return true; // ponytail: payloads without tool_name (old tests); Claude Code always sends it
|
|
119
|
+
if (toolName === 'Grep') return true;
|
|
120
|
+
if (toolName === 'Read') return SHORTEN_READ_EXT.test(String(toolInput?.file_path || ''));
|
|
121
|
+
if (toolName !== 'Bash') return false;
|
|
122
|
+
const cmd = String(toolInput?.command || '').trim().replace(/^(cd\s+\S+\s*(&&|;)\s*)+/, '');
|
|
123
|
+
if (/^python3?\s+-m\s+pytest\b/.test(cmd)) return true;
|
|
124
|
+
return SHORTEN_COMMANDS.has(path.basename(cmd.split(/\s+/)[0] || ''));
|
|
125
|
+
}
|
|
126
|
+
|
|
110
127
|
export async function buildHookResponse(toolUseBlock, {
|
|
111
128
|
sandboxFraction = 1,
|
|
112
129
|
holdoutFraction = 0.2,
|
|
@@ -124,7 +141,7 @@ export async function buildHookResponse(toolUseBlock, {
|
|
|
124
141
|
return undefined;
|
|
125
142
|
}
|
|
126
143
|
const output = extractText(toolResponse);
|
|
127
|
-
if (output === null || !shouldSandbox(output)) {
|
|
144
|
+
if (output === null || !shouldSandbox(output) || !isShortenable(toolUseBlock?.tool_name, toolUseBlock?.tool_input)) {
|
|
128
145
|
writeState(storeDir, 'skipped');
|
|
129
146
|
return undefined;
|
|
130
147
|
}
|
|
@@ -271,3 +271,25 @@ test('posttooluse-hook is a no-op unless TROXY_COMPRESS=1', () => {
|
|
|
271
271
|
fs.rmSync(tmpHome, { recursive: true, force: true });
|
|
272
272
|
}
|
|
273
273
|
});
|
|
274
|
+
|
|
275
|
+
test('isShortenable: only output kinds Claude rarely fetches back', async () => {
|
|
276
|
+
const { isShortenable } = await import('../sandbox/posttooluse-hook.js');
|
|
277
|
+
assert.equal(isShortenable('Grep', {}), true);
|
|
278
|
+
assert.equal(isShortenable('Read', { file_path: '/a/b/app.js' }), true);
|
|
279
|
+
assert.equal(isShortenable('Read', { file_path: '/a/b/notes.md' }), false);
|
|
280
|
+
assert.equal(isShortenable('Read', { file_path: '/a/b/x.py' }), false);
|
|
281
|
+
assert.equal(isShortenable('Bash', { command: 'cd /repo && ls -la src' }), true);
|
|
282
|
+
assert.equal(isShortenable('Bash', { command: 'python3 -m pytest -q' }), true);
|
|
283
|
+
assert.equal(isShortenable('Bash', { command: 'sed -n 1,200p file.js' }), false);
|
|
284
|
+
assert.equal(isShortenable('Bash', { command: 'cat big.log' }), false);
|
|
285
|
+
assert.equal(isShortenable('mcp__claude_ai_Gmail__get_thread', {}), false);
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
test('a large sed output passes through unshortened', async () => {
|
|
289
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'troxy-sandbox-'));
|
|
290
|
+
const response = await buildHookResponse(
|
|
291
|
+
{ tool_name: 'Bash', tool_input: { command: 'sed -n 1,400p a.js' }, tool_response: bashResult('x'.repeat(5000)) },
|
|
292
|
+
{ sandboxFraction: 1, holdoutFraction: 0, randomFn: () => 0.5, storeDir: dir });
|
|
293
|
+
assert.equal(response, undefined);
|
|
294
|
+
assert.equal(readState(dir).state, 'skipped');
|
|
295
|
+
});
|
|
@@ -89,12 +89,6 @@ describe('bin/troxy.js: proxy subcommand wiring', () => {
|
|
|
89
89
|
assert.match(proxyBlock, /runProxyDisable/);
|
|
90
90
|
assert.match(proxyBlock, /runProxyStatus/);
|
|
91
91
|
});
|
|
92
|
-
|
|
93
|
-
it('is listed in the default help banner - init already enables it for real users', () => {
|
|
94
|
-
const helpBannerStart = binSrc.indexOf("Troxy: a secure control layer for AI agents");
|
|
95
|
-
const helpBanner = binSrc.slice(helpBannerStart);
|
|
96
|
-
assert.ok(helpBanner.includes('troxy proxy '), 'troxy proxy should appear in the printed help banner');
|
|
97
|
-
});
|
|
98
92
|
});
|
|
99
93
|
|
|
100
94
|
describe('uninstall.js: settings.json symmetry (fail-open layer 6)', () => {
|