@xopcai/xopc 0.0.283 → 0.0.285

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 (234) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/telegram/xopc.extension.json +1 -1
  3. package/dist/gateway/static/root/assets/{activity-store-iPtG94qr.js → activity-store-B0BTIk9j.js} +1 -1
  4. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-DQ74xqLi.js → agent-avatar-dicebear-Dc-0QZTQ.js} +1 -1
  5. package/dist/gateway/static/root/assets/agent-defaults-settings-panel-CcmqK1Sc.js +1 -0
  6. package/dist/gateway/static/root/assets/agents-B_7PVt7S.js +1 -0
  7. package/dist/gateway/static/root/assets/api-COG_Tzpv.js +1 -0
  8. package/dist/gateway/static/root/assets/{api-KMJzMqvZ.js → api-Coly9ab6.js} +1 -1
  9. package/dist/gateway/static/root/assets/{app-management-settings-panel-B42TpqIb.js → app-management-settings-panel-TWBO-msg.js} +1 -1
  10. package/dist/gateway/static/root/assets/{appearance-settings-D9Lfe7Sk.js → appearance-settings-CpTZ2KuT.js} +1 -1
  11. package/dist/gateway/static/root/assets/apps-page-6qxxKFsK.js +1 -0
  12. package/dist/gateway/static/root/assets/{archive-plugin-BdmQwj4x.js → archive-plugin-z_u6alob.js} +1 -1
  13. package/dist/gateway/static/root/assets/{attachment-load-B4S0_nji.js → attachment-load-C2nS04TC.js} +1 -1
  14. package/dist/gateway/static/root/assets/{automations-page-B3TmHNjQ.js → automations-page-aIBsw9zR.js} +1 -1
  15. package/dist/gateway/static/root/assets/automations-workspace-BRQlU4qk.js +6 -0
  16. package/dist/gateway/static/root/assets/{binary-plugins-B3cXF_DP.js → binary-plugins-CGmXoZnl.js} +1 -1
  17. package/dist/gateway/static/root/assets/{block-editor-05zyBXBe.js → block-editor-CtvzCZhC.js} +3 -3
  18. package/dist/gateway/static/root/assets/{browser-automation-inputs-BTIufVbM.js → browser-automation-inputs-BOTU1isH.js} +1 -1
  19. package/dist/gateway/static/root/assets/{browser-automations-page-DRBA9nwe.js → browser-automations-page-BwU5V8J0.js} +2 -2
  20. package/dist/gateway/static/root/assets/browser-settings-page-DDjbDeHQ.js +8 -0
  21. package/dist/gateway/static/root/assets/capabilities-settings-panel-DM1Bcf1x.js +8 -0
  22. package/dist/gateway/static/root/assets/channels-settings-Bq36CUxL.js +1 -0
  23. package/dist/gateway/static/root/assets/{channels-status-swr-DMYQNs-J.js → channels-status-swr-Dfi0Desh.js} +1 -1
  24. package/dist/gateway/static/root/assets/{chat-terminal-dock-Dx5KQyo5.js → chat-terminal-dock-B0MvcO6j.js} +2 -2
  25. package/dist/gateway/static/root/assets/connectors-page-L8JehrxF.js +2 -0
  26. package/dist/gateway/static/root/assets/{date-picker-CkDROZoe.js → date-picker-DXOvbHCr.js} +1 -1
  27. package/dist/gateway/static/root/assets/{dependency-picker-Bl8HcvzL.js → dependency-picker-BEnC-eS0.js} +1 -1
  28. package/dist/gateway/static/root/assets/{desktop-pet-BcknAjbW.js → desktop-pet-DtPNvznL.js} +1 -1
  29. package/dist/gateway/static/root/assets/{desktop-pet-settings-BuCpXQPm.js → desktop-pet-settings-CYyjcSn6.js} +1 -1
  30. package/dist/gateway/static/root/assets/device-pairing-wizard-BPMGvAKE.js +1 -0
  31. package/dist/gateway/static/root/assets/{extension-debug-page-qRpb2FFa.js → extension-debug-page-H_aPMGXW.js} +1 -1
  32. package/dist/gateway/static/root/assets/{extension-page-C7gs9wu4.js → extension-page-C1JnpeMt.js} +1 -1
  33. package/dist/gateway/static/root/assets/{extension-settings-page-CyJP1CMZ.js → extension-settings-page-C75kCOqe.js} +1 -1
  34. package/dist/gateway/static/root/assets/{gateway-config-swr-Cpmyrr6j.js → gateway-config-swr-G2lQt7Ak.js} +1 -1
  35. package/dist/gateway/static/root/assets/gateway-settings-BCF732TU.js +1 -0
  36. package/dist/gateway/static/root/assets/{gateway-startup-retry-DoiPeb65.js → gateway-startup-retry-GP31PUrG.js} +1 -1
  37. package/dist/gateway/static/root/assets/{heartbeat-settings-CV6Dolcn.js → heartbeat-settings-C7fjiowt.js} +1 -1
  38. package/dist/gateway/static/root/assets/home-page-DZXeXdUL.js +1 -0
  39. package/dist/gateway/static/root/assets/{image-generation-api-CqjHWl2T.js → image-generation-api-CKFhfHSw.js} +1 -1
  40. package/dist/gateway/static/root/assets/imports-page-GROumhj8.js +1 -0
  41. package/dist/gateway/static/root/assets/{index-CHgOG1aR.js → index-ChIrvv3t.js} +47 -47
  42. package/dist/gateway/static/root/assets/index-R1VNlHjj.css +2 -0
  43. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-B3XqA41D.js → keyboard-shortcuts-settings-zcI822m_.js} +1 -1
  44. package/dist/gateway/static/root/assets/local-app-workbench-page-CAEKGTAg.js +2 -0
  45. package/dist/gateway/static/root/assets/{local-apps-page-BcIVYp0J.js → local-apps-page-BtgDJ92Q.js} +1 -1
  46. package/dist/gateway/static/root/assets/{locale-store-1urrBBCQ.js → locale-store-DlOv7896.js} +1 -1
  47. package/dist/gateway/static/root/assets/logs-page-DHZ4BEkG.js +2 -0
  48. package/dist/gateway/static/root/assets/{management-settings-DNFCdgzR.js → management-settings-DasZxCyD.js} +1 -1
  49. package/dist/gateway/static/root/assets/{markdown-view-BZmcFIWa.js → markdown-view-6oYdBahT.js} +1 -1
  50. package/dist/gateway/static/root/assets/{media-plugins-BDxNhoBC.js → media-plugins-CxthbBvk.js} +1 -1
  51. package/dist/gateway/static/root/assets/messages-Ls1Aaiqd.js +3 -0
  52. package/dist/gateway/static/root/assets/note-detail-panel-BcWq3ddw.js +5 -0
  53. package/dist/gateway/static/root/assets/notes-page-DfZ8QWyv.js +8 -0
  54. package/dist/gateway/static/root/assets/{page-tabs-agH3a2PI.js → page-tabs-BG39KidE.js} +1 -1
  55. package/dist/gateway/static/root/assets/{preview-open-alternatives-XEZ7zDOP.js → preview-open-alternatives-Bevpbq9f.js} +1 -1
  56. package/dist/gateway/static/root/assets/proactive-page-D2U_xQaS.js +1 -0
  57. package/dist/gateway/static/root/assets/{product-open-page-lXAbj85Z.js → product-open-page-Bi1seZpM.js} +1 -1
  58. package/dist/gateway/static/root/assets/project-detail-page-CzlGTW4F.js +1 -0
  59. package/dist/gateway/static/root/assets/projects-page-mQsM-TsP.js +1 -0
  60. package/dist/gateway/static/root/assets/{registry-api-CxQstD00.js → registry-api-D0ddwBFj.js} +1 -1
  61. package/dist/gateway/static/root/assets/remote-access-hub-Cb56JcO1.js +1 -0
  62. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-DErKZlVJ.js → runtime-tools-settings-panel-Wkru8_My.js} +2 -2
  63. package/dist/gateway/static/root/assets/{schema-form-DhCmJQVJ.js → schema-form-CSMRzj9Y.js} +1 -1
  64. package/dist/gateway/static/root/assets/sessions-page-Hj9uFcIO.js +1 -0
  65. package/dist/gateway/static/root/assets/settings-advanced-gate-CV4-_EVA.js +1 -0
  66. package/dist/gateway/static/root/assets/{settings-form-section-Dg6chotg.js → settings-form-section-wcAdCkK8.js} +1 -1
  67. package/dist/gateway/static/root/assets/{settings-page-LyuN5N3M.js → settings-page-BcQGAnCs.js} +1 -1
  68. package/dist/gateway/static/root/assets/setup-status-panel-DzxmsaQG.js +1 -0
  69. package/dist/gateway/static/root/assets/{share-preview-page-CZvmf4Ob.js → share-preview-page-DQocUl-1.js} +1 -1
  70. package/dist/gateway/static/root/assets/shares-settings-xlJh1ccG.js +1 -0
  71. package/dist/gateway/static/root/assets/skill-reload-api-wSXJRSxX.js +1 -0
  72. package/dist/gateway/static/root/assets/skills-page-AmqtBJo7.js +2 -0
  73. package/dist/gateway/static/root/assets/{skills-page.utils-Eu0WVHTS.js → skills-page.utils-DtlfCgJp.js} +1 -1
  74. package/dist/gateway/static/root/assets/{system-settings-panel-DQKkM7B2.js → system-settings-panel-Ct698Nf-.js} +1 -1
  75. package/dist/gateway/static/root/assets/task-detail-page-sQpwq5-c.js +1 -0
  76. package/dist/gateway/static/root/assets/{theme-store-Dc5s20V3.js → theme-store-ng8KJsAT.js} +1 -1
  77. package/dist/gateway/static/root/assets/{tunnel-api-TKQpPVd6.js → tunnel-api-Boajol0M.js} +1 -1
  78. package/dist/gateway/static/root/assets/{use-autosave-CkVlrFk1.js → use-autosave-BSyxRHt2.js} +1 -1
  79. package/dist/gateway/static/root/assets/user-model-page-DIEHnAER.js +2 -0
  80. package/dist/gateway/static/root/assets/{voice-api-key-field-B12fFN-L.js → voice-api-key-field-SE3OQI1s.js} +1 -1
  81. package/dist/gateway/static/root/assets/{voice-config-api-Mk78BrpP.js → voice-config-api-VNuCmdFe.js} +1 -1
  82. package/dist/gateway/static/root/assets/{work-discovery-overlay-CT9Zy36i.js → work-discovery-overlay-Dn1vy2oq.js} +1 -1
  83. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-BwefQliE.js → workflow-run-setup-panel-BJEfGY-k.js} +2 -2
  84. package/dist/gateway/static/root/assets/workflows-page-DgRNIyr7.js +6 -0
  85. package/dist/gateway/static/root/index.html +10 -10
  86. package/dist/package.js +1 -1
  87. package/dist/packages/gateway-contract/src/proactive.js +13 -4
  88. package/dist/src/agent/coding/repository-instructions.js +1 -1
  89. package/dist/src/agent/coding/workspace-revision.js +1 -1
  90. package/dist/src/agent/commands/command-isolation.js +1 -1
  91. package/dist/src/agent/tools/factory.js +7 -3
  92. package/dist/src/agent/tools/review-workspace.js +1 -1
  93. package/dist/src/browser/providers/browser-ext-install.js +10 -2
  94. package/dist/src/cli/command-loaders.js +1 -0
  95. package/dist/src/cli/command-manifest.js +4 -0
  96. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  97. package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
  98. package/dist/src/cli/commands/gateway/run-foreground.js +3 -0
  99. package/dist/src/cli/commands/import.d.ts +2 -0
  100. package/dist/src/cli/commands/import.js +78 -0
  101. package/dist/src/discussions/action-tasks.js +1 -1
  102. package/dist/src/discussions/edits.js +1 -1
  103. package/dist/src/discussions/index.js +1 -1
  104. package/dist/src/discussions/organizer.js +1 -1
  105. package/dist/src/discussions/recording.js +1 -1
  106. package/dist/src/discussions/sealer.js +1 -1
  107. package/dist/src/discussions/service.js +2 -2
  108. package/dist/src/endpoint-tools/invocation-service.d.ts +2 -0
  109. package/dist/src/endpoint-tools/invocation-service.js +48 -19
  110. package/dist/src/gateway/heartbeat/check-store.d.ts +18 -0
  111. package/dist/src/gateway/heartbeat/check-store.js +73 -0
  112. package/dist/src/gateway/heartbeat/service.d.ts +12 -3
  113. package/dist/src/gateway/heartbeat/service.js +61 -44
  114. package/dist/src/gateway/hono/routes/discussions.js +2 -2
  115. package/dist/src/gateway/hono/routes/imports.d.ts +3 -0
  116. package/dist/src/gateway/hono/routes/imports.js +56 -0
  117. package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
  118. package/dist/src/gateway/hono/routes/proactive-control.js +2 -1
  119. package/dist/src/gateway/service.d.ts +7 -0
  120. package/dist/src/gateway/service.js +26 -9
  121. package/dist/src/imports/compatibility.d.ts +7 -0
  122. package/dist/src/imports/compatibility.js +174 -0
  123. package/dist/src/imports/contextImport.d.ts +3 -0
  124. package/dist/src/imports/contextImport.js +56 -0
  125. package/dist/src/imports/files.d.ts +13 -0
  126. package/dist/src/imports/files.js +77 -0
  127. package/dist/src/imports/importService.d.ts +50 -0
  128. package/dist/src/imports/importService.js +414 -0
  129. package/dist/src/imports/inventory.d.ts +11 -0
  130. package/dist/src/imports/inventory.js +202 -0
  131. package/dist/src/imports/planner.d.ts +11 -0
  132. package/dist/src/imports/planner.js +98 -0
  133. package/dist/src/imports/productImport.d.ts +10 -0
  134. package/dist/src/imports/productImport.js +241 -0
  135. package/dist/src/imports/runtime.d.ts +5 -0
  136. package/dist/src/imports/runtime.js +61 -0
  137. package/dist/src/imports/scanner.d.ts +14 -0
  138. package/dist/src/imports/scanner.js +159 -0
  139. package/dist/src/imports/secrets.d.ts +2 -0
  140. package/dist/src/imports/secrets.js +9 -0
  141. package/dist/src/imports/sources.d.ts +37 -0
  142. package/dist/src/imports/sources.js +75 -0
  143. package/dist/src/imports/types.d.ts +144 -0
  144. package/dist/src/imports/types.js +11 -0
  145. package/dist/src/infra/background-task.d.ts +2 -0
  146. package/dist/src/infra/background-task.js +25 -0
  147. package/dist/src/infra/process-diagnostics.d.ts +5 -0
  148. package/dist/src/infra/process-diagnostics.js +97 -0
  149. package/dist/src/infra/unhandled-rejections.js +6 -3
  150. package/dist/src/infra/update-restart.js +1 -1
  151. package/dist/src/knowledge/ingestion-service.js +1 -0
  152. package/dist/src/knowledge-memory/repository.js +4 -2
  153. package/dist/src/media-shared/http/ssrf-guard.js +1 -1
  154. package/dist/src/notifications/proactive-policy.js +7 -5
  155. package/dist/src/proactive/execution/authorization.d.ts +2 -0
  156. package/dist/src/proactive/execution/authorization.js +10 -1
  157. package/dist/src/proactive/execution/context.js +1 -0
  158. package/dist/src/proactive/execution/repository.d.ts +2 -0
  159. package/dist/src/proactive/execution/repository.js +19 -2
  160. package/dist/src/proactive/execution/worker.js +14 -4
  161. package/dist/src/proactive/experience.d.ts +4 -1
  162. package/dist/src/proactive/experience.js +3 -0
  163. package/dist/src/proactive/follow-ups.d.ts +5 -0
  164. package/dist/src/proactive/follow-ups.js +18 -7
  165. package/dist/src/proactive/inbox/cards.js +2 -1
  166. package/dist/src/proactive/inbox/delivery.js +6 -7
  167. package/dist/src/proactive/inbox/digest.d.ts +1 -1
  168. package/dist/src/proactive/inbox/digest.js +23 -13
  169. package/dist/src/proactive/inbox/notification-policy.d.ts +1 -1
  170. package/dist/src/proactive/inbox/notification-policy.js +9 -6
  171. package/dist/src/proactive/inbox/repository.js +3 -2
  172. package/dist/src/proactive/inbox/service.d.ts +6 -0
  173. package/dist/src/proactive/inbox/service.js +18 -0
  174. package/dist/src/proactive/inbox/types.d.ts +1 -0
  175. package/dist/src/proactive/policy/presence.d.ts +1 -1
  176. package/dist/src/proactive/policy/presence.js +7 -5
  177. package/dist/src/proactive/policy/service.d.ts +8 -5
  178. package/dist/src/proactive/policy/service.js +12 -12
  179. package/dist/src/proactive/scenarios/control.d.ts +3 -3
  180. package/dist/src/proactive/scenarios/control.js +12 -2
  181. package/dist/src/proactive/source-freshness.d.ts +8 -0
  182. package/dist/src/proactive/source-freshness.js +27 -0
  183. package/dist/src/proactive/status.d.ts +2 -0
  184. package/dist/src/proactive/status.js +21 -0
  185. package/dist/src/proactive/temporal/worker.js +2 -1
  186. package/dist/src/realtime/runtime.js +31 -5
  187. package/dist/src/realtime/writer.js +16 -9
  188. package/dist/src/storage/sqlite/import-repository.d.ts +18 -0
  189. package/dist/src/storage/sqlite/import-repository.js +35 -0
  190. package/dist/src/storage/sqlite/import-selection-repository.d.ts +15 -0
  191. package/dist/src/storage/sqlite/import-selection-repository.js +28 -0
  192. package/dist/src/storage/sqlite/knowledge-repository.d.ts +1 -0
  193. package/dist/src/storage/sqlite/knowledge-repository.js +2 -2
  194. package/dist/src/storage/sqlite/migrations/174_capability_imports.sql +11 -0
  195. package/dist/src/storage/sqlite/migrations/175_import_selections.sql +8 -0
  196. package/dist/src/storage/sqlite/migrations/176_proactive_quality.sql +5 -0
  197. package/dist/src/storage/sqlite/migrations/177_proactive_check_controls.sql +25 -0
  198. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  199. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  200. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  201. package/dist/src/storage/sqlite/schema.js +1 -1
  202. package/dist/src/work-discovery/candidate-discovery.js +1 -1
  203. package/dist/src/work-discovery/probe.js +1 -1
  204. package/package.json +4 -3
  205. package/dist/gateway/static/root/assets/agent-defaults-settings-panel-SElFkQd6.js +0 -1
  206. package/dist/gateway/static/root/assets/agents-DYazUaZ6.js +0 -1
  207. package/dist/gateway/static/root/assets/apps-page-BzrodN8C.js +0 -1
  208. package/dist/gateway/static/root/assets/automations-workspace--A4I0jMu.js +0 -6
  209. package/dist/gateway/static/root/assets/browser-settings-page-DDTXUNWK.js +0 -8
  210. package/dist/gateway/static/root/assets/capabilities-settings-panel-Ll5UeXq5.js +0 -8
  211. package/dist/gateway/static/root/assets/channels-settings-BzL15bkU.js +0 -1
  212. package/dist/gateway/static/root/assets/connectors-page-RPw1AOMb.js +0 -2
  213. package/dist/gateway/static/root/assets/device-pairing-wizard-D0_tCX49.js +0 -1
  214. package/dist/gateway/static/root/assets/gateway-settings-DOeeTdWE.js +0 -1
  215. package/dist/gateway/static/root/assets/home-page-Djw1H_Go.js +0 -1
  216. package/dist/gateway/static/root/assets/index-B_Sg5BKz.css +0 -2
  217. package/dist/gateway/static/root/assets/local-app-workbench-page-BPqgyYJq.js +0 -2
  218. package/dist/gateway/static/root/assets/logs-page-BHax3XlS.js +0 -2
  219. package/dist/gateway/static/root/assets/messages-DYFcjtmG.js +0 -3
  220. package/dist/gateway/static/root/assets/note-detail-panel-Cs46LFXO.js +0 -5
  221. package/dist/gateway/static/root/assets/notes-page-BUkcn_0t.js +0 -8
  222. package/dist/gateway/static/root/assets/proactive-page-Bn484J9v.js +0 -1
  223. package/dist/gateway/static/root/assets/project-detail-page-LcH21ixD.js +0 -1
  224. package/dist/gateway/static/root/assets/projects-page-DelBiwyB.js +0 -1
  225. package/dist/gateway/static/root/assets/remote-access-hub-3BjFYXCf.js +0 -1
  226. package/dist/gateway/static/root/assets/sessions-page-CVFirgVd.js +0 -1
  227. package/dist/gateway/static/root/assets/settings-advanced-gate-DyfBpqkY.js +0 -1
  228. package/dist/gateway/static/root/assets/setup-status-panel-CrXhTzHr.js +0 -1
  229. package/dist/gateway/static/root/assets/shares-settings-DyM_D8hM.js +0 -1
  230. package/dist/gateway/static/root/assets/skill-reload-api-Bys58-15.js +0 -1
  231. package/dist/gateway/static/root/assets/skills-page-sWNvFo6D.js +0 -2
  232. package/dist/gateway/static/root/assets/task-detail-page-B18NcZQJ.js +0 -1
  233. package/dist/gateway/static/root/assets/user-model-page-ul-zr58b.js +0 -2
  234. package/dist/gateway/static/root/assets/workflows-page-BY_AWs7S.js +0 -6
