@particle-academy/agent-integrations 0.19.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/bridges/catalog.d.cts +150 -0
  2. package/dist/bridges/catalog.d.ts +150 -0
  3. package/dist/bridges/features.d.cts +108 -0
  4. package/dist/bridges/features.d.ts +108 -0
  5. package/dist/bridges-catalog.cjs +428 -0
  6. package/dist/bridges-catalog.cjs.map +1 -0
  7. package/dist/bridges-catalog.js +7 -0
  8. package/dist/bridges-catalog.js.map +1 -0
  9. package/dist/bridges-features.cjs +341 -0
  10. package/dist/bridges-features.cjs.map +1 -0
  11. package/dist/bridges-features.js +7 -0
  12. package/dist/bridges-features.js.map +1 -0
  13. package/dist/chunk-267JS64O.js +308 -0
  14. package/dist/chunk-267JS64O.js.map +1 -0
  15. package/dist/{chunk-CPNOF4HI.js → chunk-FUI7KXE7.js} +41 -18
  16. package/dist/chunk-FUI7KXE7.js.map +1 -0
  17. package/dist/{chunk-YEEOTIGY.js → chunk-HKQBG2HZ.js} +3 -3
  18. package/dist/{chunk-YEEOTIGY.js.map → chunk-HKQBG2HZ.js.map} +1 -1
  19. package/dist/chunk-XQZGB4FP.js +221 -0
  20. package/dist/chunk-XQZGB4FP.js.map +1 -0
  21. package/dist/components-shared-whiteboard.cjs +39 -16
  22. package/dist/components-shared-whiteboard.cjs.map +1 -1
  23. package/dist/components-shared-whiteboard.js +2 -2
  24. package/dist/index.cjs +558 -18
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.cts +2 -0
  27. package/dist/index.d.ts +2 -0
  28. package/dist/index.js +9 -7
  29. package/dist/index.js.map +1 -1
  30. package/dist/sharing/index.d.cts +7 -1
  31. package/dist/sharing/index.d.ts +7 -1
  32. package/dist/sharing.cjs +39 -16
  33. package/dist/sharing.cjs.map +1 -1
  34. package/dist/sharing.js +1 -1
  35. package/package.json +36 -1
  36. package/dist/chunk-CPNOF4HI.js.map +0 -1
