@xopcai/xopc 0.0.222 → 0.0.223

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 (199) 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-NItGQmvR.js → activity-store--GBciH10.js} +1 -1
  4. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-DsR_2U9-.js → agent-avatar-dicebear-C93T3FLD.js} +1 -1
  5. package/dist/gateway/static/root/assets/agents-Ba4XF0Im.js +68 -0
  6. package/dist/gateway/static/root/assets/{api-53ZXsuyr.js → api-HoIz_ija.js} +1 -1
  7. package/dist/gateway/static/root/assets/{app-management-settings-panel-BCzVX09V.js → app-management-settings-panel-Bhl8Xluk.js} +1 -1
  8. package/dist/gateway/static/root/assets/{appearance-settings-DgSpwqQI.js → appearance-settings-CywEN-kG.js} +1 -1
  9. package/dist/gateway/static/root/assets/apps-page-BZY09nuJ.js +1 -0
  10. package/dist/gateway/static/root/assets/{archive-plugin-D8o8CP9Y.js → archive-plugin-CteCVsBP.js} +1 -1
  11. package/dist/gateway/static/root/assets/{attachment-load-BQHcAR6J.js → attachment-load-CbNE_EPd.js} +1 -1
  12. package/dist/gateway/static/root/assets/{automations-page-CswK3HFb.js → automations-page-Bcqk6tOx.js} +1 -1
  13. package/dist/gateway/static/root/assets/automations-workspace-DtI6vsMf.js +6 -0
  14. package/dist/gateway/static/root/assets/{binary-plugins-BlpyTJ1b.js → binary-plugins-M_i3WS5M.js} +1 -1
  15. package/dist/gateway/static/root/assets/block-editor-eg9LEH2o.js +184 -0
  16. package/dist/gateway/static/root/assets/browser-settings-page-kKwbvzjj.js +8 -0
  17. package/dist/gateway/static/root/assets/{browser-workflow-inputs-Bi6RSSpD.js → browser-workflow-inputs-1UZ0vnIy.js} +1 -1
  18. package/dist/gateway/static/root/assets/{browser-workflows-page-CZNOOQY4.js → browser-workflows-page-D8ATQqZ5.js} +2 -2
  19. package/dist/gateway/static/root/assets/capabilities-settings-panel-CZRcis8B.js +7 -0
  20. package/dist/gateway/static/root/assets/{capability-presets-api-pC2fno7r.js → capability-presets-api-yNqTIuyd.js} +1 -1
  21. package/dist/gateway/static/root/assets/capability-presets-settings-panel-CgdRkwnk.js +3 -0
  22. package/dist/gateway/static/root/assets/{channel-recipient-api-B5Szgu_z.js → channel-recipient-api-krezGsKc.js} +1 -1
  23. package/dist/gateway/static/root/assets/channels-settings-DETCY0Ku.js +1 -0
  24. package/dist/gateway/static/root/assets/channels-status-swr-DMxB7slL.js +1 -0
  25. package/dist/gateway/static/root/assets/{chat-terminal-dock-2fpzufzt.js → chat-terminal-dock-DQ5bB4Su.js} +2 -2
  26. package/dist/gateway/static/root/assets/connectors-page-D9D92u28.js +2 -0
  27. package/dist/gateway/static/root/assets/{cron-expression-VePLfPUR.js → cron-expression-CEwPkdhh.js} +1 -1
  28. package/dist/gateway/static/root/assets/{date-picker-CJegmPTJ.js → date-picker-DDAxTir8.js} +1 -1
  29. package/dist/gateway/static/root/assets/{dependency-picker-B2o9unBk.js → dependency-picker-CYVoSZwI.js} +1 -1
  30. package/dist/gateway/static/root/assets/{desktop-pet-BH4Np-ay.js → desktop-pet-CrNKfW_c.js} +1 -1
  31. package/dist/gateway/static/root/assets/{desktop-pet-settings-Duu3ctxD.js → desktop-pet-settings-C06PaB46.js} +1 -1
  32. package/dist/gateway/static/root/assets/{directory-picker-path-field-BBCUZOg2.js → directory-picker-path-field--D7laeLF.js} +1 -1
  33. package/dist/gateway/static/root/assets/dist-DaxPZj9e.js +1 -0
  34. package/dist/gateway/static/root/assets/{dist-DY4Af0fY.js → dist-J_ySZvz0.js} +1 -1
  35. package/dist/gateway/static/root/assets/{extension-debug-page-DS_o3F97.js → extension-debug-page-Dn4Px9KQ.js} +1 -1
  36. package/dist/gateway/static/root/assets/{extension-page-BTLBZdCq.js → extension-page-DzLf3fMQ.js} +1 -1
  37. package/dist/gateway/static/root/assets/{extension-settings-page-KTGrYbgk.js → extension-settings-page-DIchwtvB.js} +1 -1
  38. package/dist/gateway/static/root/assets/floating-ui.dom-B7uQOZZW.js +1 -0
  39. package/dist/gateway/static/root/assets/{gateway-config-api-D3YJVfzY.js → gateway-config-api-SmTGLhqK.js} +1 -1
  40. package/dist/gateway/static/root/assets/{gateway-config-swr-BzTbnu4O.js → gateway-config-swr-yUxMMVjK.js} +1 -1
  41. package/dist/gateway/static/root/assets/{gateway-settings-B8--s3yC.js → gateway-settings-yCDkOYzA.js} +1 -1
  42. package/dist/gateway/static/root/assets/{gateway-startup-retry-B2zpkNvi.js → gateway-startup-retry-9mrsL84j.js} +1 -1
  43. package/dist/gateway/static/root/assets/heartbeat-settings-CquIULHX.js +1 -0
  44. package/dist/gateway/static/root/assets/home-page-COSGBh4c.js +1 -0
  45. package/dist/gateway/static/root/assets/{image-generation-api-BXRjz-iY.js → image-generation-api-CgFAGDi8.js} +1 -1
  46. package/dist/gateway/static/root/assets/index-5XkDWH_Q.css +2 -0
  47. package/dist/gateway/static/root/assets/{index-jqDCfet4.js → index-wnDPGRTi.js} +47 -47
  48. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-D-Ib-suu.js → keyboard-shortcuts-settings-DI2To7yl.js} +1 -1
  49. package/dist/gateway/static/root/assets/local-app-workbench-page-CAF5SZXv.js +2 -0
  50. package/dist/gateway/static/root/assets/{local-apps-page-Bsy1lvAc.js → local-apps-page-CSEJsQ1F.js} +1 -1
  51. package/dist/gateway/static/root/assets/{locale-store-iKjqemWG.js → locale-store-CPylQjH9.js} +1 -1
  52. package/dist/gateway/static/root/assets/logs-page-D1KFPHL1.js +2 -0
  53. package/dist/gateway/static/root/assets/{management-settings-D-akydG4.js → management-settings-CgyaIyuk.js} +1 -1
  54. package/dist/gateway/static/root/assets/{markdown-split-DHH4Cu2i.js → markdown-split-paG9qap9.js} +1 -1
  55. package/dist/gateway/static/root/assets/markdown-view-CUsob4Pz.js +69 -0
  56. package/dist/gateway/static/root/assets/{media-plugins-BwHtUXkj.js → media-plugins-0Q-c_QIy.js} +1 -1
  57. package/dist/gateway/static/root/assets/messages-B82nsi9k.js +3 -0
  58. package/dist/gateway/static/root/assets/notes-api-CfKZYrBC.js +1 -0
  59. package/dist/gateway/static/root/assets/{notes-page-BxM1p6hz.js → notes-page-Bvb-ycX_.js} +1 -1
  60. package/dist/gateway/static/root/assets/notes-workbench-DelxGBof.js +4 -0
  61. package/dist/gateway/static/root/assets/oauth-authorization-window-Cdolr2wE.js +1 -0
  62. package/dist/gateway/static/root/assets/{popover-select-BzdnTy3h.js → popover-select-CccGri5Y.js} +1 -1
  63. package/dist/gateway/static/root/assets/{product-open-page-DrfWH1ir.js → product-open-page-DrvBwAns.js} +1 -1
  64. package/dist/gateway/static/root/assets/project-detail-page-C2EnYXEQ.js +1 -0
  65. package/dist/gateway/static/root/assets/projects-page-BiMPU6Oa.js +1 -0
  66. package/dist/gateway/static/root/assets/{registry-api-Cx3JxXSL.js → registry-api-B_5D8epF.js} +1 -1
  67. package/dist/gateway/static/root/assets/remote-access-hub-CpbNivVs.js +1 -0
  68. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-DF7syQRd.js → runtime-tools-settings-panel-DKjdN4Lr.js} +1 -1
  69. package/dist/gateway/static/root/assets/{schema-form-BQ_qRIsM.js → schema-form-DskqaNqC.js} +1 -1
  70. package/dist/gateway/static/root/assets/sessions-page-BxKtzsKI.js +1 -0
  71. package/dist/gateway/static/root/assets/settings-advanced-gate-BfBKMMUx.js +1 -0
  72. package/dist/gateway/static/root/assets/{settings-form-section-BBHGOQHU.js → settings-form-section-CZlMvHA9.js} +1 -1
  73. package/dist/gateway/static/root/assets/{settings-loading-skeleton-CiwjP2-b.js → settings-loading-skeleton-DS6lcAg9.js} +1 -1
  74. package/dist/gateway/static/root/assets/{settings-page-Rl9waapk.js → settings-page-oE5J9tsV.js} +1 -1
  75. package/dist/gateway/static/root/assets/setup-status-panel-C7oFCIqD.js +1 -0
  76. package/dist/gateway/static/root/assets/share-preview-page-CPFAj8NE.js +2 -0
  77. package/dist/gateway/static/root/assets/shares-settings-B46DWZK4.js +1 -0
  78. package/dist/gateway/static/root/assets/skill-reload-api-CyKolUZ-.js +1 -0
  79. package/dist/gateway/static/root/assets/{skills-page-YbtR_zEU.js → skills-page-Bh4gFi8e.js} +1 -1
  80. package/dist/gateway/static/root/assets/{skills-page.utils-DTmx2Zza.js → skills-page.utils-BQaUWP9z.js} +1 -1
  81. package/dist/gateway/static/root/assets/{system-settings-panel-BZDJcKdS.js → system-settings-panel-COAZoh_O.js} +1 -1
  82. package/dist/gateway/static/root/assets/task-detail-page-CXkhprf-.js +1 -0
  83. package/dist/gateway/static/root/assets/{theme-store-BOD2D0dJ.js → theme-store-BN-32Fz5.js} +1 -1
  84. package/dist/gateway/static/root/assets/{use-autosave-Be9aR9qA.js → use-autosave-BAkToU15.js} +1 -1
  85. package/dist/gateway/static/root/assets/user-context-page-Br5wgARM.js +1 -0
  86. package/dist/gateway/static/root/assets/{voice-api-key-field-IMc3Ua6C.js → voice-api-key-field-CC4z9-ux.js} +1 -1
  87. package/dist/gateway/static/root/assets/{work-discovery-overlay-TC-xQenr.js → work-discovery-overlay-dLVUYSSE.js} +1 -1
  88. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-B2auxcRv.js → workflow-run-setup-panel-BYU5-0KF.js} +2 -2
  89. package/dist/gateway/static/root/assets/workflows-page-CJ24a1HM.js +5 -0
  90. package/dist/gateway/static/root/index.html +15 -14
  91. package/dist/package.js +1 -1
  92. package/dist/src/agent/agent-instance-gateway.d.ts +3 -0
  93. package/dist/src/agent/agent-manager.d.ts +3 -0
  94. package/dist/src/agent/agent-manager.js +30 -24
  95. package/dist/src/agent/embedded/index.d.ts +1 -1
  96. package/dist/src/agent/embedded/index.js +2 -2
  97. package/dist/src/agent/embedded/run-for-session.js +101 -62
  98. package/dist/src/agent/embedded/run-turn.js +56 -28
  99. package/dist/src/agent/embedded/runs.d.ts +16 -29
  100. package/dist/src/agent/embedded/runs.js +44 -65
  101. package/dist/src/agent/embedded/types.d.ts +4 -0
  102. package/dist/src/agent/orchestration/agent-run-supervisor.d.ts +28 -0
  103. package/dist/src/agent/orchestration/agent-run-supervisor.js +49 -0
  104. package/dist/src/agent/orchestration/agent-turn-policy.d.ts +18 -0
  105. package/dist/src/agent/orchestration/agent-turn-policy.js +36 -0
  106. package/dist/src/agent/orchestration/llm-turn-retry.d.ts +5 -0
  107. package/dist/src/agent/orchestration/llm-turn-retry.js +34 -4
  108. package/dist/src/agent/orchestration/run-agent-turn-with-timeout.d.ts +10 -1
  109. package/dist/src/agent/orchestration/run-agent-turn-with-timeout.js +25 -4
  110. package/dist/src/agent/service/async-queue.d.ts +16 -3
  111. package/dist/src/agent/service/async-queue.js +29 -11
  112. package/dist/src/agent/service/process-direct-streaming.js +27 -4
  113. package/dist/src/agent/service.js +1 -0
  114. package/dist/src/agent/tools/exec-command.d.ts +2 -0
  115. package/dist/src/agent/tools/exec-command.js +70 -7
  116. package/dist/src/auth/oauth/index.js +3 -3
  117. package/dist/src/auth/oauth/registry.d.ts +2 -0
  118. package/dist/src/auth/oauth/registry.js +20 -12
  119. package/dist/src/auth/oauth/xopc-cloud.d.ts +1 -0
  120. package/dist/src/auth/oauth/xopc-cloud.js +32 -16
  121. package/dist/src/cli/commands/tunnel.js +4 -5
  122. package/dist/src/config/agent-profile.js +1 -1
  123. package/dist/src/config/schema.d.ts +18 -0
  124. package/dist/src/config/schema.js +19 -1
  125. package/dist/src/gateway/agents-admin.js +1 -1
  126. package/dist/src/gateway/hono/app.js +2 -1
  127. package/dist/src/gateway/hono/oauth-async.d.ts +1 -0
  128. package/dist/src/gateway/hono/oauth-async.js +11 -4
  129. package/dist/src/gateway/hono/routes/config.js +10 -0
  130. package/dist/src/gateway/hono/routes/host-fs.js +40 -1
  131. package/dist/src/gateway/hono/routes/media.js +33 -1
  132. package/dist/src/gateway/hono/routes/notes.js +162 -7
  133. package/dist/src/gateway/hono/routes/shares.js +141 -6
  134. package/dist/src/gateway/hono/routes/tunnel.js +47 -10
  135. package/dist/src/gateway/hono/routes/voice.d.ts +2 -6
  136. package/dist/src/gateway/hono/routes/voice.js +20 -4
  137. package/dist/src/gateway/hono/routes/workflows.js +2 -2
  138. package/dist/src/gateway/service.js +6 -6
  139. package/dist/src/notes/service.d.ts +6 -2
  140. package/dist/src/notes/service.js +36 -4
  141. package/dist/src/providers/index.js +2 -2
  142. package/dist/src/share/note-share-service.d.ts +67 -0
  143. package/dist/src/share/note-share-service.js +283 -0
  144. package/dist/src/share/share-config.js +32 -3
  145. package/dist/src/share/share-landing.d.ts +5 -1
  146. package/dist/src/share/share-landing.js +28 -1
  147. package/dist/src/share/share-store.d.ts +34 -5
  148. package/dist/src/share/share-store.js +125 -19
  149. package/dist/src/share/share-thumbnail.js +4 -2
  150. package/dist/src/share/share-types.d.ts +52 -14
  151. package/dist/src/share/share-types.js +9 -0
  152. package/dist/src/share/share-zip.d.ts +2 -2
  153. package/dist/src/tunnel/env.d.ts +12 -6
  154. package/dist/src/tunnel/env.js +34 -26
  155. package/dist/src/tunnel/gateway-lifecycle.js +2 -13
  156. package/dist/src/tunnel/index.d.ts +1 -1
  157. package/dist/src/tunnel/index.js +2 -2
  158. package/dist/src/tunnel/tunnel-audit.d.ts +1 -1
  159. package/dist/src/tunnel/tunnel-config.d.ts +1 -1
  160. package/dist/src/tunnel/tunnel-config.js +9 -4
  161. package/dist/src/tunnel/tunnel-service.d.ts +1 -1
  162. package/dist/src/tunnel/tunnel-service.js +3 -0
  163. package/dist/src/tunnel/well-known.d.ts +1 -1
  164. package/dist/src/tunnel/well-known.js +1 -1
  165. package/dist/src/tunnel/xopc-cloud-registration.d.ts +9 -0
  166. package/dist/src/tunnel/xopc-cloud-registration.js +42 -0
  167. package/package.json +3 -3
  168. package/dist/gateway/static/root/assets/agents-DijFbC4G.js +0 -68
  169. package/dist/gateway/static/root/assets/apps-page-BFAmf9hq.js +0 -1
  170. package/dist/gateway/static/root/assets/automations-workspace-B8us5JBf.js +0 -6
  171. package/dist/gateway/static/root/assets/block-editor-C3mwGsms.js +0 -184
  172. package/dist/gateway/static/root/assets/browser-settings-page-BeBd17-3.js +0 -8
  173. package/dist/gateway/static/root/assets/capabilities-settings-panel-Bv0z3KRq.js +0 -7
  174. package/dist/gateway/static/root/assets/capability-presets-settings-panel-BczD-pWE.js +0 -3
  175. package/dist/gateway/static/root/assets/channels-settings-2KQYq6xl.js +0 -1
  176. package/dist/gateway/static/root/assets/channels-status-swr-ByRbxC6z.js +0 -1
  177. package/dist/gateway/static/root/assets/connectors-page-PB1boaNy.js +0 -2
  178. package/dist/gateway/static/root/assets/dist-C230izr8.js +0 -1
  179. package/dist/gateway/static/root/assets/heartbeat-settings-5YQ5BUwp.js +0 -1
  180. package/dist/gateway/static/root/assets/home-page-DfIVWJl3.js +0 -1
  181. package/dist/gateway/static/root/assets/index-BXGC3VFm.css +0 -2
  182. package/dist/gateway/static/root/assets/local-app-workbench-page-CAb0odzu.js +0 -2
  183. package/dist/gateway/static/root/assets/logs-page-CFxKbDZl.js +0 -2
  184. package/dist/gateway/static/root/assets/markdown-view-DYkxMtJA.js +0 -69
  185. package/dist/gateway/static/root/assets/messages-kx5W39dO.js +0 -3
  186. package/dist/gateway/static/root/assets/notes-api-DJizatxh.js +0 -1
  187. package/dist/gateway/static/root/assets/notes-workbench-BGjEC89c.js +0 -4
  188. package/dist/gateway/static/root/assets/project-detail-page-Dj9kEQwg.js +0 -1
  189. package/dist/gateway/static/root/assets/projects-page-CBRy5Ezz.js +0 -1
  190. package/dist/gateway/static/root/assets/remote-access-hub-DSwz9RBh.js +0 -1
  191. package/dist/gateway/static/root/assets/sessions-page-WtattyVX.js +0 -1
  192. package/dist/gateway/static/root/assets/settings-advanced-gate-CNAz_q1F.js +0 -1
  193. package/dist/gateway/static/root/assets/setup-status-panel-CNo4Ajhn.js +0 -1
  194. package/dist/gateway/static/root/assets/share-preview-page-B7PI3yZ6.js +0 -2
  195. package/dist/gateway/static/root/assets/shares-settings-DpOH0zWB.js +0 -1
  196. package/dist/gateway/static/root/assets/skill-reload-api-BOf4xCat.js +0 -1
  197. package/dist/gateway/static/root/assets/task-detail-page-DrMX5Yhj.js +0 -1
  198. package/dist/gateway/static/root/assets/user-context-page-ewIK2ddC.js +0 -1
  199. package/dist/gateway/static/root/assets/workflows-page-Bdn82kFQ.js +0 -5
