troxy-cli 1.29.52 → 1.29.53

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.29.52",
3
+ "version": "1.29.53",
4
4
  "description": "A secure control layer for AI agents: policies across payments, messages, logins, destructive actions, model usage, and secrets, all enforceable from the CLI",
5
5
  "homepage": "https://troxy.io",
6
6
  "bugs": {
package/src/daemon.js CHANGED
@@ -23,7 +23,7 @@ import { api } from './api.js';
23
23
  import { createInterceptor } from './interceptor.js';
24
24
  import { enabledProviders, interceptHostsFor, troxyRouteFor } from './providers.js';
25
25
  import { ensureInterceptionCerts } from './tls-ca.js';
26
- import { migrateBaseUrlProxyToInterception, claudeCodeSettingsPath } from './init.js';
26
+ import { migrateBaseUrlProxyToInterception, claudeCodeSettingsPath, refreshTroxyGuardMod } from './init.js';
27
27
 
28
28
  const INTERVAL_MS = 60_000;
29
29
 
@@ -139,6 +139,8 @@ export async function runDaemon() {
139
139
  process.on('unhandledRejection', (err) => {
140
140
  process.stderr.write(`[troxy-daemon] unhandled rejection (continuing): ${err?.stack || err}\n`);
141
141
  });
142
+ // A CLI upgrade (npm or troxy update) then a daemon restart brings the mod up to date.
143
+ refreshTroxyGuardMod().catch((err) => process.stderr.write(`[troxy-daemon] mod refresh failed: ${err.message}\n`));
142
144
 
143
145
  const cfg = loadConfig();
144
146
  if (!cfg?.apiKey) {
package/src/init.js CHANGED
@@ -38,6 +38,18 @@ export async function installTroxyGuardMod(home = os.homedir(), detectFn = detec
38
38
  return true;
39
39
  }
40
40
 
41
+ // Only `troxy init` used to install the mod, so a CLI upgrade left the old mod running.
42
+ // Re-copy it when the installed one is stamped with a different CLI version; a machine
43
+ // without the mod (never ran init, or uninstalled) is left alone.
44
+ export async function refreshTroxyGuardMod(home = os.homedir(), detectFn = detectInstalledClaudeCodeVersion) {
45
+ const manifest = path.join(home, '.claude', 'skills', 'troxy-guard', '.claude-plugin', 'plugin.json');
46
+ let installed;
47
+ try { installed = JSON.parse(fs.readFileSync(manifest, 'utf8')).version; } catch { return false; }
48
+ const { version } = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8'));
49
+ if (installed === version) return false;
50
+ return installTroxyGuardMod(home, detectFn);
51
+ }
52
+
41
53
  export function uninstallTroxyGuardMod(home = os.homedir()) {
42
54
  fs.rmSync(path.join(home, '.claude', 'skills', 'troxy-guard'), { recursive: true, force: true });
43
55
  }
@@ -113,6 +125,7 @@ export async function refreshHostedInstructions(key) {
113
125
 
114
126
  export async function reprovisionKeyConsumers(key, agentName, proxyOptIn = null, conciseOptIn = null) {
115
127
  const platform = process.platform;
128
+ try { if (await refreshTroxyGuardMod()) console.log(' • Troxy model routing updated ✓'); } catch {}
116
129
  const { detected, hasOpenClaw, hasClaude, none } = detectMcpClients();
117
130
 
118
131
  if (none) {
@@ -3,7 +3,7 @@ import assert from 'node:assert/strict';
3
3
  import os from 'node:os';
4
4
  import path from 'node:path';
5
5
  import fs from 'node:fs';
6
- import { installTroxyGuardMod, uninstallTroxyGuardMod } from '../init.js';
6
+ import { installTroxyGuardMod, uninstallTroxyGuardMod, refreshTroxyGuardMod } from '../init.js';
7
7
 
8
8
  test('installTroxyGuardMod: copies the mod when the version supports it', async () => {
9
9
  const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'troxy-mod-home-'));
@@ -77,3 +77,20 @@ test('uninstallTroxyGuardMod: a no-op (not a throw) when nothing was installed',
77
77
  fs.rmSync(tmpHome, { recursive: true, force: true });
78
78
  }
79
79
  });
80
+
81
+ test('refreshTroxyGuardMod: re-copies a stale mod, leaves a current or missing one alone', async () => {
82
+ const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'troxy-mod-home-'));
83
+ const cc = async () => ({ major: 2, minor: 1, patch: 289 });
84
+ const manifest = path.join(tmpHome, '.claude', 'skills', 'troxy-guard', '.claude-plugin', 'plugin.json');
85
+ try {
86
+ assert.equal(await refreshTroxyGuardMod(tmpHome, cc), false); // never installed: stays off
87
+ assert.equal(fs.existsSync(manifest), false);
88
+ await installTroxyGuardMod(tmpHome, cc);
89
+ assert.equal(await refreshTroxyGuardMod(tmpHome, cc), false); // already current
90
+ fs.writeFileSync(manifest, JSON.stringify({ name: 'troxy-guard', version: '0.0.1' }));
91
+ assert.equal(await refreshTroxyGuardMod(tmpHome, cc), true); // stale: re-copied and re-stamped
92
+ assert.notEqual(JSON.parse(fs.readFileSync(manifest, 'utf8')).version, '0.0.1');
93
+ } finally {
94
+ fs.rmSync(tmpHome, { recursive: true, force: true });
95
+ }
96
+ });