troxy-cli 1.30.16 → 1.30.18

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.30.16",
3
+ "version": "1.30.18",
4
4
  "description": "Make your Claude Code usage go further: lighter requests, less usage, less money",
5
5
  "homepage": "https://troxy.io",
6
6
  "bugs": {
@@ -116,12 +116,10 @@ const RULES = [
116
116
  const paths = pathArgs(segment, 2).filter((w) => !w.startsWith('-'));
117
117
  return { action_verb: 'delete', resource: summarizeResource(paths, 'a branch'), item_count: 1 };
118
118
  }
119
- if (/^git\s+push\b/.test(segment) && (/\s--delete\b/.test(segment) || /\s\S*:\S+/.test(segment))) {
120
- // `git push origin :branch` (empty source = delete) and
121
- // `git push origin local:branch` both use the same refspec shape;
122
- // either way the part after the colon is the remote branch being
123
- // touched, which is what matters here regardless of which it deletes.
124
- const target = segment.match(/--delete\s+(\S+)/)?.[1] || segment.match(/\s\S*:(\S+)/)?.[1];
119
+ if (/^git\s+push\b/.test(segment) && (/\s--delete\b/.test(segment) || /\s:\S+/.test(segment))) {
120
+ // Only an empty source (`git push origin :branch`) deletes.
121
+ // `git push origin HEAD:main` is a normal push, not a delete.
122
+ const target = segment.match(/--delete\s+(\S+)/)?.[1] || segment.match(/\s:(\S+)/)?.[1];
125
123
  return { action_verb: 'delete', resource: target || 'a remote branch', item_count: 1 };
126
124
  }
127
125
  return null;
package/src/init.js CHANGED
@@ -195,7 +195,7 @@ export async function reprovisionKeyConsumers(key, agentName, proxyOptIn = null,
195
195
  const installed = await installTroxyGuardMod();
196
196
  console.log(installed
197
197
  ? ` • Troxy model routing (terminal + Desktop) ✓`
198
- : ` • Troxy model routing - (needs Claude Code 2.1.287+, skipped)`);
198
+ : ` • Troxy model routing ✗ NOT installed: needs Claude Code 2.1.287+ (run claude --version). Usage and Savings stays empty until it is.`);
199
199
  } catch (err) {
200
200
  console.log(` • Troxy model routing ✗ (${err.message})`);
201
201
  }
@@ -486,6 +486,11 @@ const MCP_CLIENTS = [
486
486
  ];
487
487
 
488
488
  export async function runInit({ key, name, proxy, concise } = {}) {
489
+ // Under sudo, os.homedir() can be root's, so the mod would land where Claude Code never looks.
490
+ if (process.env.SUDO_USER) {
491
+ console.error('\n Run this without sudo: troxy init --key ...\n (sudo is only for the install command.)\n');
492
+ process.exit(1);
493
+ }
489
494
  // undefined (flag never passed) -> null, so reprovisionKeyConsumers can
490
495
  // tell "not decided, ask interactively" apart from an explicit --proxy.
491
496
  const proxyOptIn = proxy === undefined ? null : Boolean(proxy);
@@ -86,6 +86,10 @@ describe('actionFor - git force push / branch delete', () => {
86
86
  assert.equal(bash('git push origin --delete old-feature').action_verb, 'delete');
87
87
  assert.equal(bash('git push origin :old-feature').action_verb, 'delete');
88
88
  });
89
+
90
+ it('does not treat a normal src:dst push as a delete', () => {
91
+ assert.equal(bash('git push -q origin HEAD:main'), null);
92
+ });
89
93
  });
90
94
 
91
95
  describe('actionFor - terraform / kubectl / docker / aws', () => {
@@ -1,3 +1,5 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { fileURLToPath } from 'node:url';
1
3
  import { test } from 'node:test';
2
4
  import assert from 'node:assert/strict';
3
5
  import os from 'node:os';
@@ -94,3 +96,10 @@ test('refreshTroxyGuardMod: re-copies a stale mod, leaves a current or missing o
94
96
  fs.rmSync(tmpHome, { recursive: true, force: true });
95
97
  }
96
98
  });
99
+
100
+ test('init refuses to run under sudo, so the mod is not installed in root\'s home', () => {
101
+ const r = spawnSync(process.execPath, [fileURLToPath(new URL('../../bin/troxy.js', import.meta.url)), 'init', '--key', 'x'],
102
+ { env: { ...process.env, SUDO_USER: 'someone' }, encoding: 'utf8' });
103
+ assert.equal(r.status, 1);
104
+ assert.match(r.stderr, /without sudo/);
105
+ });