unbound-cli 1.20.2 → 1.20.3

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": "unbound-cli",
3
- "version": "1.20.2",
3
+ "version": "1.20.3",
4
4
  "description": "CLI tool for Unbound - AI Gateway management",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/toolHealth.js CHANGED
@@ -206,12 +206,27 @@ function envKeyCheck(label, name, kind = 'aux') {
206
206
  };
207
207
  }
208
208
 
209
- // Setup writes UNBOUND_DEVICE_TOKEN; installs from before it carry only the per-tool name.
209
+ function configDeviceToken() {
210
+ try {
211
+ const cfg = JSON.parse(fs.readFileSync(path.join(os.homedir(), '.unbound', 'config.json'), 'utf8'));
212
+ return cfg.api_key || cfg.device_token || null;
213
+ } catch {
214
+ return null;
215
+ }
216
+ }
217
+
218
+ // Env wins when present so a stale exported key is still validated; setup keeps the token in ~/.unbound/config.json and removes the env var.
210
219
  function deviceTokenCheck(legacyName) {
211
220
  const check = envKeyCheck('API key env', 'UNBOUND_DEVICE_TOKEN');
212
221
  if (check.ok) return check;
213
222
  const legacy = envKeyCheck('API key env', legacyName);
214
- return legacy.ok ? legacy : check;
223
+ if (legacy.ok) return legacy;
224
+ const value = configDeviceToken();
225
+ if (!value) return check;
226
+ return {
227
+ name: 'API key env', ok: true, kind: 'aux', detail: 'device token set (config.json)', summary: 'API key set',
228
+ keyCheck: true, envName: 'device token', value, candidates: [value],
229
+ };
215
230
  }
216
231
 
217
232
  function envCheck(label, name, expected, kind = 'aux') {
@@ -1111,6 +1111,31 @@ test('claude-code: only the per-tool UNBOUND_CLAUDE_API_KEY → healthy', () =>
1111
1111
  assert.equal(claudeWithEnv({ UNBOUND_CLAUDE_API_KEY: 'k' }).status, 'healthy');
1112
1112
  });
1113
1113
 
1114
+ test('claude-code: device token in config.json and no env → healthy', () => {
1115
+ const t = withHome((tmp, th) => {
1116
+ writeFile(path.join(tmp, '.claude', 'settings.json'), JSON.stringify({ hooks: { x: [{ command: path.join(tmp, '.claude', 'hooks', 'unbound.py') }] } }));
1117
+ writeFile(path.join(tmp, '.claude', 'hooks', 'unbound.py'), '#');
1118
+ writeFile(path.join(tmp, '.unbound', 'config.json'), JSON.stringify({ device_token: 'k' }));
1119
+ return th.detectTools({ apiKey: 'k' }).find((x) => x.family === 'claude-code');
1120
+ });
1121
+ assert.equal(t.status, 'healthy');
1122
+ });
1123
+
1124
+ test('claude-code: an exported key is the one validated even when config.json holds another', () => {
1125
+ process.env.UNBOUND_DEVICE_TOKEN = 'from-env';
1126
+ try {
1127
+ const t = withHome((tmp, th) => {
1128
+ writeFile(path.join(tmp, '.claude', 'settings.json'), JSON.stringify({ hooks: { x: [{ command: path.join(tmp, '.claude', 'hooks', 'unbound.py') }] } }));
1129
+ writeFile(path.join(tmp, '.claude', 'hooks', 'unbound.py'), '#');
1130
+ writeFile(path.join(tmp, '.unbound', 'config.json'), JSON.stringify({ device_token: 'from-config' }));
1131
+ return th.detectTools({ apiKey: 'k' }).find((x) => x.family === 'claude-code');
1132
+ });
1133
+ assert.equal(t.checks.find((c) => c.keyCheck).value, 'from-env');
1134
+ } finally {
1135
+ delete process.env.UNBOUND_DEVICE_TOKEN;
1136
+ }
1137
+ });
1138
+
1114
1139
  test('claude-code: neither env set → tampered, naming UNBOUND_DEVICE_TOKEN', () => {
1115
1140
  const t = claudeWithEnv({});
1116
1141
  assert.equal(t.status, 'tampered');