botmux 3.12.0 → 3.12.1

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 (229) hide show
  1. package/dist/.runtime-build-id +1 -1
  2. package/dist/adapters/backend/riff-backend.d.ts +18 -0
  3. package/dist/adapters/backend/riff-backend.d.ts.map +1 -1
  4. package/dist/adapters/backend/riff-backend.js +143 -0
  5. package/dist/adapters/backend/riff-backend.js.map +1 -1
  6. package/dist/adapters/cli/kimi.d.ts.map +1 -1
  7. package/dist/adapters/cli/kimi.js +22 -8
  8. package/dist/adapters/cli/kimi.js.map +1 -1
  9. package/dist/adapters/cli/pi.d.ts.map +1 -1
  10. package/dist/adapters/cli/pi.js +3 -1
  11. package/dist/adapters/cli/pi.js.map +1 -1
  12. package/dist/bot-registry.d.ts.map +1 -1
  13. package/dist/bot-registry.js +4 -1
  14. package/dist/bot-registry.js.map +1 -1
  15. package/dist/cli.d.ts.map +1 -1
  16. package/dist/cli.js +65 -27
  17. package/dist/cli.js.map +1 -1
  18. package/dist/core/command-handler.d.ts.map +1 -1
  19. package/dist/core/command-handler.js +16 -2
  20. package/dist/core/command-handler.js.map +1 -1
  21. package/dist/core/dashboard-ipc-server.js +1 -1
  22. package/dist/core/dashboard-ipc-server.js.map +1 -1
  23. package/dist/core/dashboard-rows.d.ts +17 -1
  24. package/dist/core/dashboard-rows.d.ts.map +1 -1
  25. package/dist/core/dashboard-rows.js +13 -1
  26. package/dist/core/dashboard-rows.js.map +1 -1
  27. package/dist/core/maintenance.d.ts.map +1 -1
  28. package/dist/core/maintenance.js +5 -0
  29. package/dist/core/maintenance.js.map +1 -1
  30. package/dist/core/skills/cli-admin-command.d.ts.map +1 -1
  31. package/dist/core/skills/cli-admin-command.js +142 -3
  32. package/dist/core/skills/cli-admin-command.js.map +1 -1
  33. package/dist/core/skills/frontmatter.d.ts.map +1 -1
  34. package/dist/core/skills/frontmatter.js +44 -7
  35. package/dist/core/skills/frontmatter.js.map +1 -1
  36. package/dist/core/skills/im-command.d.ts +6 -0
  37. package/dist/core/skills/im-command.d.ts.map +1 -1
  38. package/dist/core/skills/im-command.js +34 -7
  39. package/dist/core/skills/im-command.js.map +1 -1
  40. package/dist/core/skills/install-command.d.ts +4 -0
  41. package/dist/core/skills/install-command.d.ts.map +1 -0
  42. package/dist/core/skills/install-command.js +89 -0
  43. package/dist/core/skills/install-command.js.map +1 -0
  44. package/dist/core/skills/package.d.ts.map +1 -1
  45. package/dist/core/skills/package.js +10 -3
  46. package/dist/core/skills/package.js.map +1 -1
  47. package/dist/core/skills/policy.d.ts +5 -1
  48. package/dist/core/skills/policy.d.ts.map +1 -1
  49. package/dist/core/skills/policy.js +66 -3
  50. package/dist/core/skills/policy.js.map +1 -1
  51. package/dist/core/skills/references.d.ts +9 -1
  52. package/dist/core/skills/references.d.ts.map +1 -1
  53. package/dist/core/skills/references.js +35 -2
  54. package/dist/core/skills/references.js.map +1 -1
  55. package/dist/core/skills/registry-paths.d.ts +1 -0
  56. package/dist/core/skills/registry-paths.d.ts.map +1 -1
  57. package/dist/core/skills/registry-paths.js +3 -0
  58. package/dist/core/skills/registry-paths.js.map +1 -1
  59. package/dist/core/skills/session-resolver.d.ts +2 -1
  60. package/dist/core/skills/session-resolver.d.ts.map +1 -1
  61. package/dist/core/skills/session-resolver.js +1 -0
  62. package/dist/core/skills/session-resolver.js.map +1 -1
  63. package/dist/core/skills/session-runtime.d.ts.map +1 -1
  64. package/dist/core/skills/session-runtime.js +2 -0
  65. package/dist/core/skills/session-runtime.js.map +1 -1
  66. package/dist/core/skills/sources.d.ts +23 -0
  67. package/dist/core/skills/sources.d.ts.map +1 -1
  68. package/dist/core/skills/sources.js +128 -12
  69. package/dist/core/skills/sources.js.map +1 -1
  70. package/dist/core/skills/types.d.ts +22 -1
  71. package/dist/core/skills/types.d.ts.map +1 -1
  72. package/dist/core/types.d.ts +2 -0
  73. package/dist/core/types.d.ts.map +1 -1
  74. package/dist/core/types.js.map +1 -1
  75. package/dist/core/worker-pool.d.ts.map +1 -1
  76. package/dist/core/worker-pool.js +52 -1
  77. package/dist/core/worker-pool.js.map +1 -1
  78. package/dist/dashboard/bot-onboarding.d.ts.map +1 -1
  79. package/dist/dashboard/bot-onboarding.js +2 -114
  80. package/dist/dashboard/bot-onboarding.js.map +1 -1
  81. package/dist/dashboard/connector-api.d.ts.map +1 -1
  82. package/dist/dashboard/connector-api.js +66 -6
  83. package/dist/dashboard/connector-api.js.map +1 -1
  84. package/dist/dashboard/public-redact.d.ts.map +1 -1
  85. package/dist/dashboard/public-redact.js +3 -0
  86. package/dist/dashboard/public-redact.js.map +1 -1
  87. package/dist/dashboard/skill-install-request.js +10 -11
  88. package/dist/dashboard/skill-install-request.js.map +1 -1
  89. package/dist/dashboard/skill-pack-response.d.ts +15 -0
  90. package/dist/dashboard/skill-pack-response.d.ts.map +1 -0
  91. package/dist/dashboard/skill-pack-response.js +32 -0
  92. package/dist/dashboard/skill-pack-response.js.map +1 -0
  93. package/dist/dashboard/web/connectors-page.d.ts +69 -0
  94. package/dist/dashboard/web/connectors-page.d.ts.map +1 -1
  95. package/dist/dashboard/web/connectors-page.js +47 -7
  96. package/dist/dashboard/web/connectors-page.js.map +1 -1
  97. package/dist/dashboard/web/i18n.d.ts.map +1 -1
  98. package/dist/dashboard/web/i18n.js +301 -26
  99. package/dist/dashboard/web/i18n.js.map +1 -1
  100. package/dist/dashboard/web/preferences.d.ts +4 -2
  101. package/dist/dashboard/web/preferences.d.ts.map +1 -1
  102. package/dist/dashboard/web/preferences.js +14 -6
  103. package/dist/dashboard/web/preferences.js.map +1 -1
  104. package/dist/dashboard/web/sessions-page.d.ts.map +1 -1
  105. package/dist/dashboard/web/sessions-page.js +99 -3
  106. package/dist/dashboard/web/sessions-page.js.map +1 -1
  107. package/dist/dashboard/web/sessions.d.ts +3 -1
  108. package/dist/dashboard/web/sessions.d.ts.map +1 -1
  109. package/dist/dashboard/web/sessions.js +13 -6
  110. package/dist/dashboard/web/sessions.js.map +1 -1
  111. package/dist/dashboard/web/skills/bot-assignments-tab.d.ts +27 -0
  112. package/dist/dashboard/web/skills/bot-assignments-tab.d.ts.map +1 -0
  113. package/dist/dashboard/web/skills/bot-assignments-tab.js +257 -0
  114. package/dist/dashboard/web/skills/bot-assignments-tab.js.map +1 -0
  115. package/dist/dashboard/web/skills/delivery-settings-tab.d.ts +25 -0
  116. package/dist/dashboard/web/skills/delivery-settings-tab.d.ts.map +1 -0
  117. package/dist/dashboard/web/skills/delivery-settings-tab.js +20 -0
  118. package/dist/dashboard/web/skills/delivery-settings-tab.js.map +1 -0
  119. package/dist/dashboard/web/skills/shared.d.ts +86 -0
  120. package/dist/dashboard/web/skills/shared.d.ts.map +1 -0
  121. package/dist/dashboard/web/skills/shared.js +178 -0
  122. package/dist/dashboard/web/skills/shared.js.map +1 -0
  123. package/dist/dashboard/web/skills/skill-library-tab.d.ts +75 -0
  124. package/dist/dashboard/web/skills/skill-library-tab.d.ts.map +1 -0
  125. package/dist/dashboard/web/skills/skill-library-tab.js +108 -0
  126. package/dist/dashboard/web/skills/skill-library-tab.js.map +1 -0
  127. package/dist/dashboard/web/skills/skill-packs-tab.d.ts +21 -0
  128. package/dist/dashboard/web/skills/skill-packs-tab.d.ts.map +1 -0
  129. package/dist/dashboard/web/skills/skill-packs-tab.js +185 -0
  130. package/dist/dashboard/web/skills/skill-packs-tab.js.map +1 -0
  131. package/dist/dashboard/web/skills/types.d.ts +95 -0
  132. package/dist/dashboard/web/skills/types.d.ts.map +1 -0
  133. package/dist/dashboard/web/skills/types.js +2 -0
  134. package/dist/dashboard/web/skills/types.js.map +1 -0
  135. package/dist/dashboard/web/skills/use-skills-data.d.ts +31 -0
  136. package/dist/dashboard/web/skills/use-skills-data.d.ts.map +1 -0
  137. package/dist/dashboard/web/skills/use-skills-data.js +86 -0
  138. package/dist/dashboard/web/skills/use-skills-data.js.map +1 -0
  139. package/dist/dashboard/web/skills-page.d.ts +34 -31
  140. package/dist/dashboard/web/skills-page.d.ts.map +1 -1
  141. package/dist/dashboard/web/skills-page.js +321 -176
  142. package/dist/dashboard/web/skills-page.js.map +1 -1
  143. package/dist/dashboard/webhook-routes.d.ts +18 -0
  144. package/dist/dashboard/webhook-routes.d.ts.map +1 -1
  145. package/dist/dashboard/webhook-routes.js +35 -1
  146. package/dist/dashboard/webhook-routes.js.map +1 -1
  147. package/dist/dashboard-web/app.js +1 -1
  148. package/dist/dashboard-web/chunks/{bot-defaults-page-USC6EZXP.js → bot-defaults-page-HL655CNR.js} +1 -1
  149. package/dist/dashboard-web/chunks/{chunk-KOPISPHN.js → chunk-5PAYQ3KT.js} +1 -1
  150. package/dist/dashboard-web/chunks/{chunk-RXRJB5SO.js → chunk-5ZL7NLR5.js} +1 -1
  151. package/dist/dashboard-web/chunks/{chunk-A7VC4BXR.js → chunk-BFUTTYVK.js} +1 -1
  152. package/dist/dashboard-web/chunks/chunk-ELL53D62.js +55 -0
  153. package/dist/dashboard-web/chunks/connectors-page-3QQACF23.js +1 -0
  154. package/dist/dashboard-web/chunks/{groups-page-C6NSDTIN.js → groups-page-SDZMJ7UV.js} +1 -1
  155. package/dist/dashboard-web/chunks/{insights-page-UTOVYLLY.js → insights-page-ZZ3JEJ6L.js} +1 -1
  156. package/dist/dashboard-web/chunks/{monitor-room-UKTJYTA2.js → monitor-room-ROJ5GZ4N.js} +1 -1
  157. package/dist/dashboard-web/chunks/{monitoring-page-Q3CEZIVX.js → monitoring-page-IATV7KJF.js} +1 -1
  158. package/dist/dashboard-web/chunks/{office-page-GYCZOOVJ.js → office-page-TYO5OIIT.js} +1 -1
  159. package/dist/dashboard-web/chunks/{overview-page-W2VBJ6OH.js → overview-page-VJUULZF2.js} +1 -1
  160. package/dist/dashboard-web/chunks/{roles-page-U4SHER4K.js → roles-page-7RJJKZGK.js} +1 -1
  161. package/dist/dashboard-web/chunks/{schedules-page-TNPW3N3Q.js → schedules-page-JTEJ5IXW.js} +1 -1
  162. package/dist/dashboard-web/chunks/sessions-page-KD7HDT3U.js +8 -0
  163. package/dist/dashboard-web/chunks/{settings-page-VKW5ZK5K.js → settings-page-7CTTFBRS.js} +1 -1
  164. package/dist/dashboard-web/chunks/skills-page-57NZECJ3.js +4 -0
  165. package/dist/dashboard-web/chunks/{team-federation-page-OMFGGBT2.js → team-federation-page-6YYIMJG3.js} +1 -1
  166. package/dist/dashboard-web/chunks/{v3-page-PLXWK4KO.js → v3-page-ZGKBSBK3.js} +1 -1
  167. package/dist/dashboard-web/chunks/{whiteboards-page-NJQOEO2P.js → whiteboards-page-AACJF6DB.js} +1 -1
  168. package/dist/dashboard-web/style.css +1623 -160
  169. package/dist/dashboard.js +200 -16
  170. package/dist/dashboard.js.map +1 -1
  171. package/dist/i18n/en.d.ts.map +1 -1
  172. package/dist/i18n/en.js +2 -0
  173. package/dist/i18n/en.js.map +1 -1
  174. package/dist/i18n/zh.d.ts.map +1 -1
  175. package/dist/i18n/zh.js +2 -0
  176. package/dist/i18n/zh.js.map +1 -1
  177. package/dist/index-daemon.js +6 -1
  178. package/dist/index-daemon.js.map +1 -1
  179. package/dist/services/connector-store.d.ts +14 -1
  180. package/dist/services/connector-store.d.ts.map +1 -1
  181. package/dist/services/connector-store.js.map +1 -1
  182. package/dist/services/connector-topic-template.d.ts +7 -0
  183. package/dist/services/connector-topic-template.d.ts.map +1 -0
  184. package/dist/services/connector-topic-template.js +210 -0
  185. package/dist/services/connector-topic-template.js.map +1 -0
  186. package/dist/services/project-scanner.d.ts +33 -0
  187. package/dist/services/project-scanner.d.ts.map +1 -1
  188. package/dist/services/project-scanner.js +40 -1
  189. package/dist/services/project-scanner.js.map +1 -1
  190. package/dist/services/skill-pack-store.d.ts +50 -0
  191. package/dist/services/skill-pack-store.d.ts.map +1 -0
  192. package/dist/services/skill-pack-store.js +270 -0
  193. package/dist/services/skill-pack-store.js.map +1 -0
  194. package/dist/services/skill-registry-store.d.ts +8 -0
  195. package/dist/services/skill-registry-store.d.ts.map +1 -1
  196. package/dist/services/skill-registry-store.js +100 -68
  197. package/dist/services/skill-registry-store.js.map +1 -1
  198. package/dist/services/todo-state.d.ts +74 -0
  199. package/dist/services/todo-state.d.ts.map +1 -0
  200. package/dist/services/todo-state.js +351 -0
  201. package/dist/services/todo-state.js.map +1 -0
  202. package/dist/setup/agent-preset.d.ts.map +1 -1
  203. package/dist/setup/agent-preset.js +2 -0
  204. package/dist/setup/agent-preset.js.map +1 -1
  205. package/dist/setup/owner-identity.d.ts +47 -0
  206. package/dist/setup/owner-identity.d.ts.map +1 -0
  207. package/dist/setup/owner-identity.js +196 -0
  208. package/dist/setup/owner-identity.js.map +1 -0
  209. package/dist/setup/register-app.d.ts +4 -3
  210. package/dist/setup/register-app.d.ts.map +1 -1
  211. package/dist/setup/register-app.js.map +1 -1
  212. package/dist/setup/setup-args.d.ts +1 -1
  213. package/dist/setup/setup-args.d.ts.map +1 -1
  214. package/dist/setup/setup-args.js +7 -3
  215. package/dist/setup/setup-args.js.map +1 -1
  216. package/dist/types.d.ts +11 -0
  217. package/dist/types.d.ts.map +1 -1
  218. package/dist/utils/child-env.d.ts +25 -0
  219. package/dist/utils/child-env.d.ts.map +1 -1
  220. package/dist/utils/child-env.js +47 -0
  221. package/dist/utils/child-env.js.map +1 -1
  222. package/dist/worker.js +55 -16
  223. package/dist/worker.js.map +1 -1
  224. package/dist/workflows/definition.d.ts +44 -44
  225. package/package.json +1 -1
  226. package/dist/dashboard-web/chunks/chunk-M6BHWQFO.js +0 -51
  227. package/dist/dashboard-web/chunks/connectors-page-4Z76HWRH.js +0 -1
  228. package/dist/dashboard-web/chunks/sessions-page-YWWDDV57.js +0 -6
  229. package/dist/dashboard-web/chunks/skills-page-ZCEDFGZZ.js +0 -4
