@ken-jo/agent-connector 0.2.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.
Files changed (84) hide show
  1. package/README.md +9 -2
  2. package/dist/chunk-44HUXXC6.js +284 -0
  3. package/dist/chunk-44HUXXC6.js.map +1 -0
  4. package/dist/{chunk-AKJGNTU3.js → chunk-DS3LUFVN.js} +21 -21
  5. package/dist/chunk-DS3LUFVN.js.map +1 -0
  6. package/dist/{chunk-KHO5WNTP.js → chunk-KVAAFYRR.js} +18 -2
  7. package/dist/chunk-KVAAFYRR.js.map +1 -0
  8. package/dist/{chunk-O2SBA5KR.js → chunk-NJU275US.js} +9 -9
  9. package/dist/{chunk-WCETQWWK.js → chunk-OP4KBXUO.js} +1 -17
  10. package/dist/chunk-OP4KBXUO.js.map +1 -0
  11. package/dist/chunk-QADSG2JY.js +1989 -0
  12. package/dist/chunk-QADSG2JY.js.map +1 -0
  13. package/dist/{package-O63K4S7T.js → chunk-WNREHWKL.js} +20 -114
  14. package/dist/chunk-WNREHWKL.js.map +1 -0
  15. package/dist/{chunk-4ML7SZ7R.js → chunk-ZAM4M7LI.js} +7 -7
  16. package/dist/cli/sdk.js +1 -1
  17. package/dist/cli.js +1 -1
  18. package/dist/{detect-JK7T46N6.js → detect-MNDHGLSV.js} +3 -3
  19. package/dist/{doctor-62BHIXOR.js → doctor-N5KZDD55.js} +35 -5
  20. package/dist/doctor-N5KZDD55.js.map +1 -0
  21. package/dist/{hook-AC7APC66.js → hook-7LUOKIXH.js} +6 -6
  22. package/dist/{install-DMW2C7SV.js → install-TQTT4F3I.js} +31 -7
  23. package/dist/install-TQTT4F3I.js.map +1 -0
  24. package/dist/kilo-PKAJCWPX.js +852 -0
  25. package/dist/kilo-PKAJCWPX.js.map +1 -0
  26. package/dist/{kilo-cli-2RNODHWY.js → kilo-cli-QWUVXG2O.js} +157 -6
  27. package/dist/kilo-cli-QWUVXG2O.js.map +1 -0
  28. package/dist/{kimi-Q5RG7CYT.js → kimi-EVD7YKXS.js} +79 -4
  29. package/dist/kimi-EVD7YKXS.js.map +1 -0
  30. package/dist/{kiro-EKTRW424.js → kiro-UQRY6EIO.js} +68 -3
  31. package/dist/kiro-UQRY6EIO.js.map +1 -0
  32. package/dist/{leaderboard-R5HRJHNU.js → leaderboard-MUJ7DURM.js} +2 -2
  33. package/dist/package-SYPA5POO.js +130 -0
  34. package/dist/package-SYPA5POO.js.map +1 -0
  35. package/dist/{pi-SJPIL4VE.js → pi-WMTUX6ES.js} +102 -23
  36. package/dist/pi-WMTUX6ES.js.map +1 -0
  37. package/dist/{qwen-code-IFCM6BJY.js → qwen-code-MRRIUQ3P.js} +78 -5
  38. package/dist/qwen-code-MRRIUQ3P.js.map +1 -0
  39. package/dist/runtime/index.js +4 -4
  40. package/dist/{serve-LFOUSZAD.js → serve-XJOSA7BY.js} +6 -6
  41. package/dist/{status-M7JLZVLC.js → status-DYSS6BOR.js} +16 -7
  42. package/dist/status-DYSS6BOR.js.map +1 -0
  43. package/dist/{telemetry-3NVEXPYX.js → telemetry-IJ4GP2Q3.js} +2 -2
  44. package/dist/uninstall-3DXIBRCV.js +162 -0
  45. package/dist/uninstall-3DXIBRCV.js.map +1 -0
  46. package/dist/{upgrade-76BQZBMA.js → upgrade-GJEB3ITY.js} +26 -8
  47. package/dist/upgrade-GJEB3ITY.js.map +1 -0
  48. package/dist/{usage-G3GSCX23.js → usage-2BJLM3GJ.js} +2 -2
  49. package/dist/{usage-event-QUTMNBS5.js → usage-event-472X3DOY.js} +5 -5
  50. package/dist/{warp-LLOSXR7A.js → warp-X6MB67NT.js} +81 -3
  51. package/dist/warp-X6MB67NT.js.map +1 -0
  52. package/dist/{zed-HYOITK7F.js → zed-NTCYW4V2.js} +68 -3
  53. package/dist/zed-NTCYW4V2.js.map +1 -0
  54. package/package.json +1 -1
  55. package/dist/chunk-AKJGNTU3.js.map +0 -1
  56. package/dist/chunk-KHO5WNTP.js.map +0 -1
  57. package/dist/chunk-WCETQWWK.js.map +0 -1
  58. package/dist/chunk-Z2BVYKZL.js +0 -369
  59. package/dist/chunk-Z2BVYKZL.js.map +0 -1
  60. package/dist/doctor-62BHIXOR.js.map +0 -1
  61. package/dist/install-DMW2C7SV.js.map +0 -1
  62. package/dist/kilo-ISR5FBBJ.js +0 -408
  63. package/dist/kilo-ISR5FBBJ.js.map +0 -1
  64. package/dist/kilo-cli-2RNODHWY.js.map +0 -1
  65. package/dist/kimi-Q5RG7CYT.js.map +0 -1
  66. package/dist/kiro-EKTRW424.js.map +0 -1
  67. package/dist/package-O63K4S7T.js.map +0 -1
  68. package/dist/pi-SJPIL4VE.js.map +0 -1
  69. package/dist/qwen-code-IFCM6BJY.js.map +0 -1
  70. package/dist/status-M7JLZVLC.js.map +0 -1
  71. package/dist/uninstall-H6FGMIHN.js +0 -73
  72. package/dist/uninstall-H6FGMIHN.js.map +0 -1
  73. package/dist/upgrade-76BQZBMA.js.map +0 -1
  74. package/dist/warp-LLOSXR7A.js.map +0 -1
  75. package/dist/zed-HYOITK7F.js.map +0 -1
  76. /package/dist/{chunk-O2SBA5KR.js.map → chunk-NJU275US.js.map} +0 -0
  77. /package/dist/{chunk-4ML7SZ7R.js.map → chunk-ZAM4M7LI.js.map} +0 -0
  78. /package/dist/{detect-JK7T46N6.js.map → detect-MNDHGLSV.js.map} +0 -0
  79. /package/dist/{hook-AC7APC66.js.map → hook-7LUOKIXH.js.map} +0 -0
  80. /package/dist/{leaderboard-R5HRJHNU.js.map → leaderboard-MUJ7DURM.js.map} +0 -0
  81. /package/dist/{serve-LFOUSZAD.js.map → serve-XJOSA7BY.js.map} +0 -0
  82. /package/dist/{telemetry-3NVEXPYX.js.map → telemetry-IJ4GP2Q3.js.map} +0 -0
  83. /package/dist/{usage-G3GSCX23.js.map → usage-2BJLM3GJ.js.map} +0 -0
  84. /package/dist/{usage-event-QUTMNBS5.js.map → usage-event-472X3DOY.js.map} +0 -0
