@ken-jo/agent-connector 0.1.0 → 0.3.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/README.md +183 -24
- package/dist/{amp-QSFH2V7Y.js → amp-YGV66GJT.js} +9 -2
- package/dist/amp-YGV66GJT.js.map +1 -0
- package/dist/{antigravity-CX2YLSY7.js → antigravity-7IBYFO6P.js} +4 -3
- package/dist/{antigravity-cli-YQDFVV4U.js → antigravity-cli-JEOUP2VN.js} +4 -3
- package/dist/{antigravity-cli-YQDFVV4U.js.map → antigravity-cli-JEOUP2VN.js.map} +1 -1
- package/dist/{chunk-LTT56GI3.js → chunk-3JCRUROU.js} +18 -5
- package/dist/chunk-3JCRUROU.js.map +1 -0
- package/dist/chunk-44HUXXC6.js +284 -0
- package/dist/chunk-44HUXXC6.js.map +1 -0
- package/dist/{chunk-54EMKTTX.js → chunk-DS3LUFVN.js} +21 -21
- package/dist/chunk-DS3LUFVN.js.map +1 -0
- package/dist/chunk-GSGDG2BV.js +568 -0
- package/dist/chunk-GSGDG2BV.js.map +1 -0
- package/dist/{chunk-TRKWUNO4.js → chunk-KQSNDFBN.js} +13 -2
- package/dist/chunk-KQSNDFBN.js.map +1 -0
- package/dist/{chunk-KHO5WNTP.js → chunk-KVAAFYRR.js} +18 -2
- package/dist/chunk-KVAAFYRR.js.map +1 -0
- package/dist/chunk-N6HH7K6P.js +494 -0
- package/dist/chunk-N6HH7K6P.js.map +1 -0
- package/dist/{chunk-XUCWLJY5.js → chunk-NJU275US.js} +30 -30
- package/dist/{chunk-WCETQWWK.js → chunk-OP4KBXUO.js} +1 -17
- package/dist/chunk-OP4KBXUO.js.map +1 -0
- package/dist/{chunk-TIIP3RG2.js → chunk-PD4VFEBY.js} +342 -2
- package/dist/chunk-PD4VFEBY.js.map +1 -0
- package/dist/chunk-QADSG2JY.js +1989 -0
- package/dist/chunk-QADSG2JY.js.map +1 -0
- package/dist/{chunk-WV63X7IK.js → chunk-SCVBYSF7.js} +2 -2
- package/dist/chunk-SCVBYSF7.js.map +1 -0
- package/dist/{package-DLOVE2E4.js → chunk-WNREHWKL.js} +26 -115
- package/dist/chunk-WNREHWKL.js.map +1 -0
- package/dist/{chunk-YNZU6PAV.js → chunk-ZAM4M7LI.js} +74 -21
- package/dist/chunk-ZAM4M7LI.js.map +1 -0
- package/dist/claude-code-4275HQU4.js +1432 -0
- package/dist/claude-code-4275HQU4.js.map +1 -0
- package/dist/cli/sdk.js +4 -3
- package/dist/cli/sdk.js.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/{codebuff-QX57FJDN.js → codebuff-LE3KBZGM.js} +9 -2
- package/dist/codebuff-LE3KBZGM.js.map +1 -0
- package/dist/{codex-JFQTS6LU.js → codex-4BY2ZJIW.js} +148 -7
- package/dist/codex-4BY2ZJIW.js.map +1 -0
- package/dist/{copilot-cli-MTCGHZ2T.js → copilot-cli-IPISKO3V.js} +109 -2
- package/dist/copilot-cli-IPISKO3V.js.map +1 -0
- package/dist/{crush-I7PK3FGI.js → crush-X3E25EQG.js} +31 -3
- package/dist/crush-X3E25EQG.js.map +1 -0
- package/dist/{cursor-4TMXKJOZ.js → cursor-UH3W622V.js} +66 -5
- package/dist/cursor-UH3W622V.js.map +1 -0
- package/dist/{detect-WIAGVYS4.js → detect-MNDHGLSV.js} +7 -3
- package/dist/detect-MNDHGLSV.js.map +1 -0
- package/dist/{doctor-EZW2K4NI.js → doctor-N5KZDD55.js} +45 -7
- package/dist/doctor-N5KZDD55.js.map +1 -0
- package/dist/{droid-KN6VRMMT.js → droid-T5VBRXZF.js} +32 -3
- package/dist/droid-T5VBRXZF.js.map +1 -0
- package/dist/{gemini-cli-Y35FFK4M.js → gemini-cli-ZSUK55UA.js} +51 -3
- package/dist/gemini-cli-ZSUK55UA.js.map +1 -0
- package/dist/{goose-ZTGTSUFJ.js → goose-M3RR3CZG.js} +63 -4
- package/dist/goose-M3RR3CZG.js.map +1 -0
- package/dist/{hermes-H2KV44GK.js → hermes-CUNKIAR5.js} +76 -3
- package/dist/hermes-CUNKIAR5.js.map +1 -0
- package/dist/{hook-YVDILAMZ.js → hook-7LUOKIXH.js} +28 -13
- package/dist/hook-7LUOKIXH.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -1
- package/dist/install-TQTT4F3I.js +89 -0
- package/dist/install-TQTT4F3I.js.map +1 -0
- package/dist/{jetbrains-copilot-HWA3DMH7.js → jetbrains-copilot-C3HDGGML.js} +16 -2
- package/dist/jetbrains-copilot-C3HDGGML.js.map +1 -0
- package/dist/kilo-PKAJCWPX.js +852 -0
- package/dist/kilo-PKAJCWPX.js.map +1 -0
- package/dist/{kilo-cli-EAQTHXUW.js → kilo-cli-QWUVXG2O.js} +186 -7
- package/dist/kilo-cli-QWUVXG2O.js.map +1 -0
- package/dist/{kimi-GQ7VXNYT.js → kimi-EVD7YKXS.js} +167 -9
- package/dist/kimi-EVD7YKXS.js.map +1 -0
- package/dist/{kiro-VQTT6LO2.js → kiro-UQRY6EIO.js} +98 -4
- package/dist/kiro-UQRY6EIO.js.map +1 -0
- package/dist/{leaderboard-K6ON3SWW.js → leaderboard-MUJ7DURM.js} +3 -3
- package/dist/{mux-T2TV4CIQ.js → mux-5CI5FP2W.js} +9 -2
- package/dist/mux-5CI5FP2W.js.map +1 -0
- package/dist/{omp-PB4VDKS6.js → omp-AXA7XY22.js} +13 -2
- package/dist/omp-AXA7XY22.js.map +1 -0
- package/dist/{openclaw-ETN2M63O.js → openclaw-UZ5BMCGQ.js} +101 -4
- package/dist/openclaw-UZ5BMCGQ.js.map +1 -0
- package/dist/{opencode-FZ33JKUZ.js → opencode-NZBRLJN3.js} +53 -7
- package/dist/opencode-NZBRLJN3.js.map +1 -0
- package/dist/package-SYPA5POO.js +130 -0
- package/dist/package-SYPA5POO.js.map +1 -0
- package/dist/{pi-CIU2QHT4.js → pi-WMTUX6ES.js} +106 -23
- package/dist/pi-WMTUX6ES.js.map +1 -0
- package/dist/{qwen-code-5POCNU6P.js → qwen-code-MRRIUQ3P.js} +187 -6
- package/dist/qwen-code-MRRIUQ3P.js.map +1 -0
- package/dist/{roo-code-IOIUXRDE.js → roo-code-NI5UU6JB.js} +26 -2
- package/dist/roo-code-NI5UU6JB.js.map +1 -0
- package/dist/runtime/index.d.ts +38 -4
- package/dist/runtime/index.js +11 -6
- package/dist/{serve-HVWWY4B6.js → serve-XJOSA7BY.js} +9 -8
- package/dist/{serve-HVWWY4B6.js.map → serve-XJOSA7BY.js.map} +1 -1
- package/dist/{status-FE7TECZD.js → status-DYSS6BOR.js} +19 -9
- package/dist/status-DYSS6BOR.js.map +1 -0
- package/dist/{telemetry-36GESKQQ.js → telemetry-IJ4GP2Q3.js} +5 -4
- package/dist/{telemetry-36GESKQQ.js.map → telemetry-IJ4GP2Q3.js.map} +1 -1
- package/dist/{trae-JSNDZFKL.js → trae-CQC72RHH.js} +9 -2
- package/dist/trae-CQC72RHH.js.map +1 -0
- package/dist/types-CNigfEuR.d.ts +757 -0
- package/dist/uninstall-3DXIBRCV.js +162 -0
- package/dist/uninstall-3DXIBRCV.js.map +1 -0
- package/dist/{upgrade-JV2VK242.js → upgrade-GJEB3ITY.js} +29 -10
- package/dist/upgrade-GJEB3ITY.js.map +1 -0
- package/dist/{usage-6ER6EMEY.js → usage-2BJLM3GJ.js} +3 -3
- package/dist/{usage-event-JKBI6XCF.js → usage-event-472X3DOY.js} +8 -7
- package/dist/{usage-event-JKBI6XCF.js.map → usage-event-472X3DOY.js.map} +1 -1
- package/dist/{vscode-copilot-CK3NO54M.js → vscode-copilot-4RVRTCKV.js} +53 -4
- package/dist/vscode-copilot-4RVRTCKV.js.map +1 -0
- package/dist/{warp-QNVDXOG4.js → warp-X6MB67NT.js} +110 -4
- package/dist/warp-X6MB67NT.js.map +1 -0
- package/dist/{zed-VRJKWYUR.js → zed-NTCYW4V2.js} +120 -5
- package/dist/zed-NTCYW4V2.js.map +1 -0
- package/package.json +4 -3
- package/dist/amp-QSFH2V7Y.js.map +0 -1
- package/dist/chunk-54EMKTTX.js.map +0 -1
- package/dist/chunk-7MSDGHJW.js +0 -316
- package/dist/chunk-7MSDGHJW.js.map +0 -1
- package/dist/chunk-KHO5WNTP.js.map +0 -1
- package/dist/chunk-LTT56GI3.js.map +0 -1
- package/dist/chunk-TIIP3RG2.js.map +0 -1
- package/dist/chunk-TRKWUNO4.js.map +0 -1
- package/dist/chunk-WCETQWWK.js.map +0 -1
- package/dist/chunk-WV63X7IK.js.map +0 -1
- package/dist/chunk-XNAM7F35.js +0 -276
- package/dist/chunk-XNAM7F35.js.map +0 -1
- package/dist/chunk-YNZU6PAV.js.map +0 -1
- package/dist/claude-code-HKDV7T32.js +0 -637
- package/dist/claude-code-HKDV7T32.js.map +0 -1
- package/dist/codebuff-QX57FJDN.js.map +0 -1
- package/dist/codex-JFQTS6LU.js.map +0 -1
- package/dist/copilot-cli-MTCGHZ2T.js.map +0 -1
- package/dist/crush-I7PK3FGI.js.map +0 -1
- package/dist/cursor-4TMXKJOZ.js.map +0 -1
- package/dist/detect-WIAGVYS4.js.map +0 -1
- package/dist/doctor-EZW2K4NI.js.map +0 -1
- package/dist/droid-KN6VRMMT.js.map +0 -1
- package/dist/gemini-cli-Y35FFK4M.js.map +0 -1
- package/dist/goose-ZTGTSUFJ.js.map +0 -1
- package/dist/hermes-H2KV44GK.js.map +0 -1
- package/dist/hook-YVDILAMZ.js.map +0 -1
- package/dist/install-HQRYZOQ2.js +0 -60
- package/dist/install-HQRYZOQ2.js.map +0 -1
- package/dist/jetbrains-copilot-HWA3DMH7.js.map +0 -1
- package/dist/kilo-QXJRCSLT.js +0 -384
- package/dist/kilo-QXJRCSLT.js.map +0 -1
- package/dist/kilo-cli-EAQTHXUW.js.map +0 -1
- package/dist/kimi-GQ7VXNYT.js.map +0 -1
- package/dist/kiro-VQTT6LO2.js.map +0 -1
- package/dist/mux-T2TV4CIQ.js.map +0 -1
- package/dist/omp-PB4VDKS6.js.map +0 -1
- package/dist/openclaw-ETN2M63O.js.map +0 -1
- package/dist/opencode-FZ33JKUZ.js.map +0 -1
- package/dist/package-DLOVE2E4.js.map +0 -1
- package/dist/pi-CIU2QHT4.js.map +0 -1
- package/dist/qwen-code-5POCNU6P.js.map +0 -1
- package/dist/roo-code-IOIUXRDE.js.map +0 -1
- package/dist/status-FE7TECZD.js.map +0 -1
- package/dist/trae-JSNDZFKL.js.map +0 -1
- package/dist/types-CYUB1OTr.d.ts +0 -427
- package/dist/uninstall-IGCKNOSF.js +0 -72
- package/dist/uninstall-IGCKNOSF.js.map +0 -1
- package/dist/upgrade-JV2VK242.js.map +0 -1
- package/dist/vscode-copilot-CK3NO54M.js.map +0 -1
- package/dist/warp-QNVDXOG4.js.map +0 -1
- package/dist/zed-VRJKWYUR.js.map +0 -1
- /package/dist/{antigravity-CX2YLSY7.js.map → antigravity-7IBYFO6P.js.map} +0 -0
- /package/dist/{chunk-XUCWLJY5.js.map → chunk-NJU275US.js.map} +0 -0
- /package/dist/{leaderboard-K6ON3SWW.js.map → leaderboard-MUJ7DURM.js.map} +0 -0
- /package/dist/{usage-6ER6EMEY.js.map → usage-2BJLM3GJ.js.map} +0 -0
package/README.md
CHANGED
|
@@ -1,17 +1,39 @@
|
|
|
1
1
|
# agent-connector
|
|
2
2
|
|
|
3
|
-
> **
|
|
4
|
-
>
|
|
3
|
+
> **If you BUILD an MCP integration:** write your server + hooks once with
|
|
4
|
+
> `defineConnector()`, deploy it to every detected AI-agent platform, and measure
|
|
5
|
+
> your own server's per-tool tokens.
|
|
6
|
+
> **If you just USE agent CLIs:** run `agent-connector usage` to read their logs
|
|
7
|
+
> and see per-CLI / per-model token totals — no connector, config, or install.
|
|
5
8
|
|
|
6
9
|
[](https://www.npmjs.com/package/@ken-jo/agent-connector)
|
|
7
10
|
[](LICENSE)
|
|
8
11
|

|
|
9
|
-

|
|
12
|
+

|
|
10
13
|

|
|
11
14
|

|
|
12
15
|

|
|
13
16
|

|
|
14
|
-

|
|
18
|
+
|
|
19
|
+
## Who this is for
|
|
20
|
+
|
|
21
|
+
agent-connector serves **two distinct audiences** — pick your track:
|
|
22
|
+
|
|
23
|
+
- **I build an MCP integration** (MCP developer) → you write your server + hooks
|
|
24
|
+
once and deploy them everywhere, then measure **your own server's** per-tool
|
|
25
|
+
tokens. Start at [**Quick start → MCP developer**](#mcp-developer).
|
|
26
|
+
- **I just use agent CLIs and want to see token usage** (agent-CLI user) → you
|
|
27
|
+
already run Claude Code / Codex / Cursor and haven't authored a connector; you
|
|
28
|
+
just want per-CLI / per-model token totals. Run
|
|
29
|
+
[**`agent-connector usage`**](#agent-cli-end-user) — no connector, config, or
|
|
30
|
+
install required.
|
|
31
|
+
|
|
32
|
+
> The dividing line: the connector-free `usage` path reports **whole-conversation
|
|
33
|
+
> totals** per agent CLI / model / project / session / day. It does **not** itemize
|
|
34
|
+
> cost by individual MCP server or tool — agent CLIs don't log per-tool token
|
|
35
|
+
> attribution. Per-MCP and per-tool numbers come only from the serve-proxy
|
|
36
|
+
> telemetry that an MCP developer's own connector produces (the developer track).
|
|
15
37
|
|
|
16
38
|
Every agent host — Claude Code, Codex, Cursor, OpenCode, Copilot, Gemini, Warp,
|
|
17
39
|
… — re-invents the same two integration surfaces (**MCP registration** and
|
|
@@ -28,8 +50,12 @@ agent-connector is the middleware that does it for you:
|
|
|
28
50
|
2. **Token telemetry, by default.** No host reports per-tool usage back to an MCP
|
|
29
51
|
server. agent-connector measures your server's *own* bytes (args in, results
|
|
30
52
|
out, tool schemas) and tokenizes them locally — so you get a
|
|
31
|
-
platform-independent answer to *"which of
|
|
32
|
-
|
|
53
|
+
platform-independent answer to *"which of **your server's own tools** (the MCP
|
|
54
|
+
your connector declares and wraps) cost the most context?"*, with **aggregate
|
|
55
|
+
counts only, stored locally, zero egress by default.** Per-tool telemetry is
|
|
56
|
+
automatic for **stdio** servers only; remote (`http`/`sse`/`ws`) servers are
|
|
57
|
+
registered but **not wrapped** (the proxy cannot intercept remote transports),
|
|
58
|
+
so they yield no per-tool telemetry.
|
|
33
59
|
|
|
34
60
|
> Status: **29 platforms, all 3 hook paradigms** (exceeds the
|
|
35
61
|
> [tokscale](https://github.com/junhoyeo/tokscale) token-leaderboard coverage).
|
|
@@ -47,9 +73,9 @@ agent-connector is the middleware that does it for you:
|
|
|
47
73
|
## Verification
|
|
48
74
|
|
|
49
75
|
The full single-API contract is **install-verified across all 29 platforms**. A
|
|
50
|
-
sample connector declaring **all four surfaces** — MCP server **+**
|
|
51
|
-
hooks **+** slash commands **+** tools (skills + subagents) — was
|
|
52
|
-
an isolated environment for every adapter and inspected on disk:
|
|
76
|
+
sample connector declaring **all four launch surfaces** — MCP server **+**
|
|
77
|
+
lifecycle hooks **+** slash commands **+** tools (skills + subagents) — was
|
|
78
|
+
installed into an isolated environment for every adapter and inspected on disk:
|
|
53
79
|
|
|
54
80
|
- **29 / 29 platforms — zero missing, zero failed surfaces.** Each surface is
|
|
55
81
|
written where the host supports it and gracefully *skip-warned* (never silently
|
|
@@ -76,7 +102,12 @@ an isolated environment for every adapter and inspected on disk:
|
|
|
76
102
|
- **Clean uninstall + `--purge`.** Every installed surface reverses; `--purge`
|
|
77
103
|
deregisters the connector record and tears down the home binary when no
|
|
78
104
|
connectors remain (29 / 29).
|
|
79
|
-
- **
|
|
105
|
+
- **1438 tests passing** · `tsc` clean · build green.
|
|
106
|
+
|
|
107
|
+
The 0.2.0 additions — the `memory` surface, the `nativeHooks` passthrough, and
|
|
108
|
+
`configPatch` — went through the same bar: dogfooded against real connector
|
|
109
|
+
migrations (context-mode, oh-my-claudecode) and verified in isolated-home
|
|
110
|
+
installs before landing (see [`CHANGELOG.md`](CHANGELOG.md)).
|
|
80
111
|
|
|
81
112
|
Coverage was confirmed by **installing the real, not-yet-present agent CLIs into
|
|
82
113
|
isolated homes and observing their actual config** — which caught defects a
|
|
@@ -85,6 +116,54 @@ static code/web audit missed. See the reports under
|
|
|
85
116
|
|
|
86
117
|
## Quick start
|
|
87
118
|
|
|
119
|
+
The Quick start forks by audience. Just want to see your agent CLIs' token
|
|
120
|
+
usage? **Agent-CLI end user** comes first — it needs no connector at all, and
|
|
121
|
+
those few lines are the entire track. Build an integration? Skip ahead to
|
|
122
|
+
**MCP developer** — everything from there to the end of the README is yours.
|
|
123
|
+
|
|
124
|
+
### Agent-CLI end user
|
|
125
|
+
|
|
126
|
+
> **Audience B** — you already run agent CLIs (Claude Code / Codex / Cursor / …)
|
|
127
|
+
> and have **not** authored a connector. You just want to know how many tokens
|
|
128
|
+
> your agent CLIs are burning.
|
|
129
|
+
|
|
130
|
+
**No connector, no config file, no install.** Run it straight from `npx`; it
|
|
131
|
+
reads your local agent-CLI session logs **read-only** and never writes any host
|
|
132
|
+
config:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
# how many tokens are my agent CLIs burning, grouped by CLI/model/project/session/day?
|
|
136
|
+
npx @ken-jo/agent-connector usage report --by platform # or model|project|session|day
|
|
137
|
+
|
|
138
|
+
# which agent CLI burned the most tokens?
|
|
139
|
+
npx @ken-jo/agent-connector usage leaderboard --by platform # or --by model
|
|
140
|
+
|
|
141
|
+
# export the raw aggregate rows (counts only — never your prompts or results)
|
|
142
|
+
npx @ken-jo/agent-connector usage export --format csv --out usage.csv
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
> **What `usage` does — and doesn't — show.** It reports **whole-conversation
|
|
146
|
+
> totals** per agent CLI / model / project / session / day. It does **not** break
|
|
147
|
+
> down cost by individual MCP server or by tool — agent CLIs don't log per-tool
|
|
148
|
+
> token attribution, so the connector-free path can only see session totals.
|
|
149
|
+
> To get **per-MCP / per-tool** numbers for an MCP, that MCP must be deployed and
|
|
150
|
+
> wrapped via a connector (the MCP-developer track and its `telemetry` command).
|
|
151
|
+
|
|
152
|
+
> **Coverage caveats.** Local readers (claude-code, codex, gemini-cli, …) report
|
|
153
|
+
> host-logged exact counts; a few readers are host-estimated (labeled in the
|
|
154
|
+
> `CONFIDENCE` column). Five "synced" platforms — **cursor, antigravity,
|
|
155
|
+
> antigravity-cli, trae, warp** — are reported as skipped (`requires sync — no
|
|
156
|
+
> local cache found`) unless a local cache already exists, since agent-connector
|
|
157
|
+
> does not populate that cache.
|
|
158
|
+
|
|
159
|
+
> **That's the entire agent-CLI track.** Everything below this point is the
|
|
160
|
+
> MCP-developer track. ([back to top](#agent-connector))
|
|
161
|
+
|
|
162
|
+
### MCP developer
|
|
163
|
+
|
|
164
|
+
> **Audience A** — you write an MCP server + hooks once and deploy them across
|
|
165
|
+
> every detected host, measuring **your own server's** per-tool tokens.
|
|
166
|
+
|
|
88
167
|
agent-connector is an **SDK you depend on**, not a global tool. Add it to the
|
|
89
168
|
package that holds your connector, declare the connector once, then **either**
|
|
90
169
|
ship a branded CLI your users drive directly **or** run it with `npx`. No
|
|
@@ -98,16 +177,23 @@ npm install @ken-jo/agent-connector
|
|
|
98
177
|
|
|
99
178
|
# 3a. ship a branded CLI so YOUR users drive it (auto-scoped — no --connector):
|
|
100
179
|
acme-db detect # which platforms are installed here?
|
|
101
|
-
acme-db install --dry-run # preview every change
|
|
102
|
-
acme-db install # deploy across
|
|
180
|
+
acme-db install --dry-run # preview every change across the detected hosts
|
|
181
|
+
acme-db install # deploy across the hosts detected on this machine
|
|
182
|
+
acme-db doctor # health-check every detected platform — add --probe for a live MCP handshake (initialize → ping → tools/list)
|
|
183
|
+
acme-db upgrade # day 2: re-render configs + heal the home-binary pointer (aliases: sync, update)
|
|
103
184
|
acme-db leaderboard # acme-db's token footprint vs the boards
|
|
104
|
-
acme-db package # OR
|
|
185
|
+
acme-db package # OR distribute: marketplace plugin (9 formats) — or --format mcp-server-json | mcpb for the MCP Registry / an MCPB bundle (see "Publish to the MCP ecosystem")
|
|
186
|
+
acme-db uninstall # full inverse — removes everything install wrote; --purge clears framework state; --dry-run works here too
|
|
105
187
|
|
|
106
188
|
# 3b. …or just run it from the project with npx — still no global install:
|
|
107
189
|
npx @ken-jo/agent-connector detect
|
|
108
190
|
npx @ken-jo/agent-connector install
|
|
109
191
|
```
|
|
110
192
|
|
|
193
|
+
> `install` targets only the hosts actually **detected** on this machine (or an
|
|
194
|
+
> explicit `--targets` / `connector.targets` list), intersected with the
|
|
195
|
+
> 29-adapter registry — there is no "install to all 29 unconditionally" path.
|
|
196
|
+
|
|
111
197
|
> **Optional convenience.** A global `npm i -g @ken-jo/agent-connector` is **not**
|
|
112
198
|
> required for the flow above — `npx @ken-jo/agent-connector …` runs it straight from
|
|
113
199
|
> your project. Install it globally only if you want to poke at the CLI by hand
|
|
@@ -128,7 +214,7 @@ users never install agent-connector globally or type `--connector`. See
|
|
|
128
214
|
"name": "acme-db-tools",
|
|
129
215
|
"type": "module",
|
|
130
216
|
"bin": { "acme-db": "./bin.mjs" },
|
|
131
|
-
"dependencies": { "@ken-jo/agent-connector": "0.
|
|
217
|
+
"dependencies": { "@ken-jo/agent-connector": "^0.2.0" }
|
|
132
218
|
}
|
|
133
219
|
```
|
|
134
220
|
|
|
@@ -151,11 +237,11 @@ After a consumer installs **your** package (`npm install acme-db-tools`), the
|
|
|
151
237
|
`acme-db` bin is on their PATH and every command is scoped to your connector:
|
|
152
238
|
|
|
153
239
|
```bash
|
|
154
|
-
acme-db install # deploy
|
|
240
|
+
acme-db install # deploy acme-db across the detected hosts (no --connector)
|
|
155
241
|
acme-db upgrade # bring everything current (alias: sync, update)
|
|
156
|
-
acme-db doctor # health-check every platform for acme-db
|
|
242
|
+
acme-db doctor # health-check every detected platform for acme-db
|
|
157
243
|
acme-db leaderboard # the 🔌 MCP/plugin section, scoped to acme-db
|
|
158
|
-
acme-db telemetry report --by tool # acme-db's
|
|
244
|
+
acme-db telemetry report --by tool # per-tool tokens for acme-db's own wrapped server
|
|
159
245
|
acme-db --help # every agent-connector subcommand, branded
|
|
160
246
|
```
|
|
161
247
|
|
|
@@ -174,6 +260,13 @@ Same one definition, your choice of distribution:
|
|
|
174
260
|
|
|
175
261
|
- **Direct install** (above) — `install` writes each host's native MCP + plugin/
|
|
176
262
|
extension config in place; no per-platform marketplace submission or review.
|
|
263
|
+
- **Marketplace install** — `install --method marketplace` drives the host's own
|
|
264
|
+
plugin flow end-to-end for **Claude Code, codex, and agy/Antigravity** (stages
|
|
265
|
+
the bundle, registers a local marketplace where the host has one, then runs the
|
|
266
|
+
host's plugin-install verb; headless, idempotent — verified live on Linux and
|
|
267
|
+
native Windows). `uninstall --method auto` reverses whichever method is actually
|
|
268
|
+
installed, a guard refuses installing the same connector by BOTH methods
|
|
269
|
+
(duplicated hooks/servers), and `doctor` checks registration drift.
|
|
177
270
|
- **Marketplace package** — `agent-connector package` turns the connector into a
|
|
178
271
|
marketplace/extension bundle (manifest + bundled commands, agents, skills,
|
|
179
272
|
hooks, MCP) for **9 host formats** (plus 2 official MCP standard artifacts —
|
|
@@ -183,7 +276,7 @@ Same one definition, your choice of distribution:
|
|
|
183
276
|
`qwen-extension` · `agy-plugin` (Antigravity CLI/IDE) · `cursor-plugin` ·
|
|
184
277
|
`kimi-plugin` · `npm-plugin` (OpenCode / Kilo CLI / Pi). Hooks + MCP keep the
|
|
185
278
|
telemetry serve-wrapper, so a marketplace-installed connector still reports
|
|
186
|
-
per-tool tokens.
|
|
279
|
+
per-tool tokens (for its stdio server).
|
|
187
280
|
|
|
188
281
|
```bash
|
|
189
282
|
# emit all 9 host formats (mcp-server-json + mcpb are opt-in by name — they need publish{})
|
|
@@ -194,6 +287,11 @@ Same one definition, your choice of distribution:
|
|
|
194
287
|
# e.g. Gemini CLI: gemini extensions install ./dist-plugin/gemini-extension/<id>
|
|
195
288
|
```
|
|
196
289
|
|
|
290
|
+
> **Embedded-path caveat.** 8 of the 9 host bundles bake in the absolute
|
|
291
|
+
> home-bin launcher path of the machine that ran `package`, so they're valid
|
|
292
|
+
> for a **local install on that same machine/home**. For shared distribution use
|
|
293
|
+
> `npm-plugin` or the 2 MCP standard artifacts, or re-run `package` per machine.
|
|
294
|
+
|
|
197
295
|
## Define once
|
|
198
296
|
|
|
199
297
|
```ts
|
|
@@ -221,6 +319,24 @@ export default defineConnector({
|
|
|
221
319
|
});
|
|
222
320
|
```
|
|
223
321
|
|
|
322
|
+
> **Native hooks escape hatch.** The normalized `hooks` API covers the 12
|
|
323
|
+
> cross-platform events. For host-only events — Claude Code alone ships 30
|
|
324
|
+
> (`TaskCompleted`, `TeammateIdle`, `WorktreeCreate`, …) — declare
|
|
325
|
+
> `platforms: { "claude-code": { nativeHooks: { TaskCompleted: { handler } } } }`:
|
|
326
|
+
> the handler receives the host's **raw** payload and whatever it returns is the
|
|
327
|
+
> **verbatim** JSON reply (exit 0 only — exit-2 blocking isn't modeled). Claude
|
|
328
|
+
> Code only for now; other hosts skip-warn, never silently.
|
|
329
|
+
|
|
330
|
+
> **Host-config key patches.** For host-exclusive *settings keys* no other
|
|
331
|
+
> surface reaches (Claude Code's `statusLine`, an experimental `env.*` flag),
|
|
332
|
+
> declare `platforms: { "claude-code": { configPatch: [{ key, value, reason }] } }`.
|
|
333
|
+
> Semantics are fixed: **set-if-absent + skip-warn on any conflict** — never
|
|
334
|
+
> overwrite, never deep-merge. Ownership is refcounted in a persisted ledger, so
|
|
335
|
+
> uninstall removes a key only when the last owning connector releases it and the
|
|
336
|
+
> value is untouched; security-relevant keys (`permissions*`, `apiKey*`,
|
|
337
|
+
> `env.ANTHROPIC_*`, token/secret env vars, …) are hard-refused. Claude Code only
|
|
338
|
+
> for now; other hosts skip-warn with the exact manual edit.
|
|
339
|
+
|
|
224
340
|
`agent-connector install` turns that into, e.g.:
|
|
225
341
|
|
|
226
342
|
| Host | What gets written |
|
|
@@ -232,6 +348,49 @@ export default defineConnector({
|
|
|
232
348
|
…each pointing hooks at a **single stable home binary**, so one update propagates
|
|
233
349
|
everywhere.
|
|
234
350
|
|
|
351
|
+
### Standing guidance (`memory`) — aligned with the AGENTS.md standard
|
|
352
|
+
|
|
353
|
+
Ship the rules every agent should follow when your MCP is installed:
|
|
354
|
+
|
|
355
|
+
```ts
|
|
356
|
+
memory: [
|
|
357
|
+
{
|
|
358
|
+
content:
|
|
359
|
+
"Use the acme-db MCP tools for schema questions; never hand-edit migrations.",
|
|
360
|
+
},
|
|
361
|
+
],
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
**Write the guidance once — it lands in the standard
|
|
365
|
+
[AGENTS.md](https://agents.md) on 27 of the 29 hosts** (the open, Linux
|
|
366
|
+
Foundation-stewarded "README for agents" format): project scope targets
|
|
367
|
+
`<projectDir>/AGENTS.md` — and where a host resolves its rules file
|
|
368
|
+
exclusively, the target is *probed* so the block lands in the file the host
|
|
369
|
+
actually reads (zed's first-match rules list, warp's `WARP.md` priority,
|
|
370
|
+
hermes' `.hermes.md`, opencode's `CLAUDE.md` fallback, codex's
|
|
371
|
+
`AGENTS.override.md`). User scope goes to the host's documented global memory
|
|
372
|
+
file (AGENTS.md where one exists, else the host's own file — `~/.qwen/QWEN.md`,
|
|
373
|
+
goose `.goosehints`, kilo/roo/kiro rules dirs).
|
|
374
|
+
The two hosts that don't read AGENTS.md are wired per their own official docs:
|
|
375
|
+
|
|
376
|
+
- **Claude Code** → the block goes in `CLAUDE.md` (the official memory docs are
|
|
377
|
+
explicit: *"Claude Code reads CLAUDE.md, not AGENTS.md"*). Opt-in
|
|
378
|
+
`platforms: { "claude-code": { memory: { mode: "agents-import" } } }` instead
|
|
379
|
+
writes the canonical AGENTS.md block plus Anthropic's documented `@AGENTS.md`
|
|
380
|
+
import line as a managed bridge in CLAUDE.md — opt-in because the import makes
|
|
381
|
+
Claude read the *entire* AGENTS.md.
|
|
382
|
+
- **Gemini CLI** → `GEMINI.md`, unless the user's `context.fileName` setting
|
|
383
|
+
already opts Gemini into AGENTS.md (probed and respected — never edited).
|
|
384
|
+
|
|
385
|
+
Writes are **surgical managed blocks** — marker-fenced
|
|
386
|
+
(`<!-- agent-connector:begin <id>/memory hash=… -->`), hash-stamped, multiple
|
|
387
|
+
connectors coexist in one file, and bytes outside your own markers are never
|
|
388
|
+
touched. If a user edits inside the block, the hash mismatch is detected and the
|
|
389
|
+
edit is *left intact* (sync warns; `install --force` overwrites after a backup).
|
|
390
|
+
Uninstall excises exactly your blocks and `doctor` verifies them (present /
|
|
391
|
+
hash-intact / user-edited / file missing). Hosts with no writable memory file at
|
|
392
|
+
a scope skip-warn, never silently.
|
|
393
|
+
|
|
235
394
|
## How it works (operating model)
|
|
236
395
|
|
|
237
396
|
- **Home-dir, single binary.** The runtime installs once under
|
|
@@ -252,20 +411,20 @@ everywhere.
|
|
|
252
411
|
| Command | Purpose |
|
|
253
412
|
|---|---|
|
|
254
413
|
| `detect` | List installed platforms, scopes, capabilities, hook paradigm. |
|
|
255
|
-
| `install [--scope user\|project] [--targets …] [--dry-run]` | Render + write MCP + hooks across targets. |
|
|
414
|
+
| `install [--scope user\|project] [--targets …] [--dry-run] [--force]` | Render + write MCP + hooks + content surfaces (commands / skills / subagents / memory) across targets. `--force` overwrites user-edited memory blocks (after a backup). |
|
|
256
415
|
| `uninstall [--targets …]` | Full inverse — removes everything we wrote. |
|
|
257
416
|
| `upgrade [--channel stable\|latest]` | One verb (alias: `update`, `sync`) — re-render host config + heal stale pointers + refresh the home-binary pointer, printing managed-update guidance (never a silent self-update). |
|
|
258
417
|
| `doctor [--probe]` | Per-platform health checks with fixes; `--probe` runs a live MCP handshake (initialize → ping → tools/list) against the real server. |
|
|
259
418
|
| `status` | Light install-state: which connectors are present on which hosts (always exits 0). |
|
|
260
419
|
| `package [--format <fmt>\|all]` | Emit a host bundle, or an OFFICIAL standard artifact: `mcp-server-json` (registry) · `mcpb` (one-click bundle). |
|
|
261
|
-
| `telemetry report [--by tool\|session\|project] [--since 7d] [--connector <id>]` |
|
|
262
|
-
| `telemetry export [--format csv\|json] [--connector <id>]` | Raw aggregate records. |
|
|
263
|
-
| `usage report\|export\|leaderboard` | Host-native token usage parsed read-only from each agent CLI's own logs
|
|
264
|
-
| `leaderboard [--since 7d] [--connector <id>] [--scope <slice>]` | Three origin-labeled boards; `--connector` filters the 🔌
|
|
420
|
+
| `telemetry report [--by tool\|session\|project] [--since 7d] [--connector <id>]` | **MCP-developer track.** Per-tool token footprint of **your connector's own wrapped server** (scope with `--connector`). Stdio servers only. |
|
|
421
|
+
| `telemetry export [--format csv\|json] [--connector <id>]` | Raw aggregate records for your wrapped server. |
|
|
422
|
+
| `usage report\|export\|leaderboard [--by platform\|model\|project\|session\|day]` | **Agent-CLI-user track (no connector needed).** Host-native token usage parsed read-only from each agent CLI's own logs — **whole-conversation totals per platform / model / project / session / day. Does NOT break down by individual MCP or tool** (agent CLIs don't log per-tool attribution). Never summed with `telemetry`. |
|
|
423
|
+
| `leaderboard [--since 7d] [--connector <id>] [--scope <slice>]` | Three origin-labeled boards with **different prerequisites** (counts are never summed across them): 🔌 MCP/plugin needs a connector + serve traffic; 🛰️ host-native turns need the opt-in usage hook (Gemini CLI / Antigravity only); 🖥️ host/user works with **no setup**. `--connector` filters the 🔌 board to one connector. |
|
|
265
424
|
|
|
266
425
|
> `hook` and `serve` also exist — internal entrypoints the written host configs
|
|
267
426
|
> point at; you never run them by hand. Full flag-level reference: the
|
|
268
|
-
> [docs site `/docs/cli`](https://github.com/ken-jo/agent-connector) · `llms-full.txt` §3 (canonical, drift-guarded by tests).
|
|
427
|
+
> [docs site `/docs/dev/cli`](https://github.com/ken-jo/agent-connector) · `llms-full.txt` §3 (canonical, drift-guarded by tests).
|
|
269
428
|
|
|
270
429
|
> A **branded CLI** auto-injects `--connector` for you: `<your-tool>
|
|
271
430
|
> leaderboard` ≈ `agent-connector leaderboard --connector <id>`, and
|
|
@@ -4,13 +4,14 @@ import {
|
|
|
4
4
|
} from "./chunk-DBQMX75U.js";
|
|
5
5
|
import {
|
|
6
6
|
BaseAdapter
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-PD4VFEBY.js";
|
|
8
8
|
import "./chunk-7L6ITC4T.js";
|
|
9
9
|
import {
|
|
10
10
|
buildServeWrapperCommand,
|
|
11
11
|
isHomeBinHookCommand,
|
|
12
12
|
shouldWrapForTelemetry
|
|
13
13
|
} from "./chunk-MEH7GC7R.js";
|
|
14
|
+
import "./chunk-N6HH7K6P.js";
|
|
14
15
|
import "./chunk-TRQKHP7D.js";
|
|
15
16
|
|
|
16
17
|
// src/adapters/amp/index.ts
|
|
@@ -24,6 +25,9 @@ var AmpAdapter = class extends BaseAdapter {
|
|
|
24
25
|
name = "Amp";
|
|
25
26
|
paradigm = "mcp-only";
|
|
26
27
|
capabilities = {
|
|
28
|
+
// Memory surface: AGENTS.md-first managed block via the BaseAdapter default
|
|
29
|
+
// (memoryTargets: project <projectDir>/AGENTS.md; user scope where documented).
|
|
30
|
+
supportsMemory: true,
|
|
27
31
|
// Amp has no lifecycle hook system — every hook capability is false.
|
|
28
32
|
preToolUse: false,
|
|
29
33
|
postToolUse: false,
|
|
@@ -186,6 +190,9 @@ var AmpAdapter = class extends BaseAdapter {
|
|
|
186
190
|
{
|
|
187
191
|
name: `${this.name}: server entry registered`,
|
|
188
192
|
check: () => {
|
|
193
|
+
if (!ctx.connector.server) {
|
|
194
|
+
return { status: "OK", detail: "no MCP server declared" };
|
|
195
|
+
}
|
|
189
196
|
const cfg = this.readJson(settingsPath);
|
|
190
197
|
const bucket = cfg?.[MCP_ROOT_KEY];
|
|
191
198
|
if (!cfg || !bucket) {
|
|
@@ -214,4 +221,4 @@ export {
|
|
|
214
221
|
adapter,
|
|
215
222
|
amp_default as default
|
|
216
223
|
};
|
|
217
|
-
//# sourceMappingURL=amp-
|
|
224
|
+
//# sourceMappingURL=amp-YGV66GJT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapters/amp/index.ts"],"sourcesContent":["/**\n * adapters/amp — Amp (Sourcegraph / AmpCode) platform adapter for agent-connector.\n *\n * Amp is an **mcp-only** host: it exposes no lifecycle hook system, so MCP is the\n * only extensibility surface. This adapter installs the MCP server entry and\n * reports hooks as unavailable — the same shape validated by the Warp adapter.\n *\n * MCP config (report §2 / §5.3):\n * - user scope → ~/.config/amp/settings.json\n * - project scope → <projectDir>/.amp/settings.json\n * Both are JSONC (we write plain JSON, which is valid JSONC). The settings file\n * is SHARED with the rest of Amp's configuration, so we MERGE our entry into\n * the existing object and never clobber unrelated keys.\n *\n * QUIRK 1 — dotted top-level key: Amp does NOT use a nested `mcpServers` object.\n * The MCP registry is a single FLAT settings key literally named\n * `\"amp.mcpServers\"`, whose value is an object of `{ id: { command, args, env } }`.\n * We therefore set `settings[\"amp.mcpServers\"][connectorId] = entry`. Because the\n * base JSON helpers index `config[rootKey][serverId]`, passing the dotted string\n * as the root key writes exactly that flat key while preserving every sibling\n * setting (true merge).\n *\n * QUIRK 2 — native interpolation: Amp expands `${VAR_NAME}` in stdio entries at\n * runtime, so we KEEP env/header/url refs native by rewriting the portable\n * `${env:VAR}` syntax to Amp's `${VAR}` token rather than resolving to literals\n * (secrets stay out of the settings file).\n */\n\nimport { existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nimport { BaseAdapter } from \"../base.js\";\nimport type { Adapter, InstallContext } from \"../spi.js\";\nimport type {\n ChangeRecord,\n DetectedPlatform,\n HealthCheck,\n HookParadigm,\n PlatformCapabilities,\n PlatformId,\n ServerDef,\n Transport,\n} from \"../../core/types.js\";\nimport { rewriteEnvRefs } from \"../../core/interpolate.js\";\nimport {\n buildServeWrapperCommand,\n isHomeBinHookCommand,\n shouldWrapForTelemetry,\n} from \"../../core/spawn.js\";\n\nconst HOST: PlatformId = \"amp\";\n/**\n * QUIRK: a single FLAT, dotted settings key — NOT a nested object. The base\n * JSON helpers treat this as `config[\"amp.mcpServers\"][serverId]`.\n */\nconst MCP_ROOT_KEY = \"amp.mcpServers\";\n\n/** Native MCP server entry shapes Amp accepts under `amp.mcpServers`. */\ninterface AmpStdioServer {\n command: string;\n args?: string[];\n env?: Record<string, string>;\n}\ninterface AmpHttpServer {\n url: string;\n headers?: Record<string, string>;\n}\n\nexport class AmpAdapter extends BaseAdapter implements Adapter {\n readonly id: PlatformId = HOST;\n readonly name = \"Amp\";\n readonly paradigm: HookParadigm = \"mcp-only\";\n\n readonly capabilities: PlatformCapabilities = {\n // Memory surface: AGENTS.md-first managed block via the BaseAdapter default\n // (memoryTargets: project <projectDir>/AGENTS.md; user scope where documented).\n supportsMemory: true,\n // Amp has no lifecycle hook system — every hook capability is false.\n preToolUse: false,\n postToolUse: false,\n preCompact: false,\n sessionStart: false,\n sessionEnd: false,\n userPromptSubmit: false,\n stop: false,\n notification: false,\n canModifyArgs: false,\n canModifyOutput: false,\n canInjectSessionContext: false,\n // Amp registers stdio and Streamable HTTP MCP servers.\n transports: [\"stdio\", \"http\"],\n };\n\n // ── Detection ────────────────────────────────────────────────────────────\n\n detectInstalled(projectDir: string): DetectedPlatform {\n const userDir = join(homedir(), \".config\", \"amp\");\n const userSettings = join(userDir, \"settings.json\");\n const projectDirAmp = join(projectDir, \".amp\");\n const projectSettings = join(projectDirAmp, \"settings.json\");\n\n const userInstalled = existsSync(userDir) || existsSync(userSettings);\n const projectInstalled = existsSync(projectDirAmp) || existsSync(projectSettings);\n const installed = userInstalled || projectInstalled;\n const scope = projectInstalled && !userInstalled ? \"project\" : \"user\";\n const configPath = scope === \"project\" ? projectSettings : userSettings;\n\n return {\n id: this.id,\n name: this.name,\n installed,\n paradigm: this.paradigm,\n capabilities: this.capabilities,\n configPath,\n scope,\n reason: installed\n ? `found Amp config (${scope}) at ${configPath}`\n : `no Amp config at ${userSettings} or ${projectSettings}`,\n confidence: installed ? \"high\" : \"low\",\n };\n }\n\n // ── Native paths ─────────────────────────────────────────────────────────\n\n getConfigDir(ctx: InstallContext): string {\n return ctx.scope === \"project\"\n ? join(ctx.projectDir, \".amp\")\n : join(homedir(), \".config\", \"amp\");\n }\n\n getServerConfigPath(ctx: InstallContext): string {\n return join(this.getConfigDir(ctx), \"settings.json\");\n }\n\n /**\n * Amp has no hook file — hooks are not a thing here. The hook \"config path\" is\n * the same settings.json so the generic doctor/backup behave sensibly.\n */\n getHookConfigPath(ctx: InstallContext): string {\n return this.getServerConfigPath(ctx);\n }\n\n // ── MCP server install / uninstall ───────────────────────────────────────\n\n installServer(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n const override = connector.platforms[HOST]?.server;\n if (!connector.server || override === false) {\n return [\n {\n platform: this.id,\n action: \"skip\",\n detail: connector.server\n ? \"server registration disabled for amp\"\n : \"connector declares no MCP server\",\n },\n ];\n }\n\n // Shallow-merge any per-platform server override into the base ServerDef.\n const server: ServerDef =\n override && typeof override === \"object\"\n ? { ...connector.server, ...override }\n : connector.server;\n\n const serverPath = this.getServerConfigPath(ctx);\n const entry = this.renderServerEntry(ctx, server);\n\n // Upsert into the flat \"amp.mcpServers\" key, merging into existing settings.\n return [\n this.upsertServerInJson(serverPath, MCP_ROOT_KEY, connector.id, entry, ctx.dryRun),\n ];\n }\n\n uninstallServer(ctx: InstallContext): ChangeRecord[] {\n const serverPath = this.getServerConfigPath(ctx);\n return [\n this.removeServerFromJson(serverPath, MCP_ROOT_KEY, ctx.connector.id, ctx.dryRun),\n ];\n }\n\n /** Render a normalized ServerDef into Amp's native `amp.mcpServers` entry. */\n private renderServerEntry(\n ctx: InstallContext,\n server: ServerDef,\n ): AmpStdioServer | AmpHttpServer {\n const transport: Transport = server.transport;\n\n if (transport === \"stdio\") {\n let command = server.command ?? \"\";\n let args = [...(server.args ?? [])];\n\n // Transparent telemetry wrapping: route the real command through\n // `<homeBin> serve --connector <id> -- <command> <args...>`.\n if (shouldWrapForTelemetry(server, ctx.connector.telemetry)) {\n const wrapped = buildServeWrapperCommand(\n ctx.homeBinPath,\n ctx.connector.id,\n command,\n args,\n ctx.scope,\n this.id,\n );\n command = wrapped.command;\n args = wrapped.args;\n }\n\n // Amp expands ${VAR_NAME} natively, so keep refs native (no literals).\n const entry: AmpStdioServer = { command: this.rewrite(command) };\n if (args.length > 0) entry.args = args.map((a) => this.rewrite(a));\n const env = this.renderEnv(server.env);\n if (env) entry.env = env;\n return entry;\n }\n\n // http (and any other remote transport we surface) — Amp registers a URL.\n const entry: AmpHttpServer = { url: this.rewrite(server.url ?? \"\") };\n const headers = this.renderEnv(server.headers);\n if (headers) entry.headers = headers;\n return entry;\n }\n\n /**\n * Render env/header values. Amp supports native `${VAR_NAME}` interpolation,\n * so translate `${env:VAR}` refs to that native token rather than baking\n * secrets into the file. Literals pass through unchanged.\n */\n private renderEnv(\n env: Record<string, string> | undefined,\n ): Record<string, string> | undefined {\n if (!env || Object.keys(env).length === 0) return undefined;\n const out: Record<string, string> = {};\n for (const [k, v] of Object.entries(env)) out[k] = this.rewrite(v);\n return out;\n }\n\n /** Translate `${env:VAR(:-default)}` to Amp's native `${VAR}` token. */\n private rewrite(value: string): string {\n return rewriteEnvRefs(value, ampEnvToken);\n }\n\n // ── Hooks (unavailable — Amp is mcp-only) ────────────────────────────────\n\n installHooks(_ctx: InstallContext): ChangeRecord[] {\n return [\n {\n platform: this.id,\n action: \"skip\",\n detail: \"hooks unavailable (Amp is mcp-only)\",\n },\n ];\n }\n\n uninstallHooks(_ctx: InstallContext): ChangeRecord[] {\n return [\n {\n platform: this.id,\n action: \"skip\",\n detail: \"hooks unavailable (Amp is mcp-only)\",\n },\n ];\n }\n\n /**\n * True when a hook command references our home binary AND this connector id\n * (anchored so a shared-prefix id can't collide — see isHomeBinHookCommand).\n * Amp installs no hooks, but this guard keeps any future shared-file edit from\n * removing another connector's entries.\n */\n private isOurCommand(command: string | undefined, ctx: InstallContext): boolean {\n return isHomeBinHookCommand(command, ctx.homeBinPath, ctx.connector.id);\n }\n\n // ── Diagnostics ──────────────────────────────────────────────────────────\n\n override getHealthChecks(ctx: InstallContext): readonly HealthCheck[] {\n const settingsPath = this.getServerConfigPath(ctx);\n const connectorId = ctx.connector.id;\n return [\n {\n name: `${this.name}: settings.json present`,\n check: () =>\n existsSync(settingsPath)\n ? { status: \"OK\", detail: settingsPath }\n : { status: \"FAIL\", detail: `not found: ${settingsPath}` },\n },\n {\n name: `${this.name}: server entry registered`,\n check: () => {\n // Only assert what the connector declares: a server-less connector\n // never writes a server entry, so its absence is healthy.\n if (!ctx.connector.server) {\n return { status: \"OK\", detail: \"no MCP server declared\" };\n }\n const cfg = this.readJson<{ [k: string]: Record<string, unknown> }>(settingsPath);\n const bucket = cfg?.[MCP_ROOT_KEY];\n if (!cfg || !bucket) {\n return { status: \"FAIL\", detail: `no \"${MCP_ROOT_KEY}\" in ${settingsPath}` };\n }\n return connectorId in bucket\n ? { status: \"OK\", detail: `\"${MCP_ROOT_KEY}\".${connectorId} present` }\n : {\n status: \"FAIL\",\n detail: `no \"${MCP_ROOT_KEY}\".${connectorId} in ${settingsPath}`,\n };\n },\n },\n ];\n }\n}\n\n/**\n * Amp native interpolation token: `${env:VAR}` → `${VAR}`.\n *\n * When the portable ref carried a default (`${env:VAR:-fallback}`), Amp's native\n * `${VAR}` token has no way to express it — so a bare native token would silently\n * DROP the default. Instead, resolve the default at install time: emit the live\n * value when VAR is set and non-empty, else the literal fallback. The native\n * token is only emitted when there is no default to preserve.\n */\nfunction ampEnvToken(name: string, def?: string): string {\n if (def !== undefined) {\n const v = process.env[name];\n return v != null && v !== \"\" ? v : def;\n }\n return `\\${${name}}`;\n}\n\nexport const adapter = new AmpAdapter();\nexport default adapter;\n"],"mappings":"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","names":["entry"]}
|
|
@@ -3,15 +3,16 @@ import {
|
|
|
3
3
|
AntigravityAdapter,
|
|
4
4
|
adapter,
|
|
5
5
|
antigravity_default
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-KQSNDFBN.js";
|
|
7
7
|
import "./chunk-DBQMX75U.js";
|
|
8
|
-
import "./chunk-
|
|
8
|
+
import "./chunk-PD4VFEBY.js";
|
|
9
9
|
import "./chunk-7L6ITC4T.js";
|
|
10
10
|
import "./chunk-MEH7GC7R.js";
|
|
11
|
+
import "./chunk-N6HH7K6P.js";
|
|
11
12
|
import "./chunk-TRQKHP7D.js";
|
|
12
13
|
export {
|
|
13
14
|
AntigravityAdapter,
|
|
14
15
|
adapter,
|
|
15
16
|
antigravity_default as default
|
|
16
17
|
};
|
|
17
|
-
//# sourceMappingURL=antigravity-
|
|
18
|
+
//# sourceMappingURL=antigravity-7IBYFO6P.js.map
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
AntigravityAdapter
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-KQSNDFBN.js";
|
|
5
5
|
import "./chunk-DBQMX75U.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-PD4VFEBY.js";
|
|
7
7
|
import "./chunk-7L6ITC4T.js";
|
|
8
8
|
import "./chunk-MEH7GC7R.js";
|
|
9
|
+
import "./chunk-N6HH7K6P.js";
|
|
9
10
|
import "./chunk-TRQKHP7D.js";
|
|
10
11
|
|
|
11
12
|
// src/adapters/antigravity-cli/index.ts
|
|
@@ -83,4 +84,4 @@ export {
|
|
|
83
84
|
adapter,
|
|
84
85
|
antigravity_cli_default as default
|
|
85
86
|
};
|
|
86
|
-
//# sourceMappingURL=antigravity-cli-
|
|
87
|
+
//# sourceMappingURL=antigravity-cli-JEOUP2VN.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/adapters/antigravity-cli/index.ts"],"sourcesContent":["/**\n * adapters/antigravity-cli — Google Antigravity CLI (`agy`) platform adapter.\n *\n * Antigravity ships in two distinct runtimes that share the Gemini-family\n * `~/.gemini` tree and ALL of the same native formats (the JSON `mcp_config.json`\n * shape, the `hooks.json` lifecycle-hook shape, markdown Workflows, and Agent\n * Skills `SKILL.md`): the IDE/desktop app (the parent `AntigravityAdapter`,\n * id \"antigravity\") and this standalone CLI binary `agy` (id \"antigravity-cli\").\n *\n * This adapter is a thin fork of the IDE adapter. It REUSES every render / hook /\n * parse / surface path from {@link AntigravityAdapter} unchanged — the only\n * differences are identity and detection:\n *\n * - id = \"antigravity-cli\", name = \"Antigravity CLI\". Identity flows into the\n * universal hook command (`<homeBin> hook antigravity-cli <event> …`) and the\n * `hostPlatform` stamp, so a CLI-installed hook dispatches back to THIS\n * adapter at runtime — the reason the parent's install/parse logic was made\n * to read `this.id` rather than a fixed host constant.\n * - Detection probes the `agy` binary at ~/.local/bin/agy and/or the SHARED\n * ~/.gemini/antigravity/ presence (distinct runtime markers from the IDE).\n *\n * CONFIRMED-BY-INSTALL (2026-06-03, docs/research/antigravity-paths-confirmed.md):\n * the `agy` CLI v1.0.0 has NO separate config dir — `~/.gemini/antigravity-cli/`,\n * `~/.config/antigravity*`, and `~/.agy` are ALL ABSENT. `agy` SHARES the IDE's\n * `~/.gemini/antigravity/` tree (mcp_config.json, hooks.json, workflows, skills).\n * So the user-scope config resolution here is IDENTICAL to the IDE adapter (the\n * inherited USER_CONFIG_CANDIDATES) — installing both the IDE and CLI connectors\n * therefore writes the SAME files and is idempotent (observed as skip), which is\n * expected and correct. `agy`'s own extension surface is the `agy plugin` system\n * (install/uninstall/list/enable/disable) — future work would deploy as an agy\n * plugin; for now the MCP/workflows/skills surfaces ride the shared IDE files.\n *\n * LIVE-TEST RESOLUTION (2026-06-04, real `agy` v1.0.5 login + spawn): the prior\n * \"shares the IDE's ~/.gemini/antigravity/ file\" claim was WRONG for the CLI. The\n * standalone `agy` reads user MCP from **~/.gemini/config/mcp_config.json**\n * (root key `mcpServers`; project scope <proj>/.agents/mcp_config.json) — PROVEN:\n * a live `agy -p` session spawned an MCP server placed in `config/` and completed\n * a real initialize+tools/list+tools/call handshake; a negative control in\n * `antigravity/` was ignored. The binary carries \"failed to read mcp_config.json\"\n * + a literal \"/.gemini/config\" path; there is NO `--mcp-config` flag and `agy\n * mcp` is TUI-only. So this adapter now OVERRIDES userConfigCandidates to prefer\n * `~/.gemini/config/mcp_config.json` (canonical) over the inherited IDE\n * `antigravity/` default (kept as a prefer-existing fallback). Telemetry-wrap is\n * compatible: agy spawns command+args verbatim, so the home-bin serve wrapper is\n * spawned as written — i.e. a real agy session now emits a per-MCP telemetry row.\n * (agy also keeps its own home ~/.gemini/antigravity-cli/ for auth/state, but MCP\n * config lives in the shared ~/.gemini/config/.)\n *\n * Project scope is IDENTICAL to the IDE adapter (`<proj>/.agents/…`), as is every\n * hook/command/skill render and the runtime parse/format — all inherited.\n *\n * The user-scope config paths shared with the IDE (hooks.json + the global skills\n * dir) remain MEDIUM-CONFIDENCE / PATH-PROBED in the parent (not present on the\n * observed install) and surfaced by the doctor; user-scope mcp_config + workflows\n * are CONFIRMED at ~/.gemini/antigravity/.\n */\n\nimport { existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nimport { AntigravityAdapter } from \"../antigravity/index.js\";\nimport type { Adapter } from \"../spi.js\";\nimport type { DetectedPlatform, PlatformId } from \"../../core/types.js\";\n\nconst HOST: PlatformId = \"antigravity-cli\";\n\nexport class AntigravityCliAdapter extends AntigravityAdapter implements Adapter {\n override readonly id: PlatformId = HOST;\n override readonly name = \"Antigravity CLI\";\n\n // ── Detection ────────────────────────────────────────────────────────────\n\n /**\n * Probe the CLI's runtime markers, DISTINCT from the IDE app even though the\n * two share `~/.gemini/antigravity/`: the `agy` binary at ~/.local/bin/agy\n * (the definitive CLI marker, CONFIRMED present) and the shared\n * ~/.gemini/antigravity/ tree (or a user-scope mcp_config candidate / the\n * project config). Reported before the IDE adapter in the registry so the\n * more-specific CLI marker (the `agy` binary) wins host detection. Note: a\n * machine with ONLY the IDE (no `agy` binary) will not match here, while one\n * with the CLI matches on the binary regardless of the shared config dir.\n */\n override detectInstalled(projectDir: string): DetectedPlatform {\n const home = homedir();\n const userConfig = this.resolveUserConfigPath();\n const agyBin = join(home, \".local\", \"bin\", \"agy\");\n const sharedDir = join(home, \".gemini\", \"antigravity\");\n const projectConfig = join(projectDir, \".agents\", \"mcp_config.json\");\n\n // The `agy` binary is the definitive CLI marker. The shared antigravity dir\n // and the user-scope mcp_config candidates are secondary signals (they also\n // match a pure-IDE install, so the binary is what truly distinguishes the CLI).\n const cliMarker = existsSync(agyBin);\n const userInstalled =\n cliMarker ||\n existsSync(sharedDir) ||\n this.userConfigCandidates().some((parts) => existsSync(join(home, ...parts)));\n const projInstalled = existsSync(projectConfig);\n const installed = userInstalled || projInstalled;\n\n // Report the scope/path that actually matched, so a project-only install\n // isn't misreported as a (non-existent) user install.\n const scope = projInstalled && !userInstalled ? \"project\" : \"user\";\n const configPath = scope === \"project\" ? projectConfig : userConfig;\n\n return {\n id: this.id,\n name: this.name,\n installed,\n paradigm: this.paradigm,\n capabilities: this.capabilities,\n configPath,\n scope,\n reason: installed\n ? scope === \"project\"\n ? `found project Antigravity CLI config at ${projectConfig}`\n : `found Antigravity CLI (agy) at ${agyBin} / shared ${sharedDir}`\n : `no Antigravity CLI install at ${agyBin}, ${sharedDir}, or ${projectConfig}`,\n // The `agy` binary is a high-confidence CLI marker; matching only the shared\n // config dir is weaker (it could be a pure-IDE install).\n confidence: cliMarker ? \"high\" : installed ? \"medium\" : \"low\",\n };\n }\n\n /**\n * MCP user-config path — LIVE-PROVEN for `agy` v1.0.5 (2026-06-04). The\n * standalone CLI reads user MCP servers from `~/.gemini/config/mcp_config.json`\n * (root key `mcpServers`), NOT the IDE's `~/.gemini/antigravity/mcp_config.json`:\n * a real `agy -p` session spawned an MCP server ONLY when it was placed in\n * `config/` (negative control: a server in `antigravity/` was ignored), and the\n * binary carries the error string \"failed to read mcp_config.json\" + a literal\n * \"/.gemini/config\" path. `agy mcp` is TUI-only (no headless add) and there is\n * no `--mcp-config` flag. So the CLI must PREFER `config/` over the inherited\n * `antigravity/` default; the IDE path stays as a prefer-existing fallback.\n * (Skills/workflows/hooks dirs remain inherited; project scope\n * `<proj>/.agents/mcp_config.json` is already correct and live-proven.)\n */\n private static readonly CLI_USER_CONFIG_CANDIDATES = [\n [\".gemini\", \"config\", \"mcp_config.json\"], // CONFIRMED canonical for agy v1.0.5\n [\".gemini\", \"antigravity\", \"mcp_config.json\"], // legacy IDE layout (prefer-existing)\n ] as const;\n\n protected override userConfigCandidates(): ReadonlyArray<readonly string[]> {\n return AntigravityCliAdapter.CLI_USER_CONFIG_CANDIDATES;\n }\n}\n\nexport const adapter = new AntigravityCliAdapter();\nexport default adapter;\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/adapters/antigravity-cli/index.ts"],"sourcesContent":["/**\n * adapters/antigravity-cli — Google Antigravity CLI (`agy`) platform adapter.\n *\n * Antigravity ships in two distinct runtimes that share the Gemini-family\n * `~/.gemini` tree and ALL of the same native formats (the JSON `mcp_config.json`\n * shape, the `hooks.json` lifecycle-hook shape, markdown Workflows, and Agent\n * Skills `SKILL.md`): the IDE/desktop app (the parent `AntigravityAdapter`,\n * id \"antigravity\") and this standalone CLI binary `agy` (id \"antigravity-cli\").\n *\n * This adapter is a thin fork of the IDE adapter. It REUSES every render / hook /\n * parse / surface path from {@link AntigravityAdapter} unchanged — the only\n * differences are identity and detection:\n *\n * - id = \"antigravity-cli\", name = \"Antigravity CLI\". Identity flows into the\n * universal hook command (`<homeBin> hook antigravity-cli <event> …`) and the\n * `hostPlatform` stamp, so a CLI-installed hook dispatches back to THIS\n * adapter at runtime — the reason the parent's install/parse logic was made\n * to read `this.id` rather than a fixed host constant.\n * - Detection probes the `agy` binary at ~/.local/bin/agy and/or the SHARED\n * ~/.gemini/antigravity/ presence (distinct runtime markers from the IDE).\n *\n * CONFIRMED-BY-INSTALL (2026-06-03, docs/research/antigravity-paths-confirmed.md):\n * the `agy` CLI v1.0.0 has NO separate config dir — `~/.gemini/antigravity-cli/`,\n * `~/.config/antigravity*`, and `~/.agy` are ALL ABSENT. `agy` SHARES the IDE's\n * `~/.gemini/antigravity/` tree (mcp_config.json, hooks.json, workflows, skills).\n * So the user-scope config resolution here is IDENTICAL to the IDE adapter (the\n * inherited USER_CONFIG_CANDIDATES) — installing both the IDE and CLI connectors\n * therefore writes the SAME files and is idempotent (observed as skip), which is\n * expected and correct. `agy`'s own extension surface is the `agy plugin` system\n * (install/uninstall/list/enable/disable) — future work would deploy as an agy\n * plugin; for now the MCP/workflows/skills surfaces ride the shared IDE files.\n *\n * LIVE-TEST RESOLUTION (2026-06-04, real `agy` v1.0.5 login + spawn): the prior\n * \"shares the IDE's ~/.gemini/antigravity/ file\" claim was WRONG for the CLI. The\n * standalone `agy` reads user MCP from **~/.gemini/config/mcp_config.json**\n * (root key `mcpServers`; project scope <proj>/.agents/mcp_config.json) — PROVEN:\n * a live `agy -p` session spawned an MCP server placed in `config/` and completed\n * a real initialize+tools/list+tools/call handshake; a negative control in\n * `antigravity/` was ignored. The binary carries \"failed to read mcp_config.json\"\n * + a literal \"/.gemini/config\" path; there is NO `--mcp-config` flag and `agy\n * mcp` is TUI-only. So this adapter now OVERRIDES userConfigCandidates to prefer\n * `~/.gemini/config/mcp_config.json` (canonical) over the inherited IDE\n * `antigravity/` default (kept as a prefer-existing fallback). Telemetry-wrap is\n * compatible: agy spawns command+args verbatim, so the home-bin serve wrapper is\n * spawned as written — i.e. a real agy session now emits a per-MCP telemetry row.\n * (agy also keeps its own home ~/.gemini/antigravity-cli/ for auth/state, but MCP\n * config lives in the shared ~/.gemini/config/.)\n *\n * Project scope is IDENTICAL to the IDE adapter (`<proj>/.agents/…`), as is every\n * hook/command/skill render and the runtime parse/format — all inherited.\n *\n * The user-scope config paths shared with the IDE (hooks.json + the global skills\n * dir) remain MEDIUM-CONFIDENCE / PATH-PROBED in the parent (not present on the\n * observed install) and surfaced by the doctor; user-scope mcp_config + workflows\n * are CONFIRMED at ~/.gemini/antigravity/.\n */\n\nimport { existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nimport { AntigravityAdapter } from \"../antigravity/index.js\";\nimport type { Adapter } from \"../spi.js\";\nimport type { DetectedPlatform, PlatformId } from \"../../core/types.js\";\n\nconst HOST: PlatformId = \"antigravity-cli\";\n\nexport class AntigravityCliAdapter extends AntigravityAdapter implements Adapter {\n override readonly id: PlatformId = HOST;\n override readonly name = \"Antigravity CLI\";\n\n // ── Detection ────────────────────────────────────────────────────────────\n\n /**\n * Probe the CLI's runtime markers, DISTINCT from the IDE app even though the\n * two share `~/.gemini/antigravity/`: the `agy` binary at ~/.local/bin/agy\n * (the definitive CLI marker, CONFIRMED present) and the shared\n * ~/.gemini/antigravity/ tree (or a user-scope mcp_config candidate / the\n * project config). Reported before the IDE adapter in the registry so the\n * more-specific CLI marker (the `agy` binary) wins host detection. Note: a\n * machine with ONLY the IDE (no `agy` binary) will not match here, while one\n * with the CLI matches on the binary regardless of the shared config dir.\n */\n override detectInstalled(projectDir: string): DetectedPlatform {\n const home = homedir();\n const userConfig = this.resolveUserConfigPath();\n const agyBin = join(home, \".local\", \"bin\", \"agy\");\n const sharedDir = join(home, \".gemini\", \"antigravity\");\n const projectConfig = join(projectDir, \".agents\", \"mcp_config.json\");\n\n // The `agy` binary is the definitive CLI marker. The shared antigravity dir\n // and the user-scope mcp_config candidates are secondary signals (they also\n // match a pure-IDE install, so the binary is what truly distinguishes the CLI).\n const cliMarker = existsSync(agyBin);\n const userInstalled =\n cliMarker ||\n existsSync(sharedDir) ||\n this.userConfigCandidates().some((parts) => existsSync(join(home, ...parts)));\n const projInstalled = existsSync(projectConfig);\n const installed = userInstalled || projInstalled;\n\n // Report the scope/path that actually matched, so a project-only install\n // isn't misreported as a (non-existent) user install.\n const scope = projInstalled && !userInstalled ? \"project\" : \"user\";\n const configPath = scope === \"project\" ? projectConfig : userConfig;\n\n return {\n id: this.id,\n name: this.name,\n installed,\n paradigm: this.paradigm,\n capabilities: this.capabilities,\n configPath,\n scope,\n reason: installed\n ? scope === \"project\"\n ? `found project Antigravity CLI config at ${projectConfig}`\n : `found Antigravity CLI (agy) at ${agyBin} / shared ${sharedDir}`\n : `no Antigravity CLI install at ${agyBin}, ${sharedDir}, or ${projectConfig}`,\n // The `agy` binary is a high-confidence CLI marker; matching only the shared\n // config dir is weaker (it could be a pure-IDE install).\n confidence: cliMarker ? \"high\" : installed ? \"medium\" : \"low\",\n };\n }\n\n /**\n * MCP user-config path — LIVE-PROVEN for `agy` v1.0.5 (2026-06-04). The\n * standalone CLI reads user MCP servers from `~/.gemini/config/mcp_config.json`\n * (root key `mcpServers`), NOT the IDE's `~/.gemini/antigravity/mcp_config.json`:\n * a real `agy -p` session spawned an MCP server ONLY when it was placed in\n * `config/` (negative control: a server in `antigravity/` was ignored), and the\n * binary carries the error string \"failed to read mcp_config.json\" + a literal\n * \"/.gemini/config\" path. `agy mcp` is TUI-only (no headless add) and there is\n * no `--mcp-config` flag. So the CLI must PREFER `config/` over the inherited\n * `antigravity/` default; the IDE path stays as a prefer-existing fallback.\n * (Skills/workflows/hooks dirs remain inherited; project scope\n * `<proj>/.agents/mcp_config.json` is already correct and live-proven.)\n */\n private static readonly CLI_USER_CONFIG_CANDIDATES = [\n [\".gemini\", \"config\", \"mcp_config.json\"], // CONFIRMED canonical for agy v1.0.5\n [\".gemini\", \"antigravity\", \"mcp_config.json\"], // legacy IDE layout (prefer-existing)\n ] as const;\n\n protected override userConfigCandidates(): ReadonlyArray<readonly string[]> {\n return AntigravityCliAdapter.CLI_USER_CONFIG_CANDIDATES;\n }\n}\n\nexport const adapter = new AntigravityCliAdapter();\nexport default adapter;\n"],"mappings":";;;;;;;;;;;;AAyDA,SAAS,kBAAkB;AAC3B,SAAS,eAAe;AACxB,SAAS,YAAY;AAMrB,IAAM,OAAmB;AAElB,IAAM,wBAAN,MAAM,+BAA8B,mBAAsC;AAAA,EAC7D,KAAiB;AAAA,EACjB,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAchB,gBAAgB,YAAsC;AAC7D,UAAM,OAAO,QAAQ;AACrB,UAAM,aAAa,KAAK,sBAAsB;AAC9C,UAAM,SAAS,KAAK,MAAM,UAAU,OAAO,KAAK;AAChD,UAAM,YAAY,KAAK,MAAM,WAAW,aAAa;AACrD,UAAM,gBAAgB,KAAK,YAAY,WAAW,iBAAiB;AAKnE,UAAM,YAAY,WAAW,MAAM;AACnC,UAAM,gBACJ,aACA,WAAW,SAAS,KACpB,KAAK,qBAAqB,EAAE,KAAK,CAAC,UAAU,WAAW,KAAK,MAAM,GAAG,KAAK,CAAC,CAAC;AAC9E,UAAM,gBAAgB,WAAW,aAAa;AAC9C,UAAM,YAAY,iBAAiB;AAInC,UAAM,QAAQ,iBAAiB,CAAC,gBAAgB,YAAY;AAC5D,UAAM,aAAa,UAAU,YAAY,gBAAgB;AAEzD,WAAO;AAAA,MACL,IAAI,KAAK;AAAA,MACT,MAAM,KAAK;AAAA,MACX;AAAA,MACA,UAAU,KAAK;AAAA,MACf,cAAc,KAAK;AAAA,MACnB;AAAA,MACA;AAAA,MACA,QAAQ,YACJ,UAAU,YACR,2CAA2C,aAAa,KACxD,kCAAkC,MAAM,aAAa,SAAS,KAChE,iCAAiC,MAAM,KAAK,SAAS,QAAQ,aAAa;AAAA;AAAA;AAAA,MAG9E,YAAY,YAAY,SAAS,YAAY,WAAW;AAAA,IAC1D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,OAAwB,6BAA6B;AAAA,IACnD,CAAC,WAAW,UAAU,iBAAiB;AAAA;AAAA,IACvC,CAAC,WAAW,eAAe,iBAAiB;AAAA;AAAA,EAC9C;AAAA,EAEmB,uBAAyD;AAC1E,WAAO,uBAAsB;AAAA,EAC/B;AACF;AAEO,IAAM,UAAU,IAAI,sBAAsB;AACjD,IAAO,0BAAQ;","names":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
defineConnector
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-GSGDG2BV.js";
|
|
5
5
|
import {
|
|
6
6
|
connectorDir,
|
|
7
7
|
connectorsDir,
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
} from "./chunk-TRQKHP7D.js";
|
|
10
10
|
|
|
11
11
|
// src/core/load-connector.ts
|
|
12
|
-
import { existsSync, readdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
12
|
+
import { existsSync, readdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "fs";
|
|
13
13
|
import { dirname, isAbsolute, join, resolve } from "path";
|
|
14
14
|
import { pathToFileURL } from "url";
|
|
15
15
|
var CONFIG_FILENAMES = [
|
|
@@ -34,7 +34,11 @@ function isResolvedConnector(value) {
|
|
|
34
34
|
return typeof value === "object" && value !== null && Array.isArray(value.hookEvents);
|
|
35
35
|
}
|
|
36
36
|
async function loadConnectorFromPath(path) {
|
|
37
|
-
|
|
37
|
+
let modulePath = isAbsolute(path) ? path : resolve(path);
|
|
38
|
+
try {
|
|
39
|
+
modulePath = realpathSync.native(modulePath);
|
|
40
|
+
} catch {
|
|
41
|
+
}
|
|
38
42
|
if (modulePath.endsWith(".json")) {
|
|
39
43
|
const text = readFileSync(modulePath, "utf8");
|
|
40
44
|
const config = JSON.parse(text);
|
|
@@ -55,9 +59,16 @@ function serializablePlatforms(platforms) {
|
|
|
55
59
|
const out = {};
|
|
56
60
|
for (const [id, override] of Object.entries(platforms)) {
|
|
57
61
|
if (!override) continue;
|
|
58
|
-
const { hooks, ...rest } = override;
|
|
62
|
+
const { hooks, nativeHooks, ...rest } = override;
|
|
59
63
|
const clean = { ...rest };
|
|
60
64
|
if (typeof hooks === "boolean") clean.hooks = hooks;
|
|
65
|
+
if (nativeHooks && typeof nativeHooks === "object") {
|
|
66
|
+
const stripped = {};
|
|
67
|
+
for (const [event, def] of Object.entries(nativeHooks)) {
|
|
68
|
+
stripped[event] = typeof def?.matcher === "string" ? { matcher: def.matcher } : {};
|
|
69
|
+
}
|
|
70
|
+
clean.nativeHooks = stripped;
|
|
71
|
+
}
|
|
61
72
|
out[id] = clean;
|
|
62
73
|
}
|
|
63
74
|
return out;
|
|
@@ -75,6 +86,7 @@ function registerConnector(connector, modulePath) {
|
|
|
75
86
|
commands: connector.commands,
|
|
76
87
|
skills: connector.skills,
|
|
77
88
|
subagents: connector.subagents,
|
|
89
|
+
memory: connector.memory,
|
|
78
90
|
targets: connector.targets,
|
|
79
91
|
platforms: serializablePlatforms(connector.platforms)
|
|
80
92
|
};
|
|
@@ -127,6 +139,7 @@ function connectorFromMeta(meta) {
|
|
|
127
139
|
commands: meta.commands ?? [],
|
|
128
140
|
skills: meta.skills ?? [],
|
|
129
141
|
subagents: meta.subagents ?? [],
|
|
142
|
+
memory: meta.memory ?? [],
|
|
130
143
|
platforms: meta.platforms ?? {},
|
|
131
144
|
targets: meta.targets
|
|
132
145
|
};
|
|
@@ -152,4 +165,4 @@ export {
|
|
|
152
165
|
connectorFromMeta,
|
|
153
166
|
listRegisteredConnectors
|
|
154
167
|
};
|
|
155
|
-
//# sourceMappingURL=chunk-
|
|
168
|
+
//# sourceMappingURL=chunk-3JCRUROU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/load-connector.ts"],"sourcesContent":["/**\n * core/load-connector — discover, load, and register connector definitions.\n *\n * Responsibilities:\n * 1. Locate the developer's `agent-connector.config.{mjs,js,json}` by walking\n * upward from a start directory (the project root convention).\n * 2. Load a config from a path into a LIVE ResolvedConnector (with handlers).\n * 3. Register a connector by persisting SERIALIZABLE metadata (no functions)\n * to `connectorDir(id)/connector.json`, pointing at the source module so\n * live handlers can be re-imported at runtime from a stable record.\n *\n * Why split live vs. serialized? Hook handlers are functions and cannot survive\n * JSON. The registry stores only what JSON can hold plus an absolute\n * `modulePath`; the runtime re-imports that module to recover the live handlers.\n */\n\nimport { existsSync, readdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from \"node:fs\";\nimport { dirname, isAbsolute, join, resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\n\nimport type {\n CommandDef,\n HookEventName,\n MemoryDef,\n PlatformId,\n PlatformOverride,\n ResolvedConnector,\n ServerDef,\n SkillDef,\n SubagentDef,\n} from \"./types.js\";\nimport { defineConnector } from \"./define-connector.js\";\nimport { connectorDir, connectorsDir, ensureDir } from \"./paths.js\";\n\n/** Candidate config filenames, in resolution-precedence order. */\nconst CONFIG_FILENAMES = [\n \"agent-connector.config.mjs\",\n \"agent-connector.config.js\",\n \"agent-connector.config.json\",\n] as const;\n\n/**\n * The serializable subset of a connector persisted to `connector.json`.\n * Hook handler functions are intentionally absent — only the absolute\n * `modulePath` is stored so the live ResolvedConnector can be re-imported.\n */\nexport interface RegisteredMeta {\n id: string;\n displayName: string;\n version: string;\n /** Absolute path to the source module that produced this connector. */\n modulePath: string;\n telemetry: ResolvedConnector[\"telemetry\"];\n /** Canonical event names the connector handles (handlers themselves stripped). */\n hookEvents: HookEventName[];\n hasServer: boolean;\n server: ServerDef | null;\n /**\n * Content surfaces (commands/skills/subagents). These are JSON-serializable\n * (no functions) so they persist cleanly and let uninstall locate the files\n * to remove even when the source module is gone.\n */\n commands: CommandDef[];\n skills: SkillDef[];\n subagents: SubagentDef[];\n /**\n * Memory entries (managed-block content surface). Plain content, persisted\n * whole so uninstall normally has the full entry list even when the source\n * module is gone (the marker prefix-scan is the fallback beyond that).\n */\n memory: MemoryDef[];\n targets: ResolvedConnector[\"targets\"];\n /** Per-platform overrides with any hook handler functions stripped. */\n platforms: Partial<Record<PlatformId, PlatformOverride>>;\n}\n\n/**\n * Search `startDir` and each ancestor for a connector config file. Returns the\n * absolute path to the first match (filesystem-root-stopping), or null.\n */\nexport function findConnectorConfig(startDir: string): string | null {\n let dir = resolve(startDir);\n // Walk upward until `dirname(dir) === dir` (filesystem root).\n for (;;) {\n for (const name of CONFIG_FILENAMES) {\n const candidate = join(dir, name);\n if (existsSync(candidate)) return candidate;\n }\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return null;\n}\n\n/** True if a value already looks like a ResolvedConnector (vs. a raw config). */\nfunction isResolvedConnector(value: unknown): value is ResolvedConnector {\n return (\n typeof value === \"object\" &&\n value !== null &&\n Array.isArray((value as { hookEvents?: unknown }).hookEvents)\n );\n}\n\n/**\n * Load a connector from an absolute or relative path.\n * - `.json` → JSON.parse, then defineConnector().\n * - `.mjs` / `.js` → dynamic import (via pathToFileURL, Windows-safe),\n * take the default export. If it already looks like a\n * ResolvedConnector use it as-is; otherwise treat it as\n * a ConnectorConfig and run it through defineConnector().\n * Returns the live connector plus the absolute module path it came from.\n */\nexport async function loadConnectorFromPath(\n path: string,\n): Promise<{ connector: ResolvedConnector; modulePath: string }> {\n let modulePath = isAbsolute(path) ? path : resolve(path);\n // Canonicalize before building the file:// URL for dynamic import. On Windows a\n // short (8.3) temp path like C:\\Users\\RUNNER~1\\...\\x.mjs round-trips the \"~\" as\n // %7E and fails to load; realpathSync.native expands it to the real long path.\n // It also resolves symlinked temp roots (e.g. macOS /var -> /private/var).\n // If the file does not exist yet, fall through with the resolve()'d path so the\n // subsequent read/import throws the real not-found error.\n try {\n modulePath = realpathSync.native(modulePath);\n } catch {\n /* keep the resolve()'d path */\n }\n\n if (modulePath.endsWith(\".json\")) {\n const text = readFileSync(modulePath, \"utf8\");\n const config = JSON.parse(text) as unknown;\n const connector = defineConnector(config as Parameters<typeof defineConnector>[0]);\n return { connector, modulePath };\n }\n\n // .mjs / .js — dynamic ESM import. pathToFileURL keeps Windows paths valid.\n const mod = (await import(pathToFileURL(modulePath).href)) as {\n default?: unknown;\n };\n const exported = mod.default;\n if (exported == null) {\n throw new Error(\n `Connector module has no default export: ${modulePath}`,\n );\n }\n const connector = isResolvedConnector(exported)\n ? exported\n : defineConnector(exported as Parameters<typeof defineConnector>[0]);\n return { connector, modulePath };\n}\n\n/**\n * Strip any function-valued `hooks` from per-platform overrides so the result\n * is JSON-serializable. A boolean `hooks` override is preserved; an object\n * override is dropped (it can only carry handler functions, which are not\n * serializable). `nativeHooks` declarations are persisted HANDLER-LESS (event\n * name + matcher only) so meta-based inspection (doctor / health checks / the\n * hook CLI) can still see WHICH native events were declared — the live handlers\n * are re-imported from `modulePath` at runtime, exactly like normalized hooks.\n * Everything else on the override is kept verbatim — notably `configPatch`\n * declarations, which are pure JSON (key/value/reason/docsUrl, no functions)\n * and persist WHOLE so uninstall/doctor can reason about them even when the\n * source module is gone.\n */\nfunction serializablePlatforms(\n platforms: Partial<Record<PlatformId, PlatformOverride>>,\n): Partial<Record<PlatformId, PlatformOverride>> {\n const out: Partial<Record<PlatformId, PlatformOverride>> = {};\n for (const [id, override] of Object.entries(platforms) as [\n PlatformId,\n PlatformOverride | undefined,\n ][]) {\n if (!override) continue;\n const { hooks, nativeHooks, ...rest } = override;\n const clean: PlatformOverride = { ...rest };\n // Keep only a boolean hooks flag; object hooks carry handler functions.\n if (typeof hooks === \"boolean\") clean.hooks = hooks;\n if (nativeHooks && typeof nativeHooks === \"object\") {\n const stripped: Record<string, { matcher?: string }> = {};\n for (const [event, def] of Object.entries(nativeHooks)) {\n stripped[event] =\n typeof def?.matcher === \"string\" ? { matcher: def.matcher } : {};\n }\n // Cast: the persisted record deliberately omits the (non-serializable)\n // handler; consumers of meta-derived connectors never dispatch handlers.\n clean.nativeHooks = stripped as PlatformOverride[\"nativeHooks\"];\n }\n out[id] = clean;\n }\n return out;\n}\n\n/**\n * Persist a connector's serializable metadata to `connectorDir(id)/connector.json`.\n * Handlers are NOT serialized — they are re-imported from `modulePath` at runtime.\n * Returns the absolute path of the written record.\n */\nexport function registerConnector(\n connector: ResolvedConnector,\n modulePath: string,\n): string {\n const meta: RegisteredMeta = {\n id: connector.id,\n displayName: connector.displayName,\n version: connector.version,\n modulePath: resolve(modulePath),\n telemetry: connector.telemetry,\n hookEvents: connector.hookEvents,\n hasServer: !!connector.server,\n server: connector.server ?? null,\n commands: connector.commands,\n skills: connector.skills,\n subagents: connector.subagents,\n memory: connector.memory,\n targets: connector.targets,\n platforms: serializablePlatforms(connector.platforms),\n };\n\n const dir = connectorDir(connector.id);\n ensureDir(dir);\n const outPath = join(dir, \"connector.json\");\n writeFileSync(outPath, `${JSON.stringify(meta, null, 2)}\\n`, \"utf8\");\n return outPath;\n}\n\n/**\n * Deregister a connector by removing its DATA-dir record directory\n * (`connectorDir(id)`) recursively. This is the inverse of {@link registerConnector}:\n * registerConnector writes `connectorDir(id)/connector.json`, but per-target\n * uninstall only strips host-native registrations — it never removes this record,\n * leaving an orphan behind. Best-effort + guarded: returns the directory path and\n * whether it existed before removal.\n */\nexport function deregisterConnector(id: string): {\n removed: boolean;\n path: string;\n} {\n const dir = connectorDir(id);\n const existed = existsSync(dir);\n if (existed) rmSync(dir, { recursive: true, force: true });\n return { removed: existed, path: dir };\n}\n\n/**\n * Read the registered metadata record for `id`, or null if not registered or\n * the record is unreadable/corrupt.\n */\nexport function readRegisteredMeta(id: string): RegisteredMeta | null {\n const recordPath = join(connectorDir(id), \"connector.json\");\n if (!existsSync(recordPath)) return null;\n try {\n return JSON.parse(readFileSync(recordPath, \"utf8\")) as RegisteredMeta;\n } catch {\n return null;\n }\n}\n\n/**\n * Load the LIVE ResolvedConnector (with hook handlers) for a registered id by\n * re-importing its source module. Throws a clear error when the connector is\n * not registered or its source module no longer resolves to it.\n */\nexport async function loadRegisteredConnector(\n id: string,\n): Promise<ResolvedConnector> {\n const meta = readRegisteredMeta(id);\n if (!meta) {\n throw new Error(\n `Connector \"${id}\" is not registered (no record at ${join(connectorDir(id), \"connector.json\")}). ` +\n `Run an install/register step first.`,\n );\n }\n if (!meta.modulePath || !existsSync(meta.modulePath)) {\n throw new Error(\n `Connector \"${id}\" record points at a missing module: ${meta.modulePath}`,\n );\n }\n const { connector } = await loadConnectorFromPath(meta.modulePath);\n return connector;\n}\n\n/**\n * Build a handler-less ResolvedConnector from stored metadata. Suitable for\n * inspection/doctor where live hook handlers are not needed (and where the\n * source module may even be gone). `hooks` is empty; `hookEvents` is preserved.\n */\nexport function connectorFromMeta(meta: RegisteredMeta): ResolvedConnector {\n return {\n id: meta.id,\n displayName: meta.displayName,\n version: meta.version,\n ...(meta.server ? { server: meta.server } : {}),\n hooks: {},\n hookEvents: meta.hookEvents ?? [],\n telemetry: meta.telemetry,\n commands: meta.commands ?? [],\n skills: meta.skills ?? [],\n subagents: meta.subagents ?? [],\n memory: meta.memory ?? [],\n platforms: meta.platforms ?? {},\n targets: meta.targets,\n };\n}\n\n/**\n * List every registered connector as a handler-less ResolvedConnector (read\n * from `connectorsDir()`). Used by doctor to health-check what is actually\n * installed, rather than guessing from the local working directory.\n */\nexport function listRegisteredConnectors(): ResolvedConnector[] {\n const dir = connectorsDir();\n if (!existsSync(dir)) return [];\n const out: ResolvedConnector[] = [];\n for (const entry of readdirSync(dir)) {\n const meta = readRegisteredMeta(entry);\n if (meta) out.push(connectorFromMeta(meta));\n }\n return out;\n}\n"],"mappings":";;;;;;;;;;;AAgBA,SAAS,YAAY,aAAa,cAAc,cAAc,QAAQ,qBAAqB;AAC3F,SAAS,SAAS,YAAY,MAAM,eAAe;AACnD,SAAS,qBAAqB;AAiB9B,IAAM,mBAAmB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AACF;AAyCO,SAAS,oBAAoB,UAAiC;AACnE,MAAI,MAAM,QAAQ,QAAQ;AAE1B,aAAS;AACP,eAAW,QAAQ,kBAAkB;AACnC,YAAM,YAAY,KAAK,KAAK,IAAI;AAChC,UAAI,WAAW,SAAS,EAAG,QAAO;AAAA,IACpC;AACA,UAAM,SAAS,QAAQ,GAAG;AAC1B,QAAI,WAAW,IAAK;AACpB,UAAM;AAAA,EACR;AACA,SAAO;AACT;AAGA,SAAS,oBAAoB,OAA4C;AACvE,SACE,OAAO,UAAU,YACjB,UAAU,QACV,MAAM,QAAS,MAAmC,UAAU;AAEhE;AAWA,eAAsB,sBACpB,MAC+D;AAC/D,MAAI,aAAa,WAAW,IAAI,IAAI,OAAO,QAAQ,IAAI;AAOvD,MAAI;AACF,iBAAa,aAAa,OAAO,UAAU;AAAA,EAC7C,QAAQ;AAAA,EAER;AAEA,MAAI,WAAW,SAAS,OAAO,GAAG;AAChC,UAAM,OAAO,aAAa,YAAY,MAAM;AAC5C,UAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,UAAMA,aAAY,gBAAgB,MAA+C;AACjF,WAAO,EAAE,WAAAA,YAAW,WAAW;AAAA,EACjC;AAGA,QAAM,MAAO,MAAM,OAAO,cAAc,UAAU,EAAE;AAGpD,QAAM,WAAW,IAAI;AACrB,MAAI,YAAY,MAAM;AACpB,UAAM,IAAI;AAAA,MACR,2CAA2C,UAAU;AAAA,IACvD;AAAA,EACF;AACA,QAAM,YAAY,oBAAoB,QAAQ,IAC1C,WACA,gBAAgB,QAAiD;AACrE,SAAO,EAAE,WAAW,WAAW;AACjC;AAeA,SAAS,sBACP,WAC+C;AAC/C,QAAM,MAAqD,CAAC;AAC5D,aAAW,CAAC,IAAI,QAAQ,KAAK,OAAO,QAAQ,SAAS,GAGhD;AACH,QAAI,CAAC,SAAU;AACf,UAAM,EAAE,OAAO,aAAa,GAAG,KAAK,IAAI;AACxC,UAAM,QAA0B,EAAE,GAAG,KAAK;AAE1C,QAAI,OAAO,UAAU,UAAW,OAAM,QAAQ;AAC9C,QAAI,eAAe,OAAO,gBAAgB,UAAU;AAClD,YAAM,WAAiD,CAAC;AACxD,iBAAW,CAAC,OAAO,GAAG,KAAK,OAAO,QAAQ,WAAW,GAAG;AACtD,iBAAS,KAAK,IACZ,OAAO,KAAK,YAAY,WAAW,EAAE,SAAS,IAAI,QAAQ,IAAI,CAAC;AAAA,MACnE;AAGA,YAAM,cAAc;AAAA,IACtB;AACA,QAAI,EAAE,IAAI;AAAA,EACZ;AACA,SAAO;AACT;AAOO,SAAS,kBACd,WACA,YACQ;AACR,QAAM,OAAuB;AAAA,IAC3B,IAAI,UAAU;AAAA,IACd,aAAa,UAAU;AAAA,IACvB,SAAS,UAAU;AAAA,IACnB,YAAY,QAAQ,UAAU;AAAA,IAC9B,WAAW,UAAU;AAAA,IACrB,YAAY,UAAU;AAAA,IACtB,WAAW,CAAC,CAAC,UAAU;AAAA,IACvB,QAAQ,UAAU,UAAU;AAAA,IAC5B,UAAU,UAAU;AAAA,IACpB,QAAQ,UAAU;AAAA,IAClB,WAAW,UAAU;AAAA,IACrB,QAAQ,UAAU;AAAA,IAClB,SAAS,UAAU;AAAA,IACnB,WAAW,sBAAsB,UAAU,SAAS;AAAA,EACtD;AAEA,QAAM,MAAM,aAAa,UAAU,EAAE;AACrC,YAAU,GAAG;AACb,QAAM,UAAU,KAAK,KAAK,gBAAgB;AAC1C,gBAAc,SAAS,GAAG,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AAAA,GAAM,MAAM;AACnE,SAAO;AACT;AAUO,SAAS,oBAAoB,IAGlC;AACA,QAAM,MAAM,aAAa,EAAE;AAC3B,QAAM,UAAU,WAAW,GAAG;AAC9B,MAAI,QAAS,QAAO,KAAK,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AACzD,SAAO,EAAE,SAAS,SAAS,MAAM,IAAI;AACvC;AAMO,SAAS,mBAAmB,IAAmC;AACpE,QAAM,aAAa,KAAK,aAAa,EAAE,GAAG,gBAAgB;AAC1D,MAAI,CAAC,WAAW,UAAU,EAAG,QAAO;AACpC,MAAI;AACF,WAAO,KAAK,MAAM,aAAa,YAAY,MAAM,CAAC;AAAA,EACpD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAOA,eAAsB,wBACpB,IAC4B;AAC5B,QAAM,OAAO,mBAAmB,EAAE;AAClC,MAAI,CAAC,MAAM;AACT,UAAM,IAAI;AAAA,MACR,cAAc,EAAE,qCAAqC,KAAK,aAAa,EAAE,GAAG,gBAAgB,CAAC;AAAA,IAE/F;AAAA,EACF;AACA,MAAI,CAAC,KAAK,cAAc,CAAC,WAAW,KAAK,UAAU,GAAG;AACpD,UAAM,IAAI;AAAA,MACR,cAAc,EAAE,wCAAwC,KAAK,UAAU;AAAA,IACzE;AAAA,EACF;AACA,QAAM,EAAE,UAAU,IAAI,MAAM,sBAAsB,KAAK,UAAU;AACjE,SAAO;AACT;AAOO,SAAS,kBAAkB,MAAyC;AACzE,SAAO;AAAA,IACL,IAAI,KAAK;AAAA,IACT,aAAa,KAAK;AAAA,IAClB,SAAS,KAAK;AAAA,IACd,GAAI,KAAK,SAAS,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;AAAA,IAC7C,OAAO,CAAC;AAAA,IACR,YAAY,KAAK,cAAc,CAAC;AAAA,IAChC,WAAW,KAAK;AAAA,IAChB,UAAU,KAAK,YAAY,CAAC;AAAA,IAC5B,QAAQ,KAAK,UAAU,CAAC;AAAA,IACxB,WAAW,KAAK,aAAa,CAAC;AAAA,IAC9B,QAAQ,KAAK,UAAU,CAAC;AAAA,IACxB,WAAW,KAAK,aAAa,CAAC;AAAA,IAC9B,SAAS,KAAK;AAAA,EAChB;AACF;AAOO,SAAS,2BAAgD;AAC9D,QAAM,MAAM,cAAc;AAC1B,MAAI,CAAC,WAAW,GAAG,EAAG,QAAO,CAAC;AAC9B,QAAM,MAA2B,CAAC;AAClC,aAAW,SAAS,YAAY,GAAG,GAAG;AACpC,UAAM,OAAO,mBAAmB,KAAK;AACrC,QAAI,KAAM,KAAI,KAAK,kBAAkB,IAAI,CAAC;AAAA,EAC5C;AACA,SAAO;AACT;","names":["connector"]}
|