package/dist/dashboard.js CHANGED
@@ -62,14 +62,17 @@ import { listTeamReports, readTeamBoard, setTeamBoardEntry } from './services/te
62
62
  import { createCliAdapterSync } from './adapters/cli/registry.js';
63
63
  import { hd2dAssetPath, hd2dStatus, startHd2dDownload } from './dashboard/hd2d-assets.js';
64
64
  import { buildSkillInstallAuditSummary, installLocalSkillLinks, readSkillRegistry, removeInstalledSkill, removeInstalledSkills, updateInstalledSkillAsync, } from './services/skill-registry-store.js';
65
+ import { readSkillPackRegistry } from './services/skill-pack-store.js';
66
+ import { cloneSkillPack, createSkillPack, deleteSkillPack, getSkillPack, listSkillPacks, updateSkillPack, SkillPackStoreError, } from './services/skill-pack-store.js';
65
67
  import { redactGitUrlCredentials } from './core/skills/sources.js';
68
+ import { enrichPackForDashboard, enrichPacksForDashboard, sanitizeSkillForDashboard, } from './dashboard/skill-pack-response.js';
66
69
  import { effectiveDefaultWorkingDir, getBot, loadBotConfigs, parseBotConfigsFromText } from './bot-registry.js';
67
70
  import { addChatToFeedGroup, createFeedGroup, FEED_GROUP_SCOPES, FeedGroupApiError, listFeedGroups } from './dashboard/feed-groups.js';
