@xopcai/xopc 0.0.254 → 0.0.255

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 (154) 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-Ci5Rxmi_.js → activity-store-DvjK5FEm.js} +1 -1
  4. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-D82jpmFL.js → agent-avatar-dicebear-Cqspzbf_.js} +1 -1
  5. package/dist/gateway/static/root/assets/agent-defaults-settings-panel-CL3Mi-uM.js +1 -0
  6. package/dist/gateway/static/root/assets/agents-odqXmGhA.js +1 -0
  7. package/dist/gateway/static/root/assets/{api-CXIzQJ2Z.js → api-CsYi3heY.js} +1 -1
  8. package/dist/gateway/static/root/assets/{app-management-settings-panel-mhxcQpkQ.js → app-management-settings-panel-Gd4a5hdE.js} +1 -1
  9. package/dist/gateway/static/root/assets/{appearance-settings-BIMwACUF.js → appearance-settings-CZ6KOnbd.js} +1 -1
  10. package/dist/gateway/static/root/assets/apps-page-DyBDoCam.js +1 -0
  11. package/dist/gateway/static/root/assets/{archive-plugin-C63Xp_sU.js → archive-plugin-y82Rwhjm.js} +1 -1
  12. package/dist/gateway/static/root/assets/{attachment-load-D12KYOav.js → attachment-load-D46SGNpv.js} +1 -1
  13. package/dist/gateway/static/root/assets/{automations-page-DXj0o5CY.js → automations-page-wilciIZ1.js} +1 -1
  14. package/dist/gateway/static/root/assets/automations-workspace-8U86Umig.js +6 -0
  15. package/dist/gateway/static/root/assets/{binary-plugins-Bcv8hun6.js → binary-plugins-CuMkNNCe.js} +1 -1
  16. package/dist/gateway/static/root/assets/{block-editor-C61LqK_-.js → block-editor-DFnPsrxI.js} +24 -24
  17. package/dist/gateway/static/root/assets/{browser-automation-inputs-CAclu735.js → browser-automation-inputs-hlmcSp4p.js} +1 -1
  18. package/dist/gateway/static/root/assets/{browser-automations-page-BITfKX8L.js → browser-automations-page-OjZvbARU.js} +2 -2
  19. package/dist/gateway/static/root/assets/browser-settings-page-DOUG84qV.js +8 -0
  20. package/dist/gateway/static/root/assets/capabilities-settings-panel-CG73Mwi8.js +7 -0
  21. package/dist/gateway/static/root/assets/channels-settings-MDywEWWM.js +1 -0
  22. package/dist/gateway/static/root/assets/{channels-status-swr-Cmbx0E8O.js → channels-status-swr-D1HHFzkE.js} +1 -1
  23. package/dist/gateway/static/root/assets/{chat-terminal-dock-CNBgHN9U.js → chat-terminal-dock-Ce_T9BPq.js} +2 -2
  24. package/dist/gateway/static/root/assets/connectors-page-D29p6Zvm.js +2 -0
  25. package/dist/gateway/static/root/assets/{cron-expression-DCTN4QAG.js → cron-expression-BRrCwYyD.js} +1 -1
  26. package/dist/gateway/static/root/assets/date-picker-C35RuuWW.js +1 -0
  27. package/dist/gateway/static/root/assets/{dependency-picker-l6IXzk0C.js → dependency-picker-4pNXVcRM.js} +1 -1
  28. package/dist/gateway/static/root/assets/{desktop-pet-settings-DIfccDKS.js → desktop-pet-settings-BvV1UA5g.js} +1 -1
  29. package/dist/gateway/static/root/assets/{desktop-pet-cGn_20DO.js → desktop-pet-z91GYnPf.js} +1 -1
  30. package/dist/gateway/static/root/assets/{extension-debug-page-CTS5RP_D.js → extension-debug-page-CkVF5HvV.js} +1 -1
  31. package/dist/gateway/static/root/assets/{extension-page-Dg9YssYH.js → extension-page-DW0zQGaB.js} +1 -1
  32. package/dist/gateway/static/root/assets/{extension-settings-page-CWZYL8Fi.js → extension-settings-page-BLQhizZ3.js} +1 -1
  33. package/dist/gateway/static/root/assets/{gateway-config-swr-Chsr5wGH.js → gateway-config-swr-02BBC1mK.js} +1 -1
  34. package/dist/gateway/static/root/assets/{gateway-settings-vHI_Que8.js → gateway-settings-rAXx8FGP.js} +1 -1
  35. package/dist/gateway/static/root/assets/{gateway-startup-retry-C9ynH6VV.js → gateway-startup-retry-XZnY6621.js} +1 -1
  36. package/dist/gateway/static/root/assets/heartbeat-settings-BaW_r1t9.js +1 -0
  37. package/dist/gateway/static/root/assets/home-page-BoTS8-Z9.js +1 -0
  38. package/dist/gateway/static/root/assets/{image-generation-api-D_TsVe7h.js → image-generation-api-CIcKz4Xs.js} +1 -1
  39. package/dist/gateway/static/root/assets/index-DsonmRCH.css +2 -0
  40. package/dist/gateway/static/root/assets/index-PksHo9hS.js +74 -0
  41. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-DGBf6YfD.js → keyboard-shortcuts-settings-yfaGz6SE.js} +1 -1
  42. package/dist/gateway/static/root/assets/local-app-workbench-page-B3flRQH1.js +2 -0
  43. package/dist/gateway/static/root/assets/{local-apps-page-DGWP_xsQ.js → local-apps-page-Cp-nEtgV.js} +1 -1
  44. package/dist/gateway/static/root/assets/{locale-store-B2v8mNJw.js → locale-store-VsboRLD7.js} +1 -1
  45. package/dist/gateway/static/root/assets/logs-page-DkqsRCri.js +2 -0
  46. package/dist/gateway/static/root/assets/{management-settings-DUAZ-dtt.js → management-settings-24_JyDPk.js} +1 -1
  47. package/dist/gateway/static/root/assets/{markdown-view-DBWu9d_n.js → markdown-view-CPXhy6nh.js} +1 -1
  48. package/dist/gateway/static/root/assets/{media-plugins-CsUjcIBy.js → media-plugins-CwLvIb9r.js} +1 -1
  49. package/dist/gateway/static/root/assets/messages-DngsF_Kx.js +3 -0
  50. package/dist/gateway/static/root/assets/{mobile-pairing-wizard-Dxu8twiQ.js → mobile-pairing-wizard-BbJCDv57.js} +1 -1
  51. package/dist/gateway/static/root/assets/note-detail-panel-OklYXK7y.js +5 -0
  52. package/dist/gateway/static/root/assets/notes-page-BjfgJIQw.js +8 -0
  53. package/dist/gateway/static/root/assets/{page-tabs-YeJKwV-7.js → page-tabs-0YXn9Gkl.js} +1 -1
  54. package/dist/gateway/static/root/assets/popover-select-CKBYnT3c.js +1 -0
  55. package/dist/gateway/static/root/assets/{preview-open-alternatives-BfJ3jMDG.js → preview-open-alternatives-2jVV5kfZ.js} +1 -1
  56. package/dist/gateway/static/root/assets/{product-open-page-B7NRGPox.js → product-open-page-D7p0NfTK.js} +1 -1
  57. package/dist/gateway/static/root/assets/project-detail-page-rLNM2mP0.js +1 -0
  58. package/dist/gateway/static/root/assets/projects-page-D6jY2ZWl.js +1 -0
  59. package/dist/gateway/static/root/assets/{registry-api-B9OZUdwC.js → registry-api-DlTWbXQA.js} +1 -1
  60. package/dist/gateway/static/root/assets/{remote-access-hub-DTO-faxH.js → remote-access-hub-CZZg0kXc.js} +1 -1
  61. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-uJ8Br3ZL.js → runtime-tools-settings-panel-B-NDgmJ4.js} +2 -2
  62. package/dist/gateway/static/root/assets/{schema-form-CXwn6up5.js → schema-form-DZYeBS1v.js} +1 -1
  63. package/dist/gateway/static/root/assets/sessions-page-BvuxJegi.js +1 -0
  64. package/dist/gateway/static/root/assets/settings-advanced-gate-BAQHP4mB.js +1 -0
  65. package/dist/gateway/static/root/assets/{settings-form-section-YVNYsR0U.js → settings-form-section-zy5UMvPk.js} +1 -1
  66. package/dist/gateway/static/root/assets/{settings-page-cHqrIdVR.js → settings-page-CTuipbJq.js} +1 -1
  67. package/dist/gateway/static/root/assets/setup-status-panel-BkWoc5j5.js +1 -0
  68. package/dist/gateway/static/root/assets/{share-preview-page-DnjSDQru.js → share-preview-page-D3Uawo_h.js} +1 -1
  69. package/dist/gateway/static/root/assets/shares-settings-B2doziIn.js +1 -0
  70. package/dist/gateway/static/root/assets/skill-reload-api-D2ymjqG5.js +1 -0
  71. package/dist/gateway/static/root/assets/{skills-page-DkrGEdiY.js → skills-page-DDbabz7p.js} +1 -1
  72. package/dist/gateway/static/root/assets/{skills-page.utils-CqZbiZuK.js → skills-page.utils-DkGhbiMa.js} +1 -1
  73. package/dist/gateway/static/root/assets/{system-settings-panel-Dmw0RfxZ.js → system-settings-panel-Fc-WbX6-.js} +1 -1
  74. package/dist/gateway/static/root/assets/task-detail-page-7w7eB9nB.js +1 -0
  75. package/dist/gateway/static/root/assets/{theme-store-CubltMKm.js → theme-store-vdB57bTn.js} +1 -1
  76. package/dist/gateway/static/root/assets/{tunnel-api-CiieC6HP.js → tunnel-api-DZ0evABj.js} +1 -1
  77. package/dist/gateway/static/root/assets/{use-autosave-XQ3SuyB9.js → use-autosave-Ckof_m7e.js} +1 -1
  78. package/dist/gateway/static/root/assets/user-model-page-CcNCyrXZ.js +2 -0
  79. package/dist/gateway/static/root/assets/{voice-api-key-field-D4QzVgdt.js → voice-api-key-field-CnmtpWlq.js} +1 -1
  80. package/dist/gateway/static/root/assets/{voice-config-api-B1ZbyVmF.js → voice-config-api-OA04c71t.js} +1 -1
  81. package/dist/gateway/static/root/assets/{work-discovery-overlay-D5Tf8GRZ.js → work-discovery-overlay-sxvsDIC3.js} +1 -1
  82. package/dist/gateway/static/root/assets/workflow-run-setup-panel-CUCfBpPF.js +3 -0
  83. package/dist/gateway/static/root/assets/workflows-page-Bp-nvnLa.js +5 -0
  84. package/dist/gateway/static/root/index.html +11 -11
  85. package/dist/package.js +1 -1
  86. package/dist/src/agent/tools/create-share-tool.js +39 -1
  87. package/dist/src/agent/tools/xopc-use-tool.js +2 -2
  88. package/dist/src/cli/commands/tunnel.js +3 -3
  89. package/dist/src/gateway/device-routes.js +1 -1
  90. package/dist/src/gateway/hono/lib/config-payload.js +1 -1
  91. package/dist/src/gateway/hono/routes/lazy-bundles.js +2 -2
  92. package/dist/src/gateway/hono/routes/sessions.js +1 -1
  93. package/dist/src/gateway/hono/routes/shares.js +404 -7
  94. package/dist/src/gateway/hono/routes/site-shares.js +19 -13
  95. package/dist/src/gateway/hono/routes/tunnel.js +1 -1
  96. package/dist/src/gateway/hono/routes/workflows.js +2 -2
  97. package/dist/src/gateway/service/sessions-api.js +1 -1
  98. package/dist/src/gateway/service.js +3 -3
  99. package/dist/src/remote-access/exposure-manager.js +1 -1
  100. package/dist/src/session/index.js +1 -1
  101. package/dist/src/session/store.js +1 -1
  102. package/dist/src/share/hosted-note-publication.d.ts +22 -0
  103. package/dist/src/share/hosted-note-publication.js +96 -0
  104. package/dist/src/share/hosted-session-share.d.ts +86 -7
  105. package/dist/src/share/hosted-session-share.js +72 -15
  106. package/dist/src/share/hosted-static-site-publication.d.ts +16 -0
  107. package/dist/src/share/hosted-static-site-publication.js +108 -0
  108. package/dist/src/share/hosted-static-site-publish.d.ts +23 -0
  109. package/dist/src/share/hosted-static-site-publish.js +53 -0
  110. package/dist/src/share/note-share-service.d.ts +1 -0
  111. package/dist/src/share/note-share-service.js +4 -4
  112. package/dist/src/share/share-url.d.ts +1 -1
  113. package/dist/src/share/share-url.js +14 -9
  114. package/dist/src/tasks/home-query-service.js +2 -2
  115. package/dist/src/tui/backends/embedded-backend.js +2 -2
  116. package/dist/src/tui/tui-agent-events.js +1 -1
  117. package/dist/src/tunnel/index.js +1 -1
  118. package/dist/src/tunnel/tunnel-service.js +1 -1
  119. package/dist/src/voice/realtime/agentEngine.js +17 -0
  120. package/dist/src/voice/realtime/memory-context.js +1 -1
  121. package/dist/src/voice/realtime/playback-echo.d.ts +2 -0
  122. package/dist/src/voice/realtime/playback-echo.js +15 -0
  123. package/dist/src/workflows/service/workflow-run-service.js +1 -1
  124. package/package.json +3 -3
  125. package/dist/gateway/static/root/assets/agent-defaults-settings-panel-vul32IsP.js +0 -1
  126. package/dist/gateway/static/root/assets/agents-p3kWyz1T.js +0 -1
  127. package/dist/gateway/static/root/assets/apps-page-CJFJKNwS.js +0 -1
  128. package/dist/gateway/static/root/assets/automations-workspace-B2wxcpgq.js +0 -6
  129. package/dist/gateway/static/root/assets/browser-settings-page-BMjY8cyv.js +0 -8
  130. package/dist/gateway/static/root/assets/capabilities-settings-panel-wB3Vgmp3.js +0 -7
  131. package/dist/gateway/static/root/assets/channels-settings-U5ffzu4r.js +0 -1
  132. package/dist/gateway/static/root/assets/connectors-page-BGaF--m5.js +0 -2
  133. package/dist/gateway/static/root/assets/date-picker-BuEyUxZn.js +0 -1
  134. package/dist/gateway/static/root/assets/heartbeat-settings-WUhSfvob.js +0 -1
  135. package/dist/gateway/static/root/assets/home-page-CYjlMzPx.js +0 -1
  136. package/dist/gateway/static/root/assets/index-BBlJYVxR.js +0 -74
  137. package/dist/gateway/static/root/assets/index-_I0dD2LN.css +0 -2
  138. package/dist/gateway/static/root/assets/local-app-workbench-page-CLlIl83V.js +0 -2
  139. package/dist/gateway/static/root/assets/logs-page-DuhCigiB.js +0 -2
  140. package/dist/gateway/static/root/assets/messages-ChSTFZR5.js +0 -3
  141. package/dist/gateway/static/root/assets/note-detail-panel-DMdbaTZp.js +0 -5
  142. package/dist/gateway/static/root/assets/notes-page-xe_vX0nM.js +0 -8
  143. package/dist/gateway/static/root/assets/popover-select-C-Hj-nXR.js +0 -1
  144. package/dist/gateway/static/root/assets/project-detail-page-hG7ELibZ.js +0 -1
  145. package/dist/gateway/static/root/assets/projects-page-C5G2ET4w.js +0 -1
  146. package/dist/gateway/static/root/assets/sessions-page-CwzsUCgP.js +0 -1
  147. package/dist/gateway/static/root/assets/settings-advanced-gate-Djc3EdoO.js +0 -1
  148. package/dist/gateway/static/root/assets/setup-status-panel-MUAMcZLD.js +0 -1
  149. package/dist/gateway/static/root/assets/shares-settings-C7A0aywX.js +0 -1
  150. package/dist/gateway/static/root/assets/skill-reload-api-D1xSGJAQ.js +0 -1
  151. package/dist/gateway/static/root/assets/task-detail-page-BvZSCLUH.js +0 -1
  152. package/dist/gateway/static/root/assets/user-model-page-BKJC7tyW.js +0 -2
  153. package/dist/gateway/static/root/assets/workflow-run-setup-panel-BW9p8Maz.js +0 -3
  154. package/dist/gateway/static/root/assets/workflows-page-DuqRodIc.js +0 -5
