@jasonews/agent-squad-gateway 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +200 -0
- package/SECURITY.md +44 -0
- package/dist/bin/agent-squad-gateway.js +55 -0
- package/dist/bin/agent-squad-gateway.js.map +1 -0
- package/dist/config/config.js +124 -0
- package/dist/config/config.js.map +1 -0
- package/dist/config/paths.js +16 -0
- package/dist/config/paths.js.map +1 -0
- package/dist/control-plane/clients.js +77 -0
- package/dist/control-plane/clients.js.map +1 -0
- package/dist/control-plane/core-connection.js +54 -0
- package/dist/control-plane/core-connection.js.map +1 -0
- package/dist/control-plane/credentials.js +134 -0
- package/dist/control-plane/credentials.js.map +1 -0
- package/dist/control-plane/db.js +33 -0
- package/dist/control-plane/db.js.map +1 -0
- package/dist/control-plane/extensions.js +59 -0
- package/dist/control-plane/extensions.js.map +1 -0
- package/dist/control-plane/grants.js +107 -0
- package/dist/control-plane/grants.js.map +1 -0
- package/dist/control-plane/idempotency.js +132 -0
- package/dist/control-plane/idempotency.js.map +1 -0
- package/dist/control-plane/migrations.js +133 -0
- package/dist/control-plane/migrations.js.map +1 -0
- package/dist/control-plane/response-sessions.js +359 -0
- package/dist/control-plane/response-sessions.js.map +1 -0
- package/dist/control-plane/retention.js +138 -0
- package/dist/control-plane/retention.js.map +1 -0
- package/dist/control-plane/runs.js +156 -0
- package/dist/control-plane/runs.js.map +1 -0
- package/dist/control-plane/targets.js +449 -0
- package/dist/control-plane/targets.js.map +1 -0
- package/dist/control-plane/types.js +2 -0
- package/dist/control-plane/types.js.map +1 -0
- package/dist/core-client/client.js +275 -0
- package/dist/core-client/client.js.map +1 -0
- package/dist/core-client/event-proxy.js +105 -0
- package/dist/core-client/event-proxy.js.map +1 -0
- package/dist/core-client/types.js +2 -0
- package/dist/core-client/types.js.map +1 -0
- package/dist/extensions/contract.js +2 -0
- package/dist/extensions/contract.js.map +1 -0
- package/dist/extensions/openai/auth.js +16 -0
- package/dist/extensions/openai/auth.js.map +1 -0
- package/dist/extensions/openai/chat.js +403 -0
- package/dist/extensions/openai/chat.js.map +1 -0
- package/dist/extensions/openai/errors.js +79 -0
- package/dist/extensions/openai/errors.js.map +1 -0
- package/dist/extensions/openai/image-input.js +79 -0
- package/dist/extensions/openai/image-input.js.map +1 -0
- package/dist/extensions/openai/models.js +40 -0
- package/dist/extensions/openai/models.js.map +1 -0
- package/dist/extensions/openai/responses.js +731 -0
- package/dist/extensions/openai/responses.js.map +1 -0
- package/dist/extensions/openai/routes.js +81 -0
- package/dist/extensions/openai/routes.js.map +1 -0
- package/dist/extensions/openai/run-attempt.js +96 -0
- package/dist/extensions/openai/run-attempt.js.map +1 -0
- package/dist/extensions/openai/schemas.js +176 -0
- package/dist/extensions/openai/schemas.js.map +1 -0
- package/dist/extensions/openai/sse.js +421 -0
- package/dist/extensions/openai/sse.js.map +1 -0
- package/dist/extensions/openai/tools.js +727 -0
- package/dist/extensions/openai/tools.js.map +1 -0
- package/dist/extensions/registry.js +30 -0
- package/dist/extensions/registry.js.map +1 -0
- package/dist/lifecycle/daemon.js +437 -0
- package/dist/lifecycle/daemon.js.map +1 -0
- package/dist/lifecycle/pidfile.js +34 -0
- package/dist/lifecycle/pidfile.js.map +1 -0
- package/dist/provider-runtime/antigravity/adapter.js +398 -0
- package/dist/provider-runtime/antigravity/adapter.js.map +1 -0
- package/dist/provider-runtime/antigravity/conversation.js +31 -0
- package/dist/provider-runtime/antigravity/conversation.js.map +1 -0
- package/dist/provider-runtime/capability-service.js +191 -0
- package/dist/provider-runtime/capability-service.js.map +1 -0
- package/dist/provider-runtime/claude/adapter.js +397 -0
- package/dist/provider-runtime/claude/adapter.js.map +1 -0
- package/dist/provider-runtime/claude/protocol.js +143 -0
- package/dist/provider-runtime/claude/protocol.js.map +1 -0
- package/dist/provider-runtime/codex/adapter.js +440 -0
- package/dist/provider-runtime/codex/adapter.js.map +1 -0
- package/dist/provider-runtime/codex/json-rpc-client.js +182 -0
- package/dist/provider-runtime/codex/json-rpc-client.js.map +1 -0
- package/dist/provider-runtime/codex/protocol.js +128 -0
- package/dist/provider-runtime/codex/protocol.js.map +1 -0
- package/dist/provider-runtime/command-discovery.js +106 -0
- package/dist/provider-runtime/command-discovery.js.map +1 -0
- package/dist/provider-runtime/cursor/adapter.js +427 -0
- package/dist/provider-runtime/cursor/adapter.js.map +1 -0
- package/dist/provider-runtime/cursor/protocol.js +81 -0
- package/dist/provider-runtime/cursor/protocol.js.map +1 -0
- package/dist/provider-runtime/fake/adapter.js +138 -0
- package/dist/provider-runtime/fake/adapter.js.map +1 -0
- package/dist/provider-runtime/image-assets.js +409 -0
- package/dist/provider-runtime/image-assets.js.map +1 -0
- package/dist/provider-runtime/image-support.js +5 -0
- package/dist/provider-runtime/image-support.js.map +1 -0
- package/dist/provider-runtime/invocation-service.js +503 -0
- package/dist/provider-runtime/invocation-service.js.map +1 -0
- package/dist/provider-runtime/kimi/adapter.js +657 -0
- package/dist/provider-runtime/kimi/adapter.js.map +1 -0
- package/dist/provider-runtime/opencode/adapter.js +610 -0
- package/dist/provider-runtime/opencode/adapter.js.map +1 -0
- package/dist/provider-runtime/opencode/server.js +46 -0
- package/dist/provider-runtime/opencode/server.js.map +1 -0
- package/dist/provider-runtime/process/line-reader.js +49 -0
- package/dist/provider-runtime/process/line-reader.js.map +1 -0
- package/dist/provider-runtime/process/managed-process.js +151 -0
- package/dist/provider-runtime/process/managed-process.js.map +1 -0
- package/dist/provider-runtime/provider-input.js +77 -0
- package/dist/provider-runtime/provider-input.js.map +1 -0
- package/dist/provider-runtime/register-providers.js +26 -0
- package/dist/provider-runtime/register-providers.js.map +1 -0
- package/dist/provider-runtime/registry.js +35 -0
- package/dist/provider-runtime/registry.js.map +1 -0
- package/dist/provider-runtime/replay-buffer.js +168 -0
- package/dist/provider-runtime/replay-buffer.js.map +1 -0
- package/dist/provider-runtime/scheduler.js +155 -0
- package/dist/provider-runtime/scheduler.js.map +1 -0
- package/dist/provider-runtime/types.js +2 -0
- package/dist/provider-runtime/types.js.map +1 -0
- package/dist/provider-runtime/workspaces.js +814 -0
- package/dist/provider-runtime/workspaces.js.map +1 -0
- package/dist/security/admin-auth.js +109 -0
- package/dist/security/admin-auth.js.map +1 -0
- package/dist/security/crypto.js +52 -0
- package/dist/security/crypto.js.map +1 -0
- package/dist/security/secret-files.js +33 -0
- package/dist/security/secret-files.js.map +1 -0
- package/dist/server/admin-routes.js +610 -0
- package/dist/server/admin-routes.js.map +1 -0
- package/dist/server/app.js +178 -0
- package/dist/server/app.js.map +1 -0
- package/dist/server/auth-hooks.js +43 -0
- package/dist/server/auth-hooks.js.map +1 -0
- package/dist/server/core-routes.js +127 -0
- package/dist/server/core-routes.js.map +1 -0
- package/dist/server/errors.js +19 -0
- package/dist/server/errors.js.map +1 -0
- package/dist/server/ui-routes.js +77 -0
- package/dist/server/ui-routes.js.map +1 -0
- package/dist/web/assets/index-BhqNoPpE.js +206 -0
- package/dist/web/assets/index-BhqNoPpE.js.map +1 -0
- package/dist/web/assets/index-C5nnaBfZ.css +1 -0
- package/dist/web/index.html +14 -0
- package/package.json +74 -0
- package/translations/README.zh-CN.md +175 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 JASONews
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# Agent Squad Gateway
|
|
2
|
+
|
|
3
|
+
**A local control plane and OpenAI-compatible API gateway for coding CLI models.**
|
|
4
|
+
|
|
5
|
+
**English (default)** | [简体中文](https://github.com/JASONews/agent-squad-gateway/blob/main/translations/README.zh-CN.md)
|
|
6
|
+
|
|
7
|
+
Agent Squad Gateway exposes models from locally installed coding CLIs through a managed Web UI and
|
|
8
|
+
OpenAI-compatible endpoints. Each provider keeps its existing local login; the Gateway adds model
|
|
9
|
+
aliases, API keys, access grants, concurrency controls, streaming, and capability verification.
|
|
10
|
+
|
|
11
|
+
Agent Squad Core is optional and independent. The Gateway can display Core sessions and pending
|
|
12
|
+
choices for debugging, but Core does not require the Gateway and the Gateway provider runtime can
|
|
13
|
+
operate without Core.
|
|
14
|
+
|
|
15
|
+
```text
|
|
16
|
+
OpenAI client / LiteLLM / IDE
|
|
17
|
+
|
|
|
18
|
+
/v1 API + SSE
|
|
19
|
+
|
|
|
20
|
+
Agent Squad Gateway + Web UI
|
|
21
|
+
|
|
|
22
|
+
Codex / Claude Code / Cursor Agent / OpenCode / Antigravity / Kimi Code
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Features
|
|
26
|
+
|
|
27
|
+
- OpenAI-compatible `GET /v1/models`, `POST /v1/chat/completions`, and `POST /v1/responses`.
|
|
28
|
+
- Streaming responses over SSE, including resumable Responses sessions through
|
|
29
|
+
`previous_response_id`.
|
|
30
|
+
- Client-side function tools: the Gateway returns tool calls, while the API client executes them.
|
|
31
|
+
- Provider-native tools and extensions are disabled where supported and rejected at the adapter
|
|
32
|
+
boundary where a CLI cannot disable them completely.
|
|
33
|
+
- Web UI for CLI discovery, invocation targets, verification, clients, recoverable local API keys,
|
|
34
|
+
grants, run metadata, extensions, settings, and optional Core debugging.
|
|
35
|
+
- Per-target model, reasoning effort, isolation, streaming, queue, concurrency, timeout, and
|
|
36
|
+
workspace controls.
|
|
37
|
+
- Version-aware capability verification: a CLI upgrade keeps the provider available while affected
|
|
38
|
+
targets require conformance verification again.
|
|
39
|
+
- Metadata-only Gateway persistence: prompts, completions, tool payloads, and raw provider events
|
|
40
|
+
are not stored in the Gateway database.
|
|
41
|
+
|
|
42
|
+
## Requirements
|
|
43
|
+
|
|
44
|
+
- Node.js 20 or newer
|
|
45
|
+
- npm
|
|
46
|
+
- At least one installed and authenticated provider CLI:
|
|
47
|
+
- Codex (`codex`)
|
|
48
|
+
- Claude Code (`claude`)
|
|
49
|
+
- Cursor Agent (`cursor-agent`)
|
|
50
|
+
- OpenCode (`opencode`)
|
|
51
|
+
- Antigravity (`agy`)
|
|
52
|
+
- Kimi Code (`kimi`)
|
|
53
|
+
|
|
54
|
+
## Install
|
|
55
|
+
|
|
56
|
+
### npm
|
|
57
|
+
|
|
58
|
+
Agent Squad Gateway is a public package on the npm registry. No registry token is required:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npm install -g @jasonews/agent-squad-gateway
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Verify the command:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
agent-squad-gateway --help
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Build From Source
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
git clone https://github.com/JASONews/agent-squad-gateway.git
|
|
74
|
+
cd agent-squad-gateway
|
|
75
|
+
npm ci
|
|
76
|
+
npm run build
|
|
77
|
+
npm link
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Quick Start
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
agent-squad-gateway start
|
|
84
|
+
agent-squad-gateway open
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The default server address is `0.0.0.0:28772`, allowing local containers to connect. On first
|
|
88
|
+
start, the Gateway creates `~/.agent-squad/gateway/config.json`:
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"address": "0.0.0.0",
|
|
93
|
+
"port": 28772,
|
|
94
|
+
"web_ui_auth": "disabled"
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
For host-only access, change `address` to `127.0.0.1`. Set `web_ui_auth` to `token` when the admin
|
|
99
|
+
UI is reachable from an untrusted network. Restart the Gateway after editing the file.
|
|
100
|
+
|
|
101
|
+
In the Web UI:
|
|
102
|
+
|
|
103
|
+
1. Scan installed CLIs.
|
|
104
|
+
2. Create and verify an invocation target. Verification performs real model calls and may consume
|
|
105
|
+
quota.
|
|
106
|
+
3. Enable the verified target.
|
|
107
|
+
4. Create a client and API key.
|
|
108
|
+
5. Enable the OpenAI extension and grant the client access to the target.
|
|
109
|
+
|
|
110
|
+
## OpenAI-Compatible API
|
|
111
|
+
|
|
112
|
+
Use `http://127.0.0.1:28772/v1` as the base URL. Docker Desktop containers can use
|
|
113
|
+
`http://host.docker.internal:28772/v1`.
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
export AGENT_SQUAD_BASE_URL=http://127.0.0.1:28772/v1
|
|
117
|
+
export AGENT_SQUAD_API_KEY=asqsk_your_key
|
|
118
|
+
|
|
119
|
+
curl "$AGENT_SQUAD_BASE_URL/models" \
|
|
120
|
+
-H "Authorization: Bearer $AGENT_SQUAD_API_KEY"
|
|
121
|
+
|
|
122
|
+
curl "$AGENT_SQUAD_BASE_URL/chat/completions" \
|
|
123
|
+
-H "Authorization: Bearer $AGENT_SQUAD_API_KEY" \
|
|
124
|
+
-H "Content-Type: application/json" \
|
|
125
|
+
-d '{
|
|
126
|
+
"model": "codex-gpt56-max",
|
|
127
|
+
"messages": [{"role":"user","content":"Explain this repository."}],
|
|
128
|
+
"stream": true
|
|
129
|
+
}'
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
The model name is the enabled invocation target ID or one of its configured aliases. Unconfigured
|
|
133
|
+
models are rejected.
|
|
134
|
+
|
|
135
|
+
## Tool Calls
|
|
136
|
+
|
|
137
|
+
API tool calls belong to the client. Send OpenAI function definitions in the request, execute the
|
|
138
|
+
returned calls in the client application, and submit tool results on the next turn. The Gateway
|
|
139
|
+
does not expose the provider CLI's personal tools, MCP servers, skills, or workspace as API tools.
|
|
140
|
+
|
|
141
|
+
## CLI
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
agent-squad-gateway start [--foreground] [--address ADDRESS] [--port PORT]
|
|
145
|
+
agent-squad-gateway stop
|
|
146
|
+
agent-squad-gateway status
|
|
147
|
+
agent-squad-gateway open
|
|
148
|
+
agent-squad-gateway doctor
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Configuration priority, from lowest to highest, is built-in defaults, `config.json`, environment
|
|
152
|
+
variables, and CLI options. Supported overrides include:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
AGENT_SQUAD_GATEWAY_ADDRESS=127.0.0.1
|
|
156
|
+
AGENT_SQUAD_GATEWAY_PORT=28772
|
|
157
|
+
AGENT_SQUAD_GATEWAY_WEB_UI_AUTH=token
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Managed State
|
|
161
|
+
|
|
162
|
+
Gateway state is stored under `~/.agent-squad/gateway/`:
|
|
163
|
+
|
|
164
|
+
| Path | Purpose |
|
|
165
|
+
| --- | --- |
|
|
166
|
+
| `config.json` | Address, port, and Web UI authentication mode |
|
|
167
|
+
| `gateway.db` | Targets, clients, encrypted credentials, grants, and run metadata |
|
|
168
|
+
| `master.key` | Encryption key for recoverable client API keys |
|
|
169
|
+
| `admin-secret` | Local admin bootstrap secret |
|
|
170
|
+
| `gateway.pid` / `gateway.log` | Background lifecycle state |
|
|
171
|
+
| `workspaces/` | Gateway-managed provider workspaces |
|
|
172
|
+
|
|
173
|
+
Client API keys are encrypted at rest but intentionally recoverable in the local admin UI. Protect
|
|
174
|
+
`gateway.db` and `master.key` together.
|
|
175
|
+
|
|
176
|
+
## Security
|
|
177
|
+
|
|
178
|
+
The Gateway is local developer infrastructure, not an internet-facing service. Do not expose it
|
|
179
|
+
through a public router or reverse proxy. Review [SECURITY.md](SECURITY.md) before enabling LAN or
|
|
180
|
+
container access, using fixed workspaces, or sending sensitive content to provider CLIs.
|
|
181
|
+
|
|
182
|
+
## Development
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
npm ci
|
|
186
|
+
npm run build
|
|
187
|
+
npm test
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
The default test suite uses fake providers. Real-provider tests are opt-in and may consume quota:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
GATEWAY_REAL_TARGETS=target_a,target_b npm run test:real-providers
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
For UI development, run `npm run dev:server` and `npm run dev` in separate terminals.
|
|
197
|
+
|
|
198
|
+
## License
|
|
199
|
+
|
|
200
|
+
[MIT](LICENSE)
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
Agent Squad Gateway runs local coding CLIs and exposes a network API. Treat it as local developer
|
|
4
|
+
infrastructure, not as an internet-facing service.
|
|
5
|
+
|
|
6
|
+
## Network Boundary
|
|
7
|
+
|
|
8
|
+
- The default `0.0.0.0` bind address supports local container access but also exposes the port to
|
|
9
|
+
reachable host networks. Use `127.0.0.1` when LAN or container access is unnecessary.
|
|
10
|
+
- Do not expose the Gateway through a public router, tunnel, or reverse proxy.
|
|
11
|
+
- Web UI authentication defaults to `disabled`. Use `web_ui_auth: "token"` on any untrusted
|
|
12
|
+
network. The `/v1` API always requires a Gateway client API key.
|
|
13
|
+
- CORS is disabled and is not a substitute for a firewall or authentication.
|
|
14
|
+
|
|
15
|
+
## Credentials and State
|
|
16
|
+
|
|
17
|
+
- Client API keys are encrypted at rest but intentionally recoverable in the local Web UI.
|
|
18
|
+
- Protect `gateway.db` and `master.key` together. Disclosure of both exposes stored client keys;
|
|
19
|
+
losing `master.key` makes those keys unrecoverable.
|
|
20
|
+
- Never commit GitHub tokens, npm tokens, provider credentials, generated Gateway state, or local
|
|
21
|
+
`.npmrc` files.
|
|
22
|
+
|
|
23
|
+
## Provider Boundaries
|
|
24
|
+
|
|
25
|
+
- Gateway SQLite persistence excludes prompts, completions, tool payloads, and raw provider events,
|
|
26
|
+
but provider CLIs may retain their own transcripts.
|
|
27
|
+
- Provider isolation, sandboxing, cancellation, and transcript suppression are best-effort CLI
|
|
28
|
+
integrations, not operating-system security boundaries.
|
|
29
|
+
- API clients own function execution. Provider-native tool activity is disabled where supported and
|
|
30
|
+
rejected by adapters where a CLI cannot disable tools completely.
|
|
31
|
+
- Antigravity currently receives prompt content as a command-line argument, which can be visible to
|
|
32
|
+
local process inspection or audit tooling.
|
|
33
|
+
- Fixed workspaces reduce isolation. Review the selected path and acknowledgement before enabling a
|
|
34
|
+
target.
|
|
35
|
+
|
|
36
|
+
## Verification
|
|
37
|
+
|
|
38
|
+
Target verification sends bounded requests to the configured provider model and may consume quota.
|
|
39
|
+
Run it intentionally and review provider account limits before enabling automatic consumers.
|
|
40
|
+
|
|
41
|
+
## Reporting Vulnerabilities
|
|
42
|
+
|
|
43
|
+
Report security issues privately to the repository owner. Do not include live credentials, private
|
|
44
|
+
repository content, prompts, or provider transcripts in public issues.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command, Option } from 'commander';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { doctorGateway, openGateway, startGateway, statusGateway, stopGateway, } from '../lifecycle/daemon.js';
|
|
5
|
+
const defaultActions = {
|
|
6
|
+
start: startGateway,
|
|
7
|
+
stop: stopGateway,
|
|
8
|
+
status: statusGateway,
|
|
9
|
+
open: openGateway,
|
|
10
|
+
doctor: doctorGateway,
|
|
11
|
+
};
|
|
12
|
+
export function createGatewayProgram(actions = defaultActions) {
|
|
13
|
+
const program = new Command();
|
|
14
|
+
program
|
|
15
|
+
.name('agent-squad-gateway')
|
|
16
|
+
.description('Local Agent Squad Gateway')
|
|
17
|
+
.version('0.1.0');
|
|
18
|
+
addEndpointOptions(program.command('start')
|
|
19
|
+
.option('--foreground', 'run in the foreground'))
|
|
20
|
+
.addOption(new Option('--web-ui-auth <mode>', 'Web UI authentication mode')
|
|
21
|
+
.choices(['disabled', 'token'])
|
|
22
|
+
.env('AGENT_SQUAD_GATEWAY_WEB_UI_AUTH'))
|
|
23
|
+
.action(async (options) => {
|
|
24
|
+
await actions.start({
|
|
25
|
+
foreground: options.foreground,
|
|
26
|
+
address: options.address,
|
|
27
|
+
port: options.port,
|
|
28
|
+
webUiAuth: options.webUiAuth,
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
program.command('stop').action(async () => { await actions.stop(); });
|
|
32
|
+
addEndpointOptions(program.command('status')).action(async (options) => {
|
|
33
|
+
await actions.status(options);
|
|
34
|
+
});
|
|
35
|
+
addEndpointOptions(program.command('open')).action(async (options) => {
|
|
36
|
+
await actions.open(options);
|
|
37
|
+
});
|
|
38
|
+
program.command('doctor').action(async () => { await actions.doctor(); });
|
|
39
|
+
return program;
|
|
40
|
+
}
|
|
41
|
+
function addEndpointOptions(command) {
|
|
42
|
+
return command
|
|
43
|
+
.addOption(new Option('--address <address>', 'Gateway bind address')
|
|
44
|
+
.env('AGENT_SQUAD_GATEWAY_ADDRESS'))
|
|
45
|
+
.addOption(new Option('--port <port>', 'Gateway bind port')
|
|
46
|
+
.env('AGENT_SQUAD_GATEWAY_PORT')
|
|
47
|
+
.argParser(Number));
|
|
48
|
+
}
|
|
49
|
+
if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
50
|
+
void createGatewayProgram().parseAsync(process.argv).catch((error) => {
|
|
51
|
+
console.error(error);
|
|
52
|
+
process.exitCode = 1;
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=agent-squad-gateway.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-squad-gateway.js","sourceRoot":"","sources":["../../src/bin/agent-squad-gateway.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { isIP } from 'node:net';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { resolveGatewayPaths } from './paths.js';
|
|
5
|
+
export const GATEWAY_BIND_HOST = '0.0.0.0';
|
|
6
|
+
export const GATEWAY_LOOPBACK_HOST = '127.0.0.1';
|
|
7
|
+
const HOSTNAME_PATTERN = /^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)*[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$/;
|
|
8
|
+
const gatewayAddressSchema = z.string()
|
|
9
|
+
.trim()
|
|
10
|
+
.min(1)
|
|
11
|
+
.max(253)
|
|
12
|
+
.refine((value) => isIP(value) !== 0 || HOSTNAME_PATTERN.test(value), 'invalid address')
|
|
13
|
+
.transform((value) => value.toLowerCase());
|
|
14
|
+
const gatewayConfigFileSchema = z.object({
|
|
15
|
+
address: gatewayAddressSchema,
|
|
16
|
+
port: z.number().int().min(1).max(65_535),
|
|
17
|
+
web_ui_auth: z.enum(['disabled', 'token']),
|
|
18
|
+
}).strict();
|
|
19
|
+
const runtimeGatewayConfigSchema = gatewayConfigFileSchema.extend({
|
|
20
|
+
port: z.number().int().min(0).max(65_535),
|
|
21
|
+
});
|
|
22
|
+
const DEFAULT_GATEWAY_CONFIG_FILE = {
|
|
23
|
+
address: GATEWAY_BIND_HOST,
|
|
24
|
+
port: 28_772,
|
|
25
|
+
web_ui_auth: 'disabled',
|
|
26
|
+
};
|
|
27
|
+
export function resolveGatewayConfig(input = {}) {
|
|
28
|
+
const fileConfig = parseRuntimeConfig({
|
|
29
|
+
address: input.address ?? DEFAULT_GATEWAY_CONFIG_FILE.address,
|
|
30
|
+
port: input.port ?? DEFAULT_GATEWAY_CONFIG_FILE.port,
|
|
31
|
+
web_ui_auth: input.webUiAuth ?? DEFAULT_GATEWAY_CONFIG_FILE.web_ui_auth,
|
|
32
|
+
}, 'Gateway configuration');
|
|
33
|
+
return {
|
|
34
|
+
host: fileConfig.address,
|
|
35
|
+
port: fileConfig.port,
|
|
36
|
+
coreUrl: (input.coreUrl ?? 'http://127.0.0.1:28771').replace(/\/$/, ''),
|
|
37
|
+
webUiAuth: fileConfig.web_ui_auth,
|
|
38
|
+
paths: resolveGatewayPaths(input.baseDir),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export function loadGatewayConfig(input = {}) {
|
|
42
|
+
const paths = resolveGatewayPaths(input.baseDir);
|
|
43
|
+
const fileConfig = readGatewayConfigFile(paths.configPath) ?? DEFAULT_GATEWAY_CONFIG_FILE;
|
|
44
|
+
const mergedConfig = parseConfigFile({
|
|
45
|
+
address: input.address ?? fileConfig.address,
|
|
46
|
+
port: input.port ?? fileConfig.port,
|
|
47
|
+
web_ui_auth: input.webUiAuth ?? fileConfig.web_ui_auth,
|
|
48
|
+
}, 'Gateway configuration');
|
|
49
|
+
return resolveGatewayConfig({
|
|
50
|
+
...input,
|
|
51
|
+
address: mergedConfig.address,
|
|
52
|
+
port: mergedConfig.port,
|
|
53
|
+
webUiAuth: mergedConfig.web_ui_auth,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
export function ensureGatewayConfigFile(baseDir) {
|
|
57
|
+
const paths = resolveGatewayPaths(baseDir);
|
|
58
|
+
mkdirSync(paths.stateDir, { recursive: true, mode: 0o700 });
|
|
59
|
+
try {
|
|
60
|
+
writeFileSync(paths.configPath, `${JSON.stringify(DEFAULT_GATEWAY_CONFIG_FILE, null, 2)}\n`, { encoding: 'utf8', flag: 'wx', mode: 0o600 });
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
if (!isFileExistsError(error))
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
return paths.configPath;
|
|
67
|
+
}
|
|
68
|
+
export function gatewayLoopbackOrigin(config) {
|
|
69
|
+
const host = config.host === GATEWAY_BIND_HOST
|
|
70
|
+
? GATEWAY_LOOPBACK_HOST
|
|
71
|
+
: config.host === '::'
|
|
72
|
+
? '::1'
|
|
73
|
+
: config.host;
|
|
74
|
+
return `http://${formatGatewayAddress(host, config.port)}`;
|
|
75
|
+
}
|
|
76
|
+
export function formatGatewayAddress(host, port) {
|
|
77
|
+
return `${host.includes(':') ? `[${host}]` : host}:${port}`;
|
|
78
|
+
}
|
|
79
|
+
export function gatewayBindPolicy(host) {
|
|
80
|
+
if (host === '0.0.0.0' || host === '::')
|
|
81
|
+
return 'all-interfaces';
|
|
82
|
+
if (host === '127.0.0.1' || host === '::1' || host === 'localhost')
|
|
83
|
+
return 'loopback';
|
|
84
|
+
return 'configured-address';
|
|
85
|
+
}
|
|
86
|
+
function readGatewayConfigFile(path) {
|
|
87
|
+
let content;
|
|
88
|
+
try {
|
|
89
|
+
content = readFileSync(path, 'utf8');
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
if (isFileNotFoundError(error))
|
|
93
|
+
return null;
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
return parseConfigFile(JSON.parse(content), `Gateway config file ${path}`);
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
throw new Error(`invalid Gateway config file: ${path}`, { cause: error });
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function parseConfigFile(value, label) {
|
|
104
|
+
const result = gatewayConfigFileSchema.safeParse(value);
|
|
105
|
+
if (!result.success)
|
|
106
|
+
throw new Error(`invalid ${label}`, { cause: result.error });
|
|
107
|
+
return result.data;
|
|
108
|
+
}
|
|
109
|
+
function parseRuntimeConfig(value, label) {
|
|
110
|
+
const result = runtimeGatewayConfigSchema.safeParse(value);
|
|
111
|
+
if (!result.success)
|
|
112
|
+
throw new Error(`invalid ${label}`, { cause: result.error });
|
|
113
|
+
return result.data;
|
|
114
|
+
}
|
|
115
|
+
function isFileNotFoundError(error) {
|
|
116
|
+
return isNodeError(error) && error.code === 'ENOENT';
|
|
117
|
+
}
|
|
118
|
+
function isFileExistsError(error) {
|
|
119
|
+
return isNodeError(error) && error.code === 'EEXIST';
|
|
120
|
+
}
|
|
121
|
+
function isNodeError(error) {
|
|
122
|
+
return error instanceof Error && 'code' in error;
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
export function resolveGatewayPaths(baseDir = homedir()) {
|
|
4
|
+
const stateDir = join(baseDir, '.agent-squad', 'gateway');
|
|
5
|
+
return {
|
|
6
|
+
stateDir,
|
|
7
|
+
configPath: join(stateDir, 'config.json'),
|
|
8
|
+
dbPath: join(stateDir, 'gateway.db'),
|
|
9
|
+
pidPath: join(stateDir, 'gateway.pid'),
|
|
10
|
+
logPath: join(stateDir, 'gateway.log'),
|
|
11
|
+
masterKeyPath: join(stateDir, 'master.key'),
|
|
12
|
+
adminSecretPath: join(stateDir, 'admin-secret'),
|
|
13
|
+
workspacesDir: join(stateDir, 'workspaces'),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/config/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAajC,MAAM,UAAU,mBAAmB,CAAC,OAAO,GAAG,OAAO,EAAE;IACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,cAAc,EAAE,SAAS,CAAC,CAAC;IAC1D,OAAO;QACL,QAAQ;QACR,UAAU,EAAE,IAAI,CAAC,QAAQ,EAAE,aAAa,CAAC;QACzC,MAAM,EAAE,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC;QACpC,OAAO,EAAE,IAAI,CAAC,QAAQ,EAAE,aAAa,CAAC;QACtC,OAAO,EAAE,IAAI,CAAC,QAAQ,EAAE,aAAa,CAAC;QACtC,aAAa,EAAE,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC;QAC3C,eAAe,EAAE,IAAI,CAAC,QAAQ,EAAE,cAAc,CAAC;QAC/C,aAAa,EAAE,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC;KAC5C,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
function toClientRecord(row) {
|
|
3
|
+
return {
|
|
4
|
+
id: row.id,
|
|
5
|
+
name: row.name,
|
|
6
|
+
status: row.status,
|
|
7
|
+
createdAt: row.created_at,
|
|
8
|
+
updatedAt: row.updated_at,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export class ClientRepository {
|
|
12
|
+
db;
|
|
13
|
+
constructor(db) {
|
|
14
|
+
this.db = db;
|
|
15
|
+
}
|
|
16
|
+
create(name) {
|
|
17
|
+
const now = new Date().toISOString();
|
|
18
|
+
const client = {
|
|
19
|
+
id: `client_${randomUUID()}`,
|
|
20
|
+
name,
|
|
21
|
+
status: 'active',
|
|
22
|
+
createdAt: now,
|
|
23
|
+
updatedAt: now,
|
|
24
|
+
};
|
|
25
|
+
try {
|
|
26
|
+
this.db.prepare(`
|
|
27
|
+
INSERT INTO clients (id, name, status, created_at, updated_at)
|
|
28
|
+
VALUES (?, ?, ?, ?, ?)
|
|
29
|
+
`).run(client.id, client.name, client.status, client.createdAt, client.updatedAt);
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
if (isUniqueNameError(error))
|
|
33
|
+
throw new Error('client_name_exists');
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
return client;
|
|
37
|
+
}
|
|
38
|
+
list() {
|
|
39
|
+
return this.db.prepare(`
|
|
40
|
+
SELECT id, name, status, created_at, updated_at
|
|
41
|
+
FROM clients
|
|
42
|
+
ORDER BY created_at DESC, rowid DESC
|
|
43
|
+
`).all().map(toClientRecord);
|
|
44
|
+
}
|
|
45
|
+
setStatus(id, status) {
|
|
46
|
+
const result = this.db.prepare(`
|
|
47
|
+
UPDATE clients SET status = ?, updated_at = ? WHERE id = ?
|
|
48
|
+
`).run(status, new Date().toISOString(), id);
|
|
49
|
+
if (result.changes === 0)
|
|
50
|
+
throw new Error('client_not_found');
|
|
51
|
+
}
|
|
52
|
+
delete(id) {
|
|
53
|
+
const remove = this.db.transaction(() => {
|
|
54
|
+
const client = this.db.prepare('SELECT id FROM clients WHERE id = ?').get(id);
|
|
55
|
+
if (!client)
|
|
56
|
+
throw new Error('client_not_found');
|
|
57
|
+
const active = this.db.prepare(`
|
|
58
|
+
SELECT COUNT(*) AS count FROM runs
|
|
59
|
+
WHERE client_id = ? AND status IN ('queued', 'running')
|
|
60
|
+
`).get(id);
|
|
61
|
+
if ((active?.count ?? 0) > 0)
|
|
62
|
+
throw new Error('client_in_use');
|
|
63
|
+
this.db.prepare('UPDATE runs SET client_id = NULL WHERE client_id = ?').run(id);
|
|
64
|
+
const result = this.db.prepare('DELETE FROM clients WHERE id = ?').run(id);
|
|
65
|
+
if (result.changes !== 1)
|
|
66
|
+
throw new Error('client_not_found');
|
|
67
|
+
});
|
|
68
|
+
remove();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function isUniqueNameError(error) {
|
|
72
|
+
return typeof error === 'object'
|
|
73
|
+
&& error !== null
|
|
74
|
+
&& 'code' in error
|
|
75
|
+
&& error.code === 'SQLITE_CONSTRAINT_UNIQUE';
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=clients.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clients.js","sourceRoot":"","sources":["../../src/control-plane/clients.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { parseCoreBaseUrl } from '../core-client/client.js';
|
|
2
|
+
export class CoreConnectionRepository {
|
|
3
|
+
db;
|
|
4
|
+
now;
|
|
5
|
+
constructor(db, now = () => new Date()) {
|
|
6
|
+
this.db = db;
|
|
7
|
+
this.now = now;
|
|
8
|
+
}
|
|
9
|
+
get() {
|
|
10
|
+
const row = this.db.prepare(`
|
|
11
|
+
SELECT base_url, last_status, last_version, last_checked_at
|
|
12
|
+
FROM core_connection WHERE singleton = 1
|
|
13
|
+
`).get();
|
|
14
|
+
if (!row)
|
|
15
|
+
throw new Error('core_connection_missing');
|
|
16
|
+
return {
|
|
17
|
+
baseUrl: row.base_url,
|
|
18
|
+
status: asStatus(row.last_status),
|
|
19
|
+
version: row.last_version,
|
|
20
|
+
lastCheckedAt: row.last_checked_at,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
update(baseUrl) {
|
|
24
|
+
const normalized = parseCoreBaseUrl(baseUrl);
|
|
25
|
+
const current = this.get();
|
|
26
|
+
if (current.baseUrl === normalized)
|
|
27
|
+
return current;
|
|
28
|
+
this.db.prepare(`
|
|
29
|
+
UPDATE core_connection
|
|
30
|
+
SET base_url = ?, last_status = NULL, last_version = NULL, last_checked_at = NULL
|
|
31
|
+
WHERE singleton = 1
|
|
32
|
+
`).run(normalized);
|
|
33
|
+
return this.get();
|
|
34
|
+
}
|
|
35
|
+
markHealth(health) {
|
|
36
|
+
const status = health === undefined
|
|
37
|
+
? 'offline'
|
|
38
|
+
: health.ok && health.db_ok ? 'online' : 'degraded';
|
|
39
|
+
const checkedAt = toIso(this.now());
|
|
40
|
+
this.db.prepare(`
|
|
41
|
+
UPDATE core_connection
|
|
42
|
+
SET last_status = ?, last_version = COALESCE(?, last_version), last_checked_at = ?
|
|
43
|
+
WHERE singleton = 1
|
|
44
|
+
`).run(status, health?.version ?? null, checkedAt);
|
|
45
|
+
return this.get();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function asStatus(value) {
|
|
49
|
+
return value === 'online' || value === 'degraded' || value === 'offline' ? value : 'unknown';
|
|
50
|
+
}
|
|
51
|
+
function toIso(value) {
|
|
52
|
+
return typeof value === 'string' ? value : value.toISOString();
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=core-connection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core-connection.js","sourceRoot":"","sources":["../../src/control-plane/core-connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAoB5D,MAAM,OAAO,wBAAwB;IAEhB;IACA;IAFnB,YACmB,EAAa,EACb,MAA2B,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE;QAD3C,OAAE,GAAF,EAAE,CAAW;QACb,QAAG,GAAH,GAAG,CAAwC;IAC3D,CAAC;IAEJ,GAAG;QACD,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAwB;;;KAGlD,CAAC,CAAC,GAAG,EAAE,CAAC;QACT,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;QACrD,OAAO;YACL,OAAO,EAAE,GAAG,CAAC,QAAQ;YACrB,MAAM,EAAE,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC;YACjC,OAAO,EAAE,GAAG,CAAC,YAAY;YACzB,aAAa,EAAE,GAAG,CAAC,eAAe;SACnC,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,OAAe;QACpB,MAAM,UAAU,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;QAC7C,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC3B,IAAI,OAAO,CAAC,OAAO,KAAK,UAAU;YAAE,OAAO,OAAO,CAAC;QACnD,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC;;;;KAIf,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACnB,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC;IACpB,CAAC;IAED,UAAU,CAAC,MAAmB;QAC5B,MAAM,MAAM,GAAyB,MAAM,KAAK,SAAS;YACvD,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC;QACtD,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QACpC,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC;;;;KAIf,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,IAAI,IAAI,EAAE,SAAS,CAAC,CAAC;QACnD,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC;IACpB,CAAC;CACF;AAED,SAAS,QAAQ,CAAC,KAAoB;IACpC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,UAAU,IAAI,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AAC/F,CAAC;AAED,SAAS,KAAK,CAAC,KAAoB;IACjC,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;AACjE,CAAC"}
|