agent-relay 2.0.12 → 2.0.14
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/bin/relay-pty-darwin-arm64 +0 -0
- package/bin/relay-pty-darwin-x64 +0 -0
- package/bin/relay-pty-linux-x64 +0 -0
- package/deploy/workspace/codex.config.toml +5 -0
- package/deploy/workspace/entrypoint.sh +10 -2
- package/dist/dashboard/out/404.html +1 -1
- package/dist/dashboard/out/app/onboarding.html +1 -1
- package/dist/dashboard/out/app/onboarding.txt +1 -1
- package/dist/dashboard/out/app.html +1 -1
- package/dist/dashboard/out/app.txt +1 -1
- package/dist/dashboard/out/cloud/link.html +1 -1
- package/dist/dashboard/out/cloud/link.txt +1 -1
- package/dist/dashboard/out/connect-repos.html +1 -1
- package/dist/dashboard/out/connect-repos.txt +1 -1
- package/dist/dashboard/out/history.html +1 -1
- package/dist/dashboard/out/history.txt +1 -1
- package/dist/dashboard/out/index.html +1 -1
- package/dist/dashboard/out/index.txt +1 -1
- package/dist/dashboard/out/login.html +1 -1
- package/dist/dashboard/out/login.txt +1 -1
- package/dist/dashboard/out/metrics.html +1 -1
- package/dist/dashboard/out/metrics.txt +1 -1
- package/dist/dashboard/out/pricing.html +1 -1
- package/dist/dashboard/out/pricing.txt +1 -1
- package/dist/dashboard/out/providers/setup/claude.html +1 -1
- package/dist/dashboard/out/providers/setup/claude.txt +1 -1
- package/dist/dashboard/out/providers/setup/codex.html +1 -1
- package/dist/dashboard/out/providers/setup/codex.txt +1 -1
- package/dist/dashboard/out/providers/setup/cursor.html +1 -1
- package/dist/dashboard/out/providers/setup/cursor.txt +1 -1
- package/dist/dashboard/out/providers.html +1 -1
- package/dist/dashboard/out/providers.txt +1 -1
- package/dist/dashboard/out/signup.html +1 -1
- package/dist/dashboard/out/signup.txt +1 -1
- package/dist/src/cli/index.js +131 -21
- package/package.json +20 -19
- package/packages/api-types/package.json +1 -1
- package/packages/bridge/dist/index.d.ts +1 -1
- package/packages/bridge/dist/index.js +1 -1
- package/packages/bridge/dist/spawner.d.ts +18 -0
- package/packages/bridge/dist/spawner.js +144 -39
- package/packages/bridge/package.json +8 -7
- package/packages/cloud/package.json +6 -6
- package/packages/config/package.json +2 -2
- package/packages/continuity/package.json +1 -1
- package/packages/daemon/dist/connection.js +5 -1
- package/packages/daemon/dist/relay-ledger.d.ts +3 -1
- package/packages/daemon/dist/relay-ledger.js +8 -2
- package/packages/daemon/dist/router.js +13 -0
- package/packages/daemon/dist/server.d.ts +7 -0
- package/packages/daemon/dist/server.js +338 -4
- package/packages/daemon/package.json +12 -12
- package/packages/dashboard/dist/server.js +29 -5
- package/packages/dashboard/package.json +13 -12
- package/packages/dashboard/ui-dist/404.html +1 -1
- package/packages/dashboard/ui-dist/app/onboarding.html +1 -1
- package/packages/dashboard/ui-dist/app/onboarding.txt +1 -1
- package/packages/dashboard/ui-dist/app.html +1 -1
- package/packages/dashboard/ui-dist/app.txt +1 -1
- package/packages/dashboard/ui-dist/cloud/link.html +1 -1
- package/packages/dashboard/ui-dist/cloud/link.txt +1 -1
- package/packages/dashboard/ui-dist/connect-repos.html +1 -1
- package/packages/dashboard/ui-dist/connect-repos.txt +1 -1
- package/packages/dashboard/ui-dist/history.html +1 -1
- package/packages/dashboard/ui-dist/history.txt +1 -1
- package/packages/dashboard/ui-dist/index.html +1 -1
- package/packages/dashboard/ui-dist/index.txt +1 -1
- package/packages/dashboard/ui-dist/login.html +1 -1
- package/packages/dashboard/ui-dist/login.txt +1 -1
- package/packages/dashboard/ui-dist/metrics.html +1 -1
- package/packages/dashboard/ui-dist/metrics.txt +1 -1
- package/packages/dashboard/ui-dist/pricing.html +1 -1
- package/packages/dashboard/ui-dist/pricing.txt +1 -1
- package/packages/dashboard/ui-dist/providers/setup/claude.html +1 -1
- package/packages/dashboard/ui-dist/providers/setup/claude.txt +1 -1
- package/packages/dashboard/ui-dist/providers/setup/codex.html +1 -1
- package/packages/dashboard/ui-dist/providers/setup/codex.txt +1 -1
- package/packages/dashboard/ui-dist/providers/setup/cursor.html +1 -1
- package/packages/dashboard/ui-dist/providers/setup/cursor.txt +1 -1
- package/packages/dashboard/ui-dist/providers.html +1 -1
- package/packages/dashboard/ui-dist/providers.txt +1 -1
- package/packages/dashboard/ui-dist/signup.html +1 -1
- package/packages/dashboard/ui-dist/signup.txt +1 -1
- package/packages/dashboard-server/dist/server.js +29 -5
- package/packages/dashboard-server/package.json +12 -12
- package/packages/hooks/package.json +4 -4
- package/packages/mcp/README.md +24 -3
- package/packages/mcp/dist/bin.js +13 -5
- package/packages/mcp/dist/client.d.ts +54 -1
- package/packages/mcp/dist/client.js +132 -18
- package/packages/mcp/dist/cloud.d.ts +12 -0
- package/packages/mcp/dist/cloud.js +125 -1
- package/packages/mcp/dist/file-transport.d.ts +97 -0
- package/packages/mcp/dist/file-transport.js +197 -0
- package/packages/mcp/dist/hybrid-client.d.ts +28 -0
- package/packages/mcp/dist/hybrid-client.js +159 -0
- package/packages/mcp/dist/index.d.ts +4 -2
- package/packages/mcp/dist/index.js +6 -2
- package/packages/mcp/dist/install.d.ts +23 -1
- package/packages/mcp/dist/install.js +229 -31
- package/packages/mcp/dist/server.js +7 -1
- package/packages/mcp/dist/simple.d.ts +1 -1
- package/packages/mcp/dist/tools/index.d.ts +1 -0
- package/packages/mcp/dist/tools/index.js +1 -0
- package/packages/mcp/dist/tools/relay-continuity.d.ts +35 -0
- package/packages/mcp/dist/tools/relay-continuity.js +101 -0
- package/packages/mcp/dist/tools/relay-health.d.ts +1 -4
- package/packages/mcp/dist/tools/relay-health.js +7 -15
- package/packages/mcp/dist/tools/relay-logs.js +4 -2
- package/packages/mcp/dist/tools/relay-metrics.d.ts +1 -4
- package/packages/mcp/dist/tools/relay-metrics.js +4 -15
- package/packages/mcp/dist/tools/relay-send.d.ts +2 -2
- package/packages/mcp/package.json +3 -2
- package/packages/memory/package.json +2 -2
- package/packages/policy/package.json +2 -2
- package/packages/protocol/dist/relay-pty-schemas.d.ts +14 -0
- package/packages/protocol/dist/types.d.ts +152 -2
- package/packages/protocol/package.json +1 -1
- package/packages/resiliency/package.json +1 -1
- package/packages/sdk/dist/client.js +7 -0
- package/packages/sdk/package.json +2 -2
- package/packages/spawner/package.json +1 -1
- package/packages/state/package.json +1 -1
- package/packages/storage/package.json +2 -2
- package/packages/telemetry/package.json +1 -1
- package/packages/trajectory/package.json +2 -2
- package/packages/user-directory/package.json +2 -2
- package/packages/utils/dist/logger.js +3 -1
- package/packages/utils/package.json +1 -1
- package/packages/wrapper/dist/relay-pty-orchestrator.d.ts +28 -1
- package/packages/wrapper/dist/relay-pty-orchestrator.js +358 -43
- package/packages/wrapper/package.json +6 -6
- package/scripts/demos/README.md +79 -0
- package/scripts/demos/server-capacity.sh +69 -0
- package/scripts/demos/sprint-planning.sh +73 -0
- /package/dist/dashboard/out/_next/static/{h1U3qU5XIfQSy46M_SDsz → RgEj_9Y-mWbLaxggzni-X}/_buildManifest.js +0 -0
- /package/dist/dashboard/out/_next/static/{h1U3qU5XIfQSy46M_SDsz → RgEj_9Y-mWbLaxggzni-X}/_ssgManifest.js +0 -0
- /package/packages/dashboard/ui-dist/_next/static/{4WryIM4xHT22BbJ46YITr → RgEj_9Y-mWbLaxggzni-X}/_buildManifest.js +0 -0
- /package/packages/dashboard/ui-dist/_next/static/{4WryIM4xHT22BbJ46YITr → RgEj_9Y-mWbLaxggzni-X}/_ssgManifest.js +0 -0
- /package/packages/dashboard/ui-dist/_next/static/{dS0EgrS-iG-_pkUVhBypz → UkLmDJOkaPWU2PaNQnkx5}/_buildManifest.js +0 -0
- /package/packages/dashboard/ui-dist/_next/static/{dS0EgrS-iG-_pkUVhBypz → UkLmDJOkaPWU2PaNQnkx5}/_ssgManifest.js +0 -0
- /package/packages/dashboard/ui-dist/_next/static/{h1U3qU5XIfQSy46M_SDsz → bv9xidgU2pXi7xxPoCAK-}/_buildManifest.js +0 -0
- /package/packages/dashboard/ui-dist/_next/static/{h1U3qU5XIfQSy46M_SDsz → bv9xidgU2pXi7xxPoCAK-}/_ssgManifest.js +0 -0
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
15
15
|
import { dirname, join } from 'node:path';
|
|
16
16
|
import { homedir, platform } from 'node:os';
|
|
17
|
+
import { execSync } from 'node:child_process';
|
|
18
|
+
import * as TOML from 'smol-toml';
|
|
17
19
|
/**
|
|
18
20
|
* Get platform-specific config paths
|
|
19
21
|
*/
|
|
@@ -39,7 +41,7 @@ function getConfigPaths() {
|
|
|
39
41
|
},
|
|
40
42
|
'claude-code': {
|
|
41
43
|
name: 'Claude Code',
|
|
42
|
-
configPath: join(home, '.claude.json'),
|
|
44
|
+
configPath: join(home, '.claude', 'settings.json'),
|
|
43
45
|
configKey: 'mcpServers',
|
|
44
46
|
format: 'json',
|
|
45
47
|
supportsLocal: true,
|
|
@@ -92,6 +94,13 @@ function getConfigPaths() {
|
|
|
92
94
|
format: 'json',
|
|
93
95
|
supportsLocal: true,
|
|
94
96
|
},
|
|
97
|
+
codex: {
|
|
98
|
+
name: 'Codex',
|
|
99
|
+
configPath: join(home, '.codex', 'config.toml'),
|
|
100
|
+
configKey: 'mcp_servers', // TOML uses [mcp_servers.agent-relay] tables
|
|
101
|
+
format: 'toml',
|
|
102
|
+
supportsLocal: true,
|
|
103
|
+
},
|
|
95
104
|
};
|
|
96
105
|
}
|
|
97
106
|
/**
|
|
@@ -103,6 +112,86 @@ export function getDefaultServerConfig() {
|
|
|
103
112
|
args: ['@agent-relay/mcp', 'serve'],
|
|
104
113
|
};
|
|
105
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* Check if node is installed via nvm (Node Version Manager).
|
|
117
|
+
* GUI apps (Claude, Cursor, VS Code) can't use nvm's shell function,
|
|
118
|
+
* so we need to use absolute paths for nvm installations.
|
|
119
|
+
*/
|
|
120
|
+
function isUsingNvm() {
|
|
121
|
+
try {
|
|
122
|
+
const nodePath = execSync('which node', { encoding: 'utf-8' }).trim();
|
|
123
|
+
return nodePath.includes('.nvm');
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Get absolute path to node binary
|
|
131
|
+
*/
|
|
132
|
+
function getNodePath() {
|
|
133
|
+
try {
|
|
134
|
+
return execSync('which node', { encoding: 'utf-8' }).trim();
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Get path to globally installed @agent-relay/mcp bin.js
|
|
142
|
+
* Returns null if not installed globally.
|
|
143
|
+
*/
|
|
144
|
+
function getGlobalMcpBinPath() {
|
|
145
|
+
try {
|
|
146
|
+
// Get npm global prefix
|
|
147
|
+
const npmPrefix = execSync('npm prefix -g', { encoding: 'utf-8' }).trim();
|
|
148
|
+
const binPath = join(npmPrefix, 'lib', 'node_modules', '@agent-relay', 'mcp', 'dist', 'bin.js');
|
|
149
|
+
if (existsSync(binPath)) {
|
|
150
|
+
return binPath;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
// npm not available or failed
|
|
155
|
+
}
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Build MCP server configuration with proper paths.
|
|
160
|
+
*
|
|
161
|
+
* For nvm users: Uses absolute paths (recommended by MCP community)
|
|
162
|
+
* For others: Uses npx (works when node is in standard PATH)
|
|
163
|
+
*
|
|
164
|
+
* See: https://github.com/modelcontextprotocol/servers/issues/64
|
|
165
|
+
*/
|
|
166
|
+
function buildServerConfig() {
|
|
167
|
+
// If using nvm, we need absolute paths because GUI apps can't access nvm's shell function
|
|
168
|
+
if (isUsingNvm()) {
|
|
169
|
+
const nodePath = getNodePath();
|
|
170
|
+
const mcpBinPath = getGlobalMcpBinPath();
|
|
171
|
+
if (nodePath && mcpBinPath) {
|
|
172
|
+
// Best option: globally installed package with absolute paths
|
|
173
|
+
return {
|
|
174
|
+
command: nodePath,
|
|
175
|
+
args: [mcpBinPath, 'serve'],
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
// Package not installed globally - still try with absolute node path + npx
|
|
179
|
+
if (nodePath) {
|
|
180
|
+
const npxPath = join(dirname(nodePath), 'npx');
|
|
181
|
+
if (existsSync(npxPath)) {
|
|
182
|
+
return {
|
|
183
|
+
command: npxPath,
|
|
184
|
+
args: ['@agent-relay/mcp', 'serve'],
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// Standard case: npx should work
|
|
190
|
+
return {
|
|
191
|
+
command: 'npx',
|
|
192
|
+
args: ['@agent-relay/mcp', 'serve'],
|
|
193
|
+
};
|
|
194
|
+
}
|
|
106
195
|
/**
|
|
107
196
|
* Detect which editors are installed by checking for their config directories
|
|
108
197
|
*/
|
|
@@ -145,37 +234,45 @@ function stripJsonComments(content) {
|
|
|
145
234
|
return result;
|
|
146
235
|
}
|
|
147
236
|
/**
|
|
148
|
-
* Read and parse config file, handling
|
|
237
|
+
* Read and parse config file, handling JSON, JSONC, and TOML
|
|
149
238
|
*/
|
|
150
239
|
function readConfigFile(configPath, format) {
|
|
151
240
|
if (!existsSync(configPath)) {
|
|
152
241
|
return {};
|
|
153
242
|
}
|
|
154
243
|
const content = readFileSync(configPath, 'utf-8');
|
|
155
|
-
const jsonContent = format === 'jsonc' ? stripJsonComments(content) : content;
|
|
156
244
|
try {
|
|
157
245
|
// Handle empty or whitespace-only files
|
|
158
|
-
const trimmed =
|
|
246
|
+
const trimmed = content.trim();
|
|
159
247
|
if (!trimmed) {
|
|
160
248
|
return {};
|
|
161
249
|
}
|
|
162
|
-
|
|
250
|
+
if (format === 'toml') {
|
|
251
|
+
return TOML.parse(trimmed);
|
|
252
|
+
}
|
|
253
|
+
const jsonContent = format === 'jsonc' ? stripJsonComments(content) : content;
|
|
254
|
+
return JSON.parse(jsonContent.trim());
|
|
163
255
|
}
|
|
164
256
|
catch {
|
|
165
|
-
// Invalid
|
|
257
|
+
// Invalid config, start fresh
|
|
166
258
|
return {};
|
|
167
259
|
}
|
|
168
260
|
}
|
|
169
261
|
/**
|
|
170
262
|
* Write config file with proper formatting
|
|
171
263
|
*/
|
|
172
|
-
function writeConfigFile(configPath, config) {
|
|
264
|
+
function writeConfigFile(configPath, config, format = 'json') {
|
|
173
265
|
const configDir = dirname(configPath);
|
|
174
266
|
// Ensure directory exists
|
|
175
267
|
if (!existsSync(configDir)) {
|
|
176
268
|
mkdirSync(configDir, { recursive: true });
|
|
177
269
|
}
|
|
178
|
-
|
|
270
|
+
if (format === 'toml') {
|
|
271
|
+
writeFileSync(configPath, TOML.stringify(config) + '\n');
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
275
|
+
}
|
|
179
276
|
}
|
|
180
277
|
/**
|
|
181
278
|
* Get the config path for local (project-specific) installation
|
|
@@ -200,6 +297,8 @@ function getLocalConfigPath(editor, projectDir) {
|
|
|
200
297
|
return join(projectDir, 'opencode.json');
|
|
201
298
|
case 'Droid':
|
|
202
299
|
return join(projectDir, '.factory', 'mcp.json');
|
|
300
|
+
case 'Codex':
|
|
301
|
+
return join(projectDir, 'codex.toml');
|
|
203
302
|
default:
|
|
204
303
|
return null;
|
|
205
304
|
}
|
|
@@ -209,7 +308,8 @@ function getLocalConfigPath(editor, projectDir) {
|
|
|
209
308
|
*/
|
|
210
309
|
export function installForEditor(editorKey, options = {}) {
|
|
211
310
|
const editor = getEditorConfig(editorKey);
|
|
212
|
-
|
|
311
|
+
// Allow custom configPath even without known editor
|
|
312
|
+
if (!editor && !options.configPath) {
|
|
213
313
|
return {
|
|
214
314
|
editor: editorKey,
|
|
215
315
|
configPath: '',
|
|
@@ -218,25 +318,63 @@ export function installForEditor(editorKey, options = {}) {
|
|
|
218
318
|
created: false,
|
|
219
319
|
};
|
|
220
320
|
}
|
|
221
|
-
// Determine config path
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
321
|
+
// Determine config path, format, and key
|
|
322
|
+
// Custom configPath overrides everything (for programmatic installation)
|
|
323
|
+
let configPath;
|
|
324
|
+
let configFormat;
|
|
325
|
+
let configKey;
|
|
326
|
+
if (options.configPath) {
|
|
327
|
+
// Use custom path - for programmatic/auto installation
|
|
328
|
+
configPath = options.configPath;
|
|
329
|
+
configFormat = options.configFormat || 'json';
|
|
330
|
+
configKey = options.configKey || 'mcpServers';
|
|
331
|
+
}
|
|
332
|
+
else if (editor) {
|
|
333
|
+
// Use editor's config
|
|
334
|
+
configPath = editor.configPath;
|
|
335
|
+
configFormat = editor.format;
|
|
336
|
+
configKey = editor.configKey;
|
|
337
|
+
// Check for local path override
|
|
338
|
+
if (!options.global && options.projectDir && editor.supportsLocal) {
|
|
339
|
+
const localPath = getLocalConfigPath(editor, options.projectDir);
|
|
340
|
+
if (localPath) {
|
|
341
|
+
configPath = localPath;
|
|
342
|
+
}
|
|
227
343
|
}
|
|
228
344
|
}
|
|
229
|
-
|
|
345
|
+
else {
|
|
346
|
+
// Should not reach here due to early return above
|
|
347
|
+
return {
|
|
348
|
+
editor: editorKey,
|
|
349
|
+
configPath: '',
|
|
350
|
+
success: false,
|
|
351
|
+
error: `Unknown editor: ${editorKey}`,
|
|
352
|
+
created: false,
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
// Build server config - handles nvm users with absolute paths
|
|
356
|
+
const defaultConfig = buildServerConfig();
|
|
230
357
|
const serverConfig = {
|
|
231
|
-
command: options.command ||
|
|
232
|
-
args: options.args ||
|
|
358
|
+
command: options.command || defaultConfig.command,
|
|
359
|
+
args: options.args || defaultConfig.args,
|
|
233
360
|
};
|
|
234
|
-
//
|
|
235
|
-
|
|
236
|
-
|
|
361
|
+
// Set environment variables if provided (e.g., RELAY_SOCKET for project-local installs)
|
|
362
|
+
if (options.env) {
|
|
363
|
+
serverConfig.env = { ...options.env };
|
|
364
|
+
}
|
|
365
|
+
// For project-local installs with projectDir, also set RELAY_SOCKET if not already set
|
|
366
|
+
const isProjectLocal = !options.global && options.projectDir;
|
|
367
|
+
if (isProjectLocal && !serverConfig.env?.RELAY_SOCKET) {
|
|
368
|
+
const socketPath = join(options.projectDir, '.agent-relay', 'relay.sock');
|
|
369
|
+
serverConfig.env = {
|
|
370
|
+
...serverConfig.env,
|
|
371
|
+
RELAY_SOCKET: socketPath,
|
|
372
|
+
};
|
|
373
|
+
}
|
|
237
374
|
if (options.dryRun) {
|
|
375
|
+
const editorName = editor?.name || editorKey || 'custom';
|
|
238
376
|
return {
|
|
239
|
-
editor:
|
|
377
|
+
editor: editorName,
|
|
240
378
|
configPath,
|
|
241
379
|
success: true,
|
|
242
380
|
created: !existsSync(configPath),
|
|
@@ -244,28 +382,71 @@ export function installForEditor(editorKey, options = {}) {
|
|
|
244
382
|
}
|
|
245
383
|
try {
|
|
246
384
|
// Read existing config
|
|
247
|
-
const config = readConfigFile(configPath,
|
|
385
|
+
const config = readConfigFile(configPath, configFormat);
|
|
248
386
|
const created = !existsSync(configPath);
|
|
249
387
|
// Initialize mcpServers if not present
|
|
250
|
-
const configKeyValue = config[
|
|
388
|
+
const configKeyValue = config[configKey];
|
|
251
389
|
if (!configKeyValue || typeof configKeyValue !== 'object') {
|
|
252
|
-
config[
|
|
390
|
+
config[configKey] = {};
|
|
253
391
|
}
|
|
254
392
|
// Add agent-relay server config
|
|
255
|
-
const mcpServers = config[
|
|
256
|
-
|
|
393
|
+
const mcpServers = config[configKey];
|
|
394
|
+
// OpenCode uses a different format: { type: "local", command: [...], environment: {...} }
|
|
395
|
+
if (editor?.name === 'OpenCode') {
|
|
396
|
+
const openCodeConfig = {
|
|
397
|
+
type: 'local',
|
|
398
|
+
command: [serverConfig.command, ...serverConfig.args],
|
|
399
|
+
};
|
|
400
|
+
if (serverConfig.env) {
|
|
401
|
+
openCodeConfig.environment = serverConfig.env;
|
|
402
|
+
}
|
|
403
|
+
mcpServers['agent-relay'] = openCodeConfig;
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
mcpServers['agent-relay'] = serverConfig;
|
|
407
|
+
}
|
|
408
|
+
// For Claude Code, also add permissions to auto-approve agent-relay tools
|
|
409
|
+
// Permissions go in settings.json, not .mcp.json
|
|
410
|
+
if (editor?.name === 'Claude Code') {
|
|
411
|
+
// Determine settings path based on where MCP config is being written
|
|
412
|
+
const projectDir = configPath.endsWith('.mcp.json')
|
|
413
|
+
? dirname(configPath) // project-local: same dir as .mcp.json
|
|
414
|
+
: dirname(configPath); // global: ~/.claude/
|
|
415
|
+
const settingsPath = configPath.endsWith('.mcp.json')
|
|
416
|
+
? join(projectDir, '.claude', 'settings.json') // project-local
|
|
417
|
+
: configPath; // global uses same file (settings.json)
|
|
418
|
+
// Read existing settings
|
|
419
|
+
const settings = configPath.endsWith('.mcp.json')
|
|
420
|
+
? readConfigFile(settingsPath, 'json') // separate file for project-local
|
|
421
|
+
: config; // same config object for global
|
|
422
|
+
const permissions = settings.permissions || {};
|
|
423
|
+
const allowList = permissions.allow || [];
|
|
424
|
+
// Add agent-relay permission if not already present
|
|
425
|
+
if (!allowList.includes('mcp__agent-relay__*')) {
|
|
426
|
+
allowList.push('mcp__agent-relay__*');
|
|
427
|
+
}
|
|
428
|
+
permissions.allow = allowList;
|
|
429
|
+
settings.permissions = permissions;
|
|
430
|
+
// Write settings (separate file for project-local)
|
|
431
|
+
if (configPath.endsWith('.mcp.json')) {
|
|
432
|
+
writeConfigFile(settingsPath, settings, 'json');
|
|
433
|
+
}
|
|
434
|
+
// For global, permissions are added to same config object
|
|
435
|
+
}
|
|
257
436
|
// Write updated config
|
|
258
|
-
writeConfigFile(configPath, config);
|
|
437
|
+
writeConfigFile(configPath, config, configFormat);
|
|
438
|
+
const editorName = editor?.name || editorKey || 'custom';
|
|
259
439
|
return {
|
|
260
|
-
editor:
|
|
440
|
+
editor: editorName,
|
|
261
441
|
configPath,
|
|
262
442
|
success: true,
|
|
263
443
|
created,
|
|
264
444
|
};
|
|
265
445
|
}
|
|
266
446
|
catch (err) {
|
|
447
|
+
const editorName = editor?.name || editorKey || 'custom';
|
|
267
448
|
return {
|
|
268
|
-
editor:
|
|
449
|
+
editor: editorName,
|
|
269
450
|
configPath,
|
|
270
451
|
success: false,
|
|
271
452
|
error: err instanceof Error ? err.message : String(err),
|
|
@@ -308,7 +489,7 @@ export function uninstallFromEditor(editorKey, options = {}) {
|
|
|
308
489
|
const mcpServers = config[editor.configKey];
|
|
309
490
|
if (mcpServers && 'agent-relay' in mcpServers) {
|
|
310
491
|
delete mcpServers['agent-relay'];
|
|
311
|
-
writeConfigFile(configPath, config);
|
|
492
|
+
writeConfigFile(configPath, config, editor.format);
|
|
312
493
|
}
|
|
313
494
|
return {
|
|
314
495
|
editor: editor.name,
|
|
@@ -379,6 +560,23 @@ export function install(options = {}) {
|
|
|
379
560
|
}
|
|
380
561
|
return results;
|
|
381
562
|
}
|
|
563
|
+
/**
|
|
564
|
+
* Install MCP server configuration to a specific config file path.
|
|
565
|
+
* This is a simpler API for programmatic/automated installation.
|
|
566
|
+
*
|
|
567
|
+
* @param configPath - Absolute path to config file
|
|
568
|
+
* @param options - Optional settings for format, key, etc.
|
|
569
|
+
*/
|
|
570
|
+
export function installMcpConfig(configPath, options = {}) {
|
|
571
|
+
return installForEditor('custom', {
|
|
572
|
+
configPath,
|
|
573
|
+
configFormat: options.format || 'json',
|
|
574
|
+
configKey: options.configKey || 'mcpServers',
|
|
575
|
+
command: options.command,
|
|
576
|
+
args: options.args,
|
|
577
|
+
env: options.env,
|
|
578
|
+
});
|
|
579
|
+
}
|
|
382
580
|
/**
|
|
383
581
|
* Uninstall MCP server from all detected editors (or specified editors)
|
|
384
582
|
*/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
2
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
3
|
import { CallToolRequestSchema, ListToolsRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
4
|
-
import { relaySendTool, relaySendSchema, handleRelaySend, relayInboxTool, relayInboxSchema, handleRelayInbox, relayWhoTool, relayWhoSchema, handleRelayWho, relaySpawnTool, relaySpawnSchema, handleRelaySpawn, relayReleaseTool, relayReleaseSchema, handleRelayRelease, relayStatusTool, relayStatusSchema, handleRelayStatus, relayLogsTool, relayLogsSchema, handleRelayLogs, relayMetricsTool, relayMetricsSchema, handleRelayMetrics, relayHealthTool, relayHealthSchema, handleRelayHealth, } from './tools/index.js';
|
|
4
|
+
import { relaySendTool, relaySendSchema, handleRelaySend, relayInboxTool, relayInboxSchema, handleRelayInbox, relayWhoTool, relayWhoSchema, handleRelayWho, relaySpawnTool, relaySpawnSchema, handleRelaySpawn, relayReleaseTool, relayReleaseSchema, handleRelayRelease, relayStatusTool, relayStatusSchema, handleRelayStatus, relayLogsTool, relayLogsSchema, handleRelayLogs, relayMetricsTool, relayMetricsSchema, handleRelayMetrics, relayHealthTool, relayHealthSchema, handleRelayHealth, relayContinuityTool, relayContinuitySchema, handleRelayContinuity, } from './tools/index.js';
|
|
5
5
|
import { protocolPrompt, getProtocolPrompt } from './prompts/index.js';
|
|
6
6
|
import { agentsResource, getAgentsResource, inboxResource, getInboxResource, projectResource, getProjectResource, } from './resources/index.js';
|
|
7
7
|
/**
|
|
@@ -17,6 +17,7 @@ const TOOLS = [
|
|
|
17
17
|
relayLogsTool,
|
|
18
18
|
relayMetricsTool,
|
|
19
19
|
relayHealthTool,
|
|
20
|
+
relayContinuityTool,
|
|
20
21
|
];
|
|
21
22
|
/**
|
|
22
23
|
* All available prompts
|
|
@@ -97,6 +98,11 @@ export function createMCPServer(client, config) {
|
|
|
97
98
|
result = await handleRelayHealth(client, input);
|
|
98
99
|
break;
|
|
99
100
|
}
|
|
101
|
+
case 'relay_continuity': {
|
|
102
|
+
const input = relayContinuitySchema.parse(args);
|
|
103
|
+
result = await handleRelayContinuity(client, input);
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
100
106
|
default:
|
|
101
107
|
return {
|
|
102
108
|
content: [
|
|
@@ -7,4 +7,5 @@ export { relayStatusTool, relayStatusSchema, handleRelayStatus, type RelayStatus
|
|
|
7
7
|
export { relayLogsTool, relayLogsSchema, handleRelayLogs, type RelayLogsInput, } from './relay-logs.js';
|
|
8
8
|
export { relayMetricsTool, relayMetricsSchema, handleRelayMetrics, type RelayMetricsInput, } from './relay-metrics.js';
|
|
9
9
|
export { relayHealthTool, relayHealthSchema, handleRelayHealth, type RelayHealthInput, } from './relay-health.js';
|
|
10
|
+
export { relayContinuityTool, relayContinuitySchema, handleRelayContinuity, type RelayContinuityInput, } from './relay-continuity.js';
|
|
10
11
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -7,4 +7,5 @@ export { relayStatusTool, relayStatusSchema, handleRelayStatus, } from './relay-
|
|
|
7
7
|
export { relayLogsTool, relayLogsSchema, handleRelayLogs, } from './relay-logs.js';
|
|
8
8
|
export { relayMetricsTool, relayMetricsSchema, handleRelayMetrics, } from './relay-metrics.js';
|
|
9
9
|
export { relayHealthTool, relayHealthSchema, handleRelayHealth, } from './relay-health.js';
|
|
10
|
+
export { relayContinuityTool, relayContinuitySchema, handleRelayContinuity, } from './relay-continuity.js';
|
|
10
11
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
3
|
+
import type { RelayClient } from '../client.js';
|
|
4
|
+
export declare const relayContinuitySchema: z.ZodObject<{
|
|
5
|
+
action: z.ZodEnum<["save", "load", "uncertain"]>;
|
|
6
|
+
current_task: z.ZodOptional<z.ZodString>;
|
|
7
|
+
completed: z.ZodOptional<z.ZodString>;
|
|
8
|
+
in_progress: z.ZodOptional<z.ZodString>;
|
|
9
|
+
key_decisions: z.ZodOptional<z.ZodString>;
|
|
10
|
+
files: z.ZodOptional<z.ZodString>;
|
|
11
|
+
item: z.ZodOptional<z.ZodString>;
|
|
12
|
+
}, "strip", z.ZodTypeAny, {
|
|
13
|
+
action: "save" | "load" | "uncertain";
|
|
14
|
+
current_task?: string | undefined;
|
|
15
|
+
completed?: string | undefined;
|
|
16
|
+
in_progress?: string | undefined;
|
|
17
|
+
key_decisions?: string | undefined;
|
|
18
|
+
files?: string | undefined;
|
|
19
|
+
item?: string | undefined;
|
|
20
|
+
}, {
|
|
21
|
+
action: "save" | "load" | "uncertain";
|
|
22
|
+
current_task?: string | undefined;
|
|
23
|
+
completed?: string | undefined;
|
|
24
|
+
in_progress?: string | undefined;
|
|
25
|
+
key_decisions?: string | undefined;
|
|
26
|
+
files?: string | undefined;
|
|
27
|
+
item?: string | undefined;
|
|
28
|
+
}>;
|
|
29
|
+
export type RelayContinuityInput = z.infer<typeof relayContinuitySchema>;
|
|
30
|
+
export declare const relayContinuityTool: Tool;
|
|
31
|
+
/**
|
|
32
|
+
* Handle continuity actions for session recovery.
|
|
33
|
+
*/
|
|
34
|
+
export declare function handleRelayContinuity(client: RelayClient, input: RelayContinuityInput): Promise<string>;
|
|
35
|
+
//# sourceMappingURL=relay-continuity.d.ts.map
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const relayContinuitySchema = z.object({
|
|
3
|
+
action: z.enum(['save', 'load', 'uncertain']).describe('Action: save state, load previous state, or mark uncertainty'),
|
|
4
|
+
current_task: z.string().optional().describe('Current task being worked on (for save)'),
|
|
5
|
+
completed: z.string().optional().describe('Completed work summary (for save)'),
|
|
6
|
+
in_progress: z.string().optional().describe('In-progress work summary (for save)'),
|
|
7
|
+
key_decisions: z.string().optional().describe('Key decisions made (for save)'),
|
|
8
|
+
files: z.string().optional().describe('Files being worked on (for save)'),
|
|
9
|
+
item: z.string().optional().describe('Item to mark as uncertain (for uncertain action)'),
|
|
10
|
+
});
|
|
11
|
+
export const relayContinuityTool = {
|
|
12
|
+
name: 'relay_continuity',
|
|
13
|
+
description: `Manage session continuity for agent recovery.
|
|
14
|
+
|
|
15
|
+
Use this to:
|
|
16
|
+
- Save your current state before long operations or session end
|
|
17
|
+
- Load previous context when starting a new session
|
|
18
|
+
- Mark items that need future verification
|
|
19
|
+
|
|
20
|
+
Examples:
|
|
21
|
+
- Save state: action="save", current_task="Implementing auth", completed="User model done"
|
|
22
|
+
- Load state: action="load"
|
|
23
|
+
- Mark uncertain: action="uncertain", item="API rate limit handling unclear"`,
|
|
24
|
+
inputSchema: {
|
|
25
|
+
type: 'object',
|
|
26
|
+
properties: {
|
|
27
|
+
action: {
|
|
28
|
+
type: 'string',
|
|
29
|
+
enum: ['save', 'load', 'uncertain'],
|
|
30
|
+
description: 'Action: save state, load previous state, or mark uncertainty',
|
|
31
|
+
},
|
|
32
|
+
current_task: {
|
|
33
|
+
type: 'string',
|
|
34
|
+
description: 'Current task being worked on (for save)',
|
|
35
|
+
},
|
|
36
|
+
completed: {
|
|
37
|
+
type: 'string',
|
|
38
|
+
description: 'Completed work summary (for save)',
|
|
39
|
+
},
|
|
40
|
+
in_progress: {
|
|
41
|
+
type: 'string',
|
|
42
|
+
description: 'In-progress work summary (for save)',
|
|
43
|
+
},
|
|
44
|
+
key_decisions: {
|
|
45
|
+
type: 'string',
|
|
46
|
+
description: 'Key decisions made (for save)',
|
|
47
|
+
},
|
|
48
|
+
files: {
|
|
49
|
+
type: 'string',
|
|
50
|
+
description: 'Files being worked on (for save)',
|
|
51
|
+
},
|
|
52
|
+
item: {
|
|
53
|
+
type: 'string',
|
|
54
|
+
description: 'Item to mark as uncertain (for uncertain action)',
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
required: ['action'],
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Handle continuity actions for session recovery.
|
|
62
|
+
*/
|
|
63
|
+
export async function handleRelayContinuity(client, input) {
|
|
64
|
+
const extClient = client;
|
|
65
|
+
const { action, current_task, completed, in_progress, key_decisions, files, item } = input;
|
|
66
|
+
switch (action) {
|
|
67
|
+
case 'save': {
|
|
68
|
+
if (!extClient.saveContinuity) {
|
|
69
|
+
return 'Continuity save not supported by this client';
|
|
70
|
+
}
|
|
71
|
+
await extClient.saveContinuity({
|
|
72
|
+
currentTask: current_task,
|
|
73
|
+
completed,
|
|
74
|
+
inProgress: in_progress,
|
|
75
|
+
keyDecisions: key_decisions,
|
|
76
|
+
files,
|
|
77
|
+
});
|
|
78
|
+
return 'Session state saved for recovery';
|
|
79
|
+
}
|
|
80
|
+
case 'load': {
|
|
81
|
+
if (!extClient.loadContinuity) {
|
|
82
|
+
return 'Continuity load not supported by this client';
|
|
83
|
+
}
|
|
84
|
+
await extClient.loadContinuity();
|
|
85
|
+
return 'Previous session context loaded';
|
|
86
|
+
}
|
|
87
|
+
case 'uncertain': {
|
|
88
|
+
if (!extClient.markUncertain) {
|
|
89
|
+
return 'Mark uncertain not supported by this client';
|
|
90
|
+
}
|
|
91
|
+
if (!item) {
|
|
92
|
+
return 'Item required for uncertain action';
|
|
93
|
+
}
|
|
94
|
+
await extClient.markUncertain(item);
|
|
95
|
+
return `Marked as uncertain: ${item}`;
|
|
96
|
+
}
|
|
97
|
+
default:
|
|
98
|
+
return `Unknown action: ${action}`;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=relay-continuity.js.map
|
|
@@ -2,15 +2,12 @@ import { z } from 'zod';
|
|
|
2
2
|
import type { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
3
3
|
import type { RelayClient } from '../client.js';
|
|
4
4
|
export declare const relayHealthSchema: z.ZodObject<{
|
|
5
|
-
port: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
6
5
|
include_crashes: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
7
6
|
include_alerts: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
8
7
|
}, "strip", z.ZodTypeAny, {
|
|
9
|
-
port: number;
|
|
10
8
|
include_crashes: boolean;
|
|
11
9
|
include_alerts: boolean;
|
|
12
10
|
}, {
|
|
13
|
-
port?: number | undefined;
|
|
14
11
|
include_crashes?: boolean | undefined;
|
|
15
12
|
include_alerts?: boolean | undefined;
|
|
16
13
|
}>;
|
|
@@ -19,5 +16,5 @@ export declare const relayHealthTool: Tool;
|
|
|
19
16
|
/**
|
|
20
17
|
* Get system health, crash insights, and recommendations.
|
|
21
18
|
*/
|
|
22
|
-
export declare function handleRelayHealth(
|
|
19
|
+
export declare function handleRelayHealth(client: RelayClient, input: RelayHealthInput): Promise<string>;
|
|
23
20
|
//# sourceMappingURL=relay-health.d.ts.map
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
export const relayHealthSchema = z.object({
|
|
3
|
-
port: z.number().optional().default(3888).describe('Dashboard port (default: 3888)'),
|
|
4
3
|
include_crashes: z.boolean().optional().default(true).describe('Include recent crash history'),
|
|
5
4
|
include_alerts: z.boolean().optional().default(true).describe('Include unacknowledged alerts'),
|
|
6
5
|
});
|
|
@@ -24,11 +23,6 @@ Example: Health check without crash history
|
|
|
24
23
|
inputSchema: {
|
|
25
24
|
type: 'object',
|
|
26
25
|
properties: {
|
|
27
|
-
port: {
|
|
28
|
-
type: 'number',
|
|
29
|
-
description: 'Dashboard port (default: 3888)',
|
|
30
|
-
default: 3888,
|
|
31
|
-
},
|
|
32
26
|
include_crashes: {
|
|
33
27
|
type: 'boolean',
|
|
34
28
|
description: 'Include recent crash history',
|
|
@@ -46,14 +40,12 @@ Example: Health check without crash history
|
|
|
46
40
|
/**
|
|
47
41
|
* Get system health, crash insights, and recommendations.
|
|
48
42
|
*/
|
|
49
|
-
export async function handleRelayHealth(
|
|
50
|
-
const port = input.port || 3888;
|
|
43
|
+
export async function handleRelayHealth(client, input) {
|
|
51
44
|
try {
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
const data = await response.json();
|
|
45
|
+
const data = await client.getHealth({
|
|
46
|
+
include_crashes: input.include_crashes,
|
|
47
|
+
include_alerts: input.include_alerts,
|
|
48
|
+
});
|
|
57
49
|
const lines = [];
|
|
58
50
|
// Health score header
|
|
59
51
|
const scoreEmoji = data.healthScore >= 80 ? '✅' :
|
|
@@ -129,8 +121,8 @@ export async function handleRelayHealth(_client, input) {
|
|
|
129
121
|
}
|
|
130
122
|
catch (err) {
|
|
131
123
|
const error = err;
|
|
132
|
-
if (error.code === 'ECONNREFUSED') {
|
|
133
|
-
return `Cannot connect to
|
|
124
|
+
if (error.code === 'ECONNREFUSED' || error.code === 'ENOENT') {
|
|
125
|
+
return `Cannot connect to daemon. Is the daemon running?\n\nRun 'agent-relay up' to start the daemon.`;
|
|
134
126
|
}
|
|
135
127
|
return `Failed to fetch health data: ${error.message || String(err)}`;
|
|
136
128
|
}
|
|
@@ -3,6 +3,7 @@ import fs from 'node:fs';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { promisify } from 'node:util';
|
|
5
5
|
import { exec } from 'node:child_process';
|
|
6
|
+
import { getProjectPaths } from '@agent-relay/config';
|
|
6
7
|
const execAsync = promisify(exec);
|
|
7
8
|
export const relayLogsSchema = z.object({
|
|
8
9
|
agent: z.string().describe('Name of the agent to get logs for'),
|
|
@@ -39,10 +40,11 @@ Example: Get last 100 lines from Worker1
|
|
|
39
40
|
};
|
|
40
41
|
/**
|
|
41
42
|
* Get the worker logs directory path.
|
|
42
|
-
*
|
|
43
|
+
* Uses getProjectPaths from @agent-relay/config to match the bridge package.
|
|
43
44
|
*/
|
|
44
45
|
function getWorkerLogsDir(projectRoot) {
|
|
45
|
-
|
|
46
|
+
const paths = getProjectPaths(projectRoot);
|
|
47
|
+
return path.join(paths.teamDir, 'worker-logs');
|
|
46
48
|
}
|
|
47
49
|
/**
|
|
48
50
|
* Read recent logs from an agent's output file.
|