@@ -7,10 +7,10 @@ import { messagesToClientHistory } from "../../../session/client-history.js";
7
7
  import "../../../workflows/domain/index.js";
8
8
  import { resolveWorkflowRunArtifactsDir } from "../../../workflows/store/paths.js";
9
9
  import { WorkflowDraftService } from "../../../workflows/draft/workflow-draft-service.js";
10
- import "../../../workflows/draft/index.js";
11
- import { ProjectWorkflowPresetRepository } from "../../../workflows/project-presets/project-workflow-preset-repository.js";
12
10
  import { WorkflowDraftConflictError, WorkflowDraftStore } from "../../../workflows/authoring/workflow-draft-store.js";
13
11
  import "../../../workflows/authoring/index.js";
12
+ import "../../../workflows/draft/index.js";
13
+ import { ProjectWorkflowPresetRepository } from "../../../workflows/project-presets/project-workflow-preset-repository.js";
14
14
  import { basename, join } from "node:path";
15
15
  import { readFile, stat } from "node:fs/promises";
16
16
  //#region src/gateway/hono/routes/workflows.ts
@@ -50,18 +50,18 @@ import { registerClarifyBridge } from "./clarify-runtime.js";
50
50
  import { ChannelManager } from "../channels/manager.js";
51
51
  import { getWorkspacePath } from "../config/workspace-path-helpers.js";
