@stdiobus/workers-registry 1.5.0-beta.1 → 1.5.0-beta.3
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 +127 -32
- package/out/dist/workers-registry/acp-registry/index.js +1 -1
- package/out/dist/workers-registry/acp-registry/index.js.map +2 -2
- package/out/dist/workers-registry/openai-agent/index.js +1 -1
- package/out/dist/workers-registry/openai-agent/index.js.map +2 -2
- package/out/dist/workers-registry/registry-launcher/index.js +1 -1
- package/out/dist/workers-registry/registry-launcher/index.js.map +2 -2
- package/out/tsc/workers-registry/registry-launcher/src/auth/types.d.ts +11 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -21,9 +21,15 @@ Worker implementations for [stdio Bus kernel](https://github.com/stdiobus/stdiob
|
|
|
21
21
|
npm install @stdiobus/workers-registry
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
+
For embedded usage (no Docker/binary needed):
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install @stdiobus/node @stdiobus/workers-registry
|
|
28
|
+
```
|
|
29
|
+
|
|
24
30
|
**Requirements:**
|
|
25
31
|
- Node.js ≥20.0.0
|
|
26
|
-
- [stdio Bus kernel](https://github.com/stdiobus/stdiobus) (Docker or binary)
|
|
32
|
+
- [stdio Bus kernel](https://github.com/stdiobus/stdiobus) (Docker or binary) — or [`@stdiobus/node`](https://www.npmjs.com/package/@stdiobus/node) for embedded mode
|
|
27
33
|
|
|
28
34
|
**Keywords:** `stdiobus`, `protocol`, `acp`, `mcp`, `agent`, `transport`, `json-rpc`, `stdio-bus`, `worker`
|
|
29
35
|
|
|
@@ -61,22 +67,26 @@ graph TB
|
|
|
61
67
|
|
|
62
68
|
## Prerequisites
|
|
63
69
|
|
|
64
|
-
-
|
|
65
|
-
-
|
|
70
|
+
- Node.js 20.0.0 or later
|
|
71
|
+
- One of:
|
|
72
|
+
- [`@stdiobus/node`](https://www.npmjs.com/package/@stdiobus/node) — embedded mode, no external dependencies
|
|
73
|
+
- [stdio Bus kernel via Docker](https://hub.docker.com/r/stdiobus/stdiobus) — TCP/Unix socket mode
|
|
74
|
+
- [stdio Bus kernel binary](https://github.com/stdiobus/stdiobus) — build from source
|
|
66
75
|
|
|
67
76
|
## Workers
|
|
68
77
|
|
|
69
78
|
| Worker | Description | Protocol | Command |
|
|
70
79
|
|--------|-------------|----------|---------|
|
|
71
|
-
| `acp-registry` | Registry Launcher worker that routes to ACP Registry agents (requires `api-keys.json`) | ACP | `
|
|
72
|
-
| `acp-worker` | Full ACP protocol implementation (standalone agent; does **not** route to ACP Registry) | ACP | `
|
|
80
|
+
| `acp-registry` | Registry Launcher worker that routes to ACP Registry agents (requires `api-keys.json`) | ACP | `npx @stdiobus/workers-registry acp-registry` |
|
|
81
|
+
| `acp-worker` | Full ACP protocol implementation (standalone agent; does **not** route to ACP Registry) | ACP | `npx @stdiobus/workers-registry acp-worker` |
|
|
73
82
|
| `registry-launcher` | Registry Launcher implementation module used by `acp-registry` (not a launch target) | ACP | Use `acp-registry` |
|
|
74
|
-
| `
|
|
75
|
-
| `
|
|
76
|
-
| `
|
|
83
|
+
| `openai-agent` | OpenAI Chat Completions API agent (bridges ACP to any OpenAI-compatible endpoint) | ACP | `npx @stdiobus/workers-registry openai-agent` |
|
|
84
|
+
| `mcp-to-acp-proxy` | Bridges MCP clients (like IDEs) to ACP agents | MCP → ACP | `npx @stdiobus/workers-registry mcp-to-acp-proxy` |
|
|
85
|
+
| `echo-worker` | Simple echo worker for testing NDJSON protocol | NDJSON | `npx @stdiobus/workers-registry echo-worker` |
|
|
86
|
+
| `mcp-echo-server` | MCP server example for testing | MCP | `npx @stdiobus/workers-registry mcp-echo-server` |
|
|
77
87
|
|
|
78
|
-
**Note:** The universal launcher is `@stdiobus/workers-registry/launch`.
|
|
79
|
-
`node ./launch/index.js <worker-name>` after `npm run build`.
|
|
88
|
+
**Note:** The universal launcher is `@stdiobus/workers-registry/launch`. For local
|
|
89
|
+
development in this repo, use `node ./launch/index.js <worker-name>` after `npm run build`.
|
|
80
90
|
|
|
81
91
|
## Package API
|
|
82
92
|
|
|
@@ -111,13 +121,75 @@ import type { MCPServer } from '@stdiobus/workers-registry/workers/mcp-echo-serv
|
|
|
111
121
|
|
|
112
122
|
## Quick Start
|
|
113
123
|
|
|
114
|
-
###
|
|
124
|
+
### Option A: Embedded via `@stdiobus/node` (simplest)
|
|
125
|
+
|
|
126
|
+
No Docker, no binary, no TCP — just npm packages. The bus runs inside your Node.js process.
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
npm install @stdiobus/node @stdiobus/workers-registry
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Create `config.json`:
|
|
133
|
+
|
|
134
|
+
```json
|
|
135
|
+
{
|
|
136
|
+
"pools": [
|
|
137
|
+
{
|
|
138
|
+
"id": "openai-agent",
|
|
139
|
+
"command": "npx",
|
|
140
|
+
"args": ["@stdiobus/workers-registry", "openai-agent"],
|
|
141
|
+
"instances": 1
|
|
142
|
+
}
|
|
143
|
+
]
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Use it in code:
|
|
148
|
+
|
|
149
|
+
```javascript
|
|
150
|
+
import { StdioBus } from '@stdiobus/node';
|
|
151
|
+
|
|
152
|
+
const bus = new StdioBus({ configPath: './config.json' });
|
|
153
|
+
await bus.start();
|
|
154
|
+
|
|
155
|
+
// Send ACP initialize request
|
|
156
|
+
const result = await bus.request('initialize', {
|
|
157
|
+
protocolVersion: 1,
|
|
158
|
+
clientInfo: { name: 'my-app', version: '1.0.0' },
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
console.log(result.agentInfo.name); // 'openai-agent'
|
|
162
|
+
console.log(result.authMethods); // [{ id: 'oauth2', ... }]
|
|
163
|
+
|
|
164
|
+
await bus.stop();
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Or run any other worker the same way — just change the pool config:
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
{
|
|
171
|
+
"pools": [
|
|
172
|
+
{
|
|
173
|
+
"id": "acp-registry",
|
|
174
|
+
"command": "npx",
|
|
175
|
+
"args": ["@stdiobus/workers-registry", "acp-registry"],
|
|
176
|
+
"instances": 1
|
|
177
|
+
}
|
|
178
|
+
]
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
See [`@stdiobus/node` on npm](https://www.npmjs.com/package/@stdiobus/node) for TCP mode, Unix socket mode, Docker backend, and full API reference.
|
|
183
|
+
|
|
184
|
+
### Option B: Docker / Binary (TCP mode)
|
|
185
|
+
|
|
186
|
+
#### 1. Install Package
|
|
115
187
|
|
|
116
188
|
```bash
|
|
117
189
|
npm install @stdiobus/workers-registry
|
|
118
190
|
```
|
|
119
191
|
|
|
120
|
-
|
|
192
|
+
#### 2. Get stdio Bus kernel
|
|
121
193
|
|
|
122
194
|
**Option A: Using Docker (recommended)**
|
|
123
195
|
|
|
@@ -129,7 +201,7 @@ docker pull stdiobus/stdiobus:latest
|
|
|
129
201
|
|
|
130
202
|
See [stdio Bus kernel repository](https://github.com/stdiobus/stdiobus) for build instructions.
|
|
131
203
|
|
|
132
|
-
|
|
204
|
+
#### 3. Run with ACP Registry (recommended for real agents)
|
|
133
205
|
|
|
134
206
|
**Create config.json:**
|
|
135
207
|
```json
|
|
@@ -155,7 +227,7 @@ or pass a custom config file (third arg to `launch acp-registry`) with an absolu
|
|
|
155
227
|
Use the same Docker/binary commands below (they run `config.json`), and ensure
|
|
156
228
|
`api-keys.json` is mounted into the container when using Docker.
|
|
157
229
|
|
|
158
|
-
|
|
230
|
+
#### 4. Run with ACP Worker
|
|
159
231
|
|
|
160
232
|
**Note:** `acp-worker` is a standalone ACP agent for SDK/protocol testing. It does **not**
|
|
161
233
|
route to the ACP Registry. Use `acp-registry` when you need real registry agents.
|
|
@@ -191,7 +263,7 @@ docker run -p 9000:9000 \
|
|
|
191
263
|
./stdio_bus --config config.json --tcp 0.0.0.0:9000
|
|
192
264
|
```
|
|
193
265
|
|
|
194
|
-
|
|
266
|
+
#### 5. Test Connection
|
|
195
267
|
|
|
196
268
|
```bash
|
|
197
269
|
# ACP worker (standalone)
|
|
@@ -210,8 +282,8 @@ echo '{"jsonrpc":"2.0","id":"1","method":"initialize","params":{"agentId":"claud
|
|
|
210
282
|
The simplest way to run any worker:
|
|
211
283
|
|
|
212
284
|
```bash
|
|
213
|
-
# Run any worker by name (
|
|
214
|
-
|
|
285
|
+
# Run any worker by name (recommended)
|
|
286
|
+
npx @stdiobus/workers-registry <worker-name>
|
|
215
287
|
|
|
216
288
|
# Run any worker by name (this repo, after build)
|
|
217
289
|
node ./launch/index.js <worker-name>
|
|
@@ -222,9 +294,10 @@ node ./launch/index.js <worker-name>
|
|
|
222
294
|
# - echo-worker
|
|
223
295
|
# - mcp-echo-server
|
|
224
296
|
# - mcp-to-acp-proxy
|
|
297
|
+
# - openai-agent
|
|
225
298
|
|
|
226
299
|
# Example: Run echo worker for testing
|
|
227
|
-
|
|
300
|
+
npx @stdiobus/workers-registry echo-worker
|
|
228
301
|
```
|
|
229
302
|
|
|
230
303
|
### Using in stdio Bus Configuration
|
|
@@ -814,14 +887,35 @@ rl.on('close', () => process.exit(0));
|
|
|
814
887
|
|
|
815
888
|
```
|
|
816
889
|
workers-registry/
|
|
890
|
+
├── index.ts # Package entry point
|
|
891
|
+
├── launch/ # Universal launcher (npx entry point)
|
|
817
892
|
├── acp-worker/ # Full ACP protocol implementation
|
|
818
893
|
│ ├── src/
|
|
819
894
|
│ │ ├── agent.ts # ACP Agent implementation
|
|
820
895
|
│ │ ├── index.ts # Main entry point
|
|
896
|
+
│ │ ├── acp/ # ACP protocol layer
|
|
821
897
|
│ │ ├── mcp/ # MCP server integration
|
|
898
|
+
│ │ ├── mcp-proxy/ # MCP-to-ACP proxy logic
|
|
822
899
|
│ │ ├── session/ # Session management
|
|
823
|
-
│ │
|
|
900
|
+
│ │ ├── stdio/ # Session ID routing
|
|
901
|
+
│ │ └── test-utils/ # Testing utilities
|
|
824
902
|
│ └── tests/ # Test suites
|
|
903
|
+
├── registry-launcher/ # Registry Launcher (agent discovery + routing)
|
|
904
|
+
│ └── src/
|
|
905
|
+
│ ├── auth/ # OAuth 2.1 authentication
|
|
906
|
+
│ ├── config/ # Configuration management
|
|
907
|
+
│ ├── registry/ # ACP Registry resolution
|
|
908
|
+
│ ├── router/ # Message routing
|
|
909
|
+
│ ├── runtime/ # Agent runtime management
|
|
910
|
+
│ ├── stream/ # NDJSON stream handling
|
|
911
|
+
│ └── test-utils/ # Testing utilities
|
|
912
|
+
├── openai-agent/ # OpenAI Chat Completions API agent
|
|
913
|
+
│ └── src/
|
|
914
|
+
│ ├── agent.ts # ACP Agent bridging to OpenAI API
|
|
915
|
+
│ ├── client.ts # Chat Completions HTTP + SSE client
|
|
916
|
+
│ ├── sse-parser.ts # SSE line parser
|
|
917
|
+
│ ├── session.ts # Session state management
|
|
918
|
+
│ └── config.ts # Environment-based configuration
|
|
825
919
|
├── acp-registry/ # ACP Registry worker entrypoint + configs
|
|
826
920
|
├── echo-worker/ # Simple echo worker example
|
|
827
921
|
├── mcp-echo-server/ # MCP server example
|
|
@@ -884,8 +978,8 @@ nvm install 20 # If using nvm
|
|
|
884
978
|
**Permission errors:**
|
|
885
979
|
```bash
|
|
886
980
|
npm install -g @stdiobus/workers-registry # Global install (may need sudo)
|
|
887
|
-
# Or use
|
|
888
|
-
|
|
981
|
+
# Or use npx
|
|
982
|
+
npx @stdiobus/workers-registry acp-worker
|
|
889
983
|
```
|
|
890
984
|
|
|
891
985
|
### Runtime Issues
|
|
@@ -958,27 +1052,27 @@ Registry Launcher supports OAuth 2.1 with PKCE for secure browser-based authenti
|
|
|
958
1052
|
|
|
959
1053
|
| Provider | OAuth 2.1 | API Key | Status |
|
|
960
1054
|
|----------|-----------|---------|--------|
|
|
961
|
-
| OpenAI |
|
|
962
|
-
| Anthropic |
|
|
963
|
-
| GitHub |
|
|
964
|
-
| Google |
|
|
965
|
-
| Azure AD |
|
|
966
|
-
| AWS Cognito |
|
|
1055
|
+
| OpenAI | ✓ | ✓ | Production |
|
|
1056
|
+
| Anthropic | ✓ | ✓ | Production |
|
|
1057
|
+
| GitHub | ✓ | ✓ | Production |
|
|
1058
|
+
| Google | ✓ | ✓ | Production |
|
|
1059
|
+
| Azure AD | ✓ | ✓ | Production |
|
|
1060
|
+
| AWS Cognito | ✓ | ✓ | Production |
|
|
967
1061
|
|
|
968
1062
|
### Quick Start
|
|
969
1063
|
|
|
970
1064
|
```bash
|
|
971
1065
|
# Check current authentication status
|
|
972
|
-
|
|
1066
|
+
npx @stdiobus/workers-registry acp-registry --auth-status
|
|
973
1067
|
|
|
974
1068
|
# Login with browser OAuth (opens browser)
|
|
975
|
-
|
|
1069
|
+
npx @stdiobus/workers-registry acp-registry --login openai
|
|
976
1070
|
|
|
977
1071
|
# Interactive setup wizard
|
|
978
|
-
|
|
1072
|
+
npx @stdiobus/workers-registry acp-registry --setup
|
|
979
1073
|
|
|
980
1074
|
# Logout from all providers
|
|
981
|
-
|
|
1075
|
+
npx @stdiobus/workers-registry acp-registry --logout
|
|
982
1076
|
```
|
|
983
1077
|
|
|
984
1078
|
### Backward Compatibility
|
|
@@ -1001,7 +1095,7 @@ echo '{"claude-acp":{"apiKey":"sk-..."}}' > api-keys.json
|
|
|
1001
1095
|
export ANTHROPIC_API_KEY=sk-...
|
|
1002
1096
|
|
|
1003
1097
|
# Option 3: Interactive setup (if TTY available)
|
|
1004
|
-
|
|
1098
|
+
npx @stdiobus/workers-registry acp-registry --setup
|
|
1005
1099
|
```
|
|
1006
1100
|
|
|
1007
1101
|
### Documentation
|
|
@@ -1018,6 +1112,7 @@ node ./launch/index.js acp-registry --setup
|
|
|
1018
1112
|
## Resources
|
|
1019
1113
|
|
|
1020
1114
|
- [stdio Bus kernel](https://github.com/stdiobus/stdiobus) - Core protocol and daemon (source code)
|
|
1115
|
+
- [`@stdiobus/node`](https://www.npmjs.com/package/@stdiobus/node) - Embedded Node.js binding (no Docker/binary needed)
|
|
1021
1116
|
- [stdio Bus on Docker Hub](https://hub.docker.com/r/stdiobus/stdiobus) - Docker images for easy deployment
|
|
1022
1117
|
- [stdio Bus Full Documentation](https://stdiobus.com) – Core protocol documentation
|
|
1023
1118
|
- [ACP Registry](https://cdn.agentclientprotocol.com/registry/v1/latest/registry.json) - Available ACP agents
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
var __getOwnPropNames=Object.getOwnPropertyNames;var __esm=(fn,res)=>function __init(){return fn&&(res=(0,fn[__getOwnPropNames(fn)[0]])(fn=0)),res};var DEFAULT_CONFIG;var init_types=__esm({"workers-registry/registry-launcher/src/config/types.ts"(){"use strict";DEFAULT_CONFIG={registryUrl:"https://cdn.agentclientprotocol.com/registry/v1/latest/registry.json",apiKeysPath:"./api-keys.json",shutdownTimeoutSec:5}}});import{readFileSync}from"node:fs";function logWarning(message){const timestamp=new Date().toISOString();console.error(`[${timestamp}] [WARN] [config] ${message}`)}function isNonEmptyString(value){return typeof value==="string"&&value.length>0}function isPositiveNumber(value){return typeof value==="number"&&value>0&&Number.isFinite(value)}function parseConfigObject(obj){const config={...DEFAULT_CONFIG};if(obj===null||typeof obj!=="object"){logWarning("Config file does not contain a valid object, using defaults");return config}const rawConfig=obj;if("registryUrl"in rawConfig){if(isNonEmptyString(rawConfig.registryUrl)){config.registryUrl=rawConfig.registryUrl}else{logWarning('Config field "registryUrl" is not a valid string, using default')}}if("apiKeysPath"in rawConfig){if(isNonEmptyString(rawConfig.apiKeysPath)){config.apiKeysPath=rawConfig.apiKeysPath}else{logWarning('Config field "apiKeysPath" is not a valid string, using default')}}if("shutdownTimeoutSec"in rawConfig){if(isPositiveNumber(rawConfig.shutdownTimeoutSec)){config.shutdownTimeoutSec=rawConfig.shutdownTimeoutSec}else{logWarning('Config field "shutdownTimeoutSec" is not a valid positive number, using default')}}if("customAgentsPath"in rawConfig){if(isNonEmptyString(rawConfig.customAgentsPath)){config.customAgentsPath=rawConfig.customAgentsPath}else{logWarning('Config field "customAgentsPath" is not a valid string, ignoring')}}return config}function applyEnvironmentOverrides(config){const envRegistryUrl=process.env[ENV_REGISTRY_URL];const envApiKeysPath=process.env[ENV_API_KEYS_PATH];const envCustomAgentsPath=process.env[ENV_CUSTOM_AGENTS_PATH];const overrides={};if(isNonEmptyString(envRegistryUrl)){overrides.registryUrl=envRegistryUrl}if(isNonEmptyString(envApiKeysPath)){overrides.apiKeysPath=envApiKeysPath}if(isNonEmptyString(envCustomAgentsPath)){overrides.customAgentsPath=envCustomAgentsPath}return{...config,...overrides}}function loadConfig(configPath){let config={...DEFAULT_CONFIG};if(configPath){try{const fileContent=readFileSync(configPath,"utf-8");const parsed=JSON.parse(fileContent);config=parseConfigObject(parsed)}catch(error){if(error instanceof SyntaxError){logWarning(`Config file "${configPath}" contains malformed JSON, using defaults`)}else if(error.code==="ENOENT"){logWarning(`Config file "${configPath}" not found, using defaults`)}else if(error.code==="EACCES"){logWarning(`Config file "${configPath}" is not readable, using defaults`)}else{logWarning(`Failed to read config file "${configPath}": ${error.message}, using defaults`)}}}config=applyEnvironmentOverrides(config);return config}var ENV_REGISTRY_URL,ENV_API_KEYS_PATH,ENV_CUSTOM_AGENTS_PATH;var init_config=__esm({"workers-registry/registry-launcher/src/config/config.ts"(){"use strict";init_types();ENV_REGISTRY_URL="ACP_REGISTRY_URL";ENV_API_KEYS_PATH="ACP_API_KEYS_PATH";ENV_CUSTOM_AGENTS_PATH="ACP_CUSTOM_AGENTS_PATH"}});import{readFileSync as readFileSync2}from"node:fs";function logWarning2(message){const timestamp=new Date().toISOString();console.error(`[${timestamp}] [WARN] [api-keys] ${message}`)}function logInfo(message){const timestamp=new Date().toISOString();console.error(`[${timestamp}] [INFO] [api-keys] ${message}`)}function loadApiKeys(apiKeysPath){try{const fileContent=readFileSync2(apiKeysPath,"utf-8");const parsed=JSON.parse(fileContent);if(!parsed.agents||typeof parsed.agents!=="object"){logWarning2(`API keys file "${apiKeysPath}" does not contain valid "agents" object`);return{}}const agentsWithKeys=Object.entries(parsed.agents).filter(([_,keys])=>keys.apiKey&&keys.apiKey.length>0);logInfo(`Loaded API keys for ${agentsWithKeys.length} agents from "${apiKeysPath}"`);return parsed.agents}catch(error){if(error.code==="ENOENT"){logWarning2(`API keys file "${apiKeysPath}" not found, agents will not be authenticated`)}else if(error instanceof SyntaxError){logWarning2(`API keys file "${apiKeysPath}" contains malformed JSON`)}else{logWarning2(`Failed to read API keys file "${apiKeysPath}": ${error.message}`)}return{}}}function getAgentApiKey(apiKeys,agentId){const keys=apiKeys[agentId];if(!keys||!keys.apiKey||keys.apiKey.length===0){return void 0}return keys.apiKey}function getAgentEnv(apiKeys,agentId){const keys=apiKeys[agentId];if(!keys||!keys.env){return{}}return keys.env}var init_api_keys=__esm({"workers-registry/registry-launcher/src/config/api-keys.ts"(){"use strict"}});function getCurrentPlatform(){const platform=process.platform;const arch=process.arch;if(platform==="darwin"&&arch==="arm64")return"darwin-aarch64";if(platform==="darwin"&&arch==="x64")return"darwin-x86_64";if(platform==="linux"&&arch==="arm64")return"linux-aarch64";if(platform==="linux"&&arch==="x64")return"linux-x86_64";if(platform==="win32"&&arch==="arm64")return"windows-aarch64";if(platform==="win32"&&arch==="x64")return"windows-x86_64";return"linux-x86_64"}function resolveBinary(distribution,agentId){const currentPlatform=getCurrentPlatform();const target=distribution[currentPlatform];if(!target){throw new PlatformNotSupportedError(agentId,currentPlatform)}return{command:target.cmd,args:target.args??[],env:target.env}}function resolveNpx(distribution){return{command:"npx",args:[distribution.package,...distribution.args??[]],env:distribution.env}}function resolveUvx(distribution){return{command:"uvx",args:[distribution.package,...distribution.args??[]],env:distribution.env}}function resolve(distribution,agentId){if(distribution.npx){return resolveNpx(distribution.npx)}if(distribution.uvx){return resolveUvx(distribution.uvx)}if(distribution.binary){return resolveBinary(distribution.binary,agentId)}throw new NoDistributionError(agentId)}var PlatformNotSupportedError,NoDistributionError;var init_resolver=__esm({"workers-registry/registry-launcher/src/registry/resolver.ts"(){"use strict";PlatformNotSupportedError=class extends Error{constructor(agentId,platform){super(`Platform not supported: ${platform} for agent ${agentId}`);this.agentId=agentId;this.platform=platform;this.name="PlatformNotSupportedError"}};NoDistributionError=class extends Error{constructor(agentId){super(`No supported distribution type for agent ${agentId}`);this.agentId=agentId;this.name="NoDistributionError"}}}});import{readFileSync as readFileSync3}from"node:fs";function logError(message){const timestamp=new Date().toISOString();console.error(`[${timestamp}] [ERROR] [registry] ${message}`)}function logInfo2(message){const timestamp=new Date().toISOString();console.error(`[${timestamp}] [INFO] [registry] ${message}`)}function isNonEmptyString2(value){return typeof value==="string"&&value.length>0}function isValidDistribution(value){if(value===null||typeof value!=="object"){return false}const dist=value;const hasBinary=dist.binary!==void 0&&typeof dist.binary==="object";const hasNpx=dist.npx!==void 0&&typeof dist.npx==="object";const hasUvx=dist.uvx!==void 0&&typeof dist.uvx==="object";if(!hasBinary&&!hasNpx&&!hasUvx){return false}if(hasNpx){const npx=dist.npx;if(!isNonEmptyString2(npx.package)){return false}}if(hasUvx){const uvx=dist.uvx;if(!isNonEmptyString2(uvx.package)){return false}}return true}function parseMcpServer(value,agentIndex,serverIndex){if(value===null||typeof value!=="object"){logWarning3(`Agent at index ${agentIndex}: mcpServers[${serverIndex}] is not an object, skipping`);return null}const raw=value;if(!isNonEmptyString2(raw.name)){logWarning3(`Agent at index ${agentIndex}: mcpServers[${serverIndex}] has invalid or missing "name" field, skipping`);return null}if(!isNonEmptyString2(raw.command)){logWarning3(`Agent at index ${agentIndex}: mcpServers[${serverIndex}] has invalid or missing "command" field, skipping`);return null}const server={name:raw.name,command:raw.command};if(Array.isArray(raw.args)){server.args=raw.args.filter(a=>typeof a==="string")}if(raw.env!==null&&typeof raw.env==="object"&&!Array.isArray(raw.env)){const env={};for(const[key,val]of Object.entries(raw.env)){if(typeof val==="string"){env[key]=val}}if(Object.keys(env).length>0){server.env=env}}return server}function parseMcpServers(servers,agentIndex){const result=[];for(let i=0;i<servers.length;i++){const server=parseMcpServer(servers[i],agentIndex,i);if(server!==null){result.push(server)}}return result}function logWarning3(message){const timestamp=new Date().toISOString();console.error(`[${timestamp}] [WARN] [registry] ${message}`)}function parseAuthMethod(value,agentIndex,methodIndex){if(value===null||typeof value!=="object"){logWarning3(`Agent at index ${agentIndex}: authMethods[${methodIndex}] is not an object, skipping`);return null}const raw=value;if(!isNonEmptyString2(raw.id)){logWarning3(`Agent at index ${agentIndex}: authMethods[${methodIndex}] has invalid or missing "id" field, skipping`);return null}if(!isNonEmptyString2(raw.type)||!VALID_AUTH_METHOD_TYPES.includes(raw.type)){logWarning3(`Agent at index ${agentIndex}: authMethods[${methodIndex}] has invalid or missing "type" field (must be 'oauth2' or 'api-key'), skipping`);return null}const method={id:raw.id,type:raw.type};if(isNonEmptyString2(raw.providerId)){method.providerId=raw.providerId}return method}function parseAuthMethods(methods,agentIndex){const result=[];for(let i=0;i<methods.length;i++){const method=parseAuthMethod(methods[i],agentIndex,i);if(method!==null){result.push(method)}}return result}function parseAgent(value,index){if(value===null||typeof value!=="object"){throw new RegistryParseError(`Agent at index ${index} is not an object`)}const raw=value;if(!isNonEmptyString2(raw.id)){throw new RegistryParseError(`Agent at index ${index} has invalid or missing "id" field`)}if(!isNonEmptyString2(raw.name)){throw new RegistryParseError(`Agent at index ${index} has invalid or missing "name" field`)}if(!isNonEmptyString2(raw.version)){throw new RegistryParseError(`Agent at index ${index} has invalid or missing "version" field`)}if(!isValidDistribution(raw.distribution)){throw new RegistryParseError(`Agent at index ${index} has invalid or missing "distribution" field`)}const agent={id:raw.id,name:raw.name,version:raw.version,distribution:raw.distribution};if(typeof raw.description==="string"){agent.description=raw.description}if(typeof raw.repository==="string"){agent.repository=raw.repository}if(Array.isArray(raw.authors)){agent.authors=raw.authors.filter(a=>typeof a==="string")}if(typeof raw.license==="string"){agent.license=raw.license}if(typeof raw.icon==="string"){agent.icon=raw.icon}if(Array.isArray(raw.mcpServers)){const mcpServers=parseMcpServers(raw.mcpServers,index);if(mcpServers.length>0){agent.mcpServers=mcpServers}}if(typeof raw.authRequired==="boolean"){agent.authRequired=raw.authRequired}if(Array.isArray(raw.authMethods)){const authMethods=parseAuthMethods(raw.authMethods,index);if(authMethods.length>0){agent.authMethods=authMethods}}return agent}function parseRegistry(data){if(data===null||typeof data!=="object"){throw new RegistryParseError("Registry data is not an object")}const raw=data;if(!isNonEmptyString2(raw.version)){throw new RegistryParseError('Registry has invalid or missing "version" field')}if(!Array.isArray(raw.agents)){throw new RegistryParseError('Registry has invalid or missing "agents" field')}const agents=[];for(let i=0;i<raw.agents.length;i++){agents.push(parseAgent(raw.agents[i],i))}return{version:raw.version,agents}}function loadCustomAgents(filePath){let fileContent;try{fileContent=readFileSync3(filePath,"utf-8")}catch(error){if(error.code==="ENOENT"){throw new CustomAgentsLoadError(`Custom agents file not found: ${filePath}`)}if(error.code==="EACCES"){throw new CustomAgentsLoadError(`Custom agents file not readable: ${filePath}`)}throw new CustomAgentsLoadError(`Failed to read custom agents file "${filePath}": ${error.message}`,error)}let data;try{data=JSON.parse(fileContent)}catch(error){throw new CustomAgentsLoadError(`Custom agents file "${filePath}" contains malformed JSON: ${error.message}`,error)}if(data===null||typeof data!=="object"){throw new CustomAgentsLoadError(`Custom agents file "${filePath}" does not contain a valid object`)}const raw=data;if(!Array.isArray(raw.agents)){throw new CustomAgentsLoadError(`Custom agents file "${filePath}" does not contain a valid "agents" array`)}const registryData={version:"custom",agents:raw.agents};const parsed=parseRegistry(registryData);return parsed.agents}var ENV_REGISTRY_URL2,RegistryFetchError,RegistryParseError,AgentNotFoundError,VALID_AUTH_METHOD_TYPES,RegistryIndex,CustomAgentsLoadError;var init_registry=__esm({"workers-registry/registry-launcher/src/registry/index.ts"(){"use strict";init_resolver();init_resolver();ENV_REGISTRY_URL2="ACP_REGISTRY_URL";RegistryFetchError=class extends Error{constructor(message,cause){super(message);this.cause=cause;this.name="RegistryFetchError"}};RegistryParseError=class extends Error{constructor(message,cause){super(message);this.cause=cause;this.name="RegistryParseError"}};AgentNotFoundError=class extends Error{constructor(agentId){super(`Agent not found: ${agentId}`);this.agentId=agentId;this.name="AgentNotFoundError"}};VALID_AUTH_METHOD_TYPES=["oauth2","api-key"];RegistryIndex=class{registryUrl;registry=null;agentMap=new Map;authRequirementsCache=new Map;constructor(registryUrl){const envUrl=process.env[ENV_REGISTRY_URL2];this.registryUrl=isNonEmptyString2(envUrl)?envUrl:registryUrl}async fetch(){logInfo2(`Fetching registry from ${this.registryUrl}`);let response;try{response=await fetch(this.registryUrl)}catch(error){const message=`Failed to fetch registry from ${this.registryUrl}: ${error.message}`;logError(message);throw new RegistryFetchError(message,error)}if(!response.ok){const message=`Failed to fetch registry from ${this.registryUrl}: HTTP ${response.status} ${response.statusText}`;logError(message);throw new RegistryFetchError(message)}let text;try{text=await response.text()}catch(error){const message=`Failed to read registry response body: ${error.message}`;logError(message);throw new RegistryFetchError(message,error)}let data;try{data=JSON.parse(text)}catch(error){const message=`Failed to parse registry JSON: ${error.message}`;logError(message);throw new RegistryParseError(message,error)}try{this.registry=parseRegistry(data)}catch(error){if(error instanceof RegistryParseError){logError(error.message);throw error}const message=`Failed to validate registry data: ${error.message}`;logError(message);throw new RegistryParseError(message,error)}this.agentMap.clear();this.authRequirementsCache.clear();for(const agent of this.registry.agents){this.agentMap.set(agent.id,agent)}logInfo2(`Registry loaded: version ${this.registry.version}, ${this.registry.agents.length} agents`)}lookup(agentId){return this.agentMap.get(agentId)}resolve(agentId){const agent=this.lookup(agentId);if(!agent){throw new AgentNotFoundError(agentId)}return resolve(agent.distribution,agentId)}getRegistry(){return this.registry}getAuthRequirements(agentId){const cached=this.authRequirementsCache.get(agentId);if(cached!==void 0){return cached}const agent=this.lookup(agentId);if(!agent){return void 0}const authMethods=agent.authMethods??[];const hasOAuthMethods=authMethods.some(m=>m.type==="oauth2");const authRequired=agent.authRequired??hasOAuthMethods;let primaryOAuthProviderId;for(const method of authMethods){if(method.type==="oauth2"&&method.providerId){primaryOAuthProviderId=method.providerId;break}}const requirements={authRequired,authMethods,primaryOAuthProviderId};this.authRequirementsCache.set(agentId,requirements);if(authRequired){logInfo2(`Agent "${agentId}" requires authentication${primaryOAuthProviderId?` (OAuth provider: ${primaryOAuthProviderId})`:""}`)}return requirements}clearAuthRequirementsCache(agentId){if(agentId){this.authRequirementsCache.delete(agentId);logInfo2(`Cleared auth requirements cache for agent "${agentId}"`)}else{this.authRequirementsCache.clear();logInfo2("Cleared all auth requirements cache")}}mergeCustomAgents(agents){if(agents.length===0){return}if(!this.registry){this.registry={version:"custom",agents:[]}}for(const agent of agents){const existingIndex=this.registry.agents.findIndex(a=>a.id===agent.id);if(existingIndex!==-1){this.registry.agents[existingIndex]=agent;logInfo2(`Custom agent "${agent.id}" overrides remote registry entry`)}else{this.registry.agents.push(agent);logInfo2(`Custom agent "${agent.id}" added to registry`)}this.agentMap.set(agent.id,agent);this.authRequirementsCache.delete(agent.id)}logInfo2(`Registry now contains ${this.registry.agents.length} agents (${agents.length} custom)`)}};CustomAgentsLoadError=class extends Error{constructor(message,cause){super(message);this.cause=cause;this.name="CustomAgentsLoadError"}}}});function logError2(message){const timestamp=new Date().toISOString();console.error(`[${timestamp}] [ERROR] [ndjson] ${message}`)}var NDJSONHandler;var init_ndjson_handler=__esm({"workers-registry/registry-launcher/src/stream/ndjson-handler.ts"(){"use strict";NDJSONHandler=class{buffer="";output;messageCallback=null;errorCallback=null;constructor(output){this.output=output}onMessage(callback){this.messageCallback=callback}onError(callback){this.errorCallback=callback}write(message){if(!this.output.writable){return false}try{const json=JSON.stringify(message);this.output.write(json+"\n");return true}catch{return false}}processChunk(chunk){this.buffer+=chunk.toString("utf-8");this.processBuffer()}processBuffer(){let newlineIndex;while((newlineIndex=this.buffer.indexOf("\n"))!==-1){const line=this.buffer.slice(0,newlineIndex);this.buffer=this.buffer.slice(newlineIndex+1);if(line.trim().length===0){continue}this.parseLine(line)}}parseLine(line){try{const message=JSON.parse(line);if(message===null||typeof message!=="object"){const error=new Error("Parsed JSON is not an object");logError2(`Malformed NDJSON line (not an object): ${this.truncateLine(line)}`);this.errorCallback?.(error,line);return}this.messageCallback?.(message)}catch(error){logError2(`Failed to parse NDJSON line: ${this.truncateLine(line)}`);this.errorCallback?.(error,line)}}truncateLine(line,maxLength=100){if(line.length<=maxLength){return line}return line.slice(0,maxLength)+"..."}}}});import{spawn}from"child_process";var DEFAULT_TERMINATE_TIMEOUT_MS,AgentRuntimeImpl;var init_agent_runtime=__esm({"workers-registry/registry-launcher/src/runtime/agent-runtime.ts"(){"use strict";DEFAULT_TERMINATE_TIMEOUT_MS=5e3;AgentRuntimeImpl=class _AgentRuntimeImpl{agentId;state;process;onExitCallback;constructor(agentId,process2,onExit){this.agentId=agentId;this.process=process2;this.state="starting";this.onExitCallback=onExit;this.setupProcessHandlers()}static spawn(agentId,spawnCommand,onExit){const childProcess=spawn(spawnCommand.command,spawnCommand.args,{stdio:["pipe","pipe","pipe"],env:{...process.env,...spawnCommand.env},detached:false});if(childProcess.stdout){childProcess.stdout.setEncoding("utf8")}if(childProcess.stderr){childProcess.stderr.setEncoding("utf8")}return new _AgentRuntimeImpl(agentId,childProcess,onExit)}setupProcessHandlers(){this.process.on("spawn",()=>{if(this.state==="starting"){this.state="running"}});this.process.on("error",error=>{this.state="stopped";process.stderr.write(`[${new Date().toISOString()}] ERROR: Agent ${this.agentId} process error: ${error.message}
|
|
3
3
|
`)});this.process.on("exit",(code,signal)=>{this.state="stopped";if(this.onExitCallback){this.onExitCallback(code,signal)}});if(this.process.stdin){this.process.stdin.on("error",error=>{process.stderr.write(`[${new Date().toISOString()}] WARN: Agent ${this.agentId} stdin error: ${error.message}
|
|
4
|
-
`)})}}write(message){if(this.state!=="running"&&this.state!=="starting"){return false}if(!this.process.stdin||this.process.stdin.destroyed){return false}try{const ndjsonLine=JSON.stringify(message)+"\n";return this.process.stdin.write(ndjsonLine)}catch{return false}}async terminate(timeout=DEFAULT_TERMINATE_TIMEOUT_MS){if(this.state==="stopped"){return}if(this.state==="stopping"){return this.waitForExit()}this.state="stopping";if(this.process.stdin&&!this.process.stdin.destroyed){this.process.stdin.end()}this.process.kill("SIGTERM");const exitPromise=this.waitForExit();const timeoutPromise=new Promise(resolve2=>{setTimeout(()=>resolve2("timeout"),timeout)});const result=await Promise.race([exitPromise,timeoutPromise]);if(result==="timeout"&&!this.process.killed&&this.process.exitCode===null){this.process.kill("SIGKILL");await this.waitForExit()}}waitForExit(){if(this.state==="stopped"){return Promise.resolve()}return new Promise(resolve2=>{this.process.once("exit",()=>{resolve2()})})}}}});var DEFAULT_SHUTDOWN_TIMEOUT_MS,AgentRuntimeManager;var init_manager=__esm({"workers-registry/registry-launcher/src/runtime/manager.ts"(){"use strict";init_agent_runtime();DEFAULT_SHUTDOWN_TIMEOUT_MS=5e3;AgentRuntimeManager=class{runtimes=new Map;exitCallbacks=[];async getOrSpawn(agentId,spawnCommand){const existing=this.runtimes.get(agentId);if(existing&&existing.state!=="stopped"){return existing}const runtime=AgentRuntimeImpl.spawn(agentId,spawnCommand,(code,_signal)=>{this.handleAgentExit(agentId,code)});this.runtimes.set(agentId,runtime);return runtime}get(agentId){return this.runtimes.get(agentId)}async terminate(agentId,timeout=DEFAULT_SHUTDOWN_TIMEOUT_MS){const runtime=this.runtimes.get(agentId);if(!runtime){return}await runtime.terminate(timeout);this.runtimes.delete(agentId)}async terminateAll(timeout=DEFAULT_SHUTDOWN_TIMEOUT_MS){const terminatePromises=[];for(const[agentId,runtime]of this.runtimes){if(runtime.state!=="stopped"){terminatePromises.push(runtime.terminate(timeout).then(()=>{this.runtimes.delete(agentId)}))}}await Promise.all(terminatePromises)}onAgentExit(callback){this.exitCallbacks.push(callback)}handleAgentExit(agentId,code){this.runtimes.delete(agentId);for(const callback of this.exitCallbacks){try{callback(agentId,code)}catch{}}}get size(){return this.runtimes.size}has(agentId){return this.runtimes.has(agentId)}}}});function isValidAuthMethodType(value){return typeof value==="string"&&VALID_AUTH_METHOD_TYPES2.includes(value)}function isValidProviderId(value){return typeof value==="string"&&VALID_PROVIDER_IDS.includes(value)}var VALID_AUTH_METHOD_TYPES2,DEFAULT_AUTH_METHOD_PRECEDENCE,VALID_PROVIDER_IDS,AUTH_METHOD_ID_TO_PROVIDER_ID,VALID_AUTH_METHOD_IDS;var init_types2=__esm({"workers-registry/registry-launcher/src/auth/types.ts"(){"use strict";VALID_AUTH_METHOD_TYPES2=["oauth2","api-key"];DEFAULT_AUTH_METHOD_PRECEDENCE={methodPrecedence:["oauth2","api-key"],failFastOnUnsupported:true,failFastOnAmbiguous:true};VALID_PROVIDER_IDS=["github","google","cognito","azure","oidc"];AUTH_METHOD_ID_TO_PROVIDER_ID={"oauth2-github":"github","oauth2-google":"google","oauth2-cognito":"cognito","oauth2-azure":"azure","oauth2-oidc":"oidc","github":"github","google":"google","cognito":"cognito","azure":"azure","oidc":"oidc"};VALID_AUTH_METHOD_IDS=Object.keys(AUTH_METHOD_ID_TO_PROVIDER_ID)}});import{spawn as spawn2}from"node:child_process";function parseAuthMethods2(raw){if(!Array.isArray(raw)){logError3("authMethods is not an array, skipping parsing");return[]}const methods=raw.slice(0,MAX_AUTH_METHODS);const parsed=[];const seenIds=new Set;for(const method of methods){const result=parseAuthMethod2(method,seenIds);if(result){parsed.push(result);seenIds.add(result.id)}}logInfo3(`Parsed ${parsed.length} valid auth methods from ${methods.length} raw methods`);return parsed}function parseAuthMethod2(method,seenIds){if(method===null||typeof method!=="object"){return null}const obj=method;const id=obj.id;if(typeof id!=="string"||id.length===0||id.length>MAX_METHOD_ID_LENGTH){logError3(`Invalid auth method id: ${typeof id==="string"?id.substring(0,50):typeof id}`);return null}if(seenIds.has(id)){logInfo3(`Skipping duplicate auth method id: ${id}`);return null}const type=obj.type;if(typeof type!=="string"||!VALID_AUTH_METHOD_TYPES3.includes(type)){logError3(`Invalid auth method type for id ${id}: ${type}`);return null}const rawProviderId=obj.providerId;let providerId;if(rawProviderId!==void 0){if(isValidProviderId(rawProviderId)){providerId=rawProviderId}else{logError3(`Invalid providerId in auth method ${id}: ${rawProviderId}`)}}const mappedProviderId=AUTH_METHOD_ID_TO_PROVIDER[id];if(providerId&&mappedProviderId&&providerId!==mappedProviderId){logError3(`Conflict: auth method ${id} has providerId ${providerId} but maps to ${mappedProviderId}, rejecting`);return null}const resolvedProviderId=providerId??mappedProviderId;if(type==="oauth2"){if(!resolvedProviderId){logError3(`OAuth auth method ${id} has no valid providerId, skipping`);return null}return{kind:"oauth2",id,providerId:resolvedProviderId}}if(type==="agent"){return{kind:"agent",id,providerId:resolvedProviderId}}if(type==="terminal"){const args=Array.isArray(obj.args)?obj.args.filter(a=>typeof a==="string"):void 0;const env=obj.env&&typeof obj.env==="object"&&!Array.isArray(obj.env)?Object.fromEntries(Object.entries(obj.env).filter(([,v])=>typeof v==="string")):void 0;return{kind:"terminal",id,args,env}}if(type==="api-key"){return{kind:"api-key",id,providerId:resolvedProviderId}}return null}function getOAuthMethods(methods){return methods.filter(m=>m.kind==="oauth2")}function getAgentAuthMethods(methods){return methods.filter(m=>m.kind==="agent")}function getTerminalAuthMethods(methods){return methods.filter(m=>m.kind==="terminal")}function getApiKeyMethods(methods){return methods.filter(m=>m.kind==="api-key")}function logError3(message){const timestamp=new Date().toISOString();console.error(`[${timestamp}] [ERROR] [router] ${message}`)}function logInfo3(message){const timestamp=new Date().toISOString();console.error(`[${timestamp}] [INFO] [router] ${message}`)}function createErrorResponse(id,code,message,data){const response={jsonrpc:"2.0",id,error:{code,message}};if(data!==void 0){response.error.data=data}return response}function extractAgentId(message){const msg=message;const agentId=msg.agentId;if(typeof agentId==="string"&&agentId.length>0){return agentId}return void 0}function extractId(message){const msg=message;const id=msg.id;if(typeof id==="string"||typeof id==="number"){return id}return null}function transformMessage(message){const{agentId:_,...rest}=message;return rest}var RoutingErrorCodes,AUTH_METHOD_ID_TO_PROVIDER,MAX_AUTH_METHODS,MAX_METHOD_ID_LENGTH,VALID_AUTH_METHOD_TYPES3,AGENT_AUTH_TIMEOUT_MS,TERMINAL_AUTH_TIMEOUT_MS,QUEUED_REQUEST_TIMEOUT_MS,MessageRouter;var init_message_router=__esm({"workers-registry/registry-launcher/src/router/message-router.ts"(){"use strict";init_registry();init_api_keys();init_types2();RoutingErrorCodes={MISSING_AGENT_ID:-32600,AGENT_NOT_FOUND:-32001,PLATFORM_NOT_SUPPORTED:-32002,SPAWN_FAILED:-32003,AUTH_REQUIRED:-32004};AUTH_METHOD_ID_TO_PROVIDER={"oauth2-github":"github","oauth2-google":"google","oauth2-cognito":"cognito","oauth2-azure":"azure","agent-github":"github","agent-google":"google","agent-cognito":"cognito","agent-azure":"azure","github-api-key":"github","google-api-key":"google","azure-api-key":"azure","cognito-api-key":"cognito"};MAX_AUTH_METHODS=50;MAX_METHOD_ID_LENGTH=128;VALID_AUTH_METHOD_TYPES3=["oauth2","agent","terminal","api-key"];AGENT_AUTH_TIMEOUT_MS=5*60*1e3;TERMINAL_AUTH_TIMEOUT_MS=10*60*1e3;QUEUED_REQUEST_TIMEOUT_MS=5*60*1e3;MessageRouter=class{registry;runtimeManager;writeCallback;apiKeys;spawnFn;isStdinTTY;isStdoutTTY;authManager;pendingRequests=new Map;authState=new Map;agentOAuthRequirements=new Map;requestQueue=new Map;pendingAuthenticateRequests=new Map;sessionIdMap=new Map;autoOAuth;constructor(registry,runtimeManager,writeCallback,apiKeys={},authManager,autoOAuth,deps){this.registry=registry;this.runtimeManager=runtimeManager;this.writeCallback=writeCallback;this.apiKeys=apiKeys;this.authManager=authManager;this.autoOAuth=autoOAuth??this.getAutoOAuthFromEnv();this.spawnFn=deps?.spawnFn??spawn2;this.isStdinTTY=deps?.isStdinTTY??(()=>process.stdin.isTTY??false);this.isStdoutTTY=deps?.isStdoutTTY??(()=>process.stdout.isTTY??false)}getAutoOAuthFromEnv(){const envValue=process.env.AUTH_AUTO_OAUTH;return envValue==="true"||envValue==="1"||envValue==="yes"}getSupportedAuthMethods(){const methods=[{id:"api-key",type:"api-key"}];if(this.authManager){methods.push({id:"oauth2-github",type:"oauth2",providerId:"github"},{id:"oauth2-google",type:"oauth2",providerId:"google"},{id:"oauth2-cognito",type:"oauth2",providerId:"cognito"},{id:"oauth2-azure",type:"oauth2",providerId:"azure"},{id:"oauth2-oidc",type:"oauth2",providerId:"oidc"})}return methods}async hasAuthenticationForAgent(agentId){if(this.authManager){const token=await this.authManager.getTokenForAgent(agentId);if(token){return true}}const apiKey=getAgentApiKey(this.apiKeys,agentId);return apiKey!==void 0}hasCredentialsForAgent(agentId){const apiKey=getAgentApiKey(this.apiKeys,agentId);return apiKey!==void 0}createAuthRequiredError(id,agentId,requiredMethod){const remediation={type:"login_required",commands:["npx @stdiobus/workers-registry acp-registry --setup","stdiobus acp-registry --setup"],hint:"Run: npx @stdiobus/workers-registry acp-registry --setup",docsUrl:"https://github.com/stdiobus/workers-registry/blob/main/docs/oauth/user-guide.md"};return createErrorResponse(id,RoutingErrorCodes.AUTH_REQUIRED,"Authentication required",{agentId,requiredMethod:requiredMethod??"api-key",supportedMethods:this.getSupportedAuthMethods().map(m=>m.id),remediation})}async injectAuthentication(agentId,message){if(this.authManager){return this.authManager.injectAuth(agentId,message)}return message}injectMcpServers(message,agentId){const agent=this.registry.lookup(agentId);if(!agent?.mcpServers||agent.mcpServers.length===0){return message}const msg=message;const params=msg.params||{};const existingServers=Array.isArray(params.mcpServers)?params.mcpServers:[];const registryServers=agent.mcpServers.map(server=>({name:server.name,command:server.command,args:server.args,env:server.env?Object.entries(server.env).map(([name,value])=>({name,value})):void 0}));const existingNames=new Set(existingServers.filter(s=>s!==null&&typeof s==="object").map(s=>s.name).filter(n=>typeof n==="string"));const mergedServers=[...registryServers.filter(s=>!existingNames.has(s.name)),...existingServers];logInfo3(`Injecting ${registryServers.length} MCP servers from registry for agent ${agentId}`);return{...msg,params:{...params,mcpServers:mergedServers}}}async route(message){const id=extractId(message);const agentId=extractAgentId(message);if(agentId===void 0){logError3("Missing agentId in request");return createErrorResponse(id,RoutingErrorCodes.MISSING_AGENT_ID,"Missing agentId")}const currentAuthState=this.getAuthState(agentId);if(currentAuthState==="pending"){logInfo3(`OAuth flow pending for agent ${agentId}, queueing request (id=${id})`);return this.queueRequest(agentId,message)}if(currentAuthState==="failed"){logError3(`Authentication failed for agent ${agentId}, returning AUTH_REQUIRED`);const requiredProviderId2=this.agentOAuthRequirements.get(agentId);return this.createAuthRequiredErrorWithProvider(id,agentId,requiredProviderId2)}const requiredProviderId=this.agentOAuthRequirements.get(agentId);if(requiredProviderId&¤tAuthState!=="authenticated"){const hasCredentials=await this.hasOAuthCredentialsForAgent(agentId,requiredProviderId);if(!hasCredentials){logError3(`Agent ${agentId} requires OAuth (provider: ${requiredProviderId}) but credentials not available`);return this.createAuthRequiredErrorWithProvider(id,agentId,requiredProviderId)}}return this.routeInternal(message,agentId,id)}async hasOAuthCredentialsForAgent(agentId,providerId){if(!this.authManager){return false}const token=await this.authManager.getTokenForAgent(agentId,providerId);return token!==null&&token!==void 0}createAuthRequiredErrorWithProvider(id,agentId,providerId){const supportedMethods=this.getSupportedAuthMethods();const remediation={type:"login_required",provider:providerId||"unknown",commands:providerId?[`npx @stdiobus/workers-registry acp-registry --login ${providerId}`,`stdiobus acp-registry --login ${providerId}`]:["npx @stdiobus/workers-registry acp-registry --setup","stdiobus acp-registry --setup"],hint:providerId?`Run: npx @stdiobus/workers-registry acp-registry --login ${providerId}`:"Run: npx @stdiobus/workers-registry acp-registry --setup",docsUrl:"https://github.com/stdiobus/workers-registry/blob/main/docs/oauth/user-guide.md"};return createErrorResponse(id,RoutingErrorCodes.AUTH_REQUIRED,"Authentication required",{agentId,requiredMethod:providerId?`oauth2-${providerId}`:"oauth2",supportedMethods:supportedMethods.map(m=>m.id),providerId,remediation})}async routeInternal(message,agentId,id){let spawnCommand;try{spawnCommand=this.registry.resolve(agentId)}catch(error){if(error instanceof AgentNotFoundError){logError3(`Agent not found: ${agentId}`);return createErrorResponse(id,RoutingErrorCodes.AGENT_NOT_FOUND,"Agent not found",{agentId})}if(error instanceof PlatformNotSupportedError){logError3(`Platform not supported for agent: ${agentId}`);return createErrorResponse(id,RoutingErrorCodes.PLATFORM_NOT_SUPPORTED,"Platform not supported",{agentId,platform:error.platform})}throw error}const agentEnv=getAgentEnv(this.apiKeys,agentId);if(Object.keys(agentEnv).length>0){spawnCommand={...spawnCommand,env:{...spawnCommand.env,...agentEnv}};logInfo3(`Injected ${Object.keys(agentEnv).length} env vars from api-keys.json for agent ${agentId}`)}let runtime;try{runtime=await this.runtimeManager.getOrSpawn(agentId,spawnCommand)}catch(error){logError3(`Failed to spawn agent ${agentId}: ${error.message}`);return createErrorResponse(id,RoutingErrorCodes.SPAWN_FAILED,"Agent spawn failed",{agentId,error:error.message})}if(id!==null){const msg2=message;const clientSessionId=typeof msg2.sessionId==="string"?msg2.sessionId:void 0;const method=typeof msg2.method==="string"?msg2.method:void 0;this.pendingRequests.set(id,{id,agentId,timestamp:Date.now(),method,clientSessionId})}let transformedMessage=transformMessage(message);const msg=message;if(msg.method==="session/new"){transformedMessage=this.injectMcpServers(transformedMessage,agentId)}transformedMessage=await this.injectAuthentication(agentId,transformedMessage);const success=runtime.write(transformedMessage);if(!success){logError3(`Failed to write to agent ${agentId}`);if(id!==null){this.pendingRequests.delete(id)}}else{logInfo3(`Routed message to agent ${agentId}`)}return void 0}getAuthState(agentId){return this.authState.get(agentId)??"none"}setAuthState(agentId,newState){const oldState=this.getAuthState(agentId);if(oldState===newState){return}logInfo3(`Auth state transition for ${agentId}: ${oldState} \u2192 ${newState}`);this.authState.set(agentId,newState);if(newState==="authenticated"&&oldState==="pending"){void this.processQueuedRequests(agentId)}else if(newState==="failed"&&oldState==="pending"){void this.rejectQueuedRequests(agentId)}}queueRequest(agentId,message){return new Promise(resolve2=>{const queuedRequest={message,queuedAt:Date.now(),resolve:resolve2};let queue=this.requestQueue.get(agentId);if(!queue){queue=[];this.requestQueue.set(agentId,queue)}queue.push(queuedRequest);logInfo3(`Queued request for agent ${agentId}, queue size: ${queue.length}`);setTimeout(()=>{this.handleQueuedRequestTimeout(agentId,queuedRequest)},QUEUED_REQUEST_TIMEOUT_MS)})}handleQueuedRequestTimeout(agentId,queuedRequest){const queue=this.requestQueue.get(agentId);if(!queue){return}const index=queue.indexOf(queuedRequest);if(index===-1){return}queue.splice(index,1);logError3(`Queued request timed out for agent ${agentId}`);const id=extractId(queuedRequest.message);queuedRequest.resolve(createErrorResponse(id,RoutingErrorCodes.AUTH_REQUIRED,"Authentication timeout",{agentId,reason:"OAuth flow timed out while request was queued"}))}async processQueuedRequests(agentId){const queue=this.requestQueue.get(agentId);if(!queue||queue.length===0){return}logInfo3(`Processing ${queue.length} queued requests for agent ${agentId}`);this.requestQueue.delete(agentId);for(const queuedRequest of queue){try{const id=extractId(queuedRequest.message);const result=await this.routeInternal(queuedRequest.message,agentId,id);queuedRequest.resolve(result)}catch(error){const id=extractId(queuedRequest.message);logError3(`Error processing queued request for ${agentId}: ${error.message}`);queuedRequest.resolve(createErrorResponse(id,RoutingErrorCodes.SPAWN_FAILED,"Failed to process queued request",{agentId,error:error.message}))}}logInfo3(`Completed processing queued requests for agent ${agentId}`)}async rejectQueuedRequests(agentId){const queue=this.requestQueue.get(agentId);if(!queue||queue.length===0){return}logInfo3(`Rejecting ${queue.length} queued requests for agent ${agentId} due to auth failure`);this.requestQueue.delete(agentId);for(const queuedRequest of queue){const id=extractId(queuedRequest.message);queuedRequest.resolve(this.createAuthRequiredError(id,agentId,"oauth2"))}}getQueuedRequestCount(agentId){const queue=this.requestQueue.get(agentId);return queue?.length??0}getTotalQueuedRequestCount(){let total=0;for(const queue of this.requestQueue.values()){total+=queue.length}return total}handleAgentResponse(agentId,response){const id=extractId(response);let msg=response;const method=typeof msg.method==="string"?msg.method:void 0;if(id!==null&&typeof id==="string"){const pendingAuth=this.pendingAuthenticateRequests.get(id);if(pendingAuth&&pendingAuth.agentId===agentId){this.handleAuthenticateResponse(pendingAuth,msg);return}}if(id!==null&&method){this.handleAgentRequest(agentId,id,method,msg);return}if(id!==null){const pending=this.pendingRequests.get(id);if(pending&&pending.agentId===agentId){const result=msg.result;const isInitializeResponse=pending.method==="initialize"&&result!==void 0;if(isInitializeResponse){const ourAuthMethods=this.getSupportedAuthMethods();const existingAuthMethods=Array.isArray(result.authMethods)?result.authMethods:[];const mergedAuthMethods=[...ourAuthMethods,...existingAuthMethods.filter(m=>!ourAuthMethods.some(our=>our.id===m.id))];msg={...msg,result:{...result,authMethods:mergedAuthMethods}};logInfo3(`Injected ${ourAuthMethods.length} auth methods into initialize response for ${agentId}`)}if(isInitializeResponse&&result&&Array.isArray(result.authMethods)&&result.authMethods.length>0){const parsedMethods=parseAuthMethods2(result.authMethods);if(parsedMethods.length>0){const oauthMethods=getOAuthMethods(parsedMethods);const apiKeyMethods=getApiKeyMethods(parsedMethods);const hasApiKeyCredentials=this.hasCredentialsForAgent(agentId);if(oauthMethods.length>0&&!(apiKeyMethods.length>0&&hasApiKeyCredentials)){const requiredProviderId=oauthMethods[0].providerId;this.agentOAuthRequirements.set(agentId,requiredProviderId);logInfo3(`Agent ${agentId} requires OAuth authentication with provider: ${requiredProviderId}`)}else if(apiKeyMethods.length>0&&hasApiKeyCredentials){logInfo3(`Agent ${agentId} supports OAuth but api-key credentials available, using api-key`)}if(this.autoOAuth){logInfo3(`Agent ${agentId} requires authentication, attempting auto-auth with ${parsedMethods.length} valid methods`);this.setAuthState(agentId,"pending");void this.attemptAuthentication(agentId,parsedMethods)}else{logInfo3(`Agent ${agentId} requires authentication but AUTH_AUTO_OAUTH is disabled. Use --login to authenticate.`)}}else{logError3(`Agent ${agentId} has authMethods but none are valid after parsing`);this.setAuthState(agentId,"none")}}if(result&&typeof result.sessionId==="string"){const agentSessionId=result.sessionId;const clientSessionId=pending.clientSessionId;if(clientSessionId){this.sessionIdMap.set(agentSessionId,clientSessionId);logInfo3(`Mapped agent sessionId ${agentSessionId} to client sessionId ${clientSessionId}`)}}this.pendingRequests.delete(id)}}if(id===null&&method){logInfo3(`Received notification: ${method}`);const params=msg.params;if(params&&typeof params.sessionId==="string"){const agentSessionId=params.sessionId;const clientSessionId=this.sessionIdMap.get(agentSessionId);if(clientSessionId){const enriched={...msg,sessionId:clientSessionId,params:{...params,sessionId:agentSessionId}};logInfo3(`Forwarding notification with mapped sessionId: ${clientSessionId}`);this.writeCallback(enriched);return}else{logError3(`Notification with unmapped agentSessionId: ${agentSessionId}, using default sessionId`);const enriched={...msg,sessionId:"global-notifications",params:{...params,sessionId:agentSessionId}};this.writeCallback(enriched);return}}else{const topLevelSessionId=msg.sessionId;if(topLevelSessionId){this.writeCallback(response);return}else{logError3(`Notification without sessionId: ${method}, adding default sessionId for routing`);const enriched={...msg,sessionId:"global-notifications"};this.writeCallback(enriched);return}}}this.writeCallback(msg)}handleAgentRequest(agentId,id,method,msg){logInfo3(`Agent ${agentId} sent request: ${method} (id=${id}), auto-responding`);let result;if(method==="session/request_permission"){result=this.buildPermissionResponse(msg)}else{logInfo3(`Unknown agent request method: ${method}, sending generic success`);result={}}const response={jsonrpc:"2.0",id,result};this.sendToAgent(agentId,response)}buildPermissionResponse(msg){const params=msg.params;const options=params?.options;if(!options||options.length===0){return{optionId:"approved"}}const allowAlways=options.find(o=>o.kind==="allow_always");if(allowAlways&&typeof allowAlways.optionId==="string"){logInfo3(`Auto-approving permission with option: ${allowAlways.optionId} (allow_always)`);return{optionId:allowAlways.optionId}}const allowOnce=options.find(o=>o.kind==="allow_once");if(allowOnce&&typeof allowOnce.optionId==="string"){logInfo3(`Auto-approving permission with option: ${allowOnce.optionId} (allow_once)`);return{optionId:allowOnce.optionId}}const firstOption=options[0];const optionId=typeof firstOption.optionId==="string"?firstOption.optionId:"approved";logInfo3(`Auto-approving permission with fallback option: ${optionId}`);return{optionId}}handleAuthenticateResponse(pendingAuth,response){const{agentId,authMethodId,requestId}=pendingAuth;if(response.error){const error=response.error;const errorCode=error.code??"UNKNOWN";const errorMessage=typeof error.message==="string"?error.message:"Unknown error";logError3(`Agent Auth failed for ${agentId}: [${errorCode}] ${errorMessage}`);pendingAuth.resolve(false,errorMessage);return}if(response.result!==void 0){logInfo3(`Agent Auth succeeded for ${agentId} (method: ${authMethodId}, request: ${requestId})`);pendingAuth.resolve(true);return}logError3(`Unexpected authenticate response format for ${agentId}: ${JSON.stringify(response)}`);pendingAuth.resolve(false,"Unexpected response format")}sendToAgent(agentId,message){let runtime;try{runtime=this.runtimeManager.get(agentId)}catch{logError3(`Failed to get runtime for agent ${agentId} to send response`);return}if(!runtime){logError3(`No runtime found for agent ${agentId}, cannot send response`);return}const success=runtime.write(message);if(!success){logError3(`Failed to write response to agent ${agentId}`)}else{logInfo3(`Sent auto-response to agent ${agentId}`)}}async attemptAuthentication(agentId,authMethods){const agentAuthMethods=getAgentAuthMethods(authMethods);if(agentAuthMethods.length>0){await this.attemptAgentAuthentication(agentId,agentAuthMethods);return}const terminalAuthMethods=getTerminalAuthMethods(authMethods);if(terminalAuthMethods.length>0){await this.attemptTerminalAuthentication(agentId,terminalAuthMethods);return}const oauthMethods=getOAuthMethods(authMethods);if(oauthMethods.length>0){await this.attemptOAuthAuthentication(agentId,oauthMethods);return}await this.attemptApiKeyAuthentication(agentId,authMethods)}async attemptAgentAuthentication(agentId,agentAuthMethods){const selectedMethod=agentAuthMethods[0];logInfo3(`Agent ${agentId} requires Agent Auth with method: ${selectedMethod.id}`);logInfo3(`Calling authenticate method on agent - agent will handle OAuth flow internally`);this.setAuthState(agentId,"pending");try{let runtime;try{let spawnCommand=this.registry.resolve(agentId);const agentEnv=getAgentEnv(this.apiKeys,agentId);if(Object.keys(agentEnv).length>0){spawnCommand={...spawnCommand,env:{...spawnCommand.env,...agentEnv}}}runtime=await this.runtimeManager.getOrSpawn(agentId,spawnCommand)}catch(error){logError3(`Failed to get runtime for Agent Auth: ${error.message}`);this.setAuthState(agentId,"failed");return}const success=await this.callAgentAuthenticate(agentId,selectedMethod.id,runtime);if(success){logInfo3(`Agent Auth successful for agent ${agentId}`);this.setAuthState(agentId,"authenticated")}else{logError3(`Agent Auth failed for agent ${agentId}`);this.setAuthState(agentId,"failed")}}catch(error){const errorMessage=error instanceof Error?error.message:String(error);logError3(`Agent Auth error for agent ${agentId}: ${errorMessage}`);this.setAuthState(agentId,"failed")}}callAgentAuthenticate(agentId,authMethodId,runtime){return new Promise(resolve2=>{const requestId=`agent-auth-${agentId}-${Date.now()}`;const authenticateRequest={jsonrpc:"2.0",id:requestId,method:"authenticate",params:{id:authMethodId}};const pendingRequest={requestId,agentId,authMethodId,sentAt:Date.now(),resolve:(success2,error)=>{this.pendingAuthenticateRequests.delete(requestId);if(error){logError3(`Agent Auth response error: ${error}`)}resolve2(success2)}};this.pendingAuthenticateRequests.set(requestId,pendingRequest);setTimeout(()=>{const pending=this.pendingAuthenticateRequests.get(requestId);if(pending){logError3(`Agent Auth timeout for agent ${agentId} (method: ${authMethodId})`);this.pendingAuthenticateRequests.delete(requestId);resolve2(false)}},AGENT_AUTH_TIMEOUT_MS);const success=runtime.write(authenticateRequest);if(!success){logError3(`Failed to send authenticate request to agent ${agentId}`);this.pendingAuthenticateRequests.delete(requestId);resolve2(false)}else{logInfo3(`Sent authenticate request to agent ${agentId} (id: ${requestId}, method: ${authMethodId})`)}})}async attemptTerminalAuthentication(agentId,terminalAuthMethods){const selectedMethod=terminalAuthMethods[0];logInfo3(`Agent ${agentId} requires Terminal Auth with method: ${selectedMethod.id}`);if(!this.isStdinTTY()||!this.isStdoutTTY()){logError3(`Terminal Auth requires interactive terminal (TTY). Run in a terminal with stdin/stdout connected.`);this.setAuthState(agentId,"failed");return}this.setAuthState(agentId,"pending");try{const existingRuntime=this.runtimeManager.get(agentId);if(existingRuntime){logInfo3(`Stopping existing runtime for agent ${agentId} before Terminal Auth`);await this.runtimeManager.terminate(agentId)}const baseSpawnCommand=this.registry.resolve(agentId);const terminalArgs=selectedMethod.args??[];const terminalEnv={...process.env,...selectedMethod.env??{}};logInfo3(`Launching Terminal Auth for ${agentId}: ${baseSpawnCommand.command} ${terminalArgs.join(" ")}`);const exitCode=await this.runTerminalAuthProcess(baseSpawnCommand.command,terminalArgs,terminalEnv);if(exitCode===0){logInfo3(`Terminal Auth process exited successfully for ${agentId}`);const authVerified=await this.verifyTerminalAuthSuccess(agentId);if(authVerified){logInfo3(`Terminal Auth verified for ${agentId}`);this.setAuthState(agentId,"authenticated")}else{logError3(`Terminal Auth completed but verification failed for ${agentId}`);this.setAuthState(agentId,"failed")}}else{logError3(`Terminal Auth process exited with code ${exitCode} for ${agentId}`);this.setAuthState(agentId,"failed")}}catch(error){const errorMessage=error instanceof Error?error.message:String(error);logError3(`Terminal Auth error for agent ${agentId}: ${errorMessage}`);this.setAuthState(agentId,"failed")}}runTerminalAuthProcess(command,args,env){return new Promise((resolve2,reject)=>{logInfo3(`Spawning Terminal Auth process: ${command} ${args.join(" ")}`);const child=this.spawnFn(command,args,{env,stdio:"inherit",shell:false});const timeoutId=setTimeout(()=>{logError3(`Terminal Auth process timed out after ${TERMINAL_AUTH_TIMEOUT_MS}ms`);child.kill("SIGTERM");setTimeout(()=>{if(!child.killed){child.kill("SIGKILL")}},5e3)},TERMINAL_AUTH_TIMEOUT_MS);child.on("error",error=>{clearTimeout(timeoutId);reject(error)});child.on("exit",(code,signal)=>{clearTimeout(timeoutId);if(signal){logError3(`Terminal Auth process killed by signal: ${signal}`);resolve2(1)}else{resolve2(code??1)}})})}async verifyTerminalAuthSuccess(agentId){try{let spawnCommand=this.registry.resolve(agentId);const agentEnv=getAgentEnv(this.apiKeys,agentId);if(Object.keys(agentEnv).length>0){spawnCommand={...spawnCommand,env:{...spawnCommand.env,...agentEnv}}}const runtime=await this.runtimeManager.getOrSpawn(agentId,spawnCommand);return runtime.state==="running"}catch(error){const errorMessage=error instanceof Error?error.message:String(error);logError3(`Failed to verify Terminal Auth for ${agentId}: ${errorMessage}`);return false}}async attemptOAuthAuthentication(agentId,oauthMethods){if(!this.authManager){logError3(`OAuth authentication required for agent ${agentId}, but AuthManager not available`);this.setAuthState(agentId,"failed");return}const selectedMethod=oauthMethods[0];const providerId=selectedMethod.providerId;logInfo3(`Agent ${agentId} requires OAuth authentication with provider: ${providerId}`);logInfo3(`Initiating OAuth 2.1 Authorization Code flow with PKCE for ${providerId}`);this.setAuthState(agentId,"pending");try{const result=await this.authManager.authenticateAgent(providerId);if(result.success){logInfo3(`OAuth authentication successful for agent ${agentId} with provider ${providerId}`);this.setAuthState(agentId,"authenticated");await this.sendOAuthCredentialsToAgent(agentId,selectedMethod)}else{const errorMsg=result.error?.message??"Unknown error";const errorCode=result.error?.code??"UNKNOWN";logError3(`OAuth authentication failed for agent ${agentId}: [${errorCode}] ${errorMsg}`);this.setAuthState(agentId,"failed")}}catch(error){const errorMessage=error instanceof Error?error.message:String(error);logError3(`OAuth authentication error for agent ${agentId}: ${errorMessage}`);this.setAuthState(agentId,"failed")}}async sendOAuthCredentialsToAgent(agentId,method){if(!this.authManager){logError3(`Cannot send OAuth credentials: AuthManager not available`);return}const token=await this.authManager.getTokenForAgent(agentId,method.providerId);if(!token){logError3(`No OAuth token available for agent ${agentId} after successful auth`);return}let runtime;try{let spawnCommand=this.registry.resolve(agentId);const agentEnv=getAgentEnv(this.apiKeys,agentId);if(Object.keys(agentEnv).length>0){spawnCommand={...spawnCommand,env:{...spawnCommand.env,...agentEnv}}}runtime=await this.runtimeManager.getOrSpawn(agentId,spawnCommand)}catch(error){logError3(`Failed to get runtime for OAuth credential injection: ${error.message}`);return}const authRequest={jsonrpc:"2.0",id:`auth-${agentId}-${Date.now()}`,method:"authenticate",params:{methodId:method.id,credentials:{accessToken:token}}};const transformed=transformMessage(authRequest);const serialized=JSON.stringify(transformed)+"\n";if(runtime.process.stdin){runtime.process.stdin.write(serialized,error=>{if(error){logError3(`Failed to send OAuth authenticate request to ${agentId}: ${error.message}`)}else{logInfo3(`Sent OAuth authenticate request to agent ${agentId}`)}})}}async attemptApiKeyAuthentication(agentId,authMethods){const apiKey=getAgentApiKey(this.apiKeys,agentId);if(!apiKey){logError3(`No API key found for agent ${agentId}, authentication will fail`);this.setAuthState(agentId,"failed");return}const apiKeyMethods=getApiKeyMethods(authMethods);const SAFE_API_KEY_METHODS=["api-key","openai-api-key","github-api-key","google-api-key","azure-api-key","cognito-api-key"];const selectedMethod=apiKeyMethods.find(m=>SAFE_API_KEY_METHODS.includes(m.id));if(!selectedMethod){logError3(`No safe API key method available for agent ${agentId}, skipping auto-auth`);this.setAuthState(agentId,"failed");return}logInfo3(`Authenticating agent ${agentId} with API key method: ${selectedMethod.id} (providerId: ${selectedMethod.providerId??"none"})`);let runtime;try{let spawnCommand=this.registry.resolve(agentId);const agentEnv=getAgentEnv(this.apiKeys,agentId);if(Object.keys(agentEnv).length>0){spawnCommand={...spawnCommand,env:{...spawnCommand.env,...agentEnv}}}runtime=await this.runtimeManager.getOrSpawn(agentId,spawnCommand)}catch(error){logError3(`Failed to get runtime for authentication: ${error.message}`);this.setAuthState(agentId,"failed");return}const authRequest={jsonrpc:"2.0",id:`auth-${agentId}-${Date.now()}`,method:"authenticate",params:{methodId:selectedMethod.id,credentials:{apiKey}}};const transformed=transformMessage(authRequest);const serialized=JSON.stringify(transformed)+"\n";if(runtime.process.stdin){runtime.process.stdin.write(serialized,error=>{if(error){logError3(`Failed to send authenticate request to ${agentId}: ${error.message}`);this.setAuthState(agentId,"failed")}else{logInfo3(`Sent authenticate request to agent ${agentId}`);this.setAuthState(agentId,"authenticated")}})}}get pendingCount(){return this.pendingRequests.size}isPending(id){return this.pendingRequests.has(id)}clearPending(){this.pendingRequests.clear()}clearQueues(){for(const[agentId,queue]of this.requestQueue.entries()){for(const queuedRequest of queue){const id=extractId(queuedRequest.message);queuedRequest.resolve(createErrorResponse(id,RoutingErrorCodes.SPAWN_FAILED,"Router shutdown",{agentId,reason:"Router is shutting down"}))}}this.requestQueue.clear();this.authState.clear();this.agentOAuthRequirements.clear();logInfo3("Cleared all request queues, auth state, and OAuth requirements")}resetAuthState(agentId){this.setAuthState(agentId,"none")}getAgentOAuthRequirement(agentId){return this.agentOAuthRequirements.get(agentId)}setAgentOAuthRequirement(agentId,providerId){this.agentOAuthRequirements.set(agentId,providerId);logInfo3(`Set OAuth requirement for agent ${agentId}: provider ${providerId}`)}clearAgentOAuthRequirement(agentId){this.agentOAuthRequirements.delete(agentId);logInfo3(`Cleared OAuth requirement for agent ${agentId}`)}}}});function formatLogMessage(level,message,context=LOG_CONTEXT){const timestamp=new Date().toISOString();return`[${timestamp}] [${level}] [${context}] ${message}`}function log(level,message,context){const formatted=formatLogMessage(level,message,context);console.error(formatted)}function logExit(agentId,exitCode,signal){if(signal){log("INFO",`Agent "${agentId}" exited with signal ${signal}`)}else if(exitCode!==null){log("INFO",`Agent "${agentId}" exited with code ${exitCode}`)}else{log("INFO",`Agent "${agentId}" exited`)}}function logInfo4(message,context){log("INFO",message,context)}function logWarn(message,context){log("WARN",message,context)}function logError4(message,context){log("ERROR",message,context)}var LOG_CONTEXT;var init_log=__esm({"workers-registry/registry-launcher/src/log.ts"(){"use strict";LOG_CONTEXT="registry-launcher"}});import*as readline from"readline";var PROVIDER_INFO,TerminalAuthFlow;var init_terminal_auth_flow=__esm({"workers-registry/registry-launcher/src/auth/flows/terminal-auth-flow.ts"(){"use strict";init_types2();PROVIDER_INFO=[{id:"github",name:"GitHub",requiresClientSecret:true,requiresCustomEndpoints:false,supportsApiKey:true,supportsOAuth:true,apiKeyLabel:"Personal Access Token",apiKeyEnvVar:"GITHUB_TOKEN"},{id:"google",name:"Google",requiresClientSecret:true,requiresCustomEndpoints:false,supportsApiKey:false,supportsOAuth:true},{id:"cognito",name:"AWS Cognito",requiresClientSecret:true,requiresCustomEndpoints:true,supportsApiKey:false,supportsOAuth:true},{id:"azure",name:"Microsoft Entra ID",requiresClientSecret:true,requiresCustomEndpoints:true,supportsApiKey:false,supportsOAuth:true},{id:"oidc",name:"Generic OIDC",requiresClientSecret:true,requiresCustomEndpoints:true,supportsApiKey:false,supportsOAuth:true}];TerminalAuthFlow=class{credentialStore;validateCredentials;input;output;rl=null;constructor(dependencies){this.credentialStore=dependencies.credentialStore;this.validateCredentials=dependencies.validateCredentials;this.input=dependencies.input??process.stdin;this.output=dependencies.output??process.stderr}async execute(providerId){this.rl=readline.createInterface({input:this.input,output:this.output});try{this.writeLine("\n=== OAuth Authentication Setup ===\n");const selectedProvider=providerId??await this.selectProvider();const providerInfo=PROVIDER_INFO.find(p=>p.id===selectedProvider);if(!providerInfo){return{useBrowserOAuth:false,authResult:{success:false,providerId:selectedProvider,error:{code:"UNSUPPORTED_PROVIDER",message:`Provider '${selectedProvider}' is not supported.`,details:{supportedProviders:VALID_PROVIDER_IDS}}}}}this.writeLine(`
|
|
4
|
+
`)})}}write(message){if(this.state!=="running"&&this.state!=="starting"){return false}if(!this.process.stdin||this.process.stdin.destroyed){return false}try{const ndjsonLine=JSON.stringify(message)+"\n";return this.process.stdin.write(ndjsonLine)}catch{return false}}async terminate(timeout=DEFAULT_TERMINATE_TIMEOUT_MS){if(this.state==="stopped"){return}if(this.state==="stopping"){return this.waitForExit()}this.state="stopping";if(this.process.stdin&&!this.process.stdin.destroyed){this.process.stdin.end()}this.process.kill("SIGTERM");const exitPromise=this.waitForExit();const timeoutPromise=new Promise(resolve2=>{setTimeout(()=>resolve2("timeout"),timeout)});const result=await Promise.race([exitPromise,timeoutPromise]);if(result==="timeout"&&!this.process.killed&&this.process.exitCode===null){this.process.kill("SIGKILL");await this.waitForExit()}}waitForExit(){if(this.state==="stopped"){return Promise.resolve()}return new Promise(resolve2=>{this.process.once("exit",()=>{resolve2()})})}}}});var DEFAULT_SHUTDOWN_TIMEOUT_MS,AgentRuntimeManager;var init_manager=__esm({"workers-registry/registry-launcher/src/runtime/manager.ts"(){"use strict";init_agent_runtime();DEFAULT_SHUTDOWN_TIMEOUT_MS=5e3;AgentRuntimeManager=class{runtimes=new Map;exitCallbacks=[];async getOrSpawn(agentId,spawnCommand){const existing=this.runtimes.get(agentId);if(existing&&existing.state!=="stopped"){return existing}const runtime=AgentRuntimeImpl.spawn(agentId,spawnCommand,(code,_signal)=>{this.handleAgentExit(agentId,code)});this.runtimes.set(agentId,runtime);return runtime}get(agentId){return this.runtimes.get(agentId)}async terminate(agentId,timeout=DEFAULT_SHUTDOWN_TIMEOUT_MS){const runtime=this.runtimes.get(agentId);if(!runtime){return}await runtime.terminate(timeout);this.runtimes.delete(agentId)}async terminateAll(timeout=DEFAULT_SHUTDOWN_TIMEOUT_MS){const terminatePromises=[];for(const[agentId,runtime]of this.runtimes){if(runtime.state!=="stopped"){terminatePromises.push(runtime.terminate(timeout).then(()=>{this.runtimes.delete(agentId)}))}}await Promise.all(terminatePromises)}onAgentExit(callback){this.exitCallbacks.push(callback)}handleAgentExit(agentId,code){this.runtimes.delete(agentId);for(const callback of this.exitCallbacks){try{callback(agentId,code)}catch{}}}get size(){return this.runtimes.size}has(agentId){return this.runtimes.has(agentId)}}}});function isValidAuthMethodType(value){return typeof value==="string"&&VALID_AUTH_METHOD_TYPES2.includes(value)}function isValidProviderId(value){return typeof value==="string"&&VALID_PROVIDER_IDS.includes(value)}var VALID_AUTH_METHOD_TYPES2,DEFAULT_AUTH_METHOD_PRECEDENCE,VALID_PROVIDER_IDS,AUTH_METHOD_ID_TO_PROVIDER_ID,VALID_AUTH_METHOD_IDS;var init_types2=__esm({"workers-registry/registry-launcher/src/auth/types.ts"(){"use strict";VALID_AUTH_METHOD_TYPES2=["oauth2","api-key"];DEFAULT_AUTH_METHOD_PRECEDENCE={methodPrecedence:["oauth2","api-key"],failFastOnUnsupported:true,failFastOnAmbiguous:true};VALID_PROVIDER_IDS=["github","google","cognito","azure","oidc"];AUTH_METHOD_ID_TO_PROVIDER_ID={"oauth2-github":"github","oauth2-google":"google","oauth2-cognito":"cognito","oauth2-azure":"azure","oauth2-oidc":"oidc","github":"github","google":"google","cognito":"cognito","azure":"azure","oidc":"oidc"};VALID_AUTH_METHOD_IDS=Object.keys(AUTH_METHOD_ID_TO_PROVIDER_ID)}});import{spawn as spawn2}from"node:child_process";function parseAuthMethods2(raw){if(!Array.isArray(raw)){logError3("authMethods is not an array, skipping parsing");return[]}const methods=raw.slice(0,MAX_AUTH_METHODS);const parsed=[];const seenIds=new Set;for(const method of methods){const result=parseAuthMethod2(method,seenIds);if(result){parsed.push(result);seenIds.add(result.id)}}logInfo3(`Parsed ${parsed.length} valid auth methods from ${methods.length} raw methods`);return parsed}function parseAuthMethod2(method,seenIds){if(method===null||typeof method!=="object"){return null}const obj=method;const id=obj.id;if(typeof id!=="string"||id.length===0||id.length>MAX_METHOD_ID_LENGTH){logError3(`Invalid auth method id: ${typeof id==="string"?id.substring(0,50):typeof id}`);return null}if(seenIds.has(id)){logInfo3(`Skipping duplicate auth method id: ${id}`);return null}const type=obj.type;if(typeof type!=="string"||!VALID_AUTH_METHOD_TYPES3.includes(type)){logError3(`Invalid auth method type for id ${id}: ${type}`);return null}const rawProviderId=obj.providerId;let providerId;if(rawProviderId!==void 0){if(isValidProviderId(rawProviderId)){providerId=rawProviderId}else{logError3(`Invalid providerId in auth method ${id}: ${rawProviderId}`)}}const mappedProviderId=AUTH_METHOD_ID_TO_PROVIDER[id];if(providerId&&mappedProviderId&&providerId!==mappedProviderId){logError3(`Conflict: auth method ${id} has providerId ${providerId} but maps to ${mappedProviderId}, rejecting`);return null}const resolvedProviderId=providerId??mappedProviderId;if(type==="oauth2"){if(!resolvedProviderId){logError3(`OAuth auth method ${id} has no valid providerId, skipping`);return null}return{kind:"oauth2",id,providerId:resolvedProviderId}}if(type==="agent"){return{kind:"agent",id,providerId:resolvedProviderId}}if(type==="terminal"){const args=Array.isArray(obj.args)?obj.args.filter(a=>typeof a==="string"):void 0;const env=obj.env&&typeof obj.env==="object"&&!Array.isArray(obj.env)?Object.fromEntries(Object.entries(obj.env).filter(([,v])=>typeof v==="string")):void 0;return{kind:"terminal",id,args,env}}if(type==="api-key"){return{kind:"api-key",id,providerId:resolvedProviderId}}return null}function getOAuthMethods(methods){return methods.filter(m=>m.kind==="oauth2")}function getAgentAuthMethods(methods){return methods.filter(m=>m.kind==="agent")}function getTerminalAuthMethods(methods){return methods.filter(m=>m.kind==="terminal")}function getApiKeyMethods(methods){return methods.filter(m=>m.kind==="api-key")}function logError3(message){const timestamp=new Date().toISOString();console.error(`[${timestamp}] [ERROR] [router] ${message}`)}function logInfo3(message){const timestamp=new Date().toISOString();console.error(`[${timestamp}] [INFO] [router] ${message}`)}function createErrorResponse(id,code,message,data){const response={jsonrpc:"2.0",id,error:{code,message}};if(data!==void 0){response.error.data=data}return response}function extractAgentId(message){const msg=message;const agentId=msg.agentId;if(typeof agentId==="string"&&agentId.length>0){return agentId}return void 0}function extractId(message){const msg=message;const id=msg.id;if(typeof id==="string"||typeof id==="number"){return id}return null}function transformMessage(message){const{agentId:_,...rest}=message;return rest}var RoutingErrorCodes,AUTH_METHOD_ID_TO_PROVIDER,MAX_AUTH_METHODS,MAX_METHOD_ID_LENGTH,VALID_AUTH_METHOD_TYPES3,AGENT_AUTH_TIMEOUT_MS,TERMINAL_AUTH_TIMEOUT_MS,QUEUED_REQUEST_TIMEOUT_MS,MessageRouter;var init_message_router=__esm({"workers-registry/registry-launcher/src/router/message-router.ts"(){"use strict";init_registry();init_api_keys();init_types2();RoutingErrorCodes={MISSING_AGENT_ID:-32600,AGENT_NOT_FOUND:-32001,PLATFORM_NOT_SUPPORTED:-32002,SPAWN_FAILED:-32003,AUTH_REQUIRED:-32004};AUTH_METHOD_ID_TO_PROVIDER={"oauth2-github":"github","oauth2-google":"google","oauth2-cognito":"cognito","oauth2-azure":"azure","agent-github":"github","agent-google":"google","agent-cognito":"cognito","agent-azure":"azure","github-api-key":"github","google-api-key":"google","azure-api-key":"azure","cognito-api-key":"cognito"};MAX_AUTH_METHODS=50;MAX_METHOD_ID_LENGTH=128;VALID_AUTH_METHOD_TYPES3=["oauth2","agent","terminal","api-key"];AGENT_AUTH_TIMEOUT_MS=5*60*1e3;TERMINAL_AUTH_TIMEOUT_MS=10*60*1e3;QUEUED_REQUEST_TIMEOUT_MS=5*60*1e3;MessageRouter=class{registry;runtimeManager;writeCallback;apiKeys;spawnFn;isStdinTTY;isStdoutTTY;authManager;pendingRequests=new Map;authState=new Map;agentOAuthRequirements=new Map;requestQueue=new Map;pendingAuthenticateRequests=new Map;sessionIdMap=new Map;autoOAuth;constructor(registry,runtimeManager,writeCallback,apiKeys={},authManager,autoOAuth,deps){this.registry=registry;this.runtimeManager=runtimeManager;this.writeCallback=writeCallback;this.apiKeys=apiKeys;this.authManager=authManager;this.autoOAuth=autoOAuth??this.getAutoOAuthFromEnv();this.spawnFn=deps?.spawnFn??spawn2;this.isStdinTTY=deps?.isStdinTTY??(()=>process.stdin.isTTY??false);this.isStdoutTTY=deps?.isStdoutTTY??(()=>process.stdout.isTTY??false)}getAutoOAuthFromEnv(){const envValue=process.env.AUTH_AUTO_OAUTH;return envValue==="true"||envValue==="1"||envValue==="yes"}getSupportedAuthMethods(){const methods=[{id:"api-key",type:"api-key"}];if(this.authManager){methods.push({id:"agent-oauth",type:"agent"});methods.push({id:"terminal-setup",type:"terminal",args:["--setup"]});methods.push({id:"oauth2-github",type:"oauth2",providerId:"github"},{id:"oauth2-google",type:"oauth2",providerId:"google"},{id:"oauth2-cognito",type:"oauth2",providerId:"cognito"},{id:"oauth2-azure",type:"oauth2",providerId:"azure"},{id:"oauth2-oidc",type:"oauth2",providerId:"oidc"})}return methods}async hasAuthenticationForAgent(agentId){if(this.authManager){const token=await this.authManager.getTokenForAgent(agentId);if(token){return true}}const apiKey=getAgentApiKey(this.apiKeys,agentId);return apiKey!==void 0}hasCredentialsForAgent(agentId){const apiKey=getAgentApiKey(this.apiKeys,agentId);return apiKey!==void 0}createAuthRequiredError(id,agentId,requiredMethod){const remediation={type:"login_required",commands:["npx @stdiobus/workers-registry acp-registry --setup","stdiobus acp-registry --setup"],hint:"Run: npx @stdiobus/workers-registry acp-registry --setup",docsUrl:"https://github.com/stdiobus/workers-registry/blob/main/docs/oauth/user-guide.md"};return createErrorResponse(id,RoutingErrorCodes.AUTH_REQUIRED,"Authentication required",{agentId,requiredMethod:requiredMethod??"api-key",supportedMethods:this.getSupportedAuthMethods().map(m=>m.id),remediation})}async injectAuthentication(agentId,message){if(this.authManager){return this.authManager.injectAuth(agentId,message)}return message}injectMcpServers(message,agentId){const agent=this.registry.lookup(agentId);if(!agent?.mcpServers||agent.mcpServers.length===0){return message}const msg=message;const params=msg.params||{};const existingServers=Array.isArray(params.mcpServers)?params.mcpServers:[];const registryServers=agent.mcpServers.map(server=>({name:server.name,command:server.command,args:server.args,env:server.env?Object.entries(server.env).map(([name,value])=>({name,value})):void 0}));const existingNames=new Set(existingServers.filter(s=>s!==null&&typeof s==="object").map(s=>s.name).filter(n=>typeof n==="string"));const mergedServers=[...registryServers.filter(s=>!existingNames.has(s.name)),...existingServers];logInfo3(`Injecting ${registryServers.length} MCP servers from registry for agent ${agentId}`);return{...msg,params:{...params,mcpServers:mergedServers}}}async route(message){const id=extractId(message);const agentId=extractAgentId(message);if(agentId===void 0){logError3("Missing agentId in request");return createErrorResponse(id,RoutingErrorCodes.MISSING_AGENT_ID,"Missing agentId")}const currentAuthState=this.getAuthState(agentId);if(currentAuthState==="pending"){logInfo3(`OAuth flow pending for agent ${agentId}, queueing request (id=${id})`);return this.queueRequest(agentId,message)}if(currentAuthState==="failed"){logError3(`Authentication failed for agent ${agentId}, returning AUTH_REQUIRED`);const requiredProviderId2=this.agentOAuthRequirements.get(agentId);return this.createAuthRequiredErrorWithProvider(id,agentId,requiredProviderId2)}const requiredProviderId=this.agentOAuthRequirements.get(agentId);if(requiredProviderId&¤tAuthState!=="authenticated"){const hasCredentials=await this.hasOAuthCredentialsForAgent(agentId,requiredProviderId);if(!hasCredentials){logError3(`Agent ${agentId} requires OAuth (provider: ${requiredProviderId}) but credentials not available`);return this.createAuthRequiredErrorWithProvider(id,agentId,requiredProviderId)}}return this.routeInternal(message,agentId,id)}async hasOAuthCredentialsForAgent(agentId,providerId){if(!this.authManager){return false}const token=await this.authManager.getTokenForAgent(agentId,providerId);return token!==null&&token!==void 0}createAuthRequiredErrorWithProvider(id,agentId,providerId){const supportedMethods=this.getSupportedAuthMethods();const remediation={type:"login_required",provider:providerId||"unknown",commands:providerId?[`npx @stdiobus/workers-registry acp-registry --login ${providerId}`,`stdiobus acp-registry --login ${providerId}`]:["npx @stdiobus/workers-registry acp-registry --setup","stdiobus acp-registry --setup"],hint:providerId?`Run: npx @stdiobus/workers-registry acp-registry --login ${providerId}`:"Run: npx @stdiobus/workers-registry acp-registry --setup",docsUrl:"https://github.com/stdiobus/workers-registry/blob/main/docs/oauth/user-guide.md"};return createErrorResponse(id,RoutingErrorCodes.AUTH_REQUIRED,"Authentication required",{agentId,requiredMethod:providerId?`oauth2-${providerId}`:"oauth2",supportedMethods:supportedMethods.map(m=>m.id),providerId,remediation})}async routeInternal(message,agentId,id){let spawnCommand;try{spawnCommand=this.registry.resolve(agentId)}catch(error){if(error instanceof AgentNotFoundError){logError3(`Agent not found: ${agentId}`);return createErrorResponse(id,RoutingErrorCodes.AGENT_NOT_FOUND,"Agent not found",{agentId})}if(error instanceof PlatformNotSupportedError){logError3(`Platform not supported for agent: ${agentId}`);return createErrorResponse(id,RoutingErrorCodes.PLATFORM_NOT_SUPPORTED,"Platform not supported",{agentId,platform:error.platform})}throw error}const agentEnv=getAgentEnv(this.apiKeys,agentId);if(Object.keys(agentEnv).length>0){spawnCommand={...spawnCommand,env:{...spawnCommand.env,...agentEnv}};logInfo3(`Injected ${Object.keys(agentEnv).length} env vars from api-keys.json for agent ${agentId}`)}let runtime;try{runtime=await this.runtimeManager.getOrSpawn(agentId,spawnCommand)}catch(error){logError3(`Failed to spawn agent ${agentId}: ${error.message}`);return createErrorResponse(id,RoutingErrorCodes.SPAWN_FAILED,"Agent spawn failed",{agentId,error:error.message})}if(id!==null){const msg2=message;const clientSessionId=typeof msg2.sessionId==="string"?msg2.sessionId:void 0;const method=typeof msg2.method==="string"?msg2.method:void 0;this.pendingRequests.set(id,{id,agentId,timestamp:Date.now(),method,clientSessionId})}let transformedMessage=transformMessage(message);const msg=message;if(msg.method==="session/new"){transformedMessage=this.injectMcpServers(transformedMessage,agentId)}transformedMessage=await this.injectAuthentication(agentId,transformedMessage);const success=runtime.write(transformedMessage);if(!success){logError3(`Failed to write to agent ${agentId}`);if(id!==null){this.pendingRequests.delete(id)}}else{logInfo3(`Routed message to agent ${agentId}`)}return void 0}getAuthState(agentId){return this.authState.get(agentId)??"none"}setAuthState(agentId,newState){const oldState=this.getAuthState(agentId);if(oldState===newState){return}logInfo3(`Auth state transition for ${agentId}: ${oldState} \u2192 ${newState}`);this.authState.set(agentId,newState);if(newState==="authenticated"&&oldState==="pending"){void this.processQueuedRequests(agentId)}else if(newState==="failed"&&oldState==="pending"){void this.rejectQueuedRequests(agentId)}}queueRequest(agentId,message){return new Promise(resolve2=>{const queuedRequest={message,queuedAt:Date.now(),resolve:resolve2};let queue=this.requestQueue.get(agentId);if(!queue){queue=[];this.requestQueue.set(agentId,queue)}queue.push(queuedRequest);logInfo3(`Queued request for agent ${agentId}, queue size: ${queue.length}`);setTimeout(()=>{this.handleQueuedRequestTimeout(agentId,queuedRequest)},QUEUED_REQUEST_TIMEOUT_MS)})}handleQueuedRequestTimeout(agentId,queuedRequest){const queue=this.requestQueue.get(agentId);if(!queue){return}const index=queue.indexOf(queuedRequest);if(index===-1){return}queue.splice(index,1);logError3(`Queued request timed out for agent ${agentId}`);const id=extractId(queuedRequest.message);queuedRequest.resolve(createErrorResponse(id,RoutingErrorCodes.AUTH_REQUIRED,"Authentication timeout",{agentId,reason:"OAuth flow timed out while request was queued"}))}async processQueuedRequests(agentId){const queue=this.requestQueue.get(agentId);if(!queue||queue.length===0){return}logInfo3(`Processing ${queue.length} queued requests for agent ${agentId}`);this.requestQueue.delete(agentId);for(const queuedRequest of queue){try{const id=extractId(queuedRequest.message);const result=await this.routeInternal(queuedRequest.message,agentId,id);queuedRequest.resolve(result)}catch(error){const id=extractId(queuedRequest.message);logError3(`Error processing queued request for ${agentId}: ${error.message}`);queuedRequest.resolve(createErrorResponse(id,RoutingErrorCodes.SPAWN_FAILED,"Failed to process queued request",{agentId,error:error.message}))}}logInfo3(`Completed processing queued requests for agent ${agentId}`)}async rejectQueuedRequests(agentId){const queue=this.requestQueue.get(agentId);if(!queue||queue.length===0){return}logInfo3(`Rejecting ${queue.length} queued requests for agent ${agentId} due to auth failure`);this.requestQueue.delete(agentId);for(const queuedRequest of queue){const id=extractId(queuedRequest.message);queuedRequest.resolve(this.createAuthRequiredError(id,agentId,"oauth2"))}}getQueuedRequestCount(agentId){const queue=this.requestQueue.get(agentId);return queue?.length??0}getTotalQueuedRequestCount(){let total=0;for(const queue of this.requestQueue.values()){total+=queue.length}return total}handleAgentResponse(agentId,response){const id=extractId(response);let msg=response;const method=typeof msg.method==="string"?msg.method:void 0;if(id!==null&&typeof id==="string"){const pendingAuth=this.pendingAuthenticateRequests.get(id);if(pendingAuth&&pendingAuth.agentId===agentId){this.handleAuthenticateResponse(pendingAuth,msg);return}}if(id!==null&&method){this.handleAgentRequest(agentId,id,method,msg);return}if(id!==null){const pending=this.pendingRequests.get(id);if(pending&&pending.agentId===agentId){const result=msg.result;const isInitializeResponse=pending.method==="initialize"&&result!==void 0;if(isInitializeResponse){const ourAuthMethods=this.getSupportedAuthMethods();const existingAuthMethods=Array.isArray(result.authMethods)?result.authMethods:[];const mergedAuthMethods=[...ourAuthMethods,...existingAuthMethods.filter(m=>!ourAuthMethods.some(our=>our.id===m.id))];msg={...msg,result:{...result,authMethods:mergedAuthMethods}};logInfo3(`Injected ${ourAuthMethods.length} auth methods into initialize response for ${agentId}`)}if(isInitializeResponse&&result&&Array.isArray(result.authMethods)&&result.authMethods.length>0){const parsedMethods=parseAuthMethods2(result.authMethods);if(parsedMethods.length>0){const oauthMethods=getOAuthMethods(parsedMethods);const apiKeyMethods=getApiKeyMethods(parsedMethods);const hasApiKeyCredentials=this.hasCredentialsForAgent(agentId);if(oauthMethods.length>0&&!(apiKeyMethods.length>0&&hasApiKeyCredentials)){const requiredProviderId=oauthMethods[0].providerId;this.agentOAuthRequirements.set(agentId,requiredProviderId);logInfo3(`Agent ${agentId} requires OAuth authentication with provider: ${requiredProviderId}`)}else if(apiKeyMethods.length>0&&hasApiKeyCredentials){logInfo3(`Agent ${agentId} supports OAuth but api-key credentials available, using api-key`)}if(this.autoOAuth){logInfo3(`Agent ${agentId} requires authentication, attempting auto-auth with ${parsedMethods.length} valid methods`);this.setAuthState(agentId,"pending");void this.attemptAuthentication(agentId,parsedMethods)}else{logInfo3(`Agent ${agentId} requires authentication but AUTH_AUTO_OAUTH is disabled. Use --login to authenticate.`)}}else{logError3(`Agent ${agentId} has authMethods but none are valid after parsing`);this.setAuthState(agentId,"none")}}if(result&&typeof result.sessionId==="string"){const agentSessionId=result.sessionId;const clientSessionId=pending.clientSessionId;if(clientSessionId){this.sessionIdMap.set(agentSessionId,clientSessionId);logInfo3(`Mapped agent sessionId ${agentSessionId} to client sessionId ${clientSessionId}`)}}this.pendingRequests.delete(id)}}if(id===null&&method){logInfo3(`Received notification: ${method}`);const params=msg.params;if(params&&typeof params.sessionId==="string"){const agentSessionId=params.sessionId;const clientSessionId=this.sessionIdMap.get(agentSessionId);if(clientSessionId){const enriched={...msg,sessionId:clientSessionId,params:{...params,sessionId:agentSessionId}};logInfo3(`Forwarding notification with mapped sessionId: ${clientSessionId}`);this.writeCallback(enriched);return}else{logError3(`Notification with unmapped agentSessionId: ${agentSessionId}, using default sessionId`);const enriched={...msg,sessionId:"global-notifications",params:{...params,sessionId:agentSessionId}};this.writeCallback(enriched);return}}else{const topLevelSessionId=msg.sessionId;if(topLevelSessionId){this.writeCallback(response);return}else{logError3(`Notification without sessionId: ${method}, adding default sessionId for routing`);const enriched={...msg,sessionId:"global-notifications"};this.writeCallback(enriched);return}}}this.writeCallback(msg)}handleAgentRequest(agentId,id,method,msg){logInfo3(`Agent ${agentId} sent request: ${method} (id=${id}), auto-responding`);let result;if(method==="session/request_permission"){result=this.buildPermissionResponse(msg)}else{logInfo3(`Unknown agent request method: ${method}, sending generic success`);result={}}const response={jsonrpc:"2.0",id,result};this.sendToAgent(agentId,response)}buildPermissionResponse(msg){const params=msg.params;const options=params?.options;if(!options||options.length===0){return{optionId:"approved"}}const allowAlways=options.find(o=>o.kind==="allow_always");if(allowAlways&&typeof allowAlways.optionId==="string"){logInfo3(`Auto-approving permission with option: ${allowAlways.optionId} (allow_always)`);return{optionId:allowAlways.optionId}}const allowOnce=options.find(o=>o.kind==="allow_once");if(allowOnce&&typeof allowOnce.optionId==="string"){logInfo3(`Auto-approving permission with option: ${allowOnce.optionId} (allow_once)`);return{optionId:allowOnce.optionId}}const firstOption=options[0];const optionId=typeof firstOption.optionId==="string"?firstOption.optionId:"approved";logInfo3(`Auto-approving permission with fallback option: ${optionId}`);return{optionId}}handleAuthenticateResponse(pendingAuth,response){const{agentId,authMethodId,requestId}=pendingAuth;if(response.error){const error=response.error;const errorCode=error.code??"UNKNOWN";const errorMessage=typeof error.message==="string"?error.message:"Unknown error";logError3(`Agent Auth failed for ${agentId}: [${errorCode}] ${errorMessage}`);pendingAuth.resolve(false,errorMessage);return}if(response.result!==void 0){logInfo3(`Agent Auth succeeded for ${agentId} (method: ${authMethodId}, request: ${requestId})`);pendingAuth.resolve(true);return}logError3(`Unexpected authenticate response format for ${agentId}: ${JSON.stringify(response)}`);pendingAuth.resolve(false,"Unexpected response format")}sendToAgent(agentId,message){let runtime;try{runtime=this.runtimeManager.get(agentId)}catch{logError3(`Failed to get runtime for agent ${agentId} to send response`);return}if(!runtime){logError3(`No runtime found for agent ${agentId}, cannot send response`);return}const success=runtime.write(message);if(!success){logError3(`Failed to write response to agent ${agentId}`)}else{logInfo3(`Sent auto-response to agent ${agentId}`)}}async attemptAuthentication(agentId,authMethods){const agentAuthMethods=getAgentAuthMethods(authMethods);if(agentAuthMethods.length>0){await this.attemptAgentAuthentication(agentId,agentAuthMethods);return}const terminalAuthMethods=getTerminalAuthMethods(authMethods);if(terminalAuthMethods.length>0){await this.attemptTerminalAuthentication(agentId,terminalAuthMethods);return}const oauthMethods=getOAuthMethods(authMethods);if(oauthMethods.length>0){await this.attemptOAuthAuthentication(agentId,oauthMethods);return}await this.attemptApiKeyAuthentication(agentId,authMethods)}async attemptAgentAuthentication(agentId,agentAuthMethods){const selectedMethod=agentAuthMethods[0];logInfo3(`Agent ${agentId} requires Agent Auth with method: ${selectedMethod.id}`);logInfo3(`Calling authenticate method on agent - agent will handle OAuth flow internally`);this.setAuthState(agentId,"pending");try{let runtime;try{let spawnCommand=this.registry.resolve(agentId);const agentEnv=getAgentEnv(this.apiKeys,agentId);if(Object.keys(agentEnv).length>0){spawnCommand={...spawnCommand,env:{...spawnCommand.env,...agentEnv}}}runtime=await this.runtimeManager.getOrSpawn(agentId,spawnCommand)}catch(error){logError3(`Failed to get runtime for Agent Auth: ${error.message}`);this.setAuthState(agentId,"failed");return}const success=await this.callAgentAuthenticate(agentId,selectedMethod.id,runtime);if(success){logInfo3(`Agent Auth successful for agent ${agentId}`);this.setAuthState(agentId,"authenticated")}else{logError3(`Agent Auth failed for agent ${agentId}`);this.setAuthState(agentId,"failed")}}catch(error){const errorMessage=error instanceof Error?error.message:String(error);logError3(`Agent Auth error for agent ${agentId}: ${errorMessage}`);this.setAuthState(agentId,"failed")}}callAgentAuthenticate(agentId,authMethodId,runtime){return new Promise(resolve2=>{const requestId=`agent-auth-${agentId}-${Date.now()}`;const authenticateRequest={jsonrpc:"2.0",id:requestId,method:"authenticate",params:{id:authMethodId}};const pendingRequest={requestId,agentId,authMethodId,sentAt:Date.now(),resolve:(success2,error)=>{this.pendingAuthenticateRequests.delete(requestId);if(error){logError3(`Agent Auth response error: ${error}`)}resolve2(success2)}};this.pendingAuthenticateRequests.set(requestId,pendingRequest);setTimeout(()=>{const pending=this.pendingAuthenticateRequests.get(requestId);if(pending){logError3(`Agent Auth timeout for agent ${agentId} (method: ${authMethodId})`);this.pendingAuthenticateRequests.delete(requestId);resolve2(false)}},AGENT_AUTH_TIMEOUT_MS);const success=runtime.write(authenticateRequest);if(!success){logError3(`Failed to send authenticate request to agent ${agentId}`);this.pendingAuthenticateRequests.delete(requestId);resolve2(false)}else{logInfo3(`Sent authenticate request to agent ${agentId} (id: ${requestId}, method: ${authMethodId})`)}})}async attemptTerminalAuthentication(agentId,terminalAuthMethods){const selectedMethod=terminalAuthMethods[0];logInfo3(`Agent ${agentId} requires Terminal Auth with method: ${selectedMethod.id}`);if(!this.isStdinTTY()||!this.isStdoutTTY()){logError3(`Terminal Auth requires interactive terminal (TTY). Run in a terminal with stdin/stdout connected.`);this.setAuthState(agentId,"failed");return}this.setAuthState(agentId,"pending");try{const existingRuntime=this.runtimeManager.get(agentId);if(existingRuntime){logInfo3(`Stopping existing runtime for agent ${agentId} before Terminal Auth`);await this.runtimeManager.terminate(agentId)}const baseSpawnCommand=this.registry.resolve(agentId);const terminalArgs=selectedMethod.args??[];const terminalEnv={...process.env,...selectedMethod.env??{}};logInfo3(`Launching Terminal Auth for ${agentId}: ${baseSpawnCommand.command} ${terminalArgs.join(" ")}`);const exitCode=await this.runTerminalAuthProcess(baseSpawnCommand.command,terminalArgs,terminalEnv);if(exitCode===0){logInfo3(`Terminal Auth process exited successfully for ${agentId}`);const authVerified=await this.verifyTerminalAuthSuccess(agentId);if(authVerified){logInfo3(`Terminal Auth verified for ${agentId}`);this.setAuthState(agentId,"authenticated")}else{logError3(`Terminal Auth completed but verification failed for ${agentId}`);this.setAuthState(agentId,"failed")}}else{logError3(`Terminal Auth process exited with code ${exitCode} for ${agentId}`);this.setAuthState(agentId,"failed")}}catch(error){const errorMessage=error instanceof Error?error.message:String(error);logError3(`Terminal Auth error for agent ${agentId}: ${errorMessage}`);this.setAuthState(agentId,"failed")}}runTerminalAuthProcess(command,args,env){return new Promise((resolve2,reject)=>{logInfo3(`Spawning Terminal Auth process: ${command} ${args.join(" ")}`);const child=this.spawnFn(command,args,{env,stdio:"inherit",shell:false});const timeoutId=setTimeout(()=>{logError3(`Terminal Auth process timed out after ${TERMINAL_AUTH_TIMEOUT_MS}ms`);child.kill("SIGTERM");setTimeout(()=>{if(!child.killed){child.kill("SIGKILL")}},5e3)},TERMINAL_AUTH_TIMEOUT_MS);child.on("error",error=>{clearTimeout(timeoutId);reject(error)});child.on("exit",(code,signal)=>{clearTimeout(timeoutId);if(signal){logError3(`Terminal Auth process killed by signal: ${signal}`);resolve2(1)}else{resolve2(code??1)}})})}async verifyTerminalAuthSuccess(agentId){try{let spawnCommand=this.registry.resolve(agentId);const agentEnv=getAgentEnv(this.apiKeys,agentId);if(Object.keys(agentEnv).length>0){spawnCommand={...spawnCommand,env:{...spawnCommand.env,...agentEnv}}}const runtime=await this.runtimeManager.getOrSpawn(agentId,spawnCommand);return runtime.state==="running"}catch(error){const errorMessage=error instanceof Error?error.message:String(error);logError3(`Failed to verify Terminal Auth for ${agentId}: ${errorMessage}`);return false}}async attemptOAuthAuthentication(agentId,oauthMethods){if(!this.authManager){logError3(`OAuth authentication required for agent ${agentId}, but AuthManager not available`);this.setAuthState(agentId,"failed");return}const selectedMethod=oauthMethods[0];const providerId=selectedMethod.providerId;logInfo3(`Agent ${agentId} requires OAuth authentication with provider: ${providerId}`);logInfo3(`Initiating OAuth 2.1 Authorization Code flow with PKCE for ${providerId}`);this.setAuthState(agentId,"pending");try{const result=await this.authManager.authenticateAgent(providerId);if(result.success){logInfo3(`OAuth authentication successful for agent ${agentId} with provider ${providerId}`);this.setAuthState(agentId,"authenticated");await this.sendOAuthCredentialsToAgent(agentId,selectedMethod)}else{const errorMsg=result.error?.message??"Unknown error";const errorCode=result.error?.code??"UNKNOWN";logError3(`OAuth authentication failed for agent ${agentId}: [${errorCode}] ${errorMsg}`);this.setAuthState(agentId,"failed")}}catch(error){const errorMessage=error instanceof Error?error.message:String(error);logError3(`OAuth authentication error for agent ${agentId}: ${errorMessage}`);this.setAuthState(agentId,"failed")}}async sendOAuthCredentialsToAgent(agentId,method){if(!this.authManager){logError3(`Cannot send OAuth credentials: AuthManager not available`);return}const token=await this.authManager.getTokenForAgent(agentId,method.providerId);if(!token){logError3(`No OAuth token available for agent ${agentId} after successful auth`);return}let runtime;try{let spawnCommand=this.registry.resolve(agentId);const agentEnv=getAgentEnv(this.apiKeys,agentId);if(Object.keys(agentEnv).length>0){spawnCommand={...spawnCommand,env:{...spawnCommand.env,...agentEnv}}}runtime=await this.runtimeManager.getOrSpawn(agentId,spawnCommand)}catch(error){logError3(`Failed to get runtime for OAuth credential injection: ${error.message}`);return}const authRequest={jsonrpc:"2.0",id:`auth-${agentId}-${Date.now()}`,method:"authenticate",params:{methodId:method.id,credentials:{accessToken:token}}};const transformed=transformMessage(authRequest);const serialized=JSON.stringify(transformed)+"\n";if(runtime.process.stdin){runtime.process.stdin.write(serialized,error=>{if(error){logError3(`Failed to send OAuth authenticate request to ${agentId}: ${error.message}`)}else{logInfo3(`Sent OAuth authenticate request to agent ${agentId}`)}})}}async attemptApiKeyAuthentication(agentId,authMethods){const apiKey=getAgentApiKey(this.apiKeys,agentId);if(!apiKey){logError3(`No API key found for agent ${agentId}, authentication will fail`);this.setAuthState(agentId,"failed");return}const apiKeyMethods=getApiKeyMethods(authMethods);const SAFE_API_KEY_METHODS=["api-key","openai-api-key","github-api-key","google-api-key","azure-api-key","cognito-api-key"];const selectedMethod=apiKeyMethods.find(m=>SAFE_API_KEY_METHODS.includes(m.id));if(!selectedMethod){logError3(`No safe API key method available for agent ${agentId}, skipping auto-auth`);this.setAuthState(agentId,"failed");return}logInfo3(`Authenticating agent ${agentId} with API key method: ${selectedMethod.id} (providerId: ${selectedMethod.providerId??"none"})`);let runtime;try{let spawnCommand=this.registry.resolve(agentId);const agentEnv=getAgentEnv(this.apiKeys,agentId);if(Object.keys(agentEnv).length>0){spawnCommand={...spawnCommand,env:{...spawnCommand.env,...agentEnv}}}runtime=await this.runtimeManager.getOrSpawn(agentId,spawnCommand)}catch(error){logError3(`Failed to get runtime for authentication: ${error.message}`);this.setAuthState(agentId,"failed");return}const authRequest={jsonrpc:"2.0",id:`auth-${agentId}-${Date.now()}`,method:"authenticate",params:{methodId:selectedMethod.id,credentials:{apiKey}}};const transformed=transformMessage(authRequest);const serialized=JSON.stringify(transformed)+"\n";if(runtime.process.stdin){runtime.process.stdin.write(serialized,error=>{if(error){logError3(`Failed to send authenticate request to ${agentId}: ${error.message}`);this.setAuthState(agentId,"failed")}else{logInfo3(`Sent authenticate request to agent ${agentId}`);this.setAuthState(agentId,"authenticated")}})}}get pendingCount(){return this.pendingRequests.size}isPending(id){return this.pendingRequests.has(id)}clearPending(){this.pendingRequests.clear()}clearQueues(){for(const[agentId,queue]of this.requestQueue.entries()){for(const queuedRequest of queue){const id=extractId(queuedRequest.message);queuedRequest.resolve(createErrorResponse(id,RoutingErrorCodes.SPAWN_FAILED,"Router shutdown",{agentId,reason:"Router is shutting down"}))}}this.requestQueue.clear();this.authState.clear();this.agentOAuthRequirements.clear();logInfo3("Cleared all request queues, auth state, and OAuth requirements")}resetAuthState(agentId){this.setAuthState(agentId,"none")}getAgentOAuthRequirement(agentId){return this.agentOAuthRequirements.get(agentId)}setAgentOAuthRequirement(agentId,providerId){this.agentOAuthRequirements.set(agentId,providerId);logInfo3(`Set OAuth requirement for agent ${agentId}: provider ${providerId}`)}clearAgentOAuthRequirement(agentId){this.agentOAuthRequirements.delete(agentId);logInfo3(`Cleared OAuth requirement for agent ${agentId}`)}}}});function formatLogMessage(level,message,context=LOG_CONTEXT){const timestamp=new Date().toISOString();return`[${timestamp}] [${level}] [${context}] ${message}`}function log(level,message,context){const formatted=formatLogMessage(level,message,context);console.error(formatted)}function logExit(agentId,exitCode,signal){if(signal){log("INFO",`Agent "${agentId}" exited with signal ${signal}`)}else if(exitCode!==null){log("INFO",`Agent "${agentId}" exited with code ${exitCode}`)}else{log("INFO",`Agent "${agentId}" exited`)}}function logInfo4(message,context){log("INFO",message,context)}function logWarn(message,context){log("WARN",message,context)}function logError4(message,context){log("ERROR",message,context)}var LOG_CONTEXT;var init_log=__esm({"workers-registry/registry-launcher/src/log.ts"(){"use strict";LOG_CONTEXT="registry-launcher"}});import*as readline from"readline";var PROVIDER_INFO,TerminalAuthFlow;var init_terminal_auth_flow=__esm({"workers-registry/registry-launcher/src/auth/flows/terminal-auth-flow.ts"(){"use strict";init_types2();PROVIDER_INFO=[{id:"github",name:"GitHub",requiresClientSecret:true,requiresCustomEndpoints:false,supportsApiKey:true,supportsOAuth:true,apiKeyLabel:"Personal Access Token",apiKeyEnvVar:"GITHUB_TOKEN"},{id:"google",name:"Google",requiresClientSecret:true,requiresCustomEndpoints:false,supportsApiKey:false,supportsOAuth:true},{id:"cognito",name:"AWS Cognito",requiresClientSecret:true,requiresCustomEndpoints:true,supportsApiKey:false,supportsOAuth:true},{id:"azure",name:"Microsoft Entra ID",requiresClientSecret:true,requiresCustomEndpoints:true,supportsApiKey:false,supportsOAuth:true},{id:"oidc",name:"Generic OIDC",requiresClientSecret:true,requiresCustomEndpoints:true,supportsApiKey:false,supportsOAuth:true}];TerminalAuthFlow=class{credentialStore;validateCredentials;input;output;rl=null;constructor(dependencies){this.credentialStore=dependencies.credentialStore;this.validateCredentials=dependencies.validateCredentials;this.input=dependencies.input??process.stdin;this.output=dependencies.output??process.stderr}async execute(providerId){this.rl=readline.createInterface({input:this.input,output:this.output});try{this.writeLine("\n=== OAuth Authentication Setup ===\n");const selectedProvider=providerId??await this.selectProvider();const providerInfo=PROVIDER_INFO.find(p=>p.id===selectedProvider);if(!providerInfo){return{useBrowserOAuth:false,authResult:{success:false,providerId:selectedProvider,error:{code:"UNSUPPORTED_PROVIDER",message:`Provider '${selectedProvider}' is not supported.`,details:{supportedProviders:VALID_PROVIDER_IDS}}}}}this.writeLine(`
|
|
5
5
|
Configuring ${providerInfo.name}...
|
|
6
6
|
`);if(providerInfo.supportsOAuth){const authMode=await this.selectAuthenticationMode(providerInfo);if(authMode==="browser-oauth"){this.writeLine("\nBrowser OAuth selected. Launching browser authentication flow...\n");return{useBrowserOAuth:true,providerId:selectedProvider}}}const result=await this.collectAndValidateWithRetry(selectedProvider,providerInfo);return{useBrowserOAuth:false,authResult:result}}catch(error){const errorMessage=error instanceof Error?error.message:String(error);console.error(`[TerminalAuthFlow] Error: ${errorMessage}`);const errorProviderId=providerId||"github";return{useBrowserOAuth:false,authResult:{success:false,providerId:errorProviderId,error:{code:"PROVIDER_ERROR",message:`Terminal auth flow failed: ${errorMessage}`}}}}finally{this.cleanup()}}async selectAuthenticationMode(providerInfo){this.writeLine(`${providerInfo.name} supports multiple authentication methods:
|
|
7
7
|
`);this.writeLine(" 1. Browser OAuth (recommended) - Opens browser for secure authentication");this.writeLine(" 2. Manual API Key - Enter credentials directly in terminal\n");const selection=await this.promptSelection("Select authentication method (1-2) [default: 1]: ",1,2,1);return selection===1?"browser-oauth":"manual-api-key"}async collectAndValidateWithRetry(selectedProvider,providerInfo){let credentials=null;let validationResult=null;let attempts=0;const maxAttempts=3;const useApiKey=providerInfo.supportsApiKey;while(attempts<maxAttempts){attempts++;if(useApiKey){credentials=await this.collectApiKeyCredentials(providerInfo)}else{credentials=await this.collectCredentials(providerInfo)}this.writeLine("\nValidating credentials...");validationResult=await this.validateCredentials(selectedProvider,credentials);if(validationResult.valid){break}this.writeLine(`
|