agent-dir 0.0.0-stage → 0.1.2
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 +21 -0
- package/README.md +282 -2
- package/dist/bin/agent-dir.d.ts +3 -0
- package/dist/bin/agent-dir.d.ts.map +1 -0
- package/dist/bin/agent-dir.js +201 -0
- package/dist/bin/agent-dir.js.map +1 -0
- package/dist/src/auth.d.ts +3 -0
- package/dist/src/auth.d.ts.map +1 -0
- package/dist/src/auth.js +18 -0
- package/dist/src/auth.js.map +1 -0
- package/dist/src/config.d.ts +6 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +80 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/logging.d.ts +16 -0
- package/dist/src/logging.d.ts.map +1 -0
- package/dist/src/logging.js +243 -0
- package/dist/src/logging.js.map +1 -0
- package/dist/src/mcp.d.ts +9 -0
- package/dist/src/mcp.d.ts.map +1 -0
- package/dist/src/mcp.js +866 -0
- package/dist/src/mcp.js.map +1 -0
- package/dist/src/server.d.ts +11 -0
- package/dist/src/server.d.ts.map +1 -0
- package/dist/src/server.js +562 -0
- package/dist/src/server.js.map +1 -0
- package/dist/src/tools/code.d.ts +27 -0
- package/dist/src/tools/code.d.ts.map +1 -0
- package/dist/src/tools/code.js +147 -0
- package/dist/src/tools/code.js.map +1 -0
- package/dist/src/tools/commands.d.ts +8 -0
- package/dist/src/tools/commands.d.ts.map +1 -0
- package/dist/src/tools/commands.js +61 -0
- package/dist/src/tools/commands.js.map +1 -0
- package/dist/src/tools/diagnostics.d.ts +9 -0
- package/dist/src/tools/diagnostics.d.ts.map +1 -0
- package/dist/src/tools/diagnostics.js +83 -0
- package/dist/src/tools/diagnostics.js.map +1 -0
- package/dist/src/tools/files.d.ts +39 -0
- package/dist/src/tools/files.d.ts.map +1 -0
- package/dist/src/tools/files.js +142 -0
- package/dist/src/tools/files.js.map +1 -0
- package/dist/src/tools/git.d.ts +15 -0
- package/dist/src/tools/git.d.ts.map +1 -0
- package/dist/src/tools/git.js +104 -0
- package/dist/src/tools/git.js.map +1 -0
- package/dist/src/tools/metadata.d.ts +20 -0
- package/dist/src/tools/metadata.d.ts.map +1 -0
- package/dist/src/tools/metadata.js +101 -0
- package/dist/src/tools/metadata.js.map +1 -0
- package/dist/src/tools/npm.d.ts +3 -0
- package/dist/src/tools/npm.d.ts.map +1 -0
- package/dist/src/tools/npm.js +76 -0
- package/dist/src/tools/npm.js.map +1 -0
- package/dist/src/tools/skills.d.ts +33 -0
- package/dist/src/tools/skills.d.ts.map +1 -0
- package/dist/src/tools/skills.js +240 -0
- package/dist/src/tools/skills.js.map +1 -0
- package/dist/src/tunnel.d.ts +3 -0
- package/dist/src/tunnel.d.ts.map +1 -0
- package/dist/src/tunnel.js +114 -0
- package/dist/src/tunnel.js.map +1 -0
- package/dist/src/types.d.ts +45 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/package.json +51 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 agent-dir contributors
|
|
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.
|
package/README.md
CHANGED
|
@@ -1,3 +1,283 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<img src="assets/agent-dir-banner.svg" alt="AGENT-DIR — Expose a local project to AI agents through authenticated MCP." width="100%">
|
|
4
|
+
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
> **`agent-dir` turns a trusted local project directory into a controlled, authenticated MCP endpoint for AI agents.**
|
|
8
|
+
|
|
9
|
+
`agent-dir` is a cross-platform TypeScript CLI that exposes a local project to an AI agent through an authenticated MCP server. It provides controlled file access, targeted file patches, and explicitly allowlisted development commands, with optional tunneling through [Wormhole](https://wormhole.bar/).
|
|
10
|
+
|
|
11
|
+
> **Security:** this is a remote-control development tool. Only expose directories you trust, keep the authentication token private, and allow only commands you actually need.
|
|
12
|
+
|
|
13
|
+
## Requirements
|
|
14
|
+
|
|
15
|
+
- Node.js 24 LTS or newer
|
|
16
|
+
- npm
|
|
17
|
+
- Wormhole CLI if remote access is needed
|
|
18
|
+
|
|
19
|
+
Node.js 24 is the supported LTS baseline. The project currently tracks TypeScript 7 and Biome 2.5.
|
|
20
|
+
|
|
21
|
+
## Quick start
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install -g agent-dir
|
|
25
|
+
agent-dir .
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Or run it without a global install:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npx agent-dir .
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
For remote MCP access with Wormhole:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
agent-dir . --tunnel wormhole --subdomain my-project-x7k4m2
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Wormhole domains
|
|
41
|
+
|
|
42
|
+
When using a custom Wormhole subdomain, choose a distinctive name such as `my-project-x7k4m2`. Avoid generic names such as `project`, `test`, `dev`, or `scriptr` to reduce collisions and make the public endpoint less predictable.
|
|
43
|
+
|
|
44
|
+
The Wormhole URL is only the transport endpoint. `agent-dir` authentication is enforced by the local HTTP server; Wormhole does not provide the Bearer-token authentication described below.
|
|
45
|
+
|
|
46
|
+
> **Wormhole subdomain limit:** Wormhole limits how many subdomains a user can have registered/active at once. If you see an error such as `Subdomain limit reached (max 3 per user)`, the tunnel is being rejected by Wormhole, not by `agent-dir`. Release an existing Wormhole subdomain or use `--random` to let Wormhole choose a temporary random URL.
|
|
47
|
+
>
|
|
48
|
+
> A subdomain can remain registered while another `wormhole` process is still running, so stop unused Wormhole tunnels before creating another one. If a configured subdomain is unavailable, `agent-dir` will report the Wormhole registration failure instead of silently starting without a tunnel.
|
|
49
|
+
|
|
50
|
+
## Profiles
|
|
51
|
+
|
|
52
|
+
Profiles save settings you use repeatedly: project directory, tunnel, Wormhole subdomain, port, npm scripts, and allowed non-npm commands.
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
agent-dir config add my-project \
|
|
56
|
+
--directory ~/src/my-project \
|
|
57
|
+
--tunnel wormhole \
|
|
58
|
+
--subdomain my-project-x7k4m2 \
|
|
59
|
+
--npm typecheck,lint,format,test,build \
|
|
60
|
+
--command grep,find,rg,git
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Then:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
agent-dir my-project
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Manage profiles with:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
agent-dir config list
|
|
73
|
+
agent-dir config show my-project
|
|
74
|
+
agent-dir config remove my-project
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Profiles are stored per-user at `~/.config/agent-dir/config.json`, not in the project repository. The config directory and file are written with user-only permissions. Command-line options can override saved values for one run.
|
|
78
|
+
|
|
79
|
+
## MCP transport and compatibility
|
|
80
|
+
|
|
81
|
+
The HTTP MCP endpoint is `POST /mcp`. The implementation supports two protocol compatibility paths:
|
|
82
|
+
|
|
83
|
+
### Modern stateless MCP
|
|
84
|
+
|
|
85
|
+
The native protocol path uses MCP `2026-07-28`. Requests carry the protocol version and client capabilities in `params._meta`. The server does not require an initialize handshake or `Mcp-Session-Id` for this path.
|
|
86
|
+
|
|
87
|
+
Standard Streamable HTTP requests do **not** need the implementation-specific `MCP-Protocol-Version`, `Mcp-Method`, or `Mcp-Name` headers. If a client sends those optional headers, `agent-dir` validates them against the JSON-RPC request and metadata instead of requiring them.
|
|
88
|
+
|
|
89
|
+
The modern implementation includes:
|
|
90
|
+
|
|
91
|
+
- `server/discover`
|
|
92
|
+
- cursor pagination for list-style methods
|
|
93
|
+
- `subscriptions/listen` for tool, prompt, and resource change events
|
|
94
|
+
- resource subscriptions and filesystem change notifications
|
|
95
|
+
- cache metadata
|
|
96
|
+
- structured tool results with `resultType`
|
|
97
|
+
- `outputSchema` for tools
|
|
98
|
+
- the stable `io.modelcontextprotocol/skills` extension
|
|
99
|
+
|
|
100
|
+
### Legacy MCP compatibility
|
|
101
|
+
|
|
102
|
+
Clients using the MCP `2025-11-25` initialize-based lifecycle are also supported. A legacy client can:
|
|
103
|
+
|
|
104
|
+
1. send `initialize` with `params.protocolVersion: "2025-11-25"`, `capabilities`, and `clientInfo`;
|
|
105
|
+
2. negotiate `2025-11-25`;
|
|
106
|
+
3. send subsequent requests with `MCP-Protocol-Version: 2025-11-25` without the modern `params._meta` object.
|
|
107
|
+
|
|
108
|
+
This compatibility path is intentionally narrow. It does not weaken the modern protocol validation, and conflicting protocol headers are rejected.
|
|
109
|
+
|
|
110
|
+
The compatibility layer exists for clients such as MCP integrations that still perform the standard `initialize` handshake instead of using the native stateless lifecycle.
|
|
111
|
+
|
|
112
|
+
### Authentication and protocol troubleshooting
|
|
113
|
+
|
|
114
|
+
Authentication is independent of protocol negotiation. A valid Bearer token is still required before MCP handling:
|
|
115
|
+
|
|
116
|
+
```http
|
|
117
|
+
Authorization: Bearer <token>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
For clients that cannot send an Authorization header, the server also accepts:
|
|
121
|
+
|
|
122
|
+
```text
|
|
123
|
+
https://your-subdomain.wormhole.bar/mcp?token=<token>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
If a client reports that the server needs sign-in, inspect the server's request log before changing credentials. A `400` from MCP can be a protocol compatibility error rather than an authentication failure. Request logs now include the MCP error message and, for JSON-RPC failures, safe diagnostic context such as the method, request id, parameter names, metadata presence, protocol version, and protocol header. Parameter values are not logged.
|
|
127
|
+
|
|
128
|
+
The server log distinguishes protocol/header validation from MCP handler errors. This makes transient client interoperability failures diagnosable without exposing request payloads or secrets.
|
|
129
|
+
|
|
130
|
+
## MCP tools
|
|
131
|
+
|
|
132
|
+
| Tool | Purpose |
|
|
133
|
+
|---|---|
|
|
134
|
+
| `list_files` | Recursive project discovery |
|
|
135
|
+
| `list_dirs` | Direct directory discovery for one or more directories |
|
|
136
|
+
| `read_range` | Read a bounded line range from one UTF-8 file |
|
|
137
|
+
| `read_files` | Read one or more UTF-8 files in one call |
|
|
138
|
+
| `write_files` | Create or replace one or more files in one call |
|
|
139
|
+
| `patch_files` | Apply targeted text replacements to one or more files |
|
|
140
|
+
| `delete_files` | Delete one or more files in one call |
|
|
141
|
+
| `search_files` / `find_files` | Search file contents or find paths by glob |
|
|
142
|
+
| `search_code` | Search source-like files |
|
|
143
|
+
| `find_symbol` / `find_definition` | Locate likely symbol definitions |
|
|
144
|
+
| `find_references` | Locate symbol references |
|
|
145
|
+
| `find_imports` / `find_exports` | Inspect source dependencies and exports |
|
|
146
|
+
| `git_status` / `git_diff` / `git_log` | Git inspection |
|
|
147
|
+
| `git_stage` / `git_unstage` | Stage or unstage paths |
|
|
148
|
+
| `git_commit` | Commit staged changes |
|
|
149
|
+
| `git_restore` | Restore paths, discarding unstaged changes |
|
|
150
|
+
| `git_push` | Push the current branch to a remote |
|
|
151
|
+
| `project_overview` | Project structure, languages, package managers, and Git state |
|
|
152
|
+
| `package_info` / `file_info` | Project and filesystem metadata |
|
|
153
|
+
| `diagnostics` | Project-independent diagnostics; does not run tests, lint, or typecheck |
|
|
154
|
+
| `run_npm_batch` | Run one or more explicitly allowlisted npm scripts sequentially |
|
|
155
|
+
| `run_command_batch` | Run one or more explicitly allowlisted executables sequentially without a shell |
|
|
156
|
+
|
|
157
|
+
Batch operations execute sequentially and stop on the first failed command. Tool calls return modern `structuredContent` alongside a serialized text representation, and tools that return structured data advertise an `outputSchema`. List-style protocol methods use opaque cursors when more than 50 entries are available.
|
|
158
|
+
|
|
159
|
+
Modern `subscriptions/listen` replaces the legacy GET/SSE notification model. Clients can subscribe to tool, prompt, resource-list, and resource-specific change events; filesystem mutations publish resource-change events to active subscribers.
|
|
160
|
+
|
|
161
|
+
### Command permissions
|
|
162
|
+
|
|
163
|
+
npm scripts must be explicitly enabled:
|
|
164
|
+
|
|
165
|
+
```text
|
|
166
|
+
--npm typecheck,lint,format,test,build
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Normal executables use a separate allowlist:
|
|
170
|
+
|
|
171
|
+
```text
|
|
172
|
+
--command grep,find,rg,git
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Commands are launched with `shell: false`; the MCP client supplies the executable and arguments separately. An executable that is not in the allowlist is rejected.
|
|
176
|
+
|
|
177
|
+
Avoid allowing `sh`, `bash`, `zsh`, `cmd`, `node`, or `python` unless you intentionally want to grant a much broader execution capability.
|
|
178
|
+
|
|
179
|
+
## Agent Skills
|
|
180
|
+
|
|
181
|
+
The server implements the stable MCP Skills extension (`io.modelcontextprotocol/skills`) over the standard Resources primitive. It discovers project-local `SKILL.md` files under:
|
|
182
|
+
|
|
183
|
+
```text
|
|
184
|
+
skills/
|
|
185
|
+
.agents/skills/
|
|
186
|
+
.claude/skills/
|
|
187
|
+
.github/skills/
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Skills are exposed through `skills/list`, `skills/get`, `resources/list`, and `resources/read`. Skill entries contain parsed frontmatter plus SHA-256 digests and byte sizes for every served file. Skill manifests enforce the 512-resource and 16 MiB limits. Binary supporting files are returned as MCP blobs. `resources/directory/read` lists direct children of a skill resource directory. The extension advertises `directoryRead: true`.
|
|
191
|
+
|
|
192
|
+
This repository now includes a maintainer-facing compatibility skill at `skills/agent-dir-maintainer/SKILL.md`. It documents the modern and legacy MCP paths, troubleshooting signals, and release/test expectations for agents working on this project.
|
|
193
|
+
|
|
194
|
+
## Authentication
|
|
195
|
+
|
|
196
|
+
By default, `agent-dir` generates a random Bearer token when the server starts. The token protects both the MCP endpoint and the HTTP file API.
|
|
197
|
+
|
|
198
|
+
Clients should authenticate with:
|
|
199
|
+
|
|
200
|
+
```http
|
|
201
|
+
Authorization: Bearer <token>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
The CLI also supports explicitly configured profile tokens and the `--token` option. There is no `--no-auth` option; authentication cannot be disabled through the CLI. Keep the token secret and do not commit it to a repository or publish it alongside a tunnel URL.
|
|
205
|
+
|
|
206
|
+
For clients that cannot send an `Authorization` header, the HTTP server also accepts the token as a query parameter:
|
|
207
|
+
|
|
208
|
+
```text
|
|
209
|
+
https://your-subdomain.wormhole.bar/mcp?token=<token>
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Query-string tokens may be exposed in URL logs and should be treated as less secure than the Authorization header.
|
|
213
|
+
|
|
214
|
+
## REST API
|
|
215
|
+
|
|
216
|
+
The MCP server is the primary interface, but the HTTP server also exposes:
|
|
217
|
+
|
|
218
|
+
```text
|
|
219
|
+
GET /__tree
|
|
220
|
+
GET /path/to/file
|
|
221
|
+
PUT /path/to/file
|
|
222
|
+
DELETE /path/to/file
|
|
223
|
+
POST /mcp
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
All HTTP endpoints are authenticated by default.
|
|
227
|
+
|
|
228
|
+
## Contributing
|
|
229
|
+
|
|
230
|
+
Contributions are welcome. See [CONTRIBUTING.md](.github/CONTRIBUTING.md) for development setup, testing requirements, MCP compatibility guidance, and pull-request expectations.
|
|
231
|
+
|
|
232
|
+
Use the GitHub issue templates for bug reports, feature requests, and usage questions. Security vulnerabilities should be reported privately through the process in [SECURITY.md](SECURITY.md), not through a public issue.
|
|
233
|
+
|
|
234
|
+
See [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) for community participation guidelines.
|
|
235
|
+
|
|
236
|
+
## Development
|
|
237
|
+
|
|
238
|
+
The application source and tests are fully TypeScript. JavaScript is generated into `dist/` for execution and publishing. The published package contains only the built CLI/runtime, README, and license; development sources and tests are excluded from the npm tarball.
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
npm run build
|
|
242
|
+
npm run typecheck
|
|
243
|
+
npm test
|
|
244
|
+
npm run format
|
|
245
|
+
npm run lint
|
|
246
|
+
npm run check
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
`npm run check` runs Biome plus TypeScript. Before publishing:
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
npm run check
|
|
253
|
+
npm test
|
|
254
|
+
npm pack --dry-run
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Review the `npm pack --dry-run` file list before publishing to confirm no local configuration, credentials, source-only files, or development artifacts are included.
|
|
258
|
+
|
|
259
|
+
## Toolchain
|
|
260
|
+
|
|
261
|
+
- **Node.js:** 24 LTS baseline
|
|
262
|
+
- **TypeScript:** 7.x
|
|
263
|
+
- **Biome:** 2.5.x
|
|
264
|
+
- **Module system:** native Node ESM with `NodeNext`
|
|
265
|
+
- **Formatting/linting:** Biome with strict recommended rules and import organization
|
|
266
|
+
|
|
267
|
+
The TypeScript configuration uses strict checking, exact optional properties, unchecked indexed access, isolated modules, explicit Node typings, and consistent module resolution.
|
|
268
|
+
|
|
269
|
+
## Security model
|
|
270
|
+
|
|
271
|
+
File access is rooted at the shared directory and resolves existing paths through their real filesystem targets, preventing symlinks from escaping the exposed root. HTTP request bodies are limited to 10 MiB. npm scripts and external executables use explicit allowlists, and external commands are not passed through a shell.
|
|
272
|
+
|
|
273
|
+
Because file writes, deletion, command execution, and Git write operations can modify a project or remote repository, expose only directories and capabilities you intend an AI agent to control. Git write tools are intentionally explicit: staging, unstaging, committing, restoring, and pushing are separate operations. `git_restore` discards unstaged changes, and `git_push` can modify a remote repository. If you do not want Git mutation, do not use the Git write tools and do not allow `git` through the generic command allowlist.
|
|
274
|
+
|
|
275
|
+
The diagnostics tool is deliberately project-independent. It checks conditions such as invalid JSON, broken symlinks, and unresolved merge-conflict markers instead of assuming a particular test runner, linter, compiler, or package ecosystem. Never share directories containing credentials, SSH keys, private certificates, production secrets, or unrelated personal data.
|
|
276
|
+
|
|
277
|
+
## Credits
|
|
278
|
+
|
|
279
|
+
Remote tunneling support is provided through [Wormhole](https://wormhole.bar/), a project of the Wormhole team. `agent-dir` invokes the Wormhole CLI and does not provide the tunneling service itself.
|
|
280
|
+
|
|
281
|
+
## License
|
|
282
|
+
|
|
283
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-dir.d.ts","sourceRoot":"","sources":["../../bin/agent-dir.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { randomBytes } from "node:crypto";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { parseArgs } from "node:util";
|
|
5
|
+
import { configPath, loadConfig, mergeProfile, saveConfig } from "../src/config.js";
|
|
6
|
+
import { printBanner, printHelp, printRequestHeader } from "../src/logging.js";
|
|
7
|
+
import { startServer } from "../src/server.js";
|
|
8
|
+
import { startTunnel } from "../src/tunnel.js";
|
|
9
|
+
function fail(message) {
|
|
10
|
+
console.error(`Error: ${message}`);
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
function parseList(value) {
|
|
14
|
+
if (value === undefined)
|
|
15
|
+
return undefined;
|
|
16
|
+
return value
|
|
17
|
+
.split(",")
|
|
18
|
+
.map((item) => item.trim())
|
|
19
|
+
.filter(Boolean);
|
|
20
|
+
}
|
|
21
|
+
function parsePort(value, fallback) {
|
|
22
|
+
const port = Number(value ?? fallback);
|
|
23
|
+
if (!Number.isInteger(port) || port < 1 || port > 65_535) {
|
|
24
|
+
fail("port must be an integer between 1 and 65535.");
|
|
25
|
+
}
|
|
26
|
+
return port;
|
|
27
|
+
}
|
|
28
|
+
const { values, positionals } = parseArgs({
|
|
29
|
+
options: {
|
|
30
|
+
help: { type: "boolean", short: "h" },
|
|
31
|
+
directory: { type: "string", short: "d" },
|
|
32
|
+
port: { type: "string", short: "p" },
|
|
33
|
+
tunnel: { type: "string", short: "t" },
|
|
34
|
+
subdomain: { type: "string", short: "s" },
|
|
35
|
+
npm: { type: "string" },
|
|
36
|
+
command: { type: "string" },
|
|
37
|
+
token: { type: "string" },
|
|
38
|
+
"no-tunnel": { type: "boolean" },
|
|
39
|
+
random: { type: "boolean" },
|
|
40
|
+
},
|
|
41
|
+
allowPositionals: true,
|
|
42
|
+
strict: true,
|
|
43
|
+
});
|
|
44
|
+
if (values.help) {
|
|
45
|
+
printHelp();
|
|
46
|
+
process.exit(0);
|
|
47
|
+
}
|
|
48
|
+
const config = await loadConfig();
|
|
49
|
+
const command = positionals[0];
|
|
50
|
+
if (command === "config") {
|
|
51
|
+
const action = positionals[1];
|
|
52
|
+
if (action === "list") {
|
|
53
|
+
const names = Object.keys(config.profiles);
|
|
54
|
+
console.log(names.length ? names.join("\n") : "No profiles configured.");
|
|
55
|
+
process.exit(0);
|
|
56
|
+
}
|
|
57
|
+
if (action === "show") {
|
|
58
|
+
const name = positionals[2];
|
|
59
|
+
if (!name || !config.profiles[name])
|
|
60
|
+
fail(`Profile not found: ${name}`);
|
|
61
|
+
const profile = structuredClone(config.profiles[name]);
|
|
62
|
+
if (profile.token)
|
|
63
|
+
profile.token = "<configured>";
|
|
64
|
+
console.log(JSON.stringify(profile, null, 2));
|
|
65
|
+
process.exit(0);
|
|
66
|
+
}
|
|
67
|
+
if (action === "remove") {
|
|
68
|
+
const name = positionals[2];
|
|
69
|
+
if (!name || !config.profiles[name])
|
|
70
|
+
fail(`Profile not found: ${name}`);
|
|
71
|
+
delete config.profiles[name];
|
|
72
|
+
await saveConfig(config);
|
|
73
|
+
console.log(`✓ Removed profile '${name}'.`);
|
|
74
|
+
process.exit(0);
|
|
75
|
+
}
|
|
76
|
+
if (action === "token") {
|
|
77
|
+
const name = positionals[2];
|
|
78
|
+
if (!name || !config.profiles[name])
|
|
79
|
+
fail(`Profile not found: ${name}`);
|
|
80
|
+
config.profiles[name].token = randomBytes(24).toString("hex");
|
|
81
|
+
await saveConfig(config);
|
|
82
|
+
console.log(`✓ Rotated token for '${name}'.`);
|
|
83
|
+
console.log(` Config: ${configPath()}`);
|
|
84
|
+
process.exit(0);
|
|
85
|
+
}
|
|
86
|
+
if (action === "add") {
|
|
87
|
+
const name = positionals[2];
|
|
88
|
+
if (!name)
|
|
89
|
+
fail("Usage: agent-dir config add <name> [options]");
|
|
90
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(name))
|
|
91
|
+
fail("Profile names may contain letters, numbers, dots, underscores, and hyphens.");
|
|
92
|
+
const tunnel = values.tunnel ?? "none";
|
|
93
|
+
if (tunnel !== "none" && tunnel !== "wormhole")
|
|
94
|
+
fail(`Unsupported tunnel provider: ${tunnel}`);
|
|
95
|
+
const profile = {
|
|
96
|
+
directory: resolve(values.directory ?? "."),
|
|
97
|
+
port: parsePort(values.port, 3002),
|
|
98
|
+
tunnel,
|
|
99
|
+
token: values.token ?? randomBytes(24).toString("hex"),
|
|
100
|
+
};
|
|
101
|
+
if (values.subdomain)
|
|
102
|
+
profile.subdomain = values.subdomain;
|
|
103
|
+
const npm = parseList(values.npm);
|
|
104
|
+
if (npm !== undefined)
|
|
105
|
+
profile.npm = { allowedScripts: npm };
|
|
106
|
+
const commands = parseList(values.command);
|
|
107
|
+
if (commands !== undefined)
|
|
108
|
+
profile.commands = commands;
|
|
109
|
+
if (profile.tunnel === "wormhole" && !profile.subdomain && !values.random) {
|
|
110
|
+
fail("Wormhole profiles require --subdomain <name>, or use --random.");
|
|
111
|
+
}
|
|
112
|
+
config.profiles[name] = profile;
|
|
113
|
+
await saveConfig(config);
|
|
114
|
+
console.log(`✓ Saved profile '${name}'.`);
|
|
115
|
+
console.log(` Config: ${configPath()}`);
|
|
116
|
+
process.exit(0);
|
|
117
|
+
}
|
|
118
|
+
fail("Usage: agent-dir config {add|list|show|remove|token}");
|
|
119
|
+
}
|
|
120
|
+
const profile = command ? config.profiles[command] : undefined;
|
|
121
|
+
if (command && command !== "config" && !profile && command.startsWith("-")) {
|
|
122
|
+
fail(`Unknown command: ${command}`);
|
|
123
|
+
}
|
|
124
|
+
const positionalDirectory = profile ? positionals[1] : positionals[0];
|
|
125
|
+
const directory = resolve(values.directory ?? profile?.directory ?? positionalDirectory ?? ".");
|
|
126
|
+
const port = parsePort(values.port, profile?.port ?? 3002);
|
|
127
|
+
const tunnelValue = values["no-tunnel"] ? "none" : (values.tunnel ?? profile?.tunnel ?? "none");
|
|
128
|
+
if (tunnelValue !== "none" && tunnelValue !== "wormhole") {
|
|
129
|
+
fail(`Unsupported tunnel provider: ${tunnelValue}`);
|
|
130
|
+
}
|
|
131
|
+
const tunnel = tunnelValue;
|
|
132
|
+
const randomTunnel = values.random === true;
|
|
133
|
+
const subdomain = randomTunnel ? undefined : (values.subdomain ?? profile?.subdomain);
|
|
134
|
+
if (tunnel === "wormhole" && !subdomain && !randomTunnel) {
|
|
135
|
+
fail("Wormhole requires --subdomain <name>, or use --random.");
|
|
136
|
+
}
|
|
137
|
+
if (randomTunnel && tunnel === "none") {
|
|
138
|
+
fail("--random requires --tunnel wormhole.");
|
|
139
|
+
}
|
|
140
|
+
const token = values.token ?? profile?.token ?? randomBytes(24).toString("hex");
|
|
141
|
+
const npm = parseList(values.npm);
|
|
142
|
+
const commands = parseList(values.command);
|
|
143
|
+
const overrides = {};
|
|
144
|
+
if (npm !== undefined)
|
|
145
|
+
overrides.npm = { allowedScripts: npm };
|
|
146
|
+
if (commands !== undefined)
|
|
147
|
+
overrides.commands = commands;
|
|
148
|
+
const commandConfig = mergeProfile(profile ?? { directory, port, tunnel }, overrides);
|
|
149
|
+
const bannerOptions = {
|
|
150
|
+
root: directory,
|
|
151
|
+
port,
|
|
152
|
+
tunnel,
|
|
153
|
+
token,
|
|
154
|
+
};
|
|
155
|
+
const npmScripts = commandConfig.npm?.allowedScripts;
|
|
156
|
+
const allowedCommands = commandConfig.commands;
|
|
157
|
+
if (profile && command)
|
|
158
|
+
bannerOptions.profileName = command;
|
|
159
|
+
if (subdomain)
|
|
160
|
+
bannerOptions.subdomain = subdomain;
|
|
161
|
+
if (npmScripts)
|
|
162
|
+
bannerOptions.npmScripts = npmScripts;
|
|
163
|
+
if (allowedCommands)
|
|
164
|
+
bannerOptions.commands = allowedCommands;
|
|
165
|
+
printBanner(bannerOptions);
|
|
166
|
+
const server = await startServer({ root: directory, port, token, commandConfig });
|
|
167
|
+
let tunnelResult;
|
|
168
|
+
console.log(" ✓ SERVER ONLINE");
|
|
169
|
+
console.log(` Local target : http://127.0.0.1:${port}`);
|
|
170
|
+
console.log(" Auth : Bearer token");
|
|
171
|
+
console.log(` Token : ${token}`);
|
|
172
|
+
if (tunnel !== "none") {
|
|
173
|
+
try {
|
|
174
|
+
tunnelResult = await startTunnel({
|
|
175
|
+
provider: "wormhole",
|
|
176
|
+
port,
|
|
177
|
+
random: randomTunnel,
|
|
178
|
+
...(subdomain && !randomTunnel ? { subdomain } : {}),
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
console.error(` ✖ TUNNEL ERROR ${error instanceof Error ? error.message : String(error)}`);
|
|
183
|
+
await server.close();
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
printRequestHeader();
|
|
188
|
+
let shuttingDown = false;
|
|
189
|
+
const shutdown = async () => {
|
|
190
|
+
if (shuttingDown)
|
|
191
|
+
return;
|
|
192
|
+
shuttingDown = true;
|
|
193
|
+
console.log("\n Stopping agent-dir...");
|
|
194
|
+
if (tunnelResult?.child && !tunnelResult.child.killed)
|
|
195
|
+
tunnelResult.child.kill("SIGTERM");
|
|
196
|
+
await server.close();
|
|
197
|
+
process.exit(0);
|
|
198
|
+
};
|
|
199
|
+
process.on("SIGINT", shutdown);
|
|
200
|
+
process.on("SIGTERM", shutdown);
|
|
201
|
+
//# sourceMappingURL=agent-dir.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-dir.js","sourceRoot":"","sources":["../../bin/agent-dir.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/auth.ts"],"names":[],"mappings":"AAAA,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAMrE;AAED,wBAAgB,YAAY,IAAI,QAAQ,CASvC"}
|
package/dist/src/auth.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function authenticate(request, token) {
|
|
2
|
+
const authorization = request.headers.get("authorization");
|
|
3
|
+
if (authorization === `Bearer ${token}`)
|
|
4
|
+
return true;
|
|
5
|
+
const url = new URL(request.url);
|
|
6
|
+
return url.searchParams.get("token") === token;
|
|
7
|
+
}
|
|
8
|
+
export function unauthorized() {
|
|
9
|
+
return new Response(JSON.stringify({ error: "Unauthorized" }), {
|
|
10
|
+
status: 401,
|
|
11
|
+
headers: {
|
|
12
|
+
"content-type": "application/json; charset=utf-8",
|
|
13
|
+
"www-authenticate": "Bearer",
|
|
14
|
+
"cache-control": "no-store",
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/auth.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,YAAY,CAAC,OAAgB,EAAE,KAAa;IAC1D,MAAM,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAC3D,IAAI,aAAa,KAAK,UAAU,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAErD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACjC,OAAO,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,KAAK,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,EAAE;QAC7D,MAAM,EAAE,GAAG;QACX,OAAO,EAAE;YACP,cAAc,EAAE,iCAAiC;YACjD,kBAAkB,EAAE,QAAQ;YAC5B,eAAe,EAAE,UAAU;SAC5B;KACF,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { AgentConfig, CommandConfig, Profile } from "./types.js";
|
|
2
|
+
export declare function loadConfig(): Promise<AgentConfig>;
|
|
3
|
+
export declare function saveConfig(config: AgentConfig): Promise<void>;
|
|
4
|
+
export declare function configPath(): string;
|
|
5
|
+
export declare function mergeProfile(profile: Profile, overrides?: CommandConfig): CommandConfig;
|
|
6
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAUtE,wBAAsB,UAAU,IAAI,OAAO,CAAC,WAAW,CAAC,CASvD;AAED,wBAAsB,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAOnE;AAED,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,GAAE,aAAkB,GAAG,aAAa,CAM3F"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { chmod, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
const CONFIG_DIR = join(homedir(), ".config", "agent-dir");
|
|
5
|
+
const CONFIG_FILE = join(CONFIG_DIR, "config.json");
|
|
6
|
+
const DEFAULT_CONFIG = { version: 1, profiles: {} };
|
|
7
|
+
export async function loadConfig() {
|
|
8
|
+
try {
|
|
9
|
+
const parsed = JSON.parse(await readFile(CONFIG_FILE, "utf8"));
|
|
10
|
+
if (!isAgentConfig(parsed))
|
|
11
|
+
throw new Error("Invalid configuration format.");
|
|
12
|
+
return parsed;
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
if (error.code === "ENOENT")
|
|
16
|
+
return structuredClone(DEFAULT_CONFIG);
|
|
17
|
+
throw new Error(`Unable to read config ${CONFIG_FILE}: ${error.message}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export async function saveConfig(config) {
|
|
21
|
+
await mkdir(dirname(CONFIG_FILE), { recursive: true, mode: 0o700 });
|
|
22
|
+
await writeFile(CONFIG_FILE, `${JSON.stringify(config, null, 2)}\n`, {
|
|
23
|
+
encoding: "utf8",
|
|
24
|
+
mode: 0o600,
|
|
25
|
+
});
|
|
26
|
+
await chmod(CONFIG_FILE, 0o600);
|
|
27
|
+
}
|
|
28
|
+
export function configPath() {
|
|
29
|
+
return CONFIG_FILE;
|
|
30
|
+
}
|
|
31
|
+
export function mergeProfile(profile, overrides = {}) {
|
|
32
|
+
return {
|
|
33
|
+
...profile,
|
|
34
|
+
...overrides,
|
|
35
|
+
npm: { ...(profile.npm ?? { allowedScripts: [] }), ...(overrides.npm ?? {}) },
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function isAgentConfig(value) {
|
|
39
|
+
if (!value || typeof value !== "object")
|
|
40
|
+
return false;
|
|
41
|
+
const config = value;
|
|
42
|
+
if (config.version !== 1 || !config.profiles || typeof config.profiles !== "object")
|
|
43
|
+
return false;
|
|
44
|
+
for (const [name, profile] of Object.entries(config.profiles)) {
|
|
45
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(name) || !isProfile(profile))
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
function isProfile(value) {
|
|
51
|
+
if (!value || typeof value !== "object")
|
|
52
|
+
return false;
|
|
53
|
+
const profile = value;
|
|
54
|
+
const port = profile.port;
|
|
55
|
+
if (typeof profile.directory !== "string" || typeof port !== "number" || !Number.isInteger(port))
|
|
56
|
+
return false;
|
|
57
|
+
if (port < 1 || port > 65_535)
|
|
58
|
+
return false;
|
|
59
|
+
if (profile.tunnel !== "none" && profile.tunnel !== "wormhole")
|
|
60
|
+
return false;
|
|
61
|
+
if (profile.subdomain !== undefined && typeof profile.subdomain !== "string")
|
|
62
|
+
return false;
|
|
63
|
+
if (profile.token !== undefined && typeof profile.token !== "string")
|
|
64
|
+
return false;
|
|
65
|
+
if (profile.npm !== undefined) {
|
|
66
|
+
if (!profile.npm || typeof profile.npm !== "object")
|
|
67
|
+
return false;
|
|
68
|
+
const npm = profile.npm;
|
|
69
|
+
if (!Array.isArray(npm.allowedScripts) ||
|
|
70
|
+
npm.allowedScripts.some((item) => typeof item !== "string"))
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
if (profile.commands !== undefined) {
|
|
74
|
+
if (!Array.isArray(profile.commands) ||
|
|
75
|
+
profile.commands.some((item) => typeof item !== "string"))
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAG1C,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC;AAC3D,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AACpD,MAAM,cAAc,GAAgB,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;AAMjE,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,IAAI,CAAC;QACH,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC;QACxE,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;QAC7E,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAK,KAAyB,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,eAAe,CAAC,cAAc,CAAC,CAAC;QACzF,MAAM,IAAI,KAAK,CAAC,yBAAyB,WAAW,KAAM,KAAe,CAAC,OAAO,EAAE,CAAC,CAAC;IACvF,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,MAAmB;IAClD,MAAM,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACpE,MAAM,SAAS,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE;QACnE,QAAQ,EAAE,MAAM;QAChB,IAAI,EAAE,KAAK;KACZ,CAAC,CAAC;IACH,MAAM,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,OAAO,WAAW,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,OAAgB,EAAE,SAAS,GAAkB,EAAE;IAC1E,OAAO;QACL,GAAG,OAAO;QACV,GAAG,SAAS;QACZ,GAAG,EAAE,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE;KAC9E,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACtD,MAAM,MAAM,GAAG,KAAgC,CAAC;IAChD,IAAI,MAAM,CAAC,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAElG,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC9D,IAAI,CAAC,8BAA8B,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;YAAE,OAAO,KAAK,CAAC;IACtF,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,SAAS,CAAC,KAAc;IAC/B,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACtD,MAAM,OAAO,GAAG,KAAgC,CAAC;IACjD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAC1B,IAAI,OAAO,OAAO,CAAC,SAAS,KAAK,QAAQ,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC;QAC9F,OAAO,KAAK,CAAC;IACf,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,MAAM;QAAE,OAAO,KAAK,CAAC;IAC5C,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,IAAI,OAAO,CAAC,MAAM,KAAK,UAAU;QAAE,OAAO,KAAK,CAAC;IAC7E,IAAI,OAAO,CAAC,SAAS,KAAK,SAAS,IAAI,OAAO,OAAO,CAAC,SAAS,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC3F,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,IAAI,OAAO,OAAO,CAAC,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACnF,IAAI,OAAO,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;QAC9B,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAC;QAClE,MAAM,GAAG,GAAG,OAAO,CAAC,GAA8B,CAAC;QACnD,IACE,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;YAClC,GAAG,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC;YAE3D,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QACnC,IACE,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC;YAChC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC;YAEzD,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|