52
52
  import "../config/index.js";
53
- import { DiscussionService } from "../discussions/service.js";
54
- import { DiscussionOrganizer } from "../discussions/organizer.js";
55
- import { DiscussionLiveWorker } from "../discussions/live-worker.js";
56
- import { DiscussionOrganizerWorker } from "../discussions/organizer-worker.js";
57
- import { DiscussionSealer } from "../discussions/sealer.js";
58
- import "../discussions/index.js";
59
53
  import { ManagedComposioEventPoller } from "../connectors/composio-managed-events.js";
60
54
  import { startConnectedKnowledgeCoordinator } from "../knowledge/coordinator.js";
61
55
  import "../knowledge/index.js";
62
56
  import { startConnectorLearningCoordinator } from "../connectors/learning-coordinator.js";
63
57
  import { ConnectedSourceChangePublisher } from "../connectors/source-change-publisher.js";
64
58
  import { startConnectorSupervisor } from "../connectors/supervisor.js";
59
+ import { DiscussionService } from "../discussions/service.js";
60
+ import { DiscussionOrganizer } from "../discussions/organizer.js";
61
+ import { DiscussionLiveWorker } from "../discussions/live-worker.js";
62
+ import { DiscussionOrganizerWorker } from "../discussions/organizer-worker.js";
63
+ import { DiscussionSealer } from "../discussions/sealer.js";
64
+ import "../discussions/index.js";
65
65
  import { EndpointToolRuntime } from "../endpoint-tools/runtime.js";