@@ -0,0 +1,22 @@
1
+ import type { HostedPublicationSnapshot } from './hosted-session-share.js';
2
+ import { type NoteShareSource } from './note-share-service.js';
3
+ export interface HostedNotePublicationSnapshot extends HostedPublicationSnapshot {
4
+ kind: 'note_document';
5
+ sourceNoteId: string;
6
+ sourceVersion: number;
7
+ title: string;
8
+ attachmentCount: number;
9
+ }
10
+ export declare class HostedNotePublicationBuilder {
11
+ private readonly notes;
12
+ constructor(notes: NoteShareSource);
13
+ build(noteId: string, input: {
14
+ expectedNoteVersion?: number;
15
+ attachmentIds?: string[];
16
+ description?: string;
17
+ }): Promise<HostedNotePublicationSnapshot>;
18
+ }
19
+ export declare class HostedNoteVersionConflictError extends Error {
20
+ readonly currentVersion: number;
21
+ constructor(currentVersion: number);
22
+ }
@@ -0,0 +1,96 @@
1
+ import { collectReferencedAttachmentIds } from "../notes/note-attachment-sync.js";
2
+ import { projectPublicNoteMarkdown } from "./note-share-service.js";
3
+ import { createReadStream } from "node:fs";
4
+ import { createHash } from "node:crypto";
5
+ import { stat } from "node:fs/promises";
6
+ //#region src/share/hosted-note-publication.ts
7
+ const MAX_ATTACHMENTS = 20;
8
+ const MAX_ATTACHMENT_BYTES = 32 * 1024 * 1024;
9
+ const MAX_TOTAL_BYTES = 100 * 1024 * 1024;
10
+ const MAX_MARKDOWN_BYTES = 5 * 1024 * 1024;
11
+ var HostedNotePublicationBuilder = class {
12
+ constructor(notes) {
13
+ this.notes = notes;
14
+ }
15
+ async build(noteId, input) {
16
+ const note = await this.notes.getNote(noteId);
17
+ if (!note) throw new Error("Note not found");
18
+ if (input.expectedNoteVersion !== void 0 && note.updatedAt !== input.expectedNoteVersion) throw new HostedNoteVersionConflictError(note.updatedAt);
19
+ const referenced = collectReferencedAttachmentIds(note);
20
+ const attachments = new Map((note.attachments ?? []).map((attachment) => [attachment.id, attachment]));
21
+ for (const id of referenced) if (!attachments.has(id)) throw new Error(`Referenced attachment is missing: ${id}`);
22
+ const selected = input.attachmentIds === void 0 ? [...referenced] : [...new Set(input.attachmentIds)];
23
+ if (selected.length > MAX_ATTACHMENTS) throw new Error("Note has too many attachments to publish");
24
+ for (const id of selected) if (!referenced.has(id)) throw new Error(`Attachment is not referenced by this Note: ${id}`);
25
+ const selectedSet = new Set(selected);
26
+ const markdown = rewritePublicationAssets(projectPublicNoteMarkdown(note, selectedSet), note, selectedSet);
27
+ if (Buffer.byteLength(markdown, "utf8") > MAX_MARKDOWN_BYTES) throw new Error("Note Markdown exceeds publishing limit");
28
+ const assets = [];
29
+ const manifestAttachments = [];
30
+ let totalBytes = Buffer.byteLength(markdown, "utf8");
31
+ for (const id of selected) {
32
+ const attachment = attachments.get(id);
33
+ if (attachment.size > MAX_ATTACHMENT_BYTES) throw new Error(`Attachment exceeds publishing limit: ${attachment.fileName}`);
34
+ totalBytes += attachment.size;
35
+ if (totalBytes > MAX_TOTAL_BYTES) throw new Error("Note publication exceeds total size limit");
36
+ const resolved = await this.notes.getAttachmentPath(note.id, id);
37
+ if (!resolved) throw new Error(`Attachment is missing: ${attachment.fileName}`);
38
+ const current = await stat(resolved.filePath);
39
+ if (!current.isFile() || current.size !== attachment.size) throw new Error(`Attachment changed or is missing: ${attachment.fileName}`);
40
+ assets.push({
41
+ id,
42
+ path: resolved.filePath,
43
+ size: attachment.size
44
+ });
45
+ manifestAttachments.push({
46
+ id,
47
+ fileName: attachment.fileName,
48
+ mimeType: attachment.mimeType,
49
+ size: attachment.size,
50
+ sha256: await sha256File(resolved.filePath)
51
+ });
52
+ }
53
+ return {
54
+ kind: "note_document",
55
+ sourceNoteId: note.id,
56
+ sourceVersion: note.updatedAt,
57
+ title: note.title?.trim() || "Untitled Note",
58
+ attachmentCount: assets.length,
59
+ assets,
60
+ manifest: {
61
+ schemaVersion: 1,
62
+ kind: "note_document",
63
+ title: note.title?.trim() || "Untitled Note",
64
+ snapshotAt: (/* @__PURE__ */ new Date()).toISOString(),
65
+ ...input.description?.trim() ? { description: input.description.trim() } : {},
66
+ markdown,
67
+ attachments: manifestAttachments
68
+ }
69
+ };
70
+ }
71
+ };
72
+ var HostedNoteVersionConflictError = class extends Error {
73
+ constructor(currentVersion) {
74
+ super("Note changed after the publication preview was reviewed");
75
+ this.currentVersion = currentVersion;
76
+ this.name = "HostedNoteVersionConflictError";
77
+ }
78
+ };
79
+ function rewritePublicationAssets(markdown, note, selected) {
80
+ return markdown.replace(/xopc-attachment:\/\/notes\/([^/\s)]+)\/([^\s)]+)/gi, (match, rawNoteId, rawAttachmentId) => {
81
+ try {
82
+ const noteId = decodeURIComponent(rawNoteId);
83
+ const attachmentId = decodeURIComponent(rawAttachmentId);
84
+ return noteId === note.id && selected.has(attachmentId) ? `xopc-publication-asset://${attachmentId}` : match;
85
+ } catch {
86
+ return match;
87
+ }
88
+ });
89
+ }
90
+ async function sha256File(filePath) {
91
+ const hash = createHash("sha256");
92
+ for await (const chunk of createReadStream(filePath)) hash.update(chunk);
93
+ return hash.digest("hex");
94
+ }
95
+ //#endregion
96
+ export { HostedNotePublicationBuilder, HostedNoteVersionConflictError };
@@ -3,6 +3,7 @@ import { type SessionShareMessage, type SessionShareToolActivity } from './sessi
3
3
  import { type SessionShareSource } from './session-share-service.js';
