@stdiobus/workers-registry 1.3.18 → 1.3.20
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 +89 -43
- package/launch/.gitkeep +1 -0
- package/launch/index.js +1 -1
- package/launch/index.js.map +2 -2
- package/out/dist/workers-registry/acp-registry/acp-registry-config.json +10 -0
- package/out/dist/workers-registry/acp-registry/index.js +5 -0
- package/out/dist/workers-registry/acp-registry/index.js.map +7 -0
- package/out/dist/workers-registry/acp-worker/index.js +3 -3
- package/out/dist/workers-registry/acp-worker/index.js.map +3 -3
- package/out/dist/workers-registry/index.js +2 -2
- package/out/dist/workers-registry/mcp-to-acp-proxy/proxy.js.map +1 -1
- package/out/tsc/workers-registry/acp-worker/src/mcp-proxy/converter.d.ts +3 -0
- package/out/tsc/workers-registry/acp-worker/src/stdio/index.d.ts +1 -0
- package/out/tsc/workers-registry/acp-worker/src/stdio/session-id-router.d.ts +21 -0
- package/out/tsc/workers-registry/acp-worker/src/stdio/session-id-router.test.d.ts +1 -0
- package/package.json +1 -1
- package/out/dist/workers-registry/acp-registry/registry-launcher-client.js +0 -52
- package/out/dist/workers-registry/acp-registry/registry-launcher-client.js.map +0 -7
- package/out/dist/workers-registry/acp-registry/registry-launcher-config.json +0 -30
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# stdio Bus – Worker Registry
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@stdiobus/workers-registry)
|
|
4
4
|
[](https://github.com/stdiobus/workers-registry/blob/main/LICENSE)
|
|
@@ -39,7 +39,7 @@ stdio Bus kernel provides the core protocol and message routing infrastructure.
|
|
|
39
39
|
graph TB
|
|
40
40
|
Client[Client Application] -->|TCP/Unix Socket| Kernel[stdio Bus kernel]
|
|
41
41
|
Kernel -->|NDJSON stdin/stdout| ACP[ACP Worker]
|
|
42
|
-
Kernel -->|NDJSON stdin/stdout| Registry[Registry
|
|
42
|
+
Kernel -->|NDJSON stdin/stdout| Registry[ACP Registry Worker – acp-registry]
|
|
43
43
|
Kernel -->|NDJSON stdin/stdout| Echo[Echo Worker]
|
|
44
44
|
Kernel -->|NDJSON stdin/stdout| Proxy[MCP-to-ACP Proxy]
|
|
45
45
|
|
|
@@ -59,17 +59,6 @@ graph TB
|
|
|
59
59
|
style Proxy fill:#e67e22,stroke:#a04000,stroke-width:2px,color:#fff
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
**Architecture layers:**
|
|
63
|
-
```
|
|
64
|
-
stdio Bus kernel ← https://github.com/stdiobus/stdiobus
|
|
65
|
-
↓ (spawns workers via stdin/stdout NDJSON)
|
|
66
|
-
Workers Registry (this repo)
|
|
67
|
-
├── ACP Worker (Agent Client Protocol)
|
|
68
|
-
├── Registry Launcher (ACP Registry integration)
|
|
69
|
-
├── MCP-to-ACP Proxy (protocol bridge)
|
|
70
|
-
└── Echo Worker (testing/examples)
|
|
71
|
-
```
|
|
72
|
-
|
|
73
62
|
## Prerequisites
|
|
74
63
|
|
|
75
64
|
- stdio Bus kernel - available via [Docker](https://hub.docker.com/r/stdiobus/stdiobus) or [build from source](https://github.com/stdiobus/stdiobus)
|
|
@@ -79,11 +68,15 @@ Workers Registry (this repo)
|
|
|
79
68
|
|
|
80
69
|
| Worker | Description | Protocol | Command |
|
|
81
70
|
|--------|-------------|----------|---------|
|
|
82
|
-
| `acp-
|
|
83
|
-
| `
|
|
84
|
-
| `
|
|
85
|
-
| `
|
|
86
|
-
| `
|
|
71
|
+
| `acp-registry` | Registry Launcher worker that routes to ACP Registry agents (requires `api-keys.json`) | ACP | `node ./node_modules/@stdiobus/workers-registry/launch acp-registry` |
|
|
72
|
+
| `acp-worker` | Full ACP protocol implementation (standalone agent; does **not** route to ACP Registry) | ACP | `node ./node_modules/@stdiobus/workers-registry/launch acp-worker` |
|
|
73
|
+
| `registry-launcher` | Registry Launcher implementation module used by `acp-registry` (not a launch target) | ACP | Use `acp-registry` |
|
|
74
|
+
| `mcp-to-acp-proxy` | Bridges MCP clients (like IDEs) to ACP agents | MCP → ACP | `node ./node_modules/@stdiobus/workers-registry/launch mcp-to-acp-proxy` |
|
|
75
|
+
| `echo-worker` | Simple echo worker for testing NDJSON protocol | NDJSON | `node ./node_modules/@stdiobus/workers-registry/launch echo-worker` |
|
|
76
|
+
| `mcp-echo-server` | MCP server example for testing | MCP | `node ./node_modules/@stdiobus/workers-registry/launch mcp-echo-server` |
|
|
77
|
+
|
|
78
|
+
**Note:** The universal launcher is `@stdiobus/workers-registry/launch`. In this repo, use
|
|
79
|
+
`node ./launch/index.js <worker-name>` after `npm run build`.
|
|
87
80
|
|
|
88
81
|
## Package API
|
|
89
82
|
|
|
@@ -104,6 +97,9 @@ import { workers } from '@stdiobus/workers-registry/workers';
|
|
|
104
97
|
console.log(workers['acp-worker'].entrypoint);
|
|
105
98
|
```
|
|
106
99
|
|
|
100
|
+
**Note:** `acp-registry` is a worker runtime launched via
|
|
101
|
+
`@stdiobus/workers-registry/launch` and is not exported as a module.
|
|
102
|
+
|
|
107
103
|
### TypeScript Support
|
|
108
104
|
|
|
109
105
|
Full TypeScript definitions are included:
|
|
@@ -133,7 +129,36 @@ docker pull stdiobus/stdiobus:latest
|
|
|
133
129
|
|
|
134
130
|
See [stdio Bus kernel repository](https://github.com/stdiobus/stdiobus) for build instructions.
|
|
135
131
|
|
|
136
|
-
### 3. Run with ACP
|
|
132
|
+
### 3. Run with ACP Registry (recommended for real agents)
|
|
133
|
+
|
|
134
|
+
**Create config.json:**
|
|
135
|
+
```json
|
|
136
|
+
{
|
|
137
|
+
"pools": [
|
|
138
|
+
{
|
|
139
|
+
"id": "acp-registry",
|
|
140
|
+
"command": "node",
|
|
141
|
+
"args": [
|
|
142
|
+
"./stdiobus/node_modules/@stdiobus/workers-registry/launch",
|
|
143
|
+
"acp-registry"
|
|
144
|
+
],
|
|
145
|
+
"instances": 1
|
|
146
|
+
}
|
|
147
|
+
]
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Important:** Place `api-keys.json` next to your stdio Bus config (working directory),
|
|
152
|
+
or pass a custom config file (third arg to `launch acp-registry`) with an absolute
|
|
153
|
+
`apiKeysPath`. In this repo, the default file is
|
|
154
|
+
`workers-registry/acp-registry/acp-registry-config.json`.
|
|
155
|
+
Use the same Docker/binary commands below (they run `config.json`), and ensure
|
|
156
|
+
`api-keys.json` is mounted into the container when using Docker.
|
|
157
|
+
|
|
158
|
+
### 4. Run with ACP Worker
|
|
159
|
+
|
|
160
|
+
**Note:** `acp-worker` is a standalone ACP agent for SDK/protocol testing. It does **not**
|
|
161
|
+
route to the ACP Registry. Use `acp-registry` when you need real registry agents.
|
|
137
162
|
|
|
138
163
|
**Create config.json:**
|
|
139
164
|
```json
|
|
@@ -169,7 +194,11 @@ docker run -p 9000:9000 \
|
|
|
169
194
|
### 5. Test Connection
|
|
170
195
|
|
|
171
196
|
```bash
|
|
197
|
+
# ACP worker (standalone)
|
|
172
198
|
echo '{"jsonrpc":"2.0","id":"1","method":"initialize","params":{"clientInfo":{"name":"test","version":"1.0"}}}' | nc localhost 9000
|
|
199
|
+
|
|
200
|
+
# ACP Registry worker (route to a registry agent)
|
|
201
|
+
echo '{"jsonrpc":"2.0","id":"1","method":"initialize","params":{"agentId":"claude-acp","clientInfo":{"name":"test","version":"1.0"}}}' | nc localhost 9000
|
|
173
202
|
```
|
|
174
203
|
|
|
175
204
|
---
|
|
@@ -181,18 +210,21 @@ echo '{"jsonrpc":"2.0","id":"1","method":"initialize","params":{"clientInfo":{"n
|
|
|
181
210
|
The simplest way to run any worker:
|
|
182
211
|
|
|
183
212
|
```bash
|
|
184
|
-
# Run any worker by name
|
|
185
|
-
node ./node_modules/@stdiobus/workers-registry/
|
|
213
|
+
# Run any worker by name (package install)
|
|
214
|
+
node ./node_modules/@stdiobus/workers-registry/launch <worker-name>
|
|
215
|
+
|
|
216
|
+
# Run any worker by name (this repo, after build)
|
|
217
|
+
node ./launch/index.js <worker-name>
|
|
186
218
|
|
|
187
219
|
# Available workers:
|
|
220
|
+
# - acp-registry
|
|
188
221
|
# - acp-worker
|
|
189
|
-
# - acp-registry
|
|
190
222
|
# - echo-worker
|
|
191
223
|
# - mcp-echo-server
|
|
192
224
|
# - mcp-to-acp-proxy
|
|
193
225
|
|
|
194
226
|
# Example: Run echo worker for testing
|
|
195
|
-
node ./node_modules/@stdiobus/workers-registry/
|
|
227
|
+
node ./node_modules/@stdiobus/workers-registry/launch echo-worker
|
|
196
228
|
```
|
|
197
229
|
|
|
198
230
|
### Using in stdio Bus Configuration
|
|
@@ -212,23 +244,25 @@ node ./node_modules/@stdiobus/workers-registry/out/dist/index.js echo-worker
|
|
|
212
244
|
}
|
|
213
245
|
```
|
|
214
246
|
|
|
215
|
-
**Registry
|
|
247
|
+
**ACP Registry Worker with API Keys:**
|
|
216
248
|
```json
|
|
217
249
|
{
|
|
218
250
|
"pools": [
|
|
219
251
|
{
|
|
220
|
-
"id": "registry
|
|
252
|
+
"id": "acp-registry",
|
|
221
253
|
"command": "node",
|
|
222
254
|
"args": [
|
|
223
255
|
"./stdiobus/node_modules/@stdiobus/workers-registry/launch",
|
|
224
|
-
"registry
|
|
225
|
-
"./api-keys.json"
|
|
256
|
+
"acp-registry"
|
|
226
257
|
],
|
|
227
258
|
"instances": 1
|
|
228
259
|
}
|
|
229
260
|
]
|
|
230
261
|
}
|
|
231
262
|
```
|
|
263
|
+
**Note:** `acp-registry` reads `api-keys.json` via its config. The default
|
|
264
|
+
`apiKeysPath` is `./api-keys.json`. You can pass a custom config file as the third
|
|
265
|
+
arg to `launch acp-registry`.
|
|
232
266
|
|
|
233
267
|
**Multiple Workers:**
|
|
234
268
|
```json
|
|
@@ -264,7 +298,11 @@ Configure MCP-to-ACP Proxy in Kiro's MCP settings:
|
|
|
264
298
|
{
|
|
265
299
|
"mcpServers": {
|
|
266
300
|
"stdio-bus-acp": {
|
|
267
|
-
"command": "
|
|
301
|
+
"command": "node",
|
|
302
|
+
"args": [
|
|
303
|
+
"./node_modules/@stdiobus/workers-registry/launch",
|
|
304
|
+
"mcp-to-acp-proxy"
|
|
305
|
+
],
|
|
268
306
|
"env": {
|
|
269
307
|
"ACP_HOST": "localhost",
|
|
270
308
|
"ACP_PORT": "9000",
|
|
@@ -275,6 +313,9 @@ Configure MCP-to-ACP Proxy in Kiro's MCP settings:
|
|
|
275
313
|
}
|
|
276
314
|
```
|
|
277
315
|
|
|
316
|
+
**Note:** Run `acp-registry` on the stdio Bus side so `AGENT_ID` resolves to real
|
|
317
|
+
ACP Registry agents. `acp-worker` is a standalone agent and will not route to the registry.
|
|
318
|
+
|
|
278
319
|
---
|
|
279
320
|
|
|
280
321
|
## Worker Documentation
|
|
@@ -320,11 +361,13 @@ Using binary:
|
|
|
320
361
|
|
|
321
362
|
---
|
|
322
363
|
|
|
323
|
-
### Registry
|
|
364
|
+
### ACP Registry Worker (acp-registry)
|
|
324
365
|
|
|
325
366
|
Routes messages to any agent in the [ACP Registry](https://cdn.agentclientprotocol.com/registry/v1/latest/registry.json).
|
|
326
367
|
|
|
327
|
-
**Location:** `workers-registry/acp-
|
|
368
|
+
**Location (worker entrypoint):** `workers-registry/acp-registry/`
|
|
369
|
+
|
|
370
|
+
**Implementation:** `workers-registry/acp-worker/src/registry-launcher/`
|
|
328
371
|
|
|
329
372
|
**Features:**
|
|
330
373
|
- Automatic agent discovery from ACP Registry
|
|
@@ -344,18 +387,21 @@ Routes messages to any agent in the [ACP Registry](https://cdn.agentclientprotoc
|
|
|
344
387
|
{
|
|
345
388
|
"pools": [
|
|
346
389
|
{
|
|
347
|
-
"id": "registry
|
|
390
|
+
"id": "acp-registry",
|
|
348
391
|
"command": "node",
|
|
349
392
|
"args": [
|
|
350
393
|
"./stdiobus/node_modules/@stdiobus/workers-registry/launch",
|
|
351
|
-
"registry
|
|
352
|
-
"./api-keys.json"
|
|
394
|
+
"acp-registry"
|
|
353
395
|
],
|
|
354
396
|
"instances": 1
|
|
355
397
|
}
|
|
356
398
|
]
|
|
357
399
|
}
|
|
358
400
|
```
|
|
401
|
+
**Note:** `acp-registry` uses its default config when no path is provided. You can
|
|
402
|
+
pass a custom config file as the third arg to `launch acp-registry`. The default
|
|
403
|
+
file in this repo is `workers-registry/acp-registry/acp-registry-config.json`, which
|
|
404
|
+
expects `api-keys.json` at `./api-keys.json` unless you override `apiKeysPath`.
|
|
359
405
|
|
|
360
406
|
**Run:**
|
|
361
407
|
|
|
@@ -365,7 +411,7 @@ docker run \
|
|
|
365
411
|
--name stdiobus-registry \
|
|
366
412
|
-p 9000:9000 \
|
|
367
413
|
-v $(pwd):/stdiobus:ro \
|
|
368
|
-
-v $(pwd)/workers-registry/acp-registry/registry-
|
|
414
|
+
-v $(pwd)/workers-registry/acp-registry/acp-registry-config.json:/config.json:ro \
|
|
369
415
|
-v $(pwd)/api-keys.json:/api-keys.json:ro \
|
|
370
416
|
stdiobus/stdiobus:latest \
|
|
371
417
|
--config /config.json --tcp 0.0.0.0:9000
|
|
@@ -373,7 +419,7 @@ docker run \
|
|
|
373
419
|
|
|
374
420
|
Using binary:
|
|
375
421
|
```bash
|
|
376
|
-
./stdio_bus --config workers-registry/acp-registry/registry-
|
|
422
|
+
./stdio_bus --config workers-registry/acp-registry/acp-registry-config.json --tcp 0.0.0.0:9000
|
|
377
423
|
```
|
|
378
424
|
|
|
379
425
|
---
|
|
@@ -386,7 +432,7 @@ Bridges MCP clients (like IDE) to ACP agents through stdio Bus.
|
|
|
386
432
|
|
|
387
433
|
**Architecture:**
|
|
388
434
|
```
|
|
389
|
-
IDE (MCP Client) → MCP-to-ACP Proxy → stdio Bus → Registry
|
|
435
|
+
IDE (MCP Client) → MCP-to-ACP Proxy → stdio Bus → ACP Registry Worker (acp-registry) → ACP Agent
|
|
390
436
|
```
|
|
391
437
|
|
|
392
438
|
**Configuration for IDE:**
|
|
@@ -691,14 +737,14 @@ echo '{"jsonrpc":"2.0","id":"1","method":"initialize","params":{"clientInfo":{"n
|
|
|
691
737
|
docker stop stdiobus-acp-test && docker rm stdiobus-acp-test
|
|
692
738
|
```
|
|
693
739
|
|
|
694
|
-
**Test Registry
|
|
740
|
+
**Test ACP Registry Worker – acp-registry:**
|
|
695
741
|
```bash
|
|
696
|
-
# Start stdio Bus with Registry
|
|
742
|
+
# Start stdio Bus with ACP Registry worker
|
|
697
743
|
docker run \
|
|
698
744
|
--name stdiobus-registry-test \
|
|
699
745
|
-p 9000:9000 \
|
|
700
746
|
-v $(pwd):/stdiobus:ro \
|
|
701
|
-
-v $(pwd)/workers-registry/acp-registry/registry-
|
|
747
|
+
-v $(pwd)/workers-registry/acp-registry/acp-registry-config.json:/config.json:ro \
|
|
702
748
|
-v $(pwd)/api-keys.json:/api-keys.json:ro \
|
|
703
749
|
stdiobus/stdiobus:latest \
|
|
704
750
|
--config /config.json --tcp 0.0.0.0:9000
|
|
@@ -776,7 +822,7 @@ workers-registry/
|
|
|
776
822
|
│ │ ├── session/ # Session management
|
|
777
823
|
│ │ └── registry-launcher/ # Registry Launcher implementation
|
|
778
824
|
│ └── tests/ # Test suites
|
|
779
|
-
├── acp-registry/ # Registry
|
|
825
|
+
├── acp-registry/ # ACP Registry worker entrypoint + configs
|
|
780
826
|
├── echo-worker/ # Simple echo worker example
|
|
781
827
|
├── mcp-echo-server/ # MCP server example
|
|
782
828
|
└── mcp-to-acp-proxy/ # MCP-to-ACP protocol bridge
|
|
@@ -838,8 +884,8 @@ nvm install 20 # If using nvm
|
|
|
838
884
|
**Permission errors:**
|
|
839
885
|
```bash
|
|
840
886
|
npm install -g @stdiobus/workers-registry # Global install (may need sudo)
|
|
841
|
-
# Or use
|
|
842
|
-
|
|
887
|
+
# Or use a local install
|
|
888
|
+
node ./node_modules/@stdiobus/workers-registry/launch acp-worker
|
|
843
889
|
```
|
|
844
890
|
|
|
845
891
|
### Runtime Issues
|
|
@@ -917,7 +963,7 @@ npm test
|
|
|
917
963
|
## Worker Documentation
|
|
918
964
|
|
|
919
965
|
- [ACP Worker](https://github.com/stdiobus/workers-registry/tree/main/workers-registry/acp-worker) - Full ACP protocol implementation
|
|
920
|
-
- [Registry
|
|
966
|
+
- [ACP Registry Worker (acp-registry)](https://github.com/stdiobus/workers-registry/tree/main/workers-registry/acp-registry) - ACP Registry integration
|
|
921
967
|
- [Echo Worker](https://github.com/stdiobus/workers-registry/tree/main/workers-registry/echo-worker) - Reference implementation
|
|
922
968
|
- [MCP Echo Server](https://github.com/stdiobus/workers-registry/tree/main/workers-registry/mcp-echo-server) - MCP server example
|
|
923
969
|
- [MCP-to-ACP Proxy](https://github.com/stdiobus/workers-registry/tree/main/workers-registry/mcp-to-acp-proxy) - Protocol bridge
|
package/launch/.gitkeep
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Workers launcher
|
package/launch/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{fileURLToPath}from"url";import{dirname,join}from"path";import{readFile}from"fs/promises";var __filename=fileURLToPath(import.meta.url);var __dirname=dirname(__filename);var STDIO_BUS_WORKERS={"acp-worker":{path:"../out/dist/workers/acp-worker/index.js",description:"Full ACP protocol implementation with MCP integration"},"acp-registry":{path:"../out/dist/workers/acp-registry/
|
|
2
|
+
import{fileURLToPath}from"url";import{dirname,join}from"path";import{readFile}from"fs/promises";var __filename=fileURLToPath(import.meta.url);var __dirname=dirname(__filename);var STDIO_BUS_WORKERS={"acp-worker":{path:"../out/dist/workers-registry/acp-worker/index.js",description:"Full ACP protocol implementation with MCP integration"},"acp-registry":{path:"../out/dist/workers-registry/acp-registry/index.js",description:"Registry Launcher worker for ACP Registry agents"},"echo-worker":{path:"../out/dist/workers-registry/echo-worker/echo-worker.js",description:"Simple echo worker for testing NDJSON protocol"},"mcp-echo-server":{path:"../out/dist/workers-registry/mcp-echo-server/index.js",description:"MCP server example for testing"},"mcp-to-acp-proxy":{path:"../out/dist/workers-registry/mcp-to-acp-proxy/proxy.js",description:"MCP-to-ACP protocol bridge"}};function showUsage(){console.error("Usage: node index.js <worker-name>");console.error("");console.error("Available workers:");for(const[name,config]of Object.entries(STDIO_BUS_WORKERS)){console.error(` - ${name.padEnd(20)} ${config.description}`)}console.error("");console.error("Examples:");console.error(" node index.js acp-worker");console.error(" node index.js echo-worker");console.error(" node index.js mcp-echo-server")}function isValidWorkerName(name){return name in STDIO_BUS_WORKERS}async function main(){const workerName=process.argv[2];if(!workerName){console.error("Error: Worker name is required\n");showUsage();process.exit(1)}if(!isValidWorkerName(workerName)){console.error(`Error: Unknown worker "${workerName}"
|
|
3
3
|
`);showUsage();process.exit(1)}const workerConfig=STDIO_BUS_WORKERS[workerName];const workerPath=workerConfig.path;const absolutePath=join(__dirname,workerPath);try{await readFile(absolutePath);console.error(`[launcher] Starting worker: ${workerName}`);console.error(`[launcher] Description: ${workerConfig.description}`);console.error(`[launcher] Path: ${absolutePath}`);await import(absolutePath)}catch(error){if(error&&typeof error==="object"&&"code"in error&&error.code==="ENOENT"){console.error(`Error: Worker file not found: ${absolutePath}`);console.error("");console.error('Please run "npm run build" first to compile the workers.');process.exit(1)}const errorMessage=error instanceof Error?error.message:String(error);const errorStack=error instanceof Error?error.stack:void 0;console.error(`Error loading worker "${workerName}":`,errorMessage);if(errorStack){console.error(errorStack)}process.exit(1)}}main().catch(error=>{console.error("Fatal error:",error);process.exit(1)});export{STDIO_BUS_WORKERS,isValidWorkerName,main as launch,showUsage};
|
|
4
4
|
//# sourceMappingURL=index.js.map
|
package/launch/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../workers-registry/launch/index.ts"],
|
|
4
|
-
"sourcesContent": ["#!/usr/bin/env node\n\n/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Universal worker launcher for stdio Bus Workers Registry\n *\n * Usage:\n * node index.js <worker-name>\n * node index.js acp-worker\n * node index.js echo-worker\n * node index.js mcp-echo-server\n *\n * This script dynamically imports and runs the specified worker from the compiled output.\n */\n\nimport { fileURLToPath } from 'url';\nimport { dirname, join } from 'path';\nimport { readFile } from 'fs/promises';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n/**\n * Worker configuration mapping worker names to their entry points\n */\ninterface WorkerConfig {\n readonly path: string;\n readonly description: string;\n}\n\n/**\n * Available workers mapping\n */\nconst STDIO_BUS_WORKERS: Readonly<Record<string, WorkerConfig>> = {\n 'acp-worker': {\n path: '../out/dist/workers/acp-worker/index.js',\n description: 'Full ACP protocol implementation with MCP integration'\n },\n 'acp-registry': {\n path: '../out/dist/workers/acp-registry/
|
|
5
|
-
"mappings": ";AAqCA,OAAS,kBAAqB,MAC9B,OAAS,QAAS,SAAY,OAC9B,OAAS,aAAgB,cAEzB,IAAM,WAAa,cAAc,YAAY,GAAG,EAChD,IAAM,UAAY,QAAQ,UAAU,EAapC,IAAM,kBAA4D,CAChE,aAAc,CACZ,KAAM,
|
|
4
|
+
"sourcesContent": ["#!/usr/bin/env node\n\n/*\n * Apache License 2.0\n * Copyright (c) 2025\u2013present Raman Marozau, Target Insight Function.\n * Contact: raman@worktif.com\n *\n * This file is part of the stdio bus protocol reference implementation:\n * stdio_bus_kernel_workers (target: <target_stdio_bus_kernel_workers>).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Universal worker launcher for stdio Bus Workers Registry\n *\n * Usage:\n * node index.js <worker-name>\n * node index.js acp-worker\n * node index.js echo-worker\n * node index.js mcp-echo-server\n *\n * This script dynamically imports and runs the specified worker from the compiled output.\n */\n\nimport { fileURLToPath } from 'url';\nimport { dirname, join } from 'path';\nimport { readFile } from 'fs/promises';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n/**\n * Worker configuration mapping worker names to their entry points\n */\ninterface WorkerConfig {\n readonly path: string;\n readonly description: string;\n}\n\n/**\n * Available workers mapping\n */\nconst STDIO_BUS_WORKERS: Readonly<Record<string, WorkerConfig>> = {\n 'acp-worker': {\n path: '../out/dist/workers-registry/acp-worker/index.js',\n description: 'Full ACP protocol implementation with MCP integration'\n },\n 'acp-registry': {\n path: '../out/dist/workers-registry/acp-registry/index.js',\n description: 'Registry Launcher worker for ACP Registry agents'\n },\n 'echo-worker': {\n path: '../out/dist/workers-registry/echo-worker/echo-worker.js',\n description: 'Simple echo worker for testing NDJSON protocol'\n },\n 'mcp-echo-server': {\n path: '../out/dist/workers-registry/mcp-echo-server/index.js',\n description: 'MCP server example for testing'\n },\n 'mcp-to-acp-proxy': {\n path: '../out/dist/workers-registry/mcp-to-acp-proxy/proxy.js',\n description: 'MCP-to-ACP protocol bridge'\n }\n} as const;\n\n/**\n * Worker name type\n */\ntype WorkerName = keyof typeof STDIO_BUS_WORKERS;\n\n/**\n * Display usage information\n */\nfunction showUsage(): void {\n console.error('Usage: node index.js <worker-name>');\n console.error('');\n console.error('Available workers:');\n\n for (const [name, config] of Object.entries(STDIO_BUS_WORKERS)) {\n console.error(` - ${name.padEnd(20)} ${config.description}`);\n }\n\n console.error('');\n console.error('Examples:');\n console.error(' node index.js acp-worker');\n console.error(' node index.js echo-worker');\n console.error(' node index.js mcp-echo-server');\n}\n\n/**\n * Validate worker name\n */\nfunction isValidWorkerName(name: string): name is WorkerName {\n return name in STDIO_BUS_WORKERS;\n}\n\n/**\n * Main entry point\n */\nasync function main(): Promise<void> {\n const workerName = process.argv[2];\n\n // Check if worker name is provided\n if (!workerName) {\n console.error('Error: Worker name is required\\n');\n showUsage();\n process.exit(1);\n }\n\n // Check if worker exists\n if (!isValidWorkerName(workerName)) {\n console.error(`Error: Unknown worker \"${workerName}\"\\n`);\n showUsage();\n process.exit(1);\n }\n\n const workerConfig = STDIO_BUS_WORKERS[workerName];\n const workerPath = workerConfig.path;\n\n // Resolve absolute path\n const absolutePath = join(__dirname, workerPath);\n\n try {\n // Verify the worker file exists\n await readFile(absolutePath);\n\n // Import and run the worker\n console.error(`[launcher] Starting worker: ${workerName}`);\n console.error(`[launcher] Description: ${workerConfig.description}`);\n console.error(`[launcher] Path: ${absolutePath}`);\n\n await import(absolutePath);\n } catch (error) {\n if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT') {\n console.error(`Error: Worker file not found: ${absolutePath}`);\n console.error('');\n console.error('Please run \"npm run build\" first to compile the workers.');\n process.exit(1);\n }\n\n const errorMessage = error instanceof Error ? error.message : String(error);\n const errorStack = error instanceof Error ? error.stack : undefined;\n\n console.error(`Error loading worker \"${workerName}\":`, errorMessage);\n if (errorStack) {\n console.error(errorStack);\n }\n process.exit(1);\n }\n}\n\n// Run main function\nmain().catch((error) => {\n console.error('Fatal error:', error);\n process.exit(1);\n});\n\n// Export for programmatic usage\nexport { STDIO_BUS_WORKERS, showUsage, isValidWorkerName, main as launch };\nexport type { WorkerConfig, WorkerName };\n"],
|
|
5
|
+
"mappings": ";AAqCA,OAAS,kBAAqB,MAC9B,OAAS,QAAS,SAAY,OAC9B,OAAS,aAAgB,cAEzB,IAAM,WAAa,cAAc,YAAY,GAAG,EAChD,IAAM,UAAY,QAAQ,UAAU,EAapC,IAAM,kBAA4D,CAChE,aAAc,CACZ,KAAM,mDACN,YAAa,uDACf,EACA,eAAgB,CACd,KAAM,qDACN,YAAa,kDACf,EACA,cAAe,CACb,KAAM,0DACN,YAAa,gDACf,EACA,kBAAmB,CACjB,KAAM,wDACN,YAAa,gCACf,EACA,mBAAoB,CAClB,KAAM,yDACN,YAAa,4BACf,CACF,EAUA,SAAS,WAAkB,CACzB,QAAQ,MAAM,oCAAoC,EAClD,QAAQ,MAAM,EAAE,EAChB,QAAQ,MAAM,oBAAoB,EAElC,SAAW,CAAC,KAAM,MAAM,IAAK,OAAO,QAAQ,iBAAiB,EAAG,CAC9D,QAAQ,MAAM,OAAO,KAAK,OAAO,EAAE,CAAC,IAAI,OAAO,WAAW,EAAE,CAC9D,CAEA,QAAQ,MAAM,EAAE,EAChB,QAAQ,MAAM,WAAW,EACzB,QAAQ,MAAM,4BAA4B,EAC1C,QAAQ,MAAM,6BAA6B,EAC3C,QAAQ,MAAM,iCAAiC,CACjD,CAKA,SAAS,kBAAkB,KAAkC,CAC3D,OAAO,QAAQ,iBACjB,CAKA,eAAe,MAAsB,CACnC,MAAM,WAAa,QAAQ,KAAK,CAAC,EAGjC,GAAI,CAAC,WAAY,CACf,QAAQ,MAAM,kCAAkC,EAChD,UAAU,EACV,QAAQ,KAAK,CAAC,CAChB,CAGA,GAAI,CAAC,kBAAkB,UAAU,EAAG,CAClC,QAAQ,MAAM,0BAA0B,UAAU;AAAA,CAAK,EACvD,UAAU,EACV,QAAQ,KAAK,CAAC,CAChB,CAEA,MAAM,aAAe,kBAAkB,UAAU,EACjD,MAAM,WAAa,aAAa,KAGhC,MAAM,aAAe,KAAK,UAAW,UAAU,EAE/C,GAAI,CAEF,MAAM,SAAS,YAAY,EAG3B,QAAQ,MAAM,+BAA+B,UAAU,EAAE,EACzD,QAAQ,MAAM,2BAA2B,aAAa,WAAW,EAAE,EACnE,QAAQ,MAAM,oBAAoB,YAAY,EAAE,EAEhD,MAAM,OAAO,aACf,OAAS,MAAO,CACd,GAAI,OAAS,OAAO,QAAU,UAAY,SAAU,OAAS,MAAM,OAAS,SAAU,CACpF,QAAQ,MAAM,iCAAiC,YAAY,EAAE,EAC7D,QAAQ,MAAM,EAAE,EAChB,QAAQ,MAAM,0DAA0D,EACxE,QAAQ,KAAK,CAAC,CAChB,CAEA,MAAM,aAAe,iBAAiB,MAAQ,MAAM,QAAU,OAAO,KAAK,EAC1E,MAAM,WAAa,iBAAiB,MAAQ,MAAM,MAAQ,OAE1D,QAAQ,MAAM,yBAAyB,UAAU,KAAM,YAAY,EACnE,GAAI,WAAY,CACd,QAAQ,MAAM,UAAU,CAC1B,CACA,QAAQ,KAAK,CAAC,CAChB,CACF,CAGA,KAAK,EAAE,MAAO,OAAU,CACtB,QAAQ,MAAM,eAAgB,KAAK,EACnC,QAAQ,KAAK,CAAC,CAChB,CAAC",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "Registry Launcher worker configuration (default for acp-registry worker)",
|
|
3
|
+
"_description": "Configuration options for the Registry Launcher worker process",
|
|
4
|
+
"registryUrl": "https://cdn.agentclientprotocol.com/registry/v1/latest/registry.json",
|
|
5
|
+
"_comment_registryUrl": "URL to fetch the ACP Registry JSON. Can also be set via ACP_REGISTRY_URL environment variable",
|
|
6
|
+
"apiKeysPath": "./api-keys.json",
|
|
7
|
+
"_comment_apiKeysPath": "Path to the API keys JSON file. Can be absolute or relative to the worker process. Can also be set via ACP_API_KEYS_PATH environment variable",
|
|
8
|
+
"shutdownTimeoutSec": 5,
|
|
9
|
+
"_comment_shutdownTimeoutSec": "Timeout in seconds for graceful shutdown. Agent processes receive SIGTERM, then SIGKILL after this timeout"
|
|
10
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
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/acp-worker/src/registry-launcher/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')}}return config}function applyEnvironmentOverrides(config){const envRegistryUrl=process.env[ENV_REGISTRY_URL];const envApiKeysPath=process.env[ENV_API_KEYS_PATH];const overrides={};if(isNonEmptyString(envRegistryUrl)){overrides.registryUrl=envRegistryUrl}if(isNonEmptyString(envApiKeysPath)){overrides.apiKeysPath=envApiKeysPath}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;var init_config=__esm({"workers-registry/acp-worker/src/registry-launcher/config/config.ts"(){"use strict";init_types();ENV_REGISTRY_URL="ACP_REGISTRY_URL";ENV_API_KEYS_PATH="ACP_API_KEYS_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}var init_api_keys=__esm({"workers-registry/acp-worker/src/registry-launcher/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/acp-worker/src/registry-launcher/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"}}}});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 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}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}}var ENV_REGISTRY_URL2,RegistryFetchError,RegistryParseError,AgentNotFoundError,RegistryIndex;var init_registry=__esm({"workers-registry/acp-worker/src/registry-launcher/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"}};RegistryIndex=class{registryUrl;registry=null;agentMap=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();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}}}});function logError2(message){const timestamp=new Date().toISOString();console.error(`[${timestamp}] [ERROR] [ndjson] ${message}`)}var NDJSONHandler;var init_ndjson_handler=__esm({"workers-registry/acp-worker/src/registry-launcher/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/acp-worker/src/registry-launcher/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
|
+
`)});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/acp-worker/src/registry-launcher/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 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,MessageRouter;var init_message_router=__esm({"workers-registry/acp-worker/src/registry-launcher/router/message-router.ts"(){"use strict";init_registry();init_api_keys();RoutingErrorCodes={MISSING_AGENT_ID:-32600,AGENT_NOT_FOUND:-32001,PLATFORM_NOT_SUPPORTED:-32002,SPAWN_FAILED:-32003};MessageRouter=class{registry;runtimeManager;writeCallback;apiKeys;pendingRequests=new Map;authState=new Map;sessionIdMap=new Map;constructor(registry,runtimeManager,writeCallback,apiKeys={}){this.registry=registry;this.runtimeManager=runtimeManager;this.writeCallback=writeCallback;this.apiKeys=apiKeys}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")}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}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 msg=message;const clientSessionId=typeof msg.sessionId==="string"?msg.sessionId:void 0;this.pendingRequests.set(id,{id,agentId,timestamp:Date.now(),clientSessionId})}const transformedMessage=transformMessage(message);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}handleAgentResponse(agentId,response){const id=extractId(response);const msg=response;if(id!==null){const pending=this.pendingRequests.get(id);if(pending&&pending.agentId===agentId){const result=msg.result;if(result&&Array.isArray(result.authMethods)&&result.authMethods.length>0){logInfo3(`Agent ${agentId} requires authentication, attempting auto-auth`);this.authState.set(agentId,"pending");void this.attemptAuthentication(agentId,result.authMethods)}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&&msg.method){logInfo3(`Received notification: ${msg.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{logInfo3(`Forwarding notification with unmapped agentSessionId: ${agentSessionId}`);this.writeCallback(response);return}}else{const topLevelSessionId=msg.sessionId;if(topLevelSessionId){this.writeCallback(response);return}else{logError3(`Notification without sessionId: ${msg.method}, adding default sessionId for routing`);const enriched={...msg,sessionId:"global-notifications"};this.writeCallback(enriched);return}}}this.writeCallback(response)}async attemptAuthentication(agentId,authMethods){const apiKey=getAgentApiKey(this.apiKeys,agentId);if(!apiKey){logError3(`No API key found for agent ${agentId}, authentication will fail`);this.authState.set(agentId,"none");return}let selectedMethod=authMethods.find(m=>m.id==="openai-api-key");if(!selectedMethod){selectedMethod=authMethods.find(m=>m.id.includes("api-key")||m.id.includes("apikey"))}if(!selectedMethod){selectedMethod=authMethods[0]}logInfo3(`Authenticating agent ${agentId} with method: ${selectedMethod.id}`);let runtime;try{const spawnCommand=this.registry.resolve(agentId);runtime=await this.runtimeManager.getOrSpawn(agentId,spawnCommand)}catch(error){logError3(`Failed to get runtime for authentication: ${error.message}`);this.authState.set(agentId,"none");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.authState.set(agentId,"none")}else{logInfo3(`Sent authenticate request to agent ${agentId}`);this.authState.set(agentId,"authenticated")}})}}get pendingCount(){return this.pendingRequests.size}isPending(id){return this.pendingRequests.has(id)}clearPending(){this.pendingRequests.clear()}}}});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 logError4(message,context){log("ERROR",message,context)}var LOG_CONTEXT;var init_log=__esm({"workers-registry/acp-worker/src/registry-launcher/log.ts"(){"use strict";LOG_CONTEXT="registry-launcher"}});var registry_launcher_exports={};function parseArgs(){const args=process.argv.slice(2);if(args.length>0&&args[0]&&!args[0].startsWith("-")){return args[0]}return void 0}function setupSignalHandlers(runtimeManager,shutdownTimeoutMs){const shutdown=async()=>{if(isShuttingDown){return}isShuttingDown=true;logInfo4("Received shutdown signal, initiating graceful shutdown");try{await runtimeManager.terminateAll(shutdownTimeoutMs);logInfo4("All agent processes terminated");process.exit(ExitCodes.SUCCESS)}catch(error){logError4(`Error during shutdown: ${error.message}`);process.exit(ExitCodes.FATAL_ERROR)}};process.on("SIGTERM",()=>{void shutdown()});process.on("SIGINT",()=>{void shutdown()});return shutdown}function setupStdinHandler(router,ndjsonHandler){ndjsonHandler.onMessage(async message=>{try{const errorResponse=await router.route(message);if(errorResponse){ndjsonHandler.write(errorResponse)}}catch(error){logError4(`Unexpected error routing message: ${error.message}`)}});ndjsonHandler.onError((error,line)=>{logError4(`Failed to parse NDJSON: ${error.message} - Line: ${line.slice(0,100)}`)});process.stdin.setEncoding("utf8");process.stdin.on("data",chunk=>{const buffer=typeof chunk==="string"?Buffer.from(chunk):chunk;ndjsonHandler.processChunk(buffer)});process.stdin.on("end",()=>{logInfo4("stdin closed")});process.stdin.on("error",error=>{logError4(`stdin error: ${error.message}`)})}function setupAgentResponseHandling(runtimeManager,router){runtimeManager.onAgentExit((agentId,code)=>{logExit(agentId,code)});const originalGetOrSpawn=runtimeManager.getOrSpawn.bind(runtimeManager);runtimeManager.getOrSpawn=async function(agentId,spawnCommand){const runtime=await originalGetOrSpawn(agentId,spawnCommand);const proc=runtime.process;if(proc.stdout&&!proc.stdout.listenerCount("data")){let buffer="";proc.stdout.on("data",chunk=>{buffer+=chunk;let newlineIndex;while((newlineIndex=buffer.indexOf("\n"))!==-1){const line=buffer.slice(0,newlineIndex);buffer=buffer.slice(newlineIndex+1);if(line.trim()){try{const response=JSON.parse(line);router.handleAgentResponse(agentId,response)}catch(err){logError4(`Failed to parse agent ${agentId} response: ${err.message}`)}}}})}if(proc.stderr&&!proc.stderr.listenerCount("data")){proc.stderr.on("data",chunk=>{process.stderr.write(`[agent:${agentId}] ${chunk}`)})}return runtime}}async function main(){logInfo4("Registry Launcher starting");const configPath=parseArgs();if(configPath){logInfo4(`Loading configuration from: ${configPath}`)}const config=loadConfig(configPath);logInfo4(`Configuration loaded: registryUrl=${config.registryUrl}, apiKeysPath=${config.apiKeysPath}, shutdownTimeoutSec=${config.shutdownTimeoutSec}`);const apiKeys=loadApiKeys(config.apiKeysPath);const registry=new RegistryIndex(config.registryUrl);try{await registry.fetch()}catch(error){if(error instanceof RegistryFetchError){logError4(`Failed to fetch registry: ${error.message}`);process.exit(ExitCodes.FATAL_ERROR)}if(error instanceof RegistryParseError){logError4(`Failed to parse registry: ${error.message}`);process.exit(ExitCodes.FATAL_ERROR)}logError4(`Unexpected error fetching registry: ${error.message}`);process.exit(ExitCodes.FATAL_ERROR)}const runtimeManager=new AgentRuntimeManager;const ndjsonHandler=new NDJSONHandler(process.stdout);const router=new MessageRouter(registry,runtimeManager,message=>ndjsonHandler.write(message),apiKeys);const shutdownTimeoutMs=config.shutdownTimeoutSec*1e3;setupSignalHandlers(runtimeManager,shutdownTimeoutMs);setupAgentResponseHandling(runtimeManager,router);setupStdinHandler(router,ndjsonHandler);logInfo4("Registry Launcher ready, waiting for messages")}var ExitCodes,isShuttingDown;var init_registry_launcher=__esm({"workers-registry/acp-worker/src/registry-launcher/index.ts"(){"use strict";init_config();init_api_keys();init_registry();init_ndjson_handler();init_manager();init_message_router();init_log();ExitCodes={SUCCESS:0,FATAL_ERROR:1};isShuttingDown=false;main().catch(error=>{logError4(`Fatal error: ${error.message}`);process.exit(ExitCodes.FATAL_ERROR)})}});import{fileURLToPath}from"url";import{dirname,join}from"path";var __filename=fileURLToPath(import.meta.url);var __dirname=dirname(__filename);var DEFAULT_CONFIG_FILE="acp-registry-config.json";function ensureDefaultConfigArg(){const hasExplicitConfig=process.argv.length>2&&process.argv[2]&&!process.argv[2].startsWith("-");if(hasExplicitConfig){return}const defaultConfigPath=join(__dirname,DEFAULT_CONFIG_FILE);process.argv.splice(2,0,defaultConfigPath)}ensureDefaultConfigArg();await Promise.resolve().then(()=>(init_registry_launcher(),registry_launcher_exports));
|
|
5
|
+
//# sourceMappingURL=index.js.map
|