66
66
  import "../endpoint-tools/index.js";
67
67
  import { assertGatewayAuthConfigured, extractToken, resolveGatewayAuth, validateToken } from "./auth.js";
@@ -6,11 +6,14 @@ export declare class NotesService {
6
6
  private readonly store;
7
7
  private lastSnapshotAt;
8
8
  private quickCapturesInFlight;
9
+ private noteCapturesInFlight;
10
+ private noteAttachmentsInFlight;
9
11
  constructor(store?: NotesStore);
10
12
  initialize(): Promise<void>;
11
13
  quickCapture(markdown: string, source: CaptureSource, idempotencyKey?: string): Promise<Note>;
12
14
  private createQuickCapture;
13
- createNote(params: CreateNoteParams): Promise<Note>;
15
+ createNote(params: CreateNoteParams, idempotencyKey?: string): Promise<Note>;
16
+ private createNoteWithId;
14
17
  getNote(id: string): Promise<Note | null>;
15
18
  updateNote(id: string, patch: Partial<Note>, trigger?: SnapshotTrigger): Promise<Note | null>;
16
19
  private reconcileAttachments;
@@ -49,7 +52,8 @@ export declare class NotesService {
49
52
  mimeType: string;
50
53
  duration?: number;
51
54
  retainWithoutReference?: boolean;
52
- }): Promise<NoteAttachment | null>;
55
+ }, idempotencyKey?: string): Promise<NoteAttachment | null>;
56
+ private addAttachmentWithId;
53
57
  removeAttachment(noteId: string, attachmentId: string): Promise<boolean>;