@@ -0,0 +1,202 @@
1
+ import { init_paths, resolveSkillsDir } from "../config/paths.js";
2
+ import { listKnowledgeItems } from "../knowledge-memory/repository.js";
3
+ import "../knowledge-memory/index.js";
4
+ import { ProjectService } from "../projects/project-service.js";
5
+ import { resolveWorkspaceSkillsDir } from "../agent/skills/workspace-skills-dir.js";
6
+ import { ImportError } from "./types.js";
7
+ import { digest } from "./files.js";
8
+ import { canonicalTarget } from "./planner.js";
9
+ import { detectImportSources } from "./sources.js";
10
+ import { ImportSelectionRepository } from "../storage/sqlite/import-selection-repository.js";
11
+ import { basename, isAbsolute, parse } from "node:path";
12
+ import { realpathSync, statSync } from "node:fs";
13
+ import { randomUUID } from "node:crypto";
14
+ import { setImmediate } from "node:timers/promises";
15
+ //#region src/imports/inventory.ts
16
+ init_paths();
17
+ function contextKey(candidate) {
18
+ return `product-import:${candidate.source}:${digest(candidate.location)}`;
19
+ }
20
+ function existingContext(candidate, projectId) {
21
+ return listKnowledgeItems({
22
+ canonicalKey: contextKey(candidate),
23
+ scope: projectId ? {
24
+ type: "project",
25
+ id: projectId
26
+ } : { type: "global" },
27
+ statuses: [
28
+ "active",
29
+ "candidate",
30
+ "needs_review",
31
+ "stale",
32
+ "archived",
33
+ "rejected"
34
+ ]
35
+ })[0];
36
+ }
37
+ function publicInventory(inventory) {
38
+ return {
39
+ id: inventory.id,
40
+ source: inventory.source,
41
+ createdAt: inventory.createdAt,
42
+ expiresAt: inventory.expiresAt,
43
+ complete: inventory.complete,
44
+ notices: inventory.notices,
45
+ candidates: inventory.candidates.map(({ id, kind, parentId, name, description, displayPath, scope, status, reason, suggested, targetName }) => ({
46
+ id,
47
+ kind,
48
+ parentId,
49
+ name,
50
+ description,
51
+ displayPath,
52
+ scope,
53
+ status,
54
+ reason,
55
+ suggested,
56
+ targetName
57
+ }))
58
+ };
59
+ }
60
+ function getLiveInventory(owner, id) {
61
+ const inventory = new ImportSelectionRepository(owner).get("inventory", id);
62
+ if (Date.now() > inventory.expiresAt) throw new ImportError("inventory_stale", "This list has expired. Refresh it before importing.", 409);
63
+ return inventory;
64
+ }
65
+ async function scanProduct(service, source, owner = "gateway-owner") {
66
+ if (!detectImportSources().some((s) => s.id === source && s.detected)) throw new ImportError("source_not_found", "App is unavailable on the xopc device", 404);
67
+ const inventory = {
68
+ id: randomUUID(),
69
+ source,
70
+ createdAt: Date.now(),
71
+ expiresAt: Date.now() + 15 * 6e4,
72
+ candidates: [],
73
+ notices: [],
74
+ complete: true,
75
+ scanIds: []
76
+ };
77
+ const budget = {
78
+ bytes: 0,
79
+ files: 0
80
+ };
81
+ const projects = new ProjectService();
82
+ const addScan = (scan, targetRoot, parent) => {
83
+ inventory.scanIds.push(scan.id);
84
+ inventory.notices.push(...scan.diagnostics);
85
+ if (scan.diagnostics.length) inventory.complete = false;
86
+ const relevant = {
87
+ ...scan,
88
+ candidates: scan.candidates.filter((c) => c.scope === (parent ? "project" : "user"))
89
+ };
90
+ const skills = service.describeSkills(relevant, {
91
+ root: targetRoot,
92
+ projectId: parent?.projectId
93
+ });
94
+ for (const candidate of relevant.candidates) {
95
+ if (candidate.kind === "mcp") {
96
+ inventory.notices.push(`${candidate.name}: reconnect this tool in Connections.`);
97
+ continue;
98
+ }
99
+ const item = {
100
+ id: candidate.id,
101
+ kind: candidate.kind === "rule" ? "context" : "skill",
102
+ parentId: parent?.id,
103
+ name: candidate.name,
104
+ description: candidate.description,
105
+ displayPath: candidate.location,
106
+ location: candidate.location,
107
+ scope: candidate.scope,
108
+ status: candidate.compatibility === "compatible" ? "ready" : "blocked",
109
+ suggested: false,
110
+ reason: candidate.findings.join("; ") || void 0,
111
+ scanId: scan.id,
112
+ candidate,
113
+ targetRoot,
114
+ projectId: parent?.projectId
115
+ };
116
+ if (item.kind === "skill") {
117
+ const state = skills.find((s) => s.candidate.id === candidate.id);
118
+ item.status = state.status;
119
+ item.reason = state.reason;
120
+ item.targetName = state.targetName;
121
+ if (parent && !service.isWorkspaceTrusted(parent.location) && item.status !== "existing") {
122
+ item.status = "blocked";
123
+ item.reason = "Trust this project in Projects before importing its skills.";
124
+ }
125
+ item.suggested = item.status === "ready";
126
+ } else if (item.status === "ready") {
127
+ const existing = !parent || parent.projectId ? existingContext(candidate, parent?.projectId) : void 0;
128
+ if (existing) {
129
+ item.status = existing.source.hash === candidate.hash ? "existing" : "blocked";
130
+ item.reason = item.status === "blocked" ? "Previously imported context has changed; existing content will be kept." : void 0;
131
+ }
132
+ }
133
+ inventory.candidates.push(item);
134
+ }
135
+ };
136
+ const scan = await service.scan({
137
+ source,
138
+ budget
139
+ });
140
+ addScan(scan, canonicalTarget(resolveSkillsDir()));
141
+ const seen = /* @__PURE__ */ new Set();
142
+ const paths = scan.projectRoots ?? [];
143
+ const limit = 500;
144
+ if (paths.length > limit) {
145
+ inventory.complete = false;
146
+ inventory.notices.push(`${paths.length - limit} project directories were not scanned (limit: ${limit}).`);
147
+ }
148
+ for (const path of paths.slice(0, limit)) {
149
+ await setImmediate();
150
+ let parent;
151
+ try {
152
+ if (!isAbsolute(path) || parse(path).root === path || !statSync(path).isDirectory()) throw new Error("Unavailable project");
153
+ const location = realpathSync(path);
154
+ if (parse(location).root === location) throw new Error("Invalid project");
155
+ if (seen.has(location)) continue;
156
+ seen.add(location);
157
+ const existing = projects.findByWorkspaceRoot(location);
158
+ parent = {
159
+ id: randomUUID(),
160
+ kind: "project",
161
+ name: existing?.name ?? basename(location),
162
+ description: "",
163
+ displayPath: path,
164
+ location,
165
+ scope: "project",
166
+ status: existing ? "existing" : "ready",
167
+ suggested: false,
168
+ projectId: existing?.id
169
+ };
170
+ inventory.candidates.push(parent);
171
+ addScan(await service.scan({
172
+ source,
173
+ projectRoot: location,
174
+ projectOnly: true,
175
+ budget
176
+ }), canonicalTarget(resolveWorkspaceSkillsDir(location)), parent);
177
+ } catch {
178
+ inventory.complete = false;
179
+ inventory.notices.push(`${path}: some contents could not be scanned; only listed items can be imported.`);
180
+ }
181
+ }
182
+ const score = (item) => inventory.candidates.some((c) => c.parentId === item.id && ["ready", "conflict"].includes(c.status)) ? 0 : item.projectId ? 1 : 2;
183
+ const roots = inventory.candidates.filter((c) => c.kind === "project").sort((a, b) => score(a) - score(b) || a.name.localeCompare(b.name) || a.location.localeCompare(b.location));
184
+ inventory.candidates = [...inventory.candidates.filter((c) => c.scope === "user"), ...roots.flatMap((p) => [p, ...inventory.candidates.filter((c) => c.parentId === p.id)])];
185
+ new ImportSelectionRepository(owner).save("inventory", inventory);
186
+ return publicInventory(inventory);
187
+ }
188
+ function previewInventoryItem(service, owner, inventoryId, itemId) {
189
+ const item = getLiveInventory(owner, inventoryId).candidates.find((c) => c.id === itemId);
190
+ if (!item) throw new ImportError("not_found", "Item not found", 404);
191
+ if (item.status === "blocked") return {
192
+ text: item.reason ?? "",
193
+ truncated: false
194
+ };
195
+ const text = item.kind === "context" ? item.candidate?.content ?? "" : item.kind === "skill" ? Buffer.from(service.readSnapshot(item.scanId, item.id).find((f) => f.path === "SKILL.md").data, "base64").toString("utf8") : item.displayPath;
196
+ return {
197
+ text: text.slice(0, 2e4),
198
+ truncated: text.length > 2e4
199
+ };
200
+ }
201
+ //#endregion
202
+ export { contextKey, existingContext, getLiveInventory, previewInventoryItem, publicInventory, scanProduct };
@@ -0,0 +1,11 @@
1
+ import { type ImportAction, type ImportPlan, type ImportScan, type ImportTarget } from './types.js';
2
+ export declare function targetInventory(root: string): Array<{
3
+ directory: string;
4
+ name: string;
5
+ hash: string;
6
+ }>;
7
+ export declare function targetRevision(root: string): string;
8
+ export declare function canonicalTarget(root: string): string;
9
+ export declare function createImportPlan(scan: ImportScan, target: ImportTarget, choices: Array<Pick<ImportAction, 'candidateId' | 'operation'> & {
10
+ name?: string;
11
+ }>, reservedNames?: string[]): ImportPlan;
@@ -0,0 +1,98 @@
1
+ import { isValidSkillId } from "../agent/skills/managed-store.js";
2
+ import { ImportError } from "./types.js";
3
+ import { IMPORT_LIMITS, digest, fileHash, readTree } from "./files.js";
4
+ import { parseSkill } from "./compatibility.js";
5
+ import { join, relative, resolve } from "node:path";
6
+ import { existsSync, readdirSync, realpathSync } from "node:fs";
7
+ import { randomUUID } from "node:crypto";
8
+ //#region src/imports/planner.ts
9
+ function targetInventory(root) {
10
+ if (!existsSync(root)) return [];
11
+ let bytes = 0;
12
+ let count = 0;
13
+ return readdirSync(root, { withFileTypes: true }).filter((e) => !e.name.startsWith(".")).map((entry) => {
14
+ if (!entry.isDirectory() || entry.isSymbolicLink()) throw new ImportError("unsafe_target", "Target contains a link or non-directory item", 409);
15
+ const files = readTree(join(root, entry.name));
16
+ bytes += files.reduce((n, f) => n + Buffer.byteLength(f.data, "base64"), 0);
17
+ count += files.length;
18
+ if (bytes > IMPORT_LIMITS.total || count > IMPORT_LIMITS.count) throw new ImportError("limit_exceeded", "Target inventory exceeds import limits", 413);
19
+ const primary = files.find((f) => f.path === "SKILL.md");
20
+ let name = entry.name;
21
+ try {
22
+ if (primary) name = String(parseSkill(Buffer.from(primary.data, "base64").toString("utf8")).metadata.name);
23
+ } catch {}
24
+ return {
25
+ directory: entry.name,
26
+ name,
27
+ hash: fileHash(files)
28
+ };
29
+ }).sort((a, b) => a.directory.localeCompare(b.directory));
30
+ }
31
+ function targetRevision(root) {
32
+ return digest(targetInventory(root));
33
+ }
34
+ function canonicalTarget(root) {
35
+ let current = resolve(root);
36
+ while (!existsSync(current)) {
37
+ const parent = resolve(current, "..");
38
+ if (parent === current) break;
39
+ current = parent;
40
+ }
41
+ return join(realpathSync(current), relative(current, resolve(root)));
42
+ }
43
+ function createImportPlan(scan, target, choices, reservedNames = []) {
44
+ if (Date.now() - scan.createdAt > 1440 * 60 * 1e3) throw new ImportError("plan_stale", "Scan expired; scan again", 409);
45
+ target = {
46
+ ...target,
47
+ root: canonicalTarget(target.root)
48
+ };
49
+ const inventory = targetInventory(target.root);
50
+ const used = /* @__PURE__ */ new Set();
51
+ const candidates = /* @__PURE__ */ new Set();
52
+ const normalize = (s) => s.normalize("NFC").toLowerCase();
53
+ const actions = choices.map((choice) => {
54
+ const item = scan.candidates.find((c) => c.id === choice.candidateId);
55
+ if (!item || candidates.has(item.id)) throw new ImportError("invalid_selection", "Invalid or duplicate candidate");
56
+ candidates.add(item.id);
57
+ const name = choice.name ?? item.name;
58
+ if (!isValidSkillId(name)) throw new ImportError("invalid_name", "Choose a name using letters, numbers, dot, dash or underscore");
59
+ if (choice.operation !== "skip" && !["compatible", "needs_setup"].includes(item.compatibility)) throw new ImportError("incompatible", "Selected item requires manual adaptation");
60
+ if (item.scope === "project" && !target.projectId && choice.operation !== "skip") throw new ImportError("scope_mismatch", "Project capabilities must remain project-scoped");
61
+ if (item.kind !== "skill" && choice.operation !== "skip" && choice.operation !== "create") throw new ImportError("invalid_operation", "MCP drafts only support create");
62
+ const existing = item.kind === "skill" ? inventory.find((e) => normalize(e.directory) === normalize(name) || normalize(e.name) === normalize(name)) : void 0;
63
+ const hash = existing?.hash ?? null;
64
+ if (choice.operation !== "skip") {
65
+ const key = `${item.kind}:${normalize(name)}`;
66
+ if (used.has(key)) throw new ImportError("conflict", "Two selected items have the same target name", 409);
67
+ used.add(key);
68
+ if (existing?.hash === item.hash && choice.operation === "create") return {
69
+ candidateId: item.id,
70
+ operation: "skip",
71
+ name,
72
+ beforeHash: hash
73
+ };
74
+ if (choice.operation === "replace") {
75
+ if (!existing || existing.directory !== name || existing.name !== name) throw new ImportError("conflict", "Replace requires an exact managed directory and skill name match", 409);
76
+ } else if (existing || item.kind === "skill" && reservedNames.some((n) => normalize(n) === normalize(name))) throw new ImportError("conflict", "Name is already used; choose another name or explicitly replace a managed skill", 409);
77
+ if (choice.operation === "create" && name !== item.name) throw new ImportError("invalid_operation", "Use rename when changing the skill name");
78
+ }
79
+ return {
80
+ candidateId: item.id,
81
+ operation: choice.operation,
82
+ name,
83
+ beforeHash: hash
84
+ };
85
+ });
86
+ if (!actions.length) throw new ImportError("invalid_selection", "Select at least one item");
87
+ return {
88
+ id: randomUUID(),
89
+ scanId: scan.id,
90
+ createdAt: Date.now(),
91
+ expiresAt: Date.now() + 1440 * 60 * 1e3,
92
+ target,
93
+ targetRevision: digest(inventory),
94
+ actions
95
+ };
96
+ }
97
+ //#endregion
98
+ export { canonicalTarget, createImportPlan, targetInventory, targetRevision };
@@ -0,0 +1,10 @@
1
+ import type { ImportService } from './importService.js';
2
+ import { type ImportSelection, type ProductImportResult } from './types.js';
3
+ export declare function listImportSources(owner: string): {
4
+ lastImport: ProductImportResult;
5
+ id: import("./types.js").ImportSource;
6
+ name: string;
7
+ detected: boolean;
8
+ }[];
9
+ export declare function importSelection(service: ImportService, selection: ImportSelection, owner?: string): Promise<ProductImportResult>;
10
+ export declare function recoverSelectionRuns(service: ImportService, owner: string): Promise<void>;
@@ -0,0 +1,241 @@
1
+ import { resolveStateDir } from "../config/paths-state.js";
2
+ import { runSqliteWriteTransaction } from "../storage/sqlite/transaction.js";
3
+ import { init_paths } from "../config/paths.js";
4
+ import { ProjectService } from "../projects/project-service.js";
5
+ import { ImportError } from "./types.js";
6
+ import { digest, fileHash, readRegularFile, readTree } from "./files.js";
7
+ import { importContextDocument } from "./contextImport.js";
8
+ import { canonicalTarget, targetInventory } from "./planner.js";
9
+ import { detectImportSources } from "./sources.js";
10
+ import { ImportSelectionRepository } from "../storage/sqlite/import-selection-repository.js";
11
+ import { existingContext, getLiveInventory } from "./inventory.js";
12
+ import { join } from "node:path";
13
+ import { mkdirSync, realpathSync, statSync } from "node:fs";
14
+ import { setImmediate } from "node:timers/promises";
15
+ import lockfile from "proper-lockfile";
16
+ //#region src/imports/productImport.ts
17
+ init_paths();
18
+ function listImportSources(owner) {
19
+ const runs = new ImportSelectionRepository(owner).list("run");
20
+ return detectImportSources().map((source) => ({
21
+ ...source,
22
+ lastImport: runs.find((r) => r.source === source.id)
23
+ }));
24
+ }
25
+ function stale() {
26
+ throw new ImportError("inventory_stale", "Selected content or its destination changed. Refresh the list before importing.", 409);
27
+ }
28
+ function selectedItems(inventory, ids) {
29
+ if (!ids.length || new Set(ids).size !== ids.length) throw new ImportError("invalid_selection", "Select at least one item; duplicate selections are not allowed");
30
+ return ids.map((id) => {
31
+ const item = inventory.candidates.find((c) => c.id === id);
32
+ if (!item || item.status === "blocked" || item.status === "existing" && item.kind !== "project") throw new ImportError("invalid_selection", "Select available items from this list");
33
+ if (item.parentId && !ids.includes(item.parentId)) throw new ImportError("missing_parent", "Select the project entry along with its contents");
34
+ return item;
35
+ }).sort((a, b) => Number(b.kind === "project") - Number(a.kind === "project"));
36
+ }
37
+ async function preflight(service, inventory, items, retry) {
38
+ const projects = new ProjectService();
39
+ const names = /* @__PURE__ */ new Set();
40
+ for (const item of items) {
41
+ await setImmediate();
42
+ try {
43
+ if (realpathSync(item.location) !== item.location) stale();
44
+ if (item.kind === "project") {
45
+ if (!statSync(item.location).isDirectory()) stale();
46
+ const existing = projects.findByWorkspaceRoot(item.location);
47
+ const expectedId = item.projectId ?? retry?.items.find((i) => i.candidateId === item.id)?.targetId;
48
+ if (existing?.id !== expectedId) stale();
49
+ continue;
50
+ }
51
+ const candidate = item.candidate;
52
+ const parent = item.parentId ? inventory.candidates.find((c) => c.id === item.parentId) : void 0;
53
+ if (item.kind === "context") {
54
+ if (digest(readRegularFile(item.location, 128 * 1024).toString("utf8").trim()) !== candidate.hash) stale();
55
+ const projectId = parent ? projects.findByWorkspaceRoot(parent.location)?.id : void 0;
56
+ if ((!parent || projectId) && existingContext(candidate, projectId)) stale();
57
+ } else {
58
+ if (parent && !service.isWorkspaceTrusted(parent.location)) stale();
59
+ if (fileHash(readTree(item.location)) !== candidate.hash) stale();
60
+ service.readSnapshot(item.scanId, candidate.id);
61
+ if (canonicalTarget(item.targetRoot) !== item.targetRoot) stale();
62
+ const key = `${item.targetRoot}:${item.targetName.toLowerCase()}`;
63
+ if (names.has(key)) stale();
64
+ names.add(key);
65
+ const existing = targetInventory(item.targetRoot);
66
+ const reserved = service.reservedNames({
67
+ root: item.targetRoot,
68
+ projectId: parent?.projectId
69
+ });
70
+ if ([...existing.flatMap((e) => [e.name, e.directory]), ...reserved].some((n) => n.toLowerCase() === item.targetName.toLowerCase())) stale();
71
+ }
72
+ } catch (error) {
73
+ if (error instanceof ImportError && error.code === "inventory_stale") throw error;
74
+ stale();
75
+ }
76
+ }
77
+ }
78
+ function summarize(run) {
79
+ run.skills = run.items.filter((i) => i.kind === "skill" && i.status === "imported").length;
80
+ run.context = run.items.filter((i) => i.kind === "context" && i.status === "imported").length;
81
+ run.projects = run.items.filter((i) => i.kind === "project" && i.status === "imported").length;
82
+ run.skipped = run.items.filter((i) => i.status === "existing").length;
83
+ run.issues = run.items.filter((i) => i.status === "failed").map((i) => ({
84
+ name: i.name,
85
+ reason: i.error
86
+ }));
87
+ }
88
+ async function execute(service, repo, inventory, run) {
89
+ const projects = new ProjectService();
90
+ const persist = () => {
91
+ summarize(run);
92
+ repo.save("run", run);
93
+ };
94
+ for (const entry of run.items.filter((i) => i.status === "pending")) {
95
+ await setImmediate();
96
+ const item = inventory.candidates.find((c) => c.id === entry.candidateId);
97
+ try {
98
+ if (Date.now() - run.createdAt > 720 * 60 * 6e4) throw new ImportError("expired", "Import recovery period expired. Scan again.");
99
+ const parent = item.parentId ? run.items.find((i) => i.candidateId === item.parentId) : void 0;
100
+ if (parent?.status === "failed") throw new ImportError("parent_failed", "The project could not be imported. Retry the project and its contents.");
101
+ if (item.kind === "project") {
102
+ if (realpathSync(item.location) !== item.location || !statSync(item.location).isDirectory()) stale();
103
+ runSqliteWriteTransaction(() => {
104
+ const existing = projects.findByWorkspaceRoot(item.location);
105
+ entry.targetId = (existing ?? projects.create({ workspaceRoot: item.location })).id;
106
+ entry.status = existing ? "existing" : "imported";
107
+ persist();
108
+ });
109
+ } else if (item.kind === "context") runSqliteWriteTransaction(() => {
110
+ if (parent && projects.get(parent.targetId)?.workspaceRoot !== inventory.candidates.find((c) => c.id === item.parentId).location) stale();
111
+ const status = importContextDocument(item.candidate, parent?.targetId);
112
+ if (status === "kept") throw new ImportError("conflict", "Existing context was kept. Refresh before importing.");
113
+ entry.targetId = existingContext(item.candidate, parent?.targetId)?.id;
114
+ entry.status = status === "created" ? "imported" : "existing";
115
+ persist();
116
+ });
117
+ else {
118
+ const key = `${run.id}:${item.id}`;
119
+ let job = service.jobByKey(key);
120
+ if (!job) {
121
+ const plan = service.plan(item.scanId, {
122
+ root: item.targetRoot,
123
+ projectId: parent?.targetId
124
+ }, [{
125
+ candidateId: item.id,
126
+ operation: item.targetName === item.candidate.name ? "create" : "rename",
127
+ name: item.targetName
128
+ }]);
129
+ job = service.apply(plan.id, key);
130
+ }
131
+ entry.jobId = job.id;
132
+ persist();
133
+ job = service.getJob(job.id);
134
+ const pending = job.items.filter((i) => ["staged", "failed"].includes(i.status));
135
+ if (pending.length) try {
136
+ job = await service.activate(job.id, pending.map((i) => i.action.candidateId));
137
+ } catch (error) {
138
+ job = service.getJob(job.id);
139
+ if (!job.items.every((i) => i.status === "active" || i.status === "skipped")) throw error;
140
+ }
141
+ const published = job.items[0];
142
+ if (!["active", "skipped"].includes(published.status)) throw new ImportError("publish_failed", published.error ?? "Could not publish the skill. Retry this item.");
143
+ entry.targetId = join(item.targetRoot, item.targetName);
144
+ entry.status = published.status === "active" ? "imported" : "existing";
145
+ }
146
+ } catch (error) {
147
+ entry.status = "failed";
148
+ delete entry.targetId;
149
+ entry.error = error instanceof ImportError ? error.message : "This item could not be imported. Check access and retry.";
150
+ }
151
+ persist();
152
+ }
153
+ run.status = run.items.some((i) => i.status === "failed") ? "partial" : "completed";
154
+ persist();
155
+ return run;
156
+ }
157
+ async function withImportLock(fn) {
158
+ const path = join(resolveStateDir(), "imports", "selection-lock");
159
+ mkdirSync(path, {
160
+ recursive: true,
161
+ mode: 448
162
+ });
163
+ const release = await lockfile.lock(path, {
164
+ realpath: false,
165
+ stale: 6e4,
166
+ retries: {
167
+ retries: 15,
168
+ minTimeout: 100,
169
+ maxTimeout: 500
170
+ }
171
+ });
172
+ try {
173
+ return await fn();
174
+ } finally {
175
+ await release();
176
+ }
177
+ }
178
+ async function importSelection(service, selection, owner = "gateway-owner") {
179
+ return withImportLock(async () => {
180
+ const repo = new ImportSelectionRepository(owner);
181
+ const selectionHash = digest({
182
+ inventoryId: selection.inventoryId,
183
+ ids: [...selection.candidateIds].sort(),
184
+ retryOf: selection.retryOf
185
+ });
186
+ let previous;
187
+ try {
188
+ previous = repo.get("run", selection.requestId);
189
+ } catch (error) {
190
+ if (!(error instanceof ImportError) || error.code !== "not_found") throw error;
191
+ }
192
+ if (previous) {
193
+ if (previous.selectionHash !== selectionHash) throw new ImportError("conflict", "Request ID belongs to a different selection", 409);
194
+ return previous.status === "running" ? execute(service, repo, repo.get("inventory", previous.inventoryId), previous) : previous;
195
+ }
196
+ const inventory = getLiveInventory(owner, selection.inventoryId);
197
+ const items = selectedItems(inventory, selection.candidateIds);
198
+ const retry = selection.retryOf ? repo.get("run", selection.retryOf) : void 0;
199
+ if (retry) {
200
+ if (retry.status !== "partial" || retry.inventoryId !== inventory.id) throw new ImportError("invalid_selection", "Select failed items from the original import");
201
+ const failed = new Set(retry.items.filter((i) => i.status === "failed").map((i) => i.candidateId));
202
+ const parents = new Set(items.filter((i) => failed.has(i.id)).map((i) => i.parentId));
203
+ if (!items.some((i) => failed.has(i.id)) || items.some((i) => !failed.has(i.id) && !parents.has(i.id))) throw new ImportError("invalid_selection", "Retry can only include failed items and their project entries");
204
+ }
205
+ await preflight(service, inventory, items, retry);
206
+ const run = {
207
+ id: selection.requestId,
208
+ inventoryId: inventory.id,
209
+ selectionHash,
210
+ source: inventory.source,
211
+ createdAt: Date.now(),
212
+ status: "running",
213
+ skills: 0,
214
+ context: 0,
215
+ projects: 0,
216
+ skipped: 0,
217
+ issues: [],
218
+ items: items.map((item) => ({
219
+ candidateId: item.id,
220
+ kind: item.kind,
221
+ name: item.name,
222
+ status: "pending"
223
+ }))
224
+ };
225
+ repo.save("run", run);
226
+ return execute(service, repo, inventory, run);
227
+ });
228
+ }
229
+ async function recoverSelectionRuns(service, owner) {
230
+ await withImportLock(async () => {
231
+ const repo = new ImportSelectionRepository(owner);
232
+ for (const run of repo.list("run").filter((r) => r.status === "running")) await execute(service, repo, repo.get("inventory", run.inventoryId), run);
233
+ const referenced = new Set(repo.list("run").map((r) => r.inventoryId));
234
+ for (const inventory of repo.list("inventory")) if (!referenced.has(inventory.id) && inventory.expiresAt < Date.now() || inventory.createdAt < Date.now() - 744 * 60 * 6e4) {
235
+ for (const scanId of inventory.scanIds) service.discardUnusedScan(scanId);
236
+ repo.deleteInventory(inventory.id);
237
+ }
238
+ });
239
+ }
240
+ //#endregion
241
+ export { importSelection, listImportSources, recoverSelectionRuns };
@@ -0,0 +1,5 @@
1
+ import { ImportService } from './importService.js';
2
+ import { type ImportTarget } from './types.js';
3
+ export declare function resolveImportTarget(projectId?: string): ImportTarget;
4
+ export declare function createRuntimeImportService(owner: string, refreshSkills?: () => void | Promise<void>, checkTrust?: (root: string) => boolean): ImportService;
5
+ export declare function recoverCapabilityImports(): Promise<void>;
@@ -0,0 +1,61 @@
1
+ import { resolveStateDir } from "../config/paths-state.js";
2
+ import { init_paths, resolveBundledSkillsDir, resolveSkillsDir } from "../config/paths.js";
3
+ import { ProjectService } from "../projects/project-service.js";
4
+ import { resolveWorkspaceSkillsDir } from "../agent/skills/workspace-skills-dir.js";
5
+ import { loadSkills } from "../agent/skills/index.js";
6
+ import { ProjectTrustStore } from "../project-trust/trust-store.js";
7
+ import { ImportError } from "./types.js";
8
+ import { ImportRepository } from "../storage/sqlite/import-repository.js";
9
+ import { canonicalTarget } from "./planner.js";
10
+ import { ImportService } from "./importService.js";
11
+ import { ImportSelectionRepository } from "../storage/sqlite/import-selection-repository.js";
12
+ import { existsSync, realpathSync } from "node:fs";
13
+ //#region src/imports/runtime.ts
14
+ init_paths();
15
+ function resolveImportTarget(projectId) {
16
+ if (!projectId) return { root: canonicalTarget(resolveSkillsDir()) };
17
+ const project = new ProjectService().get(projectId);
18
+ if (!project?.workspaceRoot || !existsSync(project.workspaceRoot)) throw new ImportError("invalid_project", "Project needs an existing workspace");
19
+ return {
20
+ projectId,
21
+ root: canonicalTarget(resolveWorkspaceSkillsDir(project.workspaceRoot))
22
+ };
23
+ }
24
+ function createRuntimeImportService(owner, refreshSkills, checkTrust) {
25
+ return new ImportService({
26
+ owner,
27
+ stateDir: resolveStateDir(),
28
+ refreshSkills,
29
+ isWorkspaceTrusted: (root) => checkTrust ? checkTrust(root) : new ProjectTrustStore().get(root) === true,
30
+ isConnected(candidate, sourceWorkspace) {
31
+ if (!candidate.shared) return false;
32
+ const project = candidate.scope === "project" ? new ProjectService().resolveForWorkspacePath(candidate.location)?.project : void 0;
33
+ const workspaceDir = candidate.scope === "project" ? sourceWorkspace ?? project?.workspaceRoot : void 0;
34
+ return loadSkills({
35
+ workspaceDir,
36
+ workspaceTrust: workspaceDir && (checkTrust ? checkTrust(workspaceDir) : new ProjectTrustStore().get(workspaceDir) === true) ? "trusted" : "untrusted"
37
+ }).skills.some((s) => realpathSync(s.baseDir) === candidate.location);
38
+ },
39
+ validateTarget(target) {
40
+ if (resolveImportTarget(target.projectId).root !== target.root) throw new ImportError("scope_mismatch", "Project target changed; preview again", 409);
41
+ if (target.projectId) {
42
+ const project = new ProjectService().get(target.projectId);
43
+ if (!(checkTrust ? checkTrust(project.workspaceRoot) : new ProjectTrustStore().get(project.workspaceRoot) === true)) throw new ImportError("workspace_untrusted", "Trust this project in project settings before importing", 403);
44
+ }
45
+ },
46
+ reservedNames(target) {
47
+ return loadSkills({
48
+ workspaceDir: target.projectId ? new ProjectService().get(target.projectId)?.workspaceRoot : void 0,
49
+ workspaceTrust: "trusted",
50
+ builtinDir: resolveBundledSkillsDir() ?? void 0
51
+ }).skills.map((s) => s.name);
52
+ }
53
+ });
54
+ }
55
+ async function recoverCapabilityImports() {
56
+ for (const owner of ImportRepository.owners()) await createRuntimeImportService(owner).recoverAndClean();
57
+ const { recoverSelectionRuns } = await import("./productImport.js");
58
+ for (const owner of ImportSelectionRepository.owners()) await recoverSelectionRuns(createRuntimeImportService(owner), owner);
59
+ }
60
+ //#endregion
61
+ export { createRuntimeImportService, recoverCapabilityImports, resolveImportTarget };
@@ -0,0 +1,14 @@
1
+ import { type ImportScan, type ImportSource } from './types.js';
2
+ export { IMPORT_SOURCES } from './sources.js';
3
+ export interface ScanInput {
4
+ source: ImportSource;
5
+ root?: string;
6
+ projectRoot?: string;
7
+ home?: string;
8
+ projectOnly?: boolean;
9
+ budget?: {
10
+ bytes: number;
11
+ files: number;
12
+ };
13
+ }
14
+ export declare function scanLocal(input: ScanInput): Promise<ImportScan>;