@theone1345/smartrelay 0.2.0 → 0.3.0
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/LICENSE +21 -0
- package/README.md +127 -17
- package/config/runners/agents.yaml +14 -1
- package/dist/credentials.d.ts +94 -0
- package/dist/credentials.js +168 -0
- package/dist/credentials.js.map +1 -0
- package/dist/http-api.d.ts +1 -7
- package/dist/http-api.js +6 -169
- package/dist/http-api.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/install.d.ts +57 -0
- package/dist/install.js +216 -0
- package/dist/install.js.map +1 -0
- package/dist/logger.d.ts +2 -0
- package/dist/logger.js +21 -1
- package/dist/logger.js.map +1 -1
- package/dist/profiles/review-profiles.d.ts +32 -0
- package/dist/profiles/review-profiles.js +348 -0
- package/dist/profiles/review-profiles.js.map +1 -0
- package/dist/router.js +18 -3
- package/dist/router.js.map +1 -1
- package/dist/runners/anthropic.js +2 -3
- package/dist/runners/anthropic.js.map +1 -1
- package/dist/runners/base.d.ts +8 -0
- package/dist/runners/base.js +13 -0
- package/dist/runners/base.js.map +1 -1
- package/dist/runners/openai.js +2 -3
- package/dist/runners/openai.js.map +1 -1
- package/dist/runners/registry.d.ts +2 -0
- package/dist/runners/registry.js +4 -0
- package/dist/runners/registry.js.map +1 -1
- package/dist/server.d.ts +1 -6
- package/dist/server.js +134 -66
- package/dist/server.js.map +1 -1
- package/dist/setup.d.ts +65 -0
- package/dist/setup.js +411 -0
- package/dist/setup.js.map +1 -0
- package/dist/tools/dispatch.d.ts +33 -0
- package/dist/tools/dispatch.js +222 -0
- package/dist/tools/dispatch.js.map +1 -0
- package/dist/tools/handlers.d.ts +8 -2
- package/dist/tools/handlers.js +119 -27
- package/dist/tools/handlers.js.map +1 -1
- package/dist/util.d.ts +37 -4
- package/dist/util.js +71 -15
- package/dist/util.js.map +1 -1
- package/package.json +28 -3
- package/prompts/code_review.md +26 -20
- package/prompts/security_audit.md +109 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dhavan Bhalodiya
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **The intelligent delegation layer & zero-token task server for AI agents.**
|
|
4
4
|
|
|
5
|
+
[](https://www.npmjs.com/package/@theone1345/smartrelay)
|
|
5
6
|
[](https://modelcontextprotocol.io)
|
|
6
7
|
[](https://www.typescriptlang.org)
|
|
7
8
|
[](#-testing--verification)
|
|
@@ -21,11 +22,99 @@ Before installing, ensure you have:
|
|
|
21
22
|
|
|
22
23
|
---
|
|
23
24
|
|
|
25
|
+
## 🔑 Install in One Command
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx @theone1345/smartrelay init
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
That single command asks for your API keys in the terminal, then registers
|
|
32
|
+
SmartRelay with whichever MCP clients it finds:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
SmartRelay setup
|
|
36
|
+
────────────────
|
|
37
|
+
NVIDIA_API_KEY — NVIDIA NIM / API Catalog [required]
|
|
38
|
+
Get one at https://build.nvidia.com
|
|
39
|
+
key (input hidden, paste is fine):
|
|
40
|
+
stored nvapi-…6789
|
|
41
|
+
|
|
42
|
+
OPENROUTER_API_KEY — OpenRouter [required]
|
|
43
|
+
...
|
|
44
|
+
|
|
45
|
+
Saved 2 keys to ~/.smartrelay/.env (mode 600)
|
|
46
|
+
23 of 25 runners ready.
|
|
47
|
+
|
|
48
|
+
Register SmartRelay as an MCP server
|
|
49
|
+
────────────────────────────────────
|
|
50
|
+
Add to Claude Code? [Y/n]
|
|
51
|
+
Add to Cursor? [Y/n]
|
|
52
|
+
|
|
53
|
+
Registering…
|
|
54
|
+
✔ Claude Code — registered with the claude CLI
|
|
55
|
+
✔ Cursor — added to ~/.cursor/mcp.json
|
|
56
|
+
|
|
57
|
+
Restart the client to load SmartRelay.
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**NVIDIA** and **OpenRouter** are required — they back all five sub-agents.
|
|
61
|
+
**Anthropic** and **OpenAI** are optional; press Enter to skip. Keystrokes are
|
|
62
|
+
hidden as you type and each key is echoed back masked.
|
|
63
|
+
|
|
64
|
+
Keys go to `~/.smartrelay/.env` at mode `600`, outside any repository. Re-running
|
|
65
|
+
shows what is already set and keeps it on Enter; the file is merged, never
|
|
66
|
+
overwritten. Client configs are merged the same way — your other MCP servers stay
|
|
67
|
+
put, and a backup is written before any change.
|
|
68
|
+
|
|
69
|
+
> Keys are collected in the terminal rather than through an MCP elicitation
|
|
70
|
+
> dialog on purpose: the elicitation schema has no masked field type, so a key
|
|
71
|
+
> entered that way would appear in plain text and reach the client's transcript.
|
|
72
|
+
|
|
73
|
+
### Other ways to run it
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# Keys only, no client registration
|
|
77
|
+
npx @theone1345/smartrelay setup
|
|
78
|
+
|
|
79
|
+
# Print the config for every client, change nothing
|
|
80
|
+
npx @theone1345/smartrelay init --print-config
|
|
81
|
+
|
|
82
|
+
# Register specific clients without being asked
|
|
83
|
+
npx @theone1345/smartrelay init --client claude-code,cursor
|
|
84
|
+
|
|
85
|
+
# CI / Dockerfile — take keys from the environment, no prompts
|
|
86
|
+
NVIDIA_API_KEY=... OPENROUTER_API_KEY=... \
|
|
87
|
+
npx @theone1345/smartrelay init --non-interactive --client claude-code
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Supported client ids: `claude-code` (via the `claude` CLI), `claude-desktop`,
|
|
91
|
+
`cursor`, `windsurf`.
|
|
92
|
+
|
|
93
|
+
### Where keys are read from
|
|
94
|
+
|
|
95
|
+
Highest priority wins:
|
|
96
|
+
|
|
97
|
+
| # | Source | Notes |
|
|
98
|
+
|---|---|---|
|
|
99
|
+
| 1 | `process.env` | including your MCP client's `"env": {}` block |
|
|
100
|
+
| 2 | `<cwd>/.env` | project-local |
|
|
101
|
+
| 3 | `<project root>/.env` | |
|
|
102
|
+
| 4 | `~/.smartrelay/.env` | written by `smartrelay setup` |
|
|
103
|
+
|
|
104
|
+
Every source is read, in that order — a key defined only in a lower-priority file
|
|
105
|
+
is still picked up. If no provider key resolves, the server logs a warning naming
|
|
106
|
+
the fix on startup (to stderr, so the stdio JSON-RPC stream stays clean) rather
|
|
107
|
+
than failing silently at the first tool call.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
24
111
|
## 🚀 Quick Install
|
|
25
112
|
|
|
26
113
|
### Option A — `npx` *(Easiest — zero install, runs everywhere)*
|
|
27
114
|
|
|
28
|
-
|
|
115
|
+
`npx @theone1345/smartrelay init` does all of this for you, including the keys.
|
|
116
|
+
To wire it up by hand instead, add the following to your Claude Desktop / Cursor /
|
|
117
|
+
Windsurf configuration:
|
|
29
118
|
|
|
30
119
|
```json
|
|
31
120
|
{
|
|
@@ -118,7 +207,7 @@ Switch models on the fly during your session — just type plain English:
|
|
|
118
207
|
|
|
119
208
|
---
|
|
120
209
|
|
|
121
|
-
## 🛠️
|
|
210
|
+
## 🛠️ 15 MCP Tools
|
|
122
211
|
|
|
123
212
|
| Tool | What It Does |
|
|
124
213
|
| :--- | :--- |
|
|
@@ -243,6 +332,15 @@ npm install
|
|
|
243
332
|
|
|
244
333
|
### 2. Configure Environment Variables
|
|
245
334
|
|
|
335
|
+
The quickest route is the wizard, which writes `~/.smartrelay/.env` for every
|
|
336
|
+
project at once:
|
|
337
|
+
|
|
338
|
+
```bash
|
|
339
|
+
npm run setup
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
Or keep the keys inside the checkout:
|
|
343
|
+
|
|
246
344
|
```bash
|
|
247
345
|
cp .env.example .env
|
|
248
346
|
```
|
|
@@ -279,13 +377,12 @@ npm run build
|
|
|
279
377
|
|
|
280
378
|
#### 🟣 Claude Code (CLI)
|
|
281
379
|
|
|
282
|
-
|
|
283
|
-
|
|
380
|
+
Add globally across **all projects** with a single command:
|
|
284
381
|
```bash
|
|
285
|
-
claude mcp add -s user smartrelay --
|
|
382
|
+
claude mcp add -s user smartrelay -- npx -y @theone1345/smartrelay
|
|
286
383
|
```
|
|
287
384
|
|
|
288
|
-
|
|
385
|
+
*(Or if you are running locally from source: `claude mcp add -s user smartrelay -- node /ABSOLUTE/PATH/TO/smart-relay/dist/server.js`)*
|
|
289
386
|
|
|
290
387
|
Verify anywhere: `claude mcp list` | Remove: `claude mcp remove -s user smartrelay`
|
|
291
388
|
|
|
@@ -300,26 +397,27 @@ Config file locations:
|
|
|
300
397
|
{
|
|
301
398
|
"mcpServers": {
|
|
302
399
|
"smartrelay": {
|
|
303
|
-
"command": "
|
|
304
|
-
"args": ["
|
|
400
|
+
"command": "npx",
|
|
401
|
+
"args": ["-y", "@theone1345/smartrelay"],
|
|
305
402
|
"env": {
|
|
306
|
-
"
|
|
307
|
-
"ANTHROPIC_API_KEY": "sk-ant-...",
|
|
403
|
+
"OPENROUTER_API_KEY": "sk-or-v1-...",
|
|
308
404
|
"NVIDIA_API_KEY": "nvapi-...",
|
|
309
|
-
"
|
|
405
|
+
"ANTHROPIC_API_KEY": "sk-ant-...",
|
|
406
|
+
"OPENAI_API_KEY": "sk-proj-..."
|
|
310
407
|
}
|
|
311
408
|
}
|
|
312
409
|
}
|
|
313
410
|
}
|
|
314
411
|
```
|
|
315
|
-
*(Note: If you already configured `.env` inside the `smart-relay` repository, the `"env"` block above is optional — SmartRelay auto-detects it!)*
|
|
316
412
|
|
|
317
413
|
#### 🔵 Cursor IDE
|
|
318
414
|
|
|
319
415
|
Go to **Settings → Features → MCP → + Add New MCP Server**:
|
|
320
416
|
- **Name**: `smartrelay`
|
|
321
417
|
- **Type**: `command`
|
|
322
|
-
- **Command**: `
|
|
418
|
+
- **Command**: `npx -y @theone1345/smartrelay`
|
|
419
|
+
|
|
420
|
+
*(Or if running locally: `/opt/homebrew/bin/node /ABSOLUTE/PATH/TO/smart-relay/dist/server.js`)*
|
|
323
421
|
|
|
324
422
|
#### 🌊 Windsurf / Codeium
|
|
325
423
|
|
|
@@ -328,8 +426,12 @@ Edit `~/.codeium/windsurf/mcp_config.json`:
|
|
|
328
426
|
{
|
|
329
427
|
"mcpServers": {
|
|
330
428
|
"smartrelay": {
|
|
331
|
-
"command": "
|
|
332
|
-
"args": ["
|
|
429
|
+
"command": "npx",
|
|
430
|
+
"args": ["-y", "@theone1345/smartrelay"],
|
|
431
|
+
"env": {
|
|
432
|
+
"OPENROUTER_API_KEY": "sk-or-v1-...",
|
|
433
|
+
"NVIDIA_API_KEY": "nvapi-..."
|
|
434
|
+
}
|
|
333
435
|
}
|
|
334
436
|
}
|
|
335
437
|
}
|
|
@@ -342,8 +444,12 @@ Edit your Cline MCP settings (`cline_mcp_settings.json`):
|
|
|
342
444
|
{
|
|
343
445
|
"mcpServers": {
|
|
344
446
|
"smartrelay": {
|
|
345
|
-
"command": "
|
|
346
|
-
"args": ["
|
|
447
|
+
"command": "npx",
|
|
448
|
+
"args": ["-y", "@theone1345/smartrelay"],
|
|
449
|
+
"env": {
|
|
450
|
+
"OPENROUTER_API_KEY": "sk-or-v1-...",
|
|
451
|
+
"NVIDIA_API_KEY": "nvapi-..."
|
|
452
|
+
}
|
|
347
453
|
}
|
|
348
454
|
}
|
|
349
455
|
}
|
|
@@ -435,6 +541,10 @@ Opens at **http://localhost:6274**
|
|
|
435
541
|
## 🔒 Security Notes
|
|
436
542
|
|
|
437
543
|
- API keys are read from environment variables only — never hardcoded
|
|
544
|
+
- `smartrelay setup` stores keys in `~/.smartrelay/.env` at mode `600`, written
|
|
545
|
+
via a temp file and rename so an interrupted run leaves nothing half-written
|
|
546
|
+
- Keys are never echoed in full — the wizard and all logs show a masked form, and
|
|
547
|
+
`smartrelay_get_logs` redacts anything matching a provider key prefix
|
|
438
548
|
- The HTTP API requires a Bearer token (`SMARTRELAY_HTTP_API_KEY`)
|
|
439
549
|
- The MCP stdio server is local-only (no network exposure)
|
|
440
550
|
- No `process.exit()` calls — uses `process.exitCode` for safe shutdown
|
|
@@ -19,8 +19,9 @@ runners:
|
|
|
19
19
|
api_key_env: "NVIDIA_API_KEY"
|
|
20
20
|
cost_per_million_input_tokens: 0.70
|
|
21
21
|
cost_per_million_output_tokens: 0.90
|
|
22
|
+
timeout_seconds: 120.0
|
|
22
23
|
default_params:
|
|
23
|
-
max_tokens:
|
|
24
|
+
max_tokens: 16384
|
|
24
25
|
temperature: 0.1
|
|
25
26
|
system_prompt_file: "prompts/code_review.md"
|
|
26
27
|
|
|
@@ -45,3 +46,15 @@ runners:
|
|
|
45
46
|
max_tokens: 4096
|
|
46
47
|
temperature: 0.3
|
|
47
48
|
system_prompt_file: "prompts/explain_code.md"
|
|
49
|
+
|
|
50
|
+
security-agent:
|
|
51
|
+
type: "nvidia"
|
|
52
|
+
model: "nvidia/nemotron-3-super-120b-a12b"
|
|
53
|
+
api_key_env: "NVIDIA_API_KEY"
|
|
54
|
+
cost_per_million_input_tokens: 0.70
|
|
55
|
+
cost_per_million_output_tokens: 0.90
|
|
56
|
+
timeout_seconds: 120.0
|
|
57
|
+
default_params:
|
|
58
|
+
max_tokens: 16384
|
|
59
|
+
temperature: 0.1
|
|
60
|
+
system_prompt_file: "prompts/security_audit.md"
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared vocabulary for "which provider keys matter".
|
|
3
|
+
*
|
|
4
|
+
* Used by the setup wizard (`src/setup.ts`), the server's startup credential
|
|
5
|
+
* warning, the runner auth-error message, and the MCPHub plugin — so there is
|
|
6
|
+
* exactly one list of providers and one definition of what "ready" means.
|
|
7
|
+
*/
|
|
8
|
+
import type { Logger } from './logger.js';
|
|
9
|
+
import type { RunnerMetadata } from './runners/registry.js';
|
|
10
|
+
/** Published package name, used to build every install and setup command. */
|
|
11
|
+
export declare const PACKAGE_NAME = "@theone1345/smartrelay";
|
|
12
|
+
/** The exact command the user should run to configure credentials. */
|
|
13
|
+
export declare const SETUP_COMMAND = "npx @theone1345/smartrelay setup";
|
|
14
|
+
/** One-shot command: prompts for keys, then registers the MCP server. */
|
|
15
|
+
export declare const INSTALL_COMMAND = "npx @theone1345/smartrelay init";
|
|
16
|
+
/** One-line nudge appended to errors and warnings. */
|
|
17
|
+
export declare const SETUP_HINT = "Run `npx @theone1345/smartrelay setup` to store your provider API keys.";
|
|
18
|
+
export interface KeySpec {
|
|
19
|
+
/** Environment variable the runners actually read. */
|
|
20
|
+
env: string;
|
|
21
|
+
/** Human-readable provider name shown in the wizard. */
|
|
22
|
+
label: string;
|
|
23
|
+
/** Required keys block a successful setup; optional ones can be skipped. */
|
|
24
|
+
required: boolean;
|
|
25
|
+
/** Expected key prefix, used for a warning only — never to reject a value. */
|
|
26
|
+
prefix: string | null;
|
|
27
|
+
/** Where the user goes to obtain the key. */
|
|
28
|
+
consoleUrl: string;
|
|
29
|
+
/** Matching field name in the MCPHub plugin's config schema. */
|
|
30
|
+
configField: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Providers the wizard asks about, in prompt order.
|
|
34
|
+
*
|
|
35
|
+
* NVIDIA and OpenRouter are required because every sub-agent in
|
|
36
|
+
* `config/runners/agents.yaml` is backed by one of them. Anthropic and OpenAI
|
|
37
|
+
* unlock additional runners but nothing depends on them.
|
|
38
|
+
*/
|
|
39
|
+
export declare const PROVIDER_KEYS: readonly KeySpec[];
|
|
40
|
+
/** Look up a spec by environment variable name. */
|
|
41
|
+
export declare function findKeySpec(envVar: string): KeySpec | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* Render a secret for display: enough to recognize, never enough to use.
|
|
44
|
+
*
|
|
45
|
+
* Short values are replaced wholesale — a 10-character key would otherwise be
|
|
46
|
+
* almost fully revealed by a first-6/last-4 split.
|
|
47
|
+
*/
|
|
48
|
+
export declare function maskKey(value: string): string;
|
|
49
|
+
/**
|
|
50
|
+
* Check a key against its expected shape.
|
|
51
|
+
*
|
|
52
|
+
* Returns a warning string or `null`. It never throws and never rejects:
|
|
53
|
+
* providers rotate their key formats, and a wizard that refuses a valid new-style
|
|
54
|
+
* key is worse than one that saves an invalid one.
|
|
55
|
+
*/
|
|
56
|
+
export declare function validateKeyFormat(spec: KeySpec, value: string): string | null;
|
|
57
|
+
export interface CredentialSummary {
|
|
58
|
+
/** Runners whose credential env var is populated. */
|
|
59
|
+
ready: number;
|
|
60
|
+
/** Total registered runners. */
|
|
61
|
+
total: number;
|
|
62
|
+
/** Deduped env vars that unauthenticated runners are waiting on, in config order. */
|
|
63
|
+
missingEnvVars: string[];
|
|
64
|
+
/** Required provider keys (per `PROVIDER_KEYS`) absent from the environment. */
|
|
65
|
+
missingRequired: string[];
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* True when not one required provider key is set.
|
|
69
|
+
*
|
|
70
|
+
* Deliberately not `ready === 0`: runners that need no credential (Ollama)
|
|
71
|
+
* report as authenticated, so a count alone reads "healthy" on an install where
|
|
72
|
+
* every hosted model is unreachable.
|
|
73
|
+
*/
|
|
74
|
+
export declare function noProviderKeysConfigured(summary: CredentialSummary): boolean;
|
|
75
|
+
/** Roll runner metadata up into the counts the wizard and startup warning print. */
|
|
76
|
+
export declare function summarizeCredentials(metadata: RunnerMetadata[]): CredentialSummary;
|
|
77
|
+
/**
|
|
78
|
+
* Render the runner readiness table.
|
|
79
|
+
*
|
|
80
|
+
* Shared by `smartrelay setup` and `npm run quick-test -- --list` so both always
|
|
81
|
+
* show the same thing.
|
|
82
|
+
*/
|
|
83
|
+
export declare function formatRunnerStatusLines(metadata: RunnerMetadata[]): string[];
|
|
84
|
+
/**
|
|
85
|
+
* Tell the operator, at startup, that nothing will work.
|
|
86
|
+
*
|
|
87
|
+
* Two hard rules, both load-bearing:
|
|
88
|
+
* - Log only. `getLogger` writes to stderr; a single byte on stdout would
|
|
89
|
+
* corrupt the stdio transport's JSON-RPC framing.
|
|
90
|
+
* - Never throw and never exit. An MCP client renders a non-zero exit as
|
|
91
|
+
* "server crashed", and missing credentials must stay a recoverable state
|
|
92
|
+
* the user can fix without restarting anything.
|
|
93
|
+
*/
|
|
94
|
+
export declare function warnIfNoCredentials(metadata: RunnerMetadata[], logger: Logger): void;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared vocabulary for "which provider keys matter".
|
|
3
|
+
*
|
|
4
|
+
* Used by the setup wizard (`src/setup.ts`), the server's startup credential
|
|
5
|
+
* warning, the runner auth-error message, and the MCPHub plugin — so there is
|
|
6
|
+
* exactly one list of providers and one definition of what "ready" means.
|
|
7
|
+
*/
|
|
8
|
+
/** Published package name, used to build every install and setup command. */
|
|
9
|
+
export const PACKAGE_NAME = '@theone1345/smartrelay';
|
|
10
|
+
/** The exact command the user should run to configure credentials. */
|
|
11
|
+
export const SETUP_COMMAND = `npx ${PACKAGE_NAME} setup`;
|
|
12
|
+
/** One-shot command: prompts for keys, then registers the MCP server. */
|
|
13
|
+
export const INSTALL_COMMAND = `npx ${PACKAGE_NAME} init`;
|
|
14
|
+
/** One-line nudge appended to errors and warnings. */
|
|
15
|
+
export const SETUP_HINT = `Run \`${SETUP_COMMAND}\` to store your provider API keys.`;
|
|
16
|
+
/**
|
|
17
|
+
* Providers the wizard asks about, in prompt order.
|
|
18
|
+
*
|
|
19
|
+
* NVIDIA and OpenRouter are required because every sub-agent in
|
|
20
|
+
* `config/runners/agents.yaml` is backed by one of them. Anthropic and OpenAI
|
|
21
|
+
* unlock additional runners but nothing depends on them.
|
|
22
|
+
*/
|
|
23
|
+
export const PROVIDER_KEYS = [
|
|
24
|
+
{
|
|
25
|
+
env: 'NVIDIA_API_KEY',
|
|
26
|
+
label: 'NVIDIA NIM / API Catalog',
|
|
27
|
+
required: true,
|
|
28
|
+
prefix: 'nvapi-',
|
|
29
|
+
consoleUrl: 'https://build.nvidia.com',
|
|
30
|
+
configField: 'nvidiaApiKey',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
env: 'OPENROUTER_API_KEY',
|
|
34
|
+
label: 'OpenRouter',
|
|
35
|
+
required: true,
|
|
36
|
+
prefix: 'sk-or-v1-',
|
|
37
|
+
consoleUrl: 'https://openrouter.ai/keys',
|
|
38
|
+
configField: 'openrouterApiKey',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
env: 'ANTHROPIC_API_KEY',
|
|
42
|
+
label: 'Anthropic',
|
|
43
|
+
required: false,
|
|
44
|
+
prefix: 'sk-ant-',
|
|
45
|
+
consoleUrl: 'https://console.anthropic.com/settings/keys',
|
|
46
|
+
configField: 'anthropicApiKey',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
env: 'OPENAI_API_KEY',
|
|
50
|
+
label: 'OpenAI',
|
|
51
|
+
required: false,
|
|
52
|
+
prefix: 'sk-',
|
|
53
|
+
consoleUrl: 'https://platform.openai.com/api-keys',
|
|
54
|
+
configField: 'openaiApiKey',
|
|
55
|
+
},
|
|
56
|
+
];
|
|
57
|
+
/** Look up a spec by environment variable name. */
|
|
58
|
+
export function findKeySpec(envVar) {
|
|
59
|
+
return PROVIDER_KEYS.find((spec) => spec.env === envVar);
|
|
60
|
+
}
|
|
61
|
+
/** Shortest key length that still leaves something to hide in the middle. */
|
|
62
|
+
const MASKABLE_LENGTH = 12;
|
|
63
|
+
/**
|
|
64
|
+
* Render a secret for display: enough to recognize, never enough to use.
|
|
65
|
+
*
|
|
66
|
+
* Short values are replaced wholesale — a 10-character key would otherwise be
|
|
67
|
+
* almost fully revealed by a first-6/last-4 split.
|
|
68
|
+
*/
|
|
69
|
+
export function maskKey(value) {
|
|
70
|
+
const trimmed = value.trim();
|
|
71
|
+
if (trimmed.length < MASKABLE_LENGTH)
|
|
72
|
+
return '****';
|
|
73
|
+
return `${trimmed.slice(0, 6)}…${trimmed.slice(-4)}`;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Check a key against its expected shape.
|
|
77
|
+
*
|
|
78
|
+
* Returns a warning string or `null`. It never throws and never rejects:
|
|
79
|
+
* providers rotate their key formats, and a wizard that refuses a valid new-style
|
|
80
|
+
* key is worse than one that saves an invalid one.
|
|
81
|
+
*/
|
|
82
|
+
export function validateKeyFormat(spec, value) {
|
|
83
|
+
const trimmed = value.trim();
|
|
84
|
+
if (!trimmed)
|
|
85
|
+
return null;
|
|
86
|
+
if (spec.prefix && !trimmed.startsWith(spec.prefix)) {
|
|
87
|
+
return `expected ${spec.env} to start with "${spec.prefix}"`;
|
|
88
|
+
}
|
|
89
|
+
if (trimmed.length < 20) {
|
|
90
|
+
return `${spec.env} looks short (${trimmed.length} characters)`;
|
|
91
|
+
}
|
|
92
|
+
if (/\s/.test(trimmed)) {
|
|
93
|
+
return `${spec.env} contains whitespace`;
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* True when not one required provider key is set.
|
|
99
|
+
*
|
|
100
|
+
* Deliberately not `ready === 0`: runners that need no credential (Ollama)
|
|
101
|
+
* report as authenticated, so a count alone reads "healthy" on an install where
|
|
102
|
+
* every hosted model is unreachable.
|
|
103
|
+
*/
|
|
104
|
+
export function noProviderKeysConfigured(summary) {
|
|
105
|
+
return summary.missingRequired.length === PROVIDER_KEYS.filter((spec) => spec.required).length;
|
|
106
|
+
}
|
|
107
|
+
/** Roll runner metadata up into the counts the wizard and startup warning print. */
|
|
108
|
+
export function summarizeCredentials(metadata) {
|
|
109
|
+
const missingEnvVars = [];
|
|
110
|
+
let ready = 0;
|
|
111
|
+
for (const runner of metadata) {
|
|
112
|
+
if (runner.is_authenticated) {
|
|
113
|
+
ready++;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const envVar = runner.credentials_env_var;
|
|
117
|
+
if (envVar && !missingEnvVars.includes(envVar))
|
|
118
|
+
missingEnvVars.push(envVar);
|
|
119
|
+
}
|
|
120
|
+
const missingRequired = PROVIDER_KEYS.filter((spec) => spec.required && !process.env[spec.env]).map((spec) => spec.env);
|
|
121
|
+
return { ready, total: metadata.length, missingEnvVars, missingRequired };
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Render the runner readiness table.
|
|
125
|
+
*
|
|
126
|
+
* Shared by `smartrelay setup` and `npm run quick-test -- --list` so both always
|
|
127
|
+
* show the same thing.
|
|
128
|
+
*/
|
|
129
|
+
export function formatRunnerStatusLines(metadata) {
|
|
130
|
+
return metadata.map((runner) => {
|
|
131
|
+
const auth = runner.is_authenticated ? '🟢 AUTHENTICATED' : '🔴 MISSING_AUTH';
|
|
132
|
+
const cost = `$${runner.pricing.cost_per_million_input_tokens}/` +
|
|
133
|
+
`$${runner.pricing.cost_per_million_output_tokens} per M`;
|
|
134
|
+
return `- ${runner.runner_id.padEnd(30)} [${runner.model}] ${auth.padEnd(20)} ${cost}`;
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Tell the operator, at startup, that nothing will work.
|
|
139
|
+
*
|
|
140
|
+
* Two hard rules, both load-bearing:
|
|
141
|
+
* - Log only. `getLogger` writes to stderr; a single byte on stdout would
|
|
142
|
+
* corrupt the stdio transport's JSON-RPC framing.
|
|
143
|
+
* - Never throw and never exit. An MCP client renders a non-zero exit as
|
|
144
|
+
* "server crashed", and missing credentials must stay a recoverable state
|
|
145
|
+
* the user can fix without restarting anything.
|
|
146
|
+
*/
|
|
147
|
+
export function warnIfNoCredentials(metadata, logger) {
|
|
148
|
+
let summary;
|
|
149
|
+
try {
|
|
150
|
+
summary = summarizeCredentials(metadata);
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (summary.total === 0)
|
|
156
|
+
return;
|
|
157
|
+
if (noProviderKeysConfigured(summary)) {
|
|
158
|
+
logger.warning(`No provider API keys detected — only ${summary.ready} of ${summary.total} runners are usable ` +
|
|
159
|
+
'(those needing no credentials). Every hosted model will fail until a key is configured.');
|
|
160
|
+
logger.warning(`Fix: ${SETUP_COMMAND}`);
|
|
161
|
+
logger.warning(`Or set ${summary.missingRequired.join(' / ')} in your MCP client's "env" block.`);
|
|
162
|
+
}
|
|
163
|
+
else if (summary.missingEnvVars.length) {
|
|
164
|
+
logger.info(`Credentials: ${summary.ready}/${summary.total} runners authenticated. ` +
|
|
165
|
+
`Missing: ${summary.missingEnvVars.join(', ')}.`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.js","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"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"}
|
package/dist/http-api.d.ts
CHANGED
|
@@ -7,14 +7,8 @@
|
|
|
7
7
|
* Transport: HTTP/JSON with Bearer token auth.
|
|
8
8
|
*/
|
|
9
9
|
import { type FastifyInstance, type FastifyReply, type FastifyRequest } from 'fastify';
|
|
10
|
-
|
|
11
|
-
import { TaskRouter } from './router.js';
|
|
12
|
-
import { RunnerRegistry } from './runners/registry.js';
|
|
13
|
-
export declare function getRegistry(configPath?: string | null): RunnerRegistry;
|
|
14
|
-
export declare function getEngine(): BenchmarkEngine;
|
|
15
|
-
export declare function getRouter(): TaskRouter;
|
|
10
|
+
export { createDispatchContext, dispatchTool, getEngine, getRegistry, getRouter } from './tools/dispatch.js';
|
|
16
11
|
export declare function verifyApiKey(request: FastifyRequest, reply: FastifyReply): void;
|
|
17
|
-
export declare function dispatchTool(toolName: string, args?: Record<string, unknown>): Promise<unknown>;
|
|
18
12
|
export declare function createHttpServer(): FastifyInstance;
|
|
19
13
|
export declare function runHttpServer(options?: {
|
|
20
14
|
config?: string;
|