@ken-jo/agent-connector 0.3.0 → 0.4.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 (166) hide show
  1. package/README.md +188 -20
  2. package/dist/action-PLZW5BGX.js +54 -0
  3. package/dist/action-PLZW5BGX.js.map +1 -0
  4. package/dist/{amp-YGV66GJT.js → amp-B524U6LO.js} +81 -5
  5. package/dist/amp-B524U6LO.js.map +1 -0
  6. package/dist/{antigravity-7IBYFO6P.js → antigravity-ANDKF3K7.js} +4 -4
  7. package/dist/{antigravity-cli-JEOUP2VN.js → antigravity-cli-LLRPDDOT.js} +4 -4
  8. package/dist/chunk-22Y24V7O.js +21 -0
  9. package/dist/chunk-22Y24V7O.js.map +1 -0
  10. package/dist/chunk-3PFFTFCU.js +190 -0
  11. package/dist/chunk-3PFFTFCU.js.map +1 -0
  12. package/dist/{chunk-SCVBYSF7.js → chunk-6XHB6BE3.js} +1 -1
  13. package/dist/chunk-76FUSVIA.js +43 -0
  14. package/dist/chunk-76FUSVIA.js.map +1 -0
  15. package/dist/{chunk-GSGDG2BV.js → chunk-ADTHT2FN.js} +109 -16
  16. package/dist/chunk-ADTHT2FN.js.map +1 -0
  17. package/dist/{chunk-ZAM4M7LI.js → chunk-BHUCB6LY.js} +135 -296
  18. package/dist/chunk-BHUCB6LY.js.map +1 -0
  19. package/dist/{chunk-MEH7GC7R.js → chunk-BXGFUENQ.js} +20 -3
  20. package/dist/chunk-BXGFUENQ.js.map +1 -0
  21. package/dist/chunk-CIRDOY2V.js +351 -0
  22. package/dist/chunk-CIRDOY2V.js.map +1 -0
  23. package/dist/{chunk-QADSG2JY.js → chunk-G3ER4SY5.js} +1152 -106
  24. package/dist/chunk-G3ER4SY5.js.map +1 -0
  25. package/dist/{chunk-44HUXXC6.js → chunk-G5CQHVQG.js} +232 -5
  26. package/dist/chunk-G5CQHVQG.js.map +1 -0
  27. package/dist/{chunk-PD4VFEBY.js → chunk-IW77FFNX.js} +39 -1
  28. package/dist/chunk-IW77FFNX.js.map +1 -0
  29. package/dist/chunk-J3D5W5T2.js +83 -0
  30. package/dist/chunk-J3D5W5T2.js.map +1 -0
  31. package/dist/{openclaw-UZ5BMCGQ.js → chunk-KGGBNKI4.js} +125 -33
  32. package/dist/chunk-KGGBNKI4.js.map +1 -0
  33. package/dist/{chunk-3JCRUROU.js → chunk-KGJWE6A2.js} +13 -4
  34. package/dist/chunk-KGJWE6A2.js.map +1 -0
  35. package/dist/{chunk-DS3LUFVN.js → chunk-KGXDYGNJ.js} +26 -16
  36. package/dist/chunk-KGXDYGNJ.js.map +1 -0
  37. package/dist/{chunk-KVAAFYRR.js → chunk-KHO5WNTP.js} +2 -18
  38. package/dist/chunk-KHO5WNTP.js.map +1 -0
  39. package/dist/{chunk-KQSNDFBN.js → chunk-LL4OBGRU.js} +3 -3
  40. package/dist/{chunk-WNREHWKL.js → chunk-MAH6QQTO.js} +3 -3
  41. package/dist/{claude-code-4275HQU4.js → claude-code-MLWS7CW6.js} +180 -28
  42. package/dist/claude-code-MLWS7CW6.js.map +1 -0
  43. package/dist/cli/sdk.js +4 -3
  44. package/dist/cli/sdk.js.map +1 -1
  45. package/dist/cli.js +1 -1
  46. package/dist/{codebuff-LE3KBZGM.js → codebuff-SH2S3QAN.js} +102 -5
  47. package/dist/codebuff-SH2S3QAN.js.map +1 -0
  48. package/dist/{codex-4BY2ZJIW.js → codex-BXA4FIF4.js} +3 -3
  49. package/dist/{copilot-cli-IPISKO3V.js → copilot-cli-ROIBTTDT.js} +3 -3
  50. package/dist/{crush-X3E25EQG.js → crush-QLEFLHUC.js} +3 -3
  51. package/dist/{cursor-UH3W622V.js → cursor-ASJPWZ4R.js} +6 -3
  52. package/dist/cursor-ASJPWZ4R.js.map +1 -0
  53. package/dist/{detect-MNDHGLSV.js → detect-ZIIS55LD.js} +4 -3
  54. package/dist/{detect-MNDHGLSV.js.map → detect-ZIIS55LD.js.map} +1 -1
  55. package/dist/{doctor-N5KZDD55.js → doctor-TJUGSQ5C.js} +17 -13
  56. package/dist/doctor-TJUGSQ5C.js.map +1 -0
  57. package/dist/{droid-T5VBRXZF.js → droid-NCSS3B5Y.js} +170 -4
  58. package/dist/droid-NCSS3B5Y.js.map +1 -0
  59. package/dist/{gemini-cli-ZSUK55UA.js → gemini-cli-SLVWLXJW.js} +3 -3
  60. package/dist/{goose-M3RR3CZG.js → goose-PBA7KTE5.js} +100 -5
  61. package/dist/goose-PBA7KTE5.js.map +1 -0
  62. package/dist/{hermes-CUNKIAR5.js → hermes-ONDCECP2.js} +3 -3
  63. package/dist/{hook-7LUOKIXH.js → hook-OU522ABV.js} +12 -9
  64. package/dist/{hook-7LUOKIXH.js.map → hook-OU522ABV.js.map} +1 -1
  65. package/dist/index.d.ts +71 -3
  66. package/dist/index.js +22 -3
  67. package/dist/{install-TQTT4F3I.js → install-UDCBL7HP.js} +12 -10
  68. package/dist/{install-TQTT4F3I.js.map → install-UDCBL7HP.js.map} +1 -1
  69. package/dist/introspect-DuZSPSHB.d.ts +53 -0
  70. package/dist/{jetbrains-copilot-C3HDGGML.js → jetbrains-copilot-YABZ3HFA.js} +3 -3
  71. package/dist/{kilo-PKAJCWPX.js → kilo-F3ZDZKCT.js} +3 -3
  72. package/dist/{kilo-cli-QWUVXG2O.js → kilo-cli-3HWPWBY4.js} +3 -3
  73. package/dist/{kimi-EVD7YKXS.js → kimi-NV72FVAO.js} +3 -3
  74. package/dist/{kiro-UQRY6EIO.js → kiro-C5W6AGDR.js} +3 -3
  75. package/dist/{leaderboard-MUJ7DURM.js → leaderboard-TIBVONVT.js} +3 -3
  76. package/dist/mimo-code-IMM5YM3M.js +754 -0
  77. package/dist/mimo-code-IMM5YM3M.js.map +1 -0
  78. package/dist/{mux-5CI5FP2W.js → mux-DSSH4AFS.js} +3 -3
  79. package/dist/nemoclaw-3EVNKD3I.js +58 -0
  80. package/dist/nemoclaw-3EVNKD3I.js.map +1 -0
  81. package/dist/{omp-AXA7XY22.js → omp-Z6FRNDPN.js} +3 -3
  82. package/dist/openclaw-7G437NT7.js +18 -0
  83. package/dist/openclaw-7G437NT7.js.map +1 -0
  84. package/dist/{opencode-NZBRLJN3.js → opencode-Q6KBSAKW.js} +3 -3
  85. package/dist/{package-SYPA5POO.js → package-RMPN57YZ.js} +7 -6
  86. package/dist/{package-SYPA5POO.js.map → package-RMPN57YZ.js.map} +1 -1
  87. package/dist/{pi-WMTUX6ES.js → pi-LZT6WWAZ.js} +2 -2
  88. package/dist/{qwen-code-MRRIUQ3P.js → qwen-code-WQBKRPWJ.js} +3 -3
  89. package/dist/{roo-code-NI5UU6JB.js → roo-code-GWSNU3OW.js} +137 -4
  90. package/dist/roo-code-GWSNU3OW.js.map +1 -0
  91. package/dist/runtime/index.d.ts +96 -2
  92. package/dist/runtime/index.js +18 -10
  93. package/dist/sdk/index.d.ts +52 -0
  94. package/dist/sdk/index.js +81 -0
  95. package/dist/sdk/index.js.map +1 -0
  96. package/dist/sdk/test.d.ts +74 -0
  97. package/dist/sdk/test.js +291 -0
  98. package/dist/sdk/test.js.map +1 -0
  99. package/dist/{serve-XJOSA7BY.js → serve-D3GO36UT.js} +12 -9
  100. package/dist/{serve-XJOSA7BY.js.map → serve-D3GO36UT.js.map} +1 -1
  101. package/dist/{status-DYSS6BOR.js → status-KOSKCETC.js} +10 -8
  102. package/dist/{status-DYSS6BOR.js.map → status-KOSKCETC.js.map} +1 -1
  103. package/dist/statusline-ZNTCL5YC.js +59 -0
  104. package/dist/statusline-ZNTCL5YC.js.map +1 -0
  105. package/dist/{telemetry-IJ4GP2Q3.js → telemetry-XIKUUGTQ.js} +5 -4
  106. package/dist/{telemetry-IJ4GP2Q3.js.map → telemetry-XIKUUGTQ.js.map} +1 -1
  107. package/dist/{trae-CQC72RHH.js → trae-NWT3TMSZ.js} +99 -5
  108. package/dist/trae-NWT3TMSZ.js.map +1 -0
  109. package/dist/{types-CNigfEuR.d.ts → types-BEKsAium.d.ts} +268 -6
  110. package/dist/{uninstall-3DXIBRCV.js → uninstall-2LBLQVFA.js} +12 -10
  111. package/dist/{uninstall-3DXIBRCV.js.map → uninstall-2LBLQVFA.js.map} +1 -1
  112. package/dist/{upgrade-GJEB3ITY.js → upgrade-ZC2S7VRN.js} +12 -10
  113. package/dist/{upgrade-GJEB3ITY.js.map → upgrade-ZC2S7VRN.js.map} +1 -1
  114. package/dist/{usage-2BJLM3GJ.js → usage-E2A4O2CY.js} +3 -3
  115. package/dist/{usage-event-472X3DOY.js → usage-event-JKMC4QC4.js} +11 -8
  116. package/dist/{usage-event-472X3DOY.js.map → usage-event-JKMC4QC4.js.map} +1 -1
  117. package/dist/{vscode-copilot-4RVRTCKV.js → vscode-copilot-ALV45XF6.js} +3 -3
  118. package/dist/{warp-X6MB67NT.js → warp-H6IX4U3J.js} +3 -3
  119. package/dist/{zed-NTCYW4V2.js → zed-RH34TNSW.js} +3 -3
  120. package/package.json +9 -1
  121. package/dist/amp-YGV66GJT.js.map +0 -1
  122. package/dist/chunk-3JCRUROU.js.map +0 -1
  123. package/dist/chunk-44HUXXC6.js.map +0 -1
  124. package/dist/chunk-DS3LUFVN.js.map +0 -1
  125. package/dist/chunk-GSGDG2BV.js.map +0 -1
  126. package/dist/chunk-KVAAFYRR.js.map +0 -1
  127. package/dist/chunk-MEH7GC7R.js.map +0 -1
  128. package/dist/chunk-NJU275US.js +0 -238
  129. package/dist/chunk-NJU275US.js.map +0 -1
  130. package/dist/chunk-PD4VFEBY.js.map +0 -1
  131. package/dist/chunk-QADSG2JY.js.map +0 -1
  132. package/dist/chunk-ZAM4M7LI.js.map +0 -1
  133. package/dist/claude-code-4275HQU4.js.map +0 -1
  134. package/dist/codebuff-LE3KBZGM.js.map +0 -1
  135. package/dist/cursor-UH3W622V.js.map +0 -1
  136. package/dist/doctor-N5KZDD55.js.map +0 -1
  137. package/dist/droid-T5VBRXZF.js.map +0 -1
  138. package/dist/goose-M3RR3CZG.js.map +0 -1
  139. package/dist/openclaw-UZ5BMCGQ.js.map +0 -1
  140. package/dist/roo-code-NI5UU6JB.js.map +0 -1
  141. package/dist/trae-CQC72RHH.js.map +0 -1
  142. /package/dist/{antigravity-7IBYFO6P.js.map → antigravity-ANDKF3K7.js.map} +0 -0
  143. /package/dist/{antigravity-cli-JEOUP2VN.js.map → antigravity-cli-LLRPDDOT.js.map} +0 -0
  144. /package/dist/{chunk-SCVBYSF7.js.map → chunk-6XHB6BE3.js.map} +0 -0
  145. /package/dist/{chunk-KQSNDFBN.js.map → chunk-LL4OBGRU.js.map} +0 -0
  146. /package/dist/{chunk-WNREHWKL.js.map → chunk-MAH6QQTO.js.map} +0 -0
  147. /package/dist/{codex-4BY2ZJIW.js.map → codex-BXA4FIF4.js.map} +0 -0
  148. /package/dist/{copilot-cli-IPISKO3V.js.map → copilot-cli-ROIBTTDT.js.map} +0 -0
  149. /package/dist/{crush-X3E25EQG.js.map → crush-QLEFLHUC.js.map} +0 -0
  150. /package/dist/{gemini-cli-ZSUK55UA.js.map → gemini-cli-SLVWLXJW.js.map} +0 -0
  151. /package/dist/{hermes-CUNKIAR5.js.map → hermes-ONDCECP2.js.map} +0 -0
  152. /package/dist/{jetbrains-copilot-C3HDGGML.js.map → jetbrains-copilot-YABZ3HFA.js.map} +0 -0
  153. /package/dist/{kilo-PKAJCWPX.js.map → kilo-F3ZDZKCT.js.map} +0 -0
  154. /package/dist/{kilo-cli-QWUVXG2O.js.map → kilo-cli-3HWPWBY4.js.map} +0 -0
  155. /package/dist/{kimi-EVD7YKXS.js.map → kimi-NV72FVAO.js.map} +0 -0
  156. /package/dist/{kiro-UQRY6EIO.js.map → kiro-C5W6AGDR.js.map} +0 -0
  157. /package/dist/{leaderboard-MUJ7DURM.js.map → leaderboard-TIBVONVT.js.map} +0 -0
  158. /package/dist/{mux-5CI5FP2W.js.map → mux-DSSH4AFS.js.map} +0 -0
  159. /package/dist/{omp-AXA7XY22.js.map → omp-Z6FRNDPN.js.map} +0 -0
  160. /package/dist/{opencode-NZBRLJN3.js.map → opencode-Q6KBSAKW.js.map} +0 -0
  161. /package/dist/{pi-WMTUX6ES.js.map → pi-LZT6WWAZ.js.map} +0 -0
  162. /package/dist/{qwen-code-MRRIUQ3P.js.map → qwen-code-WQBKRPWJ.js.map} +0 -0
  163. /package/dist/{usage-2BJLM3GJ.js.map → usage-E2A4O2CY.js.map} +0 -0
  164. /package/dist/{vscode-copilot-4RVRTCKV.js.map → vscode-copilot-ALV45XF6.js.map} +0 -0
  165. /package/dist/{warp-X6MB67NT.js.map → warp-H6IX4U3J.js.map} +0 -0
  166. /package/dist/{zed-NTCYW4V2.js.map → zed-RH34TNSW.js.map} +0 -0