package/README.md CHANGED
@@ -14,7 +14,7 @@
14
14
  ![install verified](https://img.shields.io/badge/install%20verified-29%2F29-22c55e)
15
15
  ![headless runtime](https://img.shields.io/badge/headless%20runtime-10%20CLIs%20activated-22c55e)
16
16
  ![marketplace](https://img.shields.io/badge/package-9%20marketplace%20formats-2563eb)
17
- ![tests](https://img.shields.io/badge/tests-1263%20passing-22c55e)
17
+ ![tests](https://img.shields.io/badge/tests-1438%20passing-22c55e)
18
18
 
19
19
  ## Who this is for
20
20
 
@@ -102,7 +102,7 @@ installed into an isolated environment for every adapter and inspected on disk:
102
102
  - **Clean uninstall + `--purge`.** Every installed surface reverses; `--purge`
103
103
  deregisters the connector record and tears down the home binary when no
104
104
  connectors remain (29 / 29).
105
- - **1263 tests passing** · `tsc` clean · build green.
105
+ - **1438 tests passing** · `tsc` clean · build green.
106
106
 
107
107
  The 0.2.0 additions — the `memory` surface, the `nativeHooks` passthrough, and
108
108
  `configPatch` — went through the same bar: dogfooded against real connector
@@ -260,6 +260,13 @@ Same one definition, your choice of distribution:
260
260
 
261
261
  - **Direct install** (above) — `install` writes each host's native MCP + plugin/
262
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.
263
270
  - **Marketplace package** — `agent-connector package` turns the connector into a
264
271
  marketplace/extension bundle (manifest + bundled commands, agents, skills,
265
272
  hooks, MCP) for **9 host formats** (plus 2 official MCP standard artifacts —
@@ -0,0 +1,284 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ connectorDir,
4
+ connectorsDir,
5
+ dataRoot,
6
+ ensureDir
7
+ } from "./chunk-TRQKHP7D.js";
8
+
9
+ // src/core/marketplace-state.ts
10
+ import { createHash } from "crypto";
11
+ import {
12
+ existsSync,
13
+ readdirSync,
14
+ readFileSync,
15
+ rmSync,
16
+ statSync,
17
+ writeFileSync
18
+ } from "fs";
19
+ import { homedir } from "os";
20
+ import { dirname, join, relative, resolve } from "path";
21
+ import TOML from "@iarna/toml";
22
+ var MARKETPLACE_NAME = "agent-connector";
23
+ function marketplaceInstallsPath(connectorId) {
24
+ return join(connectorDir(connectorId), "marketplace-installs.json");
25
+ }
26
+ function readMarketplaceInstalls(connectorId) {
27
+ try {
28
+ const raw = readFileSync(marketplaceInstallsPath(connectorId), "utf8");
29
+ const parsed = JSON.parse(raw);
30
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
31
+ return parsed;
32
+ }
33
+ } catch {
34
+ }
35
+ return {};
36
+ }
37
+ function writeMarketplaceInstalls(connectorId, installs) {
38
+ const path = marketplaceInstallsPath(connectorId);
39
+ if (Object.keys(installs).length === 0) {
40
+ rmSync(path, { force: true });
41
+ return;
42
+ }
43
+ ensureDir(dirname(path));
44
+ writeFileSync(path, `${JSON.stringify(installs, null, 2)}
45
+ `, "utf8");
46
+ }
47
+ function connectorHasMarketplaceRecords(connectorId) {
48
+ return Object.keys(readMarketplaceInstalls(connectorId)).length > 0;
49
+ }
50
+ function anyMarketplaceRecordsRemain(excludeConnectorId) {
51
+ const dir = connectorsDir();
52
+ if (!existsSync(dir)) return false;
53
+ let entries;
54
+ try {
55
+ entries = readdirSync(dir);
56
+ } catch {
57
+ return false;
58
+ }
59
+ return entries.some(
60
+ (id) => id !== excludeConnectorId && connectorHasMarketplaceRecords(id)
61
+ );
62
+ }
63
+ function marketplaceRoot() {
64
+ return join(dataRoot(), "marketplace");
65
+ }
66
+ function claudeStagingRoot() {
67
+ return join(marketplaceRoot(), "claude");
68
+ }
69
+ function codexStagingRoot() {
70
+ return join(marketplaceRoot(), "codex");
71
+ }
72
+ function agyStagingRoot() {
73
+ return join(marketplaceRoot(), "agy");
74
+ }
75
+ function claudeConfigDir() {
76
+ const override = process.env.CLAUDE_CONFIG_DIR;
77
+ if (override && override.trim() !== "") return resolve(override);
78
+ return join(homedir(), ".claude");
79
+ }
80
+ function claudePluginKey(connectorId) {
81
+ return `${connectorId}@${MARKETPLACE_NAME}`;
82
+ }
83
+ function readJsonFile(path) {
84
+ try {
85
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
86
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
87
+ return parsed;
88
+ }
89
+ } catch {
90
+ }
91
+ return null;
92
+ }
93
+ function readClaudeInstalledPlugins() {
94
+ const file = join(claudeConfigDir(), "plugins", "installed_plugins.json");
95
+ const parsed = readJsonFile(file);
96
+ const plugins = parsed?.plugins;
97
+ if (plugins && typeof plugins === "object" && !Array.isArray(plugins)) {
98
+ return plugins;
99
+ }
100
+ return null;
101
+ }
102
+ function claudePluginInstalled(connectorId) {
103
+ const plugins = readClaudeInstalledPlugins();
104
+ if (!plugins) return false;
105
+ const entry = plugins[claudePluginKey(connectorId)];
106
+ return Array.isArray(entry) ? entry.length > 0 : entry != null;
107
+ }
108
+ function anyClaudeAgentConnectorPlugins() {
109
+ const plugins = readClaudeInstalledPlugins();
110
+ if (!plugins) return false;
111
+ return Object.entries(plugins).some(
112
+ ([key, entry]) => key.endsWith(`@${MARKETPLACE_NAME}`) && (Array.isArray(entry) ? entry.length > 0 : entry != null)
113
+ );
114
+ }
115
+ function claudeKnownMarketplacePath(name) {
116
+ const file = join(claudeConfigDir(), "plugins", "known_marketplaces.json");
117
+ const parsed = readJsonFile(file);
118
+ const entry = parsed?.[name];
119
+ if (!entry || typeof entry !== "object") return null;
120
+ const e = entry;
121
+ if (typeof e.installLocation === "string") return e.installLocation;
122
+ const sourcePath = e.source?.path;
123
+ return typeof sourcePath === "string" ? sourcePath : null;
124
+ }
125
+ function codexConfigHome() {
126
+ const env = process.env.CODEX_HOME;
127
+ if (env && env.trim() !== "") {
128
+ if (env.startsWith("~")) return join(homedir(), env.replace(/^~[/\\]?/, ""));
129
+ return resolve(env);
130
+ }
131
+ return join(homedir(), ".codex");
132
+ }
133
+ function codexPluginKey(connectorId) {
134
+ return `${connectorId}@${MARKETPLACE_NAME}`;
135
+ }
136
+ function readCodexConfig() {
137
+ const file = join(codexConfigHome(), "config.toml");
138
+ try {
139
+ return TOML.parse(readFileSync(file, "utf8"));
140
+ } catch {
141
+ return {};
142
+ }
143
+ }
144
+ function codexPluginInstalled(connectorId) {
145
+ const cfg = readCodexConfig();
146
+ const plugins = cfg["plugins"];
147
+ if (!plugins || typeof plugins !== "object" || Array.isArray(plugins)) return false;
148
+ return codexPluginKey(connectorId) in plugins;
149
+ }
150
+ function anyCodexAgentConnectorPlugins() {
151
+ const cfg = readCodexConfig();
152
+ const plugins = cfg["plugins"];
153
+ if (!plugins || typeof plugins !== "object" || Array.isArray(plugins)) return false;
154
+ return Object.keys(plugins).some(
155
+ (k) => k.endsWith(`@${MARKETPLACE_NAME}`)
156
+ );
157
+ }
158
+ function codexMarketplaceSource(name) {
159
+ const cfg = readCodexConfig();
160
+ const marketplaces = cfg["marketplaces"];
161
+ if (!marketplaces || typeof marketplaces !== "object" || Array.isArray(marketplaces)) {
162
+ return null;
163
+ }
164
+ const entry = marketplaces[name];
165
+ if (!entry || typeof entry !== "object") return null;
166
+ const source = entry.source;
167
+ return typeof source === "string" ? source : null;
168
+ }
169
+ function agyConfigDir() {
170
+ return join(homedir(), ".gemini", "config");
171
+ }
172
+ function agyPluginsDir() {
173
+ return join(agyConfigDir(), "plugins");
174
+ }
175
+ function agyImportManifestPaths() {
176
+ return [
177
+ join(agyConfigDir(), "import_manifest.json"),
178
+ // win32 location
179
+ join(agyPluginsDir(), "import_manifest.json")
180
+ // posix location
181
+ ];
182
+ }
183
+ function agyPluginInstalled(connectorId) {
184
+ for (const manifest of agyImportManifestPaths()) {
185
+ try {
186
+ const parsed = JSON.parse(readFileSync(manifest, "utf8"));
187
+ const imports = parsed.imports;
188
+ if (Array.isArray(imports) && imports.some(
189
+ (e) => e != null && typeof e === "object" && e.name === connectorId
190
+ )) {
191
+ return true;
192
+ }
193
+ } catch {
194
+ }
195
+ }
196
+ return existsSync(join(agyPluginsDir(), connectorId, "plugin.json"));
197
+ }
198
+ function marketplaceEvidence(connectorId, platform) {
199
+ if (readMarketplaceInstalls(connectorId)[platform]) {
200
+ return "recorded in marketplace-installs.json";
201
+ }
202
+ switch (platform) {
203
+ case "claude-code":
204
+ if (claudePluginInstalled(connectorId)) {
205
+ return `${claudePluginKey(connectorId)} listed in Claude's installed_plugins.json`;
206
+ }
207
+ return null;
208
+ case "gemini-cli": {
209
+ const dir = join(homedir(), ".gemini", "extensions", connectorId);
210
+ return existsSync(dir) ? `extension dir ${dir} exists` : null;
211
+ }
212
+ case "antigravity":
213
+ case "antigravity-cli": {
214
+ const dir = join(homedir(), ".gemini", "config", "plugins", connectorId);
215
+ return existsSync(dir) ? `plugin dir ${dir} exists` : null;
216
+ }
217
+ case "codex": {
218
+ const codexHome = process.env.CODEX_HOME && process.env.CODEX_HOME.trim() !== "" ? resolve(process.env.CODEX_HOME) : join(homedir(), ".codex");
219
+ const dir = join(codexHome, "plugins", "cache", MARKETPLACE_NAME, connectorId);
220
+ return existsSync(dir) ? `codex plugin cache ${dir} exists` : null;
221
+ }
222
+ default:
223
+ return null;
224
+ }
225
+ }
226
+ function hashDirectory(dir) {
227
+ if (!existsSync(dir)) return "";
228
+ const files = [];
229
+ const walk = (d) => {
230
+ let entries;
231
+ try {
232
+ entries = readdirSync(d);
233
+ } catch {
234
+ return;
235
+ }
236
+ for (const name of entries.sort()) {
237
+ const full = join(d, name);
238
+ let st;
239
+ try {
240
+ st = statSync(full);
241
+ } catch {
242
+ continue;
243
+ }
244
+ if (st.isDirectory()) walk(full);
245
+ else if (st.isFile()) files.push(full);
246
+ }
247
+ };
248
+ walk(dir);
249
+ const hash = createHash("sha256");
250
+ for (const file of files.sort()) {
251
+ hash.update(relative(dir, file).split("\\").join("/"));
252
+ hash.update("\0");
253
+ try {
254
+ hash.update(readFileSync(file));
255
+ } catch {
256
+ }
257
+ hash.update("\0");
258
+ }
259
+ return hash.digest("hex");
260
+ }
261
+
262
+ export {
263
+ MARKETPLACE_NAME,
264
+ readMarketplaceInstalls,
265
+ writeMarketplaceInstalls,
266
+ connectorHasMarketplaceRecords,
267
+ anyMarketplaceRecordsRemain,
268
+ marketplaceRoot,
269
+ claudeStagingRoot,
270
+ codexStagingRoot,
271
+ agyStagingRoot,
272
+ claudePluginKey,
273
+ claudePluginInstalled,
274
+ anyClaudeAgentConnectorPlugins,
275
+ claudeKnownMarketplacePath,
276
+ codexPluginKey,
277
+ codexPluginInstalled,
278
+ anyCodexAgentConnectorPlugins,
279
+ codexMarketplaceSource,
280
+ agyPluginInstalled,
281
+ marketplaceEvidence,
282
+ hashDirectory
283
+ };
284
+ //# sourceMappingURL=chunk-44HUXXC6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/marketplace-state.ts"],"sourcesContent":["/**\n * core/marketplace-state — pure read/write helpers for MARKETPLACE-method\n * install state. A LEAF module (fs + paths only, no adapters, no spawning) so\n * the installer, the marketplace orchestration, doctor, and status can all\n * import it without cycles.\n *\n * Two kinds of state live here:\n *\n * 1. The per-connector state record `connectorDir(id)/marketplace-installs.json`\n * — one entry per platform a connector was marketplace-installed on\n * ({ format, bundleDir, marketplace, scope, version, contentHash,\n * installedAt }). It drives `uninstall --method auto`, upgrade staleness\n * warnings, doctor, and the double-install guard.\n *\n * 2. Read-only fs EVIDENCE probes of the hosts' own plugin state (e.g.\n * Claude Code's `$CLAUDE_CONFIG_DIR/plugins/installed_plugins.json`).\n * These catch installs the user performed manually from `package`'s\n * printed instructions, which the state record alone would miss. Probes\n * read undocumented host internals, so every reader degrades to null/false\n * on absence or parse failure — the guard convention is \"refuse only on\n * positive evidence\" (docs: design risk 4).\n */\n\nimport { createHash } from \"node:crypto\";\nimport {\n existsSync,\n readdirSync,\n readFileSync,\n rmSync,\n statSync,\n writeFileSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, relative, resolve } from \"node:path\";\n\nimport TOML from \"@iarna/toml\";\n\nimport type { InstallScope, PlatformId } from \"./types.js\";\nimport type { PackageFormat } from \"./package.js\";\nimport { connectorDir, connectorsDir, dataRoot, ensureDir } from \"./paths.js\";\n\n// ─────────────────────────────────────────────────────────────────────────\n// State record: connectorDir(id)/marketplace-installs.json\n// ─────────────────────────────────────────────────────────────────────────\n\n/** The catalog name every staged local marketplace is registered under. */\nexport const MARKETPLACE_NAME = \"agent-connector\";\n\n/** One marketplace install of one connector on one platform. */\nexport interface MarketplaceInstallRecord {\n /** Bundle format that was staged + installed (e.g. \"claude-plugin\"). */\n format: PackageFormat;\n /** Absolute path of the staged plugin dir the host references/copied. */\n bundleDir: string;\n /** Marketplace/catalog name the install is keyed under ({@link MARKETPLACE_NAME}). */\n marketplace: string;\n /** Install scope (v1: always \"user\"). */\n scope: InstallScope;\n /** connector.version at install time (staleness warnings key off this). */\n version: string;\n /** sha256 over the staged plugin dir at install time (drift detection). */\n contentHash: string;\n /** ISO timestamp of the recorded install. */\n installedAt: string;\n}\n\n/** platform → record map persisted per connector. */\nexport type MarketplaceInstalls = Partial<\n Record<PlatformId, MarketplaceInstallRecord>\n>;\n\n/** Path of the per-connector marketplace state record. */\nexport function marketplaceInstallsPath(connectorId: string): string {\n return join(connectorDir(connectorId), \"marketplace-installs.json\");\n}\n\n/** Read the state record ({} on absence/parse failure — never throws). */\nexport function readMarketplaceInstalls(connectorId: string): MarketplaceInstalls {\n try {\n const raw = readFileSync(marketplaceInstallsPath(connectorId), \"utf8\");\n const parsed = JSON.parse(raw) as unknown;\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as MarketplaceInstalls;\n }\n } catch {\n /* absent / unreadable → empty */\n }\n return {};\n}\n\n/** Write the state record; an empty map DELETES the file (uninstall leaves no husk). */\nexport function writeMarketplaceInstalls(\n connectorId: string,\n installs: MarketplaceInstalls,\n): void {\n const path = marketplaceInstallsPath(connectorId);\n if (Object.keys(installs).length === 0) {\n rmSync(path, { force: true });\n return;\n }\n ensureDir(dirname(path));\n writeFileSync(path, `${JSON.stringify(installs, null, 2)}\\n`, \"utf8\");\n}\n\n/** True when THIS connector has any recorded marketplace install. */\nexport function connectorHasMarketplaceRecords(connectorId: string): boolean {\n return Object.keys(readMarketplaceInstalls(connectorId)).length > 0;\n}\n\n/**\n * True when ANY registered connector (optionally excluding one) still records a\n * marketplace install. Guards home-bin removal on purge: an installed plugin's\n * hooks exec the home-bin OUTSIDE any adapter-managed config, so removing the\n * launcher while a marketplace install survives would silently kill its hooks.\n */\nexport function anyMarketplaceRecordsRemain(excludeConnectorId?: string): boolean {\n const dir = connectorsDir();\n if (!existsSync(dir)) return false;\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return false;\n }\n return entries.some(\n (id) => id !== excludeConnectorId && connectorHasMarketplaceRecords(id),\n );\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Staging roots — bundles staged by-reference must survive cwd changes and\n// `rm -rf dist-plugin`, so they live under the data-root, one shared root per\n// host family, holding every marketplace-installed connector's bundle plus ONE\n// regenerated catalog named \"agent-connector\" listing them all.\n// ─────────────────────────────────────────────────────────────────────────\n\n/** Root for every marketplace staging tree: <dataRoot>/marketplace. */\nexport function marketplaceRoot(): string {\n return join(dataRoot(), \"marketplace\");\n}\n\n/** Shared Claude-family staging root: <dataRoot>/marketplace/claude. */\nexport function claudeStagingRoot(): string {\n return join(marketplaceRoot(), \"claude\");\n}\n\n/** Shared Codex-family staging root: <dataRoot>/marketplace/codex. */\nexport function codexStagingRoot(): string {\n return join(marketplaceRoot(), \"codex\");\n}\n\n/** Shared agy (Antigravity) staging root: <dataRoot>/marketplace/agy. */\nexport function agyStagingRoot(): string {\n return join(marketplaceRoot(), \"agy\");\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Claude Code host-state readers ($CLAUDE_CONFIG_DIR || ~/.claude)\n// ─────────────────────────────────────────────────────────────────────────\n\n/** Claude Code's config dir, honoring the CLAUDE_CONFIG_DIR override. */\nexport function claudeConfigDir(): string {\n const override = process.env.CLAUDE_CONFIG_DIR;\n if (override && override.trim() !== \"\") return resolve(override);\n return join(homedir(), \".claude\");\n}\n\n/** The installed_plugins.json key our marketplace install creates for `id`. */\nexport function claudePluginKey(connectorId: string): string {\n return `${connectorId}@${MARKETPLACE_NAME}`;\n}\n\nfunction readJsonFile(path: string): Record<string, unknown> | null {\n try {\n const parsed = JSON.parse(readFileSync(path, \"utf8\")) as unknown;\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n } catch {\n /* absent / unreadable → null */\n }\n return null;\n}\n\n/** The `plugins` map from Claude's installed_plugins.json (null when unreadable). */\nexport function readClaudeInstalledPlugins(): Record<string, unknown> | null {\n const file = join(claudeConfigDir(), \"plugins\", \"installed_plugins.json\");\n const parsed = readJsonFile(file);\n const plugins = parsed?.plugins;\n if (plugins && typeof plugins === \"object\" && !Array.isArray(plugins)) {\n return plugins as Record<string, unknown>;\n }\n return null;\n}\n\n/** True when Claude's own state lists `<id>@agent-connector` as installed. */\nexport function claudePluginInstalled(connectorId: string): boolean {\n const plugins = readClaudeInstalledPlugins();\n if (!plugins) return false;\n const entry = plugins[claudePluginKey(connectorId)];\n return Array.isArray(entry) ? entry.length > 0 : entry != null;\n}\n\n/** True when ANY `*@agent-connector` plugin remains in Claude's state. */\nexport function anyClaudeAgentConnectorPlugins(): boolean {\n const plugins = readClaudeInstalledPlugins();\n if (!plugins) return false;\n return Object.entries(plugins).some(\n ([key, entry]) =>\n key.endsWith(`@${MARKETPLACE_NAME}`) &&\n (Array.isArray(entry) ? entry.length > 0 : entry != null),\n );\n}\n\n/**\n * The directory Claude's known_marketplaces.json records for `name`, or null\n * when not registered / unreadable. Used both as a presence probe and as the\n * NAME-COLLISION check: a registration pointing somewhere other than our\n * staging root belongs to the user and must never be touched.\n */\nexport function claudeKnownMarketplacePath(name: string): string | null {\n const file = join(claudeConfigDir(), \"plugins\", \"known_marketplaces.json\");\n const parsed = readJsonFile(file);\n const entry = parsed?.[name];\n if (!entry || typeof entry !== \"object\") return null;\n const e = entry as { installLocation?: unknown; source?: { path?: unknown } };\n if (typeof e.installLocation === \"string\") return e.installLocation;\n const sourcePath = e.source?.path;\n return typeof sourcePath === \"string\" ? sourcePath : null;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Codex host-state readers ($CODEX_HOME || ~/.codex → config.toml)\n// ─────────────────────────────────────────────────────────────────────────\n\n/** Codex's config home, honoring the CODEX_HOME override (defaults to ~/.codex). */\nexport function codexConfigHome(): string {\n const env = process.env.CODEX_HOME;\n if (env && env.trim() !== \"\") {\n if (env.startsWith(\"~\")) return join(homedir(), env.replace(/^~[/\\\\]?/, \"\"));\n return resolve(env);\n }\n return join(homedir(), \".codex\");\n}\n\n/** The config.toml key our marketplace install creates for `id` (== claude's). */\nexport function codexPluginKey(connectorId: string): string {\n return `${connectorId}@${MARKETPLACE_NAME}`;\n}\n\n/** Parse <CODEX_HOME>/config.toml ({} on absence/parse failure — never throws). */\nfunction readCodexConfig(): Record<string, unknown> {\n const file = join(codexConfigHome(), \"config.toml\");\n try {\n return TOML.parse(readFileSync(file, \"utf8\")) as Record<string, unknown>;\n } catch {\n return {};\n }\n}\n\n/**\n * True when Codex's config.toml carries the `[plugins.\"<id>@agent-connector\"]`\n * table. The DEFINITIVE install probe — empty `plugins/cache/...` dirs linger\n * after uninstall, so the cache dir must NOT be used (docs: codex quirk).\n */\nexport function codexPluginInstalled(connectorId: string): boolean {\n const cfg = readCodexConfig();\n const plugins = cfg[\"plugins\"];\n if (!plugins || typeof plugins !== \"object\" || Array.isArray(plugins)) return false;\n return codexPluginKey(connectorId) in (plugins as Record<string, unknown>);\n}\n\n/** True when ANY `*@agent-connector` plugin remains in Codex's config.toml. */\nexport function anyCodexAgentConnectorPlugins(): boolean {\n const cfg = readCodexConfig();\n const plugins = cfg[\"plugins\"];\n if (!plugins || typeof plugins !== \"object\" || Array.isArray(plugins)) return false;\n return Object.keys(plugins as Record<string, unknown>).some((k) =>\n k.endsWith(`@${MARKETPLACE_NAME}`),\n );\n}\n\n/**\n * The directory config.toml records as `[marketplaces.agent-connector].source`,\n * or null when not registered / unreadable. Both a presence probe and the\n * NAME-COLLISION check: a registration pointing somewhere other than our\n * staging root belongs to the user and must never be touched.\n */\nexport function codexMarketplaceSource(name: string): string | null {\n const cfg = readCodexConfig();\n const marketplaces = cfg[\"marketplaces\"];\n if (!marketplaces || typeof marketplaces !== \"object\" || Array.isArray(marketplaces)) {\n return null;\n }\n const entry = (marketplaces as Record<string, unknown>)[name];\n if (!entry || typeof entry !== \"object\") return null;\n const source = (entry as { source?: unknown }).source;\n return typeof source === \"string\" ? source : null;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// agy (Antigravity) host-state readers (~/.gemini/config/plugins, NO env)\n// ─────────────────────────────────────────────────────────────────────────\n\n/** agy's config dir: ~/.gemini/config (NO dedicated env — HOME isolation). */\nexport function agyConfigDir(): string {\n return join(homedir(), \".gemini\", \"config\");\n}\n\n/** agy's plugins dir: ~/.gemini/config/plugins. */\nexport function agyPluginsDir(): string {\n return join(agyConfigDir(), \"plugins\");\n}\n\n/**\n * Candidate `import_manifest.json` locations. agy 1.0.7 records the manifest at\n * `<config>/plugins/import_manifest.json` on POSIX but at\n * `<config>/import_manifest.json` on Windows (live-confirmed on the my-window\n * box) — both are probed, then the copied plugin dir is the final fallback.\n */\nexport function agyImportManifestPaths(): string[] {\n return [\n join(agyConfigDir(), \"import_manifest.json\"), // win32 location\n join(agyPluginsDir(), \"import_manifest.json\"), // posix location\n ];\n}\n\n/** @deprecated single-location accessor — prefer {@link agyImportManifestPaths}. */\nexport function agyImportManifestPath(): string {\n return join(agyPluginsDir(), \"import_manifest.json\");\n}\n\n/**\n * True when `id` is installed per agy's import manifest (`imports[].name`),\n * checking BOTH the win32 and posix manifest locations, with a fallback probe of\n * the copied plugin dir's plugin.json. agy's uninstall removes the plugin dir\n * and clears the manifest entry (live-verified on Windows: install → dir +\n * manifest present, uninstall → both gone, re-install idempotent).\n */\nexport function agyPluginInstalled(connectorId: string): boolean {\n for (const manifest of agyImportManifestPaths()) {\n try {\n const parsed = JSON.parse(readFileSync(manifest, \"utf8\")) as {\n imports?: unknown;\n };\n const imports = parsed.imports;\n if (\n Array.isArray(imports) &&\n imports.some(\n (e) =>\n e != null &&\n typeof e === \"object\" &&\n (e as { name?: unknown }).name === connectorId,\n )\n ) {\n return true;\n }\n } catch {\n /* absent / unreadable manifest → try the next candidate */\n }\n }\n return existsSync(join(agyPluginsDir(), connectorId, \"plugin.json\"));\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Cross-host marketplace-install EVIDENCE (the double-install guard's probe)\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * Read-only evidence that `connectorId` is marketplace/plugin-installed on\n * `platform`: the state record first, then a cheap fs probe of the host's own\n * plugin state (catches manual installs from `package`'s printed instructions).\n * Returns a human-readable description of the evidence, or null when none.\n */\nexport function marketplaceEvidence(\n connectorId: string,\n platform: PlatformId,\n): string | null {\n if (readMarketplaceInstalls(connectorId)[platform]) {\n return \"recorded in marketplace-installs.json\";\n }\n switch (platform) {\n case \"claude-code\":\n if (claudePluginInstalled(connectorId)) {\n return `${claudePluginKey(connectorId)} listed in Claude's installed_plugins.json`;\n }\n return null;\n case \"gemini-cli\": {\n const dir = join(homedir(), \".gemini\", \"extensions\", connectorId);\n return existsSync(dir) ? `extension dir ${dir} exists` : null;\n }\n case \"antigravity\":\n case \"antigravity-cli\": {\n const dir = join(homedir(), \".gemini\", \"config\", \"plugins\", connectorId);\n return existsSync(dir) ? `plugin dir ${dir} exists` : null;\n }\n case \"codex\": {\n const codexHome =\n process.env.CODEX_HOME && process.env.CODEX_HOME.trim() !== \"\"\n ? resolve(process.env.CODEX_HOME)\n : join(homedir(), \".codex\");\n const dir = join(codexHome, \"plugins\", \"cache\", MARKETPLACE_NAME, connectorId);\n return existsSync(dir) ? `codex plugin cache ${dir} exists` : null;\n }\n default:\n return null;\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Content hashing — staged-bundle drift detection\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * Deterministic sha256 over every file under `dir` (sorted relative paths +\n * bytes). Empty string when the dir is missing/unreadable. Used to record the\n * staged bundle's content at install time and detect drift later (claude caches\n * a versioned COPY, so an edited connector goes silently stale without this).\n */\nexport function hashDirectory(dir: string): string {\n if (!existsSync(dir)) return \"\";\n const files: string[] = [];\n const walk = (d: string): void => {\n let entries: string[];\n try {\n entries = readdirSync(d);\n } catch {\n return;\n }\n for (const name of entries.sort()) {\n const full = join(d, name);\n let st;\n try {\n st = statSync(full);\n } catch {\n continue;\n }\n if (st.isDirectory()) walk(full);\n else if (st.isFile()) files.push(full);\n }\n };\n walk(dir);\n const hash = createHash(\"sha256\");\n for (const file of files.sort()) {\n hash.update(relative(dir, file).split(\"\\\\\").join(\"/\"));\n hash.update(\"\\0\");\n try {\n hash.update(readFileSync(file));\n } catch {\n /* unreadable file contributes only its path */\n }\n hash.update(\"\\0\");\n }\n return hash.digest(\"hex\");\n}\n"],"mappings":"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","names":[]}
@@ -73,35 +73,35 @@ function renderInstallResult(result, verb) {
73
73
  return lines.join("\n");
74
74
  }
75
75
  var COMMANDS = {
76
- detect: () => import("./detect-JK7T46N6.js"),
77
- install: () => import("./install-DMW2C7SV.js"),
78
- uninstall: () => import("./uninstall-H6FGMIHN.js"),
76
+ detect: () => import("./detect-MNDHGLSV.js"),
77
+ install: () => import("./install-TQTT4F3I.js"),
78
+ uninstall: () => import("./uninstall-3DXIBRCV.js"),
79
79
  // `upgrade` is the single "bring everything current" verb (re-render host
80
80
  // config + refresh the home pointer + managed channel guidance). `sync` and
81
81
  // `update` are kept as back-compat aliases that route to the same module so
82
82
  // existing scripts/docs keep working without a second concept to learn.
83
- upgrade: () => import("./upgrade-76BQZBMA.js"),
84
- sync: () => import("./upgrade-76BQZBMA.js"),
85
- update: () => import("./upgrade-76BQZBMA.js"),
86
- package: () => import("./package-O63K4S7T.js"),
87
- doctor: () => import("./doctor-62BHIXOR.js"),
88
- status: () => import("./status-M7JLZVLC.js"),
89
- telemetry: () => import("./telemetry-3NVEXPYX.js"),
90
- usage: () => import("./usage-G3GSCX23.js"),
91
- leaderboard: () => import("./leaderboard-R5HRJHNU.js"),
92
- hook: () => import("./hook-AC7APC66.js"),
93
- serve: () => import("./serve-LFOUSZAD.js"),
83
+ upgrade: () => import("./upgrade-GJEB3ITY.js"),
84
+ sync: () => import("./upgrade-GJEB3ITY.js"),
85
+ update: () => import("./upgrade-GJEB3ITY.js"),
86
+ package: () => import("./package-SYPA5POO.js"),
87
+ doctor: () => import("./doctor-N5KZDD55.js"),
88
+ status: () => import("./status-DYSS6BOR.js"),
89
+ telemetry: () => import("./telemetry-IJ4GP2Q3.js"),
90
+ usage: () => import("./usage-2BJLM3GJ.js"),
91
+ leaderboard: () => import("./leaderboard-MUJ7DURM.js"),
92
+ hook: () => import("./hook-7LUOKIXH.js"),
93
+ serve: () => import("./serve-XJOSA7BY.js"),
94
94
  // Hidden (omitted from USAGE): the opt-in host-native turn-usage entrypoint an
95
95
  // AfterModel / PostInvocation hook points at. Always exits 0; records a
96
96
  // distinct `model_turn` row. See cli/commands/usage-event.ts.
97
- "usage-event": () => import("./usage-event-QUTMNBS5.js")
97
+ "usage-event": () => import("./usage-event-472X3DOY.js")
98
98
  };
99
99
  var DEFAULT_PROGRAM_NAME = "agent-connector";
100
100
  var COMMAND_USAGE = {
101
101
  detect: "detect [--json] [--project <dir>]",
102
- install: "install [--connector <path>] [--scope user|project] [--targets a,b] [--project <dir>] [--dry-run] [--force]",
103
- uninstall: "uninstall [--connector <path>] [--connector-id <id>] [--scope user|project] [--targets a,b] [--project <dir>] [--dry-run] [--purge]",
104
- upgrade: "upgrade [--channel stable|latest] [--connector <path>] [--scope user|project] [--targets a,b] [--project <dir>] [--dry-run]",
102
+ install: "install [--method direct|marketplace] [--connector <path>] [--scope user|project] [--targets a,b] [--project <dir>] [--dry-run] [--force]",
103
+ uninstall: "uninstall [--method auto|direct|marketplace] [--connector <path>] [--connector-id <id>] [--scope user|project] [--targets a,b] [--project <dir>] [--dry-run] [--purge]",
104
+ upgrade: "upgrade [--method direct|marketplace] [--channel stable|latest] [--connector <path>] [--scope user|project] [--targets a,b] [--project <dir>] [--dry-run]",
105
105
  sync: "sync \u2014 alias of upgrade (see `upgrade --help`)",
106
106
  update: "update \u2014 alias of upgrade (see `upgrade --help`)",
107
107
  package: "package [--connector <path>] [--format <fmt>|all] [--out <dir>] [--project <dir>] [--dry-run]",
@@ -120,8 +120,8 @@ usage: ${programName} <command> [flags]
120
120
 
121
121
  commands:
122
122
  detect List the AI-agent platforms installed on this machine.
123
- install Deploy a connector across its target platforms.
124
- uninstall Remove a connector's MCP + hook registrations.
123
+ install Deploy a connector across its target platforms (--method direct writes host config; --method marketplace drives the host's plugin install).
124
+ uninstall Remove a connector's registrations (--method auto reverses whichever method is actually installed).
125
125
  upgrade Bring everything current: re-render host config + heal the home pointer + managed update guidance (alias: update, sync).
126
126
  package Emit a marketplace/extension bundle (9 host formats, or the standard artifacts mcp-server-json | mcpb).
127
127
  doctor Health-check every detected platform; non-zero exit on any failure.
@@ -188,4 +188,4 @@ export {
188
188
  renderInstallResult,
189
189
  main
190
190
  };
191
- //# sourceMappingURL=chunk-AKJGNTU3.js.map
191
+ //# sourceMappingURL=chunk-DS3LUFVN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/app.ts"],"sourcesContent":["/**\n * cli/app — shared CLI helpers + command dispatch (NO side effects).\n *\n * Command modules import the shared helpers (print, fail, parse-flags,\n * renderInstallResult) from here, and main() dispatches the first positional to a\n * command module.\n * This module never auto-runs — `cli/index.ts` is the thin bin entry that calls\n * `main()`. Keeping the auto-run out of here is what prevents a dispatch recursion\n * (a command module importing a helper must not re-trigger the program) and makes\n * it robust to bundler entry-splitting (the old import.meta.url entry-guard broke\n * once tsup hoisted this code into a shared chunk).\n */\n\nimport { createRequire } from \"node:module\";\n\nimport type {\n ChangeRecord,\n InstallResult,\n InstallScope,\n PlatformId,\n} from \"../core/types.js\";\n\n// ─────────────────────────────────────────────────────────────────────────\n// Shared helpers (imported by the command modules)\n// ─────────────────────────────────────────────────────────────────────────\n\n/** Print a line to stdout (machine-readable payloads must stay on stdout). */\nexport function print(line: string): void {\n process.stdout.write(`${line}\\n`);\n}\n\n/**\n * The brand active for THIS invocation. main() sets it from\n * {@link MainOptions.programName} before dispatch, so error messages printed by\n * command modules via {@link fail} read as the embedding tool (e.g. `acme-db:`)\n * instead of always `agent-connector:`.\n */\nlet activeProgramName = \"agent-connector\";\n\n/** Print an error to stderr (branded) and return a non-zero exit code (default 2). */\nexport function fail(message: string, code = 2): number {\n process.stderr.write(`${activeProgramName}: ${message}\\n`);\n return code;\n}\n\n/**\n * Resolve agent-connector's own package version at runtime. Works from both the\n * bundled dist layout (dist/*.js → ../package.json) and the src layout under\n * tsx/vitest (src/cli/ → ../../package.json); the name check guards against\n * accidentally reading some other package.json on the walk.\n */\nexport function resolveOwnVersion(): string {\n const req = createRequire(import.meta.url);\n for (const rel of [\"../package.json\", \"../../package.json\", \"../../../package.json\"]) {\n try {\n const pkg = req(rel) as { name?: string; version?: string };\n if (pkg.name === \"@ken-jo/agent-connector\" && typeof pkg.version === \"string\") {\n return pkg.version;\n }\n } catch {\n /* keep walking */\n }\n }\n return \"0.0.0\";\n}\n\n/** Parse a --scope value the CLI accepts (user|project) into an InstallScope. */\nexport function parseScope(value: string | undefined): InstallScope | null {\n if (value === \"user\" || value === \"project\") return value;\n return null;\n}\n\n/**\n * Parse a comma-separated --targets value into PlatformId[]. Returns undefined\n * when the flag is absent/empty so callers can fall back to connector/detection.\n */\nexport function parseTargets(value: string | undefined): PlatformId[] | undefined {\n if (value == null || value.trim() === \"\") return undefined;\n const ids = value\n .split(\",\")\n .map((s) => s.trim())\n .filter((s) => s !== \"\") as PlatformId[];\n return ids.length > 0 ? ids : undefined;\n}\n\nconst ACTION_GLYPH: Record<ChangeRecord[\"action\"], string> = {\n create: \"+\",\n update: \"~\",\n remove: \"-\",\n skip: \"=\",\n warn: \"!\",\n};\n\n/**\n * Render an InstallResult as a readable diff: one line per ChangeRecord (glyph,\n * platform, detail, path) followed by any warnings and a summary tally. Used by\n * install / upgrade / uninstall.\n */\nexport function renderInstallResult(\n result: InstallResult,\n verb: \"install\" | \"upgrade\" | \"uninstall\",\n): string {\n const lines: string[] = [];\n const mode = result.dryRun ? \" (dry-run — nothing written)\" : \"\";\n lines.push(`${verb} \"${result.connectorId}\"${mode}`);\n\n if (result.changes.length === 0) {\n lines.push(\" (no changes)\");\n } else {\n for (const c of result.changes) {\n const glyph = ACTION_GLYPH[c.action];\n const where = c.path ? ` ${c.path}` : \"\";\n lines.push(` ${glyph} [${c.platform}] ${c.action}: ${c.detail}${where}`);\n }\n }\n\n if (result.warnings.length > 0) {\n lines.push(\"\");\n lines.push(\"warnings:\");\n for (const w of result.warnings) lines.push(` ! ${w}`);\n }\n\n const tally = result.changes.reduce<Record<ChangeRecord[\"action\"], number>>(\n (acc, c) => {\n acc[c.action] += 1;\n return acc;\n },\n { create: 0, update: 0, remove: 0, skip: 0, warn: 0 },\n );\n lines.push(\"\");\n lines.push(\n `summary: ${tally.create} created, ${tally.update} updated, ` +\n `${tally.remove} removed, ${tally.skip} skipped, ${tally.warn} warning(s)`,\n );\n\n return lines.join(\"\\n\");\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Command dispatch\n// ─────────────────────────────────────────────────────────────────────────\n\n/** A command module: takes the post-command argv slice, returns an exit code. */\ntype CommandModule = { run: (argv: string[]) => Promise<number> | number };\n\n/** Lazy command loaders — keyed by the first positional. Imported on demand. */\nconst COMMANDS: Record<string, () => Promise<CommandModule>> = {\n detect: () => import(\"./commands/detect.js\"),\n install: () => import(\"./commands/install.js\"),\n uninstall: () => import(\"./commands/uninstall.js\"),\n // `upgrade` is the single \"bring everything current\" verb (re-render host\n // config + refresh the home pointer + managed channel guidance). `sync` and\n // `update` are kept as back-compat aliases that route to the same module so\n // existing scripts/docs keep working without a second concept to learn.\n upgrade: () => import(\"./commands/upgrade.js\"),\n sync: () => import(\"./commands/upgrade.js\"),\n update: () => import(\"./commands/upgrade.js\"),\n package: () => import(\"./commands/package.js\"),\n doctor: () => import(\"./commands/doctor.js\"),\n status: () => import(\"./commands/status.js\"),\n telemetry: () => import(\"./commands/telemetry.js\"),\n usage: () => import(\"./commands/usage.js\"),\n leaderboard: () => import(\"./commands/leaderboard.js\"),\n hook: () => import(\"./commands/hook.js\"),\n serve: () => import(\"./commands/serve.js\"),\n // Hidden (omitted from USAGE): the opt-in host-native turn-usage entrypoint an\n // AfterModel / PostInvocation hook points at. Always exits 0; records a\n // distinct `model_turn` row. See cli/commands/usage-event.ts.\n \"usage-event\": () => import(\"./commands/usage-event.js\"),\n};\n\n/** Default program name; an embedding SDK CLI overrides it via {@link MainOptions}. */\nexport const DEFAULT_PROGRAM_NAME = \"agent-connector\";\n\n/**\n * One-line usage signature per command — the single central copy behind\n * `<command> --help` and the friendly bad-flag error. Command modules use\n * node:util parseArgs (strict), which THROWS on an unknown flag; without the\n * dispatcher-level handling in {@link main}, `agent-connector install --help`\n * would die with a raw ERR_PARSE_ARGS stack trace — the very invocation the\n * root help tells users to run.\n */\nconst COMMAND_USAGE: Record<string, string> = {\n detect: \"detect [--json] [--project <dir>]\",\n install:\n \"install [--method direct|marketplace] [--connector <path>] [--scope user|project] [--targets a,b] [--project <dir>] [--dry-run] [--force]\",\n uninstall:\n \"uninstall [--method auto|direct|marketplace] [--connector <path>] [--connector-id <id>] [--scope user|project] [--targets a,b] [--project <dir>] [--dry-run] [--purge]\",\n upgrade:\n \"upgrade [--method direct|marketplace] [--channel stable|latest] [--connector <path>] [--scope user|project] [--targets a,b] [--project <dir>] [--dry-run]\",\n sync: \"sync — alias of upgrade (see `upgrade --help`)\",\n update: \"update — alias of upgrade (see `upgrade --help`)\",\n package:\n \"package [--connector <path>] [--format <fmt>|all] [--out <dir>] [--project <dir>] [--dry-run]\",\n doctor:\n \"doctor [--targets a,b] [--connector <path>] [--scope user|project] [--project <dir>] [--probe] [--json]\",\n status: \"status [--connector <path>] [--scope user|project] [--project <dir>] [--json]\",\n telemetry:\n \"telemetry report|export|leaderboard [--by <dim>] [--since <dur>] [--connector <id>] [--scope <slice>] [--format csv|json] [--out <file>] [--json]\",\n usage:\n \"usage report|export|leaderboard [--by <dim>] [--since <dur>] [--platform <id>] [--format csv|json] [--out <file>] [--json]\",\n leaderboard: \"leaderboard [--since <dur>] [--connector <id>] [--scope <slice>] [--json]\",\n hook: \"hook <platform> <event> --connector <id> (internal — host hook configs point here)\",\n serve:\n \"serve --connector <id> [--scope user|project] [--host <platformId>] -- <command> [args...] (internal — host MCP entries point here)\",\n};\n\n/**\n * Build the top-level usage string for a given program name. The brand replaces\n * \"agent-connector\" in the title, the `usage:` line, and the per-command help\n * footer so an embedded CLI (e.g. `acme-db`) reads as its own tool.\n */\nfunction buildUsage(programName: string): string {\n return `${programName} — write your MCP server + hooks once, install everywhere.\n\nusage: ${programName} <command> [flags]\n\ncommands:\n detect List the AI-agent platforms installed on this machine.\n install Deploy a connector across its target platforms (--method direct writes host config; --method marketplace drives the host's plugin install).\n uninstall Remove a connector's registrations (--method auto reverses whichever method is actually installed).\n upgrade Bring everything current: re-render host config + heal the home pointer + managed update guidance (alias: update, sync).\n package Emit a marketplace/extension bundle (9 host formats, or the standard artifacts mcp-server-json | mcpb).\n doctor Health-check every detected platform; non-zero exit on any failure.\n status Light install-state summary: which connectors are present on which hosts (always exits 0).\n telemetry Inspect local per-tool token telemetry (report | export | leaderboard).\n usage Inspect host-native token usage from agent CLI logs (report | export | leaderboard).\n leaderboard Three leaderboards: 🔌 MCP/plugin (mcp-self) + 🖥️ host/user (host-scan-logs) + 🛰️ host-native turns (host-native-live) — never summed.\n hook Universal json-stdio hook entrypoint (hosts call this).\n serve Telemetry-wrapping MCP stdio proxy (wraps a real server command).\n\nRun \\`${programName} <command> --help\\` for command-specific flags.`;\n}\n\n/** Options accepted by {@link main}. */\nexport interface MainOptions {\n /**\n * The brand shown in usage/help text. Defaults to {@link DEFAULT_PROGRAM_NAME}.\n * An embedding SDK CLI (see cli/sdk.ts) passes its own bin name so every\n * subcommand's help reads as the developer's tool.\n */\n programName?: string;\n}\n\nexport async function main(argv: string[], opts: MainOptions = {}): Promise<number> {\n const programName = opts.programName ?? DEFAULT_PROGRAM_NAME;\n activeProgramName = programName; // brand every fail() in this invocation\n const usage = buildUsage(programName);\n const command = argv[0];\n\n if (command == null || command === \"--help\" || command === \"-h\" || command === \"help\") {\n print(usage);\n return command == null ? 1 : 0;\n }\n if (command === \"--version\" || command === \"-v\") {\n print(`${programName} ${resolveOwnVersion()}`);\n return 0;\n }\n\n const loader = COMMANDS[command];\n if (!loader) {\n process.stderr.write(`${programName}: unknown command \"${command}\"\\n\\n`);\n process.stderr.write(`${usage}\\n`);\n return 2;\n }\n\n // Per-command help: no command module defines a --help flag (strict parseArgs\n // would throw on it), so answer it centrally from COMMAND_USAGE.\n const rest = argv.slice(1);\n if (rest.includes(\"--help\") || rest.includes(\"-h\")) {\n const sig = COMMAND_USAGE[command];\n if (sig) {\n print(`usage: ${programName} ${sig}`);\n return 0;\n }\n }\n\n const mod = await loader();\n try {\n return await mod.run(rest);\n } catch (err) {\n // Friendly bad-flag errors: strict parseArgs throws ERR_PARSE_ARGS_* for an\n // unknown/malformed option — print the message + the usage line instead of\n // letting a raw stack trace reach the user. Everything else still rethrows\n // (the bin entry reports those as fatal).\n const code = (err as { code?: string }).code;\n if (typeof code === \"string\" && code.startsWith(\"ERR_PARSE_ARGS\")) {\n const message = err instanceof Error ? err.message : String(err);\n const sig = COMMAND_USAGE[command];\n if (sig) process.stderr.write(`usage: ${programName} ${sig}\\n`);\n return fail(message);\n }\n throw err;\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ resolveSpawnCommand
4
+ } from "./chunk-MEH7GC7R.js";
2
5
 
3
6
  // src/core/logger.ts
4
7
  var ORDER = {
@@ -24,7 +27,20 @@ var log = {
24
27
  debug: (...args) => emit("debug", args)
25
28
  };
26
29
 
30
+ // src/core/spawn-child.ts
31
+ import { spawn } from "child_process";
32
+ function winQuoteArg(s) {
33
+ return `"${s.replace(/"/g, '""')}"`;
34
+ }
35
+ function spawnChild(command, args, options) {
36
+ const resolved = resolveSpawnCommand(command);
37
+ if (!resolved.needsShell) return spawn(resolved.file, args, options);
38
+ const line = [resolved.file, ...args].map(winQuoteArg).join(" ");
39
+ return spawn(line, { ...options, shell: true });
40
+ }
41
+
27
42
  export {
28
- log
43
+ log,
44
+ spawnChild
29
45
  };
30
- //# sourceMappingURL=chunk-KHO5WNTP.js.map
46
+ //# sourceMappingURL=chunk-KVAAFYRR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/logger.ts","../src/core/spawn-child.ts"],"sourcesContent":["/**\n * core/logger — minimal leveled logger. Writes human output to stderr so stdout\n * stays clean for machine-readable payloads (hook replies, --json output).\n */\n\nexport type LogLevel = \"silent\" | \"error\" | \"warn\" | \"info\" | \"debug\";\n\nconst ORDER: Record<LogLevel, number> = {\n silent: 0,\n error: 1,\n warn: 2,\n info: 3,\n debug: 4,\n};\n\nfunction currentLevel(): LogLevel {\n const env = (process.env.AGENT_CONNECTOR_LOG ?? \"info\").toLowerCase();\n return (env in ORDER ? env : \"info\") as LogLevel;\n}\n\nfunction emit(level: Exclude<LogLevel, \"silent\">, args: unknown[]): void {\n if (ORDER[currentLevel()] < ORDER[level]) return;\n const prefix = { error: \"✗\", warn: \"!\", info: \"›\", debug: \"·\" }[level];\n // eslint-disable-next-line no-console\n console.error(prefix, ...args);\n}\n\nexport const log = {\n error: (...args: unknown[]) => emit(\"error\", args),\n warn: (...args: unknown[]) => emit(\"warn\", args),\n info: (...args: unknown[]) => emit(\"info\", args),\n debug: (...args: unknown[]) => emit(\"debug\", args),\n};\n","/**\n * core/spawn-child — the cross-platform child_process.spawn wrapper.\n *\n * Kept SEPARATE from core/spawn.ts (pure string/path helpers, imported widely\n * and early) so that pulling in `node:child_process` here does not perturb the\n * module-eval order of test suites that vi.mock(\"node:child_process\").\n */\n\nimport { type ChildProcess, type SpawnOptions, spawn } from \"node:child_process\";\n\nimport { resolveSpawnCommand } from \"./spawn.js\";\n\n/**\n * Quote one token for a cmd.exe command line: wrap in double quotes, doubling any\n * embedded double quote. Sufficient for the paths / package names / flags we\n * spawn (connector config, not untrusted end-user input) — we intentionally do\n * not escape cmd metacharacters (& | ^ %), which never appear in those tokens.\n */\nfunction winQuoteArg(s: string): string {\n return `\"${s.replace(/\"/g, '\"\"')}\"`;\n}\n\n/**\n * Cross-platform child spawn that fixes native-Windows package-runner launches.\n * Direct spawn on macOS/Linux. On win32 it routes through\n * {@link resolveSpawnCommand}; an `.exe` runs directly, while a `.cmd`/`.bat`\n * (npx.cmd, the agent-connector.cmd launcher) is launched via a SINGLE quoted\n * command line with `shell: true` — a string rather than an args array, which\n * Node requires for a batch file AND which avoids the DEP0190 deprecation that\n * `shell: true` + an args array triggers on Node ≥ 24.\n */\nexport function spawnChild(\n command: string,\n args: string[],\n options: SpawnOptions,\n): ChildProcess {\n const resolved = resolveSpawnCommand(command);\n if (!resolved.needsShell) return spawn(resolved.file, args, options);\n const line = [resolved.file, ...args].map(winQuoteArg).join(\" \");\n return spawn(line, { ...options, shell: true });\n}\n"],"mappings":";;;;;;AAOA,IAAM,QAAkC;AAAA,EACtC,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AACT;AAEA,SAAS,eAAyB;AAChC,QAAM,OAAO,QAAQ,IAAI,uBAAuB,QAAQ,YAAY;AACpE,SAAQ,OAAO,QAAQ,MAAM;AAC/B;AAEA,SAAS,KAAK,OAAoC,MAAuB;AACvE,MAAI,MAAM,aAAa,CAAC,IAAI,MAAM,KAAK,EAAG;AAC1C,QAAM,SAAS,EAAE,OAAO,UAAK,MAAM,KAAK,MAAM,UAAK,OAAO,OAAI,EAAE,KAAK;AAErE,UAAQ,MAAM,QAAQ,GAAG,IAAI;AAC/B;AAEO,IAAM,MAAM;AAAA,EACjB,OAAO,IAAI,SAAoB,KAAK,SAAS,IAAI;AAAA,EACjD,MAAM,IAAI,SAAoB,KAAK,QAAQ,IAAI;AAAA,EAC/C,MAAM,IAAI,SAAoB,KAAK,QAAQ,IAAI;AAAA,EAC/C,OAAO,IAAI,SAAoB,KAAK,SAAS,IAAI;AACnD;;;ACxBA,SAA+C,aAAa;AAU5D,SAAS,YAAY,GAAmB;AACtC,SAAO,IAAI,EAAE,QAAQ,MAAM,IAAI,CAAC;AAClC;AAWO,SAAS,WACd,SACA,MACA,SACc;AACd,QAAM,WAAW,oBAAoB,OAAO;AAC5C,MAAI,CAAC,SAAS,WAAY,QAAO,MAAM,SAAS,MAAM,MAAM,OAAO;AACnE,QAAM,OAAO,CAAC,SAAS,MAAM,GAAG,IAAI,EAAE,IAAI,WAAW,EAAE,KAAK,GAAG;AAC/D,SAAO,MAAM,MAAM,EAAE,GAAG,SAAS,OAAO,KAAK,CAAC;AAChD;","names":[]}
@@ -30,7 +30,7 @@ var ADAPTER_REGISTRY = [
30
30
  },
31
31
  {
32
32
  id: "warp",
33
- load: () => import("./warp-LLOSXR7A.js").then((m) => m.default)
33
+ load: () => import("./warp-X6MB67NT.js").then((m) => m.default)
34
34
  },
35
35
  // ts-plugin paradigm: OpenCode loads a generated plugin module that bridges
36
36
  // its in-process events to the universal hook entrypoint.
@@ -45,7 +45,7 @@ var ADAPTER_REGISTRY = [
45
45
  // the "kilo" extension so there is no fork-ordering constraint between them.
46
46
  {
47
47
  id: "kilo-cli",
48
- load: () => import("./kilo-cli-2RNODHWY.js").then((m) => m.default)
48
+ load: () => import("./kilo-cli-QWUVXG2O.js").then((m) => m.default)
49
49
  },
50
50
  // Wave 1 — mcp-only adapters (no lifecycle hooks; MCP registration only).
51
51
  {
@@ -61,7 +61,7 @@ var ADAPTER_REGISTRY = [
61
61
  // globalStorage / .kilocode project scope). Distinct host from "kilo-cli".
62
62
  {
63
63
  id: "kilo",
64
- load: () => import("./kilo-ISR5FBBJ.js").then((m) => m.default)
64
+ load: () => import("./kilo-PKAJCWPX.js").then((m) => m.default)
65
65
  },
66
66
  {
67
67
  id: "trae",
@@ -81,7 +81,7 @@ var ADAPTER_REGISTRY = [
81
81
  },
82
82
  {
83
83
  id: "zed",
84
- load: () => import("./zed-HYOITK7F.js").then((m) => m.default)
84
+ load: () => import("./zed-NTCYW4V2.js").then((m) => m.default)
85
85
  },
86
86
  {
87
87
  id: "amp",
@@ -99,7 +99,7 @@ var ADAPTER_REGISTRY = [
99
99
  // surface only (writes <piDir>/skills/<name>/SKILL.md).
100
100
  {
101
101
  id: "pi",
102
- load: () => import("./pi-SJPIL4VE.js").then((m) => m.default)
102
+ load: () => import("./pi-WMTUX6ES.js").then((m) => m.default)
103
103
  },
104
104
  // Wave 2 — json-stdio adapters (full hook dispatch via the universal entrypoint).
105
105
  // jetbrains-copilot before vscode-copilot is unnecessary (distinct hosts); grouped here.
@@ -109,15 +109,15 @@ var ADAPTER_REGISTRY = [
109
109
  },
110
110
  {
111
111
  id: "qwen-code",
112
- load: () => import("./qwen-code-IFCM6BJY.js").then((m) => m.default)
112
+ load: () => import("./qwen-code-MRRIUQ3P.js").then((m) => m.default)
113
113
  },
114
114
  {
115
115
  id: "kiro",
116
- load: () => import("./kiro-EKTRW424.js").then((m) => m.default)
116
+ load: () => import("./kiro-UQRY6EIO.js").then((m) => m.default)
117
117
  },
118
118
  {
119
119
  id: "kimi",
120
- load: () => import("./kimi-Q5RG7CYT.js").then((m) => m.default)
120
+ load: () => import("./kimi-EVD7YKXS.js").then((m) => m.default)
121
121
  },
122
122
  {
123
123
  id: "crush",
@@ -235,4 +235,4 @@ export {
235
235
  detectInstalledPlatforms,
236
236
  detectRuntimeHost
237
237
  };
238
- //# sourceMappingURL=chunk-O2SBA5KR.js.map
238
+ //# sourceMappingURL=chunk-NJU275US.js.map
@@ -1,19 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- resolveSpawnCommand
4
- } from "./chunk-MEH7GC7R.js";
5
-
6
- // src/core/spawn-child.ts
7
- import { spawn } from "child_process";
8
- function winQuoteArg(s) {
9
- return `"${s.replace(/"/g, '""')}"`;
10
- }
11
- function spawnChild(command, args, options) {
12
- const resolved = resolveSpawnCommand(command);
13
- if (!resolved.needsShell) return spawn(resolved.file, args, options);
14
- const line = [resolved.file, ...args].map(winQuoteArg).join(" ");
15
- return spawn(line, { ...options, shell: true });
16
- }
17
2
 
18
3
  // src/telemetry/jsonrpc.ts
19
4
  import { StringDecoder } from "string_decoder";
@@ -51,9 +36,8 @@ var LineBuffer = class {
51
36
  };
52
37
 
53
38
  export {
54
- spawnChild,
55
39
  isObject,
56
40
  idKey,
57
41
  LineBuffer
58
42
  };
59
- //# sourceMappingURL=chunk-WCETQWWK.js.map
43
+ //# sourceMappingURL=chunk-OP4KBXUO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/telemetry/jsonrpc.ts"],"sourcesContent":["/**\n * telemetry/jsonrpc — shared MCP stdio wire primitives.\n *\n * The MCP stdio transport is NEWLINE-DELIMITED JSON-RPC: one single-line JSON\n * object per message, terminated by `\\n` (no Content-Length framing). These\n * primitives — the message shape, the id key, the line buffer — are used by BOTH\n * the telemetry proxy (which tees + measures) and `doctor --probe` (which sends\n * a real initialize/ping/tools-list handshake), so framing + id-correlation are\n * byte-identical across the two paths. Extracted here rather than duplicated.\n */\n\nimport { StringDecoder } from \"node:string_decoder\";\n\n/** Any newline-delimited JSON-RPC message — request, response, or notification. */\nexport interface JsonRpcMessage {\n jsonrpc?: unknown;\n id?: unknown;\n method?: unknown;\n params?: unknown;\n result?: unknown;\n error?: unknown;\n}\n\nexport function isObject(v: unknown): v is Record<string, unknown> {\n return typeof v === \"object\" && v !== null;\n}\n\n/** JSON-RPC ids are number | string; normalize to a Map key. `null` → no key. */\nexport function idKey(id: unknown): string | null {\n if (typeof id === \"string\") return `s:${id}`;\n if (typeof id === \"number\") return `n:${id}`;\n return null;\n}\n\n/**\n * Buffer a byte stream into complete `\\n`-terminated lines and invoke `onLine`\n * for each. Carriage returns are tolerated (some hosts emit CRLF). This is a\n * pure side-channel: it never owns or mutates the bytes being forwarded — the\n * caller has already written the original chunk downstream verbatim.\n */\nexport class LineBuffer {\n private buf = \"\";\n private readonly onLine: (line: string) => void;\n // Incremental UTF-8 decoder: `data` events split on arbitrary byte\n // boundaries, so a multi-byte sequence (emoji/CJK in a tool arg or result)\n // can straddle two chunks. StringDecoder holds back a trailing partial\n // sequence until it completes, keeping the measured copy byte-accurate.\n // (The forwarded stream is untouched — this is only the tee copy.)\n private readonly decoder = new StringDecoder(\"utf8\");\n\n constructor(onLine: (line: string) => void) {\n this.onLine = onLine;\n }\n\n push(chunk: Buffer): void {\n this.buf += this.decoder.write(chunk);\n let nl = this.buf.indexOf(\"\\n\");\n while (nl !== -1) {\n // Slice off one line (without the newline), tolerate a trailing CR.\n let line = this.buf.slice(0, nl);\n if (line.endsWith(\"\\r\")) line = line.slice(0, -1);\n if (line.length > 0) this.onLine(line);\n this.buf = this.buf.slice(nl + 1);\n nl = this.buf.indexOf(\"\\n\");\n }\n }\n}\n"],"mappings":";;;AAWA,SAAS,qBAAqB;AAYvB,SAAS,SAAS,GAA0C;AACjE,SAAO,OAAO,MAAM,YAAY,MAAM;AACxC;AAGO,SAAS,MAAM,IAA4B;AAChD,MAAI,OAAO,OAAO,SAAU,QAAO,KAAK,EAAE;AAC1C,MAAI,OAAO,OAAO,SAAU,QAAO,KAAK,EAAE;AAC1C,SAAO;AACT;AAQO,IAAM,aAAN,MAAiB;AAAA,EACd,MAAM;AAAA,EACG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,UAAU,IAAI,cAAc,MAAM;AAAA,EAEnD,YAAY,QAAgC;AAC1C,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,KAAK,OAAqB;AACxB,SAAK,OAAO,KAAK,QAAQ,MAAM,KAAK;AACpC,QAAI,KAAK,KAAK,IAAI,QAAQ,IAAI;AAC9B,WAAO,OAAO,IAAI;AAEhB,UAAI,OAAO,KAAK,IAAI,MAAM,GAAG,EAAE;AAC/B,UAAI,KAAK,SAAS,IAAI,EAAG,QAAO,KAAK,MAAM,GAAG,EAAE;AAChD,UAAI,KAAK,SAAS,EAAG,MAAK,OAAO,IAAI;AACrC,WAAK,MAAM,KAAK,IAAI,MAAM,KAAK,CAAC;AAChC,WAAK,KAAK,IAAI,QAAQ,IAAI;AAAA,IAC5B;AAAA,EACF;AACF;","names":[]}