@@ -0,0 +1,308 @@
1
+ import { ensureUndoToolsRegistered, pushUndoEntry } from './chunk-KJ5AOOV7.js';
2
+ import { wrapToolWithActivity } from './chunk-ULJL53DL.js';
3
+ import { textResult, errorResult } from './chunk-4KAIV6OD.js';
4
+
5
+ // src/bridges/catalog.ts
6
+ var DEFAULT_AGENT = { id: "agent", name: "Agent", color: "#a855f7" };
7
+ var str = (v, fallback = "") => typeof v === "string" ? v : fallback;
8
+ var num = (v, fallback = 0) => typeof v === "number" && Number.isFinite(v) ? v : fallback;
9
+ function registerCatalogBridge(host, options) {
10
+ const { adapter } = options;
11
+ const agent = { ...DEFAULT_AGENT, ...options.agent ?? {} };
12
+ const pendingMode = options.pendingMode ?? true;
13
+ const catalogId = adapter.id ?? "catalog";
14
+ const disposers = [];
15
+ ensureUndoToolsRegistered(host, { defaultAgentId: agent.id });
16
+ const target = (elementId, label) => ({
17
+ kind: "custom",
18
+ screenId: adapter.screenId,
19
+ elementId,
20
+ label: label ?? catalogId
21
+ });
22
+ const reg = (name, description, properties, required, handler, resolveTarget) => {
23
+ const wrapped = async (args) => {
24
+ try {
25
+ return await handler(args);
26
+ } catch (e) {
27
+ return errorResult(e instanceof Error ? e.message : String(e));
28
+ }
29
+ };
30
+ const final = resolveTarget ? wrapToolWithActivity(wrapped, {
31
+ toolName: name,
32
+ agent,
33
+ kind: "custom",
34
+ screenId: adapter.screenId,
35
+ resolveTarget: ({ args }) => resolveTarget(args)
36
+ }) : wrapped;
37
+ disposers.push(
38
+ host.registerTool(
39
+ { name, description, inputSchema: { type: "object", properties, required, additionalProperties: false } },
40
+ final
41
+ )
42
+ );
43
+ };
44
+ const guardDestructive = async (action, id, label, hasConfirmArg) => {
45
+ if (!pendingMode) return null;
46
+ if (options.confirm) {
47
+ const ok = await options.confirm({ action, id, label });
48
+ return ok ? null : errorResult(`Declined: ${action} ${id} (human did not confirm).`);
49
+ }
50
+ if (!hasConfirmArg) {
51
+ return errorResult(`${action} is staged (pendingMode). Re-call with confirm:true to apply, or wire a host confirm hook.`);
52
+ }
53
+ return null;
54
+ };
55
+ reg(
56
+ "catalog_list_products",
57
+ "List products: { id, name, active, lookupKey, priceCount }. Pass withTrashed:true to include soft-deleted.",
58
+ { withTrashed: { type: "boolean" } },
59
+ [],
60
+ async (args) => {
61
+ const products = await adapter.products.all({ withTrashed: args.withTrashed === true });
62
+ const list = await Promise.all(
63
+ products.map(async (p) => ({
64
+ id: p.id,
65
+ name: p.name,
66
+ active: p.active ?? true,
67
+ lookupKey: p.lookupKey ?? null,
68
+ priceCount: (await adapter.prices.forProduct(p.id)).length
69
+ }))
70
+ );
71
+ return textResult(JSON.stringify(list, null, 2), list);
72
+ },
73
+ false
74
+ );
75
+ reg(
76
+ "catalog_get_product",
77
+ "Read a product's full JSON plus its prices.",
78
+ { id: { type: "string" }, withTrashed: { type: "boolean" } },
79
+ ["id"],
80
+ async (args) => {
81
+ const id = str(args.id);
82
+ const product = await adapter.products.find(id, { withTrashed: args.withTrashed === true });
83
+ if (!product) return errorResult(`No product with id ${id}`);
84
+ const prices = await adapter.prices.forProduct(id, { withTrashed: args.withTrashed === true });
85
+ const out = { product, prices };
86
+ return textResult(JSON.stringify(out, null, 2), out);
87
+ },
88
+ false
89
+ );
90
+ reg(
91
+ "catalog_list_prices",
92
+ "List prices for a product (pass productId), or every price (omit it).",
93
+ { productId: { type: "string" }, withTrashed: { type: "boolean" } },
94
+ [],
95
+ async (args) => {
96
+ const withTrashed = args.withTrashed === true;
97
+ const prices = args.productId !== void 0 ? await adapter.prices.forProduct(str(args.productId), { withTrashed }) : await adapter.prices.all({ withTrashed });
98
+ const list = prices.map((p) => ({
99
+ id: p.id,
100
+ productId: p.productId,
101
+ currency: p.currency,
102
+ unitAmount: p.unitAmount,
103
+ type: p.type,
104
+ active: p.active ?? true,
105
+ externalId: p.externalId ?? null
106
+ }));
107
+ return textResult(JSON.stringify(list, null, 2), list);
108
+ },
109
+ false
110
+ );
111
+ reg(
112
+ "catalog_create_product",
113
+ "Create a product. `name` is required; id (ULID) is auto-assigned. Returns the created product.",
114
+ {
115
+ name: { type: "string" },
116
+ description: { type: "string" },
117
+ active: { type: "boolean" },
118
+ lookupKey: { type: "string" },
119
+ metadata: { type: "object" }
120
+ },
121
+ ["name"],
122
+ async (args) => {
123
+ const name = str(args.name);
124
+ if (!name) return errorResult("name is required.");
125
+ const product = await adapter.createProduct({
126
+ name,
127
+ ...args.description !== void 0 ? { description: str(args.description) } : {},
128
+ ...args.active !== void 0 ? { active: args.active === true } : {},
129
+ ...args.lookupKey !== void 0 ? { lookupKey: str(args.lookupKey) } : {},
130
+ ...args.metadata && typeof args.metadata === "object" ? { metadata: args.metadata } : {}
131
+ });
132
+ pushUndoEntry(agent.id, {
133
+ timestamp: Date.now(),
134
+ bridgeId: catalogId,
135
+ action: "catalog_create_product",
136
+ label: `Created product ${product.id} (${product.name})`,
137
+ undo: () => void adapter.products.remove(product.id),
138
+ redo: () => void adapter.createProduct({ ...product })
139
+ });
140
+ return textResult(`Created product ${product.id} (${product.name})`, product);
141
+ },
142
+ (args) => target(void 0, `create product ${str(args.name)}`)
143
+ );
144
+ reg(
145
+ "catalog_create_price",
146
+ "Create a price under a product. unitAmount is integer minor units (cents). type is 'recurring' | 'one_time'.",
147
+ {
148
+ productId: { type: "string" },
149
+ currency: { type: "string", description: "ISO-4217, e.g. 'USD'." },
150
+ unitAmount: { type: "number", description: "Price in cents." },
151
+ type: { type: "string", enum: ["recurring", "one_time"] },
152
+ recurringInterval: { type: "string", description: "'month' | 'year' | \u2026 (recurring only)." },
153
+ nickname: { type: "string" },
154
+ lookupKey: { type: "string" },
155
+ metadata: { type: "object" }
156
+ },
157
+ ["productId", "currency", "unitAmount", "type"],
158
+ async (args) => {
159
+ const productId = str(args.productId);
160
+ const product = await adapter.products.find(productId);
161
+ if (!product) return errorResult(`No product with id ${productId}`);
162
+ const type = str(args.type);
163
+ if (type !== "recurring" && type !== "one_time") return errorResult("type must be 'recurring' or 'one_time'.");
164
+ const price = await adapter.createPrice({
165
+ productId,
166
+ currency: str(args.currency),
167
+ unitAmount: num(args.unitAmount),
168
+ type,
169
+ ...args.recurringInterval !== void 0 ? { recurringInterval: str(args.recurringInterval) } : {},
170
+ ...args.nickname !== void 0 ? { nickname: str(args.nickname) } : {},
171
+ ...args.lookupKey !== void 0 ? { lookupKey: str(args.lookupKey) } : {},
172
+ ...args.metadata && typeof args.metadata === "object" ? { metadata: args.metadata } : {}
173
+ });
174
+ pushUndoEntry(agent.id, {
175
+ timestamp: Date.now(),
176
+ bridgeId: catalogId,
177
+ action: "catalog_create_price",
178
+ label: `Created price ${price.id} on ${productId}`,
179
+ undo: () => void adapter.prices.remove(price.id),
180
+ redo: () => void adapter.createPrice({ ...price })
181
+ });
182
+ return textResult(`Created ${type} price ${price.id} on ${productId}`, price);
183
+ },
184
+ (args) => target(str(args.productId), `create price on ${str(args.productId)}`)
185
+ );
186
+ reg(
187
+ "catalog_delete_product",
188
+ "Soft-delete a product (preserves financial history). Staged in pendingMode \u2014 pass confirm:true or wire a host confirm hook.",
189
+ { id: { type: "string" }, confirm: { type: "boolean" } },
190
+ ["id"],
191
+ async (args) => {
192
+ const id = str(args.id);
193
+ const product = await adapter.products.find(id, { withTrashed: true });
194
+ if (!product) return errorResult(`No product with id ${id}`);
195
+ const blocked = await guardDestructive("catalog_delete_product", id, product.name, args.confirm === true);
196
+ if (blocked) return blocked;
197
+ const snapshot = { ...product };
198
+ await adapter.products.remove(id);
199
+ pushUndoEntry(agent.id, {
200
+ timestamp: Date.now(),
201
+ bridgeId: catalogId,
202
+ action: "catalog_delete_product",
203
+ label: `Deleted product ${id} (${product.name})`,
204
+ undo: () => void adapter.createProduct({ ...snapshot }),
205
+ redo: () => void adapter.products.remove(id)
206
+ });
207
+ return textResult(`Deleted product ${id}`, { id });
208
+ },
209
+ (args) => target(str(args.id), `delete product ${str(args.id)}`)
210
+ );
211
+ reg(
212
+ "catalog_delete_price",
213
+ "Soft-delete a price (mirrors Stripe archiving). Staged in pendingMode \u2014 pass confirm:true or wire a host confirm hook.",
214
+ { id: { type: "string" }, confirm: { type: "boolean" } },
215
+ ["id"],
216
+ async (args) => {
217
+ const id = str(args.id);
218
+ const price = await adapter.prices.find(id, { withTrashed: true });
219
+ if (!price) return errorResult(`No price with id ${id}`);
220
+ const blocked = await guardDestructive("catalog_delete_price", id, id, args.confirm === true);
221
+ if (blocked) return blocked;
222
+ const snapshot = { ...price };
223
+ await adapter.prices.remove(id);
224
+ pushUndoEntry(agent.id, {
225
+ timestamp: Date.now(),
226
+ bridgeId: catalogId,
227
+ action: "catalog_delete_price",
228
+ label: `Deleted price ${id}`,
229
+ undo: () => void adapter.createPrice({ ...snapshot }),
230
+ redo: () => void adapter.prices.remove(id)
231
+ });
232
+ return textResult(`Deleted price ${id}`, { id });
233
+ },
234
+ (args) => target(str(args.id), `delete price ${str(args.id)}`)
235
+ );
236
+ reg(
237
+ "catalog_sync_product",
238
+ "Push a product + its prices to Stripe (creates/updates the Stripe Product + Prices, stamps external ids).",
239
+ { id: { type: "string" } },
240
+ ["id"],
241
+ async (args) => {
242
+ if (!adapter.syncProductAndPrices) return errorResult("Host did not wire Stripe sync.");
243
+ const id = str(args.id);
244
+ const product = await adapter.products.find(id);
245
+ if (!product) return errorResult(`No product with id ${id}`);
246
+ const synced = await adapter.syncProductAndPrices(product);
247
+ return textResult(`Synced product ${id} to Stripe (${synced.externalId ?? "no external id"})`, synced);
248
+ },
249
+ (args) => target(str(args.id), `sync product ${str(args.id)}`)
250
+ );
251
+ reg(
252
+ "catalog_test_connection",
253
+ "Verify the Stripe connection \u2014 returns { success, message, productCount? }.",
254
+ {},
255
+ [],
256
+ async () => {
257
+ if (!adapter.testConnection) return errorResult("Host did not wire a Stripe connection test.");
258
+ const result = await adapter.testConnection();
259
+ return textResult(JSON.stringify(result, null, 2), result);
260
+ },
261
+ false
262
+ );
263
+ reg(
264
+ "catalog_create_checkout",
265
+ "Build a hosted Stripe Checkout URL for a price. Recurring prices \u2192 subscription; one-time \u2192 payment (pass quantity). Requires the price be synced to Stripe first.",
266
+ {
267
+ priceId: { type: "string" },
268
+ successUrl: { type: "string" },
269
+ cancelUrl: { type: "string" },
270
+ customer: { type: "string", description: "Stripe customer id (optional \u2014 Checkout collects one if omitted)." },
271
+ quantity: { type: "number", description: "One-time checkouts only. Default 1." },
272
+ metadata: { type: "object" }
273
+ },
274
+ ["priceId", "successUrl", "cancelUrl"],
275
+ async (args) => {
276
+ const priceId = str(args.priceId);
277
+ const price = await adapter.prices.find(priceId);
278
+ if (!price) return errorResult(`No price with id ${priceId}`);
279
+ const checkoutArgs = {
280
+ successUrl: str(args.successUrl),
281
+ cancelUrl: str(args.cancelUrl),
282
+ ...args.customer !== void 0 ? { customer: str(args.customer) } : {},
283
+ ...args.metadata && typeof args.metadata === "object" ? { metadata: args.metadata } : {}
284
+ };
285
+ let url;
286
+ if (price.type === "recurring") {
287
+ if (!adapter.getSubscriptionCheckoutUrl) return errorResult("Host did not wire subscription checkout.");
288
+ url = await adapter.getSubscriptionCheckoutUrl(price, checkoutArgs);
289
+ } else {
290
+ if (!adapter.getOneTimeCheckoutUrl) return errorResult("Host did not wire one-time checkout.");
291
+ url = await adapter.getOneTimeCheckoutUrl(price, { ...checkoutArgs, quantity: num(args.quantity, 1) });
292
+ }
293
+ return textResult(url || "(no url)", { priceId, type: price.type, url });
294
+ },
295
+ (args) => target(str(args.priceId), `checkout ${str(args.priceId)}`)
296
+ );
297
+ return {
298
+ id: `catalog:${catalogId}`,
299
+ title: adapter.title ?? "Catalog",
300
+ dispose: () => {
301
+ for (const d of disposers.splice(0)) d();
302
+ }
303
+ };
304
+ }
305
+
306
+ export { registerCatalogBridge };
307
+ //# sourceMappingURL=chunk-267JS64O.js.map
308
+ //# sourceMappingURL=chunk-267JS64O.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/bridges/catalog.ts"],"names":[],"mappings":"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","file":"chunk-267JS64O.js","sourcesContent":["import { textResult, errorResult } from \"../mcp/server\";\nimport type { ToolHost } from \"../mcp/tool-host\";\nimport type { JsonObject } from \"../mcp/types\";\nimport type { Bridge } from \"./types\";\nimport { wrapToolWithActivity } from \"../presence/wrap-tool-with-activity\";\nimport type { AgentTarget } from \"../presence/types\";\nimport { pushUndoEntry } from \"../undo/undo-stack\";\nimport { ensureUndoToolsRegistered } from \"../undo/undo-tools\";\n\n/**\n * Headless bridge for `@particle-academy/fancy-catalog` — a Stripe catalog with\n * NO UI surface. The adapter exposes the catalog's STORES + operations (not UI\n * getters/setters), and the tools are CRUD over products / prices + checkout +\n * Stripe sync. Mutations still funnel through `wrapToolWithActivity` so presence\n * + undo compose, and the destructive delete is `pendingMode`-gated.\n *\n * The adapter shapes below are STRUCTURAL mirrors of fancy-catalog's public\n * `Catalog` + store interfaces, defined LOCALLY so this bridge builds with the\n * sibling package absent (it's an optional peer). A real `Catalog` instance is\n * assignable to `CatalogBridgeAdapter` with no import — TypeScript is structural.\n */\n\n/** Minimal product shape crossing the MCP wire (mirror of fancy-catalog `Product`). */\nexport type CatalogProduct = {\n id: string;\n name: string;\n description?: string | null;\n active?: boolean;\n lookupKey?: string | null;\n externalId?: string | null;\n metadata?: Record<string, unknown> | null;\n [k: string]: unknown;\n};\n\n/** Minimal price shape crossing the MCP wire (mirror of fancy-catalog `Price`). */\nexport type CatalogPrice = {\n id: string;\n productId: string;\n active?: boolean;\n currency: string;\n unitAmount: number;\n type: \"recurring\" | \"one_time\";\n externalId?: string | null;\n [k: string]: unknown;\n};\n\n/** Common checkout args (mirror of fancy-catalog `CheckoutArgs`). */\nexport type CatalogCheckoutArgs = {\n customer?: string;\n successUrl: string;\n cancelUrl: string;\n metadata?: Record<string, string>;\n /** One-time checkouts only. */\n quantity?: number;\n};\n\n/**\n * Adapter = the catalog instance / stores. A live `Catalog` from fancy-catalog\n * satisfies this directly (`catalog.products`, `catalog.createProduct`, …); a\n * host can also hand-roll one over its own persistence.\n */\nexport type CatalogBridgeAdapter = {\n /** Stable id for this catalog instance (multiple catalogs ⇒ distinct ids). */\n id?: string;\n /** Display label for logs / presence. */\n title?: string;\n /** Optional fancy-screens screen id (usually unset — this is headless). */\n screenId?: string;\n\n /** Product store — `catalog.products`. */\n products: {\n find(id: string, opts?: { withTrashed?: boolean }): CatalogProduct | null | Promise<CatalogProduct | null>;\n all(opts?: { withTrashed?: boolean }): CatalogProduct[] | Promise<CatalogProduct[]>;\n remove(id: string): void | Promise<void>;\n };\n /** Price store — `catalog.prices`. */\n prices: {\n find(id: string, opts?: { withTrashed?: boolean }): CatalogPrice | null | Promise<CatalogPrice | null>;\n forProduct(productId: string, opts?: { withTrashed?: boolean }): CatalogPrice[] | Promise<CatalogPrice[]>;\n all(opts?: { withTrashed?: boolean }): CatalogPrice[] | Promise<CatalogPrice[]>;\n remove(id: string): void | Promise<void>;\n };\n\n /** Authoring helpers — `catalog.createProduct` / `catalog.createPrice` (ULIDs auto-assigned). */\n createProduct(input: Partial<CatalogProduct> & { name: string }): Promise<CatalogProduct> | CatalogProduct;\n createPrice(\n input: Partial<CatalogPrice> & { productId: string; currency: string; unitAmount: number; type: \"recurring\" | \"one_time\" },\n ): Promise<CatalogPrice> | CatalogPrice;\n\n /** Stripe sync — `catalog.syncProductAndPrices`. Optional (no-op when absent). */\n syncProductAndPrices?(product: CatalogProduct): Promise<CatalogProduct> | CatalogProduct;\n /** Connection test — `catalog.testConnection`. Optional. */\n testConnection?(): Promise<{ success: boolean; message: string; productCount?: number }> | { success: boolean; message: string; productCount?: number };\n\n /** Hosted Checkout — `catalog.getSubscriptionCheckoutUrl` / `getOneTimeCheckoutUrl`. Optional. */\n getSubscriptionCheckoutUrl?(price: CatalogPrice, args: CatalogCheckoutArgs): Promise<string> | string;\n getOneTimeCheckoutUrl?(price: CatalogPrice, args: CatalogCheckoutArgs): Promise<string> | string;\n};\n\nexport type CatalogBridgeOptions = {\n adapter: CatalogBridgeAdapter;\n /** Identity stamped on activity + undo entries. */\n agent?: { id: string; name?: string; color?: string };\n /**\n * Trust-but-verify. When on (default), destructive ops (`catalog_delete_product`,\n * `catalog_delete_price`) route through `adapter`-less staging: they require an\n * explicit `confirm: true` arg OR a host `confirm` callback. Read + create +\n * checkout-url tools are unaffected.\n */\n pendingMode?: boolean;\n /** Host confirm hook for destructive ops (pendingMode). Resolves true to proceed. */\n confirm?: (req: { action: string; id: string; label: string }) => Promise<boolean> | boolean;\n};\n\nconst DEFAULT_AGENT = { id: \"agent\", name: \"Agent\", color: \"#a855f7\" };\n\nconst str = (v: unknown, fallback = \"\"): string => (typeof v === \"string\" ? v : fallback);\nconst num = (v: unknown, fallback = 0): number => (typeof v === \"number\" && Number.isFinite(v) ? v : fallback);\n\n/**\n * registerCatalogBridge — full MCP tool set over a headless Stripe catalog.\n *\n * catalog_list_products list products ({ id, name, active, priceCount })\n * catalog_get_product full product JSON + its prices\n * catalog_create_product create a product (ULID auto-assigned)\n * catalog_delete_product soft-delete a product (pendingMode-gated, undoable*)\n * catalog_list_prices list a product's prices (or all)\n * catalog_create_price create a price under a product\n * catalog_delete_price soft-delete a price (pendingMode-gated)\n * catalog_sync_product push a product + its prices to Stripe\n * catalog_test_connection verify the Stripe connection\n * catalog_create_checkout build a hosted Checkout URL for a price\n *\n * *delete undo is best-effort: stores expose soft-delete (`remove`) but no\n * un-delete, so the undo closure re-creates from the captured snapshot.\n */\nexport function registerCatalogBridge(host: ToolHost, options: CatalogBridgeOptions): Bridge {\n const { adapter } = options;\n const agent = { ...DEFAULT_AGENT, ...(options.agent ?? {}) };\n const pendingMode = options.pendingMode ?? true;\n const catalogId = adapter.id ?? \"catalog\";\n const disposers: Array<() => void> = [];\n\n ensureUndoToolsRegistered(host, { defaultAgentId: agent.id });\n\n const target = (elementId?: string, label?: string): AgentTarget => ({\n kind: \"custom\",\n screenId: adapter.screenId,\n elementId,\n label: label ?? catalogId,\n });\n\n const reg = (\n name: string,\n description: string,\n properties: Record<string, unknown>,\n required: string[],\n handler: (args: JsonObject) => Promise<unknown> | unknown,\n resolveTarget: false | ((args: JsonObject) => AgentTarget),\n ) => {\n const wrapped = async (args: JsonObject) => {\n try {\n return await handler(args);\n } catch (e) {\n return errorResult(e instanceof Error ? e.message : String(e));\n }\n };\n const final = resolveTarget\n ? wrapToolWithActivity(wrapped as never, {\n toolName: name,\n agent,\n kind: \"custom\",\n screenId: adapter.screenId,\n resolveTarget: ({ args }) => resolveTarget(args as JsonObject),\n })\n : wrapped;\n disposers.push(\n host.registerTool(\n { name, description, inputSchema: { type: \"object\", properties: properties as Record<string, never>, required, additionalProperties: false } },\n final as never,\n ),\n );\n };\n\n /** Stage a destructive op behind the human (pendingMode). Returns null to proceed, or an errorResult to block. */\n const guardDestructive = async (action: string, id: string, label: string, hasConfirmArg: boolean) => {\n if (!pendingMode) return null;\n if (options.confirm) {\n const ok = await options.confirm({ action, id, label });\n return ok ? null : errorResult(`Declined: ${action} ${id} (human did not confirm).`);\n }\n // No host confirm hook → require an explicit confirm:true arg as the staged ack.\n if (!hasConfirmArg) {\n return errorResult(`${action} is staged (pendingMode). Re-call with confirm:true to apply, or wire a host confirm hook.`);\n }\n return null;\n };\n\n // ─── Read tools ──────────────────────────────────────────────────────────\n\n reg(\n \"catalog_list_products\",\n \"List products: { id, name, active, lookupKey, priceCount }. Pass withTrashed:true to include soft-deleted.\",\n { withTrashed: { type: \"boolean\" } },\n [],\n async (args) => {\n const products = await adapter.products.all({ withTrashed: args.withTrashed === true });\n const list = await Promise.all(\n products.map(async (p) => ({\n id: p.id,\n name: p.name,\n active: p.active ?? true,\n lookupKey: p.lookupKey ?? null,\n priceCount: (await adapter.prices.forProduct(p.id)).length,\n })),\n );\n return textResult(JSON.stringify(list, null, 2), list);\n },\n false,\n );\n\n reg(\n \"catalog_get_product\",\n \"Read a product's full JSON plus its prices.\",\n { id: { type: \"string\" }, withTrashed: { type: \"boolean\" } },\n [\"id\"],\n async (args) => {\n const id = str(args.id);\n const product = await adapter.products.find(id, { withTrashed: args.withTrashed === true });\n if (!product) return errorResult(`No product with id ${id}`);\n const prices = await adapter.prices.forProduct(id, { withTrashed: args.withTrashed === true });\n const out = { product, prices };\n return textResult(JSON.stringify(out, null, 2), out);\n },\n false,\n );\n\n reg(\n \"catalog_list_prices\",\n \"List prices for a product (pass productId), or every price (omit it).\",\n { productId: { type: \"string\" }, withTrashed: { type: \"boolean\" } },\n [],\n async (args) => {\n const withTrashed = args.withTrashed === true;\n const prices = args.productId !== undefined\n ? await adapter.prices.forProduct(str(args.productId), { withTrashed })\n : await adapter.prices.all({ withTrashed });\n const list = prices.map((p) => ({\n id: p.id,\n productId: p.productId,\n currency: p.currency,\n unitAmount: p.unitAmount,\n type: p.type,\n active: p.active ?? true,\n externalId: p.externalId ?? null,\n }));\n return textResult(JSON.stringify(list, null, 2), list);\n },\n false,\n );\n\n // ─── Create tools ────────────────────────────────────────────────────────\n\n reg(\n \"catalog_create_product\",\n \"Create a product. `name` is required; id (ULID) is auto-assigned. Returns the created product.\",\n {\n name: { type: \"string\" },\n description: { type: \"string\" },\n active: { type: \"boolean\" },\n lookupKey: { type: \"string\" },\n metadata: { type: \"object\" },\n },\n [\"name\"],\n async (args) => {\n const name = str(args.name);\n if (!name) return errorResult(\"name is required.\");\n const product = await adapter.createProduct({\n name,\n ...(args.description !== undefined ? { description: str(args.description) } : {}),\n ...(args.active !== undefined ? { active: args.active === true } : {}),\n ...(args.lookupKey !== undefined ? { lookupKey: str(args.lookupKey) } : {}),\n ...(args.metadata && typeof args.metadata === \"object\" ? { metadata: args.metadata as Record<string, unknown> } : {}),\n });\n pushUndoEntry(agent.id, {\n timestamp: Date.now(),\n bridgeId: catalogId,\n action: \"catalog_create_product\",\n label: `Created product ${product.id} (${product.name})`,\n undo: () => void adapter.products.remove(product.id),\n redo: () => void adapter.createProduct({ ...product }),\n });\n return textResult(`Created product ${product.id} (${product.name})`, product);\n },\n (args) => target(undefined, `create product ${str(args.name)}`),\n );\n\n reg(\n \"catalog_create_price\",\n \"Create a price under a product. unitAmount is integer minor units (cents). type is 'recurring' | 'one_time'.\",\n {\n productId: { type: \"string\" },\n currency: { type: \"string\", description: \"ISO-4217, e.g. 'USD'.\" },\n unitAmount: { type: \"number\", description: \"Price in cents.\" },\n type: { type: \"string\", enum: [\"recurring\", \"one_time\"] },\n recurringInterval: { type: \"string\", description: \"'month' | 'year' | … (recurring only).\" },\n nickname: { type: \"string\" },\n lookupKey: { type: \"string\" },\n metadata: { type: \"object\" },\n },\n [\"productId\", \"currency\", \"unitAmount\", \"type\"],\n async (args) => {\n const productId = str(args.productId);\n const product = await adapter.products.find(productId);\n if (!product) return errorResult(`No product with id ${productId}`);\n const type = str(args.type) as \"recurring\" | \"one_time\";\n if (type !== \"recurring\" && type !== \"one_time\") return errorResult(\"type must be 'recurring' or 'one_time'.\");\n const price = await adapter.createPrice({\n productId,\n currency: str(args.currency),\n unitAmount: num(args.unitAmount),\n type,\n ...(args.recurringInterval !== undefined ? { recurringInterval: str(args.recurringInterval) } : {}),\n ...(args.nickname !== undefined ? { nickname: str(args.nickname) } : {}),\n ...(args.lookupKey !== undefined ? { lookupKey: str(args.lookupKey) } : {}),\n ...(args.metadata && typeof args.metadata === \"object\" ? { metadata: args.metadata as Record<string, unknown> } : {}),\n });\n pushUndoEntry(agent.id, {\n timestamp: Date.now(),\n bridgeId: catalogId,\n action: \"catalog_create_price\",\n label: `Created price ${price.id} on ${productId}`,\n undo: () => void adapter.prices.remove(price.id),\n redo: () => void adapter.createPrice({ ...price }),\n });\n return textResult(`Created ${type} price ${price.id} on ${productId}`, price);\n },\n (args) => target(str(args.productId), `create price on ${str(args.productId)}`),\n );\n\n // ─── Destructive tools (pendingMode-gated) ───────────────────────────────\n\n reg(\n \"catalog_delete_product\",\n \"Soft-delete a product (preserves financial history). Staged in pendingMode — pass confirm:true or wire a host confirm hook.\",\n { id: { type: \"string\" }, confirm: { type: \"boolean\" } },\n [\"id\"],\n async (args) => {\n const id = str(args.id);\n const product = await adapter.products.find(id, { withTrashed: true });\n if (!product) return errorResult(`No product with id ${id}`);\n const blocked = await guardDestructive(\"catalog_delete_product\", id, product.name, args.confirm === true);\n if (blocked) return blocked;\n const snapshot = { ...product };\n await adapter.products.remove(id);\n pushUndoEntry(agent.id, {\n timestamp: Date.now(),\n bridgeId: catalogId,\n action: \"catalog_delete_product\",\n label: `Deleted product ${id} (${product.name})`,\n undo: () => void adapter.createProduct({ ...snapshot }),\n redo: () => void adapter.products.remove(id),\n });\n return textResult(`Deleted product ${id}`, { id });\n },\n (args) => target(str(args.id), `delete product ${str(args.id)}`),\n );\n\n reg(\n \"catalog_delete_price\",\n \"Soft-delete a price (mirrors Stripe archiving). Staged in pendingMode — pass confirm:true or wire a host confirm hook.\",\n { id: { type: \"string\" }, confirm: { type: \"boolean\" } },\n [\"id\"],\n async (args) => {\n const id = str(args.id);\n const price = await adapter.prices.find(id, { withTrashed: true });\n if (!price) return errorResult(`No price with id ${id}`);\n const blocked = await guardDestructive(\"catalog_delete_price\", id, id, args.confirm === true);\n if (blocked) return blocked;\n const snapshot = { ...price };\n await adapter.prices.remove(id);\n pushUndoEntry(agent.id, {\n timestamp: Date.now(),\n bridgeId: catalogId,\n action: \"catalog_delete_price\",\n label: `Deleted price ${id}`,\n undo: () => void adapter.createPrice({ ...snapshot }),\n redo: () => void adapter.prices.remove(id),\n });\n return textResult(`Deleted price ${id}`, { id });\n },\n (args) => target(str(args.id), `delete price ${str(args.id)}`),\n );\n\n // ─── Stripe sync ─────────────────────────────────────────────────────────\n\n reg(\n \"catalog_sync_product\",\n \"Push a product + its prices to Stripe (creates/updates the Stripe Product + Prices, stamps external ids).\",\n { id: { type: \"string\" } },\n [\"id\"],\n async (args) => {\n if (!adapter.syncProductAndPrices) return errorResult(\"Host did not wire Stripe sync.\");\n const id = str(args.id);\n const product = await adapter.products.find(id);\n if (!product) return errorResult(`No product with id ${id}`);\n const synced = await adapter.syncProductAndPrices(product);\n return textResult(`Synced product ${id} to Stripe (${synced.externalId ?? \"no external id\"})`, synced);\n },\n (args) => target(str(args.id), `sync product ${str(args.id)}`),\n );\n\n reg(\n \"catalog_test_connection\",\n \"Verify the Stripe connection — returns { success, message, productCount? }.\",\n {},\n [],\n async () => {\n if (!adapter.testConnection) return errorResult(\"Host did not wire a Stripe connection test.\");\n const result = await adapter.testConnection();\n return textResult(JSON.stringify(result, null, 2), result);\n },\n false,\n );\n\n // ─── Checkout ────────────────────────────────────────────────────────────\n\n reg(\n \"catalog_create_checkout\",\n \"Build a hosted Stripe Checkout URL for a price. Recurring prices → subscription; one-time → payment (pass quantity). Requires the price be synced to Stripe first.\",\n {\n priceId: { type: \"string\" },\n successUrl: { type: \"string\" },\n cancelUrl: { type: \"string\" },\n customer: { type: \"string\", description: \"Stripe customer id (optional — Checkout collects one if omitted).\" },\n quantity: { type: \"number\", description: \"One-time checkouts only. Default 1.\" },\n metadata: { type: \"object\" },\n },\n [\"priceId\", \"successUrl\", \"cancelUrl\"],\n async (args) => {\n const priceId = str(args.priceId);\n const price = await adapter.prices.find(priceId);\n if (!price) return errorResult(`No price with id ${priceId}`);\n const checkoutArgs: CatalogCheckoutArgs = {\n successUrl: str(args.successUrl),\n cancelUrl: str(args.cancelUrl),\n ...(args.customer !== undefined ? { customer: str(args.customer) } : {}),\n ...(args.metadata && typeof args.metadata === \"object\" ? { metadata: args.metadata as Record<string, string> } : {}),\n };\n let url: string;\n if (price.type === \"recurring\") {\n if (!adapter.getSubscriptionCheckoutUrl) return errorResult(\"Host did not wire subscription checkout.\");\n url = await adapter.getSubscriptionCheckoutUrl(price, checkoutArgs);\n } else {\n if (!adapter.getOneTimeCheckoutUrl) return errorResult(\"Host did not wire one-time checkout.\");\n url = await adapter.getOneTimeCheckoutUrl(price, { ...checkoutArgs, quantity: num(args.quantity, 1) });\n }\n return textResult(url || \"(no url)\", { priceId, type: price.type, url });\n },\n (args) => target(str(args.priceId), `checkout ${str(args.priceId)}`),\n );\n\n return {\n id: `catalog:${catalogId}`,\n title: adapter.title ?? \"Catalog\",\n dispose: () => {\n for (const d of disposers.splice(0)) d();\n },\n };\n}\n"]}
@@ -57,6 +57,7 @@ function constantTimeEqual(a, b) {
57
57
  // src/sharing/sse-relay.ts
58
58
  var SseRelayTransport = class {
59
59
  constructor(options) {
60
+ this.disposed = false;
60
61
  this.sendQueue = [];
61
62
  this.connected = false;
62
63
  this.listeners = /* @__PURE__ */ new Set();
@@ -67,26 +68,45 @@ var SseRelayTransport = class {
67
68
  bindServer(server) {
68
69
  this.server = server;
69
70
  }
70
- /** Open the SSE channel. Idempotent. */
71
+ /** Open the receive channel. Idempotent. */
71
72
  start() {
72
- if (this.connected || typeof window === "undefined") return;
73
- const url = `${this.opts.baseUrl}/${encodeURIComponent(this.opts.sessionId)}/events?token=${encodeURIComponent(this.opts.token)}`;
73
+ if (this.connected || this.disposed || typeof window === "undefined") return;
74
74
  this.setState("connecting");
75
+ void import('@particle-academy/fancy-cf-relay').then(({ createRelayChannel }) => {
76
+ if (this.connected || this.disposed) return;
77
+ this.channel = createRelayChannel({
78
+ baseUrl: this.opts.baseUrl,
79
+ session: this.opts.sessionId,
80
+ token: this.opts.token,
81
+ transport: "auto",
82
+ receiveDirection: "inbound",
83
+ sendPath: "outbox",
84
+ onFrame: (raw) => this.handleInbound(raw),
85
+ onOpen: () => this.markOpen(),
86
+ onError: () => this.setState("error"),
87
+ fetchImpl: this.opts.fetch
88
+ });
89
+ this.channel.start();
90
+ }).catch(() => {
91
+ if (!this.disposed) this.startSse();
92
+ });
93
+ }
94
+ /** Fallback receive leg: a plain EventSource (no fancy-cf-relay installed). */
95
+ startSse() {
96
+ if (this.connected || this.disposed) return;
97
+ const url = `${this.opts.baseUrl}/${encodeURIComponent(this.opts.sessionId)}/events?token=${encodeURIComponent(this.opts.token)}`;
75
98
  const es = new EventSource(url, { withCredentials: false });
76
99
  this.es = es;
77
- es.addEventListener("open", () => {
78
- this.connected = true;
79
- this.setState("open");
80
- const queued = this.sendQueue.splice(0);
81
- for (const msg of queued) this.postOut(msg);
82
- });
83
- es.addEventListener("mcp", (ev) => {
84
- const raw = ev.data;
85
- this.handleInbound(raw);
86
- });
87
- es.addEventListener("error", () => {
88
- this.setState("error");
89
- });
100
+ es.addEventListener("open", () => this.markOpen());
101
+ es.addEventListener("mcp", (ev) => this.handleInbound(ev.data));
102
+ es.addEventListener("error", () => this.setState("error"));
103
+ }
104
+ /** Mark the channel live + flush any queued outbound frames. */
105
+ markOpen() {
106
+ this.connected = true;
107
+ this.setState("open");
108
+ const queued = this.sendQueue.splice(0);
109
+ for (const msg of queued) this.postOut(msg);
90
110
  }
91
111
  send(message) {
92
112
  if (!this.connected) {
@@ -96,6 +116,9 @@ var SseRelayTransport = class {
96
116
  this.postOut(message);
97
117
  }
98
118
  close() {
119
+ this.disposed = true;
120
+ this.channel?.stop();
121
+ this.channel = void 0;
99
122
  this.es?.close();
100
123
  this.es = void 0;
101
124
  this.connected = false;
@@ -168,5 +191,5 @@ function attachSseRelay(server, options) {
168
191
  }
169
192
 
170
193
  export { SseRelayTransport, attachSseRelay, buildShareConfig, buildShareUrl, constantTimeEqual, createSessionDescriptor, describeSession, readSessionFromUrl };
171
- //# sourceMappingURL=chunk-CPNOF4HI.js.map
172
- //# sourceMappingURL=chunk-CPNOF4HI.js.map
194
+ //# sourceMappingURL=chunk-FUI7KXE7.js.map
195
+ //# sourceMappingURL=chunk-FUI7KXE7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/sharing/token.ts","../src/sharing/sse-relay.ts"],"names":[],"mappings":"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","file":"chunk-FUI7KXE7.js","sourcesContent":["/**\n * Session-token utilities. The token is a high-entropy secret; possession\n * grants read/write on the session. We don't HMAC frames — frames carry\n * the token directly (which is fine for in-process / same-origin / TLS\n * transports). For lower-trust transports, host apps can layer signing\n * on top of the BroadcastChannelTransport.\n */\n\nconst TOKEN_BYTES = 24; // 192 bits, base64url-encoded → 32 chars\n\nexport type SessionDescriptor = {\n /** Stable session identifier. Channel name = `fai:share:${id}`. */\n id: string;\n /** Secret token. Treat as a password — anyone with it can read/write. */\n token: string;\n /** Pretty hash for display (first 8 chars of token). */\n display: string;\n};\n\nexport function createSessionDescriptor(): SessionDescriptor {\n const id = randomId(8);\n const token = randomToken();\n return { id, token, display: token.slice(0, 8) };\n}\n\nexport function describeSession(id: string, token: string): SessionDescriptor {\n return { id, token, display: token.slice(0, 8) };\n}\n\n/** Build the shareable URL for the current page (preserves path, adds session+token). */\nexport function buildShareUrl(\n descriptor: SessionDescriptor,\n baseUrl: string = typeof window !== \"undefined\" ? window.location.href.split(\"?\")[0] : \"\",\n): string {\n const u = new URL(baseUrl);\n u.searchParams.set(\"session\", descriptor.id);\n u.searchParams.set(\"token\", descriptor.token);\n return u.toString();\n}\n\n/** Build the JSON config form (suitable for Claude Desktop / Cline / etc.). */\nexport function buildShareConfig(descriptor: SessionDescriptor, transport = \"broadcast-channel\") {\n return {\n name: `whiteboard-${descriptor.id}`,\n transport,\n session: descriptor.id,\n token: descriptor.token,\n channel: `fai:share:${descriptor.id}`,\n protocol_version: \"2025-06-18\",\n };\n}\n\n/** Read session descriptor from current URL, or null if not a shared link. */\nexport function readSessionFromUrl(): SessionDescriptor | null {\n if (typeof window === \"undefined\") return null;\n const params = new URL(window.location.href).searchParams;\n const id = params.get(\"session\");\n const token = params.get(\"token\");\n if (!id || !token) return null;\n return describeSession(id, token);\n}\n\nfunction randomToken(): string {\n const bytes = new Uint8Array(TOKEN_BYTES);\n crypto.getRandomValues(bytes);\n return base64Url(bytes);\n}\n\nfunction randomId(len: number): string {\n const bytes = new Uint8Array(Math.ceil((len * 3) / 4));\n crypto.getRandomValues(bytes);\n return base64Url(bytes).slice(0, len);\n}\n\nfunction base64Url(bytes: Uint8Array): string {\n let s = \"\";\n for (const b of bytes) s += String.fromCharCode(b);\n return btoa(s).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\n/** Constant-time string compare so a mismatched token leaks no timing info. */\nexport function constantTimeEqual(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);\n return diff === 0;\n}\n","import type { JsonRpcMessage } from \"../mcp/types\";\nimport type { Transport } from \"../mcp/server\";\nimport type { MicroMcpServer } from \"../mcp/server\";\nimport type { RelayChannelHandle } from \"@particle-academy/fancy-cf-relay\";\nimport { constantTimeEqual } from \"./token\";\n\n/**\n * SseRelayTransport — bridges the in-page MicroMcpServer to a host-app\n * relay broker over Server-Sent Events (inbound) + POST (outbound).\n *\n * Wire model:\n * - Browser opens an EventSource at `${baseUrl}/${sessionId}/events?token=…`.\n * Each `event: mcp` carries one JSON-RPC frame from a remote client.\n * - Browser POSTs JSON-RPC frames to `${baseUrl}/${sessionId}/outbox?token=…`\n * when the local server has a response/notification to send.\n *\n * The host provides the relay endpoint (any HTTP server). See the demo\n * `WhiteboardShareController` for the reference implementation.\n *\n * Token authentication is the host's job — this transport just carries the\n * token in the query string. For lower-trust deployments, layer signing on\n * top by wrapping `send` / `deliverFromRemote`.\n */\nexport type SseRelayOptions = {\n baseUrl: string;\n sessionId: string;\n token: string;\n /** Override fetch (testing / non-browser). Defaults to global fetch. */\n fetch?: typeof fetch;\n};\n\nexport class SseRelayTransport implements Transport {\n private server?: MicroMcpServer;\n private es?: EventSource;\n private channel?: RelayChannelHandle;\n private disposed = false;\n private opts: SseRelayOptions;\n private sendQueue: JsonRpcMessage[] = [];\n private connected = false;\n private listeners = new Set<(state: RelayState) => void>();\n private state: RelayState = \"idle\";\n private expectedToken: string;\n\n constructor(options: SseRelayOptions) {\n this.opts = options;\n this.expectedToken = options.token;\n }\n\n bindServer(server: MicroMcpServer): void {\n this.server = server;\n }\n\n /** Open the receive channel. Idempotent. */\n start(): void {\n if (this.connected || this.disposed || typeof window === \"undefined\") return;\n this.setState(\"connecting\");\n // Prefer @particle-academy/fancy-cf-relay's adaptive channel (auto SSE↔\n // long-poll with Cloudflare detection) so the receive leg survives a\n // Cloudflare HTTP/3 (QUIC) edge that resets long-lived SSE streams. It's an\n // OPTIONAL peer: if it isn't installed, fall back to a plain EventSource.\n void import(\"@particle-academy/fancy-cf-relay\")\n .then(({ createRelayChannel }) => {\n if (this.connected || this.disposed) return;\n this.channel = createRelayChannel({\n baseUrl: this.opts.baseUrl,\n session: this.opts.sessionId,\n token: this.opts.token,\n transport: \"auto\",\n receiveDirection: \"inbound\",\n sendPath: \"outbox\",\n onFrame: (raw) => this.handleInbound(raw),\n onOpen: () => this.markOpen(),\n onError: () => this.setState(\"error\"),\n fetchImpl: this.opts.fetch,\n });\n this.channel.start();\n })\n .catch(() => {\n if (!this.disposed) this.startSse();\n });\n }\n\n /** Fallback receive leg: a plain EventSource (no fancy-cf-relay installed). */\n private startSse(): void {\n if (this.connected || this.disposed) return;\n const url = `${this.opts.baseUrl}/${encodeURIComponent(this.opts.sessionId)}/events?token=${encodeURIComponent(this.opts.token)}`;\n const es = new EventSource(url, { withCredentials: false });\n this.es = es;\n es.addEventListener(\"open\", () => this.markOpen());\n es.addEventListener(\"mcp\", (ev: MessageEvent) => this.handleInbound(ev.data));\n es.addEventListener(\"error\", () => this.setState(\"error\")); // EventSource auto-reconnects\n }\n\n /** Mark the channel live + flush any queued outbound frames. */\n private markOpen(): void {\n this.connected = true;\n this.setState(\"open\");\n const queued = this.sendQueue.splice(0);\n for (const msg of queued) this.postOut(msg);\n }\n\n send(message: JsonRpcMessage): void {\n if (!this.connected) {\n this.sendQueue.push(message);\n return;\n }\n this.postOut(message);\n }\n\n close(): void {\n this.disposed = true;\n this.channel?.stop();\n this.channel = undefined;\n this.es?.close();\n this.es = undefined;\n this.connected = false;\n this.setState(\"closed\");\n }\n\n onStateChange(listener: (state: RelayState) => void): () => void {\n this.listeners.add(listener);\n listener(this.state);\n return () => this.listeners.delete(listener);\n }\n\n /**\n * For relays that wrap each frame with auth metadata: hosts can call this\n * directly when a frame arrives via a non-SSE path. The transport will\n * dispatch it to the bound server.\n */\n async deliverFromRemote(payload: JsonRpcMessage | string, token?: string): Promise<void> {\n if (token !== undefined && !constantTimeEqual(token, this.expectedToken)) return;\n if (!this.server) throw new Error(\"SseRelayTransport has no bound server\");\n const message: JsonRpcMessage = typeof payload === \"string\" ? JSON.parse(payload) : payload;\n await this.server.receive(this, message);\n }\n\n private async postOut(message: JsonRpcMessage): Promise<void> {\n const url = `${this.opts.baseUrl}/${encodeURIComponent(this.opts.sessionId)}/outbox?token=${encodeURIComponent(this.opts.token)}`;\n const f = this.opts.fetch ?? fetch;\n try {\n await f(url, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", \"accept\": \"application/json\" },\n body: JSON.stringify(message),\n });\n } catch {\n // Drop — relay errors are surfaced via state change separately.\n }\n }\n\n private async handleInbound(raw: string): Promise<void> {\n if (!this.server) return;\n let message: JsonRpcMessage;\n try {\n message = JSON.parse(raw);\n } catch {\n return;\n }\n await this.server.receive(this, message);\n }\n\n private setState(state: RelayState): void {\n this.state = state;\n for (const l of this.listeners) l(state);\n }\n}\n\nexport type RelayState = \"idle\" | \"connecting\" | \"open\" | \"closed\" | \"error\";\n\nexport function attachSseRelay(server: MicroMcpServer, options: SseRelayOptions): SseRelayTransport {\n const transport = new SseRelayTransport(options);\n transport.bindServer(server);\n server.attach(transport);\n transport.start();\n\n // Forward in-process agent activity events out over the relay so external\n // subscribers can render presence indicators in real time. Uses a dynamic\n // import so the relay doesn't hard-depend on the presence module if it's\n // tree-shaken out.\n import(\"../presence/registry\").then(({ onActivity }) => {\n const off = onActivity((event) => {\n transport.send({\n jsonrpc: \"2.0\",\n method: \"notifications/agent_activity\",\n params: event as any,\n });\n });\n // Tear down the subscription when the transport closes.\n const origClose = transport.close.bind(transport);\n transport.close = () => {\n off();\n origClose();\n };\n }).catch(() => {\n // Presence module unavailable — silently no-op (relay still works).\n });\n\n return transport;\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { buildShareUrl, buildShareConfig } from './chunk-CPNOF4HI.js';
1
+ import { buildShareUrl, buildShareConfig } from './chunk-FUI7KXE7.js';
2
2
  import { useRef, useEffect, useState } from 'react';
3
3
  import { jsxs, jsx } from 'react/jsx-runtime';
4
4
 
@@ -326,5 +326,5 @@ function buildCurlRecipe(session) {
326
326
  }
327
327
 
328
328
  export { AgentActivityHighlight, AgentCursor, AgentPanel, ShareControls, buildAgentPrompt };
329
- //# sourceMappingURL=chunk-YEEOTIGY.js.map
330
- //# sourceMappingURL=chunk-YEEOTIGY.js.map
329
+ //# sourceMappingURL=chunk-HKQBG2HZ.js.map
330
+ //# sourceMappingURL=chunk-HKQBG2HZ.js.map