@@ -2,13 +2,14 @@
2
2
  import {
3
3
  installInstructions,
4
4
  packageConnector
5
- } from "./chunk-WNREHWKL.js";
5
+ } from "./chunk-MAH6QQTO.js";
6
6
  import {
7
7
  MARKETPLACE_NAME,
8
8
  agyPluginInstalled,
9
9
  agyStagingRoot,
10
10
  anyClaudeAgentConnectorPlugins,
11
11
  anyCodexAgentConnectorPlugins,
12
+ anyDroidAgentConnectorPlugins,
12
13
  anyMarketplaceRecordsRemain,
13
14
  claudeKnownMarketplacePath,
14
15
  claudePluginInstalled,
@@ -19,29 +20,46 @@ import {
19
20
  codexPluginKey,
20
21
  codexStagingRoot,
21
22
  connectorHasMarketplaceRecords,
23
+ droidMarketplaceSource,
24
+ droidPluginInstalled,
25
+ droidPluginKey,
26
+ droidStagingRoot,
27
+ geminiExtensionInstalled,
28
+ geminiStagingRoot,
22
29
  hashDirectory,
23
30
  marketplaceEvidence,
24
31
  marketplaceRoot,
32
+ npmConfigFilePath,
33
+ npmPluginArrayEntry,
34
+ npmPluginInstalled,
35
+ npmStagingRoot,
36
+ qwenExtensionInstalled,
37
+ qwenStagingRoot,
25
38
  readMarketplaceInstalls,
39
+ readNpmConfig,
26
40
  writeMarketplaceInstalls
27
- } from "./chunk-44HUXXC6.js";
41
+ } from "./chunk-G5CQHVQG.js";
28
42
  import {
29
- log,
30
43
  spawnChild
31
- } from "./chunk-KVAAFYRR.js";
44
+ } from "./chunk-22Y24V7O.js";
32
45
  import {
33
- REGISTERED_PLATFORM_IDS,
34
- detectInstalledPlatforms,
35
- loadAdapter
36
- } from "./chunk-NJU275US.js";
46
+ detectInstalledPlatforms
47
+ } from "./chunk-J3D5W5T2.js";
48
+ import {
49
+ log
50
+ } from "./chunk-KHO5WNTP.js";
37
51
  import {
38
52
  deregisterConnector,
39
53
  loadRegisteredConnector,
40
54
  registerConnector
41
- } from "./chunk-3JCRUROU.js";
55
+ } from "./chunk-KGJWE6A2.js";
42
56
  import {
43
57
  configPatchManualEdit
44
- } from "./chunk-GSGDG2BV.js";
58
+ } from "./chunk-ADTHT2FN.js";
59
+ import {
60
+ REGISTERED_PLATFORM_IDS,
61
+ loadAdapter
62
+ } from "./chunk-3PFFTFCU.js";
45
63
  import {
46
64
  hasMemoryLedger
47
65
  } from "./chunk-N6HH7K6P.js";
@@ -67,7 +85,7 @@ async function installConnector(opts) {
67
85
  log.warn(`ensureHomeBin failed: ${errMessage(err)}`);
68
86
  }
69
87
  try {
70
- registerConnector(connector, modulePath);
88
+ registerConnector(connector, modulePath, scope);
71
89
  } catch (err) {
72
90
  log.warn(`registerConnector failed: ${errMessage(err)}`);
73
91
  }
@@ -160,6 +178,16 @@ async function installConnector(opts) {
160
178
  }
161
179
  }
162
180
  }
181
+ if (connector.statusline != null) {
182
+ runStep(id, "installStatusline", result, () => {
183
+ pushAll(result.changes, adapter.installStatusline(ctx));
184
+ });
185
+ }
186
+ if ((connector.actions ?? []).length) {
187
+ runStep(id, "installActions", result, () => {
188
+ pushAll(result.changes, adapter.installActions(ctx));
189
+ });
190
+ }
163
191
  }
164
192
  return result;
165
193
  }
