@sphyr/cli 2.0.0-beta.1
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 +167 -0
- package/index.js +89 -0
- package/package.json +31 -0
- package/src/commands/guard.js +145 -0
- package/src/commands/init.js +484 -0
- package/src/commands/login.js +91 -0
- package/src/commands/verify.js +34 -0
- package/src/lib/ide-clients.js +411 -0
- package/src/lib/url-guard.js +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sphyr
|
|
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
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
<!-- generated-by: gsd-doc-writer -->
|
|
2
|
+
|
|
3
|
+
# @sphyr/cli
|
|
4
|
+
|
|
5
|
+
Sphyr platform CLI. Currently ships Agent Guard commands; additional product groups will be added as they launch.
|
|
6
|
+
|
|
7
|
+
Part of the [Sphyr Agent Guard monorepo](../../README.md).
|
|
8
|
+
|
|
9
|
+
## Commands
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
sphyr login Sign in to Sphyr (device flow — writes ~/.sphyr/config.json)
|
|
13
|
+
sphyr guard init Interactive setup wizard — writes IDE MCP configs
|
|
14
|
+
sphyr guard verify Connectivity check (delegates to @sphyr/sdk sphyr-mcp)
|
|
15
|
+
sphyr guard run STDIO↔HTTP bridge — used directly in IDE MCP config entries
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
> **`sphyr guard verify`** delegates to the `sphyr-mcp` binary from `@sphyr/sdk` — it performs
|
|
19
|
+
> a real HMAC handshake and `agent_guard_up` round-trip, reports gateway reachability and
|
|
20
|
+
> per-library instrumentation status, and supports `--json` for CI output.
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
Designed to be used via `npx` without a permanent install:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx @sphyr/cli login
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
To install globally:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm install -g @sphyr/cli
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Quick start — Claude Desktop setup
|
|
37
|
+
|
|
38
|
+
The recommended way to configure Claude Desktop is to run the interactive setup wizard, which writes the correct config automatically:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npx @sphyr/cli guard init
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The wizard writes a config entry that routes through the `sphyr-mcp` command from `@sphyr/sdk`, which signs every outbound request with HMAC. The resulting entry looks like:
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"mcpServers": {
|
|
49
|
+
"sphyr": {
|
|
50
|
+
"command": "npx",
|
|
51
|
+
"args": ["-y", "-p", "@sphyr/sdk", "sphyr-mcp"],
|
|
52
|
+
"env": {
|
|
53
|
+
"SPHYR_CREDENTIAL": "your-credential"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`SPHYR_DOWNSTREAM` is optional — add a JSON array of MCP servers to also wrap and guard them
|
|
61
|
+
(tool-poisoning + tool-argument-entropy blocking). The old standalone `sphyr-proxy` binary has been
|
|
62
|
+
**removed**; `sphyr-mcp` replaces it (`init` rewrites any legacy `sphyr-proxy` and `sphyr-guard` IDE entries automatically).
|
|
63
|
+
|
|
64
|
+
> **Note:** The `init` wizard configures the unified SDK binary (`sphyr-mcp` from `@sphyr/sdk`). The `sphyr guard run` command in *this* CLI package is a simpler STDIO↔HTTP bridge intended for development and advanced manual setups.
|
|
65
|
+
|
|
66
|
+
## Sign in for SDK use (`login`)
|
|
67
|
+
|
|
68
|
+
If you are using the Python or TypeScript SDK directly (not just the IDE MCP integration), run:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npx @sphyr/cli login
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
This runs the device flow and writes your credential to `~/.sphyr/config.json` (owner-only `0600`).
|
|
75
|
+
The SDKs then auto-load it — `auto_instrument()` / `autoInstrument()` work with no arguments and no
|
|
76
|
+
manual key copy-paste. Credential precedence: explicit args → `SPHYR_*` env vars → `~/.sphyr/config.json`.
|
|
77
|
+
|
|
78
|
+
## Headless environments (SSH, containers, CI)
|
|
79
|
+
|
|
80
|
+
If you are running in a terminal without a browser — SSH session, Docker container, GitHub Codespaces, or a CI environment — use the device flow:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npx @sphyr/cli guard init
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The wizard will print a short authorization code (e.g. `J3K9-M7PQ`) and a URL:
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
◆ Authorization code: J3K9-M7PQ
|
|
90
|
+
→ Open: https://console.sphyr.io/device
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Open the URL on any device, log in with GitHub if prompted, enter the code, and click **Authorize**. The CLI will detect the approval within 5 seconds and write your IDE configs automatically.
|
|
94
|
+
|
|
95
|
+
`sphyr guard init` also auto-detects headless environments: if `stdin` is not a TTY (e.g., piped input or a non-interactive shell), the device flow starts automatically.
|
|
96
|
+
|
|
97
|
+
### Behind a corporate NAT or VPN
|
|
98
|
+
|
|
99
|
+
If many users on your network run `sphyr guard init` from a shared egress IP (corporate VPN, NAT'd office network, CI fleet, GitHub Codespaces region), the API may transiently rate-limit polling requests from that IP and respond with `HTTP 429 Too Many Requests` and a `Retry-After` header. The CLI handles this automatically: it sleeps for the server-specified duration and resumes polling without surfacing an error.
|
|
100
|
+
|
|
101
|
+
## Environment variables
|
|
102
|
+
|
|
103
|
+
| Variable | Required | Default | Description |
|
|
104
|
+
| --------------------- | ------------------------ | -------------------------- | --------------------------------------------------------------------- |
|
|
105
|
+
| `SPHYR_CREDENTIAL` | Yes (injected by `init`) | — | Single credential passed through to the Agent Guard HTTP endpoint |
|
|
106
|
+
| `AGENT_GUARD_MCP_URL` | No | `https://api.sphyr.io/mcp` | Override the upstream MCP endpoint (dev/staging only; must use HTTPS) |
|
|
107
|
+
|
|
108
|
+
The bridge enforces HTTPS for `AGENT_GUARD_MCP_URL` and will throw at startup if a non-HTTPS URL is provided.
|
|
109
|
+
|
|
110
|
+
## Source structure
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
packages/cli/
|
|
114
|
+
index.js # Entry point — two-level command dispatch
|
|
115
|
+
src/
|
|
116
|
+
commands/
|
|
117
|
+
guard.js # runGuard() — STDIO↔HTTP bridge
|
|
118
|
+
init.js # runInit() — interactive setup wizard
|
|
119
|
+
login.js # runLogin() — device-flow sign-in → ~/.sphyr/config.json
|
|
120
|
+
verify.js # runVerify() — delegates to @sphyr/sdk sphyr-mcp verify
|
|
121
|
+
lib/
|
|
122
|
+
ide-clients.js # IDE_CLIENTS registry, config read/merge/write helpers
|
|
123
|
+
url-guard.js # HTTPS/SSRF URL validation for AGENT_GUARD_MCP_URL
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Supported IDE clients
|
|
127
|
+
|
|
128
|
+
The `init` wizard detects and can configure:
|
|
129
|
+
|
|
130
|
+
- Claude Desktop
|
|
131
|
+
- Cursor
|
|
132
|
+
- VS Code (via Cline / Roo extension)
|
|
133
|
+
- Antigravity
|
|
134
|
+
- Zed
|
|
135
|
+
- Windsurf
|
|
136
|
+
|
|
137
|
+
## Development
|
|
138
|
+
|
|
139
|
+
This package has no build step — it is plain ES module JavaScript targeting Node >= 22.
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
# From the monorepo root
|
|
143
|
+
nvm use
|
|
144
|
+
npm ci
|
|
145
|
+
|
|
146
|
+
# Run the bridge against the live API
|
|
147
|
+
AGENT_GUARD_MCP_URL=https://api.sphyr.io/mcp node packages/cli/index.js guard run
|
|
148
|
+
|
|
149
|
+
# Run the init wizard
|
|
150
|
+
node packages/cli/index.js guard init
|
|
151
|
+
|
|
152
|
+
# Sign in
|
|
153
|
+
node packages/cli/index.js login
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Tests
|
|
157
|
+
|
|
158
|
+
Tests are not yet present in this package. The `init` wizard and bridge logic are covered by integration tests in the root `tests/` directory.
|
|
159
|
+
|
|
160
|
+
## License
|
|
161
|
+
|
|
162
|
+
MIT
|
|
163
|
+
|
|
164
|
+
## Prerequisites
|
|
165
|
+
|
|
166
|
+
- `Node.js >= 22.0.0 < 24.0.0` (enforced by the package `engines` field). Use [nvm](https://github.com/nvm-sh/nvm) and run `nvm use` from the monorepo root to switch to the correct version automatically.
|
|
167
|
+
- When invoking via `npx`, ensure the Node.js version in your shell meets this requirement — `npx` inherits the active Node version and will fail with an engines mismatch error on older releases.
|
package/index.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// SPDX-License-Identifier: MIT
|
|
4
|
+
// Copyright (c) 2026 Sphyr
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Sphyr platform CLI
|
|
8
|
+
*
|
|
9
|
+
* Top-level commands:
|
|
10
|
+
* login — platform-wide device-flow sign-in (cross-product)
|
|
11
|
+
* guard — Agent Guard subcommand group
|
|
12
|
+
*
|
|
13
|
+
* Agent Guard subcommands (sphyr guard <subcommand>):
|
|
14
|
+
* init — interactive setup wizard (configures IDE MCP configs)
|
|
15
|
+
* verify — connectivity check (delegates to @sphyr/sdk sphyr-mcp)
|
|
16
|
+
* run — STDIO↔HTTP bridge (used in IDE MCP config entries)
|
|
17
|
+
*
|
|
18
|
+
* Reserved (products not yet available):
|
|
19
|
+
* box — future product placeholder
|
|
20
|
+
*
|
|
21
|
+
* Usage in IDE MCP config:
|
|
22
|
+
* "command": "npx",
|
|
23
|
+
* "args": ["-y", "-p", "@sphyr/sdk@<version>", "sphyr-mcp"]
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { runGuard } from './src/commands/guard.js';
|
|
27
|
+
import { runInit } from './src/commands/init.js';
|
|
28
|
+
import { runLogin } from './src/commands/login.js';
|
|
29
|
+
import { runVerify } from './src/commands/verify.js';
|
|
30
|
+
|
|
31
|
+
const [, , topLevel, subCommand] = process.argv;
|
|
32
|
+
|
|
33
|
+
switch (topLevel) {
|
|
34
|
+
case 'login':
|
|
35
|
+
runLogin();
|
|
36
|
+
break;
|
|
37
|
+
|
|
38
|
+
case 'guard':
|
|
39
|
+
switch (subCommand) {
|
|
40
|
+
case 'init':
|
|
41
|
+
runInit();
|
|
42
|
+
break;
|
|
43
|
+
case 'verify':
|
|
44
|
+
runVerify();
|
|
45
|
+
break;
|
|
46
|
+
case 'run':
|
|
47
|
+
runGuard();
|
|
48
|
+
break;
|
|
49
|
+
case undefined:
|
|
50
|
+
process.stderr.write(
|
|
51
|
+
'Usage: sphyr guard <subcommand>\n\n' +
|
|
52
|
+
'Subcommands:\n' +
|
|
53
|
+
' init Interactive setup wizard — configures IDE MCP configs\n' +
|
|
54
|
+
' verify Check connectivity to Sphyr Agent Guard\n' +
|
|
55
|
+
' run Start the STDIO↔HTTP MCP bridge\n',
|
|
56
|
+
);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
break;
|
|
59
|
+
default:
|
|
60
|
+
process.stderr.write(
|
|
61
|
+
`Unknown subcommand: sphyr guard ${subCommand}\n` +
|
|
62
|
+
'Run "sphyr guard" for available subcommands.\n',
|
|
63
|
+
);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
break;
|
|
67
|
+
|
|
68
|
+
case 'box':
|
|
69
|
+
process.stderr.write('sphyr box: not yet available.\n');
|
|
70
|
+
process.exit(1);
|
|
71
|
+
break;
|
|
72
|
+
|
|
73
|
+
case undefined:
|
|
74
|
+
process.stderr.write(
|
|
75
|
+
'Usage: sphyr <command>\n\n' +
|
|
76
|
+
'Commands:\n' +
|
|
77
|
+
' login Sign in to Sphyr (device flow)\n' +
|
|
78
|
+
' guard Agent Guard commands (init, verify, run)\n',
|
|
79
|
+
);
|
|
80
|
+
process.exit(1);
|
|
81
|
+
break;
|
|
82
|
+
|
|
83
|
+
default:
|
|
84
|
+
process.stderr.write(
|
|
85
|
+
`Unknown command: sphyr ${topLevel}\n` +
|
|
86
|
+
'Run "sphyr" for available commands.\n',
|
|
87
|
+
);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sphyr/cli",
|
|
3
|
+
"version": "2.0.0-beta.1",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"description": "Sphyr platform CLI",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "index.js",
|
|
8
|
+
"bin": {
|
|
9
|
+
"sphyr": "./index.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"index.js",
|
|
13
|
+
"src",
|
|
14
|
+
"README.md",
|
|
15
|
+
"LICENSE"
|
|
16
|
+
],
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"start": "node index.js"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@clack/prompts": "^1.2.0",
|
|
25
|
+
"@sphyr/sdk": "^2.0.0-beta.1",
|
|
26
|
+
"open": "^11.0.0"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=22.0.0 <24.0.0"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (c) 2026 Sphyr
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* sphyr guard — STDIO-to-HTTP MCP bridge
|
|
6
|
+
*
|
|
7
|
+
* Proxies JSON-RPC lines from stdin to the Sphyr Agent Guard HTTP endpoint
|
|
8
|
+
* and writes responses to stdout. Used as the command in IDE MCP config:
|
|
9
|
+
*
|
|
10
|
+
* "command": "npx",
|
|
11
|
+
* "args": ["-y", "@sphyr/guard", "guard"]
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import readline from 'node:readline';
|
|
15
|
+
import { URL as NURL } from 'node:url';
|
|
16
|
+
|
|
17
|
+
const AGENT_GUARD_URL = process.env.AGENT_GUARD_MCP_URL || 'https://api.sphyr.io/mcp';
|
|
18
|
+
|
|
19
|
+
function safeUrl(raw) {
|
|
20
|
+
try {
|
|
21
|
+
const u = new NURL(raw);
|
|
22
|
+
u.username = '';
|
|
23
|
+
u.password = '';
|
|
24
|
+
return u.toString();
|
|
25
|
+
} catch {
|
|
26
|
+
return '<invalid url>';
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!AGENT_GUARD_URL.startsWith('https://')) {
|
|
31
|
+
throw new Error(
|
|
32
|
+
`AGENT_GUARD_MCP_URL must use HTTPS. Refusing to proxy over insecure connection: ${safeUrl(AGENT_GUARD_URL)}`,
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const AGENT_GUARD_SAFE_URL = safeUrl(AGENT_GUARD_URL);
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Runs the STDIO-to-HTTP MCP bridge.
|
|
40
|
+
* Reads JSON-RPC lines from stdin, proxies each to the MCP HTTP endpoint,
|
|
41
|
+
* and writes the response to stdout as a single line.
|
|
42
|
+
*/
|
|
43
|
+
export function runGuard() {
|
|
44
|
+
const rl = readline.createInterface({
|
|
45
|
+
input: process.stdin,
|
|
46
|
+
terminal: false,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
rl.on('close', () => {
|
|
50
|
+
// stdin closed (IDE shut down or pipe ended) — event loop drains naturally
|
|
51
|
+
process.exit(0);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
let inFlight = 0;
|
|
55
|
+
const MAX_CONCURRENT = 10;
|
|
56
|
+
|
|
57
|
+
rl.on('line', async (line) => {
|
|
58
|
+
const content = line.trim();
|
|
59
|
+
if (!content) return;
|
|
60
|
+
|
|
61
|
+
if (inFlight >= MAX_CONCURRENT) {
|
|
62
|
+
// Drop the request and emit a JSON-RPC error to avoid unbounded concurrency.
|
|
63
|
+
let requestId = null;
|
|
64
|
+
try {
|
|
65
|
+
requestId = JSON.parse(content).id ?? null;
|
|
66
|
+
} catch {
|
|
67
|
+
/* ignore */
|
|
68
|
+
}
|
|
69
|
+
const errMessage = JSON.stringify({
|
|
70
|
+
jsonrpc: '2.0',
|
|
71
|
+
error: { code: -32000, message: 'Agent Guard CLI Bridge Error: too many concurrent requests' },
|
|
72
|
+
id: requestId,
|
|
73
|
+
});
|
|
74
|
+
process.stdout.write(errMessage + '\n');
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
inFlight = inFlight + 1;
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
// Attempt to extract the request ID for well-formed error responses
|
|
82
|
+
let requestId = null;
|
|
83
|
+
try {
|
|
84
|
+
const parsed = JSON.parse(content);
|
|
85
|
+
requestId = parsed.id ?? null;
|
|
86
|
+
} catch {
|
|
87
|
+
// Ignore parse errors here, let the upstream gateway handle it
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
const response = await fetch(AGENT_GUARD_URL, {
|
|
92
|
+
method: 'POST',
|
|
93
|
+
headers: {
|
|
94
|
+
'Content-Type': 'application/json',
|
|
95
|
+
Accept: 'application/json',
|
|
96
|
+
},
|
|
97
|
+
body: content,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const body = await response.text();
|
|
101
|
+
if (!response.ok) {
|
|
102
|
+
// Non-2xx response — emit a proper JSON-RPC error instead of forwarding
|
|
103
|
+
// a raw HTML error page that would corrupt the JSON-RPC stream.
|
|
104
|
+
process.stderr.write(`[guard] upstream error ${response.status}: ${body.slice(0, 200)}\n`);
|
|
105
|
+
const errMessage = JSON.stringify({
|
|
106
|
+
jsonrpc: '2.0',
|
|
107
|
+
error: {
|
|
108
|
+
code: -32000,
|
|
109
|
+
message: `Agent Guard CLI Bridge Error: upstream returned ${response.status}`,
|
|
110
|
+
},
|
|
111
|
+
id: requestId,
|
|
112
|
+
});
|
|
113
|
+
process.stdout.write(errMessage + '\n');
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
// Ensure the response is written to stdout as a single line
|
|
117
|
+
process.stdout.write(body.replace(/\r?\n/g, '') + '\n');
|
|
118
|
+
} catch (networkError) {
|
|
119
|
+
// Log raw error (may contain credentials) to stderr only — never stdout
|
|
120
|
+
process.stderr.write(`[guard] network error: ${networkError.message}\n`);
|
|
121
|
+
// Ensure STDIO JSON-RPC clients get a properly formatted message on failure
|
|
122
|
+
const errMessage = JSON.stringify({
|
|
123
|
+
jsonrpc: '2.0',
|
|
124
|
+
error: {
|
|
125
|
+
code: -32000,
|
|
126
|
+
message: `Agent Guard CLI Bridge Error: Could not reach gateway at ${AGENT_GUARD_SAFE_URL}`,
|
|
127
|
+
},
|
|
128
|
+
id: requestId,
|
|
129
|
+
});
|
|
130
|
+
process.stdout.write(errMessage + '\n');
|
|
131
|
+
}
|
|
132
|
+
} catch (fatalError) {
|
|
133
|
+
// Write the real error to stderr for operator visibility; never expose it to the agent.
|
|
134
|
+
process.stderr.write(`[agent-guard] fatal: ${fatalError?.message ?? fatalError}\n`);
|
|
135
|
+
const fatalMsg = JSON.stringify({
|
|
136
|
+
jsonrpc: '2.0',
|
|
137
|
+
error: { code: -32603, message: 'Internal agent guard error. Check stderr for details.' },
|
|
138
|
+
id: null,
|
|
139
|
+
});
|
|
140
|
+
process.stdout.write(fatalMsg + '\n');
|
|
141
|
+
} finally {
|
|
142
|
+
inFlight = inFlight - 1;
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
}
|