@velum-labs/routekit-tool-claude 0.18.0 → 0.18.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/dist/install.d.ts +5 -0
- package/dist/install.js +33 -1
- package/dist/test/install.test.js +46 -0
- package/package.json +6 -6
package/dist/install.d.ts
CHANGED
|
@@ -9,6 +9,11 @@ export type ClaudeInstallInput = {
|
|
|
9
9
|
gatewayUrl: string;
|
|
10
10
|
/** Canonical RouteKit catalog ids permitted by the current model policy. */
|
|
11
11
|
models: readonly string[];
|
|
12
|
+
/**
|
|
13
|
+
* Command Claude Code invokes when it needs the gateway bearer token. The
|
|
14
|
+
* helper output is never persisted in settings.json.
|
|
15
|
+
*/
|
|
16
|
+
apiKeyHelper?: string;
|
|
12
17
|
owner: ClaudeInstallOwner;
|
|
13
18
|
claudeConfigDir?: string;
|
|
14
19
|
};
|
package/dist/install.js
CHANGED
|
@@ -109,7 +109,8 @@ function isInstalledManifest(value) {
|
|
|
109
109
|
(value.managedPickerModels === undefined || isStringArray(value.managedPickerModels)) &&
|
|
110
110
|
(value.managedAvailableModels === undefined || value.managedAvailableModels === true) &&
|
|
111
111
|
(value.managedEnforceAvailableModels === undefined ||
|
|
112
|
-
value.managedEnforceAvailableModels === true)
|
|
112
|
+
value.managedEnforceAvailableModels === true) &&
|
|
113
|
+
(value.managedApiKeyHelper === undefined || typeof value.managedApiKeyHelper === "string"));
|
|
113
114
|
}
|
|
114
115
|
function parseManifest(content, manifestPath) {
|
|
115
116
|
let parsed;
|
|
@@ -449,6 +450,23 @@ export async function installClaudeIntegration(input) {
|
|
|
449
450
|
const settings = parseSettings(beforeSettings.content ?? "{}\n", configPath);
|
|
450
451
|
const env = { ...(settings.env ?? {}) };
|
|
451
452
|
const nextManaged = managedEnv(input);
|
|
453
|
+
if (settings.apiKeyHelper !== undefined &&
|
|
454
|
+
typeof settings.apiKeyHelper !== "string") {
|
|
455
|
+
throw new Error(`the "apiKeyHelper" field in your Claude settings (${configPath}) must be a string`);
|
|
456
|
+
}
|
|
457
|
+
const currentApiKeyHelper = settings.apiKeyHelper;
|
|
458
|
+
const previousApiKeyHelper = previousManifest?.managedApiKeyHelper;
|
|
459
|
+
if (previousApiKeyHelper !== undefined &&
|
|
460
|
+
currentApiKeyHelper !== previousApiKeyHelper) {
|
|
461
|
+
throw new Error(`your Claude settings changed RouteKit-managed apiKeyHelper in ${configPath}; ` +
|
|
462
|
+
"restore that value before rerunning the install command");
|
|
463
|
+
}
|
|
464
|
+
if (previousManifest === undefined &&
|
|
465
|
+
input.apiKeyHelper !== undefined &&
|
|
466
|
+
currentApiKeyHelper !== undefined) {
|
|
467
|
+
throw new Error(`your Claude settings already define apiKeyHelper in ${configPath}; ` +
|
|
468
|
+
`remove it before rerunning \`${input.owner.installCommand}\``);
|
|
469
|
+
}
|
|
452
470
|
const desiredPickerModels = pickerModels(input);
|
|
453
471
|
if (desiredPickerModels.length === 0) {
|
|
454
472
|
throw new Error("RouteKit's Claude picker catalog cannot be empty");
|
|
@@ -490,10 +508,16 @@ export async function installClaudeIntegration(input) {
|
|
|
490
508
|
...settings,
|
|
491
509
|
env,
|
|
492
510
|
availableModels: nextPickerModels,
|
|
511
|
+
...(input.apiKeyHelper !== undefined
|
|
512
|
+
? { apiKeyHelper: input.apiKeyHelper }
|
|
513
|
+
: {}),
|
|
493
514
|
...(nextManagedEnforceAvailableModels === true
|
|
494
515
|
? { enforceAvailableModels: true }
|
|
495
516
|
: {})
|
|
496
517
|
};
|
|
518
|
+
if (input.apiKeyHelper === undefined && previousApiKeyHelper !== undefined) {
|
|
519
|
+
delete nextSettings.apiKeyHelper;
|
|
520
|
+
}
|
|
497
521
|
const nextContent = serialize(nextSettings);
|
|
498
522
|
const targetSettings = snapshot(nextContent, 0o600);
|
|
499
523
|
const exactRestoreEligible = previousManifest === undefined
|
|
@@ -514,6 +538,9 @@ export async function installClaudeIntegration(input) {
|
|
|
514
538
|
: {}),
|
|
515
539
|
...(nextManagedEnforceAvailableModels === true
|
|
516
540
|
? { managedEnforceAvailableModels: true }
|
|
541
|
+
: {}),
|
|
542
|
+
...(input.apiKeyHelper !== undefined
|
|
543
|
+
? { managedApiKeyHelper: input.apiKeyHelper }
|
|
517
544
|
: {})
|
|
518
545
|
};
|
|
519
546
|
const pendingManifest = {
|
|
@@ -543,6 +570,7 @@ export async function installClaudeIntegration(input) {
|
|
|
543
570
|
action: previousManifest === undefined ? "installed" : "updated",
|
|
544
571
|
managedKeys: [
|
|
545
572
|
...Object.keys(nextManaged),
|
|
573
|
+
...(input.apiKeyHelper !== undefined ? ["apiKeyHelper"] : []),
|
|
546
574
|
"availableModels",
|
|
547
575
|
...(nextManagedEnforceAvailableModels === true ? ["enforceAvailableModels"] : [])
|
|
548
576
|
]
|
|
@@ -595,6 +623,10 @@ export async function uninstallClaudeIntegration(input) {
|
|
|
595
623
|
delete env[key];
|
|
596
624
|
}
|
|
597
625
|
const next = { ...settings };
|
|
626
|
+
if (installed.managedApiKeyHelper !== undefined &&
|
|
627
|
+
settings.apiKeyHelper === installed.managedApiKeyHelper) {
|
|
628
|
+
delete next.apiKeyHelper;
|
|
629
|
+
}
|
|
598
630
|
if (Object.keys(env).length === 0)
|
|
599
631
|
delete next.env;
|
|
600
632
|
else
|
|
@@ -67,6 +67,52 @@ test("Claude managed install updates and restores the exact original settings",
|
|
|
67
67
|
rmSync(configDirectory, { recursive: true, force: true });
|
|
68
68
|
}
|
|
69
69
|
});
|
|
70
|
+
test("Claude install owns a pull-based apiKeyHelper without storing its output", async () => {
|
|
71
|
+
const configDirectory = mkdtempSync(join(tmpdir(), "routekit-claude-helper-"));
|
|
72
|
+
const configPath = join(configDirectory, "settings.json");
|
|
73
|
+
const original = '{"theme":"dark"}\n';
|
|
74
|
+
writeFileSync(configPath, original);
|
|
75
|
+
try {
|
|
76
|
+
const helper = "'/opt/routekit/node' '/opt/routekit/index.js' credential get";
|
|
77
|
+
const installed = await installClaudeIntegration({
|
|
78
|
+
gatewayUrl: "http://127.0.0.1:9999",
|
|
79
|
+
models: ["openai/mock-model"],
|
|
80
|
+
apiKeyHelper: helper,
|
|
81
|
+
owner: OWNER,
|
|
82
|
+
claudeConfigDir: configDirectory
|
|
83
|
+
});
|
|
84
|
+
assert.ok(installed.managedKeys.includes("apiKeyHelper"));
|
|
85
|
+
assert.equal(JSON.parse(readFileSync(configPath, "utf8")).apiKeyHelper, helper);
|
|
86
|
+
const settings = JSON.parse(readFileSync(configPath, "utf8"));
|
|
87
|
+
settings.theme = "light";
|
|
88
|
+
writeFileSync(configPath, `${JSON.stringify(settings, null, 2)}\n`);
|
|
89
|
+
await uninstallClaudeIntegration({
|
|
90
|
+
ownerId: OWNER.id,
|
|
91
|
+
claudeConfigDir: configDirectory
|
|
92
|
+
});
|
|
93
|
+
assert.equal(JSON.parse(readFileSync(configPath, "utf8")).apiKeyHelper, undefined);
|
|
94
|
+
assert.equal(JSON.parse(readFileSync(configPath, "utf8")).theme, "light");
|
|
95
|
+
}
|
|
96
|
+
finally {
|
|
97
|
+
rmSync(configDirectory, { recursive: true, force: true });
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
test("Claude install refuses to overwrite a user apiKeyHelper", async () => {
|
|
101
|
+
const configDirectory = mkdtempSync(join(tmpdir(), "routekit-claude-helper-conflict-"));
|
|
102
|
+
writeFileSync(join(configDirectory, "settings.json"), '{"apiKeyHelper":"/usr/local/bin/user-helper"}\n');
|
|
103
|
+
try {
|
|
104
|
+
await assert.rejects(installClaudeIntegration({
|
|
105
|
+
gatewayUrl: "http://127.0.0.1:9999",
|
|
106
|
+
models: ["openai/mock-model"],
|
|
107
|
+
apiKeyHelper: "/usr/local/bin/routekit-helper",
|
|
108
|
+
owner: OWNER,
|
|
109
|
+
claudeConfigDir: configDirectory
|
|
110
|
+
}), /already define apiKeyHelper/);
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
rmSync(configDirectory, { recursive: true, force: true });
|
|
114
|
+
}
|
|
115
|
+
});
|
|
70
116
|
test("Claude uninstall preserves user edits made after install", async () => {
|
|
71
117
|
const configDirectory = mkdtempSync(join(tmpdir(), "routekit-claude-edits-"));
|
|
72
118
|
const configPath = join(configDirectory, "settings.json");
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velum-labs/routekit-tool-claude",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.18.
|
|
4
|
+
"version": "0.18.2",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/velum-labs/routekit.git",
|
|
@@ -30,11 +30,11 @@
|
|
|
30
30
|
"@anthropic-ai/sdk": "0.115.0",
|
|
31
31
|
"@modelcontextprotocol/sdk": "1.29.0",
|
|
32
32
|
"zod": "4.4.3",
|
|
33
|
-
"@velum-labs/routekit-contracts": "0.18.
|
|
34
|
-
"@velum-labs/routekit-harness-core": "0.18.
|
|
35
|
-
"@velum-labs/routekit-registry": "0.18.
|
|
36
|
-
"@velum-labs/routekit-runtime": "0.18.
|
|
37
|
-
"@velum-labs/routekit-tools": "0.18.
|
|
33
|
+
"@velum-labs/routekit-contracts": "0.18.2",
|
|
34
|
+
"@velum-labs/routekit-harness-core": "0.18.2",
|
|
35
|
+
"@velum-labs/routekit-registry": "0.18.2",
|
|
36
|
+
"@velum-labs/routekit-runtime": "0.18.2",
|
|
37
|
+
"@velum-labs/routekit-tools": "0.18.2"
|
|
38
38
|
},
|
|
39
39
|
"keywords": [
|
|
40
40
|
"llm",
|