4
4
  export interface HostedSessionShareManifest {
5
5
  schemaVersion: 1;
6
+ kind: 'session_document';
6
7
  title: string;
7
8
  snapshotAt: string;
8
9
  description?: string;
@@ -49,7 +50,17 @@ export interface HostedShareResult {
49
50
  viewCount: number;
50
51
  snapshotRevision: number;
51
52
  }
52
- type OwnerShare = {
53
+ export type HostedPublicationKind = 'session_document' | 'note_document' | 'static_site';
54
+ export interface HostedPublicationSnapshot {
55
+ kind: HostedPublicationKind;
56
+ manifest: unknown;
57
+ assets: Array<{
58
+ id: string;
59
+ path: string;
60
+ size: number;
61
+ }>;
62
+ }
63
+ export type HostedPublicationSummary = {
53
64
  id: string;
54
65
  title: string;
55
66
  description: string | null;
@@ -60,8 +71,42 @@ type OwnerShare = {
60
71
  viewCount: number;
61
72
  createdAt: string;
62
73
  updatedAt: string;
74
+ kind: HostedPublicationKind;
75
+ deliveryMode: 'hosted_snapshot';
76
+ owner: {
77
+ type: 'user' | 'workspace';
78
+ id: string;
79
+ };
80
+ workspaceId: string;
81
+ createdByPrincipalId: string;
63
82
  };
64
- export declare class HostedSessionSharePublisher {
83
+ type OwnerShare = HostedPublicationSummary;
84
+ export interface HostedPublicationCapabilities {
85
+ protocolVersion: string;
86
+ kinds: string[];
87
+ deliveryModes: string[];
88
+ accessModes: string[];
89
+ upload: {
90
+ direct: boolean;
91
+ multipart: boolean;
92
+ resumable: boolean;
93
+ streaming: boolean;
94
+ };
95
+ security?: {
96
+ contentScanning: boolean;
97
+ quarantineBeforeActivation: boolean;
98
+ };
99
+ lifecycle: {
100
+ revisions: boolean;
101
+ revoke: boolean;
102
+ asynchronousDeletion: boolean;
103
+ };
104
+ publishing: {
105
+ allowed: boolean;
106
+ managedBy: 'platform_admin';
107
+ };
108
+ }
109
+ export declare class HostedPublicationPublisher {
65
110
  private readonly credentials;
66
111
  private readonly fetchImpl;
67
112
  private readonly baseUrl;
@@ -70,23 +115,54 @@ export declare class HostedSessionSharePublisher {
70
115
  ttlMs: number;
71
116
  maxViews: number | null;
72
117
  }): Promise<HostedShareResult>;
118
+ createPublication(snapshot: HostedPublicationSnapshot, lifecycle: {
119
+ ttlMs: number;
120
+ maxViews: number | null;
121
+ }): Promise<HostedShareResult>;
73
122
  refresh(shareId: string, expectedRevision: number, publicUrl: string, snapshot: HostedSessionShareSnapshot): Promise<HostedShareResult>;
74
- list(): Promise<OwnerShare[]>;
123
+ refreshPublication(shareId: string, expectedRevision: number, publicUrl: string, snapshot: HostedPublicationSnapshot): Promise<HostedShareResult>;
124
+ listPublications(): Promise<HostedPublicationSummary[]>;
125
+ capabilities(): Promise<HostedPublicationCapabilities>;
75
126
  revoke(shareId: string): Promise<void>;
127
+ revokePublication(shareId: string): Promise<void>;
128
+ listRevisions(shareId: string): Promise<Array<{
129
+ revision: number;
130
+ createdAt: string;
131
+ current: boolean;
132
+ }>>;
133
+ rollbackPublication(shareId: string, expectedRevision: number, targetRevision: number): Promise<HostedPublicationSummary>;
76
134
  private uploadAssets;
77
135
  private request;
78
136
  }
79
137
  export declare class HostedShareAuthorizationError extends Error {
80
138
  constructor();
81
139
  }
140
+ export declare class HostedShareRemoteError extends Error {
141
+ readonly status: number;
142
+ readonly code?: string;
143
+ constructor(message: string, status: number, code?: string);
144
+ }
145
+ export type HostedPublicationSource = {
146
+ kind: 'session' | 'note' | 'static_site';
147
+ id: string;
148
+ version: string;
149
+ };
82
150
  export interface HostedShareBinding extends HostedShareResult {
83
- sessionId: string;
84
- cutoffSeq: number;
151
+ kind: HostedPublicationKind;
152
+ source: HostedPublicationSource;
153
+ revisionSources: Record<string, HostedPublicationSource>;
154
+ workspaceContext?: {
155
+ workspaceRoot?: string;
156
+ sessionKey?: string;
157
+ agentId?: string;
158
+ };
159
+ sessionId?: string;
160
+ cutoffSeq?: number;
85
161
  title: string;
86
162
  description: string | null;
87
- messageCount: number;
163
+ messageCount?: number;
88
164
  attachmentCount: number;
89
- includeToolActivities: boolean;
165
+ includeToolActivities?: boolean;
90
166
  attachmentIds: string[];
91
167
  createdAt: string;
92
168
  updatedAt: string;
@@ -95,7 +171,10 @@ export interface HostedShareBinding extends HostedShareResult {
95
171
  export declare class HostedShareBindingStore {
96
172
  private readonly state;
97
173
  list(sessionId: string): Promise<HostedShareBinding[]>;
174
+ listBySource(kind: HostedPublicationSource['kind'], id: string): Promise<HostedShareBinding[]>;
175
+ listAll(): Promise<HostedShareBinding[]>;
98
176
  upsert(binding: HostedShareBinding): Promise<void>;
177
+ markRevoked(id: string): Promise<void>;
99
178
  reconcile(remote: OwnerShare[]): Promise<void>;
100
179
  }
101
180
  export {};
@@ -9,7 +9,7 @@ import { SessionShareSnapshotConflictError } from "./session-share-service.js";
9
9
  import { basename } from "node:path";
10
10
  import { createReadStream } from "node:fs";
11
11
  import { createHash, randomBytes, randomUUID } from "node:crypto";
12
- import { readFile, stat } from "node:fs/promises";
12
+ import { stat } from "node:fs/promises";
13
13
  //#region src/share/hosted-session-share.ts
14
14
  init_credentials();
15
15
  init_resolution();
@@ -82,6 +82,7 @@ var HostedSessionShareBuilder = class {
82
82
  cutoffSeq: source.snapshot.lastSeq,
83
83
  manifest: {
84
84
  schemaVersion: 1,
85
+ kind: "session_document",
85
86
  title: (source.metadata.name?.trim() || "Shared conversation").slice(0, 200),
86
87
  snapshotAt: (/* @__PURE__ */ new Date()).toISOString(),
87
88
  ...input.description?.trim() ? { description: input.description.trim().slice(0, 1e3) } : {},
@@ -105,7 +106,7 @@ var HostedSessionShareBuilder = class {
105
106
  };
106
107
  }
107
108
  };
108
- var HostedSessionSharePublisher = class {
109
+ var HostedPublicationPublisher = class {
109
110
  baseUrl;
110
111
  constructor(baseUrl = process.env.XOPC_SHARE_URL ?? resolveConnectedPlatformEndpoint("shareApi") ?? DEFAULT_SHARE_URL, credentials = new CredentialResolver(), fetchImpl = fetch) {
111
112
  this.credentials = credentials;
@@ -113,23 +114,38 @@ var HostedSessionSharePublisher = class {
113
114
  this.baseUrl = baseUrl.replace(/\/+$/, "");
114
115
  }
115
116
  async create(snapshot, lifecycle) {
117
+ return this.createPublication({
118
+ kind: "session_document",
119
+ manifest: snapshot.manifest,
120
+ assets: snapshot.assets
121
+ }, lifecycle);
122
+ }
123
+ async createPublication(snapshot, lifecycle) {
116
124
  const publicToken = randomBytes(32).toString("base64url");
117
- const draft = await this.request("/api/v1/session-shares", {
125
+ const draft = await this.request("/api/v1/publications", {
118
126
  method: "POST",
119
127
  headers: { "Idempotency-Key": `xopc-${randomUUID()}` },
120
128
  body: JSON.stringify({
129
+ kind: snapshot.kind,
121
130
  publicToken,
122
131
  manifest: snapshot.manifest,
123
132
  ...lifecycle
124
133
  })
125
134
  });
126
135
  await this.uploadAssets(draft, snapshot.assets);
127
- const finalized = await this.request(`/api/v1/session-shares/${encodeURIComponent(draft.shareId)}/uploads/${encodeURIComponent(draft.uploadId)}/finalize`, { method: "POST" });
136
+ const finalized = await this.request(`/api/v1/publications/${encodeURIComponent(draft.shareId)}/uploads/${encodeURIComponent(draft.uploadId)}/finalize`, { method: "POST" });
128
137
  if (!draft.publicUrl) throw new Error("Hosted Share did not return a public URL");
129
138
  return toResult(finalized.item, draft.publicUrl);
130
139
  }
131
140
  async refresh(shareId, expectedRevision, publicUrl, snapshot) {
132
- const draft = await this.request(`/api/v1/session-shares/${encodeURIComponent(shareId)}/revisions`, {
141
+ return this.refreshPublication(shareId, expectedRevision, publicUrl, {
142
+ kind: "session_document",
143
+ manifest: snapshot.manifest,
144
+ assets: snapshot.assets
145
+ });
146
+ }
147
+ async refreshPublication(shareId, expectedRevision, publicUrl, snapshot) {
148
+ const draft = await this.request(`/api/v1/publications/${encodeURIComponent(shareId)}/revisions`, {
133
149
  method: "POST",
134
150
  headers: { "Idempotency-Key": `xopc-${randomUUID()}` },
135
151
  body: JSON.stringify({
@@ -138,28 +154,47 @@ var HostedSessionSharePublisher = class {
138
154
  })
139
155
  });
140
156
  await this.uploadAssets(draft, snapshot.assets);
141
- return toResult((await this.request(`/api/v1/session-shares/${encodeURIComponent(shareId)}/uploads/${encodeURIComponent(draft.uploadId)}/finalize`, { method: "POST" })).item, publicUrl);
157
+ return toResult((await this.request(`/api/v1/publications/${encodeURIComponent(shareId)}/uploads/${encodeURIComponent(draft.uploadId)}/finalize`, { method: "POST" })).item, publicUrl);
142
158
  }
143
- async list() {
144
- return (await this.request("/api/v1/session-shares")).items;
159
+ async listPublications() {
160
+ return (await this.request("/api/v1/publications")).items;
161
+ }
162
+ async capabilities() {
163
+ return this.request("/api/v1/publications/capabilities");
145
164
  }
146
165
  async revoke(shareId) {
147
- await this.request(`/api/v1/session-shares/${encodeURIComponent(shareId)}`, { method: "DELETE" });
166
+ await this.revokePublication(shareId);
167
+ }
168
+ async revokePublication(shareId) {
169
+ await this.request(`/api/v1/publications/${encodeURIComponent(shareId)}`, { method: "DELETE" });
170
+ }
171
+ async listRevisions(shareId) {
172
+ return (await this.request(`/api/v1/publications/${encodeURIComponent(shareId)}/revisions`)).items;
173
+ }
174
+ async rollbackPublication(shareId, expectedRevision, targetRevision) {
175
+ return (await this.request(`/api/v1/publications/${encodeURIComponent(shareId)}/rollback`, {
176
+ method: "POST",
177
+ body: JSON.stringify({
178
+ expectedRevision,
179
+ targetRevision
180
+ })
181
+ })).item;
148
182
  }
149
183
  async uploadAssets(draft, assets) {
150
184
  const uploads = new Map(draft.assetUploads.map((upload) => [upload.assetId, upload.uploadUrl]));
151
185
  for (const asset of assets) {
152
186
  const uploadUrl = uploads.get(asset.id);
153
187
  if (!uploadUrl) throw new Error(`Hosted Share did not accept attachment: ${asset.id}`);
154
- const bytes = await readFile(asset.path);
155
- if (bytes.byteLength !== asset.size) throw new Error(`Attachment changed during upload: ${asset.id}`);
188
+ const current = await stat(asset.path);
189
+ if (!current.isFile() || current.size !== asset.size) throw new Error(`Attachment changed during upload: ${asset.id}`);
156
190
  await this.request(uploadUrl, {
157
191
  method: "PUT",
158
192
  headers: {
159
- "Content-Length": String(bytes.byteLength),
193
+ "Content-Length": String(asset.size),
160
194
  "Content-Type": "application/octet-stream"
161
195
  },
162
- body: bytes
196
+ body: createReadStream(asset.path),
197
+ duplex: "half"
163
198
  });
164
199
  }
165
200
  }
@@ -180,7 +215,7 @@ var HostedSessionSharePublisher = class {
180
215
  });
181
216
  const body = await response.json().catch(() => ({}));
182
217
  if (response.status === 401) throw new HostedShareAuthorizationError();
183
- if (!response.ok) throw new Error(body.error?.message || `Hosted Share request failed (${response.status})`);
218
+ if (!response.ok) throw new HostedShareRemoteError(body.error?.message || `Hosted Share request failed (${response.status})`, response.status, body.error?.code);
184
219
  return body;
185
220
  }
186
221
  };
@@ -190,14 +225,36 @@ var HostedShareAuthorizationError = class extends Error {
190
225
  this.name = "HostedShareAuthorizationError";
191
226
  }
192
227
  };
228
+ var HostedShareRemoteError = class extends Error {
229
+ constructor(message, status, code) {
230
+ super(message);
231
+ this.status = status;
232
+ this.code = code;
233
+ this.name = "HostedShareRemoteError";
234
+ }
235
+ };
193
236
  var HostedShareBindingStore = class {
194
237
  state = new DurableState("hosted-share-bindings");
195
238
  async list(sessionId) {
196
239
  return this.state.values().filter((item) => item.sessionId === sessionId).sort((a, b) => b.createdAt.localeCompare(a.createdAt));
197
240
  }
241
+ async listBySource(kind, id) {
242
+ return this.state.values().filter((item) => item.source.kind === kind && item.source.id === id).sort((a, b) => b.createdAt.localeCompare(a.createdAt));
243
+ }
244
+ async listAll() {
245
+ return this.state.values().sort((a, b) => b.createdAt.localeCompare(a.createdAt));
246
+ }
198
247
  async upsert(binding) {
199
248
  this.state.set(binding.id, binding);
200
249
  }
250
+ async markRevoked(id) {
251
+ const binding = this.state.get(id);
252
+ if (binding) this.state.set(id, {
253
+ ...binding,
254
+ revoked: true,
255
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
256
+ });
257
+ }
201
258
  async reconcile(remote) {
202
259
  const byId = new Map(remote.map((item) => [item.id, item]));
203
260
  requireXopcDatabase();
@@ -242,4 +299,4 @@ async function checksumFile(path) {
242
299
  return hash.digest("hex");
243
300
  }
244
301
  //#endregion
245
- export { HostedSessionShareBuilder, HostedSessionSharePublisher, HostedShareAuthorizationError, HostedShareBindingStore };
302
+ export { HostedPublicationPublisher, HostedSessionShareBuilder, HostedShareAuthorizationError, HostedShareBindingStore, HostedShareRemoteError };
@@ -0,0 +1,16 @@
1
+ import type { HostedPublicationSnapshot } from './hosted-session-share.js';
2
+ export interface HostedStaticSitePublicationSnapshot extends HostedPublicationSnapshot {
3
+ kind: 'static_site';
4
+ rootDir: string;
5
+ title: string;
6
+ fileCount: number;
7
+ totalBytes: number;
8
+ }
9
+ export declare class HostedStaticSitePublicationBuilder {
10
+ build(workspaceRoot: string, input: {
11
+ path: string;
12
+ title?: string;
13
+ description?: string;
14
+ spaFallback?: boolean;
15
+ }): Promise<HostedStaticSitePublicationSnapshot>;
16
+ }
@@ -0,0 +1,108 @@
1
+ import { isPathUnderWorkspace } from "../gateway/workspace-editor-path.js";
2
+ import { resolveMimeType } from "./share-store.js";
3
+ import { basename, dirname, extname, posix, relative, resolve } from "node:path";
4
+ import { createReadStream } from "node:fs";
5
+ import { createHash } from "node:crypto";
6
+ import { lstat, readdir, realpath, stat } from "node:fs/promises";
7
+ //#region src/share/hosted-static-site-publication.ts
8
+ const MAX_FILES = 2e3;
9
+ const MAX_FILE_BYTES = 64 * 1024 * 1024;
10
+ const MAX_TOTAL_BYTES = 512 * 1024 * 1024;
11
+ var HostedStaticSitePublicationBuilder = class {
12
+ async build(workspaceRoot, input) {
13
+ const realSource = await realpath(resolve(workspaceRoot, normalizeInputPath(input.path)));
14
+ if (!isPathUnderWorkspace(workspaceRoot, realSource)) throw new Error("Static site path is outside the workspace");
15
+ const sourceStat = await stat(realSource);
16
+ const singleHtml = sourceStat.isFile();
17
+ if (singleHtml && resolveMimeType(realSource) !== "text/html") throw new Error("Static site file must be an HTML document");
18
+ if (!singleHtml && !sourceStat.isDirectory()) throw new Error("Static site source must be an HTML file or directory");
19
+ const rootDir = singleHtml ? dirname(realSource) : realSource;
20
+ const entries = singleHtml ? [{
21
+ path: "index.html",
22
+ absolutePath: realSource
23
+ }] : (await collectFiles(rootDir)).map((path) => ({
24
+ path,
25
+ absolutePath: resolve(rootDir, path)
26
+ }));
27
+ if (!singleHtml && !entries.some((entry) => entry.path === "index.html")) throw new Error("Static site must contain index.html");
28
+ const assets = [];
29
+ const files = [];
30
+ let totalBytes = 0;
31
+ for (const entry of entries) {
32
+ const filePath = entry.path;
33
+ const absolutePath = entry.absolutePath;
34
+ const fileStat = await stat(absolutePath);
35
+ if (fileStat.size > MAX_FILE_BYTES) throw new Error(`Static site file exceeds publishing limit: ${filePath}`);
36
+ totalBytes += fileStat.size;
37
+ if (totalBytes > MAX_TOTAL_BYTES) throw new Error("Static site exceeds total publishing limit");
38
+ const id = createHash("sha256").update(filePath).digest("hex").slice(0, 40);
39
+ assets.push({
40
+ id,
41
+ path: absolutePath,
42
+ size: fileStat.size
43
+ });
44
+ files.push({
45
+ id,
46
+ path: filePath,
47
+ mimeType: resolveMimeType(filePath),
48
+ size: fileStat.size,
49
+ sha256: await sha256File(absolutePath)
50
+ });
51
+ }
52
+ const defaultTitle = singleHtml ? basename(realSource, extname(realSource)) : basename(realSource);
53
+ const title = input.title?.trim() || defaultTitle || "Published site";
54
+ return {
55
+ kind: "static_site",
56
+ rootDir,
57
+ title,
58
+ fileCount: files.length,
59
+ totalBytes,
60
+ assets,
61
+ manifest: {
62
+ schemaVersion: 1,
63
+ kind: "static_site",
64
+ title,
65
+ snapshotAt: (/* @__PURE__ */ new Date()).toISOString(),
66
+ ...input.description?.trim() ? { description: input.description.trim() } : {},
67
+ entrypoint: "index.html",
68
+ spaFallback: input.spaFallback ?? true,
69
+ files
70
+ }
71
+ };
72
+ }
73
+ };
74
+ async function collectFiles(rootDir) {
75
+ const files = [];
76
+ const visit = async (directory) => {
77
+ const entries = await readdir(directory, { withFileTypes: true });
78
+ entries.sort((left, right) => left.name.localeCompare(right.name));
79
+ for (const entry of entries) {
80
+ const absolutePath = resolve(directory, entry.name);
81
+ const details = await lstat(absolutePath);
82
+ if (details.isSymbolicLink()) throw new Error(`Static site cannot contain symbolic links: ${entry.name}`);
83
+ if (details.isDirectory()) {
84
+ await visit(absolutePath);
85
+ continue;
86
+ }
87
+ if (!details.isFile()) throw new Error(`Unsupported static site entry: ${entry.name}`);
88
+ const filePath = relative(rootDir, absolutePath).split("\\").join("/");
89
+ if (filePath.split("/").some((part) => !part || part === "." || part === "..")) throw new Error("Invalid static site path");
90
+ files.push(posix.normalize(filePath));
91
+ if (files.length > MAX_FILES) throw new Error(`Static site has more than ${MAX_FILES} files`);
92
+ }
93
+ };
94
+ await visit(rootDir);
95
+ return files;
96
+ }
97
+ function normalizeInputPath(value) {
98
+ const normalized = value.trim().replace(/\\/g, "/").replace(/^\/+/, "");
99
+ if (!normalized || normalized.includes("\0") || normalized.split("/").some((part) => part === "..")) throw new Error("Invalid static site path");
100
+ return normalized;
101
+ }
102
+ async function sha256File(filePath) {
103
+ const hash = createHash("sha256");
104
+ for await (const chunk of createReadStream(filePath)) hash.update(chunk);
105
+ return hash.digest("hex");
106
+ }
107
+ //#endregion
108
+ export { HostedStaticSitePublicationBuilder };
@@ -0,0 +1,23 @@
1
+ import { HostedPublicationPublisher, HostedShareBindingStore, type HostedShareBinding } from './hosted-session-share.js';
2
+ import { HostedStaticSitePublicationBuilder, type HostedStaticSitePublicationSnapshot } from './hosted-static-site-publication.js';
3
+ export interface PublishHostedStaticSiteInput {
4
+ workspaceRoot: string;
5
+ path: string;
6
+ title?: string;
7
+ description?: string;
8
+ spaFallback?: boolean;
9
+ ttlMs: number;
10
+ maxViews: number | null;
11
+ sessionKey?: string;
12
+ agentId?: string;
13
+ }
14
+ export interface PublishHostedStaticSiteResult {
15
+ binding: HostedShareBinding;
16
+ snapshot: HostedStaticSitePublicationSnapshot;
17
+ }
18
+ /** Build, upload, activate, and locally bind one immutable static-site snapshot. */
19
+ export declare function publishHostedStaticSite(input: PublishHostedStaticSiteInput, deps?: {
20
+ builder?: HostedStaticSitePublicationBuilder;
21
+ publisher?: HostedPublicationPublisher;
22
+ bindings?: HostedShareBindingStore;
23
+ }): Promise<PublishHostedStaticSiteResult>;
@@ -0,0 +1,53 @@
1
+ import { HostedPublicationPublisher, HostedShareBindingStore } from "./hosted-session-share.js";
2
+ import { HostedStaticSitePublicationBuilder } from "./hosted-static-site-publication.js";
3
+ //#region src/share/hosted-static-site-publish.ts
4
+ /** Build, upload, activate, and locally bind one immutable static-site snapshot. */
5
+ async function publishHostedStaticSite(input, deps = {}) {
6
+ const builder = deps.builder ?? new HostedStaticSitePublicationBuilder();
7
+ const publisher = deps.publisher ?? new HostedPublicationPublisher();
8
+ const bindings = deps.bindings ?? new HostedShareBindingStore();
9
+ const snapshot = await builder.build(input.workspaceRoot, {
10
+ path: input.path,
11
+ title: input.title,
12
+ description: input.description,
13
+ spaFallback: input.spaFallback
14
+ });
15
+ const result = await publisher.createPublication(snapshot, {
16
+ ttlMs: input.ttlMs,
17
+ maxViews: input.maxViews
18
+ });
19
+ const now = (/* @__PURE__ */ new Date()).toISOString();
20
+ const binding = {
21
+ ...result,
22
+ kind: "static_site",
23
+ source: {
24
+ kind: "static_site",
25
+ id: input.path,
26
+ version: now
27
+ },
28
+ revisionSources: { [String(result.snapshotRevision)]: {
29
+ kind: "static_site",
30
+ id: input.path,
31
+ version: now
32
+ } },
33
+ workspaceContext: {
34
+ workspaceRoot: input.workspaceRoot,
35
+ ...input.sessionKey ? { sessionKey: input.sessionKey } : {},
36
+ ...input.agentId ? { agentId: input.agentId } : {}
37
+ },
38
+ title: snapshot.title,
39
+ description: input.description?.trim() || null,
40
+ attachmentCount: snapshot.fileCount,
41
+ attachmentIds: snapshot.assets.map((asset) => asset.id),
42
+ createdAt: now,
43
+ updatedAt: now,
44
+ revoked: false
45
+ };
46
+ await bindings.upsert(binding);
47
+ return {
48
+ binding,
49
+ snapshot
50
+ };
51
+ }
52
+ //#endregion
53
+ export { publishHostedStaticSite };
@@ -65,3 +65,4 @@ export declare class NoteShareService {
65
65
  private artifactRelativePath;
66
66
  private artifactDir;
67
67
  }
68
+ export declare function projectPublicNoteMarkdown(note: Note, selected: Set<string>): string;
@@ -2,8 +2,8 @@ import { resolveStateDir } from "../config/paths-state.js";
2
2
  import { createLogger } from "../utils/logger/index.js";
3
3
  import { init_logger } from "../utils/logger.js";
4
4
  import { init_paths } from "../config/paths.js";
5
- import { collectReferencedAttachmentIds } from "../notes/note-attachment-sync.js";
6
5
  import { issueShareAssetTicket, verifyShareAssetTicket } from "./share-asset-ticket.js";
6
+ import { collectReferencedAttachmentIds } from "../notes/note-attachment-sync.js";
7
7
  import { dirname, join } from "node:path";
8
8
  import { createHash, randomUUID } from "node:crypto";
9
9
  import { copyFile, mkdir, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
@@ -155,7 +155,7 @@ var NoteShareService = class {
155
155
  for (const id of referenced) if (!known.has(id)) throw new Error(`Referenced attachment is missing: ${id}`);
156
156
  const selected = requestedAttachmentIds === void 0 ? [...referenced] : Array.from(new Set(requestedAttachmentIds));
157
157
  for (const id of selected) if (!referenced.has(id)) throw new Error(`Attachment is not referenced by this Note: ${id}`);
158
- const projectedMarkdown = projectPublicMarkdown(note, new Set(selected));
158
+ const projectedMarkdown = projectPublicNoteMarkdown(note, new Set(selected));
159
159
  const markdownBytes = Buffer.byteLength(projectedMarkdown, "utf8");
160
160
  if (markdownBytes > cfg.maxMarkdownBytes) throw new Error("Note Markdown exceeds sharing limit");
161
161
  if (selected.length > cfg.maxAttachmentCount) throw new Error("Note has too many attachments to share");
@@ -235,7 +235,7 @@ var NoteShareService = class {
235
235
  return join(resolveStateDir(), this.artifactRelativePath(shareId));
236
236
  }
237
237
  };
238
- function projectPublicMarkdown(note, selected) {
238
+ function projectPublicNoteMarkdown(note, selected) {
239
239
  const canonicalTarget = "xopc-attachment://notes/([^/\\s)]+)/([^\\s)]+)";
240
240
  const assertOwned = new RegExp(canonicalTarget, "gi");
241
241
  note.markdown.replace(assertOwned, (_match, rawNoteId) => {
@@ -260,4 +260,4 @@ function projectPublicMarkdown(note, selected) {
260
260
  return markdown;
261
261
  }
262
262
  //#endregion
263
- export { NoteShareService, NoteShareVersionConflictError };
263
+ export { NoteShareService, NoteShareVersionConflictError, projectPublicNoteMarkdown };