@@ -182,6 +210,16 @@ async function uninstallConnector(opts) {
182
210
  const adapter = await tryLoadAdapter(id, result);
183
211
  if (!adapter) continue;
184
212
  const ctx = buildContext(connector, id, scope, projectDir, dryRun);
213
+ if ((connector.actions ?? []).length) {
214
+ runStep(id, "uninstallActions", result, () => {
215
+ pushAll(result.changes, adapter.uninstallActions(ctx));
216
+ });
217
+ }
218
+ if (adapter.capabilities.supportsStatusline ?? false) {
219
+ runStep(id, "uninstallStatusline", result, () => {
220
+ pushAll(result.changes, adapter.uninstallStatusline(ctx));
221
+ });
222
+ }
185
223
  if (adapter.capabilities.supportsConfigPatch ?? false) {
186
224
  runStep(id, "uninstallConfigPatches", result, () => {
187
225
  pushAll(result.changes, adapter.uninstallConfigPatches(ctx));
@@ -394,6 +432,7 @@ function syntheticConnector(id) {
394
432
  skills: [],
395
433
  subagents: [],
396
434
  memory: [],
435
+ actions: [],
397
436
  platforms: {},
398
437
  targets: "auto"
399
438
  };
@@ -409,15 +448,15 @@ function errMessage(err) {
409
448
  }
410
449
 
411
450
  // 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";
451
+ import { existsSync as existsSync10, readdirSync as readdirSync5, readFileSync as readFileSync4, rmSync as rmSync9 } from "fs";
452
+ import { join as join10 } from "path";
414
453
 
415
454
  // src/core/marketplace-drivers/claude.ts
416
455
  import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync, rmSync as rmSync2, writeFileSync } from "fs";
417
456
  import { dirname as dirname2, join as join3 } from "path";
418
457
 
419
458
  // src/core/marketplace-drivers/shared.ts
420
- import { existsSync as existsSync2, statSync } from "fs";
459
+ import { existsSync as existsSync2, realpathSync, statSync } from "fs";
421
460
  import { delimiter, join as join2, resolve } from "path";
422
461
  var HOST_COMMAND_TIMEOUT_MS = 3e4;
423
462
  function findOnPath(command) {
@@ -437,7 +476,10 @@ function findOnPath(command) {
437
476
  }
438
477
  return null;
439
478
  }
440
- function runHostCommand(command, args, timeoutMs = HOST_COMMAND_TIMEOUT_MS) {
479
+ function runHostCommand(command, args, optionsOrTimeout = HOST_COMMAND_TIMEOUT_MS) {
480
+ const opts = typeof optionsOrTimeout === "number" ? { timeoutMs: optionsOrTimeout } : optionsOrTimeout;
481
+ const timeoutMs = opts.timeoutMs ?? HOST_COMMAND_TIMEOUT_MS;
482
+ const cwd = opts.cwd;
441
483
  return new Promise((resolvePromise) => {
442
484
  let stdout = "";
443
485
  let stderr = "";
@@ -453,7 +495,10 @@ function runHostCommand(command, args, timeoutMs = HOST_COMMAND_TIMEOUT_MS) {
453
495
  try {
454
496
  child = spawnChild(command, args, {
455
497
  stdio: ["ignore", "pipe", "pipe"],
456
- env: process.env
498
+ env: process.env,
499
+ // Only set cwd when a caller asked for it — omitting the key inherits
500
+ // the parent's cwd, keeping claude/codex/agy spawns byte-identical.
501
+ ...cwd != null ? { cwd } : {}
457
502
  });
458
503
  } catch (err) {
459
504
  const message = err instanceof Error ? err.message : String(err);
@@ -512,7 +557,11 @@ function samePath(a, b) {
512
557
  if (process.platform === "win32") {
513
558
  s = s.replace(/^\\\\\?\\UNC\\/, "\\\\").replace(/^\\\\\?\\/, "");
514
559
  }
515
- s = resolve(s);
560
+ try {
561
+ s = realpathSync.native(s);
562
+ } catch {
563
+ s = resolve(s);
564
+ }
516
565
  return process.platform === "win32" ? s.toLowerCase() : s;
517
566
  };
518
567
  return norm(a) === norm(b);
@@ -1192,25 +1241,326 @@ var codexDriver = {
1192
1241
  }
1193
1242
  };
1194
1243
 
1195
- // src/core/marketplace-drivers/agy.ts
1196
- import { existsSync as existsSync5, rmSync as rmSync4 } from "fs";
1244
+ // src/core/marketplace-drivers/droid.ts
1245
+ import { existsSync as existsSync5, readdirSync as readdirSync4, readFileSync as readFileSync3, rmSync as rmSync4, writeFileSync as writeFileSync3 } from "fs";
1197
1246
  import { join as join5 } from "path";
1198
- function agyBinary() {
1199
- return findOnPath("agy");
1247
+ var PLATFORM3 = "droid";
1248
+ function droidBinary() {
1249
+ return findOnPath("droid");
1250
+ }
1251
+ function droidManualInstallCommands(connectorId, stagingRoot) {
1252
+ return `droid plugin marketplace add ${stagingRoot} && droid plugin install ${droidPluginKey(connectorId)}`;
1200
1253
  }
1201
1254
  function errMessage4(err) {
1202
1255
  return err instanceof Error ? err.message : String(err);
1203
1256
  }
1257
+ function warn3(detail, path) {
1258
+ return { platform: PLATFORM3, action: "warn", detail, ...path ? { path } : {} };
1259
+ }
1204
1260
  function failDetail3(step, r) {
1205
1261
  if (r.timedOut) return `${step} timed out`;
1206
1262
  if (r.error) return `${step} failed to spawn: ${r.error}`;
1207
1263
  const line = firstLine(r.stderr) || firstLine(r.stdout);
1208
1264
  return `${step} exited ${r.code}${line ? `: ${line}` : ""}`;
1209
1265
  }
1210
- function stageAgyBundle(connector, platform, changes) {
1211
- const stagingRoot = agyStagingRoot();
1266
+ function stagedDroidPlugins(stagingRoot) {
1267
+ if (!existsSync5(stagingRoot)) return [];
1268
+ try {
1269
+ return readdirSync4(stagingRoot).filter(
1270
+ (name) => existsSync5(join5(stagingRoot, name, ".factory-plugin", "plugin.json"))
1271
+ ).sort();
1272
+ } catch {
1273
+ return [];
1274
+ }
1275
+ }
1276
+ function droidCatalogPath(stagingRoot) {
1277
+ return join5(stagingRoot, "marketplace.json");
1278
+ }
1279
+ function regenerateDroidCatalog(stagingRoot, changes) {
1280
+ const catalogPath = droidCatalogPath(stagingRoot);
1281
+ const plugins = stagedDroidPlugins(stagingRoot).map((name) => {
1282
+ let description = `${name} \u2014 connector emitted by agent-connector`;
1283
+ try {
1284
+ const manifest = JSON.parse(
1285
+ readFileSync3(join5(stagingRoot, name, ".factory-plugin", "plugin.json"), "utf8")
1286
+ );
1287
+ if (typeof manifest.description === "string") description = manifest.description;
1288
+ } catch {
1289
+ }
1290
+ return { name, source: `./${name}`, description };
1291
+ });
1292
+ const catalog = { name: MARKETPLACE_NAME, owner: { name: MARKETPLACE_NAME }, plugins };
1293
+ const serialized = `${JSON.stringify(catalog, null, 2)}
1294
+ `;
1295
+ let existing = null;
1296
+ try {
1297
+ existing = readFileSync3(catalogPath, "utf8");
1298
+ } catch {
1299
+ }
1300
+ if (existing === serialized) return;
1301
+ ensureDir(stagingRoot);
1302
+ writeFileSync3(catalogPath, serialized, "utf8");
1303
+ changes.push({
1304
+ platform: PLATFORM3,
1305
+ action: existing == null ? "create" : "update",
1306
+ path: catalogPath,
1307
+ detail: `regenerated shared marketplace catalog (${plugins.length} plugin(s))`
1308
+ });
1309
+ }
1310
+ function stageDroidBundle(connector, changes) {
1311
+ const stagingRoot = droidStagingRoot();
1212
1312
  const pluginDir = join5(stagingRoot, connector.id);
1213
1313
  const existed = existsSync5(pluginDir);
1314
+ const result = packageConnector(connector, { outDir: stagingRoot, format: "factory-plugin" });
1315
+ changes.push({
1316
+ platform: PLATFORM3,
1317
+ action: existed ? "update" : "create",
1318
+ path: pluginDir,
1319
+ detail: `staged marketplace bundle (${result.files.length} files, factory-plugin)`
1320
+ });
1321
+ regenerateDroidCatalog(stagingRoot, changes);
1322
+ return { pluginDir, contentHash: hashDirectory(pluginDir) };
1323
+ }
1324
+ async function driveInstall2(connectorId) {
1325
+ const stagingRoot = droidStagingRoot();
1326
+ const changes = [];
1327
+ const pluginKey = droidPluginKey(connectorId);
1328
+ const bin = droidBinary();
1329
+ if (!bin) {
1330
+ changes.push(
1331
+ warn3(
1332
+ `droid CLI not found on PATH \u2014 bundle staged but not installed. Install manually: ${droidManualInstallCommands(connectorId, stagingRoot)}`,
1333
+ stagingRoot
1334
+ )
1335
+ );
1336
+ return { changes, ok: false };
1337
+ }
1338
+ const registeredAt = droidMarketplaceSource(MARKETPLACE_NAME);
1339
+ if (samePath(registeredAt, stagingRoot)) {
1340
+ changes.push({
1341
+ platform: PLATFORM3,
1342
+ action: "skip",
1343
+ path: stagingRoot,
1344
+ detail: `marketplace "${MARKETPLACE_NAME}" already registered`
1345
+ });
1346
+ } else if (registeredAt != null) {
1347
+ changes.push(
1348
+ warn3(
1349
+ `a marketplace named "${MARKETPLACE_NAME}" is already registered at ${registeredAt} (not ours) \u2014 refusing to touch it. Remove it first (droid plugin marketplace remove ${MARKETPLACE_NAME}) or install manually: ` + droidManualInstallCommands(connectorId, stagingRoot)
1350
+ )
1351
+ );
1352
+ return { changes, ok: false };
1353
+ } else {
1354
+ const add = await runHostCommand(bin, ["plugin", "marketplace", "add", stagingRoot]);
1355
+ if (!samePath(droidMarketplaceSource(MARKETPLACE_NAME), stagingRoot)) {
1356
+ changes.push(
1357
+ warn3(
1358
+ `could not register the local marketplace \u2014 ` + failDetail3("droid plugin marketplace add", add) + `. Install manually: ${droidManualInstallCommands(connectorId, stagingRoot)}`,
1359
+ stagingRoot
1360
+ )
1361
+ );
1362
+ return { changes, ok: false };
1363
+ }
1364
+ changes.push({
1365
+ platform: PLATFORM3,
1366
+ action: "create",
1367
+ path: stagingRoot,
1368
+ detail: `registered local marketplace "${MARKETPLACE_NAME}" (droid plugin marketplace add)`
1369
+ });
1370
+ }
1371
+ if (droidPluginInstalled(connectorId)) {
1372
+ changes.push({
1373
+ platform: PLATFORM3,
1374
+ action: "skip",
1375
+ detail: `plugin ${pluginKey} already installed`
1376
+ });
1377
+ return { changes, ok: true };
1378
+ }
1379
+ const install = await runHostCommand(bin, ["plugin", "install", pluginKey]);
1380
+ if (!install.ok || !droidPluginInstalled(connectorId)) {
1381
+ changes.push(
1382
+ warn3(`plugin install did not complete \u2014 ` + failDetail3(`droid plugin install ${pluginKey}`, install))
1383
+ );
1384
+ return { changes, ok: false };
1385
+ }
1386
+ changes.push({
1387
+ platform: PLATFORM3,
1388
+ action: "create",
1389
+ detail: `installed plugin ${pluginKey} (scope user)`
1390
+ });
1391
+ return { changes, ok: true };
1392
+ }
1393
+ async function driveUninstall2(connectorId) {
1394
+ const changes = [];
1395
+ const pluginKey = droidPluginKey(connectorId);
1396
+ if (!droidPluginInstalled(connectorId)) {
1397
+ changes.push({
1398
+ platform: PLATFORM3,
1399
+ action: "skip",
1400
+ detail: `plugin ${pluginKey} not installed on droid`
1401
+ });
1402
+ return { changes, ok: true };
1403
+ }
1404
+ const bin = droidBinary();
1405
+ if (!bin) {
1406
+ changes.push(
1407
+ warn3(
1408
+ `droid CLI not found on PATH \u2014 cannot drive the uninstall. Run manually: droid plugin uninstall ${pluginKey}`
1409
+ )
1410
+ );
1411
+ return { changes, ok: false };
1412
+ }
1413
+ const remove = await runHostCommand(bin, ["plugin", "uninstall", pluginKey]);
1414
+ if (!remove.ok || droidPluginInstalled(connectorId)) {
1415
+ changes.push(
1416
+ warn3(`plugin uninstall did not complete \u2014 ` + failDetail3(`droid plugin uninstall ${pluginKey}`, remove))
1417
+ );
1418
+ return { changes, ok: false };
1419
+ }
1420
+ changes.push({
1421
+ platform: PLATFORM3,
1422
+ action: "remove",
1423
+ detail: `uninstalled plugin ${pluginKey} (droid plugin uninstall)`
1424
+ });
1425
+ return { changes, ok: true };
1426
+ }
1427
+ async function driveMarketplaceRemove2(stagingRoot) {
1428
+ const bin = droidBinary();
1429
+ if (!bin) {
1430
+ return [
1431
+ warn3(
1432
+ `droid CLI not found on PATH \u2014 marketplace registration "${MARKETPLACE_NAME}" left behind. Run manually: droid plugin marketplace remove ${MARKETPLACE_NAME}`,
1433
+ stagingRoot
1434
+ )
1435
+ ];
1436
+ }
1437
+ const remove = await runHostCommand(bin, ["plugin", "marketplace", "remove", MARKETPLACE_NAME]);
1438
+ if (!remove.ok && droidMarketplaceSource(MARKETPLACE_NAME) != null) {
1439
+ return [
1440
+ warn3(
1441
+ `could not remove the marketplace registration \u2014 ` + failDetail3(`droid plugin marketplace remove ${MARKETPLACE_NAME}`, remove),
1442
+ stagingRoot
1443
+ )
1444
+ ];
1445
+ }
1446
+ return [
1447
+ {
1448
+ platform: PLATFORM3,
1449
+ action: "remove",
1450
+ path: stagingRoot,
1451
+ detail: `removed marketplace registration "${MARKETPLACE_NAME}" (no plugins remain)`
1452
+ }
1453
+ ];
1454
+ }
1455
+ var droidDriver = {
1456
+ platform: PLATFORM3,
1457
+ format: "factory-plugin",
1458
+ binary: droidBinary,
1459
+ stagingRoot: droidStagingRoot,
1460
+ pluginDir(id) {
1461
+ return join5(droidStagingRoot(), id);
1462
+ },
1463
+ installed: droidPluginInstalled,
1464
+ stage(connector, changes) {
1465
+ return stageDroidBundle(connector, changes).contentHash;
1466
+ },
1467
+ planInstall(connector, changes) {
1468
+ const stagingRoot = droidStagingRoot();
1469
+ const registered = samePath(droidMarketplaceSource(MARKETPLACE_NAME), stagingRoot);
1470
+ changes.push({
1471
+ platform: PLATFORM3,
1472
+ action: registered ? "skip" : "create",
1473
+ path: stagingRoot,
1474
+ detail: registered ? `marketplace "${MARKETPLACE_NAME}" already registered` : `run: droid plugin marketplace add ${stagingRoot}`
1475
+ });
1476
+ changes.push({
1477
+ platform: PLATFORM3,
1478
+ action: droidPluginInstalled(connector.id) ? "skip" : "create",
1479
+ detail: droidPluginInstalled(connector.id) ? `plugin ${droidPluginKey(connector.id)} already installed` : `run: droid plugin install ${droidPluginKey(connector.id)}`
1480
+ });
1481
+ },
1482
+ planUninstall(id, changes) {
1483
+ const stagingRoot = droidStagingRoot();
1484
+ const pluginDir = join5(stagingRoot, id);
1485
+ const pluginKey = droidPluginKey(id);
1486
+ changes.push({
1487
+ platform: PLATFORM3,
1488
+ action: droidPluginInstalled(id) ? "remove" : "skip",
1489
+ detail: droidPluginInstalled(id) ? `run: droid plugin uninstall ${pluginKey}` : `plugin ${pluginKey} not installed on droid`
1490
+ });
1491
+ if (existsSync5(pluginDir)) {
1492
+ changes.push({
1493
+ platform: PLATFORM3,
1494
+ action: "remove",
1495
+ path: pluginDir,
1496
+ detail: "remove staged marketplace bundle"
1497
+ });
1498
+ }
1499
+ const othersStaged = stagedDroidPlugins(stagingRoot).some((n) => n !== id);
1500
+ if (!othersStaged && samePath(droidMarketplaceSource(MARKETPLACE_NAME), stagingRoot)) {
1501
+ changes.push({
1502
+ platform: PLATFORM3,
1503
+ action: "remove",
1504
+ path: stagingRoot,
1505
+ detail: `run: droid plugin marketplace remove ${MARKETPLACE_NAME} (when no plugins remain)`
1506
+ });
1507
+ }
1508
+ },
1509
+ driveInstall: driveInstall2,
1510
+ driveUninstall: driveUninstall2,
1511
+ // droid has no documented `plugin update` verb — update IS re-stage (done by
1512
+ // the caller) + `plugin install`, assumed idempotent + version-cached (DOCS-only).
1513
+ async driveUpdate(id) {
1514
+ return driveInstall2(id);
1515
+ },
1516
+ async finishUninstall(id, changes) {
1517
+ const stagingRoot = droidStagingRoot();
1518
+ const pluginDir = join5(stagingRoot, id);
1519
+ if (existsSync5(pluginDir)) {
1520
+ try {
1521
+ rmSync4(pluginDir, { recursive: true, force: true });
1522
+ changes.push({
1523
+ platform: PLATFORM3,
1524
+ action: "remove",
1525
+ path: pluginDir,
1526
+ detail: "removed staged marketplace bundle"
1527
+ });
1528
+ } catch (err) {
1529
+ changes.push({
1530
+ platform: PLATFORM3,
1531
+ action: "warn",
1532
+ path: pluginDir,
1533
+ detail: `could not remove staged bundle: ${errMessage4(err)}`
1534
+ });
1535
+ }
1536
+ }
1537
+ if (existsSync5(stagingRoot)) regenerateDroidCatalog(stagingRoot, changes);
1538
+ const nothingStaged = stagedDroidPlugins(stagingRoot).length === 0;
1539
+ if (nothingStaged && !anyDroidAgentConnectorPlugins() && samePath(droidMarketplaceSource(MARKETPLACE_NAME), stagingRoot)) {
1540
+ changes.push(...await driveMarketplaceRemove2(stagingRoot));
1541
+ }
1542
+ }
1543
+ };
1544
+
1545
+ // src/core/marketplace-drivers/agy.ts
1546
+ import { existsSync as existsSync6, rmSync as rmSync5 } from "fs";
1547
+ import { join as join6 } from "path";
1548
+ function agyBinary() {
1549
+ return findOnPath("agy");
1550
+ }
1551
+ function errMessage5(err) {
1552
+ return err instanceof Error ? err.message : String(err);
1553
+ }
1554
+ function failDetail4(step, r) {
1555
+ if (r.timedOut) return `${step} timed out`;
1556
+ if (r.error) return `${step} failed to spawn: ${r.error}`;
1557
+ const line = firstLine(r.stderr) || firstLine(r.stdout);
1558
+ return `${step} exited ${r.code}${line ? `: ${line}` : ""}`;
1559
+ }
1560
+ function stageAgyBundle(connector, platform, changes) {
1561
+ const stagingRoot = agyStagingRoot();
1562
+ const pluginDir = join6(stagingRoot, connector.id);
1563
+ const existed = existsSync6(pluginDir);
1214
1564
  const result = packageConnector(connector, { outDir: stagingRoot, format: "agy-plugin" });
1215
1565
  changes.push({
1216
1566
  platform,
@@ -1221,27 +1571,27 @@ function stageAgyBundle(connector, platform, changes) {
1221
1571
  return { pluginDir, contentHash: hashDirectory(pluginDir) };
1222
1572
  }
1223
1573
  function makeAgyDriver(platform) {
1224
- const warn3 = (detail, path) => ({
1574
+ const warn6 = (detail, path) => ({
1225
1575
  platform,
1226
1576
  action: "warn",
1227
1577
  detail,
1228
1578
  ...path ? { path } : {}
1229
1579
  });
1230
- const manualInstall = (pluginDir) => `agy plugin install ${pluginDir}`;
1580
+ const manualInstall3 = (pluginDir) => `agy plugin install ${pluginDir}`;
1231
1581
  return {
1232
1582
  platform,
1233
1583
  format: "agy-plugin",
1234
1584
  binary: agyBinary,
1235
1585
  stagingRoot: agyStagingRoot,
1236
1586
  pluginDir(id) {
1237
- return join5(agyStagingRoot(), id);
1587
+ return join6(agyStagingRoot(), id);
1238
1588
  },
1239
1589
  installed: agyPluginInstalled,
1240
1590
  stage(connector, changes) {
1241
1591
  return stageAgyBundle(connector, platform, changes).contentHash;
1242
1592
  },
1243
1593
  planInstall(connector, changes) {
1244
- const pluginDir = join5(agyStagingRoot(), connector.id);
1594
+ const pluginDir = join6(agyStagingRoot(), connector.id);
1245
1595
  changes.push({
1246
1596
  platform,
1247
1597
  action: agyPluginInstalled(connector.id) ? "skip" : "create",
@@ -1249,13 +1599,13 @@ function makeAgyDriver(platform) {
1249
1599
  });
1250
1600
  },
1251
1601
  planUninstall(id, changes) {
1252
- const pluginDir = join5(agyStagingRoot(), id);
1602
+ const pluginDir = join6(agyStagingRoot(), id);
1253
1603
  changes.push({
1254
1604
  platform,
1255
1605
  action: agyPluginInstalled(id) ? "remove" : "skip",
1256
1606
  detail: agyPluginInstalled(id) ? `run: agy plugin uninstall ${id}` : `plugin ${id} not installed on ${platform}`
1257
1607
  });
1258
- if (existsSync5(pluginDir)) {
1608
+ if (existsSync6(pluginDir)) {
1259
1609
  changes.push({
1260
1610
  platform,
1261
1611
  action: "remove",
@@ -1266,12 +1616,12 @@ function makeAgyDriver(platform) {
1266
1616
  },
1267
1617
  async driveInstall(id) {
1268
1618
  const changes = [];
1269
- const pluginDir = join5(agyStagingRoot(), id);
1619
+ const pluginDir = join6(agyStagingRoot(), id);
1270
1620
  const bin = agyBinary();
1271
1621
  if (!bin) {
1272
1622
  changes.push(
1273
- warn3(
1274
- `agy CLI not found on PATH \u2014 bundle staged but not installed. Install manually: ${manualInstall(pluginDir)}`,
1623
+ warn6(
1624
+ `agy CLI not found on PATH \u2014 bundle staged but not installed. Install manually: ${manualInstall3(pluginDir)}`,
1275
1625
  pluginDir
1276
1626
  )
1277
1627
  );
@@ -1289,7 +1639,7 @@ function makeAgyDriver(platform) {
1289
1639
  const install = await runHostCommand(bin, ["plugin", "install", pluginDir]);
1290
1640
  if (!install.ok || !agyPluginInstalled(id)) {
1291
1641
  changes.push(
1292
- warn3(`plugin install did not complete \u2014 ` + failDetail3(`agy plugin install ${pluginDir}`, install))
1642
+ warn6(`plugin install did not complete \u2014 ` + failDetail4(`agy plugin install ${pluginDir}`, install))
1293
1643
  );
1294
1644
  return { changes, ok: false };
1295
1645
  }
@@ -1313,7 +1663,7 @@ function makeAgyDriver(platform) {
1313
1663
  const bin = agyBinary();
1314
1664
  if (!bin) {
1315
1665
  changes.push(
1316
- warn3(
1666
+ warn6(
1317
1667
  `agy CLI not found on PATH \u2014 cannot drive the uninstall. Run manually: agy plugin uninstall ${id}`
1318
1668
  )
1319
1669
  );
@@ -1322,7 +1672,7 @@ function makeAgyDriver(platform) {
1322
1672
  const uninstall = await runHostCommand(bin, ["plugin", "uninstall", id]);
1323
1673
  if (!uninstall.ok || agyPluginInstalled(id)) {
1324
1674
  changes.push(
1325
- warn3(`plugin uninstall did not complete \u2014 ` + failDetail3(`agy plugin uninstall ${id}`, uninstall))
1675
+ warn6(`plugin uninstall did not complete \u2014 ` + failDetail4(`agy plugin uninstall ${id}`, uninstall))
1326
1676
  );
1327
1677
  return { changes, ok: false };
1328
1678
  }
@@ -1338,12 +1688,12 @@ function makeAgyDriver(platform) {
1338
1688
  // the probe already shows it present (the bundle on disk changed).
1339
1689
  async driveUpdate(id) {
1340
1690
  const changes = [];
1341
- const pluginDir = join5(agyStagingRoot(), id);
1691
+ const pluginDir = join6(agyStagingRoot(), id);
1342
1692
  const bin = agyBinary();
1343
1693
  if (!bin) {
1344
1694
  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)}`,
1695
+ warn6(
1696
+ `agy CLI not found on PATH \u2014 bundle re-staged but the installed copy was not updated. Run manually: ${manualInstall3(pluginDir)}`,
1347
1697
  pluginDir
1348
1698
  )
1349
1699
  );
@@ -1352,7 +1702,7 @@ function makeAgyDriver(platform) {
1352
1702
  const install = await runHostCommand(bin, ["plugin", "install", pluginDir]);
1353
1703
  if (!install.ok || !agyPluginInstalled(id)) {
1354
1704
  changes.push(
1355
- warn3(`plugin install did not complete \u2014 ` + failDetail3(`agy plugin install ${pluginDir}`, install))
1705
+ warn6(`plugin install did not complete \u2014 ` + failDetail4(`agy plugin install ${pluginDir}`, install))
1356
1706
  );
1357
1707
  return { changes, ok: false };
1358
1708
  }
@@ -1366,10 +1716,10 @@ function makeAgyDriver(platform) {
1366
1716
  // Direct driver: no catalog, no marketplace de-registration. Cleanup is just
1367
1717
  // removing the staged bundle dir (the host copied it into its own store).
1368
1718
  async finishUninstall(id, changes) {
1369
- const pluginDir = join5(agyStagingRoot(), id);
1370
- if (existsSync5(pluginDir)) {
1719
+ const pluginDir = join6(agyStagingRoot(), id);
1720
+ if (existsSync6(pluginDir)) {
1371
1721
  try {
1372
- rmSync4(pluginDir, { recursive: true, force: true });
1722
+ rmSync5(pluginDir, { recursive: true, force: true });
1373
1723
  changes.push({
1374
1724
  platform,
1375
1725
  action: "remove",
@@ -1381,7 +1731,7 @@ function makeAgyDriver(platform) {
1381
1731
  platform,
1382
1732
  action: "warn",
1383
1733
  path: pluginDir,
1384
- detail: `could not remove staged bundle: ${errMessage4(err)}`
1734
+ detail: `could not remove staged bundle: ${errMessage5(err)}`
1385
1735
  });
1386
1736
  }
1387
1737
  }
@@ -1389,49 +1739,712 @@ function makeAgyDriver(platform) {
1389
1739
  };
1390
1740
  }
1391
1741
 
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;
1742
+ // src/core/marketplace-drivers/gemini.ts
1743
+ import { existsSync as existsSync7, rmSync as rmSync6 } from "fs";
1744
+ import { join as join7 } from "path";
1745
+ var PLATFORM4 = "gemini-cli";
1746
+ function geminiBinary() {
1747
+ return findOnPath("gemini");
1748
+ }
1749
+ function errMessage6(err) {
1750
+ return err instanceof Error ? err.message : String(err);
1401
1751
  }
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
- }
1752
+ function warn4(detail, path) {
1753
+ return { platform: PLATFORM4, action: "warn", detail, ...path ? { path } : {} };
1414
1754
  }
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;
1755
+ function failDetail5(step, r) {
1756
+ if (r.timedOut) return `${step} timed out`;
1757
+ if (r.error) return `${step} failed to spawn: ${r.error}`;
1758
+ const line = firstLine(r.stderr) || firstLine(r.stdout);
1759
+ return `${step} exited ${r.code}${line ? `: ${line}` : ""}`;
1422
1760
  }
1423
- function parseUninstallMethod(value) {
1424
- if (value == null || value === "auto" || value === "direct" || value === "marketplace") {
1425
- return value ?? "auto";
1426
- }
1427
- return null;
1761
+ function manualInstall(pluginDir) {
1762
+ return `gemini extensions install ${pluginDir} --consent`;
1428
1763
  }
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",
1764
+ function stageGeminiBundle(connector, changes) {
1765
+ const stagingRoot = geminiStagingRoot();
1766
+ const pluginDir = join7(stagingRoot, connector.id);
1767
+ const existed = existsSync7(pluginDir);
1768
+ const result = packageConnector(connector, { outDir: stagingRoot, format: "gemini-extension" });
1769
+ changes.push({
1770
+ platform: PLATFORM4,
1771
+ action: existed ? "update" : "create",
1772
+ path: pluginDir,
1773
+ detail: `staged marketplace bundle (${result.files.length} files, gemini-extension)`
1774
+ });
1775
+ return { pluginDir, contentHash: hashDirectory(pluginDir) };
1776
+ }
1777
+ var geminiDriver = {
1778
+ platform: PLATFORM4,
1779
+ format: "gemini-extension",
1780
+ binary: geminiBinary,
1781
+ stagingRoot: geminiStagingRoot,
1782
+ pluginDir(id) {
1783
+ return join7(geminiStagingRoot(), id);
1784
+ },
1785
+ installed: geminiExtensionInstalled,
1786
+ stage(connector, changes) {
1787
+ return stageGeminiBundle(connector, changes).contentHash;
1788
+ },
1789
+ planInstall(connector, changes) {
1790
+ const pluginDir = join7(geminiStagingRoot(), connector.id);
1791
+ changes.push({
1792
+ platform: PLATFORM4,
1793
+ action: geminiExtensionInstalled(connector.id) ? "skip" : "create",
1794
+ detail: geminiExtensionInstalled(connector.id) ? `extension ${connector.id} already installed` : `run: ${manualInstall(pluginDir)}`
1795
+ });
1796
+ },
1797
+ planUninstall(id, changes) {
1798
+ const pluginDir = join7(geminiStagingRoot(), id);
1799
+ changes.push({
1800
+ platform: PLATFORM4,
1801
+ action: geminiExtensionInstalled(id) ? "remove" : "skip",
1802
+ detail: geminiExtensionInstalled(id) ? `run: gemini extensions uninstall ${id}` : `extension ${id} not installed on ${PLATFORM4}`
1803
+ });
1804
+ if (existsSync7(pluginDir)) {
1805
+ changes.push({
1806
+ platform: PLATFORM4,
1807
+ action: "remove",
1808
+ path: pluginDir,
1809
+ detail: "remove staged marketplace bundle"
1810
+ });
1811
+ }
1812
+ },
1813
+ async driveInstall(id) {
1814
+ const changes = [];
1815
+ const pluginDir = join7(geminiStagingRoot(), id);
1816
+ const bin = geminiBinary();
1817
+ if (!bin) {
1818
+ changes.push(
1819
+ warn4(
1820
+ `gemini CLI not found on PATH \u2014 bundle staged but not installed. Install manually: ${manualInstall(pluginDir)}`,
1821
+ pluginDir
1822
+ )
1823
+ );
1824
+ return { changes, ok: false };
1825
+ }
1826
+ if (geminiExtensionInstalled(id)) {
1827
+ changes.push({
1828
+ platform: PLATFORM4,
1829
+ action: "skip",
1830
+ detail: `extension ${id} already installed`
1831
+ });
1832
+ return { changes, ok: true };
1833
+ }
1834
+ await runHostCommand(bin, ["extensions", "validate", pluginDir]);
1835
+ const install = await runHostCommand(bin, ["extensions", "install", pluginDir, "--consent"]);
1836
+ if (!geminiExtensionInstalled(id)) {
1837
+ const trustGated = /trust (the |this )?(files|folder)/i.test(
1838
+ `${install.stdout}
1839
+ ${install.stderr}`
1840
+ );
1841
+ changes.push(
1842
+ warn4(
1843
+ trustGated ? `extension install blocked by gemini's folder-trust prompt (newer gemini gates local-path installs and --consent does not bypass it). Trust the folder once, then re-run: run \`gemini extensions install ${pluginDir} --consent\` interactively (answer "y" to trust), or set \`security.folderTrust.enabled: false\` in ~/.gemini/settings.json. The bundle is staged and ready.` : `extension install did not complete \u2014 ` + failDetail5(`gemini extensions install ${pluginDir} --consent`, install),
1844
+ pluginDir
1845
+ )
1846
+ );
1847
+ return { changes, ok: false };
1848
+ }
1849
+ changes.push({
1850
+ platform: PLATFORM4,
1851
+ action: "create",
1852
+ detail: `installed extension ${id} (scope user)`
1853
+ });
1854
+ return { changes, ok: true };
1855
+ },
1856
+ async driveUninstall(id) {
1857
+ const changes = [];
1858
+ if (!geminiExtensionInstalled(id)) {
1859
+ changes.push({
1860
+ platform: PLATFORM4,
1861
+ action: "skip",
1862
+ detail: `extension ${id} not installed on ${PLATFORM4}`
1863
+ });
1864
+ return { changes, ok: true };
1865
+ }
1866
+ const bin = geminiBinary();
1867
+ if (!bin) {
1868
+ changes.push(
1869
+ warn4(
1870
+ `gemini CLI not found on PATH \u2014 cannot drive the uninstall. Run manually: gemini extensions uninstall ${id}`
1871
+ )
1872
+ );
1873
+ return { changes, ok: false };
1874
+ }
1875
+ const uninstall = await runHostCommand(bin, ["extensions", "uninstall", id]);
1876
+ if (geminiExtensionInstalled(id)) {
1877
+ changes.push(
1878
+ warn4(
1879
+ `extension uninstall did not complete \u2014 ` + failDetail5(`gemini extensions uninstall ${id}`, uninstall)
1880
+ )
1881
+ );
1882
+ return { changes, ok: false };
1883
+ }
1884
+ changes.push({
1885
+ platform: PLATFORM4,
1886
+ action: "remove",
1887
+ detail: `uninstalled extension ${id} (gemini extensions uninstall)`
1888
+ });
1889
+ return { changes, ok: true };
1890
+ },
1891
+ // gemini has NO overwrite-install (re-install REFUSES), so update is
1892
+ // uninstall-THEN-install. The bundle was already re-staged by the caller.
1893
+ async driveUpdate(id) {
1894
+ const changes = [];
1895
+ const pluginDir = join7(geminiStagingRoot(), id);
1896
+ const bin = geminiBinary();
1897
+ if (!bin) {
1898
+ changes.push(
1899
+ warn4(
1900
+ `gemini CLI not found on PATH \u2014 bundle re-staged but the installed copy was not updated. Run manually: gemini extensions uninstall ${id} && ${manualInstall(pluginDir)}`,
1901
+ pluginDir
1902
+ )
1903
+ );
1904
+ return { changes, ok: false };
1905
+ }
1906
+ if (geminiExtensionInstalled(id)) {
1907
+ const uninstall = await runHostCommand(bin, ["extensions", "uninstall", id]);
1908
+ if (geminiExtensionInstalled(id)) {
1909
+ changes.push(
1910
+ warn4(
1911
+ `extension update could not remove the old copy \u2014 ` + failDetail5(`gemini extensions uninstall ${id}`, uninstall)
1912
+ )
1913
+ );
1914
+ return { changes, ok: false };
1915
+ }
1916
+ }
1917
+ await runHostCommand(bin, ["extensions", "validate", pluginDir]);
1918
+ const install = await runHostCommand(bin, ["extensions", "install", pluginDir, "--consent"]);
1919
+ if (!geminiExtensionInstalled(id)) {
1920
+ changes.push(
1921
+ warn4(
1922
+ `extension install did not complete \u2014 ` + failDetail5(`gemini extensions install ${pluginDir} --consent`, install)
1923
+ )
1924
+ );
1925
+ return { changes, ok: false };
1926
+ }
1927
+ changes.push({
1928
+ platform: PLATFORM4,
1929
+ action: "update",
1930
+ detail: `updated extension ${id} (gemini extensions uninstall + install)`
1931
+ });
1932
+ return { changes, ok: true };
1933
+ },
1934
+ // Direct driver: no catalog, no marketplace de-registration. Cleanup is just
1935
+ // removing the staged bundle dir (the host copied it into its own store).
1936
+ async finishUninstall(id, changes) {
1937
+ const pluginDir = join7(geminiStagingRoot(), id);
1938
+ if (existsSync7(pluginDir)) {
1939
+ try {
1940
+ rmSync6(pluginDir, { recursive: true, force: true });
1941
+ changes.push({
1942
+ platform: PLATFORM4,
1943
+ action: "remove",
1944
+ path: pluginDir,
1945
+ detail: "removed staged marketplace bundle"
1946
+ });
1947
+ } catch (err) {
1948
+ changes.push({
1949
+ platform: PLATFORM4,
1950
+ action: "warn",
1951
+ path: pluginDir,
1952
+ detail: `could not remove staged bundle: ${errMessage6(err)}`
1953
+ });
1954
+ }
1955
+ }
1956
+ }
1957
+ };
1958
+
1959
+ // src/core/marketplace-drivers/npm-local.ts
1960
+ import { existsSync as existsSync8, mkdirSync, rmSync as rmSync7, writeFileSync as writeFileSync4 } from "fs";
1961
+ import { dirname as dirname4, join as join8 } from "path";
1962
+ import { pathToFileURL } from "url";
1963
+ function errMessage7(err) {
1964
+ return err instanceof Error ? err.message : String(err);
1965
+ }
1966
+ function failDetail6(step, r) {
1967
+ if (r.timedOut) return `${step} timed out`;
1968
+ if (r.error) return `${step} failed to spawn: ${r.error}`;
1969
+ const line = firstLine(r.stderr) || firstLine(r.stdout);
1970
+ return `${step} exited ${r.code}${line ? `: ${line}` : ""}`;
1971
+ }
1972
+ function fileUrlFor(connectorId) {
1973
+ return pathToFileURL(join8(npmStagingRoot(), connectorId)).href;
1974
+ }
1975
+ function makeNpmLocalDriver(platform, opts) {
1976
+ const { binaryName } = opts;
1977
+ const binary = () => findOnPath(binaryName);
1978
+ const warn6 = (detail, path) => ({
1979
+ platform,
1980
+ action: "warn",
1981
+ detail,
1982
+ ...path ? { path } : {}
1983
+ });
1984
+ const manualInstall3 = (connectorId) => `${binaryName} plugin --global ${fileUrlFor(connectorId)}`;
1985
+ const stageBundle = (connector, changes) => {
1986
+ const stagingRoot = npmStagingRoot();
1987
+ const pluginDir = join8(stagingRoot, connector.id);
1988
+ const existed = existsSync8(pluginDir);
1989
+ const result = packageConnector(connector, { outDir: stagingRoot, format: "npm-plugin" });
1990
+ changes.push({
1991
+ platform,
1992
+ action: existed ? "update" : "create",
1993
+ path: pluginDir,
1994
+ detail: `staged marketplace bundle (${result.files.length} files, npm-plugin)`
1995
+ });
1996
+ return { pluginDir, contentHash: hashDirectory(pluginDir) };
1997
+ };
1998
+ const driveInstall3 = async (id) => {
1999
+ const changes = [];
2000
+ if (npmPluginInstalled(platform, id)) {
2001
+ changes.push({
2002
+ platform,
2003
+ action: "skip",
2004
+ detail: `plugin ${id} already installed`
2005
+ });
2006
+ return { changes, ok: true };
2007
+ }
2008
+ const bin = binary();
2009
+ if (!bin) {
2010
+ changes.push(
2011
+ warn6(
2012
+ `${binaryName} CLI not found on PATH \u2014 bundle staged but not installed. Install manually: ${manualInstall3(id)}`,
2013
+ join8(npmStagingRoot(), id)
2014
+ )
2015
+ );
2016
+ return { changes, ok: false };
2017
+ }
2018
+ const stagingRoot = npmStagingRoot();
2019
+ try {
2020
+ mkdirSync(stagingRoot, { recursive: true });
2021
+ } catch {
2022
+ }
2023
+ const install = await runHostCommand(bin, ["plugin", "--global", fileUrlFor(id)], {
2024
+ cwd: stagingRoot
2025
+ });
2026
+ if (!npmPluginInstalled(platform, id)) {
2027
+ changes.push(
2028
+ warn6(`plugin install did not complete \u2014 ` + failDetail6(manualInstall3(id), install))
2029
+ );
2030
+ return { changes, ok: false };
2031
+ }
2032
+ changes.push({
2033
+ platform,
2034
+ action: "create",
2035
+ detail: `installed plugin ${id} (scope user)`
2036
+ });
2037
+ return { changes, ok: true };
2038
+ };
2039
+ return {
2040
+ platform,
2041
+ format: "npm-plugin",
2042
+ binary,
2043
+ stagingRoot: npmStagingRoot,
2044
+ pluginDir(id) {
2045
+ return join8(npmStagingRoot(), id);
2046
+ },
2047
+ installed: (id) => npmPluginInstalled(platform, id),
2048
+ stage(connector, changes) {
2049
+ return stageBundle(connector, changes).contentHash;
2050
+ },
2051
+ planInstall(connector, changes) {
2052
+ changes.push({
2053
+ platform,
2054
+ action: npmPluginInstalled(platform, connector.id) ? "skip" : "create",
2055
+ detail: npmPluginInstalled(platform, connector.id) ? `plugin ${connector.id} already installed` : `run: ${manualInstall3(connector.id)}`
2056
+ });
2057
+ },
2058
+ planUninstall(id, changes) {
2059
+ const pluginDir = join8(npmStagingRoot(), id);
2060
+ const present = npmPluginInstalled(platform, id);
2061
+ changes.push({
2062
+ platform,
2063
+ action: present ? "remove" : "skip",
2064
+ detail: present ? `edit ${npmConfigFilePath(platform)}: drop the file:// plugin entry for ${id}` : `plugin ${id} not installed on ${platform}`
2065
+ });
2066
+ if (existsSync8(pluginDir)) {
2067
+ changes.push({
2068
+ platform,
2069
+ action: "remove",
2070
+ path: pluginDir,
2071
+ detail: "remove staged marketplace bundle"
2072
+ });
2073
+ }
2074
+ },
2075
+ driveInstall: driveInstall3,
2076
+ // No host uninstall verb (`<bin> uninstall` removes the HOST). Removal is an
2077
+ // EDIT of the config `plugin` array: drop the entry referencing our staged
2078
+ // dir, delete the `plugin` key when empty, preserve every other key.
2079
+ async driveUninstall(id) {
2080
+ const changes = [];
2081
+ const entry = npmPluginArrayEntry(platform, id);
2082
+ if (entry == null) {
2083
+ changes.push({
2084
+ platform,
2085
+ action: "skip",
2086
+ detail: `plugin ${id} not installed on ${platform}`
2087
+ });
2088
+ return { changes, ok: true };
2089
+ }
2090
+ const configPath = npmConfigFilePath(platform);
2091
+ const cfg = readNpmConfig(platform);
2092
+ if (!cfg) {
2093
+ changes.push(
2094
+ warn6(
2095
+ `could not parse ${platform} config ${configPath} \u2014 leaving the plugin entry in place. Remove this entry from the "plugin" array by hand: ${entry}`,
2096
+ configPath
2097
+ )
2098
+ );
2099
+ return { changes, ok: false };
2100
+ }
2101
+ const arr = Array.isArray(cfg["plugin"]) ? cfg["plugin"] : [];
2102
+ const filtered = arr.filter((e) => e !== entry);
2103
+ if (filtered.length === 0) delete cfg["plugin"];
2104
+ else cfg["plugin"] = filtered;
2105
+ try {
2106
+ mkdirSync(dirname4(configPath), { recursive: true });
2107
+ writeFileSync4(configPath, `${JSON.stringify(cfg, null, 2)}
2108
+ `, "utf8");
2109
+ } catch (err) {
2110
+ changes.push(
2111
+ warn6(`could not rewrite ${platform} config: ${errMessage7(err)}`, configPath)
2112
+ );
2113
+ return { changes, ok: false };
2114
+ }
2115
+ if (npmPluginInstalled(platform, id)) {
2116
+ changes.push(
2117
+ warn6(`plugin entry persisted after the config edit \u2014 ${configPath}`, configPath)
2118
+ );
2119
+ return { changes, ok: false };
2120
+ }
2121
+ changes.push({
2122
+ platform,
2123
+ action: "remove",
2124
+ path: configPath,
2125
+ detail: `removed plugin ${id} from the ${platform} config "plugin" array`
2126
+ });
2127
+ return { changes, ok: true };
2128
+ },
2129
+ // The `file://` reference points at the LIVE staged dir, so an update is just
2130
+ // a re-run of install (the bundle was re-staged in place by the caller). When
2131
+ // the entry is already present, that is an idempotent `=` skip.
2132
+ async driveUpdate(id) {
2133
+ return driveInstall3(id);
2134
+ },
2135
+ // No catalog, no marketplace de-registration: cleanup is removing the staged
2136
+ // bundle dir (the host no longer references it after driveUninstall's edit).
2137
+ async finishUninstall(id, changes) {
2138
+ const pluginDir = join8(npmStagingRoot(), id);
2139
+ if (existsSync8(pluginDir)) {
2140
+ try {
2141
+ rmSync7(pluginDir, { recursive: true, force: true });
2142
+ changes.push({
2143
+ platform,
2144
+ action: "remove",
2145
+ path: pluginDir,
2146
+ detail: "removed staged marketplace bundle"
2147
+ });
2148
+ } catch (err) {
2149
+ changes.push({
2150
+ platform,
2151
+ action: "warn",
2152
+ path: pluginDir,
2153
+ detail: `could not remove staged bundle: ${errMessage7(err)}`
2154
+ });
2155
+ }
2156
+ }
2157
+ }
2158
+ };
2159
+ }
2160
+
2161
+ // src/core/marketplace-drivers/qwen.ts
2162
+ import { existsSync as existsSync9, rmSync as rmSync8 } from "fs";
2163
+ import { join as join9 } from "path";
2164
+ var PLATFORM5 = "qwen-code";
2165
+ function qwenBinary() {
2166
+ return findOnPath("qwen");
2167
+ }
2168
+ function errMessage8(err) {
2169
+ return err instanceof Error ? err.message : String(err);
2170
+ }
2171
+ function warn5(detail, path) {
2172
+ return { platform: PLATFORM5, action: "warn", detail, ...path ? { path } : {} };
2173
+ }
2174
+ function failDetail7(step, r) {
2175
+ if (r.timedOut) return `${step} timed out`;
2176
+ if (r.error) return `${step} failed to spawn: ${r.error}`;
2177
+ const line = firstLine(r.stderr) || firstLine(r.stdout);
2178
+ return `${step} exited ${r.code}${line ? `: ${line}` : ""}`;
2179
+ }
2180
+ function manualInstall2(pluginDir) {
2181
+ return `qwen extensions install ${pluginDir}`;
2182
+ }
2183
+ function stageQwenBundle(connector, changes) {
2184
+ const stagingRoot = qwenStagingRoot();
2185
+ const pluginDir = join9(stagingRoot, connector.id);
2186
+ const existed = existsSync9(pluginDir);
2187
+ const result = packageConnector(connector, { outDir: stagingRoot, format: "qwen-extension" });
2188
+ changes.push({
2189
+ platform: PLATFORM5,
2190
+ action: existed ? "update" : "create",
2191
+ path: pluginDir,
2192
+ detail: `staged marketplace bundle (${result.files.length} files, qwen-extension)`
2193
+ });
2194
+ return { pluginDir, contentHash: hashDirectory(pluginDir) };
2195
+ }
2196
+ var qwenDriver = {
2197
+ platform: PLATFORM5,
2198
+ format: "qwen-extension",
2199
+ binary: qwenBinary,
2200
+ stagingRoot: qwenStagingRoot,
2201
+ pluginDir(id) {
2202
+ return join9(qwenStagingRoot(), id);
2203
+ },
2204
+ installed: qwenExtensionInstalled,
2205
+ stage(connector, changes) {
2206
+ return stageQwenBundle(connector, changes).contentHash;
2207
+ },
2208
+ planInstall(connector, changes) {
2209
+ const pluginDir = join9(qwenStagingRoot(), connector.id);
2210
+ changes.push({
2211
+ platform: PLATFORM5,
2212
+ action: qwenExtensionInstalled(connector.id) ? "skip" : "create",
2213
+ detail: qwenExtensionInstalled(connector.id) ? `extension ${connector.id} already installed` : `run: ${manualInstall2(pluginDir)}`
2214
+ });
2215
+ },
2216
+ planUninstall(id, changes) {
2217
+ const pluginDir = join9(qwenStagingRoot(), id);
2218
+ changes.push({
2219
+ platform: PLATFORM5,
2220
+ action: qwenExtensionInstalled(id) ? "remove" : "skip",
2221
+ detail: qwenExtensionInstalled(id) ? `run: qwen extensions uninstall ${id}` : `extension ${id} not installed on ${PLATFORM5}`
2222
+ });
2223
+ if (existsSync9(pluginDir)) {
2224
+ changes.push({
2225
+ platform: PLATFORM5,
2226
+ action: "remove",
2227
+ path: pluginDir,
2228
+ detail: "remove staged marketplace bundle"
2229
+ });
2230
+ }
2231
+ },
2232
+ async driveInstall(id) {
2233
+ const changes = [];
2234
+ const pluginDir = join9(qwenStagingRoot(), id);
2235
+ const bin = qwenBinary();
2236
+ if (!bin) {
2237
+ changes.push(
2238
+ warn5(
2239
+ `qwen CLI not found on PATH \u2014 bundle staged but not installed. Install manually: ${manualInstall2(pluginDir)}`,
2240
+ pluginDir
2241
+ )
2242
+ );
2243
+ return { changes, ok: false };
2244
+ }
2245
+ if (qwenExtensionInstalled(id)) {
2246
+ changes.push({
2247
+ platform: PLATFORM5,
2248
+ action: "skip",
2249
+ detail: `extension ${id} already installed`
2250
+ });
2251
+ return { changes, ok: true };
2252
+ }
2253
+ const install = await runHostCommand(bin, ["extensions", "install", pluginDir]);
2254
+ if (!qwenExtensionInstalled(id)) {
2255
+ changes.push(
2256
+ warn5(
2257
+ `extension install did not complete \u2014 ` + failDetail7(`qwen extensions install ${pluginDir}`, install)
2258
+ )
2259
+ );
2260
+ return { changes, ok: false };
2261
+ }
2262
+ changes.push({
2263
+ platform: PLATFORM5,
2264
+ action: "create",
2265
+ detail: `installed extension ${id} (scope user)`
2266
+ });
2267
+ return { changes, ok: true };
2268
+ },
2269
+ async driveUninstall(id) {
2270
+ const changes = [];
2271
+ if (!qwenExtensionInstalled(id)) {
2272
+ changes.push({
2273
+ platform: PLATFORM5,
2274
+ action: "skip",
2275
+ detail: `extension ${id} not installed on ${PLATFORM5}`
2276
+ });
2277
+ return { changes, ok: true };
2278
+ }
2279
+ const bin = qwenBinary();
2280
+ if (!bin) {
2281
+ changes.push(
2282
+ warn5(
2283
+ `qwen CLI not found on PATH \u2014 cannot drive the uninstall. Run manually: qwen extensions uninstall ${id}`
2284
+ )
2285
+ );
2286
+ return { changes, ok: false };
2287
+ }
2288
+ const uninstall = await runHostCommand(bin, ["extensions", "uninstall", id]);
2289
+ if (qwenExtensionInstalled(id)) {
2290
+ changes.push(
2291
+ warn5(
2292
+ `extension uninstall did not complete \u2014 ` + failDetail7(`qwen extensions uninstall ${id}`, uninstall)
2293
+ )
2294
+ );
2295
+ return { changes, ok: false };
2296
+ }
2297
+ changes.push({
2298
+ platform: PLATFORM5,
2299
+ action: "remove",
2300
+ detail: `uninstalled extension ${id} (qwen extensions uninstall)`
2301
+ });
2302
+ return { changes, ok: true };
2303
+ },
2304
+ // qwen documents an `extensions update <id>` verb (UNLIKE gemini, which has no
2305
+ // overwrite path) — PREFER it when the extension is already installed, else
2306
+ // fall back to a fresh install. The bundle was already re-staged by the caller.
2307
+ // DOCS-only: the `update` verb + its exit semantics are unverified live.
2308
+ async driveUpdate(id) {
2309
+ const changes = [];
2310
+ const pluginDir = join9(qwenStagingRoot(), id);
2311
+ const bin = qwenBinary();
2312
+ if (!bin) {
2313
+ changes.push(
2314
+ warn5(
2315
+ `qwen CLI not found on PATH \u2014 bundle re-staged but the installed copy was not updated. Run manually: qwen extensions update ${id}`,
2316
+ pluginDir
2317
+ )
2318
+ );
2319
+ return { changes, ok: false };
2320
+ }
2321
+ if (qwenExtensionInstalled(id)) {
2322
+ const update = await runHostCommand(bin, ["extensions", "update", id]);
2323
+ if (!qwenExtensionInstalled(id)) {
2324
+ changes.push(
2325
+ warn5(
2326
+ `extension update did not complete \u2014 ` + failDetail7(`qwen extensions update ${id}`, update)
2327
+ )
2328
+ );
2329
+ return { changes, ok: false };
2330
+ }
2331
+ changes.push({
2332
+ platform: PLATFORM5,
2333
+ action: "update",
2334
+ detail: `updated extension ${id} (qwen extensions update)`
2335
+ });
2336
+ return { changes, ok: true };
2337
+ }
2338
+ const install = await runHostCommand(bin, ["extensions", "install", pluginDir]);
2339
+ if (!qwenExtensionInstalled(id)) {
2340
+ changes.push(
2341
+ warn5(
2342
+ `extension install did not complete \u2014 ` + failDetail7(`qwen extensions install ${pluginDir}`, install)
2343
+ )
2344
+ );
2345
+ return { changes, ok: false };
2346
+ }
2347
+ changes.push({
2348
+ platform: PLATFORM5,
2349
+ action: "update",
2350
+ detail: `updated extension ${id} (qwen extensions install)`
2351
+ });
2352
+ return { changes, ok: true };
2353
+ },
2354
+ // Direct driver: no catalog, no marketplace de-registration. Cleanup is just
2355
+ // removing the staged bundle dir (the host copied it into its own store).
2356
+ async finishUninstall(id, changes) {
2357
+ const pluginDir = join9(qwenStagingRoot(), id);
2358
+ if (existsSync9(pluginDir)) {
2359
+ try {
2360
+ rmSync8(pluginDir, { recursive: true, force: true });
2361
+ changes.push({
2362
+ platform: PLATFORM5,
2363
+ action: "remove",
2364
+ path: pluginDir,
2365
+ detail: "removed staged marketplace bundle"
2366
+ });
2367
+ } catch (err) {
2368
+ changes.push({
2369
+ platform: PLATFORM5,
2370
+ action: "warn",
2371
+ path: pluginDir,
2372
+ detail: `could not remove staged bundle: ${errMessage8(err)}`
2373
+ });
2374
+ }
2375
+ }
2376
+ }
2377
+ };
2378
+
2379
+ // src/core/marketplace-drivers/registry.ts
2380
+ var agyDrivers = /* @__PURE__ */ new Map();
2381
+ function agyDriver(platform) {
2382
+ let driver = agyDrivers.get(platform);
2383
+ if (!driver) {
2384
+ driver = makeAgyDriver(platform);
2385
+ agyDrivers.set(platform, driver);
2386
+ }
2387
+ return driver;
2388
+ }
2389
+ var NPM_LOCAL_BINARIES = {
2390
+ opencode: "opencode",
2391
+ kilo: "kilo",
2392
+ "kilo-cli": "kilo"
2393
+ };
2394
+ var npmLocalDrivers = /* @__PURE__ */ new Map();
2395
+ function npmLocalDriver(platform) {
2396
+ let driver = npmLocalDrivers.get(platform);
2397
+ if (!driver) {
2398
+ driver = makeNpmLocalDriver(platform, {
2399
+ binaryName: NPM_LOCAL_BINARIES[platform] ?? platform
2400
+ });
2401
+ npmLocalDrivers.set(platform, driver);
2402
+ }
2403
+ return driver;
2404
+ }
2405
+ function getMarketplaceDriver(platform) {
2406
+ switch (platform) {
2407
+ case "claude-code":
2408
+ return claudeDriver;
2409
+ case "codex":
2410
+ return codexDriver;
2411
+ case "antigravity":
2412
+ case "antigravity-cli":
2413
+ return agyDriver(platform);
2414
+ case "gemini-cli":
2415
+ return geminiDriver;
2416
+ case "qwen-code":
2417
+ return qwenDriver;
2418
+ case "droid":
2419
+ return droidDriver;
2420
+ case "opencode":
2421
+ case "kilo":
2422
+ case "kilo-cli":
2423
+ return npmLocalDriver(platform);
2424
+ default:
2425
+ return null;
2426
+ }
2427
+ }
2428
+
2429
+ // src/core/marketplace.ts
2430
+ function parseInstallMethod(value) {
2431
+ if (value == null || value === "direct" || value === "marketplace") {
2432
+ return value ?? "direct";
2433
+ }
2434
+ return null;
2435
+ }
2436
+ function parseUninstallMethod(value) {
2437
+ if (value == null || value === "auto" || value === "direct" || value === "marketplace") {
2438
+ return value ?? "auto";
2439
+ }
2440
+ return null;
2441
+ }
2442
+ var MARKETPLACE_FORMAT_BY_PLATFORM = {
2443
+ "claude-code": "claude-plugin",
2444
+ codex: "codex-plugin",
2445
+ droid: "factory-plugin",
2446
+ "gemini-cli": "gemini-extension",
2447
+ "qwen-code": "qwen-extension",
1435
2448
  antigravity: "agy-plugin",
1436
2449
  "antigravity-cli": "agy-plugin",
1437
2450
  cursor: "cursor-plugin",
@@ -1448,12 +2461,18 @@ var DRIVABLE_MARKETPLACE_PLATFORMS = /* @__PURE__ */ new Set([
1448
2461
  "claude-code",
1449
2462
  "codex",
1450
2463
  "antigravity",
1451
- "antigravity-cli"
2464
+ "antigravity-cli",
2465
+ "gemini-cli",
2466
+ "qwen-code",
2467
+ "droid",
2468
+ "opencode",
2469
+ "kilo",
2470
+ "kilo-cli"
1452
2471
  ]);
1453
2472
  function newResult2(connectorId, dryRun) {
1454
2473
  return { connectorId, dryRun, changes: [], warnings: [] };
1455
2474
  }
1456
- function errMessage5(err) {
2475
+ function errMessage9(err) {
1457
2476
  return err instanceof Error ? err.message : String(err);
1458
2477
  }
1459
2478
  function manualHint(platform, format, id) {
@@ -1477,7 +2496,7 @@ async function directInstallPresent(connector, platform, scope, projectDir) {
1477
2496
  const hookCommand = new RegExp(`--connector\\s+${escaped}(?![\\w-])`);
1478
2497
  const matches = (path, re) => {
1479
2498
  try {
1480
- return re.test(readFileSync3(path, "utf8"));
2499
+ return re.test(readFileSync4(path, "utf8"));
1481
2500
  } catch {
1482
2501
  return false;
1483
2502
  }
@@ -1508,12 +2527,12 @@ async function installViaMarketplace(opts) {
1508
2527
  try {
1509
2528
  ensureHomeBin(resolveCliEntry());
1510
2529
  } catch (err) {
1511
- log.warn(`ensureHomeBin failed: ${errMessage5(err)}`);
2530
+ log.warn(`ensureHomeBin failed: ${errMessage9(err)}`);
1512
2531
  }
1513
2532
  try {
1514
- registerConnector(connector, modulePath);
2533
+ registerConnector(connector, modulePath, scope);
1515
2534
  } catch (err) {
1516
- log.warn(`registerConnector failed: ${errMessage5(err)}`);
2535
+ log.warn(`registerConnector failed: ${errMessage9(err)}`);
1517
2536
  }
1518
2537
  }
1519
2538
  const explicit = opts.targets != null && opts.targets.length > 0;
@@ -1663,16 +2682,16 @@ async function uninstallViaMarketplace(opts) {
1663
2682
  const root2 = driver.stagingRoot();
1664
2683
  if (seenRoots.has(root2)) continue;
1665
2684
  seenRoots.add(root2);
1666
- if (!existsSync6(root2)) continue;
2685
+ if (!existsSync10(root2)) continue;
1667
2686
  let bundleDirs;
1668
2687
  try {
1669
- bundleDirs = readdirSync4(root2).filter((n) => !n.startsWith("."));
2688
+ bundleDirs = readdirSync5(root2).filter((n) => !n.startsWith("."));
1670
2689
  } catch {
1671
2690
  bundleDirs = [];
1672
2691
  }
1673
2692
  if (bundleDirs.length > 0) continue;
1674
2693
  try {
1675
- rmSync5(root2, { recursive: true, force: true });
2694
+ rmSync9(root2, { recursive: true, force: true });
1676
2695
  result.changes.push({
1677
2696
  platform: id,
1678
2697
  action: "remove",
@@ -1680,13 +2699,13 @@ async function uninstallViaMarketplace(opts) {
1680
2699
  detail: "removed empty marketplace staging root (--purge)"
1681
2700
  });
1682
2701
  } catch (err) {
1683
- log.warn(`marketplace staging-root removal failed: ${errMessage5(err)}`);
2702
+ log.warn(`marketplace staging-root removal failed: ${errMessage9(err)}`);
1684
2703
  }
1685
2704
  }
1686
2705
  const root = marketplaceRoot();
1687
2706
  try {
1688
- if (existsSync6(root) && readdirSync4(root).length === 0) {
1689
- rmSync5(root, { recursive: true, force: true });
2707
+ if (existsSync10(root) && readdirSync5(root).length === 0) {
2708
+ rmSync9(root, { recursive: true, force: true });
1690
2709
  }
1691
2710
  } catch {
1692
2711
  }
@@ -1784,11 +2803,21 @@ async function marketplaceDoctorChecks(connector, scope, projectDir) {
1784
2803
  const claude = await claudeMarketplaceGroup(connector, scope, projectDir);
1785
2804
  if (claude) groups.push(claude);
1786
2805
  const seenHosts = /* @__PURE__ */ new Set();
1787
- const candidates = ["codex", "antigravity", "antigravity-cli"].sort((a, b) => recordRank(connector.id, a) - recordRank(connector.id, b));
2806
+ const candidates = [
2807
+ "codex",
2808
+ "droid",
2809
+ "antigravity",
2810
+ "antigravity-cli",
2811
+ "gemini-cli",
2812
+ "qwen-code",
2813
+ "opencode",
2814
+ "kilo",
2815
+ "kilo-cli"
2816
+ ].sort((a, b) => recordRank(connector.id, a) - recordRank(connector.id, b));
1788
2817
  for (const platform of candidates) {
1789
2818
  const driver = getMarketplaceDriver(platform);
1790
2819
  if (!driver) continue;
1791
- const hostKey = driver.stagingRoot();
2820
+ const hostKey = doctorHostKey(platform, driver.stagingRoot());
1792
2821
  if (seenHosts.has(hostKey)) continue;
1793
2822
  const group = await genericMarketplaceGroup(connector, platform, scope, projectDir);
1794
2823
  if (group) {
@@ -1799,11 +2828,28 @@ async function marketplaceDoctorChecks(connector, scope, projectDir) {
1799
2828
  groups.sort((a, b) => platformDoctorRank(a.platform) - platformDoctorRank(b.platform));
1800
2829
  return groups;
1801
2830
  }
2831
+ function doctorHostKey(platform, stagingRoot) {
2832
+ if (platform === "opencode" || platform === "kilo" || platform === "kilo-cli") {
2833
+ return npmConfigFilePath(platform);
2834
+ }
2835
+ return stagingRoot;
2836
+ }
1802
2837
  function recordRank(connectorId, platform) {
1803
2838
  return readMarketplaceInstalls(connectorId)[platform] ? 0 : 1;
1804
2839
  }
1805
2840
  function platformDoctorRank(platform) {
1806
- const order = ["claude-code", "codex", "antigravity", "antigravity-cli"];
2841
+ const order = [
2842
+ "claude-code",
2843
+ "codex",
2844
+ "droid",
2845
+ "antigravity",
2846
+ "antigravity-cli",
2847
+ "gemini-cli",
2848
+ "qwen-code",
2849
+ "opencode",
2850
+ "kilo",
2851
+ "kilo-cli"
2852
+ ];
1807
2853
  const i = order.indexOf(platform);
1808
2854
  return i === -1 ? order.length : i;
1809
2855
  }
@@ -1826,19 +2872,19 @@ async function claudeMarketplaceGroup(connector, scope, projectDir) {
1826
2872
  }
1827
2873
  if (record) {
1828
2874
  const registeredAt = claudeKnownMarketplacePath(MARKETPLACE_NAME);
1829
- const catalogPath = join6(stagingRoot, ".claude-plugin", "marketplace.json");
2875
+ const catalogPath = join10(stagingRoot, ".claude-plugin", "marketplace.json");
1830
2876
  let catalogListed = false;
1831
2877
  try {
1832
- const catalog = JSON.parse(readFileSync3(catalogPath, "utf8"));
2878
+ const catalog = JSON.parse(readFileSync4(catalogPath, "utf8"));
1833
2879
  catalogListed = (catalog.plugins ?? []).some((p) => p.name === id);
1834
2880
  } catch {
1835
2881
  catalogListed = false;
1836
2882
  }
1837
- const intact = registeredAt === stagingRoot && existsSync6(record.bundleDir) && catalogListed;
2883
+ const intact = registeredAt === stagingRoot && existsSync10(record.bundleDir) && catalogListed;
1838
2884
  results.push({
1839
2885
  check: `${id}: marketplace registration`,
1840
2886
  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"})`,
2887
+ message: intact ? `local marketplace "${MARKETPLACE_NAME}" registered at ${stagingRoot}, bundle staged + cataloged` : `marketplace registration broken (registered at ${registeredAt ?? "nowhere"}, bundle ${existsSync10(record.bundleDir) ? "present" : "missing"}, catalog ${catalogListed ? "lists it" : "does not list it"})`,
1842
2888
  ...intact ? {} : { fix: `agent-connector install --method marketplace --targets claude-code` }
1843
2889
  });
1844
2890
  }
@@ -1865,7 +2911,7 @@ async function claudeMarketplaceGroup(connector, scope, projectDir) {
1865
2911
  message: `connector.version ${connector.version} \u2260 installed ${record.version}`,
1866
2912
  fix: `agent-connector upgrade --method marketplace --targets claude-code`
1867
2913
  });
1868
- } else if (existsSync6(record.bundleDir) && record.contentHash !== "" && hashDirectory(record.bundleDir) !== record.contentHash) {
2914
+ } else if (existsSync10(record.bundleDir) && record.contentHash !== "" && hashDirectory(record.bundleDir) !== record.contentHash) {
1869
2915
  results.push({
1870
2916
  check: `${id}: marketplace staleness`,
1871
2917
  status: "warn",
@@ -1874,7 +2920,7 @@ async function claudeMarketplaceGroup(connector, scope, projectDir) {
1874
2920
  });
1875
2921
  }
1876
2922
  }
1877
- if (pluginPresent && !existsSync6(homeBinPath())) {
2923
+ if (pluginPresent && !existsSync10(homeBinPath())) {
1878
2924
  results.push({
1879
2925
  check: `${id}: marketplace home-bin`,
1880
2926
  status: "fail",
@@ -1938,7 +2984,7 @@ async function genericMarketplaceGroup(connector, platform, scope, projectDir) {
1938
2984
  message: `connector.version ${connector.version} \u2260 installed ${record.version}`,
1939
2985
  fix: `agent-connector upgrade --method marketplace --targets ${platform}`
1940
2986
  });
1941
- } else if (existsSync6(record.bundleDir) && record.contentHash !== "" && hashDirectory(record.bundleDir) !== record.contentHash) {
2987
+ } else if (existsSync10(record.bundleDir) && record.contentHash !== "" && hashDirectory(record.bundleDir) !== record.contentHash) {
1942
2988
  results.push({
1943
2989
  check: `${id}: marketplace staleness`,
1944
2990
  status: "warn",
@@ -1947,7 +2993,7 @@ async function genericMarketplaceGroup(connector, platform, scope, projectDir) {
1947
2993
  });
1948
2994
  }
1949
2995
  }
1950
- if (installed && !existsSync6(homeBinPath())) {
2996
+ if (installed && !existsSync10(homeBinPath())) {
1951
2997
  results.push({
1952
2998
  check: `${id}: marketplace home-bin`,
1953
2999
  status: "fail",
@@ -1986,4 +3032,4 @@ export {
1986
3032
  upgradeViaMarketplace,
1987
3033
  marketplaceDoctorChecks
1988
3034
  };
1989
- //# sourceMappingURL=chunk-QADSG2JY.js.map
3035
+ //# sourceMappingURL=chunk-G3ER4SY5.js.map