@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,77 @@
1
+ import { ImportError } from "./types.js";
2
+ import { isAbsolute, join, relative } from "node:path";
3
+ import { closeSync, constants, fstatSync, lstatSync, openSync, readFileSync, readdirSync, realpathSync } from "node:fs";
4
+ import { createHash } from "node:crypto";
5
+ //#region src/imports/files.ts
6
+ const IMPORT_LIMITS = {
7
+ file: 15 * 1024 * 1024,
8
+ total: 100 * 1024 * 1024,
9
+ count: 5e3,
10
+ depth: 16
11
+ };
12
+ function digest(value) {
13
+ return createHash("sha256").update(JSON.stringify(value)).digest("hex");
14
+ }
15
+ function fileHash(files) {
16
+ return digest([...files].sort((a, b) => a.path.localeCompare(b.path, "en")));
17
+ }
18
+ function inside(root, path) {
19
+ const rel = relative(root, path);
20
+ return rel === "" || !isAbsolute(rel) && rel !== ".." && !rel.startsWith("../") && !rel.startsWith("..\\");
21
+ }
22
+ function safePath(path) {
23
+ return !!path && !path.includes("\\") && !path.includes("\0") && !path.startsWith("/") && path.split("/").every((p) => !!p && p !== "." && p !== ".." && !/[<>:"|?*]/.test(p) && !/[. ]$/.test(p) && !/^(con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(p));
24
+ }
25
+ function readRegularFile(path, limit = IMPORT_LIMITS.file) {
26
+ if (!lstatSync(path).isFile()) throw new ImportError("unsafe_file", "Only regular files are supported");
27
+ const fd = openSync(path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
28
+ try {
29
+ const before = fstatSync(fd);
30
+ if (!before.isFile() || before.size > limit) throw new ImportError("limit_exceeded", "File exceeds import limit", 413);
31
+ const data = readFileSync(fd);
32
+ const after = fstatSync(fd);
33
+ if (data.length > limit || before.size !== after.size || before.mtimeMs !== after.mtimeMs) throw new ImportError("source_changed", "Source changed while reading", 409);
34
+ return data;
35
+ } finally {
36
+ closeSync(fd);
37
+ }
38
+ }
39
+ function readTree(rootPath) {
40
+ const root = realpathSync(rootPath);
41
+ const files = [];
42
+ let total = 0;
43
+ const names = /* @__PURE__ */ new Set();
44
+ function walk(dir, prefix, depth) {
45
+ if (depth > IMPORT_LIMITS.depth) throw new ImportError("limit_exceeded", "Directory is too deep", 413);
46
+ for (const entry of readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
47
+ if ([
48
+ ".git",
49
+ ".DS_Store",
50
+ "__MACOSX"
51
+ ].includes(entry.name)) continue;
52
+ const rel = prefix + entry.name;
53
+ if (!safePath(rel)) throw new ImportError("unsafe_file", "Unsupported file name");
54
+ const key = rel.normalize("NFC").toLowerCase();
55
+ if (names.has(key)) throw new ImportError("conflict", "File names collide on the target filesystem", 409);
56
+ names.add(key);
57
+ if (names.size > IMPORT_LIMITS.count) throw new ImportError("limit_exceeded", "Too many files", 413);
58
+ const path = join(dir, entry.name);
59
+ if (entry.isSymbolicLink() || !inside(root, realpathSync(path))) throw new ImportError("unsafe_file", "Symbolic links must be replaced with local files before importing");
60
+ if (entry.isDirectory()) walk(path, rel + "/", depth + 1);
61
+ else {
62
+ const data = readRegularFile(path);
63
+ total += data.length;
64
+ if (total > IMPORT_LIMITS.total) throw new ImportError("limit_exceeded", "Import is too large", 413);
65
+ files.push({
66
+ path: rel,
67
+ data: data.toString("base64"),
68
+ executable: (lstatSync(path).mode & 73) !== 0
69
+ });
70
+ }
71
+ }
72
+ }
73
+ walk(root, "", 0);
74
+ return files;
75
+ }
76
+ //#endregion
77
+ export { IMPORT_LIMITS, digest, fileHash, inside, readRegularFile, readTree, safePath };
@@ -0,0 +1,50 @@
1
+ import { type ScanInput } from './scanner.js';
2
+ import { type ImportAction, type ImportFile, type ImportJob, type ImportScan, type ImportTarget } from './types.js';
3
+ export interface ImportServiceOptions {
4
+ stateDir: string;
5
+ owner: string;
6
+ validateTarget?: (target: ImportTarget) => void;
7
+ isConnected?: (candidate: ImportScan['candidates'][number], workspaceRoot?: string) => boolean;
8
+ reservedNames?: (target: ImportTarget) => string[];
9
+ isWorkspaceTrusted?: (root: string) => boolean;
10
+ refreshSkills?: () => void | Promise<void>;
11
+ }
12
+ export declare class ImportService {
13
+ private readonly options;
14
+ private readonly repo;
15
+ private readonly root;
16
+ constructor(options: ImportServiceOptions);
17
+ scan(input: ScanInput): Promise<ImportScan>;
18
+ getScan(id: string): ImportScan;
19
+ discardUnusedScan(id: string): void;
20
+ storeScan(scan: ImportScan, workspaceRoot?: string): ImportScan;
21
+ plan(scanId: string, target: ImportTarget, choices: Array<Pick<ImportAction, 'candidateId' | 'operation'> & {
22
+ name?: string;
23
+ }>): import("./types.js").ImportPlan;
24
+ describeSkills(scan: ImportScan, target: ImportTarget): {
25
+ candidate: import("./types.js").ImportCandidate;
26
+ status: import("./types.js").InventoryStatus;
27
+ reason: string;
28
+ targetName: string;
29
+ }[];
30
+ readSnapshot(scanId: string, candidateId: string): ImportFile[];
31
+ jobByKey(key: string): ImportJob;
32
+ validateTarget(target: ImportTarget): void;
33
+ reservedNames(target: ImportTarget): string[];
34
+ isWorkspaceTrusted(root: string): boolean;
35
+ getPlan(id: string): import("./types.js").ImportPlan;
36
+ apply(planId: string, key: string): ImportJob;
37
+ private renameSkill;
38
+ getJob(id: string): ImportJob;
39
+ recoverAndClean(): Promise<void>;
40
+ history(): ImportJob[];
41
+ activate(id: string, candidateIds: string[]): Promise<ImportJob>;
42
+ private publish;
43
+ rollback(id: string, candidateIds: string[]): Promise<ImportJob>;
44
+ private trashPath;
45
+ private recover;
46
+ private targetHash;
47
+ private assertLive;
48
+ private checkSelection;
49
+ private lock;
50
+ }
@@ -0,0 +1,414 @@
1
+ import { createLogger } from "../utils/logger/index.js";
2
+ import { init_logger } from "../utils/logger.js";
3
+ import { init_write_file_atomic, writeTextAtomicSync } from "../infra/write-file-atomic.js";
4
+ import { prepareManagedSkillTempDir } from "../agent/skills/managed-store.js";
5
+ import { ImportError } from "./types.js";
6
+ import { fileHash, readTree } from "./files.js";
7
+ import { parseSkill } from "./compatibility.js";
8
+ import { ImportRepository } from "../storage/sqlite/import-repository.js";
9
+ import { canonicalTarget, createImportPlan, targetInventory, targetRevision } from "./planner.js";
10
+ import { scanLocal } from "./scanner.js";
11
+ import { dirname, join } from "node:path";
12
+ import { existsSync, lstatSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
13
+ import { randomUUID } from "node:crypto";
14
+ import yaml from "js-yaml";
15
+ import lockfile from "proper-lockfile";
16
+ //#region src/imports/importService.ts
17
+ init_write_file_atomic();
18
+ init_logger();
19
+ const log = createLogger("ImportService");
20
+ const RETENTION_MS = 720 * 60 * 60 * 1e3;
21
+ var ImportService = class {
22
+ repo;
23
+ root;
24
+ constructor(options) {
25
+ this.options = options;
26
+ this.repo = new ImportRepository(options.owner);
27
+ this.root = join(canonicalTarget(options.stateDir), "imports");
28
+ mkdirSync(this.root, {
29
+ recursive: true,
30
+ mode: 448
31
+ });
32
+ }
33
+ async scan(input) {
34
+ return this.storeScan(await scanLocal(input), input.projectRoot);
35
+ }
36
+ getScan(id) {
37
+ return this.repo.get("scan", id);
38
+ }
39
+ discardUnusedScan(id) {
40
+ this.lock(() => {
41
+ if (this.repo.jobs(-1).some((job) => job.plan.scanId === id)) return;
42
+ rmSync(join(this.root, id), {
43
+ recursive: true,
44
+ force: true
45
+ });
46
+ this.repo.deleteScan(id);
47
+ });
48
+ }
49
+ storeScan(scan, workspaceRoot) {
50
+ const dir = join(this.root, scan.id);
51
+ mkdirSync(dir, { mode: 448 });
52
+ for (const item of scan.candidates) if (item.kind === "skill" && item.compatibility === "compatible") writeTextAtomicSync(join(dir, `${item.id}.json`), JSON.stringify(item.files));
53
+ const manifest = {
54
+ ...scan,
55
+ candidates: scan.candidates.map((c) => ({
56
+ ...c,
57
+ connected: this.options.isConnected?.(c, workspaceRoot) ?? false,
58
+ files: c.files.map((f) => ({
59
+ ...f,
60
+ data: ""
61
+ }))
62
+ }))
63
+ };
64
+ this.repo.save("scan", manifest);
65
+ return manifest;
66
+ }
67
+ plan(scanId, target, choices) {
68
+ this.options.validateTarget?.(target);
69
+ const plan = createImportPlan(this.getScan(scanId), target, choices, this.options.reservedNames?.(target));
70
+ this.repo.save("plan", plan);
71
+ return plan;
72
+ }
73
+ describeSkills(scan, target) {
74
+ const inventory = targetInventory(target.root);
75
+ const names = new Set([...this.options.reservedNames?.(target) ?? [], ...inventory.flatMap((i) => [i.directory, i.name])].map((n) => n.toLowerCase()));
76
+ const previous = /* @__PURE__ */ new Map();
77
+ for (const job of this.repo.jobs(-1)) {
78
+ if (job.plan.target.root !== canonicalTarget(target.root)) continue;
79
+ const origin = this.getScan(job.plan.scanId);
80
+ for (const item of job.items.filter((i) => i.status === "active")) {
81
+ const candidate = origin.candidates.find((c) => c.id === item.action.candidateId);
82
+ if (candidate?.source === scan.source && !previous.has(candidate.location)) previous.set(candidate.location, item.action.name);
83
+ }
84
+ }
85
+ return scan.candidates.filter((c) => c.kind === "skill").map((candidate) => {
86
+ let status = "ready";
87
+ let reason;
88
+ let targetName = candidate.name;
89
+ if (candidate.connected) status = "existing";
90
+ else if (candidate.compatibility !== "compatible") {
91
+ status = "blocked";
92
+ reason = candidate.findings.join("; ");
93
+ } else {
94
+ const prior = previous.get(candidate.location);
95
+ const priorTarget = prior && inventory.find((i) => i.directory === prior);
96
+ const files = this.readSnapshot(scan.id, candidate.id);
97
+ if (priorTarget) {
98
+ targetName = priorTarget.directory;
99
+ status = fileHash(this.renameSkill(files, targetName)) === priorTarget.hash ? "existing" : "blocked";
100
+ if (status === "blocked") reason = "Previously imported content has changed; existing edits will be kept.";
101
+ } else if (inventory.some((i) => i.name.toLowerCase() === candidate.name.toLowerCase() && i.hash === candidate.hash)) status = "existing";
102
+ else if (names.has(targetName.toLowerCase())) {
103
+ targetName = `${candidate.source}-${candidate.name}`.slice(0, 63);
104
+ if (inventory.some((i) => i.name === targetName && i.hash === fileHash(this.renameSkill(files, targetName)))) status = "existing";
105
+ else if (names.has(targetName.toLowerCase())) {
106
+ status = "blocked";
107
+ reason = "Both the original and product-prefixed names are in use.";
108
+ } else status = "conflict";
109
+ }
110
+ }
111
+ if (status === "ready" || status === "conflict") names.add(targetName.toLowerCase());
112
+ return {
113
+ candidate,
114
+ status,
115
+ reason,
116
+ targetName
117
+ };
118
+ });
119
+ }
120
+ readSnapshot(scanId, candidateId) {
121
+ const candidate = this.getScan(scanId).candidates.find((c) => c.id === candidateId);
122
+ if (!candidate || candidate.kind !== "skill" || candidate.compatibility !== "compatible") throw new ImportError("not_found", "Snapshot not found", 404);
123
+ const files = JSON.parse(readFileSync(join(this.root, scanId, `${candidateId}.json`), "utf8"));
124
+ if (fileHash(files) !== candidate.hash) throw new ImportError("source_changed", "Snapshot changed", 409);
125
+ return files;
126
+ }
127
+ jobByKey(key) {
128
+ return this.repo.byKey(key);
129
+ }
130
+ validateTarget(target) {
131
+ this.options.validateTarget?.(target);
132
+ }
133
+ reservedNames(target) {
134
+ return this.options.reservedNames?.(target) ?? [];
135
+ }
136
+ isWorkspaceTrusted(root) {
137
+ return this.options.isWorkspaceTrusted?.(root) ?? false;
138
+ }
139
+ getPlan(id) {
140
+ return this.repo.get("plan", id);
141
+ }
142
+ apply(planId, key) {
143
+ if (!key || key.length > 128) throw new ImportError("invalid_key", "An idempotency key is required");
144
+ return this.lock(() => {
145
+ const previous = this.repo.byKey(key);
146
+ if (previous) {
147
+ if (previous.plan.id !== planId) throw new ImportError("conflict", "Idempotency key belongs to a different plan", 409);
148
+ return previous;
149
+ }
150
+ const plan = this.getPlan(planId);
151
+ this.options.validateTarget?.(plan.target);
152
+ if (Date.now() > plan.expiresAt || canonicalTarget(plan.target.root) !== plan.target.root || targetRevision(plan.target.root) !== plan.targetRevision) throw new ImportError("plan_stale", "Target changed or plan expired; preview again", 409);
153
+ const job = {
154
+ id: randomUUID(),
155
+ plan,
156
+ createdAt: Date.now(),
157
+ expiresAt: Date.now() + RETENTION_MS,
158
+ items: plan.actions.map((action) => ({
159
+ action,
160
+ status: action.operation === "skip" ? "skipped" : "staged"
161
+ }))
162
+ };
163
+ const dir = join(this.root, job.id);
164
+ mkdirSync(dir, { mode: 448 });
165
+ const scan = this.getScan(plan.scanId);
166
+ try {
167
+ for (const item of job.items) {
168
+ if (item.status === "skipped") continue;
169
+ const candidate = scan.candidates.find((c) => c.id === item.action.candidateId);
170
+ if (candidate.kind !== "skill") continue;
171
+ const files = JSON.parse(readFileSync(join(this.root, scan.id, `${candidate.id}.json`), "utf8"));
172
+ if (fileHash(files) !== candidate.hash) throw new ImportError("source_changed", "Snapshot changed; scan again", 409);
173
+ const rewritten = this.renameSkill(files, item.action.name);
174
+ item.afterHash = fileHash(rewritten);
175
+ writeTextAtomicSync(join(dir, `${candidate.id}.json`), JSON.stringify(rewritten));
176
+ }
177
+ this.repo.save("job", job, key);
178
+ } catch (error) {
179
+ rmSync(dir, {
180
+ recursive: true,
181
+ force: true
182
+ });
183
+ throw error;
184
+ }
185
+ log.info({
186
+ jobId: job.id,
187
+ count: job.items.length
188
+ }, "Import staged");
189
+ return job;
190
+ });
191
+ }
192
+ renameSkill(files, name) {
193
+ return files.map((file) => {
194
+ if (file.path !== "SKILL.md") return file;
195
+ const { metadata, body } = parseSkill(Buffer.from(file.data, "base64").toString("utf8"));
196
+ if (metadata.name === name) return file;
197
+ return {
198
+ ...file,
199
+ data: Buffer.from(`---\n${yaml.dump({
200
+ ...metadata,
201
+ name
202
+ })}---\n${body}`).toString("base64")
203
+ };
204
+ });
205
+ }
206
+ getJob(id) {
207
+ return this.lock(() => this.recover(this.repo.get("job", id)));
208
+ }
209
+ async recoverAndClean() {
210
+ const release = await lockfile.lock(this.root, {
211
+ realpath: false,
212
+ retries: {
213
+ retries: 15,
214
+ minTimeout: 250,
215
+ maxTimeout: 1e3
216
+ }
217
+ });
218
+ try {
219
+ for (const saved of this.repo.jobs(-1)) {
220
+ const job = this.recover(saved);
221
+ if (Date.now() <= job.expiresAt || canonicalTarget(job.plan.target.root) !== job.plan.target.root) continue;
222
+ for (const item of job.items) if (item.backupPath && existsSync(item.backupPath) && this.targetHash(item.backupPath) === item.action.beforeHash) rmSync(item.backupPath, {
223
+ recursive: true,
224
+ force: true
225
+ });
226
+ rmSync(join(this.root, job.id), {
227
+ recursive: true,
228
+ force: true
229
+ });
230
+ }
231
+ for (const scan of this.repo.scansBefore(Date.now() - RETENTION_MS - 1440 * 60 * 1e3)) rmSync(join(this.root, scan.id), {
232
+ recursive: true,
233
+ force: true
234
+ });
235
+ } finally {
236
+ await release();
237
+ }
238
+ }
239
+ history() {
240
+ return this.lock(() => this.repo.jobs().map((job) => this.recover(job)));
241
+ }
242
+ async activate(id, candidateIds) {
243
+ const job = this.lock(() => {
244
+ const current = this.recover(this.repo.get("job", id));
245
+ this.assertLive(current);
246
+ this.options.validateTarget?.(current.plan.target);
247
+ const scan = this.getScan(current.plan.scanId);
248
+ this.checkSelection(current, candidateIds);
249
+ for (const candidateId of candidateIds) {
250
+ const item = current.items.find((i) => i.action.candidateId === candidateId);
251
+ if (item.status === "active") continue;
252
+ if (!["staged", "failed"].includes(item.status)) throw new ImportError("invalid_state", "Item cannot be activated");
253
+ if (scan.candidates.find((c) => c.id === candidateId).kind !== "skill") throw new ImportError("needs_setup", "Review the MCP draft in connection settings before configuring it");
254
+ try {
255
+ this.publish(current, item);
256
+ } catch (error) {
257
+ if (item.status !== "publishing") item.status = "failed";
258
+ item.error = error instanceof ImportError ? error.message : "Publishing failed; retry after checking target permissions";
259
+ }
260
+ this.repo.save("job", current);
261
+ }
262
+ return current;
263
+ });
264
+ await this.options.refreshSkills?.();
265
+ return job;
266
+ }
267
+ publish(job, item) {
268
+ const root = job.plan.target.root;
269
+ if (canonicalTarget(root) !== root) throw new ImportError("unsafe_target", "Target location changed", 409);
270
+ const dest = join(root, item.action.name);
271
+ if (this.targetHash(dest) !== item.action.beforeHash) throw new ImportError("plan_stale", "Target changed after preview", 409);
272
+ const others = this.options.reservedNames?.(job.plan.target) ?? [];
273
+ if (item.action.operation !== "replace" && others.some((name) => name.toLowerCase() === item.action.name.toLowerCase())) throw new ImportError("conflict", "Skill name is now in use", 409);
274
+ const files = JSON.parse(readFileSync(join(this.root, job.id, `${item.action.candidateId}.json`), "utf8"));
275
+ if (fileHash(files) !== item.afterHash) throw new ImportError("source_changed", "Staged files changed", 409);
276
+ const { tempDir } = prepareManagedSkillTempDir(item.action.name, root);
277
+ try {
278
+ for (const file of files) {
279
+ const path = join(tempDir, file.path);
280
+ mkdirSync(dirname(path), {
281
+ recursive: true,
282
+ mode: 448
283
+ });
284
+ writeFileSync(path, Buffer.from(file.data, "base64"), {
285
+ mode: file.executable ? 448 : 384,
286
+ flag: "wx"
287
+ });
288
+ }
289
+ if (fileHash(readTree(tempDir)) !== item.afterHash) throw new ImportError("source_changed", "Staged verification failed", 409);
290
+ item.backupPath = join(root, `.tmp-import-backup-${job.id}-${item.action.candidateId}`);
291
+ item.status = "publishing";
292
+ delete item.error;
293
+ this.repo.save("job", job);
294
+ if (existsSync(dest)) renameSync(dest, item.backupPath);
295
+ renameSync(tempDir, dest);
296
+ item.status = "active";
297
+ } catch (error) {
298
+ if (item.backupPath && existsSync(item.backupPath) && !existsSync(dest)) renameSync(item.backupPath, dest);
299
+ throw error;
300
+ } finally {
301
+ rmSync(tempDir, {
302
+ recursive: true,
303
+ force: true
304
+ });
305
+ }
306
+ }
307
+ async rollback(id, candidateIds) {
308
+ const job = this.lock(() => {
309
+ const current = this.recover(this.repo.get("job", id));
310
+ this.assertLive(current);
311
+ this.checkSelection(current, candidateIds);
312
+ for (const candidateId of candidateIds) {
313
+ const item = current.items.find((i) => i.action.candidateId === candidateId);
314
+ if (["rolled_back", "skipped"].includes(item.status)) continue;
315
+ if (item.status === "staged" || item.status === "failed") {
316
+ item.status = "rolled_back";
317
+ this.repo.save("job", current);
318
+ continue;
319
+ }
320
+ const dest = join(current.plan.target.root, item.action.name);
321
+ if (canonicalTarget(current.plan.target.root) !== current.plan.target.root || this.targetHash(dest) !== item.afterHash) {
322
+ item.error = "Target changed after import; rollback would overwrite your edits";
323
+ this.repo.save("job", current);
324
+ continue;
325
+ }
326
+ if (item.action.beforeHash !== null && (!item.backupPath || this.targetHash(item.backupPath) !== item.action.beforeHash)) {
327
+ item.error = "Backup is missing or changed; manual review required";
328
+ this.repo.save("job", current);
329
+ continue;
330
+ }
331
+ item.status = "rolling_back";
332
+ this.repo.save("job", current);
333
+ const trash = this.trashPath(current, item);
334
+ renameSync(dest, trash);
335
+ if (item.backupPath && existsSync(item.backupPath)) {
336
+ if (this.targetHash(item.backupPath) !== item.action.beforeHash) throw new ImportError("rollback_conflict", "Backup changed; manual review required", 409);
337
+ renameSync(item.backupPath, dest);
338
+ }
339
+ item.status = "rolled_back";
340
+ delete item.error;
341
+ this.repo.save("job", current);
342
+ rmSync(trash, {
343
+ recursive: true,
344
+ force: true
345
+ });
346
+ }
347
+ return current;
348
+ });
349
+ await this.options.refreshSkills?.();
350
+ return job;
351
+ }
352
+ trashPath(job, item) {
353
+ return join(job.plan.target.root, `.tmp-import-undo-${job.id}-${item.action.candidateId}`);
354
+ }
355
+ recover(job) {
356
+ for (const item of job.items) {
357
+ if (!["publishing", "rolling_back"].includes(item.status)) continue;
358
+ const root = job.plan.target.root;
359
+ if (canonicalTarget(root) !== root) throw new ImportError("unsafe_target", "Target location changed", 409);
360
+ const dest = join(root, item.action.name);
361
+ const hash = this.targetHash(dest);
362
+ if (item.status === "publishing") if (hash === item.afterHash) item.status = "active";
363
+ else if (hash === item.action.beforeHash) item.status = "staged";
364
+ else if (!hash && item.action.beforeHash !== null && item.backupPath && this.targetHash(item.backupPath) === item.action.beforeHash) {
365
+ renameSync(item.backupPath, dest);
366
+ item.status = "staged";
367
+ } else {
368
+ item.status = "failed";
369
+ item.error = "Publication interrupted and target changed; manual review required";
370
+ }
371
+ else {
372
+ const trash = this.trashPath(job, item);
373
+ if (!hash && item.action.beforeHash !== null && item.backupPath && this.targetHash(item.backupPath) === item.action.beforeHash) renameSync(item.backupPath, dest);
374
+ else if (hash === item.afterHash && !existsSync(trash)) {
375
+ item.status = "active";
376
+ this.repo.save("job", job);
377
+ continue;
378
+ } else if (hash !== item.action.beforeHash) {
379
+ item.error = "Rollback interrupted; target needs review";
380
+ this.repo.save("job", job);
381
+ continue;
382
+ }
383
+ item.status = "rolled_back";
384
+ rmSync(trash, {
385
+ recursive: true,
386
+ force: true
387
+ });
388
+ }
389
+ this.repo.save("job", job);
390
+ }
391
+ return job;
392
+ }
393
+ targetHash(path) {
394
+ if (!existsSync(path)) return null;
395
+ if (canonicalTarget(path) !== path || lstatSync(path).isSymbolicLink()) throw new ImportError("unsafe_target", "Target is a symbolic link", 409);
396
+ return fileHash(readTree(path));
397
+ }
398
+ assertLive(job) {
399
+ if (Date.now() > job.expiresAt) throw new ImportError("expired", "Import recovery period has expired", 409);
400
+ }
401
+ checkSelection(job, ids) {
402
+ if (!ids.length || new Set(ids).size !== ids.length || ids.some((id) => !job.items.some((i) => i.action.candidateId === id))) throw new ImportError("invalid_selection", "Select valid import items");
403
+ }
404
+ lock(run) {
405
+ const release = lockfile.lockSync(this.root, { realpath: false });
406
+ try {
407
+ return run();
408
+ } finally {
409
+ release();
410
+ }
411
+ }
412
+ };
413
+ //#endregion
414
+ export { ImportService };
@@ -0,0 +1,11 @@
1
+ import type { ImportService } from './importService.js';
2
+ import { type ImportSource, type ImportInventory, type StoredInventory, type ImportCandidate } from './types.js';
3
+ export declare function contextKey(candidate: ImportCandidate): string;
4
+ export declare function existingContext(candidate: ImportCandidate, projectId?: string): import("../knowledge-memory/domain.js").KnowledgeItem;
5
+ export declare function publicInventory(inventory: StoredInventory): ImportInventory;
6
+ export declare function getLiveInventory(owner: string, id: string): StoredInventory;
7
+ export declare function scanProduct(service: ImportService, source: ImportSource, owner?: string): Promise<ImportInventory>;
8
+ export declare function previewInventoryItem(service: ImportService, owner: string, inventoryId: string, itemId: string): {
9
+ text: string;
10
+ truncated: boolean;
11
+ };