opalserve 3.1.0 → 3.2.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/README.md +80 -45
- package/dist/auth/api-keys.d.ts +8 -0
- package/dist/auth/api-keys.d.ts.map +1 -1
- package/dist/auth/api-keys.js +62 -2
- package/dist/auth/api-keys.js.map +1 -1
- package/dist/auth/index.d.ts +1 -1
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +1 -1
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/middleware.d.ts.map +1 -1
- package/dist/auth/middleware.js +3 -11
- package/dist/auth/middleware.js.map +1 -1
- package/dist/cli/commands/doctor.d.ts +2 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/doctor.js +107 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/link.d.ts +11 -0
- package/dist/cli/commands/link.d.ts.map +1 -0
- package/dist/cli/commands/link.js +135 -0
- package/dist/cli/commands/link.js.map +1 -0
- package/dist/cli/commands/mcp.d.ts +7 -0
- package/dist/cli/commands/mcp.d.ts.map +1 -0
- package/dist/cli/commands/mcp.js +46 -0
- package/dist/cli/commands/mcp.js.map +1 -0
- package/dist/cli/commands/start.d.ts +1 -0
- package/dist/cli/commands/start.d.ts.map +1 -1
- package/dist/cli/commands/start.js +13 -0
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/editors/base.d.ts +23 -0
- package/dist/cli/editors/base.d.ts.map +1 -0
- package/dist/cli/editors/base.js +75 -0
- package/dist/cli/editors/base.js.map +1 -0
- package/dist/cli/editors/claude.d.ts +8 -0
- package/dist/cli/editors/claude.d.ts.map +1 -0
- package/dist/cli/editors/claude.js +31 -0
- package/dist/cli/editors/claude.js.map +1 -0
- package/dist/cli/editors/cline.d.ts +14 -0
- package/dist/cli/editors/cline.d.ts.map +1 -0
- package/dist/cli/editors/cline.js +32 -0
- package/dist/cli/editors/cline.js.map +1 -0
- package/dist/cli/editors/cursor.d.ts +8 -0
- package/dist/cli/editors/cursor.d.ts.map +1 -0
- package/dist/cli/editors/cursor.js +14 -0
- package/dist/cli/editors/cursor.js.map +1 -0
- package/dist/cli/editors/index.d.ts +14 -0
- package/dist/cli/editors/index.d.ts.map +1 -0
- package/dist/cli/editors/index.js +34 -0
- package/dist/cli/editors/index.js.map +1 -0
- package/dist/cli/editors/types.d.ts +33 -0
- package/dist/cli/editors/types.d.ts.map +1 -0
- package/dist/cli/editors/types.js +2 -0
- package/dist/cli/editors/types.js.map +1 -0
- package/dist/cli/index.js +26 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/config/defaults.d.ts +1 -0
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +3 -0
- package/dist/config/defaults.js.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/server/app.d.ts.map +1 -1
- package/dist/server/app.js +34 -1
- package/dist/server/app.js.map +1 -1
- package/dist/server/routes/_schemas.d.ts +116 -0
- package/dist/server/routes/_schemas.d.ts.map +1 -0
- package/dist/server/routes/_schemas.js +53 -0
- package/dist/server/routes/_schemas.js.map +1 -0
- package/dist/server/routes/auth.d.ts.map +1 -1
- package/dist/server/routes/auth.js +28 -14
- package/dist/server/routes/auth.js.map +1 -1
- package/dist/server/routes/context.d.ts.map +1 -1
- package/dist/server/routes/context.js +14 -7
- package/dist/server/routes/context.js.map +1 -1
- package/dist/server/routes/servers.d.ts.map +1 -1
- package/dist/server/routes/servers.js +12 -7
- package/dist/server/routes/servers.js.map +1 -1
- package/dist/server/routes/stats.d.ts.map +1 -1
- package/dist/server/routes/stats.js +26 -5
- package/dist/server/routes/stats.js.map +1 -1
- package/dist/server/routes/team-servers.d.ts.map +1 -1
- package/dist/server/routes/team-servers.js +7 -6
- package/dist/server/routes/team-servers.js.map +1 -1
- package/dist/server/routes/tools.d.ts.map +1 -1
- package/dist/server/routes/tools.js +26 -12
- package/dist/server/routes/tools.js.map +1 -1
- package/dist/utils/browser.d.ts +7 -0
- package/dist/utils/browser.d.ts.map +1 -0
- package/dist/utils/browser.js +34 -0
- package/dist/utils/browser.js.map +1 -0
- package/package.json +93 -91
package/README.md
CHANGED
|
@@ -22,16 +22,16 @@
|
|
|
22
22
|
|
|
23
23
|
## Feature Highlights
|
|
24
24
|
|
|
25
|
-
| | Feature | Description |
|
|
26
|
-
|
|
27
|
-
| :books: | **Team MCP Registry** | Admin registers servers once; every developer pulls them with `opalserve sync` |
|
|
28
|
-
| :brain: | **Shared Knowledge Base** |
|
|
29
|
-
| :bar_chart: | **Usage Analytics Dashboard** | React SPA
|
|
30
|
-
| :lock: | **Auth & Access Control** | User accounts,
|
|
31
|
-
| :electric_plug: | **GitHub + Slack Integrations** | Webhooks
|
|
32
|
-
| :globe_with_meridians: | **MCP Gateway** | OpalServe itself
|
|
33
|
-
| :art: | **Beautiful CLI** | Interactive setup wizard, gradient banners, color-coded tables, 20+ commands |
|
|
34
|
-
| :seedling: | **100% Open Source** | MIT licensed, self-host for free, no vendor lock-in |
|
|
25
|
+
| | Feature | Status | Description |
|
|
26
|
+
|---|---|---|---|
|
|
27
|
+
| :books: | **Team MCP Registry** | ✅ Stable | Admin registers servers once; every developer pulls them with `opalserve sync` |
|
|
28
|
+
| :brain: | **Shared Knowledge Base** | ⚙️ Keyword search today; semantic graph (Graphiti) lands in v3.3 | Upload docs, search via the `opalserve_search` MCP tool from any connected client |
|
|
29
|
+
| :bar_chart: | **Usage Analytics Dashboard** | 🚧 Dashboard ships; live event ingestion lands in v3.3 | React SPA renders the analytics surface; full event capture is the v3.3 milestone |
|
|
30
|
+
| :lock: | **Auth & Access Control** | ✅ Stable (v3.1.1 hardened) | User accounts (scrypt-hashed), HMAC-secured API keys, per-IP/key rate limits. Per-role tool-permission enforcement targets v3.3 |
|
|
31
|
+
| :electric_plug: | **GitHub + Slack Integrations** | ⚙️ Webhook handlers ship; signature verification lands in v3.2 | Webhooks accept events; Slack slash command returns search results |
|
|
32
|
+
| :globe_with_meridians: | **MCP Gateway** | ✅ Stable (stdio); SSE/HTTP transports targeted for v3.3 | OpalServe is itself an MCP server — connect any MCP client over stdio today |
|
|
33
|
+
| :art: | **Beautiful CLI** | ✅ Stable | Interactive setup wizard, gradient banners, color-coded tables, 20+ commands |
|
|
34
|
+
| :seedling: | **100% Open Source** | ✅ Stable | MIT licensed, self-host for free, no vendor lock-in |
|
|
35
35
|
|
|
36
36
|
---
|
|
37
37
|
|
|
@@ -78,11 +78,11 @@
|
|
|
78
78
|
|
|
79
79
|
---
|
|
80
80
|
|
|
81
|
-
## Quick Start
|
|
82
|
-
|
|
83
|
-
```bash
|
|
84
|
-
# 1. Install globally
|
|
85
|
-
npm install -g opalserve
|
|
81
|
+
## Quick Start
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
# 1. Install globally
|
|
85
|
+
npm install -g opalserve
|
|
86
86
|
|
|
87
87
|
# 2. Run the interactive setup wizard
|
|
88
88
|
opalserve init
|
|
@@ -94,35 +94,63 @@ opalserve start
|
|
|
94
94
|
opalserve server add --name files --stdio "npx -y @modelcontextprotocol/server-filesystem ."
|
|
95
95
|
|
|
96
96
|
# 5. Discover available tools
|
|
97
|
-
opalserve tools search "read file"
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
OpalServe
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
#
|
|
112
|
-
#
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
97
|
+
opalserve tools search "read file"
|
|
98
|
+
|
|
99
|
+
# 6. Wire OpalServe into your AI editor
|
|
100
|
+
opalserve link claude # or: cursor, cline
|
|
101
|
+
# (restart the editor; OpalServe shows up as the "opalserve" MCP server)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
> **Tip:** OpalServe runs on port **3456** by default. The dashboard ships inside the npm package, opens automatically on `start`, and is served from `http://localhost:3456/dashboard`.
|
|
105
|
+
|
|
106
|
+
## Wire-Up — One Command
|
|
107
|
+
|
|
108
|
+
`opalserve link <editor>` writes the OpalServe MCP gateway into your editor's config so its AI assistant gets every tool you've registered. Supports **Claude Desktop**, **Cursor**, and **Cline (VS Code)**.
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
opalserve link cursor # link a single editor
|
|
112
|
+
opalserve link --all # link every detected editor
|
|
113
|
+
opalserve link --status # see what's linked, no changes
|
|
114
|
+
opalserve unlink cursor # remove later
|
|
115
|
+
opalserve doctor # diagnose port, gateway, links, secrets
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Under the hood the editor spawns `npx -y opalserve mcp` as its MCP stdio process. Override with `OPALSERVE_LINK_COMMAND` env var if you want it to invoke a specific binary path instead.
|
|
119
|
+
|
|
120
|
+
## Releases
|
|
121
|
+
|
|
122
|
+
**3.2.0 — Coherence (current)**: `opalserve link <editor>` wires OpalServe into Claude Desktop / Cursor / Cline with one command, plus `opalserve doctor` for self-serve diagnostics, the `opalserve mcp` stdio entry-point, and auto-open of the dashboard on `start`.
|
|
123
|
+
|
|
124
|
+
**3.1.1 — Trust Patch**: hardens the auth and HTTP surface.
|
|
125
|
+
|
|
126
|
+
- **API keys** now use HMAC-SHA256 with a per-installation server secret (was unsalted SHA-256). Legacy keys are rehashed transparently on first use.
|
|
127
|
+
- **Auth bypass** (env-var fallback that silently disabled auth in non-team mode) removed. The registry's mode is now the single source of truth.
|
|
128
|
+
- **Zod validation** is enforced on every HTTP route. Raw `request.body as { ... }` casts are gone.
|
|
129
|
+
- **Rate limiting** via `@fastify/rate-limit`: 200 req/min global per IP/key; 10 req/min on `/auth/register` and `/auth/login`.
|
|
130
|
+
- **Helmet** wired into Fastify (not just the Vercel docs layer); CORS narrowed from wildcard ports to a strict regex with optional allowlist via `OPALSERVE_CORS_ORIGINS`.
|
|
131
|
+
|
|
132
|
+
Disclosure policy: see [SECURITY.md](SECURITY.md).
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
opalserve --version
|
|
136
|
+
# 3.2.0
|
|
137
|
+
|
|
138
|
+
opalserve start
|
|
139
|
+
# HTTP API http://127.0.0.1:3456
|
|
140
|
+
# Dashboard http://127.0.0.1:3456/dashboard (opens in browser automatically)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
For source builds, the same release checks are used locally and in CI:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
pnpm typecheck
|
|
147
|
+
pnpm lint
|
|
148
|
+
pnpm test
|
|
149
|
+
pnpm build
|
|
150
|
+
pnpm pack --dry-run
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
---
|
|
126
154
|
|
|
127
155
|
## Team Mode Setup
|
|
128
156
|
|
|
@@ -247,8 +275,15 @@ http://localhost:3456/dashboard
|
|
|
247
275
|
| Command | Description |
|
|
248
276
|
|---|---|
|
|
249
277
|
| `opalserve init` | Interactive setup wizard -- configures mode, port, and first server |
|
|
250
|
-
| `opalserve start` | Start the registry server (HTTP API + MCP gateway) |
|
|
278
|
+
| `opalserve start` | Start the registry server (HTTP API + MCP gateway); opens dashboard in browser |
|
|
279
|
+
| `opalserve start --no-browser` | Start without auto-opening the dashboard |
|
|
251
280
|
| `opalserve status` | Show server status, connected servers, and tool counts |
|
|
281
|
+
| `opalserve doctor` | Self-diagnose port, gateway, registered servers, editor links, secrets |
|
|
282
|
+
| `opalserve link <editor>` | Wire OpalServe into Claude Desktop, Cursor, or Cline |
|
|
283
|
+
| `opalserve link --all` | Link every detected AI editor in one shot |
|
|
284
|
+
| `opalserve link --status` | Show editor link status (no changes) |
|
|
285
|
+
| `opalserve unlink <editor>` | Remove OpalServe from an editor's MCP config |
|
|
286
|
+
| `opalserve mcp` | Run as a stdio MCP server (used internally by linked editors) |
|
|
252
287
|
| `opalserve health` | Health check all registered servers |
|
|
253
288
|
| `opalserve health --server <name>` | Health check a specific server |
|
|
254
289
|
| `opalserve login` | Authenticate with a team server |
|
package/dist/auth/api-keys.d.ts
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
|
+
import type { Database } from '../storage/database.js';
|
|
1
2
|
export declare function generateApiKey(): {
|
|
2
3
|
key: string;
|
|
3
4
|
keyHash: string;
|
|
4
5
|
keyPrefix: string;
|
|
5
6
|
};
|
|
6
7
|
export declare function hashApiKey(key: string): string;
|
|
8
|
+
export declare function safeEqualHex(a: string, b: string): boolean;
|
|
9
|
+
export interface ApiKeyLookupResult {
|
|
10
|
+
id: string;
|
|
11
|
+
user_id: string;
|
|
12
|
+
expires_at: string | null;
|
|
13
|
+
}
|
|
14
|
+
export declare function lookupApiKey(db: Database, presentedKey: string): ApiKeyLookupResult | null;
|
|
7
15
|
//# sourceMappingURL=api-keys.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-keys.d.ts","sourceRoot":"","sources":["../../src/auth/api-keys.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"api-keys.d.ts","sourceRoot":"","sources":["../../src/auth/api-keys.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAkCvD,wBAAgB,cAAc,IAAI;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAMpF;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE9C;AAMD,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAO1D;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,wBAAgB,YAAY,CAAC,EAAE,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CAkB1F"}
|
package/dist/auth/api-keys.js
CHANGED
|
@@ -1,12 +1,72 @@
|
|
|
1
|
-
import { randomBytes, createHash } from 'node:crypto';
|
|
1
|
+
import { randomBytes, createHash, createHmac, timingSafeEqual } from 'node:crypto';
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, chmodSync } from 'node:fs';
|
|
3
|
+
import { dirname } from 'node:path';
|
|
4
|
+
import { getApiKeySecretPath } from '../config/defaults.js';
|
|
5
|
+
let cachedSecret = null;
|
|
6
|
+
function getServerSecret() {
|
|
7
|
+
if (cachedSecret)
|
|
8
|
+
return cachedSecret;
|
|
9
|
+
const fromEnv = process.env.OPALSERVE_API_KEY_SECRET;
|
|
10
|
+
if (fromEnv) {
|
|
11
|
+
if (!/^[0-9a-f]{64}$/i.test(fromEnv)) {
|
|
12
|
+
throw new Error('OPALSERVE_API_KEY_SECRET must be a 64-character hex string (256 bits)');
|
|
13
|
+
}
|
|
14
|
+
cachedSecret = Buffer.from(fromEnv, 'hex');
|
|
15
|
+
return cachedSecret;
|
|
16
|
+
}
|
|
17
|
+
const path = getApiKeySecretPath();
|
|
18
|
+
if (existsSync(path)) {
|
|
19
|
+
const content = readFileSync(path, 'utf8').trim();
|
|
20
|
+
if (!/^[0-9a-f]{64}$/i.test(content)) {
|
|
21
|
+
throw new Error(`Invalid API key secret in ${path}; expected 64 hex chars`);
|
|
22
|
+
}
|
|
23
|
+
cachedSecret = Buffer.from(content, 'hex');
|
|
24
|
+
return cachedSecret;
|
|
25
|
+
}
|
|
26
|
+
const secret = randomBytes(32);
|
|
27
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
28
|
+
writeFileSync(path, secret.toString('hex'), { mode: 0o600 });
|
|
29
|
+
try {
|
|
30
|
+
chmodSync(path, 0o600);
|
|
31
|
+
}
|
|
32
|
+
catch { /* Windows lacks chmod semantics */ }
|
|
33
|
+
cachedSecret = secret;
|
|
34
|
+
return cachedSecret;
|
|
35
|
+
}
|
|
2
36
|
export function generateApiKey() {
|
|
3
|
-
const random = randomBytes(16).toString('hex');
|
|
37
|
+
const random = randomBytes(16).toString('hex');
|
|
4
38
|
const key = `opal_${random}`;
|
|
5
39
|
const keyHash = hashApiKey(key);
|
|
6
40
|
const keyPrefix = key.slice(0, 8);
|
|
7
41
|
return { key, keyHash, keyPrefix };
|
|
8
42
|
}
|
|
9
43
|
export function hashApiKey(key) {
|
|
44
|
+
return createHmac('sha256', getServerSecret()).update(key).digest('hex');
|
|
45
|
+
}
|
|
46
|
+
function legacyHashApiKey(key) {
|
|
10
47
|
return createHash('sha256').update(key).digest('hex');
|
|
11
48
|
}
|
|
49
|
+
export function safeEqualHex(a, b) {
|
|
50
|
+
if (a.length !== b.length)
|
|
51
|
+
return false;
|
|
52
|
+
try {
|
|
53
|
+
return timingSafeEqual(Buffer.from(a, 'hex'), Buffer.from(b, 'hex'));
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export function lookupApiKey(db, presentedKey) {
|
|
60
|
+
const newHash = hashApiKey(presentedKey);
|
|
61
|
+
let row = db.get('SELECT id, user_id, expires_at FROM api_keys WHERE key_hash = ?', [newHash]);
|
|
62
|
+
if (row)
|
|
63
|
+
return row;
|
|
64
|
+
const legacyHash = legacyHashApiKey(presentedKey);
|
|
65
|
+
row = db.get('SELECT id, user_id, expires_at FROM api_keys WHERE key_hash = ?', [legacyHash]);
|
|
66
|
+
if (row) {
|
|
67
|
+
db.run('UPDATE api_keys SET key_hash = ? WHERE id = ?', [newHash, row.id]);
|
|
68
|
+
return row;
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
12
72
|
//# sourceMappingURL=api-keys.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-keys.js","sourceRoot":"","sources":["../../src/auth/api-keys.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"api-keys.js","sourceRoot":"","sources":["../../src/auth/api-keys.ts"],"names":[],"mappings":"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"}
|
package/dist/auth/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { hashPassword, verifyPassword } from './passwords.js';
|
|
2
|
-
export { generateApiKey, hashApiKey } from './api-keys.js';
|
|
2
|
+
export { generateApiKey, hashApiKey, lookupApiKey, safeEqualHex } from './api-keys.js';
|
|
3
3
|
export { createAuthMiddleware, registerAuthDecorator } from './middleware.js';
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/auth/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACvF,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/auth/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { hashPassword, verifyPassword } from './passwords.js';
|
|
2
|
-
export { generateApiKey, hashApiKey } from './api-keys.js';
|
|
2
|
+
export { generateApiKey, hashApiKey, lookupApiKey, safeEqualHex } from './api-keys.js';
|
|
3
3
|
export { createAuthMiddleware, registerAuthDecorator } from './middleware.js';
|
|
4
4
|
//# sourceMappingURL=index.js.map
|
package/dist/auth/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACvF,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/auth/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAG9C,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,cAAc;QACtB,IAAI,CAAC,EAAE,IAAI,CAAC;KACb;CACF;AAED,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,QAAQ,IAChB,SAAS,cAAc,EAAE,OAAO,YAAY,KAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/auth/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAG9C,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,cAAc;QACtB,IAAI,CAAC,EAAE,IAAI,CAAC;KACb;CACF;AAED,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,QAAQ,IAChB,SAAS,cAAc,EAAE,OAAO,YAAY,KAAG,OAAO,CAAC,IAAI,CAAC,CAyC5F;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI,CAEhE"}
|
package/dist/auth/middleware.js
CHANGED
|
@@ -1,30 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { lookupApiKey } from './api-keys.js';
|
|
2
2
|
export function createAuthMiddleware(db) {
|
|
3
3
|
return async function authHook(request, reply) {
|
|
4
|
-
// In local mode, skip auth entirely
|
|
5
|
-
const mode = process.env.OPALSERVE_MODE || 'local';
|
|
6
|
-
if (mode === 'local') {
|
|
7
|
-
return;
|
|
8
|
-
}
|
|
9
4
|
const authHeader = request.headers.authorization;
|
|
10
5
|
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
11
6
|
reply.code(401).send({ ok: false, error: 'Missing or invalid Authorization header' });
|
|
12
7
|
return;
|
|
13
8
|
}
|
|
14
9
|
const key = authHeader.slice(7);
|
|
15
|
-
const
|
|
16
|
-
const row = db.get('SELECT user_id, expires_at FROM api_keys WHERE key_hash = ?', [keyHash]);
|
|
10
|
+
const row = lookupApiKey(db, key);
|
|
17
11
|
if (!row) {
|
|
18
12
|
reply.code(401).send({ ok: false, error: 'Invalid API key' });
|
|
19
13
|
return;
|
|
20
14
|
}
|
|
21
|
-
// Check expiry
|
|
22
15
|
if (row.expires_at && new Date(row.expires_at) < new Date()) {
|
|
23
16
|
reply.code(401).send({ ok: false, error: 'API key has expired' });
|
|
24
17
|
return;
|
|
25
18
|
}
|
|
26
|
-
|
|
27
|
-
db.run('UPDATE api_keys SET last_used_at = datetime("now") WHERE key_hash = ?', [keyHash]);
|
|
19
|
+
db.run('UPDATE api_keys SET last_used_at = datetime("now") WHERE id = ?', [row.id]);
|
|
28
20
|
// Load user
|
|
29
21
|
const user = db.get('SELECT id, email, display_name, created_at, updated_at FROM users WHERE id = ?', [row.user_id]);
|
|
30
22
|
if (!user) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/auth/middleware.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/auth/middleware.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAQ7C,MAAM,UAAU,oBAAoB,CAAC,EAAY;IAC/C,OAAO,KAAK,UAAU,QAAQ,CAAC,OAAuB,EAAE,KAAmB;QACzE,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC;QACjD,IAAI,CAAC,UAAU,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YACrD,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,yCAAyC,EAAE,CAAC,CAAC;YACtF,OAAO;QACT,CAAC;QAED,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAChC,MAAM,GAAG,GAAG,YAAY,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;QAElC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,iBAAiB,EAAE,CAAC,CAAC;YAC9D,OAAO;QACT,CAAC;QAED,IAAI,GAAG,CAAC,UAAU,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,IAAI,IAAI,EAAE,EAAE,CAAC;YAC5D,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,qBAAqB,EAAE,CAAC,CAAC;YAClE,OAAO;QACT,CAAC;QAED,EAAE,CAAC,GAAG,CAAC,iEAAiE,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAEpF,YAAY;QACZ,MAAM,IAAI,GAAG,EAAE,CAAC,GAAG,CACjB,gFAAgF,EAChF,CAAC,GAAG,CAAC,OAAO,CAAC,CACd,CAAC;QAEF,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,CAAC;YAC7D,OAAO;QACT,CAAC;QAED,OAAO,CAAC,IAAI,GAAG;YACb,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,WAAW,EAAE,IAAI,CAAC,YAAY;YAC9B,SAAS,EAAE,IAAI,CAAC,UAAU;YAC1B,SAAS,EAAE,IAAI,CAAC,UAAU;SAC3B,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,GAAoB;IACxD,GAAG,CAAC,eAAe,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/doctor.ts"],"names":[],"mappings":"AAwBA,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CA2FnD"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { createConnection } from 'node:net';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { OpalServeRegistry } from '../../core/registry.js';
|
|
5
|
+
import { getApiKeySecretPath, getDefaultDbPath } from '../../config/defaults.js';
|
|
6
|
+
import { SUPPORTED_EDITORS, OPALSERVE_ENTRY_NAME } from '../editors/index.js';
|
|
7
|
+
import { showMiniBanner, showSuccess, showError, showInfo, showWarning } from '../ui/banner.js';
|
|
8
|
+
function isPortFree(host, port, timeoutMs = 800) {
|
|
9
|
+
return new Promise(resolve => {
|
|
10
|
+
const socket = createConnection({ host, port });
|
|
11
|
+
const done = (free) => {
|
|
12
|
+
socket.removeAllListeners();
|
|
13
|
+
socket.destroy();
|
|
14
|
+
resolve(free);
|
|
15
|
+
};
|
|
16
|
+
const timer = setTimeout(() => done(true), timeoutMs);
|
|
17
|
+
socket.once('connect', () => { clearTimeout(timer); done(false); });
|
|
18
|
+
socket.once('error', () => { clearTimeout(timer); done(true); });
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
export async function doctorCommand() {
|
|
22
|
+
showMiniBanner();
|
|
23
|
+
console.log(chalk.bold(' Running diagnostics...\n'));
|
|
24
|
+
const checks = [];
|
|
25
|
+
try {
|
|
26
|
+
const registry = await OpalServeRegistry.create({ logging: { level: 'silent' } });
|
|
27
|
+
checks.push({
|
|
28
|
+
name: 'config',
|
|
29
|
+
ok: true,
|
|
30
|
+
level: 'info',
|
|
31
|
+
detail: `mode=${registry.config.mode}, port=${registry.config.gateway.port}`,
|
|
32
|
+
});
|
|
33
|
+
const dbPath = registry.config.storage.path || getDefaultDbPath();
|
|
34
|
+
checks.push({
|
|
35
|
+
name: 'database',
|
|
36
|
+
ok: existsSync(dbPath),
|
|
37
|
+
level: existsSync(dbPath) ? 'info' : 'warn',
|
|
38
|
+
detail: dbPath,
|
|
39
|
+
});
|
|
40
|
+
const secretPath = getApiKeySecretPath();
|
|
41
|
+
checks.push({
|
|
42
|
+
name: 'api-key secret',
|
|
43
|
+
ok: existsSync(secretPath) || !!process.env.OPALSERVE_API_KEY_SECRET,
|
|
44
|
+
level: 'info',
|
|
45
|
+
detail: process.env.OPALSERVE_API_KEY_SECRET ? '(from env)' : secretPath,
|
|
46
|
+
});
|
|
47
|
+
const port = registry.config.gateway.port;
|
|
48
|
+
const host = registry.config.gateway.host;
|
|
49
|
+
const portFree = await isPortFree(host, port);
|
|
50
|
+
checks.push({
|
|
51
|
+
name: `port ${port}`,
|
|
52
|
+
ok: true,
|
|
53
|
+
level: portFree ? 'info' : 'warn',
|
|
54
|
+
detail: portFree ? 'available' : 'in use (server may already be running)',
|
|
55
|
+
});
|
|
56
|
+
const servers = registry.listServers();
|
|
57
|
+
checks.push({
|
|
58
|
+
name: 'registered servers',
|
|
59
|
+
ok: servers.length > 0,
|
|
60
|
+
level: servers.length > 0 ? 'info' : 'warn',
|
|
61
|
+
detail: servers.length === 0
|
|
62
|
+
? 'none registered yet — try `opalserve add` or `opalserve init`'
|
|
63
|
+
: `${servers.length} (${servers.filter(s => s.status === 'connected').length} connected)`,
|
|
64
|
+
});
|
|
65
|
+
for (const editor of SUPPORTED_EDITORS) {
|
|
66
|
+
const status = editor.status(OPALSERVE_ENTRY_NAME);
|
|
67
|
+
checks.push({
|
|
68
|
+
name: `editor: ${editor.displayName}`,
|
|
69
|
+
ok: status.linked,
|
|
70
|
+
level: status.installed ? (status.linked ? 'info' : 'warn') : 'info',
|
|
71
|
+
detail: !status.installed
|
|
72
|
+
? 'not installed on this machine'
|
|
73
|
+
: status.linked
|
|
74
|
+
? `linked → ${status.configPath}`
|
|
75
|
+
: `installed but not linked — run \`opalserve link ${editor.id}\``,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
await registry.stop();
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
checks.push({
|
|
82
|
+
name: 'registry',
|
|
83
|
+
ok: false,
|
|
84
|
+
level: 'error',
|
|
85
|
+
detail: err instanceof Error ? err.message : String(err),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
for (const c of checks) {
|
|
89
|
+
if (c.level === 'error')
|
|
90
|
+
showError(`${c.name.padEnd(28)} ${c.detail ? chalk.dim(c.detail) : ''}`);
|
|
91
|
+
else if (c.level === 'warn')
|
|
92
|
+
showWarning(`${c.name.padEnd(28)} ${c.detail ? chalk.dim(c.detail) : ''}`);
|
|
93
|
+
else if (c.ok)
|
|
94
|
+
showSuccess(`${c.name.padEnd(28)} ${c.detail ? chalk.dim(c.detail) : ''}`);
|
|
95
|
+
else
|
|
96
|
+
showInfo(`${c.name.padEnd(28)} ${c.detail ? chalk.dim(c.detail) : ''}`);
|
|
97
|
+
}
|
|
98
|
+
const errors = checks.filter(c => c.level === 'error').length;
|
|
99
|
+
console.log('');
|
|
100
|
+
if (errors > 0) {
|
|
101
|
+
showError(`${errors} error(s) found.`);
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
showSuccess('All checks passed.');
|
|
105
|
+
console.log('');
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../../src/cli/commands/doctor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
interface LinkOptions {
|
|
2
|
+
all?: boolean;
|
|
3
|
+
status?: boolean;
|
|
4
|
+
}
|
|
5
|
+
interface UnlinkOptions {
|
|
6
|
+
all?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function linkCommand(editorId: string | undefined, options: LinkOptions): Promise<void>;
|
|
9
|
+
export declare function unlinkCommand(editorId: string | undefined, options: UnlinkOptions): Promise<void>;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=link.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/link.ts"],"names":[],"mappings":"AAUA,UAAU,WAAW;IACnB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,UAAU,aAAa;IACrB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AA0CD,wBAAsB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CA0DnG;AAED,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAsCvG"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { SUPPORTED_EDITORS, OPALSERVE_ENTRY_NAME, buildOpalserveEntry, getEditor, } from '../editors/index.js';
|
|
3
|
+
import { showMiniBanner, showSuccess, showError, showInfo, showWarning, showBox } from '../ui/banner.js';
|
|
4
|
+
function formatStatusLine(adapter) {
|
|
5
|
+
const status = adapter.status(OPALSERVE_ENTRY_NAME);
|
|
6
|
+
const presence = status.installed
|
|
7
|
+
? chalk.green('detected')
|
|
8
|
+
: chalk.dim('not detected');
|
|
9
|
+
const linked = status.linked
|
|
10
|
+
? chalk.cyan('linked')
|
|
11
|
+
: chalk.gray('not linked');
|
|
12
|
+
return ` ${chalk.bold(adapter.displayName.padEnd(20))} ${presence} · ${linked}\n ${chalk.dim(status.configPath)}`;
|
|
13
|
+
}
|
|
14
|
+
function showStatusTable() {
|
|
15
|
+
console.log('');
|
|
16
|
+
console.log(chalk.bold(' AI editor status:'));
|
|
17
|
+
console.log('');
|
|
18
|
+
for (const editor of SUPPORTED_EDITORS) {
|
|
19
|
+
console.log(formatStatusLine(editor));
|
|
20
|
+
}
|
|
21
|
+
console.log('');
|
|
22
|
+
}
|
|
23
|
+
function linkOne(adapter) {
|
|
24
|
+
try {
|
|
25
|
+
const entry = buildOpalserveEntry();
|
|
26
|
+
const prior = adapter.link(OPALSERVE_ENTRY_NAME, entry);
|
|
27
|
+
return { ok: true, replaced: prior !== null };
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
return { ok: false, replaced: false, error: err instanceof Error ? err.message : String(err) };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function unlinkOne(adapter) {
|
|
34
|
+
try {
|
|
35
|
+
const removed = adapter.unlink(OPALSERVE_ENTRY_NAME);
|
|
36
|
+
return { ok: true, removed };
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
return { ok: false, removed: false, error: err instanceof Error ? err.message : String(err) };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export async function linkCommand(editorId, options) {
|
|
43
|
+
showMiniBanner();
|
|
44
|
+
if (options.status) {
|
|
45
|
+
showStatusTable();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
let targets = [];
|
|
49
|
+
if (editorId) {
|
|
50
|
+
const adapter = getEditor(editorId);
|
|
51
|
+
if (!adapter) {
|
|
52
|
+
showError(`Unknown editor "${editorId}". Supported: ${SUPPORTED_EDITORS.map(e => e.id).join(', ')}`);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
targets = [adapter];
|
|
56
|
+
}
|
|
57
|
+
else if (options.all) {
|
|
58
|
+
targets = SUPPORTED_EDITORS.filter(e => e.detect());
|
|
59
|
+
if (targets.length === 0) {
|
|
60
|
+
showWarning('No supported AI editors detected on this machine.');
|
|
61
|
+
showStatusTable();
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
showStatusTable();
|
|
67
|
+
showInfo('Run `opalserve link <editor>` to link a specific editor, or `opalserve link --all` for every detected one.');
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
console.log('');
|
|
71
|
+
for (const adapter of targets) {
|
|
72
|
+
if (!adapter.detect()) {
|
|
73
|
+
showWarning(`${adapter.displayName} not detected — linking anyway (config will be created at ${adapter.getConfigPath()}).`);
|
|
74
|
+
}
|
|
75
|
+
const result = linkOne(adapter);
|
|
76
|
+
if (result.ok) {
|
|
77
|
+
const verb = result.replaced ? 'updated' : 'wired up';
|
|
78
|
+
showSuccess(`${adapter.displayName} ${verb}: ${chalk.dim(adapter.getConfigPath())}`);
|
|
79
|
+
if (result.replaced) {
|
|
80
|
+
showInfo(` Prior \`${OPALSERVE_ENTRY_NAME}\` entry overwritten.`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
showError(`${adapter.displayName}: ${result.error}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
console.log('');
|
|
88
|
+
showBox([
|
|
89
|
+
`OpalServe is now reachable to ${targets.length === 1 ? 'this editor' : 'these editors'} as the MCP server "${chalk.cyan(OPALSERVE_ENTRY_NAME)}".`,
|
|
90
|
+
'',
|
|
91
|
+
chalk.bold('Next:'),
|
|
92
|
+
` 1. ${chalk.dim('Restart the editor for it to pick up the new MCP server.')}`,
|
|
93
|
+
` 2. ${chalk.dim('Try asking your AI: "what tools are available via opalserve?"')}`,
|
|
94
|
+
].join('\n'), 'Linked');
|
|
95
|
+
}
|
|
96
|
+
export async function unlinkCommand(editorId, options) {
|
|
97
|
+
showMiniBanner();
|
|
98
|
+
let targets = [];
|
|
99
|
+
if (editorId) {
|
|
100
|
+
const adapter = getEditor(editorId);
|
|
101
|
+
if (!adapter) {
|
|
102
|
+
showError(`Unknown editor "${editorId}". Supported: ${SUPPORTED_EDITORS.map(e => e.id).join(', ')}`);
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
targets = [adapter];
|
|
106
|
+
}
|
|
107
|
+
else if (options.all) {
|
|
108
|
+
targets = SUPPORTED_EDITORS.filter(e => e.isLinked(OPALSERVE_ENTRY_NAME));
|
|
109
|
+
if (targets.length === 0) {
|
|
110
|
+
showInfo('No editors currently linked.');
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
showStatusTable();
|
|
116
|
+
showInfo('Run `opalserve unlink <editor>` to unlink, or `opalserve unlink --all` to unlink everywhere.');
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
console.log('');
|
|
120
|
+
for (const adapter of targets) {
|
|
121
|
+
const result = unlinkOne(adapter);
|
|
122
|
+
if (!result.ok) {
|
|
123
|
+
showError(`${adapter.displayName}: ${result.error}`);
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (result.removed) {
|
|
127
|
+
showSuccess(`${adapter.displayName} unlinked`);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
showInfo(`${adapter.displayName} was not linked — nothing to remove.`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
console.log('');
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=link.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link.js","sourceRoot":"","sources":["../../../src/cli/commands/link.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run OpalServe as a stdio MCP server. This is what AI editors spawn after
|
|
3
|
+
* `opalserve link`. Stdout is reserved for the JSON-RPC protocol stream;
|
|
4
|
+
* everything else must go to stderr or be silenced.
|
|
5
|
+
*/
|
|
6
|
+
export declare function mcpCommand(): Promise<void>;
|
|
7
|
+
//# sourceMappingURL=mcp.d.ts.map
|