@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
@@ -0,0 +1,1989 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ installInstructions,
4
+ packageConnector
5
+ } from "./chunk-WNREHWKL.js";
6
+ import {
7
+ MARKETPLACE_NAME,
8
+ agyPluginInstalled,
9
+ agyStagingRoot,
10
+ anyClaudeAgentConnectorPlugins,
11
+ anyCodexAgentConnectorPlugins,
12
+ anyMarketplaceRecordsRemain,
13
+ claudeKnownMarketplacePath,
14
+ claudePluginInstalled,
15
+ claudePluginKey,
16
+ claudeStagingRoot,
17
+ codexMarketplaceSource,
18
+ codexPluginInstalled,
19
+ codexPluginKey,
20
+ codexStagingRoot,
21
+ connectorHasMarketplaceRecords,
22
+ hashDirectory,
23
+ marketplaceEvidence,
24
+ marketplaceRoot,
25
+ readMarketplaceInstalls,
26
+ writeMarketplaceInstalls
27
+ } from "./chunk-44HUXXC6.js";
28
+ import {
29
+ log,
30
+ spawnChild
31
+ } from "./chunk-KVAAFYRR.js";
32
+ import {
33
+ REGISTERED_PLATFORM_IDS,
34
+ detectInstalledPlatforms,
35
+ loadAdapter
36
+ } from "./chunk-NJU275US.js";
37
+ import {
38
+ deregisterConnector,
39
+ loadRegisteredConnector,
40
+ registerConnector
41
+ } from "./chunk-3JCRUROU.js";
42
+ import {
43
+ configPatchManualEdit
44
+ } from "./chunk-GSGDG2BV.js";
45
+ import {
46
+ hasMemoryLedger
47
+ } from "./chunk-N6HH7K6P.js";
48
+ import {
49
+ connectorDir,
50
+ connectorsDir,
51
+ dataRoot,
52
+ ensureDir,
53
+ ensureHomeBin,
54
+ homeBinPath
55
+ } from "./chunk-TRQKHP7D.js";
56
+
57
+ // src/core/installer.ts
58
+ import { existsSync, readdirSync, rmSync } from "fs";
59
+ import { dirname, join } from "path";
60
+ import { fileURLToPath } from "url";
61
+ async function installConnector(opts) {
62
+ const { connector, modulePath, scope, projectDir, dryRun, force } = opts;
63
+ if (!dryRun) {
64
+ try {
65
+ ensureHomeBin(resolveCliEntry());
66
+ } catch (err) {
67
+ log.warn(`ensureHomeBin failed: ${errMessage(err)}`);
68
+ }
69
+ try {
70
+ registerConnector(connector, modulePath);
71
+ } catch (err) {
72
+ log.warn(`registerConnector failed: ${errMessage(err)}`);
73
+ }
74
+ }
75
+ const targets = await resolveTargets(opts.targets, connector.targets, projectDir);
76
+ const result = newResult(connector.id, dryRun);
77
+ if (targets.length === 0) {
78
+ result.warnings.push(
79
+ "no target platforms resolved (none installed / detected, or all filtered out)"
80
+ );
81
+ return result;
82
+ }
83
+ for (const id of targets) {
84
+ const evidence = marketplaceEvidence(connector.id, id);
85
+ if (evidence) {
86
+ const detail = `"${connector.id}" is already installed via MARKETPLACE on ${id} (${evidence}) \u2014 refusing the direct install. Run \`agent-connector uninstall --method marketplace --targets ${id}\` first, or keep the marketplace install.`;
87
+ result.changes.push({ platform: id, action: "warn", detail });
88
+ result.warnings.push(detail);
89
+ continue;
90
+ }
91
+ const adapter = await tryLoadAdapter(id, result);
92
+ if (!adapter) continue;
93
+ const ctx = buildContext(connector, id, scope, projectDir, dryRun, force);
94
+ if (!dryRun) {
95
+ runStep(id, "backupSettings", result, () => {
96
+ const backup = adapter.backupSettings(ctx);
97
+ if (backup) {
98
+ result.changes.push({
99
+ platform: id,
100
+ action: "create",
101
+ path: backup,
102
+ detail: "backed up settings before install"
103
+ });
104
+ }
105
+ });
106
+ }
107
+ runStep(id, "installServer", result, () => {
108
+ pushAll(result.changes, adapter.installServer(ctx));
109
+ });
110
+ runStep(id, "installHooks", result, () => {
111
+ pushAll(result.changes, adapter.installHooks(ctx));
112
+ });
113
+ const nativeHooks = connector.platforms[id]?.nativeHooks;
114
+ const nativeCount = nativeHooks ? Object.keys(nativeHooks).length : 0;
115
+ if (nativeCount > 0 && !(adapter.capabilities.supportsNativeHooks ?? false)) {
116
+ result.changes.push({
117
+ platform: id,
118
+ action: "warn",
119
+ detail: `nativeHooks not supported on ${id}; ${nativeCount} skipped`
120
+ });
121
+ }
122
+ if (connector.commands.length) {
123
+ runStep(id, "installCommands", result, () => {
124
+ pushAll(result.changes, adapter.installCommands(ctx));
125
+ });
126
+ }
127
+ if (connector.skills.length) {
128
+ runStep(id, "installSkills", result, () => {
129
+ pushAll(result.changes, adapter.installSkills(ctx));
130
+ });
131
+ }
132
+ if (connector.subagents.length) {
133
+ runStep(id, "installSubagents", result, () => {
134
+ pushAll(result.changes, adapter.installSubagents(ctx));
135
+ });
136
+ }
137
+ if ((connector.memory ?? []).length) {
138
+ runStep(id, "installMemory", result, () => {
139
+ pushAll(result.changes, adapter.installMemory(ctx));
140
+ });
141
+ }
142
+ const configPatches = connector.platforms[id]?.configPatch ?? [];
143
+ if (configPatches.length > 0) {
144
+ if (adapter.capabilities.supportsConfigPatch ?? false) {
145
+ runStep(id, "installConfigPatches", result, () => {
146
+ pushAll(result.changes, adapter.installConfigPatches(ctx));
147
+ });
148
+ } else {
149
+ result.changes.push({
150
+ platform: id,
151
+ action: "warn",
152
+ detail: `configPatch not supported on ${id}; ${configPatches.length} skipped`
153
+ });
154
+ for (const patch of configPatches) {
155
+ result.changes.push({
156
+ platform: id,
157
+ action: "warn",
158
+ detail: `configPatch ${patch.key} skipped on ${id} \u2014 ${configPatchManualEdit(patch)}`
159
+ });
160
+ }
161
+ }
162
+ }
163
+ }
164
+ return result;
165
+ }
166
+ async function syncConnector(opts) {
167
+ return installConnector(opts);
168
+ }
169
+ async function uninstallConnector(opts) {
170
+ const { connectorId, scope, projectDir, dryRun, purge } = opts;
171
+ const connector = await loadConnectorForUninstall(connectorId);
172
+ const result = newResult(connectorId, dryRun);
173
+ const targets = await resolveTargets(opts.targets, connector.targets, projectDir);
174
+ if (targets.length === 0) {
175
+ result.warnings.push(
176
+ "no target platforms resolved for uninstall (none installed / detected, or all filtered out)"
177
+ );
178
+ if (purge) purgeFrameworkState(connectorId, dryRun, result);
179
+ return result;
180
+ }
181
+ for (const id of targets) {
182
+ const adapter = await tryLoadAdapter(id, result);
183
+ if (!adapter) continue;
184
+ const ctx = buildContext(connector, id, scope, projectDir, dryRun);
185
+ if (adapter.capabilities.supportsConfigPatch ?? false) {
186
+ runStep(id, "uninstallConfigPatches", result, () => {
187
+ pushAll(result.changes, adapter.uninstallConfigPatches(ctx));
188
+ });
189
+ }
190
+ if ((connector.memory ?? []).length > 0 || hasMemoryLedger(connector.id)) {
191
+ runStep(id, "uninstallMemory", result, () => {
192
+ pushAll(result.changes, adapter.uninstallMemory(ctx));
193
+ });
194
+ }
195
+ if (connector.subagents.length) {
196
+ runStep(id, "uninstallSubagents", result, () => {
197
+ pushAll(result.changes, adapter.uninstallSubagents(ctx));
198
+ });
199
+ }
200
+ if (connector.commands.length) {
201
+ runStep(id, "uninstallCommands", result, () => {
202
+ pushAll(result.changes, adapter.uninstallCommands(ctx));
203
+ });
204
+ }
205
+ if (connector.skills.length) {
206
+ runStep(id, "uninstallSkills", result, () => {
207
+ pushAll(result.changes, adapter.uninstallSkills(ctx));
208
+ });
209
+ }
210
+ runStep(id, "uninstallHooks", result, () => {
211
+ pushAll(result.changes, adapter.uninstallHooks(ctx));
212
+ });
213
+ runStep(id, "uninstallServer", result, () => {
214
+ pushAll(result.changes, adapter.uninstallServer(ctx));
215
+ });
216
+ }
217
+ if (purge) purgeFrameworkState(connectorId, dryRun, result);
218
+ return result;
219
+ }
220
+ function purgeFrameworkState(connectorId, dryRun, result) {
221
+ const platform = connectorId;
222
+ if (connectorHasMarketplaceRecords(connectorId)) {
223
+ const detail = `--purge skipped: "${connectorId}" still records marketplace installs \u2014 run \`agent-connector uninstall --method marketplace\` first`;
224
+ result.changes.push({ platform, action: "warn", detail });
225
+ result.warnings.push(detail);
226
+ return;
227
+ }
228
+ const recordDir = connectorDir(connectorId);
229
+ const recordExisted = existsSync(recordDir);
230
+ if (recordExisted) {
231
+ if (!dryRun) {
232
+ try {
233
+ deregisterConnector(connectorId);
234
+ } catch (err) {
235
+ log.warn(`deregisterConnector failed: ${errMessage(err)}`);
236
+ }
237
+ }
238
+ result.changes.push({
239
+ platform,
240
+ action: "remove",
241
+ path: recordDir,
242
+ detail: "deregistered connector record"
243
+ });
244
+ }
245
+ if (anyMarketplaceRecordsRemain(connectorId)) {
246
+ return;
247
+ }
248
+ if (noConnectorsRemain(connectorId, dryRun, recordExisted)) {
249
+ const binPath = homeBinPath();
250
+ if (existsSync(binPath)) {
251
+ if (!dryRun) {
252
+ try {
253
+ rmSync(binPath, { force: true });
254
+ } catch (err) {
255
+ log.warn(`home-bin removal failed: ${errMessage(err)}`);
256
+ }
257
+ }
258
+ result.changes.push({
259
+ platform,
260
+ action: "remove",
261
+ path: binPath,
262
+ detail: "removed home-bin (no connectors remain)"
263
+ });
264
+ }
265
+ }
266
+ }
267
+ function noConnectorsRemain(connectorId, dryRun, recordExisted) {
268
+ const dir = connectorsDir();
269
+ if (!existsSync(dir)) return true;
270
+ let entries;
271
+ try {
272
+ entries = readdirSync(dir);
273
+ } catch {
274
+ return false;
275
+ }
276
+ const remaining = entries.filter(
277
+ (e) => !(dryRun && recordExisted && e === connectorId)
278
+ );
279
+ return remaining.length === 0;
280
+ }
281
+ function resolveCliEntry() {
282
+ try {
283
+ let dir = dirname(fileURLToPath(import.meta.url));
284
+ for (let i = 0; i < 8; i++) {
285
+ if (existsSync(join(dir, "package.json"))) {
286
+ const cli = join(dir, "dist", "cli.js");
287
+ if (existsSync(cli)) return cli;
288
+ }
289
+ const parent = dirname(dir);
290
+ if (parent === dir) break;
291
+ dir = parent;
292
+ }
293
+ const sameDir = fileURLToPath(new URL("./cli.js", import.meta.url));
294
+ if (existsSync(sameDir)) return sameDir;
295
+ } catch {
296
+ }
297
+ const argv1 = process.argv[1];
298
+ if (argv1 && argv1.length > 0) return argv1;
299
+ return "cli.js";
300
+ }
301
+ async function resolveTargets(flagTargets, connectorTargets, projectDir) {
302
+ let requested;
303
+ if (flagTargets && flagTargets.length > 0) {
304
+ requested = flagTargets;
305
+ } else if (Array.isArray(connectorTargets) && connectorTargets.length > 0) {
306
+ requested = connectorTargets;
307
+ } else {
308
+ requested = await autoDetectIds(projectDir);
309
+ }
310
+ const seen = /* @__PURE__ */ new Set();
311
+ const out = [];
312
+ for (const id of requested) {
313
+ if (!REGISTERED_PLATFORM_IDS.has(id)) continue;
314
+ if (seen.has(id)) continue;
315
+ seen.add(id);
316
+ out.push(id);
317
+ }
318
+ return out;
319
+ }
320
+ async function autoDetectIds(projectDir) {
321
+ try {
322
+ const detected = await detectInstalledPlatforms(projectDir);
323
+ return detected.filter((p) => p.installed).map((p) => p.id);
324
+ } catch (err) {
325
+ log.warn(`platform detection failed: ${errMessage(err)}`);
326
+ return [];
327
+ }
328
+ }
329
+ function buildContext(connector, id, defaultScope, projectDir, dryRun, force = false) {
330
+ return {
331
+ connector,
332
+ scope: connector.platforms[id]?.scope ?? defaultScope,
333
+ projectDir,
334
+ homeBinPath: homeBinPath(),
335
+ dataRoot: dataRoot(),
336
+ dryRun,
337
+ force
338
+ };
339
+ }
340
+ async function tryLoadAdapter(id, result) {
341
+ try {
342
+ const adapter = await loadAdapter(id);
343
+ if (adapter) return adapter;
344
+ const detail = `no adapter registered for ${id}`;
345
+ result.changes.push({ platform: id, action: "warn", detail });
346
+ result.warnings.push(detail);
347
+ return null;
348
+ } catch (err) {
349
+ const detail = `failed to load adapter for ${id}: ${errMessage(err)}`;
350
+ result.changes.push({ platform: id, action: "warn", detail });
351
+ result.warnings.push(detail);
352
+ return null;
353
+ }
354
+ }
355
+ function runStep(id, step, result, fn) {
356
+ try {
357
+ fn();
358
+ } catch (err) {
359
+ const detail = `${step} failed on ${id}: ${errMessage(err)}`;
360
+ result.changes.push({ platform: id, action: "warn", detail });
361
+ result.warnings.push(detail);
362
+ }
363
+ }
364
+ async function resolveUninstallTargets(connectorId, flagTargets, projectDir) {
365
+ const connector = await loadConnectorForUninstall(connectorId);
366
+ return resolveTargets(flagTargets, connector.targets, projectDir);
367
+ }
368
+ async function loadConnectorForUninstall(connectorId) {
369
+ try {
370
+ return await loadRegisteredConnector(connectorId);
371
+ } catch (err) {
372
+ log.warn(
373
+ `could not load registered connector "${connectorId}" for uninstall (${errMessage(err)}); proceeding with a minimal id-only context`
374
+ );
375
+ return syntheticConnector(connectorId);
376
+ }
377
+ }
378
+ function syntheticConnector(id) {
379
+ return {
380
+ id,
381
+ displayName: id,
382
+ version: "0.0.0",
383
+ hooks: {},
384
+ hookEvents: [],
385
+ telemetry: {
386
+ enabled: true,
387
+ modelFamilyHint: "auto",
388
+ measureToolDefs: true,
389
+ hostNativeUsage: false,
390
+ store: "ndjson",
391
+ calibration: { anthropicCountTokens: false }
392
+ },
393
+ commands: [],
394
+ skills: [],
395
+ subagents: [],
396
+ memory: [],
397
+ platforms: {},
398
+ targets: "auto"
399
+ };
400
+ }
401
+ function newResult(connectorId, dryRun) {
402
+ return { connectorId, dryRun, changes: [], warnings: [] };
403
+ }
404
+ function pushAll(target, records) {
405
+ for (const r of records) target.push(r);
406
+ }
407
+ function errMessage(err) {
408
+ return err instanceof Error ? err.message : String(err);
409
+ }
410
+
411
+ // src/core/marketplace.ts
412
+ import { existsSync as existsSync6, readdirSync as readdirSync4, readFileSync as readFileSync3, rmSync as rmSync5 } from "fs";
413
+ import { join as join6 } from "path";
414
+
415
+ // src/core/marketplace-drivers/claude.ts
416
+ import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync, rmSync as rmSync2, writeFileSync } from "fs";
417
+ import { dirname as dirname2, join as join3 } from "path";
418
+
419
+ // src/core/marketplace-drivers/shared.ts
420
+ import { existsSync as existsSync2, statSync } from "fs";
421
+ import { delimiter, join as join2, resolve } from "path";
422
+ var HOST_COMMAND_TIMEOUT_MS = 3e4;
423
+ function findOnPath(command) {
424
+ const pathVar = process.env.PATH ?? "";
425
+ const exts = process.platform === "win32" ? (process.env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").concat("") : [""];
426
+ for (const dir of pathVar.split(delimiter)) {
427
+ if (dir === "") continue;
428
+ for (const ext of exts) {
429
+ const candidate = join2(dir, command + ext.toLowerCase());
430
+ try {
431
+ if (existsSync2(candidate) && statSync(candidate).isFile()) {
432
+ return candidate;
433
+ }
434
+ } catch {
435
+ }
436
+ }
437
+ }
438
+ return null;
439
+ }
440
+ function runHostCommand(command, args, timeoutMs = HOST_COMMAND_TIMEOUT_MS) {
441
+ return new Promise((resolvePromise) => {
442
+ let stdout = "";
443
+ let stderr = "";
444
+ let timedOut = false;
445
+ let settled = false;
446
+ const settle = (result) => {
447
+ if (settled) return;
448
+ settled = true;
449
+ clearTimeout(timer);
450
+ resolvePromise(result);
451
+ };
452
+ let child;
453
+ try {
454
+ child = spawnChild(command, args, {
455
+ stdio: ["ignore", "pipe", "pipe"],
456
+ env: process.env
457
+ });
458
+ } catch (err) {
459
+ const message = err instanceof Error ? err.message : String(err);
460
+ resolvePromise({
461
+ ok: false,
462
+ code: null,
463
+ stdout: "",
464
+ stderr: "",
465
+ timedOut: false,
466
+ error: message
467
+ });
468
+ return;
469
+ }
470
+ const timer = setTimeout(() => {
471
+ timedOut = true;
472
+ try {
473
+ child.kill("SIGKILL");
474
+ } catch {
475
+ }
476
+ }, timeoutMs);
477
+ child.stdout?.on("data", (chunk) => {
478
+ stdout += chunk.toString("utf8");
479
+ });
480
+ child.stderr?.on("data", (chunk) => {
481
+ stderr += chunk.toString("utf8");
482
+ });
483
+ child.on("error", (err) => {
484
+ settle({
485
+ ok: false,
486
+ code: null,
487
+ stdout,
488
+ stderr,
489
+ timedOut,
490
+ error: err.message
491
+ });
492
+ });
493
+ child.on("close", (code) => {
494
+ settle({
495
+ ok: code === 0 && !timedOut,
496
+ code,
497
+ stdout,
498
+ stderr,
499
+ timedOut
500
+ });
501
+ });
502
+ });
503
+ }
504
+ function firstLine(text) {
505
+ const line = text.split(/\r?\n/).find((l) => l.trim() !== "");
506
+ return line ? line.trim() : "";
507
+ }
508
+ function samePath(a, b) {
509
+ if (a == null || b == null) return false;
510
+ const norm = (p) => {
511
+ let s = p;
512
+ if (process.platform === "win32") {
513
+ s = s.replace(/^\\\\\?\\UNC\\/, "\\\\").replace(/^\\\\\?\\/, "");
514
+ }
515
+ s = resolve(s);
516
+ return process.platform === "win32" ? s.toLowerCase() : s;
517
+ };
518
+ return norm(a) === norm(b);
519
+ }
520
+
521
+ // src/core/marketplace-drivers/claude.ts
522
+ var PLATFORM = "claude-code";
523
+ function claudeBinary() {
524
+ return findOnPath("claude");
525
+ }
526
+ function claudeManualInstallCommands(connectorId, stagingRoot) {
527
+ return `claude plugin marketplace add ${stagingRoot} && claude plugin install ${claudePluginKey(connectorId)}`;
528
+ }
529
+ function warn(detail, path) {
530
+ return { platform: PLATFORM, action: "warn", detail, ...path ? { path } : {} };
531
+ }
532
+ function failDetail(step, r) {
533
+ if (r.timedOut) return `${step} timed out`;
534
+ if (r.error) return `${step} failed to spawn: ${r.error}`;
535
+ const line = firstLine(r.stderr) || firstLine(r.stdout);
536
+ return `${step} exited ${r.code}${line ? `: ${line}` : ""}`;
537
+ }
538
+ async function claudeDriveInstall(connectorId, stagingRoot, pluginDir) {
539
+ const changes = [];
540
+ const pluginKey = claudePluginKey(connectorId);
541
+ const bin = claudeBinary();
542
+ if (!bin) {
543
+ changes.push(
544
+ warn(
545
+ `claude CLI not found on PATH \u2014 bundle staged but not installed. Install manually: ${claudeManualInstallCommands(connectorId, stagingRoot)}`,
546
+ stagingRoot
547
+ )
548
+ );
549
+ return { changes, installed: false };
550
+ }
551
+ const validation = await runHostCommand(bin, ["plugin", "validate", pluginDir]);
552
+ if (!validation.ok && !validation.error) {
553
+ changes.push(
554
+ warn(
555
+ `bundle failed \`claude plugin validate\` \u2014 not installing. ` + failDetail("validate", validation),
556
+ pluginDir
557
+ )
558
+ );
559
+ return { changes, installed: false };
560
+ }
561
+ const registeredAt = claudeKnownMarketplacePath(MARKETPLACE_NAME);
562
+ if (registeredAt === stagingRoot) {
563
+ changes.push({
564
+ platform: PLATFORM,
565
+ action: "skip",
566
+ path: stagingRoot,
567
+ detail: `marketplace "${MARKETPLACE_NAME}" already registered`
568
+ });
569
+ } else if (registeredAt != null) {
570
+ changes.push(
571
+ warn(
572
+ `a marketplace named "${MARKETPLACE_NAME}" is already registered at ${registeredAt} (not ours) \u2014 refusing to touch it. Remove it first (claude plugin marketplace remove ${MARKETPLACE_NAME}) or install manually: ` + claudeManualInstallCommands(connectorId, stagingRoot)
573
+ )
574
+ );
575
+ return { changes, installed: false };
576
+ } else {
577
+ const add = await runHostCommand(bin, [
578
+ "plugin",
579
+ "marketplace",
580
+ "add",
581
+ stagingRoot
582
+ ]);
583
+ if (!add.ok) {
584
+ changes.push(
585
+ warn(
586
+ `could not register the local marketplace \u2014 ` + failDetail("claude plugin marketplace add", add) + `. Install manually: ${claudeManualInstallCommands(connectorId, stagingRoot)}`,
587
+ stagingRoot
588
+ )
589
+ );
590
+ return { changes, installed: false };
591
+ }
592
+ changes.push({
593
+ platform: PLATFORM,
594
+ action: "create",
595
+ path: stagingRoot,
596
+ detail: `registered local marketplace "${MARKETPLACE_NAME}" (claude plugin marketplace add)`
597
+ });
598
+ }
599
+ if (claudePluginInstalled(connectorId)) {
600
+ changes.push({
601
+ platform: PLATFORM,
602
+ action: "skip",
603
+ detail: `plugin ${pluginKey} already installed`
604
+ });
605
+ return { changes, installed: true };
606
+ }
607
+ const install = await runHostCommand(bin, ["plugin", "install", pluginKey]);
608
+ if (!install.ok || !claudePluginInstalled(connectorId)) {
609
+ changes.push(
610
+ warn(
611
+ `plugin install did not complete \u2014 ` + failDetail(`claude plugin install ${pluginKey}`, install)
612
+ )
613
+ );
614
+ return { changes, installed: false };
615
+ }
616
+ changes.push({
617
+ platform: PLATFORM,
618
+ action: "create",
619
+ detail: `installed plugin ${pluginKey} (scope user)`
620
+ });
621
+ return { changes, installed: true };
622
+ }
623
+ async function claudeDriveUninstall(connectorId) {
624
+ const changes = [];
625
+ const pluginKey = claudePluginKey(connectorId);
626
+ if (!claudePluginInstalled(connectorId)) {
627
+ changes.push({
628
+ platform: PLATFORM,
629
+ action: "skip",
630
+ detail: `plugin ${pluginKey} not installed on claude-code`
631
+ });
632
+ return { changes, removed: true };
633
+ }
634
+ const bin = claudeBinary();
635
+ if (!bin) {
636
+ changes.push(
637
+ warn(
638
+ `claude CLI not found on PATH \u2014 cannot drive the uninstall. Run manually: claude plugin uninstall ${pluginKey}`
639
+ )
640
+ );
641
+ return { changes, removed: false };
642
+ }
643
+ const uninstall = await runHostCommand(bin, ["plugin", "uninstall", pluginKey]);
644
+ if (!uninstall.ok || claudePluginInstalled(connectorId)) {
645
+ changes.push(
646
+ warn(`plugin uninstall did not complete \u2014 ` + failDetail(`claude plugin uninstall ${pluginKey}`, uninstall))
647
+ );
648
+ return { changes, removed: false };
649
+ }
650
+ changes.push({
651
+ platform: PLATFORM,
652
+ action: "remove",
653
+ detail: `uninstalled plugin ${pluginKey} (claude plugin uninstall)`
654
+ });
655
+ return { changes, removed: true };
656
+ }
657
+ async function claudeDriveMarketplaceRemove(stagingRoot) {
658
+ const bin = claudeBinary();
659
+ if (!bin) {
660
+ return [
661
+ warn(
662
+ `claude CLI not found on PATH \u2014 marketplace registration "${MARKETPLACE_NAME}" left behind. Run manually: claude plugin marketplace remove ${MARKETPLACE_NAME}`,
663
+ stagingRoot
664
+ )
665
+ ];
666
+ }
667
+ const remove = await runHostCommand(bin, [
668
+ "plugin",
669
+ "marketplace",
670
+ "remove",
671
+ MARKETPLACE_NAME
672
+ ]);
673
+ if (!remove.ok) {
674
+ return [
675
+ warn(
676
+ `could not remove the marketplace registration \u2014 ` + failDetail(`claude plugin marketplace remove ${MARKETPLACE_NAME}`, remove),
677
+ stagingRoot
678
+ )
679
+ ];
680
+ }
681
+ return [
682
+ {
683
+ platform: PLATFORM,
684
+ action: "remove",
685
+ path: stagingRoot,
686
+ detail: `removed marketplace registration "${MARKETPLACE_NAME}" (no plugins remain)`
687
+ }
688
+ ];
689
+ }
690
+ async function claudeDriveUpdate(connectorId, stagingRoot, pluginDir) {
691
+ const pluginKey = claudePluginKey(connectorId);
692
+ const bin = claudeBinary();
693
+ if (!bin) {
694
+ return {
695
+ changes: [
696
+ warn(
697
+ `claude CLI not found on PATH \u2014 bundle re-staged but the installed copy was not updated. Run manually: claude plugin update ${pluginKey}`,
698
+ stagingRoot
699
+ )
700
+ ],
701
+ installed: false
702
+ };
703
+ }
704
+ if (!claudePluginInstalled(connectorId)) {
705
+ return claudeDriveInstall(connectorId, stagingRoot, pluginDir);
706
+ }
707
+ const update = await runHostCommand(bin, ["plugin", "update", pluginKey]);
708
+ if (update.ok) {
709
+ return {
710
+ changes: [
711
+ {
712
+ platform: PLATFORM,
713
+ action: "update",
714
+ detail: `updated plugin ${pluginKey} (claude plugin update)`
715
+ }
716
+ ],
717
+ installed: true
718
+ };
719
+ }
720
+ const changes = [
721
+ warn(
722
+ `\`claude plugin update\` did not complete (` + failDetail("update", update) + `) \u2014 falling back to uninstall + install`
723
+ )
724
+ ];
725
+ const un = await claudeDriveUninstall(connectorId);
726
+ changes.push(...un.changes);
727
+ if (!un.removed) return { changes, installed: claudePluginInstalled(connectorId) };
728
+ const re = await claudeDriveInstall(connectorId, stagingRoot, pluginDir);
729
+ changes.push(...re.changes);
730
+ return { changes, installed: re.installed };
731
+ }
732
+ function errMessage2(err) {
733
+ return err instanceof Error ? err.message : String(err);
734
+ }
735
+ function stagedClaudePlugins(stagingRoot) {
736
+ if (!existsSync3(stagingRoot)) return [];
737
+ try {
738
+ return readdirSync2(stagingRoot).filter(
739
+ (name) => existsSync3(join3(stagingRoot, name, ".claude-plugin", "plugin.json"))
740
+ ).sort();
741
+ } catch {
742
+ return [];
743
+ }
744
+ }
745
+ function regenerateClaudeCatalog(stagingRoot, changes) {
746
+ const catalogPath = join3(stagingRoot, ".claude-plugin", "marketplace.json");
747
+ const plugins = stagedClaudePlugins(stagingRoot).map((name) => {
748
+ let description = `${name} \u2014 connector emitted by agent-connector`;
749
+ try {
750
+ const manifest = JSON.parse(
751
+ readFileSync(join3(stagingRoot, name, ".claude-plugin", "plugin.json"), "utf8")
752
+ );
753
+ if (typeof manifest.description === "string") description = manifest.description;
754
+ } catch {
755
+ }
756
+ return { name, source: `./${name}`, description };
757
+ });
758
+ const catalog = {
759
+ name: MARKETPLACE_NAME,
760
+ owner: { name: MARKETPLACE_NAME },
761
+ plugins
762
+ };
763
+ const serialized = `${JSON.stringify(catalog, null, 2)}
764
+ `;
765
+ let existing = null;
766
+ try {
767
+ existing = readFileSync(catalogPath, "utf8");
768
+ } catch {
769
+ }
770
+ if (existing === serialized) return;
771
+ ensureDir(dirname2(catalogPath));
772
+ writeFileSync(catalogPath, serialized, "utf8");
773
+ changes.push({
774
+ platform: PLATFORM,
775
+ action: existing == null ? "create" : "update",
776
+ path: catalogPath,
777
+ detail: `regenerated shared marketplace catalog (${plugins.length} plugin(s))`
778
+ });
779
+ }
780
+ function stageClaudeBundle(connector, changes) {
781
+ const stagingRoot = claudeStagingRoot();
782
+ const pluginDir = join3(stagingRoot, connector.id);
783
+ const existed = existsSync3(pluginDir);
784
+ const result = packageConnector(connector, {
785
+ outDir: stagingRoot,
786
+ format: "claude-plugin"
787
+ });
788
+ changes.push({
789
+ platform: PLATFORM,
790
+ action: existed ? "update" : "create",
791
+ path: pluginDir,
792
+ detail: `staged marketplace bundle (${result.files.length} files, claude-plugin)`
793
+ });
794
+ regenerateClaudeCatalog(stagingRoot, changes);
795
+ return { pluginDir, contentHash: hashDirectory(pluginDir) };
796
+ }
797
+ var claudeDriver = {
798
+ platform: PLATFORM,
799
+ format: "claude-plugin",
800
+ binary: claudeBinary,
801
+ stagingRoot: claudeStagingRoot,
802
+ pluginDir(id) {
803
+ return join3(claudeStagingRoot(), id);
804
+ },
805
+ installed: claudePluginInstalled,
806
+ stage(connector, changes) {
807
+ return stageClaudeBundle(connector, changes).contentHash;
808
+ },
809
+ planInstall(connector, changes) {
810
+ const stagingRoot = claudeStagingRoot();
811
+ const registeredAt = claudeKnownMarketplacePath(MARKETPLACE_NAME);
812
+ changes.push({
813
+ platform: PLATFORM,
814
+ action: registeredAt === stagingRoot ? "skip" : "create",
815
+ path: stagingRoot,
816
+ detail: registeredAt === stagingRoot ? `marketplace "${MARKETPLACE_NAME}" already registered` : `run: claude plugin marketplace add ${stagingRoot}`
817
+ });
818
+ changes.push({
819
+ platform: PLATFORM,
820
+ action: claudePluginInstalled(connector.id) ? "skip" : "create",
821
+ detail: claudePluginInstalled(connector.id) ? `plugin ${claudePluginKey(connector.id)} already installed` : `run: claude plugin install ${claudePluginKey(connector.id)}`
822
+ });
823
+ },
824
+ planUninstall(id, changes) {
825
+ const stagingRoot = claudeStagingRoot();
826
+ const pluginDir = join3(stagingRoot, id);
827
+ const pluginKey = claudePluginKey(id);
828
+ changes.push({
829
+ platform: PLATFORM,
830
+ action: claudePluginInstalled(id) ? "remove" : "skip",
831
+ detail: claudePluginInstalled(id) ? `run: claude plugin uninstall ${pluginKey}` : `plugin ${pluginKey} not installed on claude-code`
832
+ });
833
+ if (existsSync3(pluginDir)) {
834
+ changes.push({
835
+ platform: PLATFORM,
836
+ action: "remove",
837
+ path: pluginDir,
838
+ detail: "remove staged marketplace bundle"
839
+ });
840
+ }
841
+ const othersStaged = stagedClaudePlugins(stagingRoot).some((n) => n !== id);
842
+ if (!othersStaged && claudeKnownMarketplacePath(MARKETPLACE_NAME) === stagingRoot) {
843
+ changes.push({
844
+ platform: PLATFORM,
845
+ action: "remove",
846
+ path: stagingRoot,
847
+ detail: `run: claude plugin marketplace remove ${MARKETPLACE_NAME} (when no plugins remain)`
848
+ });
849
+ }
850
+ },
851
+ async driveInstall(id) {
852
+ const stagingRoot = claudeStagingRoot();
853
+ const out = await claudeDriveInstall(id, stagingRoot, join3(stagingRoot, id));
854
+ return { changes: out.changes, ok: out.installed };
855
+ },
856
+ async driveUninstall(id) {
857
+ const out = await claudeDriveUninstall(id);
858
+ return { changes: out.changes, ok: out.removed };
859
+ },
860
+ async driveUpdate(id) {
861
+ const stagingRoot = claudeStagingRoot();
862
+ const out = await claudeDriveUpdate(id, stagingRoot, join3(stagingRoot, id));
863
+ return { changes: out.changes, ok: out.installed };
864
+ },
865
+ async finishUninstall(id, changes) {
866
+ const stagingRoot = claudeStagingRoot();
867
+ const pluginDir = join3(stagingRoot, id);
868
+ if (existsSync3(pluginDir)) {
869
+ try {
870
+ rmSync2(pluginDir, { recursive: true, force: true });
871
+ changes.push({
872
+ platform: PLATFORM,
873
+ action: "remove",
874
+ path: pluginDir,
875
+ detail: "removed staged marketplace bundle"
876
+ });
877
+ } catch (err) {
878
+ changes.push({
879
+ platform: PLATFORM,
880
+ action: "warn",
881
+ path: pluginDir,
882
+ detail: `could not remove staged bundle: ${errMessage2(err)}`
883
+ });
884
+ }
885
+ }
886
+ if (existsSync3(stagingRoot)) regenerateClaudeCatalog(stagingRoot, changes);
887
+ const nothingStaged = stagedClaudePlugins(stagingRoot).length === 0;
888
+ if (nothingStaged && !anyClaudeAgentConnectorPlugins() && claudeKnownMarketplacePath(MARKETPLACE_NAME) === stagingRoot) {
889
+ changes.push(...await claudeDriveMarketplaceRemove(stagingRoot));
890
+ }
891
+ }
892
+ };
893
+
894
+ // src/core/marketplace-drivers/codex.ts
895
+ import { existsSync as existsSync4, readdirSync as readdirSync3, readFileSync as readFileSync2, rmSync as rmSync3, writeFileSync as writeFileSync2 } from "fs";
896
+ import { dirname as dirname3, join as join4 } from "path";
897
+ var PLATFORM2 = "codex";
898
+ function codexBinary() {
899
+ return findOnPath("codex");
900
+ }
901
+ function codexManualInstallCommands(connectorId, stagingRoot) {
902
+ return `codex plugin marketplace add ${stagingRoot} && codex plugin add ${codexPluginKey(connectorId)}`;
903
+ }
904
+ function errMessage3(err) {
905
+ return err instanceof Error ? err.message : String(err);
906
+ }
907
+ function warn2(detail, path) {
908
+ return { platform: PLATFORM2, action: "warn", detail, ...path ? { path } : {} };
909
+ }
910
+ function failDetail2(step, r) {
911
+ if (r.timedOut) return `${step} timed out`;
912
+ if (r.error) return `${step} failed to spawn: ${r.error}`;
913
+ const line = firstLine(r.stderr) || firstLine(r.stdout);
914
+ return `${step} exited ${r.code}${line ? `: ${line}` : ""}`;
915
+ }
916
+ function stagedCodexPlugins(stagingRoot) {
917
+ if (!existsSync4(stagingRoot)) return [];
918
+ try {
919
+ return readdirSync3(stagingRoot).filter(
920
+ (name) => existsSync4(join4(stagingRoot, name, ".codex-plugin", "plugin.json"))
921
+ ).sort();
922
+ } catch {
923
+ return [];
924
+ }
925
+ }
926
+ function codexCatalogPath(stagingRoot) {
927
+ return join4(stagingRoot, ".agents", "plugins", "marketplace.json");
928
+ }
929
+ function regenerateCodexCatalog(stagingRoot, changes) {
930
+ const catalogPath = codexCatalogPath(stagingRoot);
931
+ const plugins = stagedCodexPlugins(stagingRoot).map((name) => {
932
+ let description = `${name} \u2014 connector emitted by agent-connector`;
933
+ try {
934
+ const manifest = JSON.parse(
935
+ readFileSync2(join4(stagingRoot, name, ".codex-plugin", "plugin.json"), "utf8")
936
+ );
937
+ if (typeof manifest.description === "string") description = manifest.description;
938
+ } catch {
939
+ }
940
+ return { name, source: `./${name}`, description };
941
+ });
942
+ const catalog = { name: MARKETPLACE_NAME, owner: { name: MARKETPLACE_NAME }, plugins };
943
+ const serialized = `${JSON.stringify(catalog, null, 2)}
944
+ `;
945
+ let existing = null;
946
+ try {
947
+ existing = readFileSync2(catalogPath, "utf8");
948
+ } catch {
949
+ }
950
+ if (existing === serialized) return;
951
+ ensureDir(dirname3(catalogPath));
952
+ writeFileSync2(catalogPath, serialized, "utf8");
953
+ changes.push({
954
+ platform: PLATFORM2,
955
+ action: existing == null ? "create" : "update",
956
+ path: catalogPath,
957
+ detail: `regenerated shared marketplace catalog (${plugins.length} plugin(s))`
958
+ });
959
+ }
960
+ function stageCodexBundle(connector, changes) {
961
+ const stagingRoot = codexStagingRoot();
962
+ const pluginDir = join4(stagingRoot, connector.id);
963
+ const existed = existsSync4(pluginDir);
964
+ const result = packageConnector(connector, { outDir: stagingRoot, format: "codex-plugin" });
965
+ changes.push({
966
+ platform: PLATFORM2,
967
+ action: existed ? "update" : "create",
968
+ path: pluginDir,
969
+ detail: `staged marketplace bundle (${result.files.length} files, codex-plugin)`
970
+ });
971
+ regenerateCodexCatalog(stagingRoot, changes);
972
+ return { pluginDir, contentHash: hashDirectory(pluginDir) };
973
+ }
974
+ async function driveInstall(connectorId) {
975
+ const stagingRoot = codexStagingRoot();
976
+ const changes = [];
977
+ const pluginKey = codexPluginKey(connectorId);
978
+ const bin = codexBinary();
979
+ if (!bin) {
980
+ changes.push(
981
+ warn2(
982
+ `codex CLI not found on PATH \u2014 bundle staged but not installed. Install manually: ${codexManualInstallCommands(connectorId, stagingRoot)}`,
983
+ stagingRoot
984
+ )
985
+ );
986
+ return { changes, ok: false };
987
+ }
988
+ const registeredAt = codexMarketplaceSource(MARKETPLACE_NAME);
989
+ if (samePath(registeredAt, stagingRoot)) {
990
+ changes.push({
991
+ platform: PLATFORM2,
992
+ action: "skip",
993
+ path: stagingRoot,
994
+ detail: `marketplace "${MARKETPLACE_NAME}" already registered`
995
+ });
996
+ } else if (registeredAt != null) {
997
+ changes.push(
998
+ warn2(
999
+ `a marketplace named "${MARKETPLACE_NAME}" is already registered at ${registeredAt} (not ours) \u2014 refusing to touch it. Remove it first (codex plugin marketplace remove ${MARKETPLACE_NAME}) or install manually: ` + codexManualInstallCommands(connectorId, stagingRoot)
1000
+ )
1001
+ );
1002
+ return { changes, ok: false };
1003
+ } else {
1004
+ const add2 = await runHostCommand(bin, ["plugin", "marketplace", "add", stagingRoot]);
1005
+ if (!samePath(codexMarketplaceSource(MARKETPLACE_NAME), stagingRoot)) {
1006
+ changes.push(
1007
+ warn2(
1008
+ `could not register the local marketplace \u2014 ` + failDetail2("codex plugin marketplace add", add2) + `. Install manually: ${codexManualInstallCommands(connectorId, stagingRoot)}`,
1009
+ stagingRoot
1010
+ )
1011
+ );
1012
+ return { changes, ok: false };
1013
+ }
1014
+ changes.push({
1015
+ platform: PLATFORM2,
1016
+ action: "create",
1017
+ path: stagingRoot,
1018
+ detail: `registered local marketplace "${MARKETPLACE_NAME}" (codex plugin marketplace add)`
1019
+ });
1020
+ }
1021
+ if (codexPluginInstalled(connectorId)) {
1022
+ changes.push({
1023
+ platform: PLATFORM2,
1024
+ action: "skip",
1025
+ detail: `plugin ${pluginKey} already installed`
1026
+ });
1027
+ return { changes, ok: true };
1028
+ }
1029
+ const add = await runHostCommand(bin, ["plugin", "add", pluginKey]);
1030
+ if (!add.ok || !codexPluginInstalled(connectorId)) {
1031
+ changes.push(
1032
+ warn2(`plugin add did not complete \u2014 ` + failDetail2(`codex plugin add ${pluginKey}`, add))
1033
+ );
1034
+ return { changes, ok: false };
1035
+ }
1036
+ changes.push({
1037
+ platform: PLATFORM2,
1038
+ action: "create",
1039
+ detail: `installed plugin ${pluginKey} (scope user)`
1040
+ });
1041
+ return { changes, ok: true };
1042
+ }
1043
+ async function driveUninstall(connectorId) {
1044
+ const changes = [];
1045
+ const pluginKey = codexPluginKey(connectorId);
1046
+ if (!codexPluginInstalled(connectorId)) {
1047
+ changes.push({
1048
+ platform: PLATFORM2,
1049
+ action: "skip",
1050
+ detail: `plugin ${pluginKey} not installed on codex`
1051
+ });
1052
+ return { changes, ok: true };
1053
+ }
1054
+ const bin = codexBinary();
1055
+ if (!bin) {
1056
+ changes.push(
1057
+ warn2(
1058
+ `codex CLI not found on PATH \u2014 cannot drive the uninstall. Run manually: codex plugin remove ${pluginKey}`
1059
+ )
1060
+ );
1061
+ return { changes, ok: false };
1062
+ }
1063
+ const remove = await runHostCommand(bin, ["plugin", "remove", pluginKey]);
1064
+ if (!remove.ok || codexPluginInstalled(connectorId)) {
1065
+ changes.push(
1066
+ warn2(`plugin remove did not complete \u2014 ` + failDetail2(`codex plugin remove ${pluginKey}`, remove))
1067
+ );
1068
+ return { changes, ok: false };
1069
+ }
1070
+ changes.push({
1071
+ platform: PLATFORM2,
1072
+ action: "remove",
1073
+ detail: `uninstalled plugin ${pluginKey} (codex plugin remove)`
1074
+ });
1075
+ return { changes, ok: true };
1076
+ }
1077
+ async function driveMarketplaceRemove(stagingRoot) {
1078
+ const bin = codexBinary();
1079
+ if (!bin) {
1080
+ return [
1081
+ warn2(
1082
+ `codex CLI not found on PATH \u2014 marketplace registration "${MARKETPLACE_NAME}" left behind. Run manually: codex plugin marketplace remove ${MARKETPLACE_NAME}`,
1083
+ stagingRoot
1084
+ )
1085
+ ];
1086
+ }
1087
+ const remove = await runHostCommand(bin, ["plugin", "marketplace", "remove", MARKETPLACE_NAME]);
1088
+ if (!remove.ok && codexMarketplaceSource(MARKETPLACE_NAME) != null) {
1089
+ return [
1090
+ warn2(
1091
+ `could not remove the marketplace registration \u2014 ` + failDetail2(`codex plugin marketplace remove ${MARKETPLACE_NAME}`, remove),
1092
+ stagingRoot
1093
+ )
1094
+ ];
1095
+ }
1096
+ return [
1097
+ {
1098
+ platform: PLATFORM2,
1099
+ action: "remove",
1100
+ path: stagingRoot,
1101
+ detail: `removed marketplace registration "${MARKETPLACE_NAME}" (no plugins remain)`
1102
+ }
1103
+ ];
1104
+ }
1105
+ var codexDriver = {
1106
+ platform: PLATFORM2,
1107
+ format: "codex-plugin",
1108
+ binary: codexBinary,
1109
+ stagingRoot: codexStagingRoot,
1110
+ pluginDir(id) {
1111
+ return join4(codexStagingRoot(), id);
1112
+ },
1113
+ installed: codexPluginInstalled,
1114
+ stage(connector, changes) {
1115
+ return stageCodexBundle(connector, changes).contentHash;
1116
+ },
1117
+ planInstall(connector, changes) {
1118
+ const stagingRoot = codexStagingRoot();
1119
+ const registered = samePath(codexMarketplaceSource(MARKETPLACE_NAME), stagingRoot);
1120
+ changes.push({
1121
+ platform: PLATFORM2,
1122
+ action: registered ? "skip" : "create",
1123
+ path: stagingRoot,
1124
+ detail: registered ? `marketplace "${MARKETPLACE_NAME}" already registered` : `run: codex plugin marketplace add ${stagingRoot}`
1125
+ });
1126
+ changes.push({
1127
+ platform: PLATFORM2,
1128
+ action: codexPluginInstalled(connector.id) ? "skip" : "create",
1129
+ detail: codexPluginInstalled(connector.id) ? `plugin ${codexPluginKey(connector.id)} already installed` : `run: codex plugin add ${codexPluginKey(connector.id)}`
1130
+ });
1131
+ },
1132
+ planUninstall(id, changes) {
1133
+ const stagingRoot = codexStagingRoot();
1134
+ const pluginDir = join4(stagingRoot, id);
1135
+ const pluginKey = codexPluginKey(id);
1136
+ changes.push({
1137
+ platform: PLATFORM2,
1138
+ action: codexPluginInstalled(id) ? "remove" : "skip",
1139
+ detail: codexPluginInstalled(id) ? `run: codex plugin remove ${pluginKey}` : `plugin ${pluginKey} not installed on codex`
1140
+ });
1141
+ if (existsSync4(pluginDir)) {
1142
+ changes.push({
1143
+ platform: PLATFORM2,
1144
+ action: "remove",
1145
+ path: pluginDir,
1146
+ detail: "remove staged marketplace bundle"
1147
+ });
1148
+ }
1149
+ const othersStaged = stagedCodexPlugins(stagingRoot).some((n) => n !== id);
1150
+ if (!othersStaged && samePath(codexMarketplaceSource(MARKETPLACE_NAME), stagingRoot)) {
1151
+ changes.push({
1152
+ platform: PLATFORM2,
1153
+ action: "remove",
1154
+ path: stagingRoot,
1155
+ detail: `run: codex plugin marketplace remove ${MARKETPLACE_NAME} (when no plugins remain)`
1156
+ });
1157
+ }
1158
+ },
1159
+ driveInstall,
1160
+ driveUninstall,
1161
+ // Codex has no `plugin update` verb — update IS re-stage (done by the caller)
1162
+ // + `plugin add`, which is idempotent and version-cached.
1163
+ async driveUpdate(id) {
1164
+ return driveInstall(id);
1165
+ },
1166
+ async finishUninstall(id, changes) {
1167
+ const stagingRoot = codexStagingRoot();
1168
+ const pluginDir = join4(stagingRoot, id);
1169
+ if (existsSync4(pluginDir)) {
1170
+ try {
1171
+ rmSync3(pluginDir, { recursive: true, force: true });
1172
+ changes.push({
1173
+ platform: PLATFORM2,
1174
+ action: "remove",
1175
+ path: pluginDir,
1176
+ detail: "removed staged marketplace bundle"
1177
+ });
1178
+ } catch (err) {
1179
+ changes.push({
1180
+ platform: PLATFORM2,
1181
+ action: "warn",
1182
+ path: pluginDir,
1183
+ detail: `could not remove staged bundle: ${errMessage3(err)}`
1184
+ });
1185
+ }
1186
+ }
1187
+ if (existsSync4(stagingRoot)) regenerateCodexCatalog(stagingRoot, changes);
1188
+ const nothingStaged = stagedCodexPlugins(stagingRoot).length === 0;
1189
+ if (nothingStaged && !anyCodexAgentConnectorPlugins() && samePath(codexMarketplaceSource(MARKETPLACE_NAME), stagingRoot)) {
1190
+ changes.push(...await driveMarketplaceRemove(stagingRoot));
1191
+ }
1192
+ }
1193
+ };
1194
+
1195
+ // src/core/marketplace-drivers/agy.ts
1196
+ import { existsSync as existsSync5, rmSync as rmSync4 } from "fs";
1197
+ import { join as join5 } from "path";
1198
+ function agyBinary() {
1199
+ return findOnPath("agy");
1200
+ }
1201
+ function errMessage4(err) {
1202
+ return err instanceof Error ? err.message : String(err);
1203
+ }
1204
+ function failDetail3(step, r) {
1205
+ if (r.timedOut) return `${step} timed out`;
1206
+ if (r.error) return `${step} failed to spawn: ${r.error}`;
1207
+ const line = firstLine(r.stderr) || firstLine(r.stdout);
1208
+ return `${step} exited ${r.code}${line ? `: ${line}` : ""}`;
1209
+ }
1210
+ function stageAgyBundle(connector, platform, changes) {
1211
+ const stagingRoot = agyStagingRoot();
1212
+ const pluginDir = join5(stagingRoot, connector.id);
1213
+ const existed = existsSync5(pluginDir);
1214
+ const result = packageConnector(connector, { outDir: stagingRoot, format: "agy-plugin" });
1215
+ changes.push({
1216
+ platform,
1217
+ action: existed ? "update" : "create",
1218
+ path: pluginDir,
1219
+ detail: `staged marketplace bundle (${result.files.length} files, agy-plugin)`
1220
+ });
1221
+ return { pluginDir, contentHash: hashDirectory(pluginDir) };
1222
+ }
1223
+ function makeAgyDriver(platform) {
1224
+ const warn3 = (detail, path) => ({
1225
+ platform,
1226
+ action: "warn",
1227
+ detail,
1228
+ ...path ? { path } : {}
1229
+ });
1230
+ const manualInstall = (pluginDir) => `agy plugin install ${pluginDir}`;
1231
+ return {
1232
+ platform,
1233
+ format: "agy-plugin",
1234
+ binary: agyBinary,
1235
+ stagingRoot: agyStagingRoot,
1236
+ pluginDir(id) {
1237
+ return join5(agyStagingRoot(), id);
1238
+ },
1239
+ installed: agyPluginInstalled,
1240
+ stage(connector, changes) {
1241
+ return stageAgyBundle(connector, platform, changes).contentHash;
1242
+ },
1243
+ planInstall(connector, changes) {
1244
+ const pluginDir = join5(agyStagingRoot(), connector.id);
1245
+ changes.push({
1246
+ platform,
1247
+ action: agyPluginInstalled(connector.id) ? "skip" : "create",
1248
+ detail: agyPluginInstalled(connector.id) ? `plugin ${connector.id} already installed` : `run: agy plugin install ${pluginDir}`
1249
+ });
1250
+ },
1251
+ planUninstall(id, changes) {
1252
+ const pluginDir = join5(agyStagingRoot(), id);
1253
+ changes.push({
1254
+ platform,
1255
+ action: agyPluginInstalled(id) ? "remove" : "skip",
1256
+ detail: agyPluginInstalled(id) ? `run: agy plugin uninstall ${id}` : `plugin ${id} not installed on ${platform}`
1257
+ });
1258
+ if (existsSync5(pluginDir)) {
1259
+ changes.push({
1260
+ platform,
1261
+ action: "remove",
1262
+ path: pluginDir,
1263
+ detail: "remove staged marketplace bundle"
1264
+ });
1265
+ }
1266
+ },
1267
+ async driveInstall(id) {
1268
+ const changes = [];
1269
+ const pluginDir = join5(agyStagingRoot(), id);
1270
+ const bin = agyBinary();
1271
+ if (!bin) {
1272
+ changes.push(
1273
+ warn3(
1274
+ `agy CLI not found on PATH \u2014 bundle staged but not installed. Install manually: ${manualInstall(pluginDir)}`,
1275
+ pluginDir
1276
+ )
1277
+ );
1278
+ return { changes, ok: false };
1279
+ }
1280
+ if (agyPluginInstalled(id)) {
1281
+ changes.push({
1282
+ platform,
1283
+ action: "skip",
1284
+ detail: `plugin ${id} already installed`
1285
+ });
1286
+ return { changes, ok: true };
1287
+ }
1288
+ await runHostCommand(bin, ["plugin", "validate", pluginDir]);
1289
+ const install = await runHostCommand(bin, ["plugin", "install", pluginDir]);
1290
+ if (!install.ok || !agyPluginInstalled(id)) {
1291
+ changes.push(
1292
+ warn3(`plugin install did not complete \u2014 ` + failDetail3(`agy plugin install ${pluginDir}`, install))
1293
+ );
1294
+ return { changes, ok: false };
1295
+ }
1296
+ changes.push({
1297
+ platform,
1298
+ action: "create",
1299
+ detail: `installed plugin ${id} (scope user)`
1300
+ });
1301
+ return { changes, ok: true };
1302
+ },
1303
+ async driveUninstall(id) {
1304
+ const changes = [];
1305
+ if (!agyPluginInstalled(id)) {
1306
+ changes.push({
1307
+ platform,
1308
+ action: "skip",
1309
+ detail: `plugin ${id} not installed on ${platform}`
1310
+ });
1311
+ return { changes, ok: true };
1312
+ }
1313
+ const bin = agyBinary();
1314
+ if (!bin) {
1315
+ changes.push(
1316
+ warn3(
1317
+ `agy CLI not found on PATH \u2014 cannot drive the uninstall. Run manually: agy plugin uninstall ${id}`
1318
+ )
1319
+ );
1320
+ return { changes, ok: false };
1321
+ }
1322
+ const uninstall = await runHostCommand(bin, ["plugin", "uninstall", id]);
1323
+ if (!uninstall.ok || agyPluginInstalled(id)) {
1324
+ changes.push(
1325
+ warn3(`plugin uninstall did not complete \u2014 ` + failDetail3(`agy plugin uninstall ${id}`, uninstall))
1326
+ );
1327
+ return { changes, ok: false };
1328
+ }
1329
+ changes.push({
1330
+ platform,
1331
+ action: "remove",
1332
+ detail: `uninstalled plugin ${id} (agy plugin uninstall)`
1333
+ });
1334
+ return { changes, ok: true };
1335
+ },
1336
+ // No `plugin update` verb — update = re-stage (done by the caller) +
1337
+ // `plugin install` (idempotent overwrite). Force a fresh install even when
1338
+ // the probe already shows it present (the bundle on disk changed).
1339
+ async driveUpdate(id) {
1340
+ const changes = [];
1341
+ const pluginDir = join5(agyStagingRoot(), id);
1342
+ const bin = agyBinary();
1343
+ if (!bin) {
1344
+ changes.push(
1345
+ warn3(
1346
+ `agy CLI not found on PATH \u2014 bundle re-staged but the installed copy was not updated. Run manually: ${manualInstall(pluginDir)}`,
1347
+ pluginDir
1348
+ )
1349
+ );
1350
+ return { changes, ok: false };
1351
+ }
1352
+ const install = await runHostCommand(bin, ["plugin", "install", pluginDir]);
1353
+ if (!install.ok || !agyPluginInstalled(id)) {
1354
+ changes.push(
1355
+ warn3(`plugin install did not complete \u2014 ` + failDetail3(`agy plugin install ${pluginDir}`, install))
1356
+ );
1357
+ return { changes, ok: false };
1358
+ }
1359
+ changes.push({
1360
+ platform,
1361
+ action: "update",
1362
+ detail: `updated plugin ${id} (agy plugin install \u2014 idempotent overwrite)`
1363
+ });
1364
+ return { changes, ok: true };
1365
+ },
1366
+ // Direct driver: no catalog, no marketplace de-registration. Cleanup is just
1367
+ // removing the staged bundle dir (the host copied it into its own store).
1368
+ async finishUninstall(id, changes) {
1369
+ const pluginDir = join5(agyStagingRoot(), id);
1370
+ if (existsSync5(pluginDir)) {
1371
+ try {
1372
+ rmSync4(pluginDir, { recursive: true, force: true });
1373
+ changes.push({
1374
+ platform,
1375
+ action: "remove",
1376
+ path: pluginDir,
1377
+ detail: "removed staged marketplace bundle"
1378
+ });
1379
+ } catch (err) {
1380
+ changes.push({
1381
+ platform,
1382
+ action: "warn",
1383
+ path: pluginDir,
1384
+ detail: `could not remove staged bundle: ${errMessage4(err)}`
1385
+ });
1386
+ }
1387
+ }
1388
+ }
1389
+ };
1390
+ }
1391
+
1392
+ // src/core/marketplace-drivers/registry.ts
1393
+ var agyDrivers = /* @__PURE__ */ new Map();
1394
+ function agyDriver(platform) {
1395
+ let driver = agyDrivers.get(platform);
1396
+ if (!driver) {
1397
+ driver = makeAgyDriver(platform);
1398
+ agyDrivers.set(platform, driver);
1399
+ }
1400
+ return driver;
1401
+ }
1402
+ function getMarketplaceDriver(platform) {
1403
+ switch (platform) {
1404
+ case "claude-code":
1405
+ return claudeDriver;
1406
+ case "codex":
1407
+ return codexDriver;
1408
+ case "antigravity":
1409
+ case "antigravity-cli":
1410
+ return agyDriver(platform);
1411
+ default:
1412
+ return null;
1413
+ }
1414
+ }
1415
+
1416
+ // src/core/marketplace.ts
1417
+ function parseInstallMethod(value) {
1418
+ if (value == null || value === "direct" || value === "marketplace") {
1419
+ return value ?? "direct";
1420
+ }
1421
+ return null;
1422
+ }
1423
+ function parseUninstallMethod(value) {
1424
+ if (value == null || value === "auto" || value === "direct" || value === "marketplace") {
1425
+ return value ?? "auto";
1426
+ }
1427
+ return null;
1428
+ }
1429
+ var MARKETPLACE_FORMAT_BY_PLATFORM = {
1430
+ "claude-code": "claude-plugin",
1431
+ codex: "codex-plugin",
1432
+ droid: "factory-plugin",
1433
+ "gemini-cli": "gemini-extension",
1434
+ "qwen-code": "qwen-extension",
1435
+ antigravity: "agy-plugin",
1436
+ "antigravity-cli": "agy-plugin",
1437
+ cursor: "cursor-plugin",
1438
+ kimi: "kimi-plugin",
1439
+ opencode: "npm-plugin",
1440
+ kilo: "npm-plugin",
1441
+ "kilo-cli": "npm-plugin",
1442
+ pi: "npm-plugin",
1443
+ "vscode-copilot": "claude-plugin",
1444
+ openclaw: "claude-plugin",
1445
+ omp: "claude-plugin"
1446
+ };
1447
+ var DRIVABLE_MARKETPLACE_PLATFORMS = /* @__PURE__ */ new Set([
1448
+ "claude-code",
1449
+ "codex",
1450
+ "antigravity",
1451
+ "antigravity-cli"
1452
+ ]);
1453
+ function newResult2(connectorId, dryRun) {
1454
+ return { connectorId, dryRun, changes: [], warnings: [] };
1455
+ }
1456
+ function errMessage5(err) {
1457
+ return err instanceof Error ? err.message : String(err);
1458
+ }
1459
+ function manualHint(platform, format, id) {
1460
+ const steps = installInstructions(format, id, "<out-dir>").join("; ");
1461
+ return `agent-connector cannot drive the ${platform} marketplace flow yet \u2014 install manually: \`agent-connector package --format ${format} --out <out-dir>\` then: ${steps}`;
1462
+ }
1463
+ async function directInstallPresent(connector, platform, scope, projectDir) {
1464
+ try {
1465
+ const adapter = await loadAdapter(platform);
1466
+ if (!adapter) return false;
1467
+ const ctx = {
1468
+ connector,
1469
+ scope: connector.platforms[platform]?.scope ?? scope,
1470
+ projectDir,
1471
+ homeBinPath: homeBinPath(),
1472
+ dataRoot: dataRoot(),
1473
+ dryRun: true
1474
+ };
1475
+ const escaped = connector.id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1476
+ const serverKey = new RegExp(`"${escaped}"\\s*:|\\.${escaped}\\]`);
1477
+ const hookCommand = new RegExp(`--connector\\s+${escaped}(?![\\w-])`);
1478
+ const matches = (path, re) => {
1479
+ try {
1480
+ return re.test(readFileSync3(path, "utf8"));
1481
+ } catch {
1482
+ return false;
1483
+ }
1484
+ };
1485
+ try {
1486
+ if (matches(adapter.getServerConfigPath(ctx), serverKey)) return true;
1487
+ } catch {
1488
+ }
1489
+ try {
1490
+ if (matches(adapter.getHookConfigPath(ctx), hookCommand)) return true;
1491
+ } catch {
1492
+ }
1493
+ return false;
1494
+ } catch {
1495
+ return false;
1496
+ }
1497
+ }
1498
+ async function installViaMarketplace(opts) {
1499
+ const { connector, modulePath, scope, projectDir, dryRun } = opts;
1500
+ const result = newResult2(connector.id, dryRun);
1501
+ if (scope !== "user") {
1502
+ result.warnings.push(
1503
+ "--method marketplace supports --scope user only in this version (project-scope plugin installs are deferred)"
1504
+ );
1505
+ return result;
1506
+ }
1507
+ if (!dryRun) {
1508
+ try {
1509
+ ensureHomeBin(resolveCliEntry());
1510
+ } catch (err) {
1511
+ log.warn(`ensureHomeBin failed: ${errMessage5(err)}`);
1512
+ }
1513
+ try {
1514
+ registerConnector(connector, modulePath);
1515
+ } catch (err) {
1516
+ log.warn(`registerConnector failed: ${errMessage5(err)}`);
1517
+ }
1518
+ }
1519
+ const explicit = opts.targets != null && opts.targets.length > 0;
1520
+ const targets = await resolveTargets(opts.targets, connector.targets, projectDir);
1521
+ if (targets.length === 0) {
1522
+ result.warnings.push(
1523
+ "no target platforms resolved (none installed / detected, or all filtered out)"
1524
+ );
1525
+ return result;
1526
+ }
1527
+ for (const id of targets) {
1528
+ const format = MARKETPLACE_FORMAT_BY_PLATFORM[id];
1529
+ if (!format) {
1530
+ result.changes.push({
1531
+ platform: id,
1532
+ action: explicit ? "warn" : "skip",
1533
+ detail: `no marketplace/plugin distribution path exists for ${id} \u2014 use the direct method: \`agent-connector install --targets ${id}\``
1534
+ });
1535
+ continue;
1536
+ }
1537
+ if (!DRIVABLE_MARKETPLACE_PLATFORMS.has(id)) {
1538
+ result.changes.push({
1539
+ platform: id,
1540
+ action: explicit ? "warn" : "skip",
1541
+ detail: manualHint(id, format, connector.id)
1542
+ });
1543
+ continue;
1544
+ }
1545
+ const driver = getMarketplaceDriver(id);
1546
+ if (!driver) {
1547
+ result.changes.push({
1548
+ platform: id,
1549
+ action: explicit ? "warn" : "skip",
1550
+ detail: manualHint(id, format, connector.id)
1551
+ });
1552
+ continue;
1553
+ }
1554
+ if (await directInstallPresent(connector, id, scope, projectDir)) {
1555
+ result.changes.push({
1556
+ platform: id,
1557
+ action: "warn",
1558
+ detail: `"${connector.id}" is already installed DIRECTLY on ${id} \u2014 refusing the marketplace install (both at once duplicates hooks + the MCP server and corrupts telemetry). Run \`agent-connector uninstall --targets ${id}\` first, or keep the direct install.`
1559
+ });
1560
+ continue;
1561
+ }
1562
+ const pluginDir = driver.pluginDir(connector.id);
1563
+ if (dryRun) {
1564
+ const planned = packageConnector(connector, {
1565
+ outDir: driver.stagingRoot(),
1566
+ format,
1567
+ dryRun: true
1568
+ });
1569
+ for (const file of planned.files) {
1570
+ result.changes.push({
1571
+ platform: id,
1572
+ action: "create",
1573
+ path: file,
1574
+ detail: "stage bundle file"
1575
+ });
1576
+ }
1577
+ driver.planInstall(connector, result.changes);
1578
+ continue;
1579
+ }
1580
+ const contentHash = driver.stage(connector, result.changes);
1581
+ const outcome = await driver.driveInstall(connector.id);
1582
+ result.changes.push(...outcome.changes);
1583
+ if (outcome.ok) {
1584
+ const installs = readMarketplaceInstalls(connector.id);
1585
+ const record = {
1586
+ format,
1587
+ bundleDir: pluginDir,
1588
+ marketplace: MARKETPLACE_NAME,
1589
+ scope,
1590
+ version: connector.version,
1591
+ contentHash,
1592
+ installedAt: installs[id]?.installedAt ?? (/* @__PURE__ */ new Date()).toISOString()
1593
+ };
1594
+ installs[id] = record;
1595
+ writeMarketplaceInstalls(connector.id, installs);
1596
+ }
1597
+ }
1598
+ return result;
1599
+ }
1600
+ var MANUAL_UNINSTALL = {
1601
+ "gemini-cli": (id) => `gemini extensions uninstall ${id}`,
1602
+ antigravity: (id) => `agy plugin uninstall ${id}`,
1603
+ "antigravity-cli": (id) => `agy plugin uninstall ${id}`,
1604
+ codex: (id) => `codex plugin remove ${id}@${MARKETPLACE_NAME}`
1605
+ };
1606
+ async function uninstallViaMarketplace(opts) {
1607
+ const { connectorId, dryRun, purge } = opts;
1608
+ const result = newResult2(connectorId, dryRun);
1609
+ let targets;
1610
+ if (opts.targets && opts.targets.length > 0) {
1611
+ targets = [...new Set(opts.targets)];
1612
+ } else {
1613
+ targets = Object.keys(readMarketplaceInstalls(connectorId));
1614
+ for (const id of DRIVABLE_MARKETPLACE_PLATFORMS) {
1615
+ if (targets.includes(id)) continue;
1616
+ const driver = getMarketplaceDriver(id);
1617
+ if (driver?.installed(connectorId)) targets = [...targets, id];
1618
+ }
1619
+ }
1620
+ if (targets.length === 0) {
1621
+ result.warnings.push(
1622
+ `no marketplace installs found for "${connectorId}" (nothing recorded, no host evidence)`
1623
+ );
1624
+ return result;
1625
+ }
1626
+ for (const id of targets) {
1627
+ const driver = getMarketplaceDriver(id);
1628
+ if (!driver) {
1629
+ const manual = MANUAL_UNINSTALL[id];
1630
+ const recorded = readMarketplaceInstalls(connectorId)[id] != null;
1631
+ result.changes.push({
1632
+ platform: id,
1633
+ action: recorded ? "warn" : "skip",
1634
+ detail: recorded ? `agent-connector cannot drive the ${id} marketplace uninstall yet \u2014 run manually${manual ? `: ${manual(connectorId)}` : ""} (record kept until then)` : `no marketplace install recorded for ${id}`
1635
+ });
1636
+ continue;
1637
+ }
1638
+ if (dryRun) {
1639
+ driver.planUninstall(connectorId, result.changes);
1640
+ continue;
1641
+ }
1642
+ const outcome = await driver.driveUninstall(connectorId);
1643
+ result.changes.push(...outcome.changes);
1644
+ if (!outcome.ok) continue;
1645
+ await driver.finishUninstall(connectorId, result.changes);
1646
+ const installs = readMarketplaceInstalls(connectorId);
1647
+ if (installs[id]) {
1648
+ delete installs[id];
1649
+ writeMarketplaceInstalls(connectorId, installs);
1650
+ result.changes.push({
1651
+ platform: id,
1652
+ action: "remove",
1653
+ path: marketplaceRoot(),
1654
+ detail: "removed marketplace install record"
1655
+ });
1656
+ }
1657
+ }
1658
+ if (purge && !dryRun) {
1659
+ const seenRoots = /* @__PURE__ */ new Set();
1660
+ for (const id of DRIVABLE_MARKETPLACE_PLATFORMS) {
1661
+ const driver = getMarketplaceDriver(id);
1662
+ if (!driver) continue;
1663
+ const root2 = driver.stagingRoot();
1664
+ if (seenRoots.has(root2)) continue;
1665
+ seenRoots.add(root2);
1666
+ if (!existsSync6(root2)) continue;
1667
+ let bundleDirs;
1668
+ try {
1669
+ bundleDirs = readdirSync4(root2).filter((n) => !n.startsWith("."));
1670
+ } catch {
1671
+ bundleDirs = [];
1672
+ }
1673
+ if (bundleDirs.length > 0) continue;
1674
+ try {
1675
+ rmSync5(root2, { recursive: true, force: true });
1676
+ result.changes.push({
1677
+ platform: id,
1678
+ action: "remove",
1679
+ path: root2,
1680
+ detail: "removed empty marketplace staging root (--purge)"
1681
+ });
1682
+ } catch (err) {
1683
+ log.warn(`marketplace staging-root removal failed: ${errMessage5(err)}`);
1684
+ }
1685
+ }
1686
+ const root = marketplaceRoot();
1687
+ try {
1688
+ if (existsSync6(root) && readdirSync4(root).length === 0) {
1689
+ rmSync5(root, { recursive: true, force: true });
1690
+ }
1691
+ } catch {
1692
+ }
1693
+ }
1694
+ return result;
1695
+ }
1696
+ async function upgradeViaMarketplace(opts) {
1697
+ const { connector, scope, dryRun } = opts;
1698
+ const result = newResult2(connector.id, dryRun);
1699
+ if (scope !== "user") {
1700
+ result.warnings.push(
1701
+ "--method marketplace supports --scope user only in this version"
1702
+ );
1703
+ return result;
1704
+ }
1705
+ let targets;
1706
+ if (opts.targets && opts.targets.length > 0) {
1707
+ targets = [...new Set(opts.targets)];
1708
+ } else {
1709
+ targets = Object.keys(readMarketplaceInstalls(connector.id));
1710
+ for (const id of DRIVABLE_MARKETPLACE_PLATFORMS) {
1711
+ if (targets.includes(id)) continue;
1712
+ const driver = getMarketplaceDriver(id);
1713
+ if (driver?.installed(connector.id)) targets = [...targets, id];
1714
+ }
1715
+ }
1716
+ if (targets.length === 0) {
1717
+ result.warnings.push(
1718
+ `no marketplace installs found for "${connector.id}" \u2014 run \`agent-connector install --method marketplace\` first`
1719
+ );
1720
+ return result;
1721
+ }
1722
+ for (const id of targets) {
1723
+ const driver = getMarketplaceDriver(id);
1724
+ if (!driver) {
1725
+ result.changes.push({
1726
+ platform: id,
1727
+ action: "warn",
1728
+ detail: `agent-connector cannot drive a ${id} marketplace upgrade yet \u2014 re-run \`agent-connector package\` and the host's own update flow`
1729
+ });
1730
+ continue;
1731
+ }
1732
+ const record = readMarketplaceInstalls(connector.id)[id];
1733
+ if (!record && !driver.installed(connector.id)) {
1734
+ result.changes.push({
1735
+ platform: id,
1736
+ action: "warn",
1737
+ detail: `"${connector.id}" is not marketplace-installed on ${id} \u2014 run \`agent-connector install --method marketplace --targets ${id}\``
1738
+ });
1739
+ continue;
1740
+ }
1741
+ if (record && record.version === connector.version) {
1742
+ result.changes.push({
1743
+ platform: id,
1744
+ action: "warn",
1745
+ detail: `connector.version is unchanged since the recorded install (${record.version}) \u2014 ${id} caches a versioned copy, so bump connector.version for the update to take effect`
1746
+ });
1747
+ }
1748
+ const pluginDir = driver.pluginDir(connector.id);
1749
+ if (dryRun) {
1750
+ result.changes.push({
1751
+ platform: id,
1752
+ action: "update",
1753
+ path: pluginDir,
1754
+ detail: "re-stage marketplace bundle in place"
1755
+ });
1756
+ result.changes.push({
1757
+ platform: id,
1758
+ action: "update",
1759
+ detail: `drive the ${id} host update flow`
1760
+ });
1761
+ continue;
1762
+ }
1763
+ const contentHash = driver.stage(connector, result.changes);
1764
+ const outcome = await driver.driveUpdate(connector.id);
1765
+ result.changes.push(...outcome.changes);
1766
+ if (outcome.ok) {
1767
+ const installs = readMarketplaceInstalls(connector.id);
1768
+ installs[id] = {
1769
+ format: record?.format ?? driver.format,
1770
+ bundleDir: pluginDir,
1771
+ marketplace: MARKETPLACE_NAME,
1772
+ scope,
1773
+ version: connector.version,
1774
+ contentHash,
1775
+ installedAt: record?.installedAt ?? (/* @__PURE__ */ new Date()).toISOString()
1776
+ };
1777
+ writeMarketplaceInstalls(connector.id, installs);
1778
+ }
1779
+ }
1780
+ return result;
1781
+ }
1782
+ async function marketplaceDoctorChecks(connector, scope, projectDir) {
1783
+ const groups = [];
1784
+ const claude = await claudeMarketplaceGroup(connector, scope, projectDir);
1785
+ if (claude) groups.push(claude);
1786
+ const seenHosts = /* @__PURE__ */ new Set();
1787
+ const candidates = ["codex", "antigravity", "antigravity-cli"].sort((a, b) => recordRank(connector.id, a) - recordRank(connector.id, b));
1788
+ for (const platform of candidates) {
1789
+ const driver = getMarketplaceDriver(platform);
1790
+ if (!driver) continue;
1791
+ const hostKey = driver.stagingRoot();
1792
+ if (seenHosts.has(hostKey)) continue;
1793
+ const group = await genericMarketplaceGroup(connector, platform, scope, projectDir);
1794
+ if (group) {
1795
+ groups.push(group);
1796
+ seenHosts.add(hostKey);
1797
+ }
1798
+ }
1799
+ groups.sort((a, b) => platformDoctorRank(a.platform) - platformDoctorRank(b.platform));
1800
+ return groups;
1801
+ }
1802
+ function recordRank(connectorId, platform) {
1803
+ return readMarketplaceInstalls(connectorId)[platform] ? 0 : 1;
1804
+ }
1805
+ function platformDoctorRank(platform) {
1806
+ const order = ["claude-code", "codex", "antigravity", "antigravity-cli"];
1807
+ const i = order.indexOf(platform);
1808
+ return i === -1 ? order.length : i;
1809
+ }
1810
+ async function claudeMarketplaceGroup(connector, scope, projectDir) {
1811
+ const id = connector.id;
1812
+ const record = readMarketplaceInstalls(id)["claude-code"];
1813
+ const pluginPresent = claudePluginInstalled(id);
1814
+ if (!record && !pluginPresent) return null;
1815
+ const results = [];
1816
+ const stagingRoot = claudeStagingRoot();
1817
+ const pluginKey = claudePluginKey(id);
1818
+ const direct = await directInstallPresent(connector, "claude-code", scope, projectDir);
1819
+ if (direct && pluginPresent) {
1820
+ results.push({
1821
+ check: `${id}: marketplace duplicate-registration`,
1822
+ status: "fail",
1823
+ message: `installed BOTH directly and as plugin ${pluginKey} \u2014 hooks + MCP server are duplicated`,
1824
+ fix: `uninstall one method: \`agent-connector uninstall --method marketplace --targets claude-code\` or \`agent-connector uninstall --targets claude-code\``
1825
+ });
1826
+ }
1827
+ if (record) {
1828
+ const registeredAt = claudeKnownMarketplacePath(MARKETPLACE_NAME);
1829
+ const catalogPath = join6(stagingRoot, ".claude-plugin", "marketplace.json");
1830
+ let catalogListed = false;
1831
+ try {
1832
+ const catalog = JSON.parse(readFileSync3(catalogPath, "utf8"));
1833
+ catalogListed = (catalog.plugins ?? []).some((p) => p.name === id);
1834
+ } catch {
1835
+ catalogListed = false;
1836
+ }
1837
+ const intact = registeredAt === stagingRoot && existsSync6(record.bundleDir) && catalogListed;
1838
+ results.push({
1839
+ check: `${id}: marketplace registration`,
1840
+ status: intact ? "pass" : "fail",
1841
+ message: intact ? `local marketplace "${MARKETPLACE_NAME}" registered at ${stagingRoot}, bundle staged + cataloged` : `marketplace registration broken (registered at ${registeredAt ?? "nowhere"}, bundle ${existsSync6(record.bundleDir) ? "present" : "missing"}, catalog ${catalogListed ? "lists it" : "does not list it"})`,
1842
+ ...intact ? {} : { fix: `agent-connector install --method marketplace --targets claude-code` }
1843
+ });
1844
+ }
1845
+ if (record && !pluginPresent) {
1846
+ results.push({
1847
+ check: `${id}: marketplace state drift`,
1848
+ status: "warn",
1849
+ message: `recorded as marketplace-installed but Claude's state shows ${pluginKey} absent`,
1850
+ fix: `re-run \`agent-connector install --method marketplace --targets claude-code\` or \`agent-connector uninstall --method marketplace --targets claude-code\` to reconcile`
1851
+ });
1852
+ } else if (!record && pluginPresent) {
1853
+ results.push({
1854
+ check: `${id}: marketplace state drift`,
1855
+ status: "warn",
1856
+ message: `Claude lists ${pluginKey} installed but agent-connector has no record of it (manual install?)`,
1857
+ fix: `adopt it: \`agent-connector install --method marketplace --targets claude-code\` (idempotent), or remove it: \`claude plugin uninstall ${pluginKey}\``
1858
+ });
1859
+ }
1860
+ if (record) {
1861
+ if (connector.version !== "0.0.0" && connector.version !== record.version) {
1862
+ results.push({
1863
+ check: `${id}: marketplace staleness`,
1864
+ status: "warn",
1865
+ message: `connector.version ${connector.version} \u2260 installed ${record.version}`,
1866
+ fix: `agent-connector upgrade --method marketplace --targets claude-code`
1867
+ });
1868
+ } else if (existsSync6(record.bundleDir) && record.contentHash !== "" && hashDirectory(record.bundleDir) !== record.contentHash) {
1869
+ results.push({
1870
+ check: `${id}: marketplace staleness`,
1871
+ status: "warn",
1872
+ message: "staged bundle changed since install \u2014 Claude's cached copy is stale",
1873
+ fix: `agent-connector upgrade --method marketplace --targets claude-code`
1874
+ });
1875
+ }
1876
+ }
1877
+ if (pluginPresent && !existsSync6(homeBinPath())) {
1878
+ results.push({
1879
+ check: `${id}: marketplace home-bin`,
1880
+ status: "fail",
1881
+ message: `installed plugin hooks exec ${homeBinPath()} which does not exist`,
1882
+ fix: "agent-connector upgrade"
1883
+ });
1884
+ }
1885
+ if (record && claudeBinary() == null) {
1886
+ results.push({
1887
+ check: `${id}: marketplace host binary`,
1888
+ status: "warn",
1889
+ message: "claude CLI not found on PATH \u2014 the recorded marketplace install cannot be managed until it is reinstalled"
1890
+ });
1891
+ }
1892
+ if (results.every((r) => r.status === "pass")) {
1893
+ results.push({
1894
+ check: `${id}: marketplace install`,
1895
+ status: "pass",
1896
+ message: `${pluginKey} installed via marketplace (version ${record?.version ?? "unknown"})`
1897
+ });
1898
+ }
1899
+ return { platform: "claude-code", results };
1900
+ }
1901
+ async function genericMarketplaceGroup(connector, platform, scope, projectDir) {
1902
+ const driver = getMarketplaceDriver(platform);
1903
+ if (!driver) return null;
1904
+ const id = connector.id;
1905
+ const record = readMarketplaceInstalls(id)[platform];
1906
+ const installed = driver.installed(id);
1907
+ if (!record && !installed) return null;
1908
+ const results = [];
1909
+ const direct = await directInstallPresent(connector, platform, scope, projectDir);
1910
+ if (direct && installed) {
1911
+ results.push({
1912
+ check: `${id}: marketplace duplicate-registration`,
1913
+ status: "fail",
1914
+ message: `installed BOTH directly and as a marketplace plugin on ${platform} \u2014 hooks + MCP server are duplicated`,
1915
+ fix: `uninstall one method: \`agent-connector uninstall --method marketplace --targets ${platform}\` or \`agent-connector uninstall --targets ${platform}\``
1916
+ });
1917
+ }
1918
+ if (record && !installed) {
1919
+ results.push({
1920
+ check: `${id}: marketplace state drift`,
1921
+ status: "warn",
1922
+ message: `recorded as marketplace-installed but ${platform}'s state shows the plugin absent`,
1923
+ fix: `re-run \`agent-connector install --method marketplace --targets ${platform}\` or \`agent-connector uninstall --method marketplace --targets ${platform}\` to reconcile`
1924
+ });
1925
+ } else if (!record && installed) {
1926
+ results.push({
1927
+ check: `${id}: marketplace state drift`,
1928
+ status: "warn",
1929
+ message: `${platform} lists the plugin installed but agent-connector has no record of it (manual install?)`,
1930
+ fix: `adopt it: \`agent-connector install --method marketplace --targets ${platform}\` (idempotent), or remove it via the host's plugin uninstall`
1931
+ });
1932
+ }
1933
+ if (record) {
1934
+ if (connector.version !== "0.0.0" && connector.version !== record.version) {
1935
+ results.push({
1936
+ check: `${id}: marketplace staleness`,
1937
+ status: "warn",
1938
+ message: `connector.version ${connector.version} \u2260 installed ${record.version}`,
1939
+ fix: `agent-connector upgrade --method marketplace --targets ${platform}`
1940
+ });
1941
+ } else if (existsSync6(record.bundleDir) && record.contentHash !== "" && hashDirectory(record.bundleDir) !== record.contentHash) {
1942
+ results.push({
1943
+ check: `${id}: marketplace staleness`,
1944
+ status: "warn",
1945
+ message: `staged bundle changed since install \u2014 ${platform}'s cached copy is stale`,
1946
+ fix: `agent-connector upgrade --method marketplace --targets ${platform}`
1947
+ });
1948
+ }
1949
+ }
1950
+ if (installed && !existsSync6(homeBinPath())) {
1951
+ results.push({
1952
+ check: `${id}: marketplace home-bin`,
1953
+ status: "fail",
1954
+ message: `installed plugin hooks exec ${homeBinPath()} which does not exist`,
1955
+ fix: "agent-connector upgrade"
1956
+ });
1957
+ }
1958
+ if (record && driver.binary() == null) {
1959
+ results.push({
1960
+ check: `${id}: marketplace host binary`,
1961
+ status: "warn",
1962
+ message: `${platform} CLI not found on PATH \u2014 the recorded marketplace install cannot be managed until it is reinstalled`
1963
+ });
1964
+ }
1965
+ if (results.every((r) => r.status === "pass")) {
1966
+ results.push({
1967
+ check: `${id}: marketplace install`,
1968
+ status: "pass",
1969
+ message: `${id} installed via marketplace on ${platform} (version ${record?.version ?? "unknown"})`
1970
+ });
1971
+ }
1972
+ return { platform, results };
1973
+ }
1974
+
1975
+ export {
1976
+ installConnector,
1977
+ syncConnector,
1978
+ uninstallConnector,
1979
+ purgeFrameworkState,
1980
+ resolveCliEntry,
1981
+ resolveUninstallTargets,
1982
+ parseInstallMethod,
1983
+ parseUninstallMethod,
1984
+ installViaMarketplace,
1985
+ uninstallViaMarketplace,
1986
+ upgradeViaMarketplace,
1987
+ marketplaceDoctorChecks
1988
+ };
1989
+ //# sourceMappingURL=chunk-QADSG2JY.js.map