68
71
  import { generateAuthUrl, handleCallbackUrl, isCallbackUrl } from './utils/user-token.js';
69
72
  import { findEntryIndex, readRawConfig, requireConfigPath, writeRawConfigAtomic } from './services/config-store.js';
70
73
  import { emitCodexNotifierOutboxItem, installCodexNotifierHook, isCodexNotifierWorkerStateFresh, isCodexNotifierHookInstalled, listCodexNotifierOutbox, readCodexNotifierWorkerState, resolveCodexNotifierConfig, runCodexSideConversationMonitor, runCodexNotifierWorkerSupervisor, } from './features/codex-notifier/index.js';
71
74
  import { discoverNativeCliSkillGroups } from './core/skills/discovery.js';
72
- import { analyzeSkillReferences } from './core/skills/references.js';
75
+ import { analyzeSkillReferences, packsContainingSkill } from './core/skills/references.js';
73
76
  import { discoverDashboardSkills, installDashboardSkill, parseDashboardSkillInstallRequest, parseInstallLocalLinksSources, MAX_LOCAL_LINK_SOURCES } from './dashboard/skill-install-request.js';
74
77
  import { botDefaultsPayload, botSummaryPayload, brandMapByAppId } from './dashboard/bot-payload.js';
75
78
  import { handleVcMeetingConsumerProfilesGet, handleVcMeetingConsumerProfilesPut, } from './dashboard/vc-consumer-profiles-api.js';
