@jingyi0605/codingns 0.4.0 → 0.5.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 (267) hide show
  1. package/bin/codingns.mjs +425 -1
  2. package/dist/public/assets/AdaptiveButlerPage-B153lk5H.css +1 -0
  3. package/dist/public/assets/AdaptiveButlerPage-R-XZw7pd.js +3 -0
  4. package/dist/public/assets/App-DUAg5urj.css +1 -0
  5. package/dist/public/assets/App-DkvE5EyM.js +30 -0
  6. package/dist/public/assets/BootstrapPage-Vu5oEJ8z.js +1 -0
  7. package/dist/public/assets/ConversationPage-Cjpg6g0J.js +2 -0
  8. package/dist/public/assets/DesktopDetachPreviewPage-BgeEqbc5.js +1 -0
  9. package/dist/public/assets/DesktopWindowPage-1WelvxdH.js +2 -0
  10. package/dist/public/assets/FileContextPanel-D_ghXJuW.js +1 -0
  11. package/dist/public/assets/GitSidebar-D9f9Jxwr.js +6 -0
  12. package/dist/public/assets/MobileCreateSessionSheet-DLq5qPkx.js +1 -0
  13. package/dist/public/assets/MobileSheet-DLg-gX1t.js +1 -0
  14. package/dist/public/assets/MobileTopHeaderFrame-DArgZI7L.js +1 -0
  15. package/dist/public/assets/MobileWorkspaceSwitcherHeader-0ywJKfBQ.js +1 -0
  16. package/dist/public/assets/ServerSettingsModal-izoYMx9U.js +1 -0
  17. package/dist/public/assets/SessionIndexPage-C5aG8FIv.js +1 -0
  18. package/dist/public/assets/SettingsPage-HJIC-P-4.js +1 -0
  19. package/dist/public/assets/TerminalManagerPanel-DpyUTo9k.js +1 -0
  20. package/dist/public/assets/{TerminalPage-6jHZV9Mh.js → TerminalPage-CtKXIU0h.js} +19 -19
  21. package/dist/public/assets/TerminalRuntimeFallbackModal-CRhOQOsT.js +1 -0
  22. package/dist/public/assets/ToolFilesPage-DcYPsS-e.js +1 -0
  23. package/dist/public/assets/ToolGitPage-CsPl89ty.js +1 -0
  24. package/dist/public/assets/ToolProcessesPage-D0dvR8xK.js +1 -0
  25. package/dist/public/assets/ToolsHomePage-4fP-KRiv.js +1 -0
  26. package/dist/public/assets/WorkbenchLandingPage-kvlfyxRo.js +1 -0
  27. package/dist/public/assets/WorkbenchLayout-ByFw4eeu.js +3 -0
  28. package/dist/public/assets/WorkbenchModal-Ctob14VR.js +1 -0
  29. package/dist/public/assets/WorkbenchShellRoute-BUITtdAg.css +1 -0
  30. package/dist/public/assets/WorkbenchShellRoute-Kw7JEZI3.js +1 -0
  31. package/dist/public/assets/WorkspaceDebugDetailPage-Com5kEXJ.js +1 -0
  32. package/dist/public/assets/WorkspaceDetailPage-D0Lrx4Uz.js +1 -0
  33. package/dist/public/assets/WorkspaceHomePage-wR8d3aP9.js +1 -0
  34. package/dist/public/assets/butler-records-events-DgWCG364.js +1 -0
  35. package/dist/public/assets/default-session-permission-mode-CcGwR4Kk.js +1 -0
  36. package/dist/public/assets/event-DvH9tcej.js +1 -0
  37. package/dist/public/assets/file-tree-icon-UFVoVzhM.js +31 -0
  38. package/dist/public/assets/index-Byp9hJ0c.js +42 -0
  39. package/dist/public/assets/index-_52jxu4a.css +1 -0
  40. package/dist/public/assets/preferences-service-KIYeE2gk.js +1 -0
  41. package/dist/public/assets/session-runtime-machine-0KNSSPp5.js +17 -0
  42. package/dist/public/assets/styles-BWPBZvze.css +1 -0
  43. package/dist/public/assets/styles-CSUx5LGe.js +1 -0
  44. package/dist/public/assets/terminal-runtime-meta-AWXJpN4r.js +1 -0
  45. package/dist/public/assets/useRegisteredDebugTemplates-DBDRdptr.js +1 -0
  46. package/dist/public/assets/window-BWqRixxq.js +1 -0
  47. package/dist/public/index.html +2 -2
  48. package/dist/server/middlewares/auth-guard.d.ts +4 -0
  49. package/dist/server/middlewares/auth-guard.js +42 -4
  50. package/dist/server/middlewares/auth-guard.js.map +1 -1
  51. package/dist/server/modules/assistant-capability/assistant-capability-controller.d.ts +62 -1
  52. package/dist/server/modules/assistant-capability/assistant-capability-controller.js +58 -0
  53. package/dist/server/modules/assistant-capability/assistant-capability-controller.js.map +1 -1
  54. package/dist/server/modules/assistant-capability/assistant-capability-service.d.ts +66 -3
  55. package/dist/server/modules/assistant-capability/assistant-capability-service.js +173 -1
  56. package/dist/server/modules/assistant-capability/assistant-capability-service.js.map +1 -1
  57. package/dist/server/modules/auth/auth-controller.d.ts +11 -1
  58. package/dist/server/modules/auth/auth-controller.js +61 -2
  59. package/dist/server/modules/auth/auth-controller.js.map +1 -1
  60. package/dist/server/modules/auth/auth-device-display-name.d.ts +10 -0
  61. package/dist/server/modules/auth/auth-device-display-name.js +190 -0
  62. package/dist/server/modules/auth/auth-device-display-name.js.map +1 -0
  63. package/dist/server/modules/auth/auth-service.d.ts +80 -5
  64. package/dist/server/modules/auth/auth-service.js +333 -23
  65. package/dist/server/modules/auth/auth-service.js.map +1 -1
  66. package/dist/server/modules/butler/assistant-automation-service.d.ts +2 -0
  67. package/dist/server/modules/butler/assistant-automation-service.js +46 -0
  68. package/dist/server/modules/butler/assistant-automation-service.js.map +1 -1
  69. package/dist/server/modules/butler/assistant-sandbox-cleanup-scheduler.d.ts +32 -0
  70. package/dist/server/modules/butler/assistant-sandbox-cleanup-scheduler.js +93 -0
  71. package/dist/server/modules/butler/assistant-sandbox-cleanup-scheduler.js.map +1 -0
  72. package/dist/server/modules/butler/assistant-sandbox-service.d.ts +16 -2
  73. package/dist/server/modules/butler/assistant-sandbox-service.js +137 -4
  74. package/dist/server/modules/butler/assistant-sandbox-service.js.map +1 -1
  75. package/dist/server/modules/butler/butler-auth-service.js +7 -2
  76. package/dist/server/modules/butler/butler-auth-service.js.map +1 -1
  77. package/dist/server/modules/butler/butler-control-session-service.d.ts +4 -1
  78. package/dist/server/modules/butler/butler-control-session-service.js +20 -1
  79. package/dist/server/modules/butler/butler-control-session-service.js.map +1 -1
  80. package/dist/server/modules/butler/butler-follow-up-evaluation-instruction-adapter.d.ts +2 -1
  81. package/dist/server/modules/butler/butler-follow-up-evaluation-instruction-adapter.js +27 -25
  82. package/dist/server/modules/butler/butler-follow-up-evaluation-instruction-adapter.js.map +1 -1
  83. package/dist/server/modules/butler/butler-follow-up-service.d.ts +32 -4
  84. package/dist/server/modules/butler/butler-follow-up-service.js +436 -331
  85. package/dist/server/modules/butler/butler-follow-up-service.js.map +1 -1
  86. package/dist/server/modules/butler/butler-inbox-analysis-service.d.ts +1 -1
  87. package/dist/server/modules/butler/butler-inbox-analysis-service.js.map +1 -1
  88. package/dist/server/modules/butler/butler-inbox-service.js +1 -0
  89. package/dist/server/modules/butler/butler-inbox-service.js.map +1 -1
  90. package/dist/server/modules/butler/butler-session-service.d.ts +3 -1
  91. package/dist/server/modules/butler/butler-session-service.js +15 -1
  92. package/dist/server/modules/butler/butler-session-service.js.map +1 -1
  93. package/dist/server/modules/butler/butler-workspace-context.d.ts +1 -1
  94. package/dist/server/modules/butler/butler-workspace-context.js +54 -28
  95. package/dist/server/modules/butler/butler-workspace-context.js.map +1 -1
  96. package/dist/server/modules/provider/provider-controller.d.ts +1 -1
  97. package/dist/server/modules/provider/provider-controller.js.map +1 -1
  98. package/dist/server/modules/relay-tunnel/crypto/relay-tunnel-identity-service.d.ts +10 -0
  99. package/dist/server/modules/relay-tunnel/crypto/relay-tunnel-identity-service.js +48 -0
  100. package/dist/server/modules/relay-tunnel/crypto/relay-tunnel-identity-service.js.map +1 -0
  101. package/dist/server/modules/relay-tunnel/crypto/relay-tunnel-packets.d.ts +48 -0
  102. package/dist/server/modules/relay-tunnel/crypto/relay-tunnel-packets.js +11 -0
  103. package/dist/server/modules/relay-tunnel/crypto/relay-tunnel-packets.js.map +1 -0
  104. package/dist/server/modules/relay-tunnel/crypto/relay-tunnel-protocol.d.ts +74 -0
  105. package/dist/server/modules/relay-tunnel/crypto/relay-tunnel-protocol.js +302 -0
  106. package/dist/server/modules/relay-tunnel/crypto/relay-tunnel-protocol.js.map +1 -0
  107. package/dist/server/modules/relay-tunnel/relay-tunnel-controller.d.ts +33 -0
  108. package/dist/server/modules/relay-tunnel/relay-tunnel-controller.js +57 -0
  109. package/dist/server/modules/relay-tunnel/relay-tunnel-controller.js.map +1 -0
  110. package/dist/server/modules/relay-tunnel/relay-tunnel-edge-proof.d.ts +9 -0
  111. package/dist/server/modules/relay-tunnel/relay-tunnel-edge-proof.js +25 -0
  112. package/dist/server/modules/relay-tunnel/relay-tunnel-edge-proof.js.map +1 -0
  113. package/dist/server/modules/relay-tunnel/relay-tunnel-gateway-service.d.ts +18 -0
  114. package/dist/server/modules/relay-tunnel/relay-tunnel-gateway-service.js +230 -0
  115. package/dist/server/modules/relay-tunnel/relay-tunnel-gateway-service.js.map +1 -0
  116. package/dist/server/modules/relay-tunnel/relay-tunnel-runtime-adapter.d.ts +41 -0
  117. package/dist/server/modules/relay-tunnel/relay-tunnel-runtime-adapter.js +443 -0
  118. package/dist/server/modules/relay-tunnel/relay-tunnel-runtime-adapter.js.map +1 -0
  119. package/dist/server/modules/relay-tunnel/relay-tunnel-service.d.ts +111 -0
  120. package/dist/server/modules/relay-tunnel/relay-tunnel-service.js +771 -0
  121. package/dist/server/modules/relay-tunnel/relay-tunnel-service.js.map +1 -0
  122. package/dist/server/modules/sessions/codex-app-server-helper-client.d.ts +2 -1
  123. package/dist/server/modules/sessions/codex-app-server-helper-client.js +78 -0
  124. package/dist/server/modules/sessions/codex-app-server-helper-client.js.map +1 -1
  125. package/dist/server/modules/sessions/codex-app-server-helper-process.js +84 -2
  126. package/dist/server/modules/sessions/codex-app-server-helper-process.js.map +1 -1
  127. package/dist/server/modules/sessions/provider-session-delete-cli.d.ts +15 -0
  128. package/dist/server/modules/sessions/provider-session-delete-cli.js +148 -0
  129. package/dist/server/modules/sessions/provider-session-delete-cli.js.map +1 -0
  130. package/dist/server/modules/sessions/session-controller.d.ts +4 -1
  131. package/dist/server/modules/sessions/session-controller.js +4 -0
  132. package/dist/server/modules/sessions/session-controller.js.map +1 -1
  133. package/dist/server/modules/sessions/session-history-service.d.ts +17 -0
  134. package/dist/server/modules/sessions/session-history-service.js +150 -1
  135. package/dist/server/modules/sessions/session-history-service.js.map +1 -1
  136. package/dist/server/modules/sessions/session-live-runtime-router-service.d.ts +25 -0
  137. package/dist/server/modules/sessions/session-live-runtime-router-service.js +42 -0
  138. package/dist/server/modules/sessions/session-live-runtime-router-service.js.map +1 -0
  139. package/dist/server/modules/sessions/session-live-runtime-service.js +34 -18
  140. package/dist/server/modules/sessions/session-live-runtime-service.js.map +1 -1
  141. package/dist/server/modules/sessions/session-message-attachment-service.d.ts +1 -0
  142. package/dist/server/modules/sessions/session-message-attachment-service.js +22 -0
  143. package/dist/server/modules/sessions/session-message-attachment-service.js.map +1 -1
  144. package/dist/server/modules/sessions/session-permission-request-service.d.ts +1 -0
  145. package/dist/server/modules/sessions/session-permission-request-service.js +200 -5
  146. package/dist/server/modules/sessions/session-permission-request-service.js.map +1 -1
  147. package/dist/server/modules/sessions/session-provider-error-mapper.js +32 -0
  148. package/dist/server/modules/sessions/session-provider-error-mapper.js.map +1 -1
  149. package/dist/server/modules/sessions/session-provider-usage-guard-service.d.ts +37 -0
  150. package/dist/server/modules/sessions/session-provider-usage-guard-service.js +179 -0
  151. package/dist/server/modules/sessions/session-provider-usage-guard-service.js.map +1 -0
  152. package/dist/server/modules/sessions/session-provider-usage-limit.d.ts +17 -0
  153. package/dist/server/modules/sessions/session-provider-usage-limit.js +465 -0
  154. package/dist/server/modules/sessions/session-provider-usage-limit.js.map +1 -0
  155. package/dist/server/modules/skills/assistant-runtime-skill-catalog.d.ts +8 -0
  156. package/dist/server/modules/skills/assistant-runtime-skill-catalog.js +26 -0
  157. package/dist/server/modules/skills/assistant-runtime-skill-catalog.js.map +1 -0
  158. package/dist/server/modules/skills/assistant-runtime-skill-cleanup.d.ts +9 -0
  159. package/dist/server/modules/skills/assistant-runtime-skill-cleanup.js +55 -0
  160. package/dist/server/modules/skills/assistant-runtime-skill-cleanup.js.map +1 -0
  161. package/dist/server/modules/skills/builtin-skill-service.js +1 -6
  162. package/dist/server/modules/skills/builtin-skill-service.js.map +1 -1
  163. package/dist/server/modules/skills/skill-controller.d.ts +2 -2
  164. package/dist/server/modules/skills/skill-controller.js +9 -1
  165. package/dist/server/modules/skills/skill-controller.js.map +1 -1
  166. package/dist/server/modules/skills/skill-manager-service.d.ts +26 -1
  167. package/dist/server/modules/skills/skill-manager-service.js +346 -90
  168. package/dist/server/modules/skills/skill-manager-service.js.map +1 -1
  169. package/dist/server/modules/skills/skill-name-policy.d.ts +2 -0
  170. package/dist/server/modules/skills/skill-name-policy.js +10 -0
  171. package/dist/server/modules/skills/skill-name-policy.js.map +1 -0
  172. package/dist/server/modules/tailscale/tailscale-service.d.ts +2 -0
  173. package/dist/server/modules/tailscale/tailscale-service.js +21 -8
  174. package/dist/server/modules/tailscale/tailscale-service.js.map +1 -1
  175. package/dist/server/modules/tasks/task-types.d.ts +3 -0
  176. package/dist/server/modules/tasks/task-types.js +3 -0
  177. package/dist/server/modules/tasks/task-types.js.map +1 -1
  178. package/dist/server/modules/terminal/template-reverse-proxy-service.js +71 -3
  179. package/dist/server/modules/terminal/template-reverse-proxy-service.js.map +1 -1
  180. package/dist/server/routes/assistant.js +30 -0
  181. package/dist/server/routes/assistant.js.map +1 -1
  182. package/dist/server/routes/auth.js +4 -0
  183. package/dist/server/routes/auth.js.map +1 -1
  184. package/dist/server/routes/sessions.js +1 -0
  185. package/dist/server/routes/sessions.js.map +1 -1
  186. package/dist/server/routes/system.d.ts +2 -1
  187. package/dist/server/routes/system.js +13 -1
  188. package/dist/server/routes/system.js.map +1 -1
  189. package/dist/server/server/create-server.d.ts +10 -0
  190. package/dist/server/server/create-server.js +82 -12
  191. package/dist/server/server/create-server.js.map +1 -1
  192. package/dist/server/shared/utils/tokens.d.ts +3 -1
  193. package/dist/server/shared/utils/tokens.js +9 -2
  194. package/dist/server/shared/utils/tokens.js.map +1 -1
  195. package/dist/server/storage/repositories/assistant-automation-task-repository.d.ts +2 -0
  196. package/dist/server/storage/repositories/assistant-automation-task-repository.js +8 -2
  197. package/dist/server/storage/repositories/assistant-automation-task-repository.js.map +1 -1
  198. package/dist/server/storage/repositories/assistant-sandbox-workspace-repository.d.ts +1 -0
  199. package/dist/server/storage/repositories/assistant-sandbox-workspace-repository.js +27 -0
  200. package/dist/server/storage/repositories/assistant-sandbox-workspace-repository.js.map +1 -1
  201. package/dist/server/storage/repositories/auth-device-repository.d.ts +22 -0
  202. package/dist/server/storage/repositories/auth-device-repository.js +97 -0
  203. package/dist/server/storage/repositories/auth-device-repository.js.map +1 -0
  204. package/dist/server/storage/repositories/auth-device-session-repository.d.ts +17 -0
  205. package/dist/server/storage/repositories/auth-device-session-repository.js +82 -0
  206. package/dist/server/storage/repositories/auth-device-session-repository.js.map +1 -0
  207. package/dist/server/storage/repositories/auth-login-event-repository.d.ts +9 -0
  208. package/dist/server/storage/repositories/auth-login-event-repository.js +53 -0
  209. package/dist/server/storage/repositories/auth-login-event-repository.js.map +1 -0
  210. package/dist/server/storage/repositories/auth-token-repository.d.ts +4 -0
  211. package/dist/server/storage/repositories/auth-token-repository.js +58 -5
  212. package/dist/server/storage/repositories/auth-token-repository.js.map +1 -1
  213. package/dist/server/storage/repositories/butler-follow-up-task-repository.js +21 -3
  214. package/dist/server/storage/repositories/butler-follow-up-task-repository.js.map +1 -1
  215. package/dist/server/storage/repositories/instance-relay-tunnel-identity-repository.d.ts +8 -0
  216. package/dist/server/storage/repositories/instance-relay-tunnel-identity-repository.js +52 -0
  217. package/dist/server/storage/repositories/instance-relay-tunnel-identity-repository.js.map +1 -0
  218. package/dist/server/storage/repositories/instance-relay-tunnel-repository.d.ts +10 -0
  219. package/dist/server/storage/repositories/instance-relay-tunnel-repository.js +153 -0
  220. package/dist/server/storage/repositories/instance-relay-tunnel-repository.js.map +1 -0
  221. package/dist/server/storage/repositories/instance-tailscale-repository.js +6 -3
  222. package/dist/server/storage/repositories/instance-tailscale-repository.js.map +1 -1
  223. package/dist/server/storage/repositories/managed-skill-repository.d.ts +2 -1
  224. package/dist/server/storage/repositories/managed-skill-repository.js +14 -4
  225. package/dist/server/storage/repositories/managed-skill-repository.js.map +1 -1
  226. package/dist/server/storage/repositories/session-message-attachment-repository.d.ts +2 -0
  227. package/dist/server/storage/repositories/session-message-attachment-repository.js +24 -0
  228. package/dist/server/storage/repositories/session-message-attachment-repository.js.map +1 -1
  229. package/dist/server/storage/sqlite/client.js +297 -2
  230. package/dist/server/storage/sqlite/client.js.map +1 -1
  231. package/dist/server/storage/sqlite/schema.sql +122 -4
  232. package/dist/server/types/domain.d.ts +82 -1
  233. package/dist/server/ws/workbench-ws-hub.js +99 -75
  234. package/dist/server/ws/workbench-ws-hub.js.map +1 -1
  235. package/dist/server/ws/ws-auth-guard.js +1 -4
  236. package/dist/server/ws/ws-auth-guard.js.map +1 -1
  237. package/dist/server/ws/ws-server.d.ts +1 -1
  238. package/dist/server/ws/ws-server.js.map +1 -1
  239. package/node_modules/@codingns/session-sync-core/dist/codex-resume-history.d.ts +1 -0
  240. package/node_modules/@codingns/session-sync-core/dist/codex-resume-history.js +80 -0
  241. package/node_modules/@codingns/session-sync-core/dist/codex-resume-history.js.map +1 -0
  242. package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.d.ts +1 -0
  243. package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js +11 -1
  244. package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js.map +1 -1
  245. package/node_modules/@codingns/session-sync-core/dist/providers/codex.d.ts +11 -0
  246. package/node_modules/@codingns/session-sync-core/dist/providers/codex.js +132 -21
  247. package/node_modules/@codingns/session-sync-core/dist/providers/codex.js.map +1 -1
  248. package/node_modules/@codingns/session-sync-core/dist/providers/gemini.d.ts +2 -0
  249. package/node_modules/@codingns/session-sync-core/dist/providers/gemini.js +53 -1
  250. package/node_modules/@codingns/session-sync-core/dist/providers/gemini.js.map +1 -1
  251. package/node_modules/@codingns/session-sync-core/dist/providers/kimi.d.ts +1 -0
  252. package/node_modules/@codingns/session-sync-core/dist/providers/kimi.js +10 -1
  253. package/node_modules/@codingns/session-sync-core/dist/providers/kimi.js.map +1 -1
  254. package/node_modules/@codingns/session-sync-core/dist/providers/opencode.d.ts +1 -0
  255. package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js +30 -0
  256. package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js.map +1 -1
  257. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.d.ts +5 -1
  258. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js +145 -58
  259. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js.map +1 -1
  260. package/node_modules/@codingns/session-sync-core/dist/services.d.ts +1 -0
  261. package/node_modules/@codingns/session-sync-core/dist/services.js +7 -0
  262. package/node_modules/@codingns/session-sync-core/dist/services.js.map +1 -1
  263. package/node_modules/@codingns/session-sync-core/dist/types.d.ts +2 -0
  264. package/package.json +1 -1
  265. package/scripts/postinstall.mjs +0 -33
  266. package/dist/public/assets/index-CSVhg7I8.js +0 -123
  267. package/dist/public/assets/index-Ce1VX19m.css +0 -1
