agent-relay 2.0.11 → 2.0.12
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/dist/dashboard/out/404.html +1 -1
- package/dist/dashboard/out/_next/static/chunks/320-23e5ffe6aa7eb934.js +1 -0
- package/{packages/dashboard/ui-dist/_next/static/css/99c2552394077586.css → dist/dashboard/out/_next/static/css/605dd4e30c91986f.css} +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 +2 -2
- 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 +2 -2
- package/dist/dashboard/out/login.html +2 -2
- 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 +2 -2
- 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 +2 -2
- package/dist/dashboard/out/signup.txt +1 -1
- package/package.json +15 -15
- package/packages/api-types/package.json +1 -1
- package/packages/bridge/dist/spawner.js +3 -70
- package/packages/bridge/package.json +7 -7
- package/packages/cloud/dist/api/cli-pty-runner.js +4 -25
- package/packages/cloud/dist/api/provider-env.d.ts +12 -0
- package/packages/cloud/dist/api/provider-env.js +66 -0
- package/packages/cloud/dist/api/providers.js +12 -2
- 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/cli-auth.js +4 -25
- package/packages/daemon/package.json +12 -12
- package/packages/dashboard/dist/server.js +32 -0
- package/packages/dashboard/package.json +12 -12
- package/packages/dashboard/ui/react-components/settings/WorkspaceSettingsPanel.tsx +44 -3
- package/packages/dashboard/ui-dist/404.html +1 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/320-23e5ffe6aa7eb934.js +1 -0
- package/{dist/dashboard/out/_next/static/css/99c2552394077586.css → packages/dashboard/ui-dist/_next/static/css/605dd4e30c91986f.css} +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 +2 -2
- 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 +2 -2
- package/packages/dashboard/ui-dist/login.html +2 -2
- 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 +2 -2
- 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 +2 -2
- package/packages/dashboard/ui-dist/signup.txt +1 -1
- package/packages/dashboard-server/package.json +12 -12
- package/packages/hooks/package.json +4 -4
- package/packages/mcp/package.json +2 -2
- package/packages/memory/package.json +2 -2
- package/packages/policy/package.json +2 -2
- package/packages/protocol/package.json +1 -1
- package/packages/resiliency/package.json +1 -1
- 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/dist/user-directory.d.ts +17 -0
- package/packages/user-directory/dist/user-directory.js +73 -0
- package/packages/user-directory/package.json +2 -2
- package/packages/utils/dist/index.d.ts +1 -0
- package/packages/utils/dist/index.js +1 -0
- package/packages/utils/dist/relay-pty-path.d.ts +44 -0
- package/packages/utils/dist/relay-pty-path.js +127 -0
- package/packages/utils/package.json +6 -1
- package/packages/wrapper/dist/relay-pty-orchestrator.d.ts +1 -0
- package/packages/wrapper/dist/relay-pty-orchestrator.js +4 -25
- package/packages/wrapper/package.json +6 -6
- package/dist/dashboard/out/_next/static/chunks/320-22ebe7be58cf982a.js +0 -1
- package/packages/dashboard/ui-dist/_next/static/chunks/320-22ebe7be58cf982a.js +0 -1
- /package/dist/dashboard/out/_next/static/{N2nFek-KCN321ZAghmjge → h1U3qU5XIfQSy46M_SDsz}/_buildManifest.js +0 -0
- /package/dist/dashboard/out/_next/static/{N2nFek-KCN321ZAghmjge → h1U3qU5XIfQSy46M_SDsz}/_ssgManifest.js +0 -0
- /package/packages/dashboard/ui-dist/_next/static/{59SiFCGkIPHj5xnvI-Hkn → 4WryIM4xHT22BbJ46YITr}/_buildManifest.js +0 -0
- /package/packages/dashboard/ui-dist/_next/static/{59SiFCGkIPHj5xnvI-Hkn → 4WryIM4xHT22BbJ46YITr}/_ssgManifest.js +0 -0
- /package/packages/dashboard/ui-dist/_next/static/{N2nFek-KCN321ZAghmjge → dS0EgrS-iG-_pkUVhBypz}/_buildManifest.js +0 -0
- /package/packages/dashboard/ui-dist/_next/static/{N2nFek-KCN321ZAghmjge → dS0EgrS-iG-_pkUVhBypz}/_ssgManifest.js +0 -0
- /package/packages/dashboard/ui-dist/_next/static/{eWYFV8hKU_42BNS8Dj84s → h1U3qU5XIfQSy46M_SDsz}/_buildManifest.js +0 -0
- /package/packages/dashboard/ui-dist/_next/static/{eWYFV8hKU_42BNS8Dj84s → h1U3qU5XIfQSy46M_SDsz}/_ssgManifest.js +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-relay",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.12",
|
|
4
4
|
"description": "Real-time agent-to-agent communication system",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -107,20 +107,20 @@
|
|
|
107
107
|
},
|
|
108
108
|
"homepage": "https://github.com/AgentWorkforce/relay#readme",
|
|
109
109
|
"dependencies": {
|
|
110
|
-
"@agent-relay/bridge": "2.0.
|
|
111
|
-
"@agent-relay/config": "2.0.
|
|
112
|
-
"@agent-relay/continuity": "2.0.
|
|
113
|
-
"@agent-relay/daemon": "2.0.
|
|
114
|
-
"@agent-relay/hooks": "2.0.
|
|
115
|
-
"@agent-relay/protocol": "2.0.
|
|
116
|
-
"@agent-relay/resiliency": "2.0.
|
|
117
|
-
"@agent-relay/sdk": "2.0.
|
|
118
|
-
"@agent-relay/storage": "2.0.
|
|
119
|
-
"@agent-relay/telemetry": "2.0.
|
|
120
|
-
"@agent-relay/trajectory": "2.0.
|
|
121
|
-
"@agent-relay/user-directory": "2.0.
|
|
122
|
-
"@agent-relay/utils": "2.0.
|
|
123
|
-
"@agent-relay/wrapper": "2.0.
|
|
110
|
+
"@agent-relay/bridge": "2.0.12",
|
|
111
|
+
"@agent-relay/config": "2.0.12",
|
|
112
|
+
"@agent-relay/continuity": "2.0.12",
|
|
113
|
+
"@agent-relay/daemon": "2.0.12",
|
|
114
|
+
"@agent-relay/hooks": "2.0.12",
|
|
115
|
+
"@agent-relay/protocol": "2.0.12",
|
|
116
|
+
"@agent-relay/resiliency": "2.0.12",
|
|
117
|
+
"@agent-relay/sdk": "2.0.12",
|
|
118
|
+
"@agent-relay/storage": "2.0.12",
|
|
119
|
+
"@agent-relay/telemetry": "2.0.12",
|
|
120
|
+
"@agent-relay/trajectory": "2.0.12",
|
|
121
|
+
"@agent-relay/user-directory": "2.0.12",
|
|
122
|
+
"@agent-relay/utils": "2.0.12",
|
|
123
|
+
"@agent-relay/wrapper": "2.0.12",
|
|
124
124
|
"@nangohq/node": "^0.69.20",
|
|
125
125
|
"@types/jsonwebtoken": "^9.0.10",
|
|
126
126
|
"agent-trajectories": "^0.2.3",
|
|
@@ -13,6 +13,7 @@ import { resolveCommand } from '@agent-relay/utils/command-resolver';
|
|
|
13
13
|
import { createTraceableError } from '@agent-relay/utils/error-tracking';
|
|
14
14
|
import { createLogger } from '@agent-relay/utils/logger';
|
|
15
15
|
import { mapModelToCli } from '@agent-relay/utils/model-mapping';
|
|
16
|
+
import { findRelayPtyBinary as findRelayPtyBinaryUtil } from '@agent-relay/utils/relay-pty-path';
|
|
16
17
|
import { RelayPtyOrchestrator } from '@agent-relay/wrapper';
|
|
17
18
|
import { selectShadowCli } from './shadow-cli.js';
|
|
18
19
|
// Get the directory where this module is located (for binary path resolution)
|
|
@@ -182,78 +183,10 @@ function getRelayInstructions(agentName, options = {}) {
|
|
|
182
183
|
/**
|
|
183
184
|
* Check if the relay-pty binary is available.
|
|
184
185
|
* Returns the path to the binary if found, null otherwise.
|
|
185
|
-
*
|
|
186
|
-
* Search order:
|
|
187
|
-
* 1. bin/relay-pty in package root (installed by postinstall)
|
|
188
|
-
* 2. relay-pty/target/release/relay-pty (local Rust build)
|
|
189
|
-
* 3. /usr/local/bin/relay-pty (global install)
|
|
190
|
-
* 4. In node_modules when installed as dependency
|
|
191
|
-
* 5. Global npm installs (nvm) - both scoped and root packages
|
|
186
|
+
* Uses shared utility from @agent-relay/utils.
|
|
192
187
|
*/
|
|
193
188
|
function findRelayPtyBinary() {
|
|
194
|
-
|
|
195
|
-
// This code runs from either:
|
|
196
|
-
// - packages/bridge/dist/ (development/workspace)
|
|
197
|
-
// - node_modules/@agent-relay/bridge/dist/ (npm install)
|
|
198
|
-
//
|
|
199
|
-
// We need to find the agent-relay package root where bin/relay-pty lives
|
|
200
|
-
let packageRoot;
|
|
201
|
-
// Check if we're inside node_modules/@agent-relay/bridge/
|
|
202
|
-
if (__dirname.includes('node_modules/@agent-relay/bridge')) {
|
|
203
|
-
// Go from node_modules/@agent-relay/bridge/dist/ to agent-relay/
|
|
204
|
-
// dist/ -> bridge/ -> @agent-relay/ -> node_modules/ -> agent-relay/
|
|
205
|
-
packageRoot = path.join(__dirname, '..', '..', '..', '..');
|
|
206
|
-
}
|
|
207
|
-
else if (__dirname.includes('node_modules/agent-relay')) {
|
|
208
|
-
// Direct dependency: node_modules/agent-relay/packages/bridge/dist/
|
|
209
|
-
// dist/ -> bridge/ -> packages/ -> agent-relay/
|
|
210
|
-
packageRoot = path.join(__dirname, '..', '..', '..');
|
|
211
|
-
}
|
|
212
|
-
else {
|
|
213
|
-
// Development: packages/bridge/dist/ -> packages/ -> project root
|
|
214
|
-
packageRoot = path.join(__dirname, '..', '..', '..');
|
|
215
|
-
}
|
|
216
|
-
// Find the node_modules root for global installs
|
|
217
|
-
// When running from node_modules/@agent-relay/dashboard/node_modules/@agent-relay/bridge/dist/
|
|
218
|
-
// we need to look for agent-relay at node_modules/agent-relay
|
|
219
|
-
let nodeModulesRoot = null;
|
|
220
|
-
const nodeModulesMatch = __dirname.match(/^(.+?\/node_modules)\/@agent-relay\//);
|
|
221
|
-
if (nodeModulesMatch) {
|
|
222
|
-
nodeModulesRoot = nodeModulesMatch[1];
|
|
223
|
-
}
|
|
224
|
-
const candidates = [
|
|
225
|
-
// Primary: installed by postinstall from platform-specific binary
|
|
226
|
-
path.join(packageRoot, 'bin', 'relay-pty'),
|
|
227
|
-
// Development: local Rust build
|
|
228
|
-
path.join(packageRoot, 'relay-pty', 'target', 'release', 'relay-pty'),
|
|
229
|
-
path.join(packageRoot, 'relay-pty', 'target', 'debug', 'relay-pty'),
|
|
230
|
-
// Local build in cwd (for development)
|
|
231
|
-
path.join(process.cwd(), 'relay-pty', 'target', 'release', 'relay-pty'),
|
|
232
|
-
// Installed globally
|
|
233
|
-
'/usr/local/bin/relay-pty',
|
|
234
|
-
// In node_modules (when installed as local dependency)
|
|
235
|
-
path.join(process.cwd(), 'node_modules', 'agent-relay', 'bin', 'relay-pty'),
|
|
236
|
-
// Global npm install (nvm) - root package
|
|
237
|
-
path.join(process.env.HOME || '', '.nvm', 'versions', 'node', process.version, 'lib', 'node_modules', 'agent-relay', 'bin', 'relay-pty'),
|
|
238
|
-
];
|
|
239
|
-
// Add candidate for root agent-relay package when running from scoped @agent-relay/* packages
|
|
240
|
-
if (nodeModulesRoot) {
|
|
241
|
-
candidates.push(path.join(nodeModulesRoot, 'agent-relay', 'bin', 'relay-pty'));
|
|
242
|
-
}
|
|
243
|
-
// Try common global npm paths
|
|
244
|
-
if (process.env.HOME) {
|
|
245
|
-
// Homebrew npm (macOS)
|
|
246
|
-
candidates.push(path.join('/usr/local/lib/node_modules', 'agent-relay', 'bin', 'relay-pty'));
|
|
247
|
-
candidates.push(path.join('/opt/homebrew/lib/node_modules', 'agent-relay', 'bin', 'relay-pty'));
|
|
248
|
-
// pnpm global
|
|
249
|
-
candidates.push(path.join(process.env.HOME, '.local', 'share', 'pnpm', 'global', 'node_modules', 'agent-relay', 'bin', 'relay-pty'));
|
|
250
|
-
}
|
|
251
|
-
for (const candidate of candidates) {
|
|
252
|
-
if (fs.existsSync(candidate)) {
|
|
253
|
-
return candidate;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
return null;
|
|
189
|
+
return findRelayPtyBinaryUtil(__dirname);
|
|
257
190
|
}
|
|
258
191
|
/** Cached result of relay-pty binary check */
|
|
259
192
|
let relayPtyBinaryPath;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-relay/bridge",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.12",
|
|
4
4
|
"description": "Multi-project bridge client utilities for Relay",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -22,12 +22,12 @@
|
|
|
22
22
|
"test:watch": "vitest"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@agent-relay/protocol": "2.0.
|
|
26
|
-
"@agent-relay/config": "2.0.
|
|
27
|
-
"@agent-relay/utils": "2.0.
|
|
28
|
-
"@agent-relay/policy": "2.0.
|
|
29
|
-
"@agent-relay/user-directory": "2.0.
|
|
30
|
-
"@agent-relay/wrapper": "2.0.
|
|
25
|
+
"@agent-relay/protocol": "2.0.12",
|
|
26
|
+
"@agent-relay/config": "2.0.12",
|
|
27
|
+
"@agent-relay/utils": "2.0.12",
|
|
28
|
+
"@agent-relay/policy": "2.0.12",
|
|
29
|
+
"@agent-relay/user-directory": "2.0.12",
|
|
30
|
+
"@agent-relay/wrapper": "2.0.12"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/node": "^22.19.3",
|
|
@@ -7,8 +7,7 @@
|
|
|
7
7
|
* Uses the relay-pty Rust binary for PTY emulation.
|
|
8
8
|
*/
|
|
9
9
|
import { spawn } from 'node:child_process';
|
|
10
|
-
import {
|
|
11
|
-
import { join, dirname } from 'node:path';
|
|
10
|
+
import { dirname } from 'node:path';
|
|
12
11
|
import { fileURLToPath } from 'node:url';
|
|
13
12
|
import { randomUUID } from 'node:crypto';
|
|
14
13
|
// Get the directory where this module is located
|
|
@@ -16,6 +15,7 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
16
15
|
const __dirname = dirname(__filename);
|
|
17
16
|
// Import shared config and utilities
|
|
18
17
|
import { CLI_AUTH_CONFIG, stripAnsiCodes, matchesSuccessPattern, findMatchingPrompt, validateProviderConfig, validateAllProviderConfigs as validateAllConfigs, getSupportedProviders, } from '@agent-relay/config/cli-auth-config';
|
|
18
|
+
import { findRelayPtyBinary as findRelayPtyBinaryUtil } from '@agent-relay/utils/relay-pty-path';
|
|
19
19
|
// Re-export everything from shared config for backward compatibility
|
|
20
20
|
export { CLI_AUTH_CONFIG, stripAnsiCodes, matchesSuccessPattern, findMatchingPrompt, validateProviderConfig, getSupportedProviders, };
|
|
21
21
|
// Wrapper that throws instead of returning array (backward compatible)
|
|
@@ -27,31 +27,10 @@ export function validateAllProviderConfigs() {
|
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
29
29
|
* Find the relay-pty binary path.
|
|
30
|
-
*
|
|
30
|
+
* Uses shared utility from @agent-relay/utils.
|
|
31
31
|
*/
|
|
32
32
|
function findRelayPtyBinary() {
|
|
33
|
-
|
|
34
|
-
// packages/cloud/dist/api/ -> packages/cloud/dist -> packages/cloud -> packages -> project root
|
|
35
|
-
const projectRoot = join(__dirname, '..', '..', '..', '..', '..');
|
|
36
|
-
const candidates = [
|
|
37
|
-
// Primary: installed by postinstall from platform-specific binary
|
|
38
|
-
join(projectRoot, 'bin', 'relay-pty'),
|
|
39
|
-
// Development: local Rust build
|
|
40
|
-
join(projectRoot, 'relay-pty', 'target', 'release', 'relay-pty'),
|
|
41
|
-
join(projectRoot, 'relay-pty', 'target', 'debug', 'relay-pty'),
|
|
42
|
-
// Local build in cwd (for development)
|
|
43
|
-
join(process.cwd(), 'relay-pty', 'target', 'release', 'relay-pty'),
|
|
44
|
-
// Installed globally
|
|
45
|
-
'/usr/local/bin/relay-pty',
|
|
46
|
-
// In node_modules (when installed as dependency)
|
|
47
|
-
join(process.cwd(), 'node_modules', 'agent-relay', 'bin', 'relay-pty'),
|
|
48
|
-
];
|
|
49
|
-
for (const candidate of candidates) {
|
|
50
|
-
if (existsSync(candidate)) {
|
|
51
|
-
return candidate;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return null;
|
|
33
|
+
return findRelayPtyBinaryUtil(__dirname);
|
|
55
34
|
}
|
|
56
35
|
/**
|
|
57
36
|
* Run CLI auth flow via PTY using relay-pty binary
|
|
@@ -11,4 +11,16 @@ export declare function setProviderApiKeyEnv(userId: string, provider: string, a
|
|
|
11
11
|
updated: number;
|
|
12
12
|
skipped: number;
|
|
13
13
|
}>;
|
|
14
|
+
/**
|
|
15
|
+
* Clear provider credentials from workspace(s).
|
|
16
|
+
* Deletes credential files and unsets environment variables.
|
|
17
|
+
*
|
|
18
|
+
* @param userId - User ID
|
|
19
|
+
* @param provider - Provider name (e.g., 'google', 'anthropic', 'codex')
|
|
20
|
+
* @param workspaceId - Workspace to clear credentials from
|
|
21
|
+
*/
|
|
22
|
+
export declare function clearProviderCredentials(userId: string, provider: string, workspaceId: string): Promise<{
|
|
23
|
+
cleared: boolean;
|
|
24
|
+
error?: string;
|
|
25
|
+
}>;
|
|
14
26
|
//# sourceMappingURL=provider-env.d.ts.map
|
|
@@ -4,6 +4,18 @@ const PROVIDER_ENV_VARS = {
|
|
|
4
4
|
google: 'GEMINI_API_KEY',
|
|
5
5
|
gemini: 'GEMINI_API_KEY',
|
|
6
6
|
};
|
|
7
|
+
/**
|
|
8
|
+
* All providers that may have credential files on the workspace.
|
|
9
|
+
* This includes CLI-based providers that store auth locally.
|
|
10
|
+
*/
|
|
11
|
+
const ALL_CREDENTIAL_PROVIDERS = [
|
|
12
|
+
'anthropic', 'claude',
|
|
13
|
+
'codex', 'openai',
|
|
14
|
+
'google', 'gemini',
|
|
15
|
+
'opencode',
|
|
16
|
+
'droid', 'factory',
|
|
17
|
+
'cursor',
|
|
18
|
+
];
|
|
7
19
|
/**
|
|
8
20
|
* Providers that need credential files written to the workspace filesystem.
|
|
9
21
|
* These providers have CLIs that read from files rather than just env vars.
|
|
@@ -72,4 +84,58 @@ export async function setProviderApiKeyEnv(userId, provider, apiKey, workspaceId
|
|
|
72
84
|
const updated = results.filter((result) => result === 'updated').length;
|
|
73
85
|
return { updated, skipped: results.length - updated };
|
|
74
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Clear provider credentials from workspace(s).
|
|
89
|
+
* Deletes credential files and unsets environment variables.
|
|
90
|
+
*
|
|
91
|
+
* @param userId - User ID
|
|
92
|
+
* @param provider - Provider name (e.g., 'google', 'anthropic', 'codex')
|
|
93
|
+
* @param workspaceId - Workspace to clear credentials from
|
|
94
|
+
*/
|
|
95
|
+
export async function clearProviderCredentials(userId, provider, workspaceId) {
|
|
96
|
+
const envVarName = PROVIDER_ENV_VARS[provider];
|
|
97
|
+
const needsCredentialFileClear = ALL_CREDENTIAL_PROVIDERS.includes(provider);
|
|
98
|
+
// Get the workspace
|
|
99
|
+
const workspace = await db.workspaces.findById(workspaceId);
|
|
100
|
+
if (!workspace) {
|
|
101
|
+
return { cleared: false, error: 'Workspace not found' };
|
|
102
|
+
}
|
|
103
|
+
if (!workspace.publicUrl) {
|
|
104
|
+
// Workspace not running, credentials will be gone when it restarts anyway
|
|
105
|
+
return { cleared: true };
|
|
106
|
+
}
|
|
107
|
+
// Clear environment variable if applicable
|
|
108
|
+
if (envVarName && workspace.computeId) {
|
|
109
|
+
const provisioner = getProvisioner();
|
|
110
|
+
try {
|
|
111
|
+
// Set to empty string to clear
|
|
112
|
+
await provisioner.setWorkspaceEnvVars(workspace, { [envVarName]: '' });
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
console.warn(`[provider-env] Failed to clear env var ${envVarName} on workspace ${workspace.id}:`, err);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Delete credential files from workspace
|
|
119
|
+
if (needsCredentialFileClear) {
|
|
120
|
+
try {
|
|
121
|
+
const response = await fetch(`${workspace.publicUrl}/api/credentials/apikey`, {
|
|
122
|
+
method: 'DELETE',
|
|
123
|
+
headers: { 'Content-Type': 'application/json' },
|
|
124
|
+
body: JSON.stringify({ userId, provider }),
|
|
125
|
+
});
|
|
126
|
+
if (!response.ok) {
|
|
127
|
+
const data = await response.json().catch(() => ({}));
|
|
128
|
+
console.warn(`[provider-env] Failed to delete credential files for ${provider} on workspace ${workspace.id}: ${response.status}`, data);
|
|
129
|
+
return { cleared: false, error: 'Failed to delete credential files on workspace' };
|
|
130
|
+
}
|
|
131
|
+
const data = await response.json();
|
|
132
|
+
console.log(`[provider-env] Deleted ${provider} credentials for user ${userId} on workspace ${workspace.id}:`, data.deletedPaths);
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
console.warn(`[provider-env] Error deleting credential files for ${provider} on workspace ${workspace.id}:`, err);
|
|
136
|
+
return { cleared: false, error: 'Error connecting to workspace' };
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return { cleared: true };
|
|
140
|
+
}
|
|
75
141
|
//# sourceMappingURL=provider-env.js.map
|
|
@@ -9,7 +9,7 @@ import { createClient } from 'redis';
|
|
|
9
9
|
import { requireAuth } from './auth.js';
|
|
10
10
|
import { getConfig } from '../config.js';
|
|
11
11
|
import { db } from '../db/index.js';
|
|
12
|
-
import { setProviderApiKeyEnv } from './provider-env.js';
|
|
12
|
+
import { setProviderApiKeyEnv, clearProviderCredentials } from './provider-env.js';
|
|
13
13
|
export const providersRouter = Router();
|
|
14
14
|
// All routes require authentication
|
|
15
15
|
providersRouter.use(requireAuth);
|
|
@@ -402,6 +402,7 @@ providersRouter.get('/:provider/status/:flowId', (req, res) => {
|
|
|
402
402
|
/**
|
|
403
403
|
* DELETE /api/providers/:provider
|
|
404
404
|
* Disconnect a provider from a specific workspace
|
|
405
|
+
* Also clears credential files from the workspace filesystem
|
|
405
406
|
*
|
|
406
407
|
* Query: ?workspaceId=xxx
|
|
407
408
|
*/
|
|
@@ -420,12 +421,21 @@ providersRouter.delete('/:provider', async (req, res) => {
|
|
|
420
421
|
}
|
|
421
422
|
const workspaceId = workspaceIdParam;
|
|
422
423
|
const provider = providerParam;
|
|
424
|
+
// Sanitize for logging (prevent log injection)
|
|
425
|
+
const safeProvider = String(provider).replace(/[\r\n]/g, '').substring(0, 20);
|
|
423
426
|
try {
|
|
427
|
+
// Delete from database
|
|
424
428
|
await db.credentials.deleteForWorkspace(userId, workspaceId, provider);
|
|
429
|
+
// Clear credentials from workspace filesystem
|
|
430
|
+
const clearResult = await clearProviderCredentials(userId, provider, workspaceId);
|
|
431
|
+
if (!clearResult.cleared) {
|
|
432
|
+
console.warn('[providers] Failed to clear workspace credentials', { provider: safeProvider, error: clearResult.error });
|
|
433
|
+
// Don't fail the request, just warn - database entry was already deleted
|
|
434
|
+
}
|
|
425
435
|
res.json({ success: true });
|
|
426
436
|
}
|
|
427
437
|
catch (error) {
|
|
428
|
-
console.error(
|
|
438
|
+
console.error('[providers] Error disconnecting', { provider: safeProvider, error: error instanceof Error ? error.message : String(error) });
|
|
429
439
|
res.status(500).json({ error: 'Failed to disconnect provider' });
|
|
430
440
|
}
|
|
431
441
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-relay/cloud",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.12",
|
|
4
4
|
"description": "Cloud API server and services for Agent Relay",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -38,11 +38,11 @@
|
|
|
38
38
|
"test:watch": "vitest"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@agent-relay/wrapper": "2.0.
|
|
42
|
-
"@agent-relay/config": "2.0.
|
|
43
|
-
"@agent-relay/resiliency": "2.0.
|
|
44
|
-
"@agent-relay/storage": "2.0.
|
|
45
|
-
"@agent-relay/protocol": "2.0.
|
|
41
|
+
"@agent-relay/wrapper": "2.0.12",
|
|
42
|
+
"@agent-relay/config": "2.0.12",
|
|
43
|
+
"@agent-relay/resiliency": "2.0.12",
|
|
44
|
+
"@agent-relay/storage": "2.0.12",
|
|
45
|
+
"@agent-relay/protocol": "2.0.12"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/node": "^22.19.3",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-relay/config",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.12",
|
|
4
4
|
"description": "Shared configuration schemas and loaders for Agent Relay",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"test:watch": "vitest"
|
|
84
84
|
},
|
|
85
85
|
"dependencies": {
|
|
86
|
-
"@agent-relay/protocol": "2.0.
|
|
86
|
+
"@agent-relay/protocol": "2.0.12",
|
|
87
87
|
"zod": "^3.23.8",
|
|
88
88
|
"zod-to-json-schema": "^3.23.1"
|
|
89
89
|
},
|
|
@@ -8,15 +8,15 @@
|
|
|
8
8
|
* version compatibility by avoiding native module compilation.
|
|
9
9
|
*/
|
|
10
10
|
import { spawn } from 'node:child_process';
|
|
11
|
-
import { existsSync } from 'node:fs';
|
|
12
11
|
import * as fs from 'fs/promises';
|
|
13
12
|
import * as os from 'os';
|
|
14
|
-
import {
|
|
13
|
+
import { dirname } from 'node:path';
|
|
15
14
|
import { fileURLToPath } from 'node:url';
|
|
16
15
|
import * as crypto from 'crypto';
|
|
17
16
|
import { createLogger } from '@agent-relay/resiliency';
|
|
18
17
|
import { CLI_AUTH_CONFIG, stripAnsiCodes, matchesSuccessPattern, findMatchingPrompt, findMatchingError, getSupportedProviders, } from '@agent-relay/config/cli-auth-config';
|
|
19
18
|
import { getUserDirectoryService } from '@agent-relay/user-directory';
|
|
19
|
+
import { findRelayPtyBinary as findRelayPtyBinaryUtil } from '@agent-relay/utils/relay-pty-path';
|
|
20
20
|
// Get the directory where this module is located
|
|
21
21
|
const __filename = fileURLToPath(import.meta.url);
|
|
22
22
|
const __dirname = dirname(__filename);
|
|
@@ -25,31 +25,10 @@ const logger = createLogger('cli-auth');
|
|
|
25
25
|
export { CLI_AUTH_CONFIG, getSupportedProviders };
|
|
26
26
|
/**
|
|
27
27
|
* Find the relay-pty binary path.
|
|
28
|
-
*
|
|
28
|
+
* Uses shared utility from @agent-relay/utils.
|
|
29
29
|
*/
|
|
30
30
|
function findRelayPtyBinary() {
|
|
31
|
-
|
|
32
|
-
// packages/daemon/dist/ -> packages/daemon -> packages -> project root
|
|
33
|
-
const projectRoot = join(__dirname, '..', '..', '..', '..');
|
|
34
|
-
const candidates = [
|
|
35
|
-
// Primary: installed by postinstall from platform-specific binary
|
|
36
|
-
join(projectRoot, 'bin', 'relay-pty'),
|
|
37
|
-
// Development: local Rust build
|
|
38
|
-
join(projectRoot, 'relay-pty', 'target', 'release', 'relay-pty'),
|
|
39
|
-
join(projectRoot, 'relay-pty', 'target', 'debug', 'relay-pty'),
|
|
40
|
-
// Local build in cwd (for development)
|
|
41
|
-
join(process.cwd(), 'relay-pty', 'target', 'release', 'relay-pty'),
|
|
42
|
-
// Installed globally
|
|
43
|
-
'/usr/local/bin/relay-pty',
|
|
44
|
-
// In node_modules (when installed as dependency)
|
|
45
|
-
join(process.cwd(), 'node_modules', 'agent-relay', 'bin', 'relay-pty'),
|
|
46
|
-
];
|
|
47
|
-
for (const candidate of candidates) {
|
|
48
|
-
if (existsSync(candidate)) {
|
|
49
|
-
return candidate;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return null;
|
|
31
|
+
return findRelayPtyBinaryUtil(__dirname);
|
|
53
32
|
}
|
|
54
33
|
// Active sessions
|
|
55
34
|
const sessions = new Map();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-relay/daemon",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.12",
|
|
4
4
|
"description": "Relay daemon server - agent coordination and message routing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -22,17 +22,17 @@
|
|
|
22
22
|
"test:watch": "vitest"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@agent-relay/protocol": "2.0.
|
|
26
|
-
"@agent-relay/config": "2.0.
|
|
27
|
-
"@agent-relay/storage": "2.0.
|
|
28
|
-
"@agent-relay/bridge": "2.0.
|
|
29
|
-
"@agent-relay/utils": "2.0.
|
|
30
|
-
"@agent-relay/policy": "2.0.
|
|
31
|
-
"@agent-relay/memory": "2.0.
|
|
32
|
-
"@agent-relay/resiliency": "2.0.
|
|
33
|
-
"@agent-relay/user-directory": "2.0.
|
|
34
|
-
"@agent-relay/wrapper": "2.0.
|
|
35
|
-
"@agent-relay/telemetry": "2.0.
|
|
25
|
+
"@agent-relay/protocol": "2.0.12",
|
|
26
|
+
"@agent-relay/config": "2.0.12",
|
|
27
|
+
"@agent-relay/storage": "2.0.12",
|
|
28
|
+
"@agent-relay/bridge": "2.0.12",
|
|
29
|
+
"@agent-relay/utils": "2.0.12",
|
|
30
|
+
"@agent-relay/policy": "2.0.12",
|
|
31
|
+
"@agent-relay/memory": "2.0.12",
|
|
32
|
+
"@agent-relay/resiliency": "2.0.12",
|
|
33
|
+
"@agent-relay/user-directory": "2.0.12",
|
|
34
|
+
"@agent-relay/wrapper": "2.0.12",
|
|
35
|
+
"@agent-relay/telemetry": "2.0.12",
|
|
36
36
|
"ws": "^8.18.3",
|
|
37
37
|
"better-sqlite3": "^12.6.2",
|
|
38
38
|
"pg": "^8.16.3",
|
|
@@ -3422,6 +3422,38 @@ export async function startDashboard(portOrOptions, dataDirArg, teamDirArg, dbPa
|
|
|
3422
3422
|
res.status(500).json({ error: 'Failed to write credential file' });
|
|
3423
3423
|
}
|
|
3424
3424
|
});
|
|
3425
|
+
/**
|
|
3426
|
+
* DELETE /api/credentials/apikey - Delete API key credential from user's home directory
|
|
3427
|
+
* Used by cloud API to clear provider credentials when disconnecting
|
|
3428
|
+
*/
|
|
3429
|
+
app.delete('/api/credentials/apikey', express.json(), async (req, res) => {
|
|
3430
|
+
const { userId, provider } = req.body;
|
|
3431
|
+
if (!userId || typeof userId !== 'string') {
|
|
3432
|
+
return res.status(400).json({ error: 'userId is required' });
|
|
3433
|
+
}
|
|
3434
|
+
if (!provider || typeof provider !== 'string') {
|
|
3435
|
+
return res.status(400).json({ error: 'provider is required' });
|
|
3436
|
+
}
|
|
3437
|
+
// Sanitize for logging (prevent log injection)
|
|
3438
|
+
const safeProvider = String(provider).replace(/[\r\n]/g, '').substring(0, 20);
|
|
3439
|
+
const safeUserId = String(userId).replace(/[\r\n]/g, '').substring(0, 50);
|
|
3440
|
+
try {
|
|
3441
|
+
// Dynamically import to avoid loading user-directory in all cases
|
|
3442
|
+
const { getUserDirectoryService } = await import('@agent-relay/user-directory');
|
|
3443
|
+
const userDirService = getUserDirectoryService();
|
|
3444
|
+
const deletedPaths = userDirService.deleteProviderCredentials(userId, provider);
|
|
3445
|
+
console.log('[credentials] Deleted credentials', { provider: safeProvider, userId: safeUserId, count: deletedPaths.length });
|
|
3446
|
+
res.json({
|
|
3447
|
+
success: true,
|
|
3448
|
+
message: 'credentials deleted',
|
|
3449
|
+
deletedPaths,
|
|
3450
|
+
});
|
|
3451
|
+
}
|
|
3452
|
+
catch (err) {
|
|
3453
|
+
console.error('[credentials] Failed to delete credentials', { provider: safeProvider, userId: safeUserId, error: err instanceof Error ? err.message : String(err) });
|
|
3454
|
+
res.status(500).json({ error: 'Failed to delete credential files' });
|
|
3455
|
+
}
|
|
3456
|
+
});
|
|
3425
3457
|
// ===== Metrics API =====
|
|
3426
3458
|
/**
|
|
3427
3459
|
* GET /api/metrics - JSON format metrics for dashboard
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-relay/dashboard",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.12",
|
|
4
4
|
"description": "Web dashboard for Agent Relay - optional package for visual agent coordination",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -25,17 +25,17 @@
|
|
|
25
25
|
"test:watch": "vitest"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@agent-relay/protocol": "2.0.
|
|
29
|
-
"@agent-relay/config": "2.0.
|
|
30
|
-
"@agent-relay/storage": "2.0.
|
|
31
|
-
"@agent-relay/bridge": "2.0.
|
|
32
|
-
"@agent-relay/utils": "2.0.
|
|
33
|
-
"@agent-relay/resiliency": "2.0.
|
|
34
|
-
"@agent-relay/trajectory": "2.0.
|
|
35
|
-
"@agent-relay/cloud": "2.0.
|
|
36
|
-
"@agent-relay/daemon": "2.0.
|
|
37
|
-
"@agent-relay/user-directory": "2.0.
|
|
38
|
-
"@agent-relay/wrapper": "2.0.
|
|
28
|
+
"@agent-relay/protocol": "2.0.12",
|
|
29
|
+
"@agent-relay/config": "2.0.12",
|
|
30
|
+
"@agent-relay/storage": "2.0.12",
|
|
31
|
+
"@agent-relay/bridge": "2.0.12",
|
|
32
|
+
"@agent-relay/utils": "2.0.12",
|
|
33
|
+
"@agent-relay/resiliency": "2.0.12",
|
|
34
|
+
"@agent-relay/trajectory": "2.0.12",
|
|
35
|
+
"@agent-relay/cloud": "2.0.12",
|
|
36
|
+
"@agent-relay/daemon": "2.0.12",
|
|
37
|
+
"@agent-relay/user-directory": "2.0.12",
|
|
38
|
+
"@agent-relay/wrapper": "2.0.12",
|
|
39
39
|
"express": "^5.2.1",
|
|
40
40
|
"ws": "^8.18.3"
|
|
41
41
|
},
|
|
@@ -158,6 +158,9 @@ export function WorkspaceSettingsPanel({
|
|
|
158
158
|
google: true, // Default to terminal for Gemini - allows choosing OAuth or API key
|
|
159
159
|
});
|
|
160
160
|
|
|
161
|
+
// Provider disconnection state
|
|
162
|
+
const [disconnectingProvider, setDisconnectingProvider] = useState<string | null>(null);
|
|
163
|
+
|
|
161
164
|
// Repo sync state
|
|
162
165
|
const [syncingRepoId, setSyncingRepoId] = useState<string | null>(null);
|
|
163
166
|
|
|
@@ -231,6 +234,34 @@ export function WorkspaceSettingsPanel({
|
|
|
231
234
|
// ProviderAuthFlow will handle the rest when it mounts
|
|
232
235
|
};
|
|
233
236
|
|
|
237
|
+
// Disconnect a provider
|
|
238
|
+
const handleDisconnectProvider = useCallback(async (provider: AIProvider) => {
|
|
239
|
+
const confirmed = window.confirm(
|
|
240
|
+
`Are you sure you want to disconnect ${provider.displayName}? This will remove the authentication and delete credential files from the workspace.`
|
|
241
|
+
);
|
|
242
|
+
if (!confirmed) return;
|
|
243
|
+
|
|
244
|
+
setDisconnectingProvider(provider.id);
|
|
245
|
+
setProviderError(null);
|
|
246
|
+
|
|
247
|
+
try {
|
|
248
|
+
const result = await cloudApi.disconnectProvider(provider.id, workspaceId);
|
|
249
|
+
if (result.success) {
|
|
250
|
+
setProviderStatus(prev => {
|
|
251
|
+
const updated = { ...prev };
|
|
252
|
+
delete updated[provider.id];
|
|
253
|
+
return updated;
|
|
254
|
+
});
|
|
255
|
+
} else {
|
|
256
|
+
setProviderError(result.error);
|
|
257
|
+
}
|
|
258
|
+
} catch (err) {
|
|
259
|
+
setProviderError(err instanceof Error ? err.message : 'Failed to disconnect provider');
|
|
260
|
+
} finally {
|
|
261
|
+
setDisconnectingProvider(null);
|
|
262
|
+
}
|
|
263
|
+
}, [workspaceId]);
|
|
264
|
+
|
|
234
265
|
const submitApiKey = async (provider: AIProvider) => {
|
|
235
266
|
if (!apiKeyInput.trim()) {
|
|
236
267
|
setProviderError('Please enter an API key');
|
|
@@ -605,9 +636,19 @@ export function WorkspaceSettingsPanel({
|
|
|
605
636
|
</div>
|
|
606
637
|
|
|
607
638
|
{providerStatus[provider.id] ? (
|
|
608
|
-
<div className="flex items-center gap-
|
|
609
|
-
<div className="
|
|
610
|
-
|
|
639
|
+
<div className="flex items-center gap-3">
|
|
640
|
+
<div className="flex items-center gap-2 px-4 py-2 bg-success/15 rounded-full border border-success/30">
|
|
641
|
+
<div className="w-2 h-2 rounded-full bg-success animate-pulse" />
|
|
642
|
+
<span className="text-sm font-medium text-success">Connected</span>
|
|
643
|
+
</div>
|
|
644
|
+
<button
|
|
645
|
+
onClick={() => handleDisconnectProvider(provider)}
|
|
646
|
+
disabled={disconnectingProvider === provider.id}
|
|
647
|
+
className="px-3 py-2 text-xs font-medium text-error/80 hover:text-error hover:bg-error/10 rounded-lg border border-transparent hover:border-error/30 transition-all disabled:opacity-50 disabled:cursor-not-allowed"
|
|
648
|
+
title={`Disconnect ${provider.displayName}`}
|
|
649
|
+
>
|
|
650
|
+
{disconnectingProvider === provider.id ? 'Disconnecting...' : 'Disconnect'}
|
|
651
|
+
</button>
|
|
611
652
|
</div>
|
|
612
653
|
) : null}
|
|
613
654
|
</div>
|