@@ -106,6 +109,13 @@ import { inspectGatewayEntry } from './core/plugins/mcp/gateway-installer.js';
106
109
  import { fetchDaemonIpc } from './core/daemon-ipc-auth.js';
107
110
  import { buildDashboardSummary, parseDashboardSummaryRows, } from './dashboard/dashboard-summary.js';
108
111
  import { createDashboardSummaryEndpoint } from './dashboard/dashboard-summary-endpoint.js';
112
+ import { scrubWorkflowWorkerEnv } from './utils/child-env.js';
113
+ // The dashboard is an independent long-lived PM2 app and can be resurrected
114
+ // from a stale dump.pm2 without passing through cli.ts pm2Env(). Its start/stop
115
+ // and detached-restart children inherit process.env, so a leaked workflow
116
+ // marker would make those CLI commands fail at the workflow safety gate before
117
+ // they can reach their own cleanup boundary.
118
+ scrubWorkflowWorkerEnv(process.env);
109
119
  const SECRET_PATH = dashboardSecretPath();
110
120
  const TOKEN_PATH = join(homedir(), '.botmux', '.dashboard-token');
111
121
  /** Per-daemon budget for the cross-daemon insight overview fan-out — bounds
@@ -2303,16 +2313,13 @@ function startSkillJob(type, run) {
2303
2313
  })());
2304
2314
  return job;
2305
2315
  }
2306
- function sanitizeSkillForDashboard(skill) {
2307
- if (skill.source.type !== 'git')
2308
- return skill;
2309
- return {
2310
- ...skill,
2311
- source: { ...skill.source, url: redactGitUrlCredentials(skill.source.url) },
2312
- };
2313
- }
2314
2316
  function dashboardSkillCliIds() {
2315
2317
  const ids = new Set();
2318
+ // Always scan all known CLI skill dirs, not just configured bots — users may
2319
+ // want to discover codex/trae/... skills even before creating a bot for them.
2320
+ const allCliIds = ['claude-code', 'seed', 'relay', 'aiden', 'coco', 'codex', 'codex-app', 'cursor', 'gemini', 'genius', 'opencode', 'antigravity', 'mtr', 'hermes', 'mira', 'mir', 'traex', 'pi', 'copilot', 'oh-my-pi', 'kimi', 'grok', 'kiro-cli', 'riff'];
2321
+ for (const cliId of allCliIds)
2322
+ ids.add(cliId);
2316
2323
  try {
2317
2324
  for (const cliId of configuredCliIds().values())
2318
2325
  ids.add(cliId);
@@ -2342,6 +2349,76 @@ function dashboardSkillsPayload() {
2342
2349
  delivery: globalSkills.delivery ?? 'auto',
2343
2350
  };
2344
2351
  }
2352
+ // --- Skill pack dashboard helpers ------------------------------------------
2353
+ function loadBotConfigsSafe() {
2354
+ try {
2355
+ return loadBotConfigs();
2356
+ }
2357
+ catch {
2358
+ return [];
2359
+ }
2360
+ }
2361
+ function botsReferencingPack(packId, bots) {
2362
+ const selector = `pack:${packId}`;
2363
+ return bots
2364
+ .filter((bot) => Array.isArray(bot.skills?.include) && bot.skills.include.includes(selector))
2365
+ .map((bot) => ({ larkAppId: bot.larkAppId, botName: bot.name ?? bot.larkAppId }))
2366
+ .sort((a, b) => a.botName.localeCompare(b.botName));
2367
+ }
2368
+ function parsePackInput(body) {
2369
+ if (!body || typeof body !== 'object' || Array.isArray(body))
2370
+ throw new SkillPackStoreError({ code: 'SKILL_PACK_INVALID', reason: 'body must be an object' });
2371
+ const b = body;
2372
+ return {
2373
+ id: typeof b.id === 'string' ? b.id : '',
2374
+ name: typeof b.name === 'string' ? b.name : '',
2375
+ description: typeof b.description === 'string' ? b.description : undefined,
2376
+ tags: Array.isArray(b.tags) ? b.tags : undefined,
2377
+ include: Array.isArray(b.include) ? b.include : [],
2378
+ };
2379
+ }
2380
+ function parsePackUpdate(body) {
2381
+ if (!body || typeof body !== 'object' || Array.isArray(body))
2382
+ throw new SkillPackStoreError({ code: 'SKILL_PACK_INVALID', reason: 'body must be an object' });
2383
+ const b = body;
2384
+ return {
2385
+ name: typeof b.name === 'string' ? b.name : undefined,
2386
+ description: b.description === null ? null : typeof b.description === 'string' ? b.description : undefined,
2387
+ tags: b.tags === null ? null : Array.isArray(b.tags) ? b.tags : undefined,
2388
+ include: Array.isArray(b.include) ? b.include : undefined,
2389
+ expectedRevision: typeof b.expectedRevision === 'number' ? b.expectedRevision : undefined,
2390
+ };
2391
+ }
2392
+ function packErrorStatus(err) {
2393
+ if (err instanceof SkillPackStoreError) {
2394
+ switch (err.detail.code) {
2395
+ case 'SKILL_PACK_NOT_FOUND': return 404;
2396
+ case 'SKILL_PACK_ID_CONFLICT': return 409;
2397
+ case 'SKILL_PACK_REVISION_CONFLICT': return 409;
2398
+ case 'SKILL_PACK_IN_USE': return 409;
2399
+ default: return 400;
2400
+ }
2401
+ }
2402
+ return 400;
2403
+ }
2404
+ function packErrorBody(err) {
2405
+ if (err instanceof SkillPackStoreError) {
2406
+ const d = err.detail;
2407
+ const body = { ok: false, error: d.code };
2408
+ if (d.code === 'SKILL_PACK_REVISION_CONFLICT')
2409
+ body.current = d.current;
2410
+ if (d.code === 'SKILL_PACK_INVALID')
2411
+ body.reason = d.reason;
2412
+ if (d.code === 'SKILL_PACK_INVALID_SELECTOR')
2413
+ body.selector = d.selector;
2414
+ return body;
2415
+ }
2416
+ return {
2417
+ ok: false,
2418
+ error: 'internal_error',
2419
+ detail: redactGitUrlCredentials(err instanceof Error ? err.message : String(err)),
2420
+ };
2421
+ }
2345
2422
  function mergeSkillReferenceBot(refs, ref) {
2346
2423
  const current = refs.get(ref.larkAppId);
2347
2424
  if (!current) {
@@ -2353,11 +2430,18 @@ function mergeSkillReferenceBot(refs, ref) {
2353
2430
  async function dashboardSkillReferencesMany(skillNames) {
2354
2431
  const uniqueNames = [...new Set(skillNames)];
2355
2432
  const refsBySkill = new Map(uniqueNames.map(name => [name, new Map()]));
2433
+ let packs;
2434
+ try {
2435
+ packs = readSkillPackRegistry().packs;
2436
+ }
2437
+ catch {
2438
+ // packs.json may be absent; fall back to direct-only analysis.
2439
+ }
2356
2440
  try {
2357
2441
  const configuredBots = loadBotConfigs();
2358
2442
  for (const name of uniqueNames) {
2359
2443
  const refs = refsBySkill.get(name);
2360
- for (const ref of analyzeSkillReferences(name, { bots: configuredBots }).bots)
2444
+ for (const ref of analyzeSkillReferences(name, { bots: configuredBots, packs }).bots)
2361
2445
  mergeSkillReferenceBot(refs, ref);
2362
2446
  }
2363
2447
  }
@@ -2383,15 +2467,16 @@ async function dashboardSkillReferencesMany(skillNames) {
2383
2467
  const availableOnlineConfigs = onlineConfigs.filter(config => config !== null);
2384
2468
  for (const name of uniqueNames) {
2385
2469
  const refs = refsBySkill.get(name);
2386
- for (const ref of analyzeSkillReferences(name, { bots: availableOnlineConfigs }).bots)
2470
+ for (const ref of analyzeSkillReferences(name, { bots: availableOnlineConfigs, packs }).bots)
2387
2471
  mergeSkillReferenceBot(refs, ref);
2388
2472
  }
2389
2473
  return new Map([...refsBySkill].map(([name, refs]) => [name, {
2390
2474
  bots: [...refs.values()].sort((a, b) => a.botName.localeCompare(b.botName)),
2475
+ packs: packsContainingSkill(name, packs),
2391
2476
  }]));
2392
2477
  }
2393
2478
  async function dashboardSkillReferences(skillName) {
2394
- return (await dashboardSkillReferencesMany([skillName])).get(skillName) ?? { bots: [] };
2479
+ return (await dashboardSkillReferencesMany([skillName])).get(skillName) ?? { bots: [], packs: [] };
2395
2480
  }
2396
2481
  /** Extract the sessionId from a terminal path `/s/<sessionId>[/...]`. Returns
2397
2482
  * the first path segment after `/s/` (stops at the next `/`; query/hash are
@@ -3340,10 +3425,14 @@ const server = createServer(async (req, res) => {
3340
3425
  if (missing.length > 0)
3341
3426
  return jsonRes(res, 400, { ok: false, error: 'skill_not_installed', missing });
3342
3427
  const referencesBySkill = await dashboardSkillReferencesMany(names);
3343
- const references = names.map(name => ({ name, refs: referencesBySkill.get(name) ?? { bots: [] } }));
3428
+ const references = names.map(name => ({ name, refs: referencesBySkill.get(name) ?? { bots: [], packs: [] } }));
3344
3429
  const affectedSkills = references
3345
- .filter(item => item.refs.bots.length > 0)
3346
- .map(item => ({ name: item.name, affectedBots: item.refs.bots }));
3430
+ .filter(item => item.refs.bots.length > 0 || item.refs.packs.length > 0)
3431
+ .map(item => ({
3432
+ name: item.name,
3433
+ affectedBots: item.refs.bots,
3434
+ affectedPacks: item.refs.packs,
3435
+ }));
3347
3436
  if (body.force !== true && affectedSkills.length > 0) {
3348
3437
  return jsonRes(res, 409, {
3349
3438
  ok: false,
@@ -3474,11 +3563,12 @@ const server = createServer(async (req, res) => {
3474
3563
  if (!readSkillRegistry().skills[name])
3475
3564
  return jsonRes(res, 400, { ok: false, error: 'skill_not_installed' });
3476
3565
  const refs = await dashboardSkillReferences(name);
3477
- if (!force && refs.bots.length > 0) {
3566
+ if (!force && (refs.bots.length > 0 || refs.packs.length > 0)) {
3478
3567
  return jsonRes(res, 409, {
3479
3568
  ok: false,
3480
3569
  error: 'skill_in_use',
3481
3570
  affectedBots: refs.bots,
3571
+ affectedPacks: refs.packs,
3482
3572
  });
3483
3573
  }
3484
3574
  const r = removeInstalledSkill(name);
@@ -3487,9 +3577,103 @@ const server = createServer(async (req, res) => {
3487
3577
  return jsonRes(res, 200, {
3488
3578
  ok: true,
3489
3579
  affectedBots: refs.bots,
3580
+ affectedPacks: refs.packs,
3490
3581
  ...dashboardSkillsPayload(),
3491
3582
  });
3492
3583
  }
3584
+ // --- Skill pack CRUD ---------------------------------------------------
3585
+ if (req.method === 'GET' && url.pathname === '/api/skill-packs') {
3586
+ const registrySkills = readSkillRegistry().skills;
3587
+ const bots = loadBotConfigsSafe();
3588
+ const packs = enrichPacksForDashboard(listSkillPacks(), registrySkills, (packId) => botsReferencingPack(packId, bots));
3589
+ return jsonRes(res, 200, { ok: true, packs });
3590
+ }
3591
+ if (req.method === 'POST' && url.pathname === '/api/skill-packs') {
3592
+ let body;
3593
+ try {
3594
+ body = await readJsonBody(req);
3595
+ }
3596
+ catch {
3597
+ return jsonRes(res, 400, { ok: false, error: 'bad_json' });
3598
+ }
3599
+ try {
3600
+ const input = parsePackInput(body);
3601
+ const pack = createSkillPack(input);
3602
+ return jsonRes(res, 201, { ok: true, pack });
3603
+ }
3604
+ catch (err) {
3605
+ return jsonRes(res, packErrorStatus(err), packErrorBody(err));
3606
+ }
3607
+ }
3608
+ let mPack;
3609
+ if (req.method === 'GET' && (mPack = url.pathname.match(/^\/api\/skill-packs\/([^/]+)$/))) {
3610
+ const id = decodeURIComponent(mPack[1]);
3611
+ const pack = getSkillPack(id);
3612
+ if (!pack)
3613
+ return jsonRes(res, 404, { ok: false, error: 'SKILL_PACK_NOT_FOUND' });
3614
+ const registrySkills = readSkillRegistry().skills;
3615
+ const bots = loadBotConfigsSafe();
3616
+ return jsonRes(res, 200, {
3617
+ ok: true,
3618
+ pack: enrichPackForDashboard(pack, registrySkills, botsReferencingPack(pack.id, bots)),
3619
+ });
3620
+ }
3621
+ if (req.method === 'PUT' && (mPack = url.pathname.match(/^\/api\/skill-packs\/([^/]+)$/))) {
3622
+ const id = decodeURIComponent(mPack[1]);
3623
+ let body;
3624
+ try {
3625
+ body = await readJsonBody(req);
3626
+ }
3627
+ catch {
3628
+ return jsonRes(res, 400, { ok: false, error: 'bad_json' });
3629
+ }
3630
+ try {
3631
+ const input = parsePackUpdate(body);
3632
+ const pack = updateSkillPack(id, input);
3633
+ return jsonRes(res, 200, { ok: true, pack });
3634
+ }
3635
+ catch (err) {
3636
+ return jsonRes(res, packErrorStatus(err), packErrorBody(err));
3637
+ }
3638
+ }
3639
+ if (req.method === 'DELETE' && (mPack = url.pathname.match(/^\/api\/skill-packs\/([^/]+)$/))) {
3640
+ const id = decodeURIComponent(mPack[1]);
3641
+ const force = url.searchParams.get('force') === '1';
3642
+ const pack = getSkillPack(id);
3643
+ if (!pack)
3644
+ return jsonRes(res, 404, { ok: false, error: 'SKILL_PACK_NOT_FOUND' });
3645
+ const refs = botsReferencingPack(id, loadBotConfigsSafe());
3646
+ if (!force && refs.length > 0) {
3647
+ return jsonRes(res, 409, { ok: false, error: 'SKILL_PACK_IN_USE', references: refs });
3648
+ }
3649
+ try {
3650
+ deleteSkillPack(id);
3651
+ return jsonRes(res, 200, { ok: true, references: refs });
3652
+ }
3653
+ catch (err) {
3654
+ return jsonRes(res, packErrorStatus(err), packErrorBody(err));
3655
+ }
3656
+ }
3657
+ if (req.method === 'POST' && (mPack = url.pathname.match(/^\/api\/skill-packs\/([^/]+)\/clone$/))) {
3658
+ const id = decodeURIComponent(mPack[1]);
3659
+ let body;
3660
+ try {
3661
+ body = await readJsonBody(req);
3662
+ }
3663
+ catch {
3664
+ return jsonRes(res, 400, { ok: false, error: 'bad_json' });
3665
+ }
3666
+ const newId = typeof body?.id === 'string' ? body.id.trim() : '';
3667
+ if (!newId)
3668
+ return jsonRes(res, 400, { ok: false, error: 'id_required' });
3669
+ try {
3670
+ const pack = cloneSkillPack(id, newId);
3671
+ return jsonRes(res, 201, { ok: true, pack });
3672
+ }
3673
+ catch (err) {
3674
+ return jsonRes(res, packErrorStatus(err), packErrorBody(err));
3675
+ }
3676
+ }
3493
3677
  if (req.method === 'GET' && url.pathname === '/api/whiteboards') {
3494
3678
  return jsonRes(res, 200, { enabled: whiteboardEnabled(), whiteboards: listWhiteboards() });
3495
3679
  }