@@ -10,7 +10,12 @@ export class SkillController {
10
10
  };
11
11
  add = async (request, reply) => {
12
12
  requireUserId(request);
13
- reply.send(this.skillManagerService.addManagedSkill(request.body ?? { sourcePath: "", targetCli: [], sourceType: "local-import" }));
13
+ const body = request.body;
14
+ if (isMarkdownSkillInput(body)) {
15
+ reply.send(this.skillManagerService.addManagedSkillFromMarkdown(body));
16
+ return;
17
+ }
18
+ reply.send(this.skillManagerService.addManagedSkill(body ?? { sourcePath: "", targetCli: [], sourceType: "local-import" }));
14
19
  };
15
20
  import = async (request, reply) => {
16
21
  requireUserId(request);
@@ -21,6 +26,9 @@ export class SkillController {
21
26
  reply.send(this.skillManagerService.syncManagedSkill(request.body ?? { skillId: "", targetCli: [] }));
22
27
  };
23
28
  }
29
+ function isMarkdownSkillInput(input) {
30
+ return Boolean(input && "markdownContent" in input && typeof input.markdownContent === "string");
31
+ }
24
32
  function normalizeTargetCliQuery(query) {
25
33
  if (!query?.targetCli) {
26
34
  return {};
@@ -1 +1 @@
1
- {"version":3,"file":"skill-controller.js","sourceRoot":"","sources":["../../../../src/modules/skills/skill-controller.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAazD,MAAM,OAAO,eAAe;IACG;IAA7B,YAA6B,mBAAwC;QAAxC,wBAAmB,GAAnB,mBAAmB,CAAqB;IAAG,CAAC;IAEhE,WAAW,GAAG,KAAK,EAC1B,OAA4D,EAC5D,KAAmB,EACJ,EAAE;QACjB,aAAa,CAAC,OAAO,CAAC,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,WAAW,CAAC,uBAAuB,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC3F,CAAC,CAAC;IAEO,GAAG,GAAG,KAAK,EAClB,OAAuD,EACvD,KAAmB,EACJ,EAAE;QACjB,aAAa,CAAC,OAAO,CAAC,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,eAAe,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,UAAU,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC;IACtI,CAAC,CAAC;IAEO,MAAM,GAAG,KAAK,EACrB,OAA4D,EAC5D,KAAmB,EACJ,EAAE;QACjB,aAAa,CAAC,OAAO,CAAC,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,oBAAoB,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,aAAa,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;IACvH,CAAC,CAAC;IAEO,IAAI,GAAG,KAAK,EACnB,OAAwD,EACxD,KAAmB,EACJ,EAAE;QACjB,aAAa,CAAC,OAAO,CAAC,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;IACxG,CAAC,CAAC;CACH;AAED,SAAS,uBAAuB,CAAC,KAAqC;IACpE,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,CAAC;QACtB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC;QAC9C,CAAC,CAAC,KAAK,CAAC,SAAS;QACjB,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAE1E,OAAO;QACL,SAAS,EAAE,SAA2C;KACvD,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"skill-controller.js","sourceRoot":"","sources":["../../../../src/modules/skills/skill-controller.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAczD,MAAM,OAAO,eAAe;IACG;IAA7B,YAA6B,mBAAwC;QAAxC,wBAAmB,GAAnB,mBAAmB,CAAqB;IAAG,CAAC;IAEhE,WAAW,GAAG,KAAK,EAC1B,OAA4D,EAC5D,KAAmB,EACJ,EAAE;QACjB,aAAa,CAAC,OAAO,CAAC,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,WAAW,CAAC,uBAAuB,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC3F,CAAC,CAAC;IAEO,GAAG,GAAG,KAAK,EAClB,OAA0F,EAC1F,KAAmB,EACJ,EAAE;QACjB,aAAa,CAAC,OAAO,CAAC,CAAC;QACvB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QAE1B,IAAI,oBAAoB,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,2BAA2B,CAAC,IAAI,CAAC,CAAC,CAAC;YACvE,OAAO;QACT,CAAC;QAED,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,eAAe,CAAC,IAAI,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,UAAU,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC;IAC9H,CAAC,CAAC;IAEO,MAAM,GAAG,KAAK,EACrB,OAA4D,EAC5D,KAAmB,EACJ,EAAE;QACjB,aAAa,CAAC,OAAO,CAAC,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,oBAAoB,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,aAAa,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;IACvH,CAAC,CAAC;IAEO,IAAI,GAAG,KAAK,EACnB,OAAwD,EACxD,KAAmB,EACJ,EAAE;QACjB,aAAa,CAAC,OAAO,CAAC,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;IACxG,CAAC,CAAC;CACH;AAED,SAAS,oBAAoB,CAC3B,KAA0E;IAE1E,OAAO,OAAO,CAAC,KAAK,IAAI,iBAAiB,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,eAAe,KAAK,QAAQ,CAAC,CAAC;AACnG,CAAC;AAED,SAAS,uBAAuB,CAAC,KAAqC;IACpE,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,CAAC;QACtB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC;QAC9C,CAAC,CAAC,KAAK,CAAC,SAAS;QACjB,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAE1E,OAAO;QACL,SAAS,EAAE,SAA2C;KACvD,CAAC;AACJ,CAAC"}
@@ -1,4 +1,4 @@
1
- import type { ManagedSkillRecord, SkillSourceType, SkillScanResult, SkillTargetBindingRecord, SkillTargetCli, SkillTargetSyncStatus } from "../../types/domain.js";
1
+ import type { ManagedSkillRecord, SkillScope, SkillSourceType, SkillScanResult, SkillTargetBindingRecord, SkillTargetCli, SkillTargetSyncStatus } from "../../types/domain.js";
2
2
  import type { ManagedSkillRepository } from "../../storage/repositories/managed-skill-repository.js";
3
3
  import type { SkillTargetBindingRepository } from "../../storage/repositories/skill-target-binding-repository.js";
4
4
  import { type SkillTargetAdapter } from "./skill-target-adapter.js";
@@ -11,6 +11,14 @@ export interface AddManagedSkillInput {
11
11
  sourcePath: string;
12
12
  targetCli: readonly SkillTargetCli[];
13
13
  sourceType: SkillSourceType;
14
+ scope?: SkillScope;
15
+ }
16
+ export interface AddManagedSkillFromMarkdownInput {
17
+ markdownContent: string;
18
+ targetCli: readonly SkillTargetCli[];
19
+ fileName?: string | null;
20
+ directoryName?: string | null;
21
+ scope?: SkillScope;
14
22
  }
15
23
  export interface SyncManagedSkillInput {
16
24
  skillId: string;
@@ -45,6 +53,18 @@ export interface ManagedSkillOverviewItem {
45
53
  bindings: SkillTargetBindingRecord[];
46
54
  ssotPath: string;
47
55
  }
56
+ export interface AssistantRuntimeSkillOverviewItem {
57
+ name: string;
58
+ directoryName: string;
59
+ sourcePath: string;
60
+ usedByTargetCli: readonly SkillTargetCli[];
61
+ }
62
+ export interface AssistantRuntimeSkillSource {
63
+ name: string;
64
+ directoryName: string;
65
+ sourcePath: string;
66
+ usedByTargetCli: readonly SkillTargetCli[];
67
+ }
48
68
  export interface SkillOverviewResult {
49
69
  summary: {
50
70
  managedSkillCount: number;
@@ -54,6 +74,7 @@ export interface SkillOverviewResult {
54
74
  diagnosticCount: number;
55
75
  };
56
76
  managedSkills: ManagedSkillOverviewItem[];
77
+ assistantRuntimeSkills: AssistantRuntimeSkillOverviewItem[];
57
78
  managedEntries: SkillScanResult["managed"];
58
79
  unmanagedEntries: SkillScanResult["unmanaged"];
59
80
  conflictedEntries: SkillScanResult["conflicted"];
@@ -76,7 +97,9 @@ export declare class SkillManagerService {
76
97
  scanSkills(options?: ScanSkillsOptions): SkillScanResult;
77
98
  getOverview(options?: ScanSkillsOptions): SkillOverviewResult;
78
99
  addManagedSkill(input: AddManagedSkillInput): ManagedSkillMutationResult;
100
+ addManagedSkillFromMarkdown(input: AddManagedSkillFromMarkdownInput): ManagedSkillMutationResult;
79
101
  syncManagedSkill(input: SyncManagedSkillInput): ManagedSkillMutationResult;
102
+ listAssistantRuntimeSkillSources(targetCli?: readonly SkillTargetCli[]): AssistantRuntimeSkillSource[];
80
103
  ensureBuiltinSkill(input: EnsureBuiltinSkillInput): ManagedSkillMutationResult;
81
104
  importUnmanagedSkill(input: ImportUnmanagedSkillInput): ManagedSkillMutationResult;
82
105
  private syncManagedSkillRecord;
@@ -87,6 +110,8 @@ export declare class SkillManagerService {
87
110
  private resolveSsotPath;
88
111
  private createManagedSkillId;
89
112
  private now;
113
+ private registerManagedSkillFromSource;
114
+ private upsertAssistantRuntimeBindings;
90
115
  }
91
116
  export declare function readSkillDirectorySnapshot(targetCli: SkillTargetCli, rootDir: string, directoryPath: string): DiscoveredSkillDirectory;
92
117
  export declare function computeSkillDirectoryHash(directoryPath: string): string;
@@ -1,4 +1,5 @@
1
1
  import fs from "node:fs";
2
+ import os from "node:os";
2
3
  import path from "node:path";
3
4
  import { AppError } from "../../shared/errors/app-error.js";
4
5
  import { createId } from "../../shared/utils/id.js";
@@ -7,6 +8,10 @@ import { nowIso } from "../../shared/utils/time.js";
7
8
  import { resolveSkillTargetLocation } from "./skill-target-adapter.js";
8
9
  import { SkillReconciler } from "./skill-reconciler.js";
9
10
  import { SkillSyncPlanner } from "./skill-sync-planner.js";
11
+ import { getReservedAssistantSkillErrorDetail, isReservedAssistantSkillDirectoryName } from "./skill-name-policy.js";
12
+ import { listAssistantRuntimeSkills } from "./assistant-runtime-skill-catalog.js";
13
+ const ASSISTANT_RUNTIME_TARGETS = ["codex", "claude-code"];
14
+ const ASSISTANT_RUNTIME_SSOT_DIRNAME = ".assistant-runtime";
10
15
  export class SkillManagerService {
11
16
  managedSkillRepository;
12
17
  skillTargetBindingRepository;
@@ -31,24 +36,43 @@ export class SkillManagerService {
31
36
  discoveredDirectories.push(...scanResult.directories);
32
37
  diagnostics.push(...scanResult.diagnostics);
33
38
  }
34
- const managedSkills = this.managedSkillRepository.list();
35
- const bindings = managedSkills.flatMap((skill) => this.skillTargetBindingRepository.listBySkillId(skill.id));
36
- return this.skillReconciler.reconcile({
39
+ const publicManagedSkills = this.managedSkillRepository
40
+ .list()
41
+ .filter((skill) => skill.scope === "workspace")
42
+ .filter((skill) => !isReservedAssistantSkillDirectoryName(skill.directoryName));
43
+ const bindings = publicManagedSkills.flatMap((skill) => this.skillTargetBindingRepository.listBySkillId(skill.id));
44
+ const reservedDirectories = discoveredDirectories.filter((directory) => isReservedAssistantSkillDirectoryName(directory.directoryName));
45
+ const publicDirectories = discoveredDirectories.filter((directory) => !isReservedAssistantSkillDirectoryName(directory.directoryName));
46
+ const result = this.skillReconciler.reconcile({
37
47
  targetLocations,
38
- directories: discoveredDirectories,
39
- managedSkills,
48
+ directories: publicDirectories,
49
+ managedSkills: publicManagedSkills,
40
50
  bindings,
41
51
  diagnostics,
42
52
  scannedAt: nowIso()
43
53
  });
54
+ const reservedDiagnostics = reservedDirectories.map((directory) => createReservedSkillDiagnostic(directory));
55
+ const reservedEntries = reservedDirectories.map((directory) => createReservedSkillScanEntry(directory));
56
+ return {
57
+ managed: result.managed,
58
+ unmanaged: result.unmanaged,
59
+ conflicted: sortSkillScanEntries([...result.conflicted, ...reservedEntries]),
60
+ diagnostics: sortSkillScanDiagnostics([...result.diagnostics, ...reservedDiagnostics]),
61
+ scannedAt: result.scannedAt
62
+ };
44
63
  }
45
64
  getOverview(options = {}) {
46
65
  const scanResult = this.scanSkills(options);
47
- const managedSkills = this.managedSkillRepository.list().map((skill) => ({
66
+ const managedSkills = this.managedSkillRepository
67
+ .list()
68
+ .filter((skill) => skill.scope === "workspace")
69
+ .filter((skill) => !isReservedAssistantSkillDirectoryName(skill.directoryName))
70
+ .map((skill) => ({
48
71
  skill,
49
72
  bindings: this.skillTargetBindingRepository.listBySkillId(skill.id),
50
- ssotPath: this.resolveSsotPath(skill.directoryName)
73
+ ssotPath: this.resolveSsotPath(skill.scope, skill.directoryName)
51
74
  }));
75
+ const assistantRuntimeSkills = this.listAssistantRuntimeSkillSources();
52
76
  return {
53
77
  summary: {
54
78
  managedSkillCount: managedSkills.length,
@@ -58,6 +82,7 @@ export class SkillManagerService {
58
82
  diagnosticCount: scanResult.diagnostics.length
59
83
  },
60
84
  managedSkills,
85
+ assistantRuntimeSkills,
61
86
  managedEntries: scanResult.managed,
62
87
  unmanagedEntries: scanResult.unmanaged,
63
88
  conflictedEntries: scanResult.conflicted,
@@ -68,52 +93,37 @@ export class SkillManagerService {
68
93
  addManagedSkill(input) {
69
94
  const sourcePath = resolveSourceDirectoryPath(input.sourcePath);
70
95
  const sourceSnapshot = readSkillDirectorySnapshot("codex", sourcePath, sourcePath);
71
- const existingSkill = this.managedSkillRepository.findByDirectoryName(sourceSnapshot.directoryName);
72
- const existingBindings = existingSkill
73
- ? this.skillTargetBindingRepository.listBySkillId(existingSkill.id)
74
- : [];
75
- const plannedTargets = this.skillSyncPlanner.planRequestedTargets(input.targetCli, existingBindings);
76
- const ssotRootDir = this.requireSsotRootDir();
77
- const timestamp = this.now();
78
- if (existingSkill && existingSkill.contentHash !== sourceSnapshot.contentHash) {
79
- throw new AppError({
80
- statusCode: 409,
81
- errorCode: "SKILL_NAME_CONFLICT",
82
- detail: "已存在同名受管 skill,且内容与当前来源目录不一致",
83
- field: "sourcePath"
96
+ return this.registerManagedSkillFromSource({
97
+ scope: input.scope ?? "workspace",
98
+ sourceSnapshot,
99
+ copySourcePath: sourcePath,
100
+ sourceType: input.sourceType,
101
+ sourcePath,
102
+ targetCli: input.targetCli,
103
+ field: "sourcePath"
104
+ });
105
+ }
106
+ addManagedSkillFromMarkdown(input) {
107
+ const prepared = prepareUploadedSkillMarkdown(input);
108
+ const tempRootDir = fs.mkdtempSync(path.join(os.tmpdir(), "codingns-skill-upload-"));
109
+ const sourcePath = path.join(tempRootDir, prepared.directoryName);
110
+ fs.mkdirSync(sourcePath, { recursive: true });
111
+ fs.writeFileSync(path.join(sourcePath, "SKILL.md"), prepared.skillMarkdown, "utf8");
112
+ try {
113
+ const sourceSnapshot = readSkillDirectorySnapshot("codex", sourcePath, sourcePath);
114
+ return this.registerManagedSkillFromSource({
115
+ scope: input.scope ?? "workspace",
116
+ sourceSnapshot,
117
+ copySourcePath: sourcePath,
118
+ sourceType: "local-import",
119
+ sourcePath: null,
120
+ targetCli: input.targetCli,
121
+ field: "markdownContent"
84
122
  });
85
123
  }
86
- const skill = existingSkill
87
- ? {
88
- ...existingSkill,
89
- name: sourceSnapshot.name,
90
- sourceType: input.sourceType,
91
- sourcePath,
92
- contentHash: sourceSnapshot.contentHash,
93
- managedState: "active",
94
- updatedAt: timestamp
95
- }
96
- : {
97
- id: this.createManagedSkillId(),
98
- name: sourceSnapshot.name,
99
- directoryName: sourceSnapshot.directoryName,
100
- sourceType: input.sourceType,
101
- sourcePath,
102
- contentHash: sourceSnapshot.contentHash,
103
- managedState: "active",
104
- createdAt: timestamp,
105
- updatedAt: timestamp
106
- };
107
- const ssotPath = path.join(ssotRootDir, skill.directoryName);
108
- copySkillDirectory(sourcePath, ssotPath);
109
- this.managedSkillRepository.upsert(skill);
110
- const targetResults = this.syncManagedSkillRecord(skill, plannedTargets.map((target) => target.targetCli));
111
- return {
112
- skill,
113
- bindings: this.skillTargetBindingRepository.listBySkillId(skill.id),
114
- targetResults,
115
- ssotPath
116
- };
124
+ finally {
125
+ fs.rmSync(tempRootDir, { recursive: true, force: true });
126
+ }
117
127
  }
118
128
  syncManagedSkill(input) {
119
129
  const skillId = input.skillId.trim();
@@ -134,6 +144,15 @@ export class SkillManagerService {
134
144
  field: "skillId"
135
145
  });
136
146
  }
147
+ if (skill.scope !== "workspace") {
148
+ throw new AppError({
149
+ statusCode: 409,
150
+ errorCode: "SKILL_SCOPE_NOT_SYNCABLE",
151
+ detail: "助手专用 skill 不参与工作区同步",
152
+ field: "skillId"
153
+ });
154
+ }
155
+ assertSkillDirectoryNameAllowed(skill.scope, skill.directoryName, "skillId");
137
156
  const existingBindings = this.skillTargetBindingRepository.listBySkillId(skill.id);
138
157
  const plannedTargets = this.skillSyncPlanner.planRequestedTargets(input.targetCli, existingBindings);
139
158
  const targetResults = this.syncManagedSkillRecord(skill, plannedTargets.map((target) => target.targetCli));
@@ -141,46 +160,52 @@ export class SkillManagerService {
141
160
  skill: this.managedSkillRepository.findById(skill.id) ?? skill,
142
161
  bindings: this.skillTargetBindingRepository.listBySkillId(skill.id),
143
162
  targetResults,
144
- ssotPath: this.resolveSsotPath(skill.directoryName)
163
+ ssotPath: this.resolveSsotPath(skill.scope, skill.directoryName)
145
164
  };
146
165
  }
166
+ listAssistantRuntimeSkillSources(targetCli) {
167
+ const requestedTargets = targetCli?.length ? new Set(targetCli) : null;
168
+ const builtinSkills = listAssistantRuntimeSkills()
169
+ .filter((item) => matchesRequestedTargets(item.usedByTargetCli, requestedTargets))
170
+ .map((item) => ({
171
+ name: item.name,
172
+ directoryName: item.directoryName,
173
+ sourcePath: item.sourcePath,
174
+ usedByTargetCli: [...item.usedByTargetCli]
175
+ }));
176
+ const managedSkills = this.managedSkillRepository
177
+ .list()
178
+ .filter((skill) => skill.scope === "assistant")
179
+ .map((skill) => ({
180
+ skill,
181
+ bindings: this.skillTargetBindingRepository
182
+ .listBySkillId(skill.id)
183
+ .filter((binding) => binding.enabled && isAssistantRuntimeTarget(binding.targetCli))
184
+ }))
185
+ .filter((item) => item.bindings.length > 0)
186
+ .map((item) => ({
187
+ name: item.skill.name,
188
+ directoryName: item.skill.directoryName,
189
+ sourcePath: this.resolveSsotPath(item.skill.scope, item.skill.directoryName),
190
+ usedByTargetCli: item.bindings.map((binding) => binding.targetCli)
191
+ }))
192
+ .filter((item) => matchesRequestedTargets(item.usedByTargetCli, requestedTargets))
193
+ .filter((item) => isValidSkillDirectory(item.sourcePath));
194
+ return [...builtinSkills, ...managedSkills].sort((left, right) => left.directoryName.localeCompare(right.directoryName));
195
+ }
147
196
  ensureBuiltinSkill(input) {
148
197
  const sourcePath = resolveSourceDirectoryPath(input.sourcePath);
149
198
  const sourceSnapshot = readSkillDirectorySnapshot("codex", sourcePath, sourcePath);
150
- const existingSkill = this.managedSkillRepository.findByDirectoryName(sourceSnapshot.directoryName);
151
- const ssotRootDir = this.requireSsotRootDir();
152
- const timestamp = this.now();
153
- const skill = existingSkill
154
- ? {
155
- ...existingSkill,
156
- name: sourceSnapshot.name,
157
- sourceType: "builtin",
158
- sourcePath,
159
- contentHash: sourceSnapshot.contentHash,
160
- managedState: "active",
161
- updatedAt: timestamp
162
- }
163
- : {
164
- id: this.createManagedSkillId(),
165
- name: sourceSnapshot.name,
166
- directoryName: sourceSnapshot.directoryName,
167
- sourceType: "builtin",
168
- sourcePath,
169
- contentHash: sourceSnapshot.contentHash,
170
- managedState: "active",
171
- createdAt: timestamp,
172
- updatedAt: timestamp
173
- };
174
- const ssotPath = path.join(ssotRootDir, skill.directoryName);
175
- copySkillDirectory(sourcePath, ssotPath);
176
- this.managedSkillRepository.upsert(skill);
177
- const targetResults = this.forceSyncManagedSkillRecord(skill, input.targetCli);
178
- return {
179
- skill: this.managedSkillRepository.findById(skill.id) ?? skill,
180
- bindings: this.skillTargetBindingRepository.listBySkillId(skill.id),
181
- targetResults,
182
- ssotPath
183
- };
199
+ return this.registerManagedSkillFromSource({
200
+ scope: "workspace",
201
+ sourceSnapshot,
202
+ copySourcePath: sourcePath,
203
+ sourceType: "builtin",
204
+ sourcePath,
205
+ targetCli: input.targetCli,
206
+ field: "sourcePath",
207
+ forceSync: true
208
+ });
184
209
  }
185
210
  importUnmanagedSkill(input) {
186
211
  const sourceLocation = resolveSingleTargetLocation(this.targetAdapters, input.targetCli);
@@ -202,6 +227,7 @@ export class SkillManagerService {
202
227
  });
203
228
  }
204
229
  const sourceSnapshot = readSkillDirectorySnapshot(input.targetCli, sourceLocation.rootDir, directoryPath);
230
+ assertSkillDirectoryNameAllowed("workspace", sourceSnapshot.directoryName, "directoryPath");
205
231
  if (input.expectedContentHash
206
232
  && input.expectedContentHash.trim()
207
233
  && input.expectedContentHash.trim() !== sourceSnapshot.contentHash) {
@@ -215,11 +241,12 @@ export class SkillManagerService {
215
241
  return this.addManagedSkill({
216
242
  sourcePath: directoryPath,
217
243
  targetCli: [input.targetCli, ...(input.additionalTargetCli ?? [])],
218
- sourceType: "managed-copy"
244
+ sourceType: "managed-copy",
245
+ scope: "workspace"
219
246
  });
220
247
  }
221
248
  syncManagedSkillRecord(skill, targetCli) {
222
- const ssotPath = this.resolveSsotPath(skill.directoryName);
249
+ const ssotPath = this.resolveSsotPath(skill.scope, skill.directoryName);
223
250
  const timestamp = this.now();
224
251
  if (!isValidSkillDirectory(ssotPath)) {
225
252
  this.managedSkillRepository.upsert({
@@ -237,7 +264,7 @@ export class SkillManagerService {
237
264
  return plannedTargets.map((target) => this.syncOneTarget(skill, ssotPath, target.targetCli, timestamp));
238
265
  }
239
266
  forceSyncManagedSkillRecord(skill, targetCli) {
240
- const ssotPath = this.resolveSsotPath(skill.directoryName);
267
+ const ssotPath = this.resolveSsotPath(skill.scope, skill.directoryName);
241
268
  const timestamp = this.now();
242
269
  if (!isValidSkillDirectory(ssotPath)) {
243
270
  this.managedSkillRepository.upsert({
@@ -368,7 +395,10 @@ export class SkillManagerService {
368
395
  }
369
396
  return path.resolve(ssotRootDir);
370
397
  }
371
- resolveSsotPath(directoryName) {
398
+ resolveSsotPath(scope, directoryName) {
399
+ if (scope === "assistant") {
400
+ return path.join(this.requireSsotRootDir(), ASSISTANT_RUNTIME_SSOT_DIRNAME, directoryName);
401
+ }
372
402
  return path.join(this.requireSsotRootDir(), directoryName);
373
403
  }
374
404
  createManagedSkillId() {
@@ -377,6 +407,232 @@ export class SkillManagerService {
377
407
  now() {
378
408
  return this.options.now?.() ?? nowIso();
379
409
  }
410
+ registerManagedSkillFromSource(input) {
411
+ assertSkillDirectoryNameAllowed(input.scope, input.sourceSnapshot.directoryName, input.field);
412
+ const existingSkill = this.managedSkillRepository.findByScopeAndDirectoryName(input.scope, input.sourceSnapshot.directoryName);
413
+ const existingBindings = existingSkill
414
+ ? this.skillTargetBindingRepository.listBySkillId(existingSkill.id)
415
+ : [];
416
+ const timestamp = this.now();
417
+ if (existingSkill && existingSkill.contentHash !== input.sourceSnapshot.contentHash) {
418
+ throw new AppError({
419
+ statusCode: 409,
420
+ errorCode: "SKILL_NAME_CONFLICT",
421
+ detail: "已存在同名受管 skill,且内容与当前来源目录不一致",
422
+ field: input.field
423
+ });
424
+ }
425
+ const skill = existingSkill
426
+ ? {
427
+ ...existingSkill,
428
+ name: input.sourceSnapshot.name,
429
+ scope: input.scope,
430
+ sourceType: input.sourceType,
431
+ sourcePath: input.sourcePath,
432
+ contentHash: input.sourceSnapshot.contentHash,
433
+ managedState: "active",
434
+ updatedAt: timestamp
435
+ }
436
+ : {
437
+ id: this.createManagedSkillId(),
438
+ name: input.sourceSnapshot.name,
439
+ scope: input.scope,
440
+ directoryName: input.sourceSnapshot.directoryName,
441
+ sourceType: input.sourceType,
442
+ sourcePath: input.sourcePath,
443
+ contentHash: input.sourceSnapshot.contentHash,
444
+ managedState: "active",
445
+ createdAt: timestamp,
446
+ updatedAt: timestamp
447
+ };
448
+ const ssotPath = this.resolveSsotPath(skill.scope, skill.directoryName);
449
+ copySkillDirectory(input.copySourcePath, ssotPath);
450
+ this.managedSkillRepository.upsert(skill);
451
+ const targetResults = skill.scope === "assistant"
452
+ ? this.upsertAssistantRuntimeBindings(skill, input.targetCli, existingBindings, timestamp, input.field)
453
+ : (() => {
454
+ const plannedTargets = this.skillSyncPlanner.planRequestedTargets(input.targetCli, existingBindings);
455
+ return input.forceSync
456
+ ? this.forceSyncManagedSkillRecord(skill, plannedTargets.map((target) => target.targetCli))
457
+ : this.syncManagedSkillRecord(skill, plannedTargets.map((target) => target.targetCli));
458
+ })();
459
+ return {
460
+ skill: this.managedSkillRepository.findById(skill.id) ?? skill,
461
+ bindings: this.skillTargetBindingRepository.listBySkillId(skill.id),
462
+ targetResults,
463
+ ssotPath
464
+ };
465
+ }
466
+ upsertAssistantRuntimeBindings(skill, targetCli, existingBindings, timestamp, field) {
467
+ const normalizedTargets = normalizeAssistantRuntimeTargets(targetCli, field);
468
+ const requestedTargets = new Set(normalizedTargets);
469
+ const knownTargets = new Set([
470
+ ...ASSISTANT_RUNTIME_TARGETS,
471
+ ...existingBindings
472
+ .map((binding) => binding.targetCli)
473
+ .filter((target) => isAssistantRuntimeTarget(target))
474
+ ]);
475
+ for (const target of knownTargets) {
476
+ const enabled = requestedTargets.has(target);
477
+ this.skillTargetBindingRepository.upsert(buildBindingRecord(skill.id, target, {
478
+ enabled,
479
+ syncStatus: enabled ? "synced" : "pending",
480
+ lastSyncedAt: enabled ? timestamp : null,
481
+ lastErrorCode: null,
482
+ lastErrorDetail: null
483
+ }));
484
+ }
485
+ return normalizedTargets.map((target) => ({
486
+ targetCli: target,
487
+ targetDir: buildAssistantRuntimeTargetRef(target, skill.directoryName),
488
+ syncStatus: "synced",
489
+ lastSyncedAt: timestamp,
490
+ errorCode: null,
491
+ errorDetail: null
492
+ }));
493
+ }
494
+ }
495
+ function assertSkillDirectoryNameAllowed(scope, directoryName, field) {
496
+ if (scope === "workspace" && !isReservedAssistantSkillDirectoryName(directoryName)) {
497
+ return;
498
+ }
499
+ if (scope === "assistant" && !isReservedAssistantSkillDirectoryName(directoryName)) {
500
+ return;
501
+ }
502
+ throw new AppError({
503
+ statusCode: 409,
504
+ errorCode: "SKILL_RESERVED_FOR_ASSISTANT_RUNTIME",
505
+ detail: getReservedAssistantSkillErrorDetail(directoryName),
506
+ field
507
+ });
508
+ }
509
+ function prepareUploadedSkillMarkdown(input) {
510
+ const normalizedContent = input.markdownContent.replace(/^\uFEFF/, "").replace(/\r\n?/g, "\n").trim();
511
+ if (!normalizedContent) {
512
+ throw new AppError({
513
+ statusCode: 400,
514
+ errorCode: "SKILL_SOURCE_INVALID",
515
+ detail: "上传内容不能为空",
516
+ field: "markdownContent"
517
+ });
518
+ }
519
+ const requestedDirectoryName = input.directoryName?.trim() ?? "";
520
+ const fallbackDirectoryName = requestedDirectoryName || input.fileName?.trim() || "";
521
+ const normalizedDirectoryName = normalizeUploadedSkillDirectoryName(fallbackDirectoryName);
522
+ const heading = normalizedContent.match(/^#\s+(.+)$/m)?.[1]?.trim() ?? "";
523
+ if (!normalizedDirectoryName) {
524
+ throw new AppError({
525
+ statusCode: 400,
526
+ errorCode: "SKILL_SOURCE_INVALID",
527
+ detail: "上传的 skill 缺少合法目录名,请补一个只包含字母、数字、点、下划线和短横线的名称",
528
+ field: "directoryName"
529
+ });
530
+ }
531
+ const skillMarkdown = heading
532
+ ? `${normalizedContent}\n`
533
+ : `# ${formatSkillTitleFromDirectoryName(normalizedDirectoryName)}\n\n${normalizedContent}\n`;
534
+ return {
535
+ directoryName: normalizedDirectoryName,
536
+ skillMarkdown
537
+ };
538
+ }
539
+ function normalizeUploadedSkillDirectoryName(input) {
540
+ const basename = input.replace(/\\/g, "/").split("/").pop() ?? input;
541
+ const withoutExtension = basename.replace(/\.[A-Za-z0-9]+$/, "");
542
+ const normalized = withoutExtension
543
+ .trim()
544
+ .replace(/\s+/g, "-")
545
+ .replace(/[^A-Za-z0-9._-]+/g, "-")
546
+ .replace(/-+/g, "-")
547
+ .replace(/^[-._]+|[-._]+$/g, "");
548
+ return normalized && isSafeSkillDirectoryName(normalized)
549
+ ? normalized
550
+ : null;
551
+ }
552
+ function formatSkillTitleFromDirectoryName(directoryName) {
553
+ const title = directoryName
554
+ .split(/[._-]+/)
555
+ .filter(Boolean)
556
+ .map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))
557
+ .join(" ");
558
+ return title || directoryName;
559
+ }
560
+ function normalizeAssistantRuntimeTargets(targetCli, field) {
561
+ const normalizedTargets = Array.from(new Set(targetCli.map((item) => item.trim())));
562
+ if (normalizedTargets.length === 0) {
563
+ throw new AppError({
564
+ statusCode: 400,
565
+ errorCode: "SKILL_TARGET_NOT_SUPPORTED",
566
+ detail: "助手专用 skill 至少要选择一个运行时目标",
567
+ field
568
+ });
569
+ }
570
+ const invalidTarget = normalizedTargets.find((target) => !isAssistantRuntimeTarget(target));
571
+ if (invalidTarget) {
572
+ throw new AppError({
573
+ statusCode: 400,
574
+ errorCode: "SKILL_TARGET_NOT_SUPPORTED",
575
+ detail: "助手专用 skill 只支持 Codex 和 Claude Code",
576
+ field
577
+ });
578
+ }
579
+ return normalizedTargets;
580
+ }
581
+ function isAssistantRuntimeTarget(targetCli) {
582
+ return ASSISTANT_RUNTIME_TARGETS.includes(targetCli);
583
+ }
584
+ function matchesRequestedTargets(targetCli, requestedTargets) {
585
+ if (!requestedTargets || requestedTargets.size === 0) {
586
+ return true;
587
+ }
588
+ return targetCli.some((target) => requestedTargets.has(target));
589
+ }
590
+ function buildAssistantRuntimeTargetRef(targetCli, directoryName) {
591
+ return `assistant-runtime:${targetCli}/${directoryName}`;
592
+ }
593
+ function createReservedSkillDiagnostic(directory) {
594
+ return {
595
+ targetCli: directory.targetCli,
596
+ rootDir: directory.rootDir,
597
+ code: "SKILL_RESERVED_FOR_ASSISTANT_RUNTIME",
598
+ detail: getReservedAssistantSkillErrorDetail(directory.directoryName),
599
+ directoryName: directory.directoryName,
600
+ directoryPath: directory.directoryPath,
601
+ managedSkillId: null
602
+ };
603
+ }
604
+ function createReservedSkillScanEntry(directory) {
605
+ return {
606
+ targetCli: directory.targetCli,
607
+ directoryPath: directory.directoryPath,
608
+ directoryName: directory.directoryName,
609
+ name: directory.name,
610
+ contentHash: directory.contentHash,
611
+ managementState: "conflicted",
612
+ managedSkillId: null
613
+ };
614
+ }
615
+ function sortSkillScanEntries(entries) {
616
+ return entries.sort((left, right) => {
617
+ const targetOrder = left.targetCli.localeCompare(right.targetCli);
618
+ if (targetOrder !== 0) {
619
+ return targetOrder;
620
+ }
621
+ return left.directoryName.localeCompare(right.directoryName);
622
+ });
623
+ }
624
+ function sortSkillScanDiagnostics(diagnostics) {
625
+ return diagnostics.sort((left, right) => {
626
+ const targetOrder = left.targetCli.localeCompare(right.targetCli);
627
+ if (targetOrder !== 0) {
628
+ return targetOrder;
629
+ }
630
+ const codeOrder = left.code.localeCompare(right.code);
631
+ if (codeOrder !== 0) {
632
+ return codeOrder;
633
+ }
634
+ return (left.directoryName ?? "").localeCompare(right.directoryName ?? "");
635
+ });
380
636
  }
381
637
  export function readSkillDirectorySnapshot(targetCli, rootDir, directoryPath) {
382
638
  const directoryName = path.basename(directoryPath);