54
58
  getAttachmentPath(noteId: string, attachmentId: string): Promise<{
55
59
  filePath: string;
@@ -19,6 +19,12 @@ const SNAPSHOT_THROTTLE_MS = 6e4;
19
19
  function quickCaptureId(idempotencyKey) {
20
20
  return `capture_${createHash("sha256").update(`xopc:quick-capture:${idempotencyKey}`).digest("hex").slice(0, 32)}`;
21
21
  }
22
+ function noteCaptureId(idempotencyKey) {
23
+ return `note_capture_${createHash("sha256").update(`xopc:note-capture:${idempotencyKey}`).digest("hex").slice(0, 32)}`;
24
+ }
25
+ function noteAttachmentId(noteId, idempotencyKey) {
26
+ return `attachment_${createHash("sha256").update(`xopc:note-attachment:${noteId}:${idempotencyKey}`).digest("hex").slice(0, 32)}`;
27
+ }
22
28
  function inferKind(markdown, hasAttachments, attachments) {
23
29
  if (hasAttachments && attachments?.length && attachments.every((item) => item.type === "audio")) return "voice";
24
30
  if (hasAttachments) return "media";
@@ -146,6 +152,8 @@ async function buildAiCatalysisReport(note, config) {
146
152
  var NotesService = class {
147
153
  lastSnapshotAt = /* @__PURE__ */ new Map();
148
154
  quickCapturesInFlight = /* @__PURE__ */ new Map();
155
+ noteCapturesInFlight = /* @__PURE__ */ new Map();
156
+ noteAttachmentsInFlight = /* @__PURE__ */ new Map();
149
157
  constructor(store = new NotesStore()) {
150
158
  this.store = store;
151
159
  }
@@ -204,8 +212,20 @@ var NotesService = class {
204
212
  }, "Quick capture");
205
213
  return note;
206
214
  }
207
- async createNote(params) {
208
- const id = randomUUID();
215
+ async createNote(params, idempotencyKey) {
216
+ const id = idempotencyKey ? noteCaptureId(idempotencyKey) : randomUUID();
217
+ if (idempotencyKey) {
218
+ const existing = await this.store.getNote(id);
219
+ if (existing) return existing;
220
+ const inFlight = this.noteCapturesInFlight.get(id);
221
+ if (inFlight) return inFlight;
222
+ const capture = this.createNoteWithId(id, params).finally(() => this.noteCapturesInFlight.delete(id));
223
+ this.noteCapturesInFlight.set(id, capture);
224
+ return capture;
225
+ }
226
+ return this.createNoteWithId(id, params);
227
+ }
228
+ async createNoteWithId(id, params) {
209
229
  const now = Date.now();
210
230
  const markdown = params.markdown ?? "";
211
231
  const note = {
@@ -463,12 +483,24 @@ var NotesService = class {
463
483
  async listNotes(query = {}) {
464
484
  return this.store.listNotes(query);
465
485
  }
466
- async addAttachment(noteId, file) {
486
+ async addAttachment(noteId, file, idempotencyKey) {
487
+ if (!idempotencyKey) return this.addAttachmentWithId(noteId, file, randomUUID());
488
+ const attachmentId = noteAttachmentId(noteId, idempotencyKey);
489
+ const existing = (await this.store.getNote(noteId))?.attachments?.find((attachment) => attachment.id === attachmentId);
490
+ if (existing) return existing;
491
+ const inFlightKey = `${noteId}:${attachmentId}`;
492
+ const inFlight = this.noteAttachmentsInFlight.get(inFlightKey);
493
+ if (inFlight) return inFlight;
494
+ const capture = this.addAttachmentWithId(noteId, file, attachmentId).finally(() => this.noteAttachmentsInFlight.delete(inFlightKey));
495
+ this.noteAttachmentsInFlight.set(inFlightKey, capture);
496
+ return capture;
497
+ }
498
+ async addAttachmentWithId(noteId, file, attachmentId) {
467
499
  const note = await this.store.getNote(noteId);
468
500
  if (!note) return null;
469
501
  const { relativePath, size } = await this.store.saveAttachment(noteId, file.name, file.buffer);
470
502
  const attachment = {
471
- id: randomUUID(),
503
+ id: attachmentId,
472
504
  type: file.mimeType.startsWith("image/") ? "image" : file.mimeType.startsWith("audio/") ? "audio" : file.mimeType.startsWith("video/") ? "video" : "file",
473
505
  mimeType: file.mimeType,
474
506
  fileName: file.name,
@@ -1,6 +1,6 @@
1
1
  import { __esmMin, __exportAll } from "../../_virtual/_rolldown/runtime.js";
2
2
  import { getAgentDefaultModelRef, init_schema } from "../config/schema.js";
3
- import { getOAuthProviderIds, init_registry } from "../auth/oauth/registry.js";
3
+ import { getModelOAuthProviderIds, init_registry } from "../auth/oauth/registry.js";
4
4
  import { hasProviderAuthOnDiskSync, init_sync_provider_auth } from "../auth/sync-provider-auth.js";
5
5
  import { PROVIDER_ENV_MAP, getApiKeyFromEnv, init_env_keys } from "./env-keys.js";
6
6
  import { init_model_ref, splitProviderModelRef } from "./model-ref.js";
@@ -141,7 +141,7 @@ function getAllProviders() {
141
141
  for (const p of getProviders()) providers.add(p);
142
142
  for (const m of registry.getAll()) providers.add(m.provider);
143
143
  for (const plugin of getProviderRegistry().listAll()) providers.add(plugin.id);
144
- for (const provider of getOAuthProviderIds()) providers.add(provider);
144
+ for (const provider of getModelOAuthProviderIds()) providers.add(provider);
145
145
  return Array.from(providers);
146
146
  }
147
147
  async function getApiKey(provider, credentialOptions) {
@@ -0,0 +1,67 @@
1
+ import type { Note, NoteAttachment } from '../notes/types.js';
2
+ import type { NoteShareRecord } from './share-types.js';
3
+ import type { ShareStore } from './share-store.js';
4
+ export interface NoteShareManifestAttachment {
5
+ id: string;
6
+ type: NoteAttachment['type'];
7
+ mimeType: string;
8
+ fileName: string;
9
+ size: number;
10
+ artifactFileName: string;
11
+ checksum: string;
12
+ duration?: number;
13
+ }
14
+ export interface NoteShareManifest {
15
+ schemaVersion: 1;
16
+ shareId: string;
17
+ source: {
18
+ noteId: string;
19
+ noteVersion: number;
20
+ };
21
+ title: string;
22
+ markdown: string;
23
+ snapshotAt: string;
24
+ attachments: NoteShareManifestAttachment[];
25
+ }
26
+ export interface NoteShareSource {
27
+ getNote(id: string): Promise<Note | null>;
28
+ getAttachmentPath(noteId: string, attachmentId: string): Promise<{
29
+ filePath: string;
30
+ mimeType: string;
31
+ fileName: string;
32
+ } | null>;
33
+ }
34
+ export interface CreateNoteShareInput {
35
+ expectedNoteVersion?: number;
36
+ attachmentIds?: string[];
37
+ ttlMs?: number;
38
+ maxViews?: number | null;
39
+ description?: string;
40
+ gatewayTokenHash: string;
41
+ }
42
+ export declare class NoteShareVersionConflictError extends Error {
43
+ readonly currentVersion: number;
44
+ constructor(currentVersion: number);
45
+ }
46
+ export declare class NoteShareService {
47
+ private readonly store;
48
+ private readonly notes;
49
+ constructor(store: ShareStore, notes: NoteShareSource);
50
+ create(noteId: string, input: CreateNoteShareInput): Promise<NoteShareRecord>;
51
+ refresh(noteId: string, shareId: string, input: Pick<CreateNoteShareInput, 'expectedNoteVersion' | 'attachmentIds'>): Promise<NoteShareRecord>;
52
+ list(noteId: string): NoteShareRecord[];
53
+ revokeForNote(noteId: string): Promise<number>;
54
+ removeArtifact(shareId: string): Promise<void>;
55
+ readManifest(record: NoteShareRecord): Promise<NoteShareManifest>;
56
+ issueAssetTicket(record: NoteShareRecord): string;
57
+ verifyAssetTicket(record: NoteShareRecord, ticket: string): boolean;
58
+ resolveAsset(record: NoteShareRecord, attachmentId: string): Promise<{
59
+ path: string;
60
+ attachment: NoteShareManifestAttachment;
61
+ } | null>;
62
+ publicMarkdown(record: NoteShareRecord, manifest: NoteShareManifest, ticket: string): string;
63
+ private requireNote;
64
+ private buildArtifact;
65
+ private artifactRelativePath;
66
+ private artifactDir;
67
+ }
@@ -0,0 +1,283 @@
1
+ import { resolveStateDir } from "../config/paths-state.js";
2
+ import { createLogger } from "../utils/logger/index.js";
3
+ import { init_logger } from "../utils/logger.js";
4
+ import { init_paths } from "../config/paths.js";
5
+ import { collectReferencedAttachmentIds } from "../notes/note-attachment-sync.js";
6
+ import { dirname, join } from "node:path";
7
+ import { createHash, createHmac, randomUUID, timingSafeEqual } from "node:crypto";
8
+ import { copyFile, mkdir, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
9
+ //#region src/share/note-share-service.ts
10
+ init_paths();
11
+ init_logger();
12
+ const log = createLogger("NoteShareService");
13
+ const MANIFEST_SCHEMA_VERSION = 1;
14
+ var NoteShareVersionConflictError = class extends Error {
15
+ constructor(currentVersion) {
16
+ super("Note changed after the share preview was reviewed");
17
+ this.currentVersion = currentVersion;
18
+ this.name = "NoteShareVersionConflictError";
19
+ }
20
+ };
21
+ var NoteShareService = class {
22
+ constructor(store, notes) {
23
+ this.store = store;
24
+ this.notes = notes;
25
+ }
26
+ async create(noteId, input) {
27
+ const note = await this.requireNote(noteId, input.expectedNoteVersion);
28
+ const id = randomUUID();
29
+ const staged = await this.buildArtifact(id, note, input.attachmentIds);
30
+ try {
31
+ return this.store.createNoteShare({
32
+ id,
33
+ sourceNoteId: note.id,
34
+ sourceVersion: note.updatedAt,
35
+ artifactRelativePath: this.artifactRelativePath(id),
36
+ artifactSize: staged.totalSize,
37
+ attachmentCount: staged.manifest.attachments.length,
38
+ fileName: staged.manifest.title,
39
+ ttlMs: input.ttlMs,
40
+ maxViews: input.maxViews,
41
+ description: input.description,
42
+ gatewayTokenHash: input.gatewayTokenHash
43
+ });
44
+ } catch (error) {
45
+ await this.removeArtifact(id);
46
+ throw error;
47
+ }
48
+ }
49
+ async refresh(noteId, shareId, input) {
50
+ const record = this.store.getById(shareId);
51
+ if (!record || record.kind !== "note" || record.sourceNoteId !== noteId) throw new Error("Note share not found");
52
+ const note = await this.requireNote(noteId, input.expectedNoteVersion);
53
+ const finalDir = this.artifactDir(shareId);
54
+ const stagedDir = `${finalDir}.refresh-${randomUUID()}`;
55
+ const backupDir = `${finalDir}.backup-${randomUUID()}`;
56
+ const staged = await this.buildArtifact(shareId, note, input.attachmentIds, stagedDir);
57
+ let movedOld = false;
58
+ try {
59
+ await rename(finalDir, backupDir);
60
+ movedOld = true;
61
+ await rename(stagedDir, finalDir);
62
+ const updated = this.store.updateNoteSnapshot(shareId, {
63
+ sourceVersion: note.updatedAt,
64
+ artifactSize: staged.totalSize,
65
+ attachmentCount: staged.manifest.attachments.length,
66
+ fileName: staged.manifest.title
67
+ });
68
+ if (!updated) throw new Error("Note share not found");
69
+ await rm(backupDir, {
70
+ recursive: true,
71
+ force: true
72
+ });
73
+ return updated;
74
+ } catch (error) {
75
+ await rm(stagedDir, {
76
+ recursive: true,
77
+ force: true
78
+ });
79
+ if (movedOld) {
80
+ await rm(finalDir, {
81
+ recursive: true,
82
+ force: true
83
+ });
84
+ await rename(backupDir, finalDir).catch(() => void 0);
85
+ }
86
+ throw error;
87
+ }
88
+ }
89
+ list(noteId) {
90
+ return this.store.getNoteShares(noteId);
91
+ }
92
+ async revokeForNote(noteId) {
93
+ const active = this.store.getNoteShares(noteId).filter((record) => !record.revoked);
94
+ const count = this.store.revokeMany(active.map((record) => record.id));
95
+ await Promise.all(active.map((record) => this.removeArtifact(record.id)));
96
+ return count;
97
+ }
98
+ async removeArtifact(shareId) {
99
+ await rm(this.artifactDir(shareId), {
100
+ recursive: true,
101
+ force: true
102
+ }).catch((err) => {
103
+ log.warn({
104
+ err,
105
+ shareId
106
+ }, "Failed to remove Note share artifact");
107
+ });
108
+ }
109
+ async readManifest(record) {
110
+ const raw = await readFile(join(resolveStateDir(), record.artifactRelativePath, "manifest.json"), "utf8");
111
+ const manifest = JSON.parse(raw);
112
+ if (manifest.schemaVersion !== MANIFEST_SCHEMA_VERSION || manifest.shareId !== record.id) throw new Error("Invalid Note share artifact");
113
+ return manifest;
114
+ }
115
+ issueAssetTicket(record) {
116
+ const expiresAt = Date.now() + this.store.getConfig().note.assetTicketTtlMs;
117
+ const payload = Buffer.from(JSON.stringify({
118
+ shareId: record.id,
119
+ revision: record.snapshotRevision,
120
+ expiresAt
121
+ })).toString("base64url");
122
+ return `${payload}.${createHmac("sha256", record.assetTicketSecret).update(payload).digest("base64url")}`;
123
+ }
124
+ verifyAssetTicket(record, ticket) {
125
+ const [payload, signature] = ticket.split(".");
126
+ if (!payload || !signature) return false;
127
+ const expected = createHmac("sha256", record.assetTicketSecret).update(payload).digest();
128
+ let actual;
129
+ try {
130
+ actual = Buffer.from(signature, "base64url");
131
+ } catch {
132
+ return false;
133
+ }
134
+ if (actual.length !== expected.length || !timingSafeEqual(actual, expected)) return false;
135
+ try {
136
+ const value = JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
137
+ return value.shareId === record.id && value.revision === record.snapshotRevision && typeof value.expiresAt === "number" && Date.now() < value.expiresAt;
138
+ } catch {
139
+ return false;
140
+ }
141
+ }
142
+ async resolveAsset(record, attachmentId) {
143
+ const attachment = (await this.readManifest(record)).attachments.find((item) => item.id === attachmentId);
144
+ if (!attachment || attachment.artifactFileName !== attachment.id) return null;
145
+ return {
146
+ path: join(resolveStateDir(), record.artifactRelativePath, "assets", attachment.artifactFileName),
147
+ attachment
148
+ };
149
+ }
150
+ publicMarkdown(record, manifest, ticket) {
151
+ return manifest.markdown.replace(/xopc-attachment:\/\/notes\/([^/]+)\/([^\s)]+)/gi, (_match, noteId, attachmentId) => {
152
+ let decodedNoteId = noteId;
153
+ let decodedAttachmentId = attachmentId;
154
+ try {
155
+ decodedNoteId = decodeURIComponent(noteId);
156
+ decodedAttachmentId = decodeURIComponent(attachmentId);
157
+ } catch {
158
+ return "#invalid-attachment";
159
+ }
160
+ if (decodedNoteId !== record.sourceNoteId || !manifest.attachments.some((item) => item.id === decodedAttachmentId)) return "#attachment-not-shared";
161
+ return `/s/${encodeURIComponent(record.token)}/assets/${encodeURIComponent(decodedAttachmentId)}?ticket=${encodeURIComponent(ticket)}`;
162
+ });
163
+ }
164
+ async requireNote(noteId, expectedVersion) {
165
+ const note = await this.notes.getNote(noteId);
166
+ if (!note) throw new Error("Note not found");
167
+ if (expectedVersion !== void 0 && note.updatedAt !== expectedVersion) throw new NoteShareVersionConflictError(note.updatedAt);
168
+ return note;
169
+ }
170
+ async buildArtifact(shareId, note, requestedAttachmentIds, targetDir = this.artifactDir(shareId)) {
171
+ const cfg = this.store.getConfig().note;
172
+ if (!cfg.enabled) throw new Error("Note sharing is disabled");
173
+ const referenced = collectReferencedAttachmentIds(note);
174
+ const known = new Map((note.attachments ?? []).map((attachment) => [attachment.id, attachment]));
175
+ for (const id of referenced) if (!known.has(id)) throw new Error(`Referenced attachment is missing: ${id}`);
176
+ const selected = requestedAttachmentIds === void 0 ? [...referenced] : Array.from(new Set(requestedAttachmentIds));
177
+ for (const id of selected) if (!referenced.has(id)) throw new Error(`Attachment is not referenced by this Note: ${id}`);
178
+ const projectedMarkdown = projectPublicMarkdown(note, new Set(selected));
179
+ const markdownBytes = Buffer.byteLength(projectedMarkdown, "utf8");
180
+ if (markdownBytes > cfg.maxMarkdownBytes) throw new Error("Note Markdown exceeds sharing limit");
181
+ if (selected.length > cfg.maxAttachmentCount) throw new Error("Note has too many attachments to share");
182
+ const tempDir = targetDir === this.artifactDir(shareId) ? `${targetDir}.tmp-${randomUUID()}` : targetDir;
183
+ const assetsDir = join(tempDir, "assets");
184
+ await rm(tempDir, {
185
+ recursive: true,
186
+ force: true
187
+ });
188
+ await mkdir(assetsDir, {
189
+ recursive: true,
190
+ mode: 448
191
+ });
192
+ let totalSize = markdownBytes;
193
+ const attachments = [];
194
+ try {
195
+ for (const id of selected) {
196
+ const source = known.get(id);
197
+ if (source.size > cfg.maxAttachmentSize) throw new Error(`Attachment exceeds sharing limit: ${source.fileName}`);
198
+ totalSize += source.size;
199
+ if (totalSize > cfg.maxTotalSize) throw new Error("Note share exceeds total size limit");
200
+ const resolved = await this.notes.getAttachmentPath(note.id, id);
201
+ if (!resolved) throw new Error(`Attachment is missing: ${source.fileName}`);
202
+ const fileStat = await stat(resolved.filePath);
203
+ if (!fileStat.isFile() || fileStat.size !== source.size) throw new Error(`Attachment changed or is missing: ${source.fileName}`);
204
+ const bytes = await readFile(resolved.filePath);
205
+ const checksum = createHash("sha256").update(bytes).digest("hex");
206
+ await copyFile(resolved.filePath, join(assetsDir, id));
207
+ attachments.push({
208
+ id,
209
+ type: source.type,
210
+ mimeType: source.mimeType,
211
+ fileName: source.fileName,
212
+ size: source.size,
213
+ artifactFileName: id,
214
+ checksum,
215
+ duration: source.duration
216
+ });
217
+ }
218
+ const title = note.title?.trim() || "Untitled Note";
219
+ const manifest = {
220
+ schemaVersion: MANIFEST_SCHEMA_VERSION,
221
+ shareId,
222
+ source: {
223
+ noteId: note.id,
224
+ noteVersion: note.updatedAt
225
+ },
226
+ title,
227
+ markdown: projectedMarkdown,
228
+ snapshotAt: (/* @__PURE__ */ new Date()).toISOString(),
229
+ attachments
230
+ };
231
+ await writeFile(join(tempDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}\n`, { mode: 384 });
232
+ if (tempDir !== targetDir) {
233
+ await mkdir(dirname(targetDir), {
234
+ recursive: true,
235
+ mode: 448
236
+ });
237
+ await rename(tempDir, targetDir);
238
+ }
239
+ return {
240
+ manifest,
241
+ totalSize
242
+ };
243
+ } catch (error) {
244
+ await rm(tempDir, {
245
+ recursive: true,
246
+ force: true
247
+ });
248
+ throw error;
249
+ }
250
+ }
251
+ artifactRelativePath(shareId) {
252
+ return join("share-artifacts", shareId);
253
+ }
254
+ artifactDir(shareId) {
255
+ return join(resolveStateDir(), this.artifactRelativePath(shareId));
256
+ }
257
+ };
258
+ function projectPublicMarkdown(note, selected) {
259
+ const canonicalTarget = "xopc-attachment://notes/([^/\\s)]+)/([^\\s)]+)";
260
+ const assertOwned = new RegExp(canonicalTarget, "gi");
261
+ note.markdown.replace(assertOwned, (_match, rawNoteId) => {
262
+ let noteId;
263
+ try {
264
+ noteId = decodeURIComponent(rawNoteId);
265
+ } catch {
266
+ throw new Error("Invalid Note attachment reference");
267
+ }
268
+ if (noteId !== note.id) throw new Error("Cross-Note attachment references cannot be shared");
269
+ return _match;
270
+ });
271
+ const projectTarget = (rawNoteId, rawAttachmentId) => {
272
+ try {
273
+ return decodeURIComponent(rawNoteId) === note.id && selected.has(decodeURIComponent(rawAttachmentId));
274
+ } catch {
275
+ return false;
276
+ }
277
+ };
278
+ let markdown = note.markdown.replace(new RegExp(`!\\[([^\\]]*)\\]\\(${canonicalTarget}\\)`, "gi"), (_match, alt, rawNoteId, rawAttachmentId) => projectTarget(rawNoteId, rawAttachmentId) ? _match : `_[Attachment not shared${alt ? `: ${alt}` : ""}]_`);
279
+ markdown = markdown.replace(new RegExp(`\\[([^\\]]+)\\]\\(${canonicalTarget}\\)`, "gi"), (_match, label, rawNoteId, rawAttachmentId) => projectTarget(rawNoteId, rawAttachmentId) ? _match : label);
280
+ return markdown;
281
+ }
282
+ //#endregion
283
+ export { NoteShareService, NoteShareVersionConflictError };
@@ -19,6 +19,15 @@ const ShareThumbnailPatchSchema = z.object({
19
19
  failureCooldownMs: z.number().int().min(0).max(36e5).optional(),
20
20
  internalGatewayUrl: z.string().min(1).optional()
21
21
  });
22
+ const ShareNotePatchSchema = z.object({
23
+ enabled: z.boolean().optional(),
24
+ maxMarkdownBytes: z.number().int().min(1024).max(10485760).optional(),
25
+ maxAttachmentCount: z.number().int().min(0).max(500).optional(),
26
+ maxAttachmentSize: z.number().int().min(1024).max(1073741824).optional(),
27
+ maxTotalSize: z.number().int().min(1024).max(2147483648).optional(),
28
+ assetTicketTtlMs: z.number().int().min(6e4).max(36e5).optional(),
29
+ revokeOnSourceDelete: z.boolean().optional()
30
+ });
22
31
  const ShareConfigPatchSchema = z.object({
23
32
  enabled: z.boolean().optional(),
24
33
  defaultTtlMs: z.number().int().min(6e4).max(6048e5).optional(),
@@ -27,7 +36,8 @@ const ShareConfigPatchSchema = z.object({
27
36
  maxFileSize: z.number().int().min(1048576).max(10737418240).optional(),
28
37
  inlinePreviewMimes: z.array(z.string().min(1)).optional(),
29
38
  directory: ShareDirectoryPatchSchema.optional(),
30
- thumbnail: ShareThumbnailPatchSchema.optional()
39
+ thumbnail: ShareThumbnailPatchSchema.optional(),
40
+ note: ShareNotePatchSchema.optional()
31
41
  });
32
42
  function resolveDirectoryConfig(raw) {
33
43
  const base = SHARE_CONFIG_DEFAULTS.directory;
@@ -57,6 +67,20 @@ function resolveThumbnailConfig(raw) {
57
67
  internalGatewayUrl: patch.internalGatewayUrl ?? base.internalGatewayUrl
58
68
  };
59
69
  }
70
+ function resolveNoteConfig(raw) {
71
+ const base = SHARE_CONFIG_DEFAULTS.note;
72
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return { ...base };
73
+ const patch = raw;
74
+ return {
75
+ enabled: patch.enabled ?? base.enabled,
76
+ maxMarkdownBytes: patch.maxMarkdownBytes ?? base.maxMarkdownBytes,
77
+ maxAttachmentCount: patch.maxAttachmentCount ?? base.maxAttachmentCount,
78
+ maxAttachmentSize: patch.maxAttachmentSize ?? base.maxAttachmentSize,
79
+ maxTotalSize: patch.maxTotalSize ?? base.maxTotalSize,
80
+ assetTicketTtlMs: patch.assetTicketTtlMs ?? base.assetTicketTtlMs,
81
+ revokeOnSourceDelete: patch.revokeOnSourceDelete ?? base.revokeOnSourceDelete
82
+ };
83
+ }
60
84
  function resolveShareConfig(raw) {
61
85
  const patch = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
62
86
  return {
@@ -67,7 +91,8 @@ function resolveShareConfig(raw) {
67
91
  maxFileSize: patch.maxFileSize ?? SHARE_CONFIG_DEFAULTS.maxFileSize,
68
92
  inlinePreviewMimes: Array.isArray(patch.inlinePreviewMimes) ? patch.inlinePreviewMimes.filter((x) => typeof x === "string" && x.trim().length > 0) : [...SHARE_CONFIG_DEFAULTS.inlinePreviewMimes],
69
93
  directory: resolveDirectoryConfig(patch.directory),
70
- thumbnail: resolveThumbnailConfig(patch.thumbnail)
94
+ thumbnail: resolveThumbnailConfig(patch.thumbnail),
95
+ note: resolveNoteConfig(patch.note)
71
96
  };
72
97
  }
73
98
  function mergeShareConfigPatch(config, patch) {
@@ -99,7 +124,11 @@ function mergeShareConfigPatch(config, patch) {
99
124
  thumbnail: parsed.data.thumbnail ? {
100
125
  ...current.thumbnail,
101
126
  ...parsed.data.thumbnail
102
- } : current.thumbnail
127
+ } : current.thumbnail,
128
+ note: parsed.data.note ? {
129
+ ...current.note,
130
+ ...parsed.data.note
131
+ } : current.note
103
132
  };
104
133
  if (next.defaultTtlMs > next.maxTtlMs) return {
105
134
  ok: false,
@@ -1,5 +1,5 @@
1
1
  import type { DirectoryListing } from './share-store.js';
2
- import type { ShareRecord } from './share-types.js';
2
+ import type { NoteShareRecord, ShareRecord } from './share-types.js';
3
3
  export interface ShareLandingOgOptions {
4
4
  /** Absolute https/http URL of this landing page (for og:url). Omit if non-public. */
5
5
  absoluteShareUrl?: string | null;
@@ -10,6 +10,10 @@ export interface ShareLandingOgOptions {
10
10
  /** Description override (default: type + size). */
11
11
  description?: string;
12
12
  }
13
+ /** Lightweight public shell for Note shares. Crawlers get metadata; browsers continue to the SPA preview. */
14
+ export declare function renderNoteShareLandingPage(record: NoteShareRecord, previewUrl: string, options?: {
15
+ og?: ShareLandingOgOptions;
16
+ }): string;
13
17
  /** Render the file download confirmation landing page (does not consume downloadCount). */
14
18
  export declare function renderShareLandingPage(record: ShareRecord, downloadPath: string, options?: {
15
19
  /** Path to open the file inline (?inline=1) for browser-native rendering. */
@@ -35,6 +35,33 @@ body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;b
35
35
  .empty{text-align:center;color:#94a3b8;padding:2rem}
36
36
  `;
37
37
  }
38
+ /** Lightweight public shell for Note shares. Crawlers get metadata; browsers continue to the SPA preview. */
39
+ function renderNoteShareLandingPage(record, previewUrl, options) {
40
+ const title = escapeHtml(record.fileName);
41
+ const description = escapeHtml(record.description ?? "A Note shared via xopc");
42
+ const target = escapeHtml(previewUrl);
43
+ return `<!DOCTYPE html>
44
+ <html lang="zh">
45
+ <head>
46
+ <meta charset="utf-8">
47
+ <meta name="viewport" content="width=device-width,initial-scale=1">
48
+ <meta name="referrer" content="no-referrer">
49
+ <meta name="robots" content="noindex,nofollow">
50
+ ${renderOgTags({
51
+ title,
52
+ description,
53
+ url: options?.og?.absoluteShareUrl ?? null,
54
+ image: options?.og?.absoluteThumbnailUrl ?? null
55
+ })}
56
+ <title>${title} — xopc Share</title>
57
+ <meta http-equiv="refresh" content="0;url=${target}">
58
+ </head>
59
+ <body>
60
+ <p><a href="${target}">Open shared Note</a></p>
61
+ <script>location.replace(${JSON.stringify(previewUrl)})<\/script>
62
+ </body>
63
+ </html>`;
64
+ }
38
65
  /** Render the file download confirmation landing page (does not consume downloadCount). */
39
66
  function renderShareLandingPage(record, downloadPath, options) {
40
67
  const fileName = escapeHtml(record.fileName);
@@ -212,4 +239,4 @@ function renderShareExpiredPage(reason) {
212
239
  </html>`;
213
240
  }
214
241
  //#endregion
215
- export { renderFolderLandingPage, renderShareExpiredPage, renderShareLandingPage };
242
+ export { renderFolderLandingPage, renderNoteShareLandingPage, renderShareExpiredPage, renderShareLandingPage };