@zyphr-dev/mcp-server 0.1.5 → 0.1.6

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.
package/dist/index.js CHANGED
@@ -265,6 +265,49 @@ var verifyDomainShape = {
265
265
  "Domain ID (UUID). Queues an asynchronous DNS verification check; poll get_domain (or subscribe to the domain.verified webhook) for terminal status."
266
266
  )
267
267
  };
268
+ var listOrganizationsShape = {
269
+ limit: z.number().int().positive().max(200).optional(),
270
+ cursor: z.string().optional().describe("Pagination cursor from a prior listOrganizations response."),
271
+ search: z.string().optional().describe("Optional fuzzy match on org name/slug.")
272
+ };
273
+ var getOrganizationShape = {
274
+ id: z.string().min(1).describe("Organization ID (UUID).")
275
+ };
276
+ var createOrganizationShape = {
277
+ name: z.string().min(1).max(255).describe("Display name. Required."),
278
+ slug: z.string().min(1).max(120).optional().describe("Optional slug. If present must be unique within the environment."),
279
+ metadata: z.record(z.unknown()).optional().describe("Arbitrary JSON metadata.")
280
+ };
281
+ var updateOrganizationShape = {
282
+ id: z.string().min(1).describe("Organization ID (UUID)."),
283
+ name: z.string().min(1).max(255).optional(),
284
+ slug: z.string().min(1).max(120).optional(),
285
+ metadata: z.record(z.unknown()).optional()
286
+ };
287
+ var listOrganizationMembersShape = {
288
+ id: z.string().min(1).describe("Organization ID (UUID)."),
289
+ limit: z.number().int().positive().max(200).optional(),
290
+ cursor: z.string().optional()
291
+ };
292
+ var addOrganizationMemberShape = {
293
+ id: z.string().min(1).describe("Organization ID (UUID)."),
294
+ userId: z.string().min(1).describe("End-user ID (UUID) to add to the org."),
295
+ role: z.string().min(1).max(80).optional().describe(
296
+ "Customer-defined role label (opaque to Zyphr \u2014 store whatever your taxonomy uses)."
297
+ )
298
+ };
299
+ var updateOrganizationMemberRoleShape = {
300
+ id: z.string().min(1).describe("Organization ID (UUID)."),
301
+ userId: z.string().min(1).describe("End-user ID (UUID)."),
302
+ role: z.string().min(1).max(80).describe("New role label. Opaque to Zyphr.")
303
+ };
304
+ var removeOrganizationMemberShape = {
305
+ id: z.string().min(1).describe("Organization ID (UUID)."),
306
+ userId: z.string().min(1).describe("End-user ID (UUID) to remove.")
307
+ };
308
+ var listOrganizationsForUserShape = {
309
+ userId: z.string().min(1).describe("End-user ID (UUID).")
310
+ };
268
311
  var sendInboxMessageShape = {
269
312
  subscriberId: z.string().min(1).describe("Subscriber to deliver the in-app message to"),
270
313
  title: z.string().min(1),
@@ -348,6 +391,178 @@ function registerDomainTools(server, guards) {
348
391
  }
349
392
  }
350
393
 
394
+ // src/tools/organizations.ts
395
+ function registerOrganizationTools(server, guards) {
396
+ if (isToolEnabled({ name: "list_organizations", mutates: false }, guards)) {
397
+ server.registerTool(
398
+ "list_organizations",
399
+ {
400
+ title: "List organizations",
401
+ description: "List organizations in the current environment. Cursor-paginated; pass the `cursor` from the prior response to walk pages.",
402
+ inputSchema: listOrganizationsShape
403
+ },
404
+ async (args) => {
405
+ return runTool(async () => {
406
+ const zyphr = getZyphrClient();
407
+ return await zyphr.auth.organizations.listOrganizations(
408
+ args.limit,
409
+ args.cursor,
410
+ args.search
411
+ );
412
+ });
413
+ }
414
+ );
415
+ }
416
+ if (isToolEnabled({ name: "get_organization", mutates: false }, guards)) {
417
+ server.registerTool(
418
+ "get_organization",
419
+ {
420
+ title: "Get organization",
421
+ description: "Fetch a single organization by ID.",
422
+ inputSchema: getOrganizationShape
423
+ },
424
+ async (args) => {
425
+ return runTool(async () => {
426
+ const zyphr = getZyphrClient();
427
+ return await zyphr.auth.organizations.getOrganization(args.id);
428
+ });
429
+ }
430
+ );
431
+ }
432
+ if (isToolEnabled({ name: "create_organization", mutates: true }, guards)) {
433
+ server.registerTool(
434
+ "create_organization",
435
+ {
436
+ title: "Create organization",
437
+ description: "Create a new organization in the current environment. Requires Pro / Scale / Enterprise; Free and Starter accounts receive 403 plan_upgrade_required.",
438
+ inputSchema: createOrganizationShape
439
+ },
440
+ async (args) => {
441
+ return runTool(async () => {
442
+ const zyphr = getZyphrClient();
443
+ return await zyphr.auth.organizations.createOrganization({
444
+ name: args.name,
445
+ slug: args.slug,
446
+ metadata: args.metadata
447
+ });
448
+ });
449
+ }
450
+ );
451
+ }
452
+ if (isToolEnabled({ name: "update_organization", mutates: true }, guards)) {
453
+ server.registerTool(
454
+ "update_organization",
455
+ {
456
+ title: "Update organization",
457
+ description: "Update an organization\u2019s name, slug, or metadata. Cannot rename the Default org.",
458
+ inputSchema: updateOrganizationShape
459
+ },
460
+ async (args) => {
461
+ return runTool(async () => {
462
+ const zyphr = getZyphrClient();
463
+ return await zyphr.auth.organizations.updateOrganization(args.id, {
464
+ name: args.name,
465
+ slug: args.slug,
466
+ metadata: args.metadata
467
+ });
468
+ });
469
+ }
470
+ );
471
+ }
472
+ if (isToolEnabled({ name: "list_organization_members", mutates: false }, guards)) {
473
+ server.registerTool(
474
+ "list_organization_members",
475
+ {
476
+ title: "List organization members",
477
+ description: "List end-user memberships in an organization. Cursor-paginated.",
478
+ inputSchema: listOrganizationMembersShape
479
+ },
480
+ async (args) => {
481
+ return runTool(async () => {
482
+ const zyphr = getZyphrClient();
483
+ return await zyphr.auth.organizations.listOrganizationMembers(
484
+ args.id,
485
+ args.limit,
486
+ args.cursor
487
+ );
488
+ });
489
+ }
490
+ );
491
+ }
492
+ if (isToolEnabled({ name: "add_organization_member", mutates: true }, guards)) {
493
+ server.registerTool(
494
+ "add_organization_member",
495
+ {
496
+ title: "Add organization member",
497
+ description: "Add an end_user to an organization. Adding to a non-Default org requires Pro / Scale / Enterprise; gated calls return 403 plan_upgrade_required.",
498
+ inputSchema: addOrganizationMemberShape
499
+ },
500
+ async (args) => {
501
+ return runTool(async () => {
502
+ const zyphr = getZyphrClient();
503
+ return await zyphr.auth.organizations.addOrganizationMember(args.id, {
504
+ userId: args.userId,
505
+ role: args.role
506
+ });
507
+ });
508
+ }
509
+ );
510
+ }
511
+ if (isToolEnabled({ name: "update_organization_member_role", mutates: true }, guards)) {
512
+ server.registerTool(
513
+ "update_organization_member_role",
514
+ {
515
+ title: "Update organization member role",
516
+ description: "Update an existing membership\u2019s role label. Role is an opaque customer-defined string \u2014 Zyphr does not interpret values.",
517
+ inputSchema: updateOrganizationMemberRoleShape
518
+ },
519
+ async (args) => {
520
+ return runTool(async () => {
521
+ const zyphr = getZyphrClient();
522
+ return await zyphr.auth.organizations.updateOrganizationMemberRole(
523
+ args.id,
524
+ args.userId,
525
+ { role: args.role }
526
+ );
527
+ });
528
+ }
529
+ );
530
+ }
531
+ if (isToolEnabled({ name: "remove_organization_member", mutates: true }, guards)) {
532
+ server.registerTool(
533
+ "remove_organization_member",
534
+ {
535
+ title: "Remove organization member",
536
+ description: "Remove an end_user from an organization. Returns 204 on success. Cannot remove the last member of the Default org.",
537
+ inputSchema: removeOrganizationMemberShape
538
+ },
539
+ async (args) => {
540
+ return runTool(async () => {
541
+ const zyphr = getZyphrClient();
542
+ await zyphr.auth.organizations.removeOrganizationMember(args.id, args.userId);
543
+ return { ok: true };
544
+ });
545
+ }
546
+ );
547
+ }
548
+ if (isToolEnabled({ name: "list_organizations_for_user", mutates: false }, guards)) {
549
+ server.registerTool(
550
+ "list_organizations_for_user",
551
+ {
552
+ title: "List organizations for end-user",
553
+ description: "List every organization an end_user belongs to within the environment. Useful when the JWT `orgs[]` claim is capped at 50 and you need the full set.",
554
+ inputSchema: listOrganizationsForUserShape
555
+ },
556
+ async (args) => {
557
+ return runTool(async () => {
558
+ const zyphr = getZyphrClient();
559
+ return await zyphr.auth.organizations.listOrganizationsForEndUser(args.userId);
560
+ });
561
+ }
562
+ );
563
+ }
564
+ }
565
+
351
566
  // src/tools/send.ts
352
567
  function normalizeEmailAddress(value) {
353
568
  if (typeof value === "string") return { email: value };
@@ -2360,7 +2575,7 @@ function registerWebhookTools(server, guards) {
2360
2575
 
2361
2576
  // src/server.ts
2362
2577
  var SERVER_NAME = "zyphr";
2363
- var SERVER_VERSION = "0.1.0";
2578
+ var SERVER_VERSION = "0.2.0";
2364
2579
  function createServer() {
2365
2580
  const server = new McpServer(
2366
2581
  { name: SERVER_NAME, version: SERVER_VERSION },
@@ -2373,6 +2588,7 @@ function createServer() {
2373
2588
  registerWebhookTools(server, guards);
2374
2589
  registerDomainTools(server, guards);
2375
2590
  registerIntegrationTools(server, guards);
2591
+ registerOrganizationTools(server, guards);
2376
2592
  return server;
2377
2593
  }
2378
2594
 
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/server.ts","../src/config.ts","../src/client.ts","../src/result.ts","../src/schemas.ts","../src/tools/domains.ts","../src/tools/send.ts","../src/tools/subscribers.ts","../src/tools/templates.ts","../src/integration/quickstart/email.ts","../src/integration/quickstart/inbox.ts","../src/integration/quickstart/push.ts","../src/integration/quickstart/sms.ts","../src/integration/quickstart/webhook.ts","../src/integration/quickstart/index.ts","../src/integration/sdk-snippets.ts","../src/tools/integration.ts","../src/tools/webhooks.ts"],"sourcesContent":["import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { createServer } from './server.js';\nimport { getBaseUrl } from './client.js';\n\nasync function main(): Promise<void> {\n // Touch the env early so misconfiguration fails fast with a clear message.\n if (!process.env.ZYPHR_API_KEY) {\n process.stderr.write(\n '[zyphr-mcp] ZYPHR_API_KEY is not set. Provide a zy_live_* or zy_test_* key via the `env` block of your MCP client config.\\n',\n );\n process.exit(1);\n }\n\n const server = createServer();\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n process.stderr.write(`[zyphr-mcp] connected (base=${getBaseUrl()})\\n`);\n}\n\nmain().catch((err) => {\n process.stderr.write(`[zyphr-mcp] fatal: ${err instanceof Error ? err.stack ?? err.message : String(err)}\\n`);\n process.exit(1);\n});\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { loadToolGuards } from './config.js';\nimport { registerDomainTools } from './tools/domains.js';\nimport { registerSendTools } from './tools/send.js';\nimport { registerSubscriberTools } from './tools/subscribers.js';\nimport { registerTemplateTools } from './tools/templates.js';\nimport { registerIntegrationTools } from './tools/integration.js';\nimport { registerWebhookTools } from './tools/webhooks.js';\n\nexport const SERVER_NAME = 'zyphr';\nexport const SERVER_VERSION = '0.1.0';\n\nexport function createServer(): McpServer {\n const server = new McpServer(\n { name: SERVER_NAME, version: SERVER_VERSION },\n { capabilities: { tools: {} } },\n );\n\n const guards = loadToolGuards();\n registerSendTools(server, guards);\n registerTemplateTools(server, guards);\n registerSubscriberTools(server, guards);\n registerWebhookTools(server, guards);\n registerDomainTools(server, guards);\n registerIntegrationTools(server, guards);\n\n return server;\n}\n","export interface ToolGuards {\n readOnly: boolean;\n allowedTools: Set<string> | null;\n}\n\nexport function loadToolGuards(): ToolGuards {\n const readOnly = process.env.ZYPHR_READ_ONLY === 'true' || process.env.ZYPHR_READ_ONLY === '1';\n const raw = process.env.ZYPHR_ALLOWED_TOOLS;\n const allowedTools = raw\n ? new Set(\n raw\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean),\n )\n : null;\n return { readOnly, allowedTools };\n}\n\nexport interface ToolDefinition {\n name: string;\n mutates: boolean;\n}\n\nexport function isToolEnabled(tool: ToolDefinition, guards: ToolGuards): boolean {\n if (guards.allowedTools) {\n return guards.allowedTools.has(tool.name);\n }\n if (guards.readOnly && tool.mutates) {\n return false;\n }\n return true;\n}\n","import { Zyphr } from '@zyphr-dev/node-sdk';\n\nconst DEFAULT_BASE_URL = 'https://api.zyphr.dev/v1';\n\nlet cached: Zyphr | undefined;\n\nexport function getZyphrClient(): Zyphr {\n if (cached) return cached;\n\n const apiKey = process.env.ZYPHR_API_KEY;\n if (!apiKey) {\n process.stderr.write(\n '[zyphr-mcp] ZYPHR_API_KEY is not set. Provide a zy_live_* or zy_test_* key via the `env` block of your MCP client config.\\n',\n );\n process.exit(1);\n }\n\n const baseUrl = process.env.ZYPHR_BASE_URL || DEFAULT_BASE_URL;\n cached = new Zyphr({ apiKey, baseUrl });\n return cached;\n}\n\nexport function getBaseUrl(): string {\n return process.env.ZYPHR_BASE_URL || DEFAULT_BASE_URL;\n}\n","import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { ZyphrError, ZyphrRateLimitError } from '@zyphr-dev/node-sdk';\n\nexport function toolResult(data: unknown): CallToolResult {\n return {\n content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],\n };\n}\n\nexport async function runTool(fn: () => Promise<unknown>): Promise<CallToolResult> {\n try {\n const data = await fn();\n return toolResult(data);\n } catch (err: unknown) {\n return await renderApiError(err);\n }\n}\n\nfunction errorPayload(content: Record<string, unknown>): CallToolResult {\n return {\n isError: true,\n content: [{ type: 'text', text: JSON.stringify(content, null, 2) }],\n };\n}\n\nasync function renderApiError(err: unknown): Promise<CallToolResult> {\n // Typed SDK errors — surface the full envelope so the AI can act on it.\n if (err instanceof ZyphrError) {\n const payload: Record<string, unknown> = {\n name: err.name,\n message: err.message,\n status: err.status,\n };\n if (err.code) payload.code = err.code;\n if (err.requestId) payload.requestId = err.requestId;\n if (err.details) payload.details = err.details;\n if (err instanceof ZyphrRateLimitError && err.retryAfter !== undefined) {\n payload.retryAfter = err.retryAfter;\n }\n return errorPayload(payload);\n }\n\n // Raw Response error (rare — SDK middleware usually parses first).\n if (err && typeof err === 'object' && 'response' in err) {\n const response = (err as { response?: Response }).response;\n if (response && typeof response.text === 'function') {\n try {\n const text = await response.text();\n let parsed: unknown = text;\n try {\n parsed = JSON.parse(text);\n } catch {\n /* keep raw text */\n }\n return errorPayload({ status: response.status, body: parsed });\n } catch {\n /* fall through to generic message */\n }\n }\n }\n\n const message = err instanceof Error ? err.message : String(err);\n const name = err instanceof Error ? err.name : 'Error';\n return errorPayload({ name, message });\n}\n","import { z } from 'zod';\n\nconst emailAddress = z.object({\n email: z.string().email(),\n name: z.string().optional(),\n});\n\nconst recipient = z.union([z.string().email(), emailAddress]);\n\nexport const sendEmailShape = {\n to: z\n .union([recipient, z.array(recipient).min(1)])\n .describe('Recipient email address (string or {email,name}) or an array of them'),\n from: z\n .union([z.string().email(), emailAddress])\n .optional()\n .describe('Sender address. Defaults to the account-level \"from\" address.'),\n replyTo: z.union([z.string().email(), emailAddress]).optional(),\n cc: z.array(z.string().email()).optional(),\n bcc: z.array(z.string().email()).optional(),\n subject: z.string().min(1),\n html: z.string().optional().describe('Rendered HTML body. Mutually exclusive with templateId.'),\n text: z.string().optional().describe('Plain-text body. Mutually exclusive with templateId.'),\n templateId: z.string().optional().describe('Template ID. When set, html/text are ignored.'),\n templateData: z\n .record(z.unknown())\n .optional()\n .describe('Variables to interpolate into the template'),\n tags: z.array(z.string()).optional(),\n metadata: z.record(z.unknown()).optional(),\n subscriberId: z.string().optional(),\n category: z.string().optional(),\n scheduledAt: z\n .string()\n .datetime()\n .optional()\n .describe('ISO 8601 timestamp for scheduled delivery'),\n} as const;\n\nexport const sendPushShape = {\n userId: z.string().optional().describe('Send to all devices for this user/subscriber'),\n deviceId: z.string().optional().describe('Send to a specific device only'),\n title: z.string().optional(),\n body: z.string().optional(),\n data: z.record(z.unknown()).optional().describe('Custom data payload delivered to the device'),\n badge: z.number().int().nonnegative().optional(),\n sound: z.string().optional(),\n imageUrl: z.string().url().optional(),\n contentAvailable: z.boolean().optional().describe('Silent/background push'),\n tags: z.array(z.string()).optional(),\n metadata: z.record(z.unknown()).optional(),\n collapseKey: z.string().optional(),\n subscriberId: z.string().optional(),\n subscriberExternalId: z.string().optional(),\n category: z.string().optional(),\n force: z.boolean().optional().describe('Skip subscriber preference checks'),\n sendAt: z.string().datetime().optional(),\n delay: z.number().int().nonnegative().optional(),\n} as const;\n\nexport const sendSmsShape = {\n to: z.string().min(1).describe('Recipient phone number in E.164 format (e.g. +14155551234)'),\n from: z.string().optional().describe('Sender phone number or sender ID'),\n body: z.string().min(1),\n subscriberId: z.string().optional(),\n scheduledAt: z.string().datetime().optional(),\n metadata: z.record(z.unknown()).optional(),\n} as const;\n\n// Integration\n\nexport const sdkLanguages = ['node', 'python', 'ruby', 'go', 'php', 'csharp'] as const;\nexport type SdkLanguage = (typeof sdkLanguages)[number];\n\nexport const quickstartChannels = ['email', 'push', 'sms', 'inbox', 'webhook'] as const;\nexport type QuickstartChannel = (typeof quickstartChannels)[number];\n\nexport const getQuickstartShape = {\n channel: z\n .enum(quickstartChannels)\n .describe('Which Zyphr channel to wire up'),\n language: z\n .enum(['node', 'python', 'ruby', 'go', 'php', 'csharp'])\n .describe('Target language for the integration'),\n framework: z\n .string()\n .optional()\n .describe(\n 'Optional framework hint (e.g. \"express\", \"nextjs\", \"flask\", \"fastapi\", \"rails\", \"gin\", \"laravel\", \"aspnetcore\"). Falls back to plain SDK code when unrecognized.',\n ),\n} as const;\n\nexport const getSdkInstallShape = {\n language: z\n .enum(sdkLanguages)\n .describe('Target language for the integration'),\n packageManager: z\n .string()\n .optional()\n .describe(\n 'Optional package manager override (e.g. \"yarn\" instead of \"npm\"). When recognized, only that manager is returned; otherwise the full list is returned.',\n ),\n} as const;\n\n// Templates\n\nexport const listTemplatesShape = {\n limit: z.number().int().positive().max(200).optional(),\n offset: z.number().int().nonnegative().optional(),\n} as const;\n\nexport const getTemplateShape = {\n id: z.string().min(1).describe('Template ID'),\n} as const;\n\nexport const renderTemplateShape = {\n id: z.string().min(1).describe('Template ID'),\n variables: z\n .record(z.unknown())\n .describe('Key/value variables to interpolate into the template'),\n} as const;\n\nexport const createTemplateShape = {\n name: z.string().min(1),\n description: z.string().optional(),\n subject: z.string().optional().describe('Default subject (email templates)'),\n html: z.string().optional(),\n text: z.string().optional(),\n} as const;\n\n// Subscribers\n\nexport const findSubscriberShape = {\n externalId: z\n .string()\n .min(1)\n .describe('Subscriber external ID (your application user/customer ID)'),\n} as const;\n\nexport const listSubscribersShape = {\n status: z.enum(['active', 'inactive']).optional(),\n email: z.string().email().optional(),\n limit: z.number().int().positive().max(200).optional(),\n offset: z.number().int().nonnegative().optional(),\n} as const;\n\nexport const createSubscriberShape = {\n externalId: z.string().min(1).describe('Your application user/customer ID'),\n email: z.string().email().optional(),\n phone: z.string().optional(),\n name: z.string().optional(),\n avatarUrl: z.string().url().optional(),\n timezone: z.string().optional(),\n locale: z.string().optional(),\n metadata: z.record(z.unknown()).optional(),\n} as const;\n\nexport const updateSubscriberShape = {\n id: z.string().min(1).describe('Zyphr subscriber ID'),\n email: z.string().email().nullable().optional(),\n phone: z.string().nullable().optional(),\n name: z.string().nullable().optional(),\n avatarUrl: z.string().url().nullable().optional(),\n timezone: z.string().optional(),\n locale: z.string().optional(),\n metadata: z.record(z.unknown()).optional(),\n status: z.enum(['active', 'inactive']).optional(),\n} as const;\n\nexport const setSubscriberPreferencesShape = {\n id: z.string().min(1).describe('Zyphr subscriber ID'),\n preferences: z\n .array(\n z.object({\n categoryId: z.string().optional(),\n channel: z.string().optional().describe('email | push | sms | in_app'),\n enabled: z.boolean().optional(),\n }),\n )\n .min(1),\n} as const;\n\n// Webhooks\n\nexport const listWebhooksShape = {\n limit: z.number().int().positive().max(200).optional(),\n offset: z.number().int().nonnegative().optional(),\n} as const;\n\nexport const createWebhookShape = {\n url: z.string().url().describe('Receiver URL that Zyphr will POST events to'),\n events: z\n .array(z.string().min(1))\n .min(1)\n .describe('Event types to subscribe to (e.g. [\"email.*\", \"subscriber.created\"])'),\n description: z.string().optional(),\n secret: z\n .string()\n .optional()\n .describe('Optional secret used to sign payloads. If omitted, Zyphr generates one.'),\n metadata: z.record(z.unknown()).optional(),\n headers: z.record(z.string()).optional().describe('Custom headers to send with every delivery'),\n version: z.string().optional(),\n rateLimit: z.number().int().positive().optional(),\n} as const;\n\nexport const getWebhookDeliveriesShape = {\n webhookId: z.string().min(1).describe('Webhook endpoint ID'),\n status: z.enum(['pending', 'delivering', 'delivered', 'failed', 'exhausted']).optional(),\n eventType: z.string().optional(),\n search: z.string().optional(),\n startDate: z.string().datetime().optional(),\n endDate: z.string().datetime().optional(),\n limit: z.number().int().positive().max(200).optional(),\n offset: z.number().int().nonnegative().optional(),\n} as const;\n\n// Domains (sc-5443)\n//\n// NOTE on what's intentionally absent: there is NO `deleteDomainShape` here\n// and there is no `delete_domain` tool registered anywhere in the MCP server.\n// `@zyphr-dev/mcp-server` does not expose destructive `delete_*` operations\n// by policy (epic 5306). Customers who need scripted domain teardown should\n// call `zyphr.domains.deleteDomain(id)` from the Node SDK directly.\n\nexport const addDomainShape = {\n domain: z\n .string()\n .min(1)\n .describe(\n 'The sending domain to add (e.g. \"mail.example.com\"). Will be lowercased and trimmed. Idempotent — calling with an already-registered domain returns the existing record.',\n ),\n} as const;\n\nexport const listDomainsShape = {} as const;\n\nexport const getDomainShape = {\n id: z.string().min(1).describe('Domain ID (UUID)'),\n} as const;\n\nexport const verifyDomainShape = {\n id: z\n .string()\n .min(1)\n .describe(\n 'Domain ID (UUID). Queues an asynchronous DNS verification check; poll get_domain (or subscribe to the domain.verified webhook) for terminal status.',\n ),\n} as const;\n\nexport const sendInboxMessageShape = {\n subscriberId: z.string().min(1).describe('Subscriber to deliver the in-app message to'),\n title: z.string().min(1),\n body: z.string().optional(),\n actionUrl: z.string().url().optional(),\n actionLabel: z.string().optional(),\n imageUrl: z.string().url().optional(),\n icon: z.string().optional(),\n category: z.string().optional(),\n priority: z.enum(['low', 'normal', 'high', 'urgent']).optional(),\n data: z.record(z.unknown()).optional(),\n tags: z.array(z.string()).optional(),\n expiresAt: z.string().datetime().optional(),\n} as const;\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { getZyphrClient } from '../client.js';\nimport { isToolEnabled, type ToolGuards } from '../config.js';\nimport { runTool } from '../result.js';\nimport {\n addDomainShape,\n getDomainShape,\n listDomainsShape,\n verifyDomainShape,\n} from '../schemas.js';\n\n/**\n * Domain-management MCP tools (sc-5443).\n *\n * Surface lets an AI agent complete the full domain-onboarding loop without\n * touching the dashboard: add a sending domain, get back the DNS records to\n * publish (already tagged with `purpose` per record), poll verification, then\n * `send_email` from that domain once `status === 'verified'`.\n *\n * NOT registered (by policy): `delete_domain`. Destructive operations are\n * intentionally absent from `@zyphr-dev/mcp-server` — one bad model call could\n * detach an SES identity, invalidate scheduled sends, and break templates\n * hardcoded to a sender. Customers who need scripted teardown call\n * `zyphr.domains.deleteDomain(id)` from the Node SDK directly.\n */\nexport function registerDomainTools(server: McpServer, guards: ToolGuards): void {\n if (isToolEnabled({ name: 'add_domain', mutates: true }, guards)) {\n server.registerTool(\n 'add_domain',\n {\n title: 'Add a sending domain',\n description:\n 'Register a new sending domain on the account. Returns the full set of DNS records (TXT + CNAME) the AI agent should publish to its DNS provider (Route53, Cloudflare, etc.) before email can send from this domain. Idempotent: calling with an already-registered domain returns the existing record.',\n inputSchema: addDomainShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.domains.addDomain({ domain: args.domain });\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'list_domains', mutates: false }, guards)) {\n server.registerTool(\n 'list_domains',\n {\n title: 'List sending domains',\n description:\n 'List sending domains on the current project, with verification status and DNS records.',\n inputSchema: listDomainsShape,\n },\n async () => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.domains.listDomains();\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'get_domain', mutates: false }, guards)) {\n server.registerTool(\n 'get_domain',\n {\n title: 'Get a sending domain',\n description:\n 'Retrieve a single sending domain by ID, including per-record `verified` flags and `records_verified` / `records_total` counters. Useful for reasoning about partial verification (e.g. \"SPF verified, DKIM #2 still pending\").',\n inputSchema: getDomainShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.domains.getDomain(args.id);\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'verify_domain', mutates: true }, guards)) {\n server.registerTool(\n 'verify_domain',\n {\n title: 'Trigger domain verification',\n description:\n 'Queue an asynchronous DNS verification check for a sending domain. Returns a job_id immediately; poll `get_domain` (or subscribe to the `domain.verified` webhook event) for terminal status. Returns a 409 error if a verification is already in progress for this domain.',\n inputSchema: verifyDomainShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.domains.verifyDomain(args.id);\n });\n },\n );\n }\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { getZyphrClient } from '../client.js';\nimport { isToolEnabled, type ToolGuards } from '../config.js';\nimport { runTool } from '../result.js';\nimport {\n sendEmailShape,\n sendInboxMessageShape,\n sendPushShape,\n sendSmsShape,\n} from '../schemas.js';\n\nfunction normalizeEmailAddress(value: unknown): { email: string; name?: string } | undefined {\n if (typeof value === 'string') return { email: value };\n if (value && typeof value === 'object' && 'email' in value) {\n return value as { email: string; name?: string };\n }\n return undefined;\n}\n\nfunction normalizeRecipients(to: unknown): { email: string; name?: string }[] {\n if (Array.isArray(to)) {\n return to.map(normalizeEmailAddress).filter((v): v is { email: string } => Boolean(v));\n }\n const one = normalizeEmailAddress(to);\n return one ? [one] : [];\n}\n\nexport function registerSendTools(server: McpServer, guards: ToolGuards): void {\n if (isToolEnabled({ name: 'send_email', mutates: true }, guards)) {\n server.registerTool(\n 'send_email',\n {\n title: 'Send email',\n description:\n 'Send a transactional email via Zyphr. Use either html/text OR templateId+templateData, not both.',\n inputSchema: sendEmailShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.emails.sendEmail({\n to: normalizeRecipients(args.to),\n from: normalizeEmailAddress(args.from),\n replyTo: normalizeEmailAddress(args.replyTo),\n cc: args.cc,\n bcc: args.bcc,\n subject: args.subject,\n html: args.html,\n text: args.text,\n templateId: args.templateId,\n templateData: args.templateData,\n tags: args.tags,\n metadata: args.metadata,\n subscriberId: args.subscriberId,\n category: args.category,\n scheduledAt: args.scheduledAt ? new Date(args.scheduledAt) : undefined,\n });\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'send_push', mutates: true }, guards)) {\n server.registerTool(\n 'send_push',\n {\n title: 'Send push notification',\n description:\n 'Send a push notification. Target one of: userId (all devices for a user), deviceId (specific device), subscriberId, or subscriberExternalId.',\n inputSchema: sendPushShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.push.sendPush({\n userId: args.userId,\n deviceId: args.deviceId,\n title: args.title,\n body: args.body,\n data: args.data,\n badge: args.badge,\n sound: args.sound,\n imageUrl: args.imageUrl,\n contentAvailable: args.contentAvailable,\n tags: args.tags,\n metadata: args.metadata,\n collapseKey: args.collapseKey,\n subscriberId: args.subscriberId,\n subscriberExternalId: args.subscriberExternalId,\n category: args.category,\n force: args.force,\n sendAt: args.sendAt ? new Date(args.sendAt) : undefined,\n delay: args.delay,\n });\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'send_sms', mutates: true }, guards)) {\n server.registerTool(\n 'send_sms',\n {\n title: 'Send SMS',\n description: 'Send an SMS message via Zyphr. The recipient must be in E.164 format.',\n inputSchema: sendSmsShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.sms.sendSms({\n to: args.to,\n from: args.from,\n body: args.body,\n subscriberId: args.subscriberId,\n scheduledAt: args.scheduledAt ? new Date(args.scheduledAt) : undefined,\n metadata: args.metadata,\n });\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'send_inbox_message', mutates: true }, guards)) {\n server.registerTool(\n 'send_inbox_message',\n {\n title: 'Send in-app inbox message',\n description: 'Deliver an in-app inbox notification to a Zyphr subscriber.',\n inputSchema: sendInboxMessageShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.inbox.sendInApp({\n subscriberId: args.subscriberId,\n title: args.title,\n body: args.body,\n actionUrl: args.actionUrl,\n actionLabel: args.actionLabel,\n imageUrl: args.imageUrl,\n icon: args.icon,\n category: args.category,\n priority: args.priority,\n data: args.data,\n tags: args.tags,\n expiresAt: args.expiresAt ? new Date(args.expiresAt) : undefined,\n });\n });\n },\n );\n }\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { getZyphrClient } from '../client.js';\nimport { isToolEnabled, type ToolGuards } from '../config.js';\nimport { runTool } from '../result.js';\nimport {\n createSubscriberShape,\n findSubscriberShape,\n listSubscribersShape,\n setSubscriberPreferencesShape,\n updateSubscriberShape,\n} from '../schemas.js';\n\nexport function registerSubscriberTools(server: McpServer, guards: ToolGuards): void {\n if (isToolEnabled({ name: 'find_subscriber', mutates: false }, guards)) {\n server.registerTool(\n 'find_subscriber',\n {\n title: 'Find subscriber by external ID',\n description:\n 'Look up a subscriber by the external ID you assigned (typically your application user/customer ID).',\n inputSchema: findSubscriberShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.subscribers.getSubscriberByExternalId(args.externalId);\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'list_subscribers', mutates: false }, guards)) {\n server.registerTool(\n 'list_subscribers',\n {\n title: 'List subscribers',\n description: 'List subscribers, optionally filtered by status or email.',\n inputSchema: listSubscribersShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.subscribers.listSubscribers(\n args.status,\n args.email,\n args.limit,\n args.offset,\n );\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'create_subscriber', mutates: true }, guards)) {\n server.registerTool(\n 'create_subscriber',\n {\n title: 'Create subscriber',\n description: 'Create a new subscriber. `externalId` must be unique within your account.',\n inputSchema: createSubscriberShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.subscribers.createSubscriber({\n externalId: args.externalId,\n email: args.email,\n phone: args.phone,\n name: args.name,\n avatarUrl: args.avatarUrl,\n timezone: args.timezone,\n locale: args.locale,\n metadata: args.metadata,\n });\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'update_subscriber', mutates: true }, guards)) {\n server.registerTool(\n 'update_subscriber',\n {\n title: 'Update subscriber',\n description:\n 'Update a subscriber by Zyphr ID. Pass null for email/phone/name/avatarUrl to clear those fields.',\n inputSchema: updateSubscriberShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.subscribers.updateSubscriber(args.id, {\n email: args.email ?? undefined,\n phone: args.phone ?? undefined,\n name: args.name ?? undefined,\n avatarUrl: args.avatarUrl ?? undefined,\n timezone: args.timezone,\n locale: args.locale,\n metadata: args.metadata,\n status: args.status,\n });\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'set_subscriber_preferences', mutates: true }, guards)) {\n server.registerTool(\n 'set_subscriber_preferences',\n {\n title: 'Set subscriber preferences',\n description:\n 'Set notification preferences for a subscriber. Each preference targets a category and/or channel and toggles `enabled`.',\n inputSchema: setSubscriberPreferencesShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.subscribers.setSubscriberPreferences(args.id, {\n preferences: args.preferences,\n });\n });\n },\n );\n }\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { getZyphrClient } from '../client.js';\nimport { isToolEnabled, type ToolGuards } from '../config.js';\nimport { runTool } from '../result.js';\nimport {\n createTemplateShape,\n getTemplateShape,\n listTemplatesShape,\n renderTemplateShape,\n} from '../schemas.js';\n\nexport function registerTemplateTools(server: McpServer, guards: ToolGuards): void {\n if (isToolEnabled({ name: 'list_templates', mutates: false }, guards)) {\n server.registerTool(\n 'list_templates',\n {\n title: 'List templates',\n description: 'List notification templates in the account.',\n inputSchema: listTemplatesShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.templates.listTemplates(args.limit, args.offset);\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'get_template', mutates: false }, guards)) {\n server.registerTool(\n 'get_template',\n {\n title: 'Get template',\n description: 'Fetch a single template by ID.',\n inputSchema: getTemplateShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.templates.getTemplate(args.id);\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'render_template', mutates: false }, guards)) {\n server.registerTool(\n 'render_template',\n {\n title: 'Render template',\n description:\n 'Preview a template with the given variables WITHOUT sending. Returns the rendered subject/html/text so the AI can show the user what would be sent.',\n inputSchema: renderTemplateShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.templates.renderTemplate(args.id, { variables: args.variables });\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'create_template', mutates: true }, guards)) {\n server.registerTool(\n 'create_template',\n {\n title: 'Create template',\n description: 'Create a new notification template.',\n inputSchema: createTemplateShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.templates.createTemplate({\n name: args.name,\n description: args.description,\n subject: args.subject,\n html: args.html,\n text: args.text,\n });\n });\n },\n );\n }\n}\n","import type { QuickstartChannelMap } from '../quickstart-types.js';\n\nconst DOCS = 'https://docs.zyphr.dev/channels/email';\n\nconst ENV: string[] = ['ZYPHR_API_KEY'];\n\nconst NEXT_STEPS = [\n 'Add ZYPHR_API_KEY to your .env file (run get_sdk_install_for_language to confirm the install).',\n 'Wire the new service into your app entrypoint.',\n 'Verify your sender domain in the Zyphr dashboard before sending to real recipients.',\n];\n\nexport const emailChannel: QuickstartChannelMap = {\n node: {\n sdk: {\n channel: 'email',\n language: 'node',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'src/lib/zyphr.ts',\n purpose: 'Zyphr SDK client singleton',\n contents:\n \"import { Zyphr } from '@zyphr-dev/node-sdk';\\n\\n\" +\n 'export const zyphr = new Zyphr({ apiKey: process.env.ZYPHR_API_KEY! });\\n',\n overwrite: false,\n },\n {\n path: 'src/services/notify.ts',\n purpose: 'Sends a transactional email through Zyphr',\n contents:\n \"import { zyphr } from '../lib/zyphr.js';\\n\\n\" +\n 'export async function sendWelcomeEmail(to: string, name: string) {\\n' +\n ' return await zyphr.emails.sendEmail({\\n' +\n ' to: [{ email: to, name }],\\n' +\n ' subject: `Welcome, ${name}!`,\\n' +\n \" html: `<h1>Welcome aboard, ${name}!</h1><p>Glad you're here.</p>`,\\n\" +\n ' });\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n frameworks: {\n express: {\n channel: 'email',\n language: 'node',\n framework: 'express',\n variant: 'sdk',\n files: [\n {\n path: 'src/lib/zyphr.ts',\n purpose: 'Zyphr SDK client singleton',\n contents:\n \"import { Zyphr } from '@zyphr-dev/node-sdk';\\n\\n\" +\n 'export const zyphr = new Zyphr({ apiKey: process.env.ZYPHR_API_KEY! });\\n',\n overwrite: false,\n },\n {\n path: 'src/routes/notify.ts',\n purpose: 'Express route that sends a welcome email',\n contents:\n \"import { Router } from 'express';\\n\" +\n \"import { zyphr } from '../lib/zyphr.js';\\n\\n\" +\n 'export const notifyRouter = Router();\\n\\n' +\n \"notifyRouter.post('/notify', async (req, res, next) => {\\n\" +\n ' try {\\n' +\n ' const { to, name } = req.body as { to: string; name: string };\\n' +\n ' const result = await zyphr.emails.sendEmail({\\n' +\n ' to: [{ email: to, name }],\\n' +\n ' subject: `Welcome, ${name}!`,\\n' +\n ' html: `<h1>Welcome, ${name}!</h1>`,\\n' +\n ' });\\n' +\n ' res.json(result);\\n' +\n ' } catch (err) {\\n' +\n ' next(err);\\n' +\n ' }\\n' +\n '});\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: [\n ...NEXT_STEPS,\n \"Mount the router in app.ts: app.use('/api', notifyRouter)\",\n 'Test: curl -X POST http://localhost:3000/api/notify -d \\'{\"to\":\"you@example.com\",\"name\":\"You\"}\\' -H \"Content-Type: application/json\"',\n ],\n docsUrl: DOCS,\n },\n nextjs: {\n channel: 'email',\n language: 'node',\n framework: 'nextjs',\n variant: 'sdk',\n files: [\n {\n path: 'src/lib/zyphr.ts',\n purpose: 'Zyphr SDK client singleton (server-only)',\n contents:\n \"import 'server-only';\\n\" +\n \"import { Zyphr } from '@zyphr-dev/node-sdk';\\n\\n\" +\n 'export const zyphr = new Zyphr({ apiKey: process.env.ZYPHR_API_KEY! });\\n',\n overwrite: false,\n },\n {\n path: 'src/app/api/notify/route.ts',\n purpose: 'Next.js App Router route handler that sends a welcome email',\n contents:\n \"import { NextResponse } from 'next/server';\\n\" +\n \"import { zyphr } from '@/lib/zyphr';\\n\\n\" +\n 'export async function POST(req: Request) {\\n' +\n ' const { to, name } = (await req.json()) as { to: string; name: string };\\n' +\n ' const result = await zyphr.emails.sendEmail({\\n' +\n ' to: [{ email: to, name }],\\n' +\n ' subject: `Welcome, ${name}!`,\\n' +\n ' html: `<h1>Welcome, ${name}!</h1>`,\\n' +\n ' });\\n' +\n ' return NextResponse.json(result);\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: [\n ...NEXT_STEPS,\n 'Test: curl -X POST http://localhost:3000/api/notify -d \\'{\"to\":\"you@example.com\",\"name\":\"You\"}\\' -H \"Content-Type: application/json\"',\n ],\n docsUrl: DOCS,\n },\n },\n },\n python: {\n sdk: {\n channel: 'email',\n language: 'python',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'app/zyphr_client.py',\n purpose: 'REST helper for the Zyphr API',\n contents:\n 'import os\\n' +\n 'import requests\\n\\n' +\n 'ZYPHR_API_KEY = os.environ[\"ZYPHR_API_KEY\"]\\n' +\n 'BASE_URL = \"https://api.zyphr.dev/v1\"\\n\\n' +\n 'headers = {\\n' +\n ' \"X-API-Key\": ZYPHR_API_KEY,\\n' +\n ' \"Content-Type\": \"application/json\",\\n' +\n '}\\n\\n' +\n 'def zyphr_request(method, path, json=None, params=None):\\n' +\n ' response = requests.request(\\n' +\n ' method, f\"{BASE_URL}{path}\", headers=headers, json=json, params=params,\\n' +\n ' )\\n' +\n ' response.raise_for_status()\\n' +\n ' return response.json()\\n',\n overwrite: false,\n },\n {\n path: 'app/notify.py',\n purpose: 'Send a welcome email through Zyphr',\n contents:\n 'from .zyphr_client import zyphr_request\\n\\n' +\n 'def send_welcome_email(to: str, name: str) -> dict:\\n' +\n ' return zyphr_request(\"POST\", \"/emails\", json={\\n' +\n ' \"to\": [{\"email\": to, \"name\": name}],\\n' +\n ' \"subject\": f\"Welcome, {name}!\",\\n' +\n ' \"html\": f\"<h1>Welcome, {name}!</h1>\",\\n' +\n ' })\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n frameworks: {\n flask: {\n channel: 'email',\n language: 'python',\n framework: 'flask',\n variant: 'sdk',\n files: [\n {\n path: 'app/zyphr_client.py',\n purpose: 'REST helper for the Zyphr API',\n contents:\n 'import os\\n' +\n 'import requests\\n\\n' +\n 'BASE_URL = \"https://api.zyphr.dev/v1\"\\n\\n' +\n 'def zyphr_request(method, path, json=None, params=None):\\n' +\n ' response = requests.request(\\n' +\n ' method, f\"{BASE_URL}{path}\",\\n' +\n ' headers={\"X-API-Key\": os.environ[\"ZYPHR_API_KEY\"], \"Content-Type\": \"application/json\"},\\n' +\n ' json=json, params=params,\\n' +\n ' )\\n' +\n ' response.raise_for_status()\\n' +\n ' return response.json()\\n',\n overwrite: false,\n },\n {\n path: 'app/routes/notify.py',\n purpose: 'Flask blueprint that sends a welcome email',\n contents:\n 'from flask import Blueprint, request, jsonify\\n' +\n 'from ..zyphr_client import zyphr_request\\n\\n' +\n 'notify_bp = Blueprint(\"notify\", __name__)\\n\\n' +\n '@notify_bp.route(\"/notify\", methods=[\"POST\"])\\n' +\n 'def notify():\\n' +\n ' body = request.get_json() or {}\\n' +\n ' result = zyphr_request(\"POST\", \"/emails\", json={\\n' +\n ' \"to\": [{\"email\": body[\"to\"], \"name\": body.get(\"name\", \"\")}],\\n' +\n ' \"subject\": f\"Welcome, {body.get(\\'name\\', \\'friend\\')}!\",\\n' +\n ' \"html\": f\"<h1>Welcome!</h1>\",\\n' +\n ' })\\n' +\n ' return jsonify(result)\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: [\n ...NEXT_STEPS,\n 'Register the blueprint: app.register_blueprint(notify_bp, url_prefix=\"/api\")',\n ],\n docsUrl: DOCS,\n },\n fastapi: {\n channel: 'email',\n language: 'python',\n framework: 'fastapi',\n variant: 'sdk',\n files: [\n {\n path: 'app/zyphr_client.py',\n purpose: 'REST helper for the Zyphr API',\n contents:\n 'import os\\n' +\n 'import httpx\\n\\n' +\n 'BASE_URL = \"https://api.zyphr.dev/v1\"\\n\\n' +\n 'async def zyphr_request(method: str, path: str, json: dict | None = None) -> dict:\\n' +\n ' async with httpx.AsyncClient() as client:\\n' +\n ' resp = await client.request(\\n' +\n ' method, f\"{BASE_URL}{path}\",\\n' +\n ' headers={\"X-API-Key\": os.environ[\"ZYPHR_API_KEY\"], \"Content-Type\": \"application/json\"},\\n' +\n ' json=json,\\n' +\n ' )\\n' +\n ' resp.raise_for_status()\\n' +\n ' return resp.json()\\n',\n overwrite: false,\n },\n {\n path: 'app/routers/notify.py',\n purpose: 'FastAPI router that sends a welcome email',\n contents:\n 'from fastapi import APIRouter\\n' +\n 'from pydantic import BaseModel, EmailStr\\n' +\n 'from ..zyphr_client import zyphr_request\\n\\n' +\n 'router = APIRouter()\\n\\n' +\n 'class NotifyIn(BaseModel):\\n' +\n ' to: EmailStr\\n' +\n ' name: str\\n\\n' +\n '@router.post(\"/notify\")\\n' +\n 'async def notify(body: NotifyIn):\\n' +\n ' return await zyphr_request(\"POST\", \"/emails\", json={\\n' +\n ' \"to\": [{\"email\": body.to, \"name\": body.name}],\\n' +\n ' \"subject\": f\"Welcome, {body.name}!\",\\n' +\n ' \"html\": f\"<h1>Welcome, {body.name}!</h1>\",\\n' +\n ' })\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: [\n ...NEXT_STEPS,\n 'Install httpx: `pip install httpx` (or `poetry add httpx`)',\n 'Mount the router: app.include_router(router, prefix=\"/api\")',\n ],\n docsUrl: DOCS,\n },\n },\n },\n ruby: {\n sdk: {\n channel: 'email',\n language: 'ruby',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'config/initializers/zyphr.rb',\n purpose: 'Zyphr SDK configuration',\n contents:\n \"require 'zyphr'\\n\\n\" +\n 'Zyphr.configure do |config|\\n' +\n \" config.api_key['X-API-Key'] = ENV.fetch('ZYPHR_API_KEY')\\n\" +\n 'end\\n',\n overwrite: false,\n },\n {\n path: 'app/services/notify_service.rb',\n purpose: 'Service object that sends a welcome email',\n contents:\n 'class NotifyService\\n' +\n ' def self.send_welcome_email(to:, name:)\\n' +\n ' Zyphr::EmailsApi.new.send_email(\\n' +\n ' Zyphr::SendEmailRequest.new(\\n' +\n ' to: [{ email: to, name: name }],\\n' +\n \" subject: \\\"Welcome, #{name}!\\\",\\n\" +\n \" html: \\\"<h1>Welcome, #{name}!</h1>\\\"\\n\" +\n ' )\\n' +\n ' )\\n' +\n ' end\\n' +\n 'end\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n frameworks: {\n rails: {\n channel: 'email',\n language: 'ruby',\n framework: 'rails',\n variant: 'sdk',\n files: [\n {\n path: 'config/initializers/zyphr.rb',\n purpose: 'Zyphr SDK configuration',\n contents:\n \"require 'zyphr'\\n\\n\" +\n 'Zyphr.configure do |config|\\n' +\n \" config.api_key['X-API-Key'] = ENV.fetch('ZYPHR_API_KEY')\\n\" +\n 'end\\n',\n overwrite: false,\n },\n {\n path: 'app/controllers/notify_controller.rb',\n purpose: 'Rails controller that sends a welcome email',\n contents:\n 'class NotifyController < ApplicationController\\n' +\n ' def create\\n' +\n ' result = Zyphr::EmailsApi.new.send_email(\\n' +\n ' Zyphr::SendEmailRequest.new(\\n' +\n ' to: [{ email: params.require(:to), name: params[:name] }],\\n' +\n \" subject: \\\"Welcome, #{params[:name]}!\\\",\\n\" +\n \" html: \\\"<h1>Welcome, #{params[:name]}!</h1>\\\"\\n\" +\n ' )\\n' +\n ' )\\n' +\n ' render json: result\\n' +\n ' end\\n' +\n 'end\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: [\n ...NEXT_STEPS,\n \"Add a route in config/routes.rb: post '/notify', to: 'notify#create'\",\n ],\n docsUrl: DOCS,\n },\n },\n },\n go: {\n sdk: {\n channel: 'email',\n language: 'go',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'internal/zyphr/client.go',\n purpose: 'Thin REST client for the Zyphr API',\n contents:\n 'package zyphr\\n\\n' +\n 'import (\\n' +\n '\\t\"bytes\"\\n' +\n '\\t\"encoding/json\"\\n' +\n '\\t\"fmt\"\\n' +\n '\\t\"io\"\\n' +\n '\\t\"net/http\"\\n' +\n '\\t\"os\"\\n' +\n ')\\n\\n' +\n 'const baseURL = \"https://api.zyphr.dev/v1\"\\n\\n' +\n 'type Client struct {\\n' +\n '\\tAPIKey string\\n' +\n '\\tHTTPClient *http.Client\\n' +\n '}\\n\\n' +\n 'func NewClient() *Client {\\n' +\n '\\treturn &Client{APIKey: os.Getenv(\"ZYPHR_API_KEY\"), HTTPClient: &http.Client{}}\\n' +\n '}\\n\\n' +\n 'func (c *Client) Do(method, path string, body any) ([]byte, error) {\\n' +\n '\\tvar buf io.Reader\\n' +\n '\\tif body != nil {\\n' +\n '\\t\\tb, err := json.Marshal(body)\\n' +\n '\\t\\tif err != nil { return nil, fmt.Errorf(\"marshal: %w\", err) }\\n' +\n '\\t\\tbuf = bytes.NewReader(b)\\n' +\n '\\t}\\n' +\n '\\treq, _ := http.NewRequest(method, baseURL+path, buf)\\n' +\n '\\treq.Header.Set(\"X-API-Key\", c.APIKey)\\n' +\n '\\treq.Header.Set(\"Content-Type\", \"application/json\")\\n' +\n '\\tresp, err := c.HTTPClient.Do(req)\\n' +\n '\\tif err != nil { return nil, err }\\n' +\n '\\tdefer resp.Body.Close()\\n' +\n '\\tdata, _ := io.ReadAll(resp.Body)\\n' +\n '\\tif resp.StatusCode >= 400 { return nil, fmt.Errorf(\"zyphr %d: %s\", resp.StatusCode, data) }\\n' +\n '\\treturn data, nil\\n' +\n '}\\n',\n overwrite: false,\n },\n {\n path: 'internal/notify/notify.go',\n purpose: 'Send a welcome email through Zyphr',\n contents:\n 'package notify\\n\\n' +\n 'import \"yourapp/internal/zyphr\"\\n\\n' +\n 'func SendWelcomeEmail(client *zyphr.Client, to, name string) ([]byte, error) {\\n' +\n '\\treturn client.Do(\"POST\", \"/emails\", map[string]any{\\n' +\n '\\t\\t\"to\": []map[string]string{{\"email\": to, \"name\": name}},\\n' +\n '\\t\\t\"subject\": \"Welcome, \" + name + \"!\",\\n' +\n '\\t\\t\"html\": \"<h1>Welcome, \" + name + \"!</h1>\",\\n' +\n '\\t})\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n php: {\n sdk: {\n channel: 'email',\n language: 'php',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'app/Services/ZyphrClient.php',\n purpose: 'Guzzle-backed Zyphr client',\n contents:\n '<?php\\n\\n' +\n 'namespace App\\\\Services;\\n\\n' +\n 'use GuzzleHttp\\\\Client;\\n\\n' +\n 'class ZyphrClient\\n' +\n '{\\n' +\n ' private Client $http;\\n\\n' +\n ' public function __construct()\\n' +\n ' {\\n' +\n ' $this->http = new Client([\\n' +\n \" 'base_uri' => 'https://api.zyphr.dev/v1/',\\n\" +\n \" 'headers' => [\\n\" +\n \" 'X-API-Key' => getenv('ZYPHR_API_KEY'),\\n\" +\n \" 'Content-Type' => 'application/json',\\n\" +\n ' ],\\n' +\n ' ]);\\n' +\n ' }\\n\\n' +\n ' public function sendWelcomeEmail(string $to, string $name): array\\n' +\n ' {\\n' +\n \" $response = $this->http->post('emails', [\\n\" +\n \" 'json' => [\\n\" +\n \" 'to' => [['email' => $to, 'name' => $name]],\\n\" +\n \" 'subject' => \\\"Welcome, {$name}!\\\",\\n\" +\n \" 'html' => \\\"<h1>Welcome, {$name}!</h1>\\\",\\n\" +\n ' ],\\n' +\n ' ]);\\n' +\n \" return json_decode((string) $response->getBody(), true);\\n\" +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n frameworks: {\n laravel: {\n channel: 'email',\n language: 'php',\n framework: 'laravel',\n variant: 'sdk',\n files: [\n {\n path: 'app/Services/ZyphrClient.php',\n purpose: 'Laravel-friendly Zyphr client',\n contents:\n '<?php\\n\\n' +\n 'namespace App\\\\Services;\\n\\n' +\n 'use Illuminate\\\\Support\\\\Facades\\\\Http;\\n\\n' +\n 'class ZyphrClient\\n' +\n '{\\n' +\n ' public function sendWelcomeEmail(string $to, string $name): array\\n' +\n ' {\\n' +\n ' $response = Http::withHeaders([\\n' +\n \" 'X-API-Key' => config('services.zyphr.api_key'),\\n\" +\n \" 'Content-Type' => 'application/json',\\n\" +\n \" ])->post('https://api.zyphr.dev/v1/emails', [\\n\" +\n \" 'to' => [['email' => $to, 'name' => $name]],\\n\" +\n \" 'subject' => \\\"Welcome, {$name}!\\\",\\n\" +\n \" 'html' => \\\"<h1>Welcome, {$name}!</h1>\\\",\\n\" +\n ' ]);\\n' +\n ' $response->throw();\\n' +\n \" return $response->json();\\n\" +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n {\n path: 'config/services.php (snippet)',\n purpose: 'Register the Zyphr API key under services config',\n contents:\n \"'zyphr' => [\\n\" +\n \" 'api_key' => env('ZYPHR_API_KEY'),\\n\" +\n '],\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n },\n csharp: {\n sdk: {\n channel: 'email',\n language: 'csharp',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'Services/ZyphrClient.cs',\n purpose: 'Singleton-style Zyphr client wrapper',\n contents:\n 'using ZyphrDev.SDK.Api;\\n' +\n 'using ZyphrDev.SDK.Client;\\n' +\n 'using ZyphrDev.SDK.Model;\\n\\n' +\n 'namespace YourApp.Services;\\n\\n' +\n 'public class ZyphrClient\\n' +\n '{\\n' +\n ' private readonly EmailsApi _emails;\\n\\n' +\n ' public ZyphrClient()\\n' +\n ' {\\n' +\n ' var config = new Configuration\\n' +\n ' {\\n' +\n ' ApiKey = new Dictionary<string, string>\\n' +\n ' {\\n' +\n ' { \"X-API-Key\", Environment.GetEnvironmentVariable(\"ZYPHR_API_KEY\")! }\\n' +\n ' }\\n' +\n ' };\\n' +\n ' _emails = new EmailsApi(config);\\n' +\n ' }\\n\\n' +\n ' public async Task<SendEmailResponse> SendWelcomeEmailAsync(string to, string name)\\n' +\n ' {\\n' +\n ' return await _emails.SendEmailAsync(new SendEmailRequest(\\n' +\n ' to: new List<EmailAddress> { new() { Email = to, Name = name } },\\n' +\n ' subject: $\"Welcome, {name}!\",\\n' +\n ' html: $\"<h1>Welcome, {name}!</h1>\"\\n' +\n ' ));\\n' +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n frameworks: {\n aspnetcore: {\n channel: 'email',\n language: 'csharp',\n framework: 'aspnetcore',\n variant: 'sdk',\n files: [\n {\n path: 'Services/ZyphrClient.cs',\n purpose: 'DI-friendly Zyphr client',\n contents:\n 'using ZyphrDev.SDK.Api;\\n' +\n 'using ZyphrDev.SDK.Client;\\n' +\n 'using ZyphrDev.SDK.Model;\\n\\n' +\n 'namespace YourApp.Services;\\n\\n' +\n 'public class ZyphrClient\\n' +\n '{\\n' +\n ' public EmailsApi Emails { get; }\\n\\n' +\n ' public ZyphrClient(IConfiguration cfg)\\n' +\n ' {\\n' +\n ' var config = new Configuration\\n' +\n ' {\\n' +\n ' ApiKey = new Dictionary<string, string>\\n' +\n ' {\\n' +\n ' { \"X-API-Key\", cfg[\"Zyphr:ApiKey\"]! }\\n' +\n ' }\\n' +\n ' };\\n' +\n ' Emails = new EmailsApi(config);\\n' +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n {\n path: 'Controllers/NotifyController.cs',\n purpose: 'ASP.NET Core controller that sends a welcome email',\n contents:\n 'using Microsoft.AspNetCore.Mvc;\\n' +\n 'using YourApp.Services;\\n' +\n 'using ZyphrDev.SDK.Model;\\n\\n' +\n '[ApiController]\\n' +\n '[Route(\"api/[controller]\")]\\n' +\n 'public class NotifyController : ControllerBase\\n' +\n '{\\n' +\n ' private readonly ZyphrClient _zyphr;\\n' +\n ' public NotifyController(ZyphrClient zyphr) => _zyphr = zyphr;\\n\\n' +\n ' public record NotifyIn(string To, string Name);\\n\\n' +\n ' [HttpPost]\\n' +\n ' public async Task<IActionResult> Post([FromBody] NotifyIn body)\\n' +\n ' {\\n' +\n ' var result = await _zyphr.Emails.SendEmailAsync(new SendEmailRequest(\\n' +\n ' to: new List<EmailAddress> { new() { Email = body.To, Name = body.Name } },\\n' +\n ' subject: $\"Welcome, {body.Name}!\",\\n' +\n ' html: $\"<h1>Welcome, {body.Name}!</h1>\"\\n' +\n ' ));\\n' +\n ' return Ok(result);\\n' +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: [\n ...NEXT_STEPS,\n 'Register the client in Program.cs: builder.Services.AddSingleton<ZyphrClient>();',\n ],\n docsUrl: DOCS,\n },\n },\n },\n};\n","import type { QuickstartChannelMap } from '../quickstart-types.js';\n\nconst DOCS = 'https://docs.zyphr.dev/channels/in-app-messaging';\nconst ENV: string[] = ['ZYPHR_API_KEY'];\nconst NEXT_STEPS = [\n 'Add ZYPHR_API_KEY to your .env file.',\n 'Use the matching subscriberId on the client (e.g. @zyphr-dev/inbox-react) to display the message.',\n];\n\nexport const inboxChannel: QuickstartChannelMap = {\n node: {\n sdk: {\n channel: 'inbox',\n language: 'node',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'src/services/inbox.ts',\n purpose: 'Send an in-app inbox message through Zyphr',\n contents:\n \"import { Zyphr } from '@zyphr-dev/node-sdk';\\n\\n\" +\n 'const zyphr = new Zyphr({ apiKey: process.env.ZYPHR_API_KEY! });\\n\\n' +\n 'export async function notifyReportReady(subscriberId: string, reportId: string) {\\n' +\n ' return await zyphr.inbox.sendInApp({\\n' +\n ' subscriberId,\\n' +\n \" title: 'New report ready',\\n\" +\n \" body: 'Your report finished processing — click to view.',\\n\" +\n ' actionUrl: `/reports/${reportId}`,\\n' +\n \" actionLabel: 'View report',\\n\" +\n ' });\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n python: {\n sdk: {\n channel: 'inbox',\n language: 'python',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'app/inbox.py',\n purpose: 'Send an in-app inbox message through Zyphr',\n contents:\n 'from .zyphr_client import zyphr_request\\n\\n' +\n 'def notify_report_ready(subscriber_id: str, report_id: str) -> dict:\\n' +\n ' return zyphr_request(\"POST\", \"/inbox\", json={\\n' +\n ' \"subscriberId\": subscriber_id,\\n' +\n ' \"title\": \"New report ready\",\\n' +\n ' \"body\": \"Your report finished processing — click to view.\",\\n' +\n ' \"actionUrl\": f\"/reports/{report_id}\",\\n' +\n ' \"actionLabel\": \"View report\",\\n' +\n ' })\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n ruby: {\n sdk: {\n channel: 'inbox',\n language: 'ruby',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'app/services/inbox_service.rb',\n purpose: 'Send an in-app inbox message through Zyphr',\n contents:\n 'class InboxService\\n' +\n ' def self.notify_report_ready(subscriber_id:, report_id:)\\n' +\n ' Zyphr::InboxApi.new.send_in_app(\\n' +\n ' Zyphr::SendInAppRequest.new(\\n' +\n ' subscriber_id: subscriber_id,\\n' +\n \" title: 'New report ready',\\n\" +\n \" body: 'Your report finished processing — click to view.',\\n\" +\n \" action_url: \\\"/reports/#{report_id}\\\",\\n\" +\n \" action_label: 'View report'\\n\" +\n ' )\\n' +\n ' )\\n' +\n ' end\\n' +\n 'end\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n go: {\n sdk: {\n channel: 'inbox',\n language: 'go',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'internal/notify/inbox.go',\n purpose: 'Send an in-app inbox message through Zyphr',\n contents:\n 'package notify\\n\\n' +\n 'import \"yourapp/internal/zyphr\"\\n\\n' +\n 'func NotifyReportReady(client *zyphr.Client, subscriberID, reportID string) ([]byte, error) {\\n' +\n '\\treturn client.Do(\"POST\", \"/inbox\", map[string]any{\\n' +\n '\\t\\t\"subscriberId\": subscriberID,\\n' +\n '\\t\\t\"title\": \"New report ready\",\\n' +\n '\\t\\t\"body\": \"Your report finished processing — click to view.\",\\n' +\n '\\t\\t\"actionUrl\": \"/reports/\" + reportID,\\n' +\n '\\t\\t\"actionLabel\": \"View report\",\\n' +\n '\\t})\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n php: {\n sdk: {\n channel: 'inbox',\n language: 'php',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'app/Services/InboxService.php',\n purpose: 'Send an in-app inbox message through Zyphr',\n contents:\n '<?php\\n\\n' +\n 'namespace App\\\\Services;\\n\\n' +\n 'use GuzzleHttp\\\\Client;\\n\\n' +\n 'class InboxService\\n' +\n '{\\n' +\n ' public function notifyReportReady(string $subscriberId, string $reportId): array\\n' +\n ' {\\n' +\n \" $http = new Client([\\n\" +\n \" 'base_uri' => 'https://api.zyphr.dev/v1/',\\n\" +\n \" 'headers' => [\\n\" +\n \" 'X-API-Key' => getenv('ZYPHR_API_KEY'),\\n\" +\n \" 'Content-Type' => 'application/json',\\n\" +\n ' ],\\n' +\n \" ]);\\n\" +\n \" $r = $http->post('inbox', ['json' => [\\n\" +\n \" 'subscriberId' => $subscriberId,\\n\" +\n \" 'title' => 'New report ready',\\n\" +\n \" 'body' => 'Your report finished processing — click to view.',\\n\" +\n \" 'actionUrl' => \\\"/reports/{$reportId}\\\",\\n\" +\n \" 'actionLabel' => 'View report',\\n\" +\n ' ]]);\\n' +\n \" return json_decode((string) $r->getBody(), true);\\n\" +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n csharp: {\n sdk: {\n channel: 'inbox',\n language: 'csharp',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'Services/InboxService.cs',\n purpose: 'Send an in-app inbox message through Zyphr',\n contents:\n 'using ZyphrDev.SDK.Api;\\n' +\n 'using ZyphrDev.SDK.Client;\\n' +\n 'using ZyphrDev.SDK.Model;\\n\\n' +\n 'namespace YourApp.Services;\\n\\n' +\n 'public class InboxService\\n' +\n '{\\n' +\n ' private readonly InboxApi _inbox;\\n' +\n ' public InboxService()\\n' +\n ' {\\n' +\n ' var config = new Configuration\\n' +\n ' {\\n' +\n ' ApiKey = new Dictionary<string, string>\\n' +\n ' {\\n' +\n ' { \"X-API-Key\", Environment.GetEnvironmentVariable(\"ZYPHR_API_KEY\")! }\\n' +\n ' }\\n' +\n ' };\\n' +\n ' _inbox = new InboxApi(config);\\n' +\n ' }\\n\\n' +\n ' public Task<SendInAppResponse> NotifyReportReadyAsync(string subscriberId, string reportId)\\n' +\n ' {\\n' +\n ' return _inbox.SendInAppAsync(new SendInAppRequest(\\n' +\n ' subscriberId: subscriberId,\\n' +\n ' title: \"New report ready\",\\n' +\n ' body: \"Your report finished processing — click to view.\",\\n' +\n ' actionUrl: $\"/reports/{reportId}\",\\n' +\n ' actionLabel: \"View report\"\\n' +\n ' ));\\n' +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n};\n","import type { QuickstartChannelMap } from '../quickstart-types.js';\n\nconst DOCS = 'https://docs.zyphr.dev/channels/push-notifications';\nconst ENV: string[] = ['ZYPHR_API_KEY'];\nconst NEXT_STEPS = [\n 'Add ZYPHR_API_KEY to your .env file.',\n 'Register at least one device for the target subscriber (via the SDK or dashboard) before sending.',\n 'Verify your push provider credentials (APNs/FCM) are configured in the Zyphr dashboard.',\n];\n\nexport const pushChannel: QuickstartChannelMap = {\n node: {\n sdk: {\n channel: 'push',\n language: 'node',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'src/services/push.ts',\n purpose: 'Send a push notification through Zyphr',\n contents:\n \"import { Zyphr } from '@zyphr-dev/node-sdk';\\n\\n\" +\n 'const zyphr = new Zyphr({ apiKey: process.env.ZYPHR_API_KEY! });\\n\\n' +\n 'export async function pushOrderShipped(subscriberId: string, orderId: string) {\\n' +\n ' return await zyphr.push.sendPush({\\n' +\n ' subscriberId,\\n' +\n \" title: 'Order shipped',\\n\" +\n ' body: `Order ${orderId} is on its way.`,\\n' +\n ' data: { orderId },\\n' +\n ' });\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n python: {\n sdk: {\n channel: 'push',\n language: 'python',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'app/push.py',\n purpose: 'Send a push notification through Zyphr',\n contents:\n 'from .zyphr_client import zyphr_request\\n\\n' +\n 'def push_order_shipped(subscriber_id: str, order_id: str) -> dict:\\n' +\n ' return zyphr_request(\"POST\", \"/push\", json={\\n' +\n ' \"subscriberId\": subscriber_id,\\n' +\n ' \"title\": \"Order shipped\",\\n' +\n ' \"body\": f\"Order {order_id} is on its way.\",\\n' +\n ' \"data\": {\"orderId\": order_id},\\n' +\n ' })\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n ruby: {\n sdk: {\n channel: 'push',\n language: 'ruby',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'app/services/push_service.rb',\n purpose: 'Send a push notification through Zyphr',\n contents:\n 'class PushService\\n' +\n ' def self.order_shipped(subscriber_id:, order_id:)\\n' +\n ' Zyphr::PushApi.new.send_push(\\n' +\n ' Zyphr::SendPushRequest.new(\\n' +\n ' subscriber_id: subscriber_id,\\n' +\n \" title: 'Order shipped',\\n\" +\n \" body: \\\"Order #{order_id} is on its way.\\\",\\n\" +\n ' data: { orderId: order_id }\\n' +\n ' )\\n' +\n ' )\\n' +\n ' end\\n' +\n 'end\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n go: {\n sdk: {\n channel: 'push',\n language: 'go',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'internal/notify/push.go',\n purpose: 'Send a push notification through Zyphr',\n contents:\n 'package notify\\n\\n' +\n 'import \"yourapp/internal/zyphr\"\\n\\n' +\n 'func PushOrderShipped(client *zyphr.Client, subscriberID, orderID string) ([]byte, error) {\\n' +\n '\\treturn client.Do(\"POST\", \"/push\", map[string]any{\\n' +\n '\\t\\t\"subscriberId\": subscriberID,\\n' +\n '\\t\\t\"title\": \"Order shipped\",\\n' +\n '\\t\\t\"body\": \"Order \" + orderID + \" is on its way.\",\\n' +\n '\\t\\t\"data\": map[string]string{\"orderId\": orderID},\\n' +\n '\\t})\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n php: {\n sdk: {\n channel: 'push',\n language: 'php',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'app/Services/PushService.php',\n purpose: 'Send a push notification through Zyphr',\n contents:\n '<?php\\n\\n' +\n 'namespace App\\\\Services;\\n\\n' +\n 'use GuzzleHttp\\\\Client;\\n\\n' +\n 'class PushService\\n' +\n '{\\n' +\n ' public function orderShipped(string $subscriberId, string $orderId): array\\n' +\n ' {\\n' +\n \" $http = new Client([\\n\" +\n \" 'base_uri' => 'https://api.zyphr.dev/v1/',\\n\" +\n \" 'headers' => [\\n\" +\n \" 'X-API-Key' => getenv('ZYPHR_API_KEY'),\\n\" +\n \" 'Content-Type' => 'application/json',\\n\" +\n ' ],\\n' +\n \" ]);\\n\" +\n \" $r = $http->post('push', ['json' => [\\n\" +\n \" 'subscriberId' => $subscriberId,\\n\" +\n \" 'title' => 'Order shipped',\\n\" +\n \" 'body' => \\\"Order {$orderId} is on its way.\\\",\\n\" +\n \" 'data' => ['orderId' => $orderId],\\n\" +\n ' ]]);\\n' +\n \" return json_decode((string) $r->getBody(), true);\\n\" +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n csharp: {\n sdk: {\n channel: 'push',\n language: 'csharp',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'Services/PushService.cs',\n purpose: 'Send a push notification through Zyphr',\n contents:\n 'using ZyphrDev.SDK.Api;\\n' +\n 'using ZyphrDev.SDK.Client;\\n' +\n 'using ZyphrDev.SDK.Model;\\n\\n' +\n 'namespace YourApp.Services;\\n\\n' +\n 'public class PushService\\n' +\n '{\\n' +\n ' private readonly PushApi _push;\\n' +\n ' public PushService()\\n' +\n ' {\\n' +\n ' var config = new Configuration\\n' +\n ' {\\n' +\n ' ApiKey = new Dictionary<string, string>\\n' +\n ' {\\n' +\n ' { \"X-API-Key\", Environment.GetEnvironmentVariable(\"ZYPHR_API_KEY\")! }\\n' +\n ' }\\n' +\n ' };\\n' +\n ' _push = new PushApi(config);\\n' +\n ' }\\n\\n' +\n ' public Task<SendPushResponse> OrderShippedAsync(string subscriberId, string orderId)\\n' +\n ' {\\n' +\n ' return _push.SendPushAsync(new SendPushRequest(\\n' +\n ' subscriberId: subscriberId,\\n' +\n ' title: \"Order shipped\",\\n' +\n ' body: $\"Order {orderId} is on its way.\"\\n' +\n ' ));\\n' +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n};\n","import type { QuickstartChannelMap } from '../quickstart-types.js';\n\nconst DOCS = 'https://docs.zyphr.dev/channels/sms';\nconst ENV: string[] = ['ZYPHR_API_KEY'];\nconst NEXT_STEPS = [\n 'Add ZYPHR_API_KEY to your .env file.',\n 'Recipients MUST be in E.164 format (e.g. +14155551234).',\n 'Provision your SMS sender (phone number or alphanumeric sender ID) in the Zyphr dashboard.',\n];\n\nexport const smsChannel: QuickstartChannelMap = {\n node: {\n sdk: {\n channel: 'sms',\n language: 'node',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'src/services/sms.ts',\n purpose: 'Send an SMS through Zyphr',\n contents:\n \"import { Zyphr } from '@zyphr-dev/node-sdk';\\n\\n\" +\n 'const zyphr = new Zyphr({ apiKey: process.env.ZYPHR_API_KEY! });\\n\\n' +\n 'export async function sendOtp(to: string, code: string) {\\n' +\n ' return await zyphr.sms.sendSms({\\n' +\n ' to,\\n' +\n ' body: `Your verification code is ${code}. Expires in 5 minutes.`,\\n' +\n ' });\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n python: {\n sdk: {\n channel: 'sms',\n language: 'python',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'app/sms.py',\n purpose: 'Send an SMS through Zyphr',\n contents:\n 'from .zyphr_client import zyphr_request\\n\\n' +\n 'def send_otp(to: str, code: str) -> dict:\\n' +\n ' return zyphr_request(\"POST\", \"/sms\", json={\\n' +\n ' \"to\": to,\\n' +\n ' \"body\": f\"Your verification code is {code}. Expires in 5 minutes.\",\\n' +\n ' })\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n ruby: {\n sdk: {\n channel: 'sms',\n language: 'ruby',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'app/services/sms_service.rb',\n purpose: 'Send an SMS through Zyphr',\n contents:\n 'class SmsService\\n' +\n ' def self.send_otp(to:, code:)\\n' +\n ' Zyphr::SMSApi.new.send_sms(\\n' +\n ' Zyphr::SendSmsRequest.new(\\n' +\n ' to: to,\\n' +\n \" body: \\\"Your verification code is #{code}. Expires in 5 minutes.\\\"\\n\" +\n ' )\\n' +\n ' )\\n' +\n ' end\\n' +\n 'end\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n go: {\n sdk: {\n channel: 'sms',\n language: 'go',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'internal/notify/sms.go',\n purpose: 'Send an SMS through Zyphr',\n contents:\n 'package notify\\n\\n' +\n 'import \"yourapp/internal/zyphr\"\\n\\n' +\n 'func SendOtp(client *zyphr.Client, to, code string) ([]byte, error) {\\n' +\n '\\treturn client.Do(\"POST\", \"/sms\", map[string]any{\\n' +\n '\\t\\t\"to\": to,\\n' +\n '\\t\\t\"body\": \"Your verification code is \" + code + \". Expires in 5 minutes.\",\\n' +\n '\\t})\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n php: {\n sdk: {\n channel: 'sms',\n language: 'php',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'app/Services/SmsService.php',\n purpose: 'Send an SMS through Zyphr',\n contents:\n '<?php\\n\\n' +\n 'namespace App\\\\Services;\\n\\n' +\n 'use GuzzleHttp\\\\Client;\\n\\n' +\n 'class SmsService\\n' +\n '{\\n' +\n ' public function sendOtp(string $to, string $code): array\\n' +\n ' {\\n' +\n \" $http = new Client([\\n\" +\n \" 'base_uri' => 'https://api.zyphr.dev/v1/',\\n\" +\n \" 'headers' => [\\n\" +\n \" 'X-API-Key' => getenv('ZYPHR_API_KEY'),\\n\" +\n \" 'Content-Type' => 'application/json',\\n\" +\n ' ],\\n' +\n \" ]);\\n\" +\n \" $r = $http->post('sms', ['json' => [\\n\" +\n \" 'to' => $to,\\n\" +\n \" 'body' => \\\"Your verification code is {$code}. Expires in 5 minutes.\\\",\\n\" +\n ' ]]);\\n' +\n \" return json_decode((string) $r->getBody(), true);\\n\" +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n csharp: {\n sdk: {\n channel: 'sms',\n language: 'csharp',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'Services/SmsService.cs',\n purpose: 'Send an SMS through Zyphr',\n contents:\n 'using ZyphrDev.SDK.Api;\\n' +\n 'using ZyphrDev.SDK.Client;\\n' +\n 'using ZyphrDev.SDK.Model;\\n\\n' +\n 'namespace YourApp.Services;\\n\\n' +\n 'public class SmsService\\n' +\n '{\\n' +\n ' private readonly SMSApi _sms;\\n' +\n ' public SmsService()\\n' +\n ' {\\n' +\n ' var config = new Configuration\\n' +\n ' {\\n' +\n ' ApiKey = new Dictionary<string, string>\\n' +\n ' {\\n' +\n ' { \"X-API-Key\", Environment.GetEnvironmentVariable(\"ZYPHR_API_KEY\")! }\\n' +\n ' }\\n' +\n ' };\\n' +\n ' _sms = new SMSApi(config);\\n' +\n ' }\\n\\n' +\n ' public Task<SendSmsResponse> SendOtpAsync(string to, string code)\\n' +\n ' {\\n' +\n ' return _sms.SendSmsAsync(new SendSmsRequest(\\n' +\n ' to: to,\\n' +\n ' body: $\"Your verification code is {code}. Expires in 5 minutes.\"\\n' +\n ' ));\\n' +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n};\n","import type { QuickstartChannelMap } from '../quickstart-types.js';\n\nconst DOCS = 'https://docs.zyphr.dev/features/webhooks-security';\n\nconst ENV: string[] = ['ZYPHR_WEBHOOK_SECRET'];\n\nconst NEXT_STEPS = [\n 'Add ZYPHR_WEBHOOK_SECRET to your .env file — get it from `zyphr.webhooks.rotateWebhookSecret(id)` or the Zyphr dashboard.',\n 'Configure the webhook endpoint URL in the Zyphr dashboard or via `create_webhook`.',\n 'ALWAYS verify signatures before processing payloads — never trust an unverified webhook.',\n 'Reject deliveries whose timestamp is more than 5 minutes from now to prevent replay attacks.',\n];\n\nexport const webhookChannel: QuickstartChannelMap = {\n node: {\n sdk: {\n channel: 'webhook',\n language: 'node',\n framework: null,\n variant: 'webhook-handler',\n files: [\n {\n path: 'src/lib/verifyZyphrWebhook.ts',\n purpose:\n 'Standard Webhooks (HMAC-SHA256) signature + timestamp verification. Mirrors the canonical snippet in apps/docs/docs/features/webhooks-security.md.',\n contents:\n \"import crypto from 'crypto';\\n\\n\" +\n 'export function verifyZyphrWebhook(\\n' +\n ' payload: string,\\n' +\n \" headers: { 'webhook-id': string; 'webhook-timestamp': string; 'webhook-signature': string },\\n\" +\n ' secret: string,\\n' +\n '): boolean {\\n' +\n \" const msgId = headers['webhook-id'];\\n\" +\n \" const timestamp = parseInt(headers['webhook-timestamp'], 10);\\n\" +\n \" const signatures = headers['webhook-signature'];\\n\\n\" +\n ' const now = Math.floor(Date.now() / 1000);\\n' +\n ' if (Math.abs(now - timestamp) > 300) return false;\\n\\n' +\n ' const signedContent = `${msgId}.${timestamp}.${payload}`;\\n' +\n ' const secretBytes = Buffer.from(\\n' +\n \" secret.startsWith('whsec_') ? secret.slice(6) : secret,\\n\" +\n \" 'hex',\\n\" +\n ' );\\n' +\n ' const expected = crypto\\n' +\n \" .createHmac('sha256', secretBytes)\\n\" +\n ' .update(signedContent)\\n' +\n \" .digest('base64');\\n\\n\" +\n \" for (const sig of signatures.split(' ')) {\\n\" +\n ' const sigValue = sig.slice(3);\\n' +\n ' if (\\n' +\n ' sigValue.length === expected.length &&\\n' +\n ' crypto.timingSafeEqual(Buffer.from(sigValue), Buffer.from(expected))\\n' +\n ' ) {\\n' +\n ' return true;\\n' +\n ' }\\n' +\n ' }\\n' +\n ' return false;\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n frameworks: {\n express: {\n channel: 'webhook',\n language: 'node',\n framework: 'express',\n variant: 'webhook-handler',\n files: [\n {\n path: 'src/lib/verifyZyphrWebhook.ts',\n purpose: 'Standard Webhooks signature verification helper',\n contents:\n \"import crypto from 'crypto';\\n\\n\" +\n 'export function verifyZyphrWebhook(payload: string, headers: Record<string,string>, secret: string): boolean {\\n' +\n \" const msgId = headers['webhook-id'];\\n\" +\n \" const timestamp = parseInt(headers['webhook-timestamp'], 10);\\n\" +\n \" const signatures = headers['webhook-signature'] || '';\\n\" +\n ' const now = Math.floor(Date.now() / 1000);\\n' +\n ' if (Math.abs(now - timestamp) > 300) return false;\\n' +\n ' const signedContent = `${msgId}.${timestamp}.${payload}`;\\n' +\n \" const secretBytes = Buffer.from(secret.startsWith('whsec_') ? secret.slice(6) : secret, 'hex');\\n\" +\n \" const expected = crypto.createHmac('sha256', secretBytes).update(signedContent).digest('base64');\\n\" +\n \" return signatures.split(' ').some((sig) => {\\n\" +\n ' const v = sig.slice(3);\\n' +\n ' return v.length === expected.length && crypto.timingSafeEqual(Buffer.from(v), Buffer.from(expected));\\n' +\n ' });\\n' +\n '}\\n',\n overwrite: false,\n },\n {\n path: 'src/routes/zyphrWebhook.ts',\n purpose:\n 'Express route that VERIFIES the signature before processing the webhook. Uses express.raw() so we can hash the exact bytes.',\n contents:\n \"import { Router, raw } from 'express';\\n\" +\n \"import { verifyZyphrWebhook } from '../lib/verifyZyphrWebhook.js';\\n\\n\" +\n 'export const zyphrWebhookRouter = Router();\\n\\n' +\n \"zyphrWebhookRouter.post('/zyphr', raw({ type: 'application/json' }), (req, res) => {\\n\" +\n ' const payload = (req.body as Buffer).toString();\\n' +\n ' const headers = req.headers as Record<string, string>;\\n' +\n ' if (!verifyZyphrWebhook(payload, headers, process.env.ZYPHR_WEBHOOK_SECRET!)) {\\n' +\n \" return res.status(401).send('invalid signature');\\n\" +\n ' }\\n' +\n ' const event = JSON.parse(payload) as { type: string; data: unknown };\\n' +\n \" console.log('zyphr event', event.type);\\n\" +\n ' res.sendStatus(204);\\n' +\n '});\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: [\n ...NEXT_STEPS,\n 'Mount the router BEFORE express.json(): app.use(zyphrWebhookRouter)',\n ],\n docsUrl: DOCS,\n },\n nextjs: {\n channel: 'webhook',\n language: 'node',\n framework: 'nextjs',\n variant: 'webhook-handler',\n files: [\n {\n path: 'src/lib/verifyZyphrWebhook.ts',\n purpose: 'Standard Webhooks signature verification helper',\n contents:\n \"import crypto from 'crypto';\\n\\n\" +\n 'export function verifyZyphrWebhook(payload: string, headers: Headers, secret: string): boolean {\\n' +\n \" const msgId = headers.get('webhook-id') || '';\\n\" +\n \" const timestamp = parseInt(headers.get('webhook-timestamp') || '0', 10);\\n\" +\n \" const signatures = headers.get('webhook-signature') || '';\\n\" +\n ' const now = Math.floor(Date.now() / 1000);\\n' +\n ' if (Math.abs(now - timestamp) > 300) return false;\\n' +\n ' const signedContent = `${msgId}.${timestamp}.${payload}`;\\n' +\n \" const secretBytes = Buffer.from(secret.startsWith('whsec_') ? secret.slice(6) : secret, 'hex');\\n\" +\n \" const expected = crypto.createHmac('sha256', secretBytes).update(signedContent).digest('base64');\\n\" +\n \" return signatures.split(' ').some((sig) => {\\n\" +\n ' const v = sig.slice(3);\\n' +\n ' return v.length === expected.length && crypto.timingSafeEqual(Buffer.from(v), Buffer.from(expected));\\n' +\n ' });\\n' +\n '}\\n',\n overwrite: false,\n },\n {\n path: 'src/app/api/zyphr/webhook/route.ts',\n purpose: 'Next.js App Router webhook handler with signature verification',\n contents:\n \"import { NextResponse } from 'next/server';\\n\" +\n \"import { verifyZyphrWebhook } from '@/lib/verifyZyphrWebhook';\\n\\n\" +\n 'export async function POST(req: Request) {\\n' +\n ' const payload = await req.text();\\n' +\n ' if (!verifyZyphrWebhook(payload, req.headers, process.env.ZYPHR_WEBHOOK_SECRET!)) {\\n' +\n \" return new NextResponse('invalid signature', { status: 401 });\\n\" +\n ' }\\n' +\n ' const event = JSON.parse(payload) as { type: string; data: unknown };\\n' +\n \" console.log('zyphr event', event.type);\\n\" +\n ' return new NextResponse(null, { status: 204 });\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n },\n python: {\n sdk: {\n channel: 'webhook',\n language: 'python',\n framework: null,\n variant: 'webhook-handler',\n files: [\n {\n path: 'app/zyphr_webhook.py',\n purpose: 'Standard Webhooks signature verification helper (verbatim from docs)',\n contents:\n 'import hashlib\\n' +\n 'import hmac\\n' +\n 'import base64\\n' +\n 'import time\\n\\n' +\n 'def verify_zyphr_webhook(payload: str, headers: dict, secret: str) -> bool:\\n' +\n ' msg_id = headers.get(\"webhook-id\", \"\")\\n' +\n ' timestamp = headers.get(\"webhook-timestamp\", \"\")\\n' +\n ' signature = headers.get(\"webhook-signature\", \"\")\\n\\n' +\n ' now = int(time.time())\\n' +\n ' if abs(now - int(timestamp)) > 300:\\n' +\n ' return False\\n\\n' +\n ' signed_content = f\"{msg_id}.{timestamp}.{payload}\"\\n' +\n ' secret_hex = secret.removeprefix(\"whsec_\")\\n' +\n ' secret_bytes = bytes.fromhex(secret_hex)\\n' +\n ' expected = base64.b64encode(\\n' +\n ' hmac.new(secret_bytes, signed_content.encode(), hashlib.sha256).digest()\\n' +\n ' ).decode()\\n\\n' +\n ' for sig in signature.split(\" \"):\\n' +\n ' sig_value = sig.removeprefix(\"v1,\")\\n' +\n ' if hmac.compare_digest(sig_value, expected):\\n' +\n ' return True\\n' +\n ' return False\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n frameworks: {\n flask: {\n channel: 'webhook',\n language: 'python',\n framework: 'flask',\n variant: 'webhook-handler',\n files: [\n {\n path: 'app/zyphr_webhook.py',\n purpose: 'Standard Webhooks signature verification helper',\n contents:\n 'import hashlib, hmac, base64, time\\n\\n' +\n 'def verify_zyphr_webhook(payload: str, headers, secret: str) -> bool:\\n' +\n ' msg_id = headers.get(\"webhook-id\", \"\")\\n' +\n ' timestamp = headers.get(\"webhook-timestamp\", \"\")\\n' +\n ' signature = headers.get(\"webhook-signature\", \"\")\\n' +\n ' if abs(int(time.time()) - int(timestamp)) > 300:\\n' +\n ' return False\\n' +\n ' signed = f\"{msg_id}.{timestamp}.{payload}\"\\n' +\n ' secret_bytes = bytes.fromhex(secret.removeprefix(\"whsec_\"))\\n' +\n ' expected = base64.b64encode(hmac.new(secret_bytes, signed.encode(), hashlib.sha256).digest()).decode()\\n' +\n ' return any(hmac.compare_digest(sig.removeprefix(\"v1,\"), expected) for sig in signature.split(\" \"))\\n',\n overwrite: false,\n },\n {\n path: 'app/routes/zyphr_webhook.py',\n purpose: 'Flask blueprint that verifies the webhook signature before processing',\n contents:\n 'import os, json\\n' +\n 'from flask import Blueprint, request, abort\\n' +\n 'from ..zyphr_webhook import verify_zyphr_webhook\\n\\n' +\n 'zyphr_webhook_bp = Blueprint(\"zyphr_webhook\", __name__)\\n\\n' +\n '@zyphr_webhook_bp.route(\"/webhooks/zyphr\", methods=[\"POST\"])\\n' +\n 'def handle():\\n' +\n ' payload = request.get_data(as_text=True)\\n' +\n ' if not verify_zyphr_webhook(payload, request.headers, os.environ[\"ZYPHR_WEBHOOK_SECRET\"]):\\n' +\n ' abort(401, \"invalid signature\")\\n' +\n ' event = json.loads(payload)\\n' +\n ' print(\"zyphr event\", event.get(\"type\"))\\n' +\n ' return \"\", 204\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n fastapi: {\n channel: 'webhook',\n language: 'python',\n framework: 'fastapi',\n variant: 'webhook-handler',\n files: [\n {\n path: 'app/routers/zyphr_webhook.py',\n purpose: 'FastAPI router that verifies the webhook signature before processing',\n contents:\n 'import os, json, hashlib, hmac, base64, time\\n' +\n 'from fastapi import APIRouter, Request, HTTPException\\n\\n' +\n 'router = APIRouter()\\n\\n' +\n 'def verify_zyphr_webhook(payload: str, headers, secret: str) -> bool:\\n' +\n ' msg_id = headers.get(\"webhook-id\", \"\")\\n' +\n ' timestamp = headers.get(\"webhook-timestamp\", \"\")\\n' +\n ' signature = headers.get(\"webhook-signature\", \"\")\\n' +\n ' if abs(int(time.time()) - int(timestamp)) > 300:\\n' +\n ' return False\\n' +\n ' signed = f\"{msg_id}.{timestamp}.{payload}\"\\n' +\n ' secret_bytes = bytes.fromhex(secret.removeprefix(\"whsec_\"))\\n' +\n ' expected = base64.b64encode(hmac.new(secret_bytes, signed.encode(), hashlib.sha256).digest()).decode()\\n' +\n ' return any(hmac.compare_digest(sig.removeprefix(\"v1,\"), expected) for sig in signature.split(\" \"))\\n\\n' +\n '@router.post(\"/webhooks/zyphr\")\\n' +\n 'async def handle(request: Request):\\n' +\n ' body = (await request.body()).decode()\\n' +\n ' if not verify_zyphr_webhook(body, request.headers, os.environ[\"ZYPHR_WEBHOOK_SECRET\"]):\\n' +\n ' raise HTTPException(status_code=401, detail=\"invalid signature\")\\n' +\n ' event = json.loads(body)\\n' +\n ' print(\"zyphr event\", event.get(\"type\"))\\n' +\n ' return {\"ok\": True}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n },\n ruby: {\n sdk: {\n channel: 'webhook',\n language: 'ruby',\n framework: null,\n variant: 'webhook-handler',\n files: [\n {\n path: 'app/services/zyphr_webhook.rb',\n purpose: 'Standard Webhooks signature verification helper',\n contents:\n \"require 'openssl'\\n\" +\n \"require 'base64'\\n\\n\" +\n 'class ZyphrWebhook\\n' +\n ' def self.verify(payload:, headers:, secret:)\\n' +\n \" msg_id = headers['webhook-id'].to_s\\n\" +\n \" timestamp = headers['webhook-timestamp'].to_i\\n\" +\n \" signatures = headers['webhook-signature'].to_s\\n\\n\" +\n ' return false if (Time.now.to_i - timestamp).abs > 300\\n\\n' +\n ' signed = \"#{msg_id}.#{timestamp}.#{payload}\"\\n' +\n \" hex = secret.start_with?('whsec_') ? secret[6..] : secret\\n\" +\n ' secret_bytes = [hex].pack(\\'H*\\')\\n' +\n \" expected = Base64.strict_encode64(OpenSSL::HMAC.digest('SHA256', secret_bytes, signed))\\n\\n\" +\n \" signatures.split(' ').any? do |sig|\\n\" +\n ' v = sig[3..]\\n' +\n ' v && Rack::Utils.secure_compare(v, expected)\\n' +\n ' end\\n' +\n ' end\\n' +\n 'end\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n frameworks: {\n rails: {\n channel: 'webhook',\n language: 'ruby',\n framework: 'rails',\n variant: 'webhook-handler',\n files: [\n {\n path: 'app/services/zyphr_webhook.rb',\n purpose: 'Standard Webhooks signature verification helper',\n contents:\n \"require 'openssl'\\n\" +\n \"require 'base64'\\n\\n\" +\n 'class ZyphrWebhook\\n' +\n ' def self.verify(payload:, headers:, secret:)\\n' +\n \" msg_id = headers['webhook-id'].to_s\\n\" +\n \" timestamp = headers['webhook-timestamp'].to_i\\n\" +\n \" signatures = headers['webhook-signature'].to_s\\n\" +\n ' return false if (Time.now.to_i - timestamp).abs > 300\\n' +\n ' signed = \"#{msg_id}.#{timestamp}.#{payload}\"\\n' +\n \" hex = secret.start_with?('whsec_') ? secret[6..] : secret\\n\" +\n ' secret_bytes = [hex].pack(\\'H*\\')\\n' +\n \" expected = Base64.strict_encode64(OpenSSL::HMAC.digest('SHA256', secret_bytes, signed))\\n\" +\n \" signatures.split(' ').any? { |sig| sig[3..] && ActiveSupport::SecurityUtils.secure_compare(sig[3..], expected) }\\n\" +\n ' end\\n' +\n 'end\\n',\n overwrite: false,\n },\n {\n path: 'app/controllers/zyphr_webhooks_controller.rb',\n purpose: 'Rails controller that verifies the webhook signature before processing',\n contents:\n 'class ZyphrWebhooksController < ApplicationController\\n' +\n ' skip_before_action :verify_authenticity_token\\n\\n' +\n ' def create\\n' +\n \" payload = request.raw_post\\n\" +\n \" secret = ENV.fetch('ZYPHR_WEBHOOK_SECRET')\\n\" +\n ' unless ZyphrWebhook.verify(payload: payload, headers: request.headers, secret: secret)\\n' +\n ' head :unauthorized and return\\n' +\n ' end\\n' +\n ' event = JSON.parse(payload)\\n' +\n \" Rails.logger.info(\\\"zyphr event #{event['type']}\\\")\\n\" +\n ' head :no_content\\n' +\n ' end\\n' +\n 'end\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: [\n ...NEXT_STEPS,\n \"Add route: post '/webhooks/zyphr', to: 'zyphr_webhooks#create'\",\n ],\n docsUrl: DOCS,\n },\n },\n },\n go: {\n sdk: {\n channel: 'webhook',\n language: 'go',\n framework: null,\n variant: 'webhook-handler',\n files: [\n {\n path: 'internal/zyphr/verify.go',\n purpose:\n 'Standard Webhooks signature verification helper (verbatim from docs).',\n contents:\n 'package zyphr\\n\\n' +\n 'import (\\n' +\n '\\t\"crypto/hmac\"\\n' +\n '\\t\"crypto/sha256\"\\n' +\n '\\t\"encoding/base64\"\\n' +\n '\\t\"encoding/hex\"\\n' +\n '\\t\"math\"\\n' +\n '\\t\"strconv\"\\n' +\n '\\t\"strings\"\\n' +\n '\\t\"time\"\\n' +\n ')\\n\\n' +\n 'func VerifyWebhook(payload, msgID, timestamp, signature, secret string) bool {\\n' +\n '\\tts, err := strconv.ParseInt(timestamp, 10, 64)\\n' +\n '\\tif err != nil { return false }\\n' +\n '\\tif math.Abs(float64(time.Now().Unix()-ts)) > 300 { return false }\\n\\n' +\n '\\tsigned := msgID + \".\" + timestamp + \".\" + payload\\n' +\n '\\tsecretHex := strings.TrimPrefix(secret, \"whsec_\")\\n' +\n '\\tsecretBytes, err := hex.DecodeString(secretHex)\\n' +\n '\\tif err != nil { return false }\\n' +\n '\\tmac := hmac.New(sha256.New, secretBytes)\\n' +\n '\\tmac.Write([]byte(signed))\\n' +\n '\\texpected := base64.StdEncoding.EncodeToString(mac.Sum(nil))\\n\\n' +\n '\\tfor _, sig := range strings.Split(signature, \" \") {\\n' +\n '\\t\\tv := strings.TrimPrefix(sig, \"v1,\")\\n' +\n '\\t\\tif hmac.Equal([]byte(v), []byte(expected)) { return true }\\n' +\n '\\t}\\n' +\n '\\treturn false\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n php: {\n sdk: {\n channel: 'webhook',\n language: 'php',\n framework: null,\n variant: 'webhook-handler',\n files: [\n {\n path: 'app/Webhooks/ZyphrWebhook.php',\n purpose: 'Standard Webhooks signature verification helper (verbatim from docs)',\n contents:\n '<?php\\n\\n' +\n 'namespace App\\\\Webhooks;\\n\\n' +\n 'class ZyphrWebhook\\n' +\n '{\\n' +\n ' public static function verify(string $payload, array $headers, string $secret): bool\\n' +\n ' {\\n' +\n \" $msgId = $headers['webhook-id'] ?? '';\\n\" +\n \" $timestamp = $headers['webhook-timestamp'] ?? '';\\n\" +\n \" $signature = $headers['webhook-signature'] ?? '';\\n\" +\n ' if (abs(time() - intval($timestamp)) > 300) {\\n' +\n ' return false;\\n' +\n ' }\\n' +\n ' $signed = \"{$msgId}.{$timestamp}.{$payload}\";\\n' +\n \" $hex = str_starts_with($secret, 'whsec_') ? substr($secret, 6) : $secret;\\n\" +\n \" $expected = base64_encode(hash_hmac('sha256', $signed, hex2bin($hex), true));\\n\" +\n \" foreach (explode(' ', $signature) as $sig) {\\n\" +\n ' if (hash_equals(substr($sig, 3), $expected)) {\\n' +\n ' return true;\\n' +\n ' }\\n' +\n ' }\\n' +\n ' return false;\\n' +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n frameworks: {\n laravel: {\n channel: 'webhook',\n language: 'php',\n framework: 'laravel',\n variant: 'webhook-handler',\n files: [\n {\n path: 'app/Webhooks/ZyphrWebhook.php',\n purpose: 'Standard Webhooks signature verification helper',\n contents:\n '<?php\\n\\n' +\n 'namespace App\\\\Webhooks;\\n\\n' +\n 'class ZyphrWebhook\\n' +\n '{\\n' +\n ' public static function verify(string $payload, array $headers, string $secret): bool\\n' +\n ' {\\n' +\n \" $msgId = $headers['webhook-id'][0] ?? '';\\n\" +\n \" $timestamp = $headers['webhook-timestamp'][0] ?? '';\\n\" +\n \" $signature = $headers['webhook-signature'][0] ?? '';\\n\" +\n ' if (abs(time() - intval($timestamp)) > 300) {\\n' +\n ' return false;\\n' +\n ' }\\n' +\n ' $signed = \"{$msgId}.{$timestamp}.{$payload}\";\\n' +\n \" $hex = str_starts_with($secret, 'whsec_') ? substr($secret, 6) : $secret;\\n\" +\n \" $expected = base64_encode(hash_hmac('sha256', $signed, hex2bin($hex), true));\\n\" +\n \" foreach (explode(' ', $signature) as $sig) {\\n\" +\n ' if (hash_equals(substr($sig, 3), $expected)) {\\n' +\n ' return true;\\n' +\n ' }\\n' +\n ' }\\n' +\n ' return false;\\n' +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n {\n path: 'app/Http/Controllers/ZyphrWebhookController.php',\n purpose: 'Laravel controller that verifies the webhook signature before processing',\n contents:\n '<?php\\n\\n' +\n 'namespace App\\\\Http\\\\Controllers;\\n\\n' +\n 'use App\\\\Webhooks\\\\ZyphrWebhook;\\n' +\n 'use Illuminate\\\\Http\\\\Request;\\n\\n' +\n 'class ZyphrWebhookController extends Controller\\n' +\n '{\\n' +\n ' public function handle(Request $request)\\n' +\n ' {\\n' +\n \" $payload = $request->getContent();\\n\" +\n \" $secret = config('services.zyphr.webhook_secret');\\n\" +\n \" if (! ZyphrWebhook::verify($payload, $request->headers->all(), $secret)) {\\n\" +\n \" return response('invalid signature', 401);\\n\" +\n ' }\\n' +\n \" $event = json_decode($payload, true);\\n\" +\n \" \\\\Log::info('zyphr event ' . ($event['type'] ?? 'unknown'));\\n\" +\n \" return response()->noContent();\\n\" +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: [\n ...NEXT_STEPS,\n \"Register route in routes/api.php: Route::post('/webhooks/zyphr', [ZyphrWebhookController::class, 'handle']);\",\n \"Exclude this route from CSRF (VerifyCsrfToken::$except).\",\n ],\n docsUrl: DOCS,\n },\n },\n },\n csharp: {\n sdk: {\n channel: 'webhook',\n language: 'csharp',\n framework: null,\n variant: 'webhook-handler',\n files: [\n {\n path: 'Webhooks/ZyphrWebhookVerifier.cs',\n purpose: 'Standard Webhooks signature verification helper',\n contents:\n 'using System.Security.Cryptography;\\n' +\n 'using System.Text;\\n\\n' +\n 'namespace YourApp.Webhooks;\\n\\n' +\n 'public static class ZyphrWebhookVerifier\\n' +\n '{\\n' +\n ' public static bool Verify(string payload, IDictionary<string,string> headers, string secret)\\n' +\n ' {\\n' +\n ' var msgId = headers.TryGetValue(\"webhook-id\", out var id) ? id : \"\";\\n' +\n ' var timestamp = headers.TryGetValue(\"webhook-timestamp\", out var ts) ? long.Parse(ts) : 0;\\n' +\n ' var signatures = headers.TryGetValue(\"webhook-signature\", out var sig) ? sig : \"\";\\n\\n' +\n ' var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();\\n' +\n ' if (Math.Abs(now - timestamp) > 300) return false;\\n\\n' +\n ' var signed = $\"{msgId}.{timestamp}.{payload}\";\\n' +\n ' var hex = secret.StartsWith(\"whsec_\") ? secret[6..] : secret;\\n' +\n ' var secretBytes = Convert.FromHexString(hex);\\n' +\n ' using var hmac = new HMACSHA256(secretBytes);\\n' +\n ' var expected = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(signed)));\\n\\n' +\n ' foreach (var s in signatures.Split(\\' \\'))\\n' +\n ' {\\n' +\n ' var v = s.Length > 3 ? s[3..] : \"\";\\n' +\n ' if (CryptographicOperations.FixedTimeEquals(Encoding.UTF8.GetBytes(v), Encoding.UTF8.GetBytes(expected)))\\n' +\n ' return true;\\n' +\n ' }\\n' +\n ' return false;\\n' +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n frameworks: {\n aspnetcore: {\n channel: 'webhook',\n language: 'csharp',\n framework: 'aspnetcore',\n variant: 'webhook-handler',\n files: [\n {\n path: 'Webhooks/ZyphrWebhookVerifier.cs',\n purpose: 'Standard Webhooks signature verification helper',\n contents:\n 'using System.Security.Cryptography;\\n' +\n 'using System.Text;\\n\\n' +\n 'namespace YourApp.Webhooks;\\n\\n' +\n 'public static class ZyphrWebhookVerifier\\n' +\n '{\\n' +\n ' public static bool Verify(string payload, IHeaderDictionary headers, string secret)\\n' +\n ' {\\n' +\n ' string msgId = headers[\"webhook-id\"].ToString();\\n' +\n ' long timestamp = long.TryParse(headers[\"webhook-timestamp\"], out var t) ? t : 0;\\n' +\n ' string signatures = headers[\"webhook-signature\"].ToString();\\n' +\n ' if (Math.Abs(DateTimeOffset.UtcNow.ToUnixTimeSeconds() - timestamp) > 300) return false;\\n' +\n ' var signed = $\"{msgId}.{timestamp}.{payload}\";\\n' +\n ' var hex = secret.StartsWith(\"whsec_\") ? secret[6..] : secret;\\n' +\n ' using var hmac = new HMACSHA256(Convert.FromHexString(hex));\\n' +\n ' var expected = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(signed)));\\n' +\n ' foreach (var s in signatures.Split(\\' \\'))\\n' +\n ' {\\n' +\n ' var v = s.Length > 3 ? s[3..] : \"\";\\n' +\n ' if (CryptographicOperations.FixedTimeEquals(Encoding.UTF8.GetBytes(v), Encoding.UTF8.GetBytes(expected)))\\n' +\n ' return true;\\n' +\n ' }\\n' +\n ' return false;\\n' +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n {\n path: 'Controllers/ZyphrWebhookController.cs',\n purpose: 'ASP.NET Core controller that verifies the webhook signature before processing',\n contents:\n 'using Microsoft.AspNetCore.Mvc;\\n' +\n 'using YourApp.Webhooks;\\n\\n' +\n '[ApiController]\\n' +\n '[Route(\"webhooks/zyphr\")]\\n' +\n 'public class ZyphrWebhookController : ControllerBase\\n' +\n '{\\n' +\n ' [HttpPost]\\n' +\n ' public async Task<IActionResult> Handle()\\n' +\n ' {\\n' +\n ' using var reader = new StreamReader(Request.Body);\\n' +\n ' var payload = await reader.ReadToEndAsync();\\n' +\n ' var secret = Environment.GetEnvironmentVariable(\"ZYPHR_WEBHOOK_SECRET\")!;\\n' +\n ' if (!ZyphrWebhookVerifier.Verify(payload, Request.Headers, secret))\\n' +\n ' return Unauthorized(\"invalid signature\");\\n' +\n ' Console.WriteLine($\"zyphr event {payload[..Math.Min(80, payload.Length)]}\");\\n' +\n ' return NoContent();\\n' +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n },\n};\n","import type { SdkLanguage, QuickstartChannel } from '../../schemas.js';\nimport type { QuickstartChannelMap, QuickstartResult } from '../quickstart-types.js';\nimport { emailChannel } from './email.js';\nimport { inboxChannel } from './inbox.js';\nimport { pushChannel } from './push.js';\nimport { smsChannel } from './sms.js';\nimport { webhookChannel } from './webhook.js';\n\nconst REGISTRY: Record<QuickstartChannel, QuickstartChannelMap> = {\n email: emailChannel,\n push: pushChannel,\n sms: smsChannel,\n inbox: inboxChannel,\n webhook: webhookChannel,\n};\n\nexport interface ResolveQuickstartArgs {\n channel: QuickstartChannel;\n language: SdkLanguage;\n framework?: string;\n}\n\nexport interface ResolveQuickstartResult {\n result: QuickstartResult;\n frameworkRecognized: boolean;\n}\n\nexport function resolveQuickstart(\n args: ResolveQuickstartArgs,\n): ResolveQuickstartResult | null {\n const langMap = REGISTRY[args.channel]?.[args.language];\n if (!langMap) return null;\n\n if (args.framework) {\n const key = args.framework.trim().toLowerCase();\n const fw = langMap.frameworks?.[key];\n if (fw) return { result: fw, frameworkRecognized: true };\n // unknown framework → fall back to plain SDK variant, signal recognition status\n return { result: langMap.sdk, frameworkRecognized: false };\n }\n\n return { result: langMap.sdk, frameworkRecognized: true };\n}\n\nexport const QUICKSTART_REGISTRY = REGISTRY;\n","// TODO(v0.3): auto-generate these snippets from packages/sdk/<lang>/examples/\n// so docs and MCP output never drift. For v0.2 they are hand-maintained and\n// must mirror apps/docs/docs/sdks/<lang>.md exactly.\nimport type { SdkLanguage } from '../schemas.js';\n\nexport interface InstallCommand {\n manager: string;\n command: string;\n}\n\nexport interface InitSnippet {\n imports: string;\n init: string;\n fileExample: string;\n}\n\nexport type SdkKind = 'sdk' | 'rest-client';\n\nexport interface SdkInstallEntry {\n language: SdkLanguage;\n kind: SdkKind;\n packageName: string;\n registry: string;\n registryUrl: string;\n installCommands: InstallCommand[];\n initSnippet: InitSnippet;\n envVarsNeeded: string[];\n docsUrl: string;\n notes?: string;\n}\n\nconst DOCS = 'https://docs.zyphr.dev/sdks';\n\nexport const SDK_INSTALL_TABLE: Record<SdkLanguage, SdkInstallEntry> = {\n node: {\n language: 'node',\n kind: 'sdk',\n packageName: '@zyphr-dev/node-sdk',\n registry: 'npm',\n registryUrl: 'https://www.npmjs.com/package/@zyphr-dev/node-sdk',\n installCommands: [\n { manager: 'npm', command: 'npm install @zyphr-dev/node-sdk' },\n { manager: 'yarn', command: 'yarn add @zyphr-dev/node-sdk' },\n { manager: 'pnpm', command: 'pnpm add @zyphr-dev/node-sdk' },\n ],\n initSnippet: {\n imports: \"import { Zyphr } from '@zyphr-dev/node-sdk';\",\n init: \"export const zyphr = new Zyphr({ apiKey: process.env.ZYPHR_API_KEY! });\",\n fileExample: 'src/lib/zyphr.ts',\n },\n envVarsNeeded: ['ZYPHR_API_KEY'],\n docsUrl: `${DOCS}/node`,\n },\n csharp: {\n language: 'csharp',\n kind: 'sdk',\n packageName: 'ZyphrDev.SDK',\n registry: 'NuGet',\n registryUrl: 'https://www.nuget.org/packages/ZyphrDev.SDK',\n installCommands: [\n { manager: 'dotnet', command: 'dotnet add package ZyphrDev.SDK' },\n { manager: 'nuget', command: 'Install-Package ZyphrDev.SDK' },\n ],\n initSnippet: {\n imports: [\n 'using ZyphrDev.SDK.Api;',\n 'using ZyphrDev.SDK.Client;',\n 'using ZyphrDev.SDK.Model;',\n ].join('\\n'),\n init: [\n 'var config = new Configuration',\n '{',\n ' ApiKey = new Dictionary<string, string>',\n ' {',\n ' { \"X-API-Key\", Environment.GetEnvironmentVariable(\"ZYPHR_API_KEY\")! }',\n ' }',\n '};',\n 'var emails = new EmailsApi(config);',\n ].join('\\n'),\n fileExample: 'Services/ZyphrClient.cs',\n },\n envVarsNeeded: ['ZYPHR_API_KEY'],\n docsUrl: `${DOCS}/csharp`,\n },\n ruby: {\n language: 'ruby',\n kind: 'sdk',\n packageName: 'zyphr',\n registry: 'RubyGems',\n registryUrl: 'https://rubygems.org/gems/zyphr',\n installCommands: [\n { manager: 'gem', command: 'gem install zyphr' },\n { manager: 'bundler', command: \"bundle add zyphr\" },\n ],\n initSnippet: {\n imports: \"require 'zyphr'\",\n init: [\n 'Zyphr.configure do |config|',\n \" config.api_key['X-API-Key'] = ENV.fetch('ZYPHR_API_KEY')\",\n 'end',\n ].join('\\n'),\n fileExample: 'config/initializers/zyphr.rb',\n },\n envVarsNeeded: ['ZYPHR_API_KEY'],\n docsUrl: `${DOCS}/ruby`,\n },\n python: {\n language: 'python',\n kind: 'rest-client',\n packageName: 'requests',\n registry: 'PyPI',\n registryUrl: 'https://pypi.org/project/requests/',\n installCommands: [\n { manager: 'pip', command: 'pip install requests' },\n { manager: 'poetry', command: 'poetry add requests' },\n { manager: 'uv', command: 'uv add requests' },\n ],\n initSnippet: {\n imports: 'import os\\nimport requests',\n init: [\n 'ZYPHR_API_KEY = os.environ[\"ZYPHR_API_KEY\"]',\n 'BASE_URL = \"https://api.zyphr.dev/v1\"',\n '',\n 'headers = {',\n ' \"X-API-Key\": ZYPHR_API_KEY,',\n ' \"Content-Type\": \"application/json\",',\n '}',\n '',\n 'def zyphr_request(method, path, json=None, params=None):',\n ' response = requests.request(',\n ' method, f\"{BASE_URL}{path}\", headers=headers, json=json, params=params,',\n ' )',\n ' response.raise_for_status()',\n ' return response.json()',\n ].join('\\n'),\n fileExample: 'app/zyphr_client.py',\n },\n envVarsNeeded: ['ZYPHR_API_KEY'],\n docsUrl: `${DOCS}/python`,\n notes:\n 'There is no official Zyphr Python SDK yet — the canonical integration is a thin REST wrapper around the requests library.',\n },\n go: {\n language: 'go',\n kind: 'rest-client',\n packageName: 'net/http (stdlib)',\n registry: 'stdlib',\n registryUrl: 'https://pkg.go.dev/net/http',\n installCommands: [\n { manager: 'go', command: '# No install needed — net/http ships with Go.' },\n ],\n initSnippet: {\n imports: [\n 'package zyphr',\n '',\n 'import (',\n '\\t\"bytes\"',\n '\\t\"encoding/json\"',\n '\\t\"fmt\"',\n '\\t\"io\"',\n '\\t\"net/http\"',\n '\\t\"os\"',\n ')',\n ].join('\\n'),\n init: [\n 'const baseURL = \"https://api.zyphr.dev/v1\"',\n '',\n 'type Client struct {',\n '\\tAPIKey string',\n '\\tHTTPClient *http.Client',\n '}',\n '',\n 'func NewClient() *Client {',\n '\\treturn &Client{APIKey: os.Getenv(\"ZYPHR_API_KEY\"), HTTPClient: &http.Client{}}',\n '}',\n '',\n 'func (c *Client) Do(method, path string, body any) ([]byte, error) {',\n '\\tvar buf io.Reader',\n '\\tif body != nil {',\n '\\t\\tb, err := json.Marshal(body)',\n '\\t\\tif err != nil { return nil, fmt.Errorf(\"marshal: %w\", err) }',\n '\\t\\tbuf = bytes.NewReader(b)',\n '\\t}',\n '\\treq, err := http.NewRequest(method, baseURL+path, buf)',\n '\\tif err != nil { return nil, err }',\n '\\treq.Header.Set(\"X-API-Key\", c.APIKey)',\n '\\treq.Header.Set(\"Content-Type\", \"application/json\")',\n '\\tresp, err := c.HTTPClient.Do(req)',\n '\\tif err != nil { return nil, err }',\n '\\tdefer resp.Body.Close()',\n '\\tdata, _ := io.ReadAll(resp.Body)',\n '\\tif resp.StatusCode >= 400 { return nil, fmt.Errorf(\"zyphr %d: %s\", resp.StatusCode, data) }',\n '\\treturn data, nil',\n '}',\n ].join('\\n'),\n fileExample: 'internal/zyphr/client.go',\n },\n envVarsNeeded: ['ZYPHR_API_KEY'],\n docsUrl: `${DOCS}/go`,\n notes:\n 'There is no official Zyphr Go SDK yet — the canonical integration is a thin REST wrapper around net/http.',\n },\n php: {\n language: 'php',\n kind: 'rest-client',\n packageName: 'guzzlehttp/guzzle',\n registry: 'Packagist',\n registryUrl: 'https://packagist.org/packages/guzzlehttp/guzzle',\n installCommands: [\n { manager: 'composer', command: 'composer require guzzlehttp/guzzle' },\n ],\n initSnippet: {\n imports: [\n '<?php',\n '',\n 'use GuzzleHttp\\\\Client;',\n ].join('\\n'),\n init: [\n '$client = new Client([',\n \" 'base_uri' => 'https://api.zyphr.dev/v1/',\",\n \" 'headers' => [\",\n \" 'X-API-Key' => getenv('ZYPHR_API_KEY'),\",\n \" 'Content-Type' => 'application/json',\",\n ' ],',\n ']);',\n ].join('\\n'),\n fileExample: 'app/Services/ZyphrClient.php',\n },\n envVarsNeeded: ['ZYPHR_API_KEY'],\n docsUrl: `${DOCS}/php`,\n notes:\n 'There is no official Zyphr PHP SDK yet — the canonical integration uses Guzzle (or raw cURL).',\n },\n};\n\nexport function resolveInstallEntry(\n language: SdkLanguage,\n packageManager?: string,\n): SdkInstallEntry {\n const entry = SDK_INSTALL_TABLE[language];\n if (!packageManager) return entry;\n\n const normalized = packageManager.trim().toLowerCase();\n const match = entry.installCommands.find((c) => c.manager.toLowerCase() === normalized);\n if (!match) return entry;\n\n return { ...entry, installCommands: [match] };\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { isToolEnabled, type ToolGuards } from '../config.js';\nimport { resolveQuickstart } from '../integration/quickstart/index.js';\nimport { resolveInstallEntry } from '../integration/sdk-snippets.js';\nimport { toolResult } from '../result.js';\nimport { getQuickstartShape, getSdkInstallShape } from '../schemas.js';\n\nexport function registerIntegrationTools(server: McpServer, guards: ToolGuards): void {\n if (isToolEnabled({ name: 'get_sdk_install_for_language', mutates: false }, guards)) {\n server.registerTool(\n 'get_sdk_install_for_language',\n {\n title: 'Get SDK install instructions for a language',\n description:\n 'Returns install commands, init snippet, env vars, and docs URL for the chosen language. Use this when wiring Zyphr into a new project so the AI can drop the correct package + client init code.',\n inputSchema: getSdkInstallShape,\n },\n async (args) => {\n const entry = resolveInstallEntry(args.language, args.packageManager);\n return toolResult(entry);\n },\n );\n }\n\n if (isToolEnabled({ name: 'get_quickstart_for_channel', mutates: false }, guards)) {\n server.registerTool(\n 'get_quickstart_for_channel',\n {\n title: 'Get quickstart code for a channel + language',\n description:\n 'Returns drop-in service file(s) for the chosen Zyphr channel (email/push/sms/inbox/webhook), language, and optional framework (express, nextjs, flask, fastapi, rails, laravel, aspnetcore). Webhook handlers ALWAYS verify HMAC signatures. Unknown frameworks fall back to plain SDK code.',\n inputSchema: getQuickstartShape,\n },\n async (args) => {\n const resolved = resolveQuickstart({\n channel: args.channel,\n language: args.language,\n framework: args.framework,\n });\n if (!resolved) {\n return toolResult({\n error: `No quickstart available for channel=${args.channel} language=${args.language}`,\n });\n }\n const { result, frameworkRecognized } = resolved;\n return toolResult({\n ...result,\n frameworkRecognized,\n requestedFramework: args.framework ?? null,\n });\n },\n );\n }\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { getZyphrClient } from '../client.js';\nimport { isToolEnabled, type ToolGuards } from '../config.js';\nimport { runTool } from '../result.js';\nimport {\n createWebhookShape,\n getWebhookDeliveriesShape,\n listWebhooksShape,\n} from '../schemas.js';\n\nexport function registerWebhookTools(server: McpServer, guards: ToolGuards): void {\n if (isToolEnabled({ name: 'list_webhooks', mutates: false }, guards)) {\n server.registerTool(\n 'list_webhooks',\n {\n title: 'List webhooks',\n description: 'List webhook endpoints configured for the account.',\n inputSchema: listWebhooksShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.webhooks.listWebhooks(args.limit, args.offset);\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'create_webhook', mutates: true }, guards)) {\n server.registerTool(\n 'create_webhook',\n {\n title: 'Create webhook',\n description:\n 'Register a new webhook endpoint. Subscribe to event types like \"email.*\", \"subscriber.created\", or \"*\".',\n inputSchema: createWebhookShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.webhooks.createWebhook({\n url: args.url,\n events: args.events,\n description: args.description,\n secret: args.secret,\n metadata: args.metadata,\n headers: args.headers,\n version: args.version,\n rateLimit: args.rateLimit,\n });\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'get_webhook_deliveries', mutates: false }, guards)) {\n server.registerTool(\n 'get_webhook_deliveries',\n {\n title: 'List webhook deliveries',\n description:\n 'Inspect delivery history for a webhook endpoint. Filter by status (pending/delivering/delivered/failed/exhausted), event type, or date range. Useful for debugging why a webhook is not firing.',\n inputSchema: getWebhookDeliveriesShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.webhooks.listWebhookDeliveries(\n args.webhookId,\n args.status,\n args.eventType,\n args.search,\n args.startDate ? new Date(args.startDate) : undefined,\n args.endDate ? new Date(args.endDate) : undefined,\n args.limit,\n args.offset,\n );\n });\n },\n );\n }\n}\n"],"mappings":"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","names":["DOCS","ENV","NEXT_STEPS","DOCS","ENV","NEXT_STEPS","DOCS","ENV","NEXT_STEPS","DOCS","ENV","NEXT_STEPS","DOCS"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/server.ts","../src/config.ts","../src/client.ts","../src/result.ts","../src/schemas.ts","../src/tools/domains.ts","../src/tools/organizations.ts","../src/tools/send.ts","../src/tools/subscribers.ts","../src/tools/templates.ts","../src/integration/quickstart/email.ts","../src/integration/quickstart/inbox.ts","../src/integration/quickstart/push.ts","../src/integration/quickstart/sms.ts","../src/integration/quickstart/webhook.ts","../src/integration/quickstart/index.ts","../src/integration/sdk-snippets.ts","../src/tools/integration.ts","../src/tools/webhooks.ts"],"sourcesContent":["import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { createServer } from './server.js';\nimport { getBaseUrl } from './client.js';\n\nasync function main(): Promise<void> {\n // Touch the env early so misconfiguration fails fast with a clear message.\n if (!process.env.ZYPHR_API_KEY) {\n process.stderr.write(\n '[zyphr-mcp] ZYPHR_API_KEY is not set. Provide a zy_live_* or zy_test_* key via the `env` block of your MCP client config.\\n',\n );\n process.exit(1);\n }\n\n const server = createServer();\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n process.stderr.write(`[zyphr-mcp] connected (base=${getBaseUrl()})\\n`);\n}\n\nmain().catch((err) => {\n process.stderr.write(`[zyphr-mcp] fatal: ${err instanceof Error ? err.stack ?? err.message : String(err)}\\n`);\n process.exit(1);\n});\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { loadToolGuards } from './config.js';\nimport { registerDomainTools } from './tools/domains.js';\nimport { registerOrganizationTools } from './tools/organizations.js';\nimport { registerSendTools } from './tools/send.js';\nimport { registerSubscriberTools } from './tools/subscribers.js';\nimport { registerTemplateTools } from './tools/templates.js';\nimport { registerIntegrationTools } from './tools/integration.js';\nimport { registerWebhookTools } from './tools/webhooks.js';\n\nexport const SERVER_NAME = 'zyphr';\nexport const SERVER_VERSION = '0.2.0';\n\nexport function createServer(): McpServer {\n const server = new McpServer(\n { name: SERVER_NAME, version: SERVER_VERSION },\n { capabilities: { tools: {} } },\n );\n\n const guards = loadToolGuards();\n registerSendTools(server, guards);\n registerTemplateTools(server, guards);\n registerSubscriberTools(server, guards);\n registerWebhookTools(server, guards);\n registerDomainTools(server, guards);\n registerIntegrationTools(server, guards);\n registerOrganizationTools(server, guards);\n\n return server;\n}\n","export interface ToolGuards {\n readOnly: boolean;\n allowedTools: Set<string> | null;\n}\n\nexport function loadToolGuards(): ToolGuards {\n const readOnly = process.env.ZYPHR_READ_ONLY === 'true' || process.env.ZYPHR_READ_ONLY === '1';\n const raw = process.env.ZYPHR_ALLOWED_TOOLS;\n const allowedTools = raw\n ? new Set(\n raw\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean),\n )\n : null;\n return { readOnly, allowedTools };\n}\n\nexport interface ToolDefinition {\n name: string;\n mutates: boolean;\n}\n\nexport function isToolEnabled(tool: ToolDefinition, guards: ToolGuards): boolean {\n if (guards.allowedTools) {\n return guards.allowedTools.has(tool.name);\n }\n if (guards.readOnly && tool.mutates) {\n return false;\n }\n return true;\n}\n","import { Zyphr } from '@zyphr-dev/node-sdk';\n\nconst DEFAULT_BASE_URL = 'https://api.zyphr.dev/v1';\n\nlet cached: Zyphr | undefined;\n\nexport function getZyphrClient(): Zyphr {\n if (cached) return cached;\n\n const apiKey = process.env.ZYPHR_API_KEY;\n if (!apiKey) {\n process.stderr.write(\n '[zyphr-mcp] ZYPHR_API_KEY is not set. Provide a zy_live_* or zy_test_* key via the `env` block of your MCP client config.\\n',\n );\n process.exit(1);\n }\n\n const baseUrl = process.env.ZYPHR_BASE_URL || DEFAULT_BASE_URL;\n cached = new Zyphr({ apiKey, baseUrl });\n return cached;\n}\n\nexport function getBaseUrl(): string {\n return process.env.ZYPHR_BASE_URL || DEFAULT_BASE_URL;\n}\n","import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { ZyphrError, ZyphrRateLimitError } from '@zyphr-dev/node-sdk';\n\nexport function toolResult(data: unknown): CallToolResult {\n return {\n content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],\n };\n}\n\nexport async function runTool(fn: () => Promise<unknown>): Promise<CallToolResult> {\n try {\n const data = await fn();\n return toolResult(data);\n } catch (err: unknown) {\n return await renderApiError(err);\n }\n}\n\nfunction errorPayload(content: Record<string, unknown>): CallToolResult {\n return {\n isError: true,\n content: [{ type: 'text', text: JSON.stringify(content, null, 2) }],\n };\n}\n\nasync function renderApiError(err: unknown): Promise<CallToolResult> {\n // Typed SDK errors — surface the full envelope so the AI can act on it.\n if (err instanceof ZyphrError) {\n const payload: Record<string, unknown> = {\n name: err.name,\n message: err.message,\n status: err.status,\n };\n if (err.code) payload.code = err.code;\n if (err.requestId) payload.requestId = err.requestId;\n if (err.details) payload.details = err.details;\n if (err instanceof ZyphrRateLimitError && err.retryAfter !== undefined) {\n payload.retryAfter = err.retryAfter;\n }\n return errorPayload(payload);\n }\n\n // Raw Response error (rare — SDK middleware usually parses first).\n if (err && typeof err === 'object' && 'response' in err) {\n const response = (err as { response?: Response }).response;\n if (response && typeof response.text === 'function') {\n try {\n const text = await response.text();\n let parsed: unknown = text;\n try {\n parsed = JSON.parse(text);\n } catch {\n /* keep raw text */\n }\n return errorPayload({ status: response.status, body: parsed });\n } catch {\n /* fall through to generic message */\n }\n }\n }\n\n const message = err instanceof Error ? err.message : String(err);\n const name = err instanceof Error ? err.name : 'Error';\n return errorPayload({ name, message });\n}\n","import { z } from 'zod';\n\nconst emailAddress = z.object({\n email: z.string().email(),\n name: z.string().optional(),\n});\n\nconst recipient = z.union([z.string().email(), emailAddress]);\n\nexport const sendEmailShape = {\n to: z\n .union([recipient, z.array(recipient).min(1)])\n .describe('Recipient email address (string or {email,name}) or an array of them'),\n from: z\n .union([z.string().email(), emailAddress])\n .optional()\n .describe('Sender address. Defaults to the account-level \"from\" address.'),\n replyTo: z.union([z.string().email(), emailAddress]).optional(),\n cc: z.array(z.string().email()).optional(),\n bcc: z.array(z.string().email()).optional(),\n subject: z.string().min(1),\n html: z.string().optional().describe('Rendered HTML body. Mutually exclusive with templateId.'),\n text: z.string().optional().describe('Plain-text body. Mutually exclusive with templateId.'),\n templateId: z.string().optional().describe('Template ID. When set, html/text are ignored.'),\n templateData: z\n .record(z.unknown())\n .optional()\n .describe('Variables to interpolate into the template'),\n tags: z.array(z.string()).optional(),\n metadata: z.record(z.unknown()).optional(),\n subscriberId: z.string().optional(),\n category: z.string().optional(),\n scheduledAt: z\n .string()\n .datetime()\n .optional()\n .describe('ISO 8601 timestamp for scheduled delivery'),\n} as const;\n\nexport const sendPushShape = {\n userId: z.string().optional().describe('Send to all devices for this user/subscriber'),\n deviceId: z.string().optional().describe('Send to a specific device only'),\n title: z.string().optional(),\n body: z.string().optional(),\n data: z.record(z.unknown()).optional().describe('Custom data payload delivered to the device'),\n badge: z.number().int().nonnegative().optional(),\n sound: z.string().optional(),\n imageUrl: z.string().url().optional(),\n contentAvailable: z.boolean().optional().describe('Silent/background push'),\n tags: z.array(z.string()).optional(),\n metadata: z.record(z.unknown()).optional(),\n collapseKey: z.string().optional(),\n subscriberId: z.string().optional(),\n subscriberExternalId: z.string().optional(),\n category: z.string().optional(),\n force: z.boolean().optional().describe('Skip subscriber preference checks'),\n sendAt: z.string().datetime().optional(),\n delay: z.number().int().nonnegative().optional(),\n} as const;\n\nexport const sendSmsShape = {\n to: z.string().min(1).describe('Recipient phone number in E.164 format (e.g. +14155551234)'),\n from: z.string().optional().describe('Sender phone number or sender ID'),\n body: z.string().min(1),\n subscriberId: z.string().optional(),\n scheduledAt: z.string().datetime().optional(),\n metadata: z.record(z.unknown()).optional(),\n} as const;\n\n// Integration\n\nexport const sdkLanguages = ['node', 'python', 'ruby', 'go', 'php', 'csharp'] as const;\nexport type SdkLanguage = (typeof sdkLanguages)[number];\n\nexport const quickstartChannels = ['email', 'push', 'sms', 'inbox', 'webhook'] as const;\nexport type QuickstartChannel = (typeof quickstartChannels)[number];\n\nexport const getQuickstartShape = {\n channel: z\n .enum(quickstartChannels)\n .describe('Which Zyphr channel to wire up'),\n language: z\n .enum(['node', 'python', 'ruby', 'go', 'php', 'csharp'])\n .describe('Target language for the integration'),\n framework: z\n .string()\n .optional()\n .describe(\n 'Optional framework hint (e.g. \"express\", \"nextjs\", \"flask\", \"fastapi\", \"rails\", \"gin\", \"laravel\", \"aspnetcore\"). Falls back to plain SDK code when unrecognized.',\n ),\n} as const;\n\nexport const getSdkInstallShape = {\n language: z\n .enum(sdkLanguages)\n .describe('Target language for the integration'),\n packageManager: z\n .string()\n .optional()\n .describe(\n 'Optional package manager override (e.g. \"yarn\" instead of \"npm\"). When recognized, only that manager is returned; otherwise the full list is returned.',\n ),\n} as const;\n\n// Templates\n\nexport const listTemplatesShape = {\n limit: z.number().int().positive().max(200).optional(),\n offset: z.number().int().nonnegative().optional(),\n} as const;\n\nexport const getTemplateShape = {\n id: z.string().min(1).describe('Template ID'),\n} as const;\n\nexport const renderTemplateShape = {\n id: z.string().min(1).describe('Template ID'),\n variables: z\n .record(z.unknown())\n .describe('Key/value variables to interpolate into the template'),\n} as const;\n\nexport const createTemplateShape = {\n name: z.string().min(1),\n description: z.string().optional(),\n subject: z.string().optional().describe('Default subject (email templates)'),\n html: z.string().optional(),\n text: z.string().optional(),\n} as const;\n\n// Subscribers\n\nexport const findSubscriberShape = {\n externalId: z\n .string()\n .min(1)\n .describe('Subscriber external ID (your application user/customer ID)'),\n} as const;\n\nexport const listSubscribersShape = {\n status: z.enum(['active', 'inactive']).optional(),\n email: z.string().email().optional(),\n limit: z.number().int().positive().max(200).optional(),\n offset: z.number().int().nonnegative().optional(),\n} as const;\n\nexport const createSubscriberShape = {\n externalId: z.string().min(1).describe('Your application user/customer ID'),\n email: z.string().email().optional(),\n phone: z.string().optional(),\n name: z.string().optional(),\n avatarUrl: z.string().url().optional(),\n timezone: z.string().optional(),\n locale: z.string().optional(),\n metadata: z.record(z.unknown()).optional(),\n} as const;\n\nexport const updateSubscriberShape = {\n id: z.string().min(1).describe('Zyphr subscriber ID'),\n email: z.string().email().nullable().optional(),\n phone: z.string().nullable().optional(),\n name: z.string().nullable().optional(),\n avatarUrl: z.string().url().nullable().optional(),\n timezone: z.string().optional(),\n locale: z.string().optional(),\n metadata: z.record(z.unknown()).optional(),\n status: z.enum(['active', 'inactive']).optional(),\n} as const;\n\nexport const setSubscriberPreferencesShape = {\n id: z.string().min(1).describe('Zyphr subscriber ID'),\n preferences: z\n .array(\n z.object({\n categoryId: z.string().optional(),\n channel: z.string().optional().describe('email | push | sms | in_app'),\n enabled: z.boolean().optional(),\n }),\n )\n .min(1),\n} as const;\n\n// Webhooks\n\nexport const listWebhooksShape = {\n limit: z.number().int().positive().max(200).optional(),\n offset: z.number().int().nonnegative().optional(),\n} as const;\n\nexport const createWebhookShape = {\n url: z.string().url().describe('Receiver URL that Zyphr will POST events to'),\n events: z\n .array(z.string().min(1))\n .min(1)\n .describe('Event types to subscribe to (e.g. [\"email.*\", \"subscriber.created\"])'),\n description: z.string().optional(),\n secret: z\n .string()\n .optional()\n .describe('Optional secret used to sign payloads. If omitted, Zyphr generates one.'),\n metadata: z.record(z.unknown()).optional(),\n headers: z.record(z.string()).optional().describe('Custom headers to send with every delivery'),\n version: z.string().optional(),\n rateLimit: z.number().int().positive().optional(),\n} as const;\n\nexport const getWebhookDeliveriesShape = {\n webhookId: z.string().min(1).describe('Webhook endpoint ID'),\n status: z.enum(['pending', 'delivering', 'delivered', 'failed', 'exhausted']).optional(),\n eventType: z.string().optional(),\n search: z.string().optional(),\n startDate: z.string().datetime().optional(),\n endDate: z.string().datetime().optional(),\n limit: z.number().int().positive().max(200).optional(),\n offset: z.number().int().nonnegative().optional(),\n} as const;\n\n// Domains (sc-5443)\n//\n// NOTE on what's intentionally absent: there is NO `deleteDomainShape` here\n// and there is no `delete_domain` tool registered anywhere in the MCP server.\n// `@zyphr-dev/mcp-server` does not expose destructive `delete_*` operations\n// by policy (epic 5306). Customers who need scripted domain teardown should\n// call `zyphr.domains.deleteDomain(id)` from the Node SDK directly.\n\nexport const addDomainShape = {\n domain: z\n .string()\n .min(1)\n .describe(\n 'The sending domain to add (e.g. \"mail.example.com\"). Will be lowercased and trimmed. Idempotent — calling with an already-registered domain returns the existing record.',\n ),\n} as const;\n\nexport const listDomainsShape = {} as const;\n\nexport const getDomainShape = {\n id: z.string().min(1).describe('Domain ID (UUID)'),\n} as const;\n\nexport const verifyDomainShape = {\n id: z\n .string()\n .min(1)\n .describe(\n 'Domain ID (UUID). Queues an asynchronous DNS verification check; poll get_domain (or subscribe to the domain.verified webhook) for terminal status.',\n ),\n} as const;\n\n// Auth Organizations (sc-5587 / epic-5587)\n//\n// NOTE on what's intentionally absent: there is NO `deleteOrganizationShape`\n// here and no `delete_organization` tool registered. Per MCP policy\n// (epic 5306) the server does not expose destructive `delete_*` operations.\n// Customers needing scripted org teardown call\n// `zyphr.auth.organizations.deleteOrganization(id)` from the Node SDK.\n//\n// Organizations are env-scoped Auth sub-tenants. Each end_user belongs to\n// many orgs within an environment; the active `org_id` rides on the JWT.\n\nexport const listOrganizationsShape = {\n limit: z.number().int().positive().max(200).optional(),\n cursor: z.string().optional().describe('Pagination cursor from a prior listOrganizations response.'),\n search: z.string().optional().describe('Optional fuzzy match on org name/slug.'),\n} as const;\n\nexport const getOrganizationShape = {\n id: z.string().min(1).describe('Organization ID (UUID).'),\n} as const;\n\nexport const createOrganizationShape = {\n name: z.string().min(1).max(255).describe('Display name. Required.'),\n slug: z\n .string()\n .min(1)\n .max(120)\n .optional()\n .describe('Optional slug. If present must be unique within the environment.'),\n metadata: z.record(z.unknown()).optional().describe('Arbitrary JSON metadata.'),\n} as const;\n\nexport const updateOrganizationShape = {\n id: z.string().min(1).describe('Organization ID (UUID).'),\n name: z.string().min(1).max(255).optional(),\n slug: z.string().min(1).max(120).optional(),\n metadata: z.record(z.unknown()).optional(),\n} as const;\n\nexport const listOrganizationMembersShape = {\n id: z.string().min(1).describe('Organization ID (UUID).'),\n limit: z.number().int().positive().max(200).optional(),\n cursor: z.string().optional(),\n} as const;\n\nexport const addOrganizationMemberShape = {\n id: z.string().min(1).describe('Organization ID (UUID).'),\n userId: z.string().min(1).describe('End-user ID (UUID) to add to the org.'),\n role: z\n .string()\n .min(1)\n .max(80)\n .optional()\n .describe(\n 'Customer-defined role label (opaque to Zyphr — store whatever your taxonomy uses).',\n ),\n} as const;\n\nexport const updateOrganizationMemberRoleShape = {\n id: z.string().min(1).describe('Organization ID (UUID).'),\n userId: z.string().min(1).describe('End-user ID (UUID).'),\n role: z\n .string()\n .min(1)\n .max(80)\n .describe('New role label. Opaque to Zyphr.'),\n} as const;\n\nexport const removeOrganizationMemberShape = {\n id: z.string().min(1).describe('Organization ID (UUID).'),\n userId: z.string().min(1).describe('End-user ID (UUID) to remove.'),\n} as const;\n\nexport const listOrganizationsForUserShape = {\n userId: z.string().min(1).describe('End-user ID (UUID).'),\n} as const;\n\nexport const sendInboxMessageShape = {\n subscriberId: z.string().min(1).describe('Subscriber to deliver the in-app message to'),\n title: z.string().min(1),\n body: z.string().optional(),\n actionUrl: z.string().url().optional(),\n actionLabel: z.string().optional(),\n imageUrl: z.string().url().optional(),\n icon: z.string().optional(),\n category: z.string().optional(),\n priority: z.enum(['low', 'normal', 'high', 'urgent']).optional(),\n data: z.record(z.unknown()).optional(),\n tags: z.array(z.string()).optional(),\n expiresAt: z.string().datetime().optional(),\n} as const;\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { getZyphrClient } from '../client.js';\nimport { isToolEnabled, type ToolGuards } from '../config.js';\nimport { runTool } from '../result.js';\nimport {\n addDomainShape,\n getDomainShape,\n listDomainsShape,\n verifyDomainShape,\n} from '../schemas.js';\n\n/**\n * Domain-management MCP tools (sc-5443).\n *\n * Surface lets an AI agent complete the full domain-onboarding loop without\n * touching the dashboard: add a sending domain, get back the DNS records to\n * publish (already tagged with `purpose` per record), poll verification, then\n * `send_email` from that domain once `status === 'verified'`.\n *\n * NOT registered (by policy): `delete_domain`. Destructive operations are\n * intentionally absent from `@zyphr-dev/mcp-server` — one bad model call could\n * detach an SES identity, invalidate scheduled sends, and break templates\n * hardcoded to a sender. Customers who need scripted teardown call\n * `zyphr.domains.deleteDomain(id)` from the Node SDK directly.\n */\nexport function registerDomainTools(server: McpServer, guards: ToolGuards): void {\n if (isToolEnabled({ name: 'add_domain', mutates: true }, guards)) {\n server.registerTool(\n 'add_domain',\n {\n title: 'Add a sending domain',\n description:\n 'Register a new sending domain on the account. Returns the full set of DNS records (TXT + CNAME) the AI agent should publish to its DNS provider (Route53, Cloudflare, etc.) before email can send from this domain. Idempotent: calling with an already-registered domain returns the existing record.',\n inputSchema: addDomainShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.domains.addDomain({ domain: args.domain });\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'list_domains', mutates: false }, guards)) {\n server.registerTool(\n 'list_domains',\n {\n title: 'List sending domains',\n description:\n 'List sending domains on the current project, with verification status and DNS records.',\n inputSchema: listDomainsShape,\n },\n async () => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.domains.listDomains();\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'get_domain', mutates: false }, guards)) {\n server.registerTool(\n 'get_domain',\n {\n title: 'Get a sending domain',\n description:\n 'Retrieve a single sending domain by ID, including per-record `verified` flags and `records_verified` / `records_total` counters. Useful for reasoning about partial verification (e.g. \"SPF verified, DKIM #2 still pending\").',\n inputSchema: getDomainShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.domains.getDomain(args.id);\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'verify_domain', mutates: true }, guards)) {\n server.registerTool(\n 'verify_domain',\n {\n title: 'Trigger domain verification',\n description:\n 'Queue an asynchronous DNS verification check for a sending domain. Returns a job_id immediately; poll `get_domain` (or subscribe to the `domain.verified` webhook event) for terminal status. Returns a 409 error if a verification is already in progress for this domain.',\n inputSchema: verifyDomainShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.domains.verifyDomain(args.id);\n });\n },\n );\n }\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { getZyphrClient } from '../client.js';\nimport { isToolEnabled, type ToolGuards } from '../config.js';\nimport { runTool } from '../result.js';\nimport {\n addOrganizationMemberShape,\n createOrganizationShape,\n getOrganizationShape,\n listOrganizationMembersShape,\n listOrganizationsForUserShape,\n listOrganizationsShape,\n removeOrganizationMemberShape,\n updateOrganizationMemberRoleShape,\n updateOrganizationShape,\n} from '../schemas.js';\n\n// Auth Organizations (sc-5587 / epic-5587)\n//\n// Wraps `zyphr.auth.organizations.*` for AI clients. Organizations are\n// env-scoped Auth sub-tenants — each end_user belongs to many orgs within\n// an environment; the active `org_id` rides on the JWT.\n//\n// Read tools are always enabled; mutating tools are gated by ZYPHR_READ_ONLY\n// and ZYPHR_ALLOWED_TOOLS via `isToolEnabled`. No `delete_organization` tool\n// is exposed by policy (epic 5306) — destructive ops stay on the dashboard.\n\nexport function registerOrganizationTools(server: McpServer, guards: ToolGuards): void {\n if (isToolEnabled({ name: 'list_organizations', mutates: false }, guards)) {\n server.registerTool(\n 'list_organizations',\n {\n title: 'List organizations',\n description:\n 'List organizations in the current environment. Cursor-paginated; pass the `cursor` from the prior response to walk pages.',\n inputSchema: listOrganizationsShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.auth.organizations.listOrganizations(\n args.limit,\n args.cursor,\n args.search,\n );\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'get_organization', mutates: false }, guards)) {\n server.registerTool(\n 'get_organization',\n {\n title: 'Get organization',\n description: 'Fetch a single organization by ID.',\n inputSchema: getOrganizationShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.auth.organizations.getOrganization(args.id);\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'create_organization', mutates: true }, guards)) {\n server.registerTool(\n 'create_organization',\n {\n title: 'Create organization',\n description:\n 'Create a new organization in the current environment. Requires Pro / Scale / Enterprise; Free and Starter accounts receive 403 plan_upgrade_required.',\n inputSchema: createOrganizationShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.auth.organizations.createOrganization({\n name: args.name,\n slug: args.slug,\n metadata: args.metadata,\n });\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'update_organization', mutates: true }, guards)) {\n server.registerTool(\n 'update_organization',\n {\n title: 'Update organization',\n description: 'Update an organization’s name, slug, or metadata. Cannot rename the Default org.',\n inputSchema: updateOrganizationShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.auth.organizations.updateOrganization(args.id, {\n name: args.name,\n slug: args.slug,\n metadata: args.metadata,\n });\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'list_organization_members', mutates: false }, guards)) {\n server.registerTool(\n 'list_organization_members',\n {\n title: 'List organization members',\n description: 'List end-user memberships in an organization. Cursor-paginated.',\n inputSchema: listOrganizationMembersShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.auth.organizations.listOrganizationMembers(\n args.id,\n args.limit,\n args.cursor,\n );\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'add_organization_member', mutates: true }, guards)) {\n server.registerTool(\n 'add_organization_member',\n {\n title: 'Add organization member',\n description:\n 'Add an end_user to an organization. Adding to a non-Default org requires Pro / Scale / Enterprise; gated calls return 403 plan_upgrade_required.',\n inputSchema: addOrganizationMemberShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.auth.organizations.addOrganizationMember(args.id, {\n userId: args.userId,\n role: args.role,\n });\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'update_organization_member_role', mutates: true }, guards)) {\n server.registerTool(\n 'update_organization_member_role',\n {\n title: 'Update organization member role',\n description:\n 'Update an existing membership’s role label. Role is an opaque customer-defined string — Zyphr does not interpret values.',\n inputSchema: updateOrganizationMemberRoleShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.auth.organizations.updateOrganizationMemberRole(\n args.id,\n args.userId,\n { role: args.role },\n );\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'remove_organization_member', mutates: true }, guards)) {\n server.registerTool(\n 'remove_organization_member',\n {\n title: 'Remove organization member',\n description:\n 'Remove an end_user from an organization. Returns 204 on success. Cannot remove the last member of the Default org.',\n inputSchema: removeOrganizationMemberShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n await zyphr.auth.organizations.removeOrganizationMember(args.id, args.userId);\n return { ok: true };\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'list_organizations_for_user', mutates: false }, guards)) {\n server.registerTool(\n 'list_organizations_for_user',\n {\n title: 'List organizations for end-user',\n description:\n 'List every organization an end_user belongs to within the environment. Useful when the JWT `orgs[]` claim is capped at 50 and you need the full set.',\n inputSchema: listOrganizationsForUserShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.auth.organizations.listOrganizationsForEndUser(args.userId);\n });\n },\n );\n }\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { getZyphrClient } from '../client.js';\nimport { isToolEnabled, type ToolGuards } from '../config.js';\nimport { runTool } from '../result.js';\nimport {\n sendEmailShape,\n sendInboxMessageShape,\n sendPushShape,\n sendSmsShape,\n} from '../schemas.js';\n\nfunction normalizeEmailAddress(value: unknown): { email: string; name?: string } | undefined {\n if (typeof value === 'string') return { email: value };\n if (value && typeof value === 'object' && 'email' in value) {\n return value as { email: string; name?: string };\n }\n return undefined;\n}\n\nfunction normalizeRecipients(to: unknown): { email: string; name?: string }[] {\n if (Array.isArray(to)) {\n return to.map(normalizeEmailAddress).filter((v): v is { email: string } => Boolean(v));\n }\n const one = normalizeEmailAddress(to);\n return one ? [one] : [];\n}\n\nexport function registerSendTools(server: McpServer, guards: ToolGuards): void {\n if (isToolEnabled({ name: 'send_email', mutates: true }, guards)) {\n server.registerTool(\n 'send_email',\n {\n title: 'Send email',\n description:\n 'Send a transactional email via Zyphr. Use either html/text OR templateId+templateData, not both.',\n inputSchema: sendEmailShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.emails.sendEmail({\n to: normalizeRecipients(args.to),\n from: normalizeEmailAddress(args.from),\n replyTo: normalizeEmailAddress(args.replyTo),\n cc: args.cc,\n bcc: args.bcc,\n subject: args.subject,\n html: args.html,\n text: args.text,\n templateId: args.templateId,\n templateData: args.templateData,\n tags: args.tags,\n metadata: args.metadata,\n subscriberId: args.subscriberId,\n category: args.category,\n scheduledAt: args.scheduledAt ? new Date(args.scheduledAt) : undefined,\n });\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'send_push', mutates: true }, guards)) {\n server.registerTool(\n 'send_push',\n {\n title: 'Send push notification',\n description:\n 'Send a push notification. Target one of: userId (all devices for a user), deviceId (specific device), subscriberId, or subscriberExternalId.',\n inputSchema: sendPushShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.push.sendPush({\n userId: args.userId,\n deviceId: args.deviceId,\n title: args.title,\n body: args.body,\n data: args.data,\n badge: args.badge,\n sound: args.sound,\n imageUrl: args.imageUrl,\n contentAvailable: args.contentAvailable,\n tags: args.tags,\n metadata: args.metadata,\n collapseKey: args.collapseKey,\n subscriberId: args.subscriberId,\n subscriberExternalId: args.subscriberExternalId,\n category: args.category,\n force: args.force,\n sendAt: args.sendAt ? new Date(args.sendAt) : undefined,\n delay: args.delay,\n });\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'send_sms', mutates: true }, guards)) {\n server.registerTool(\n 'send_sms',\n {\n title: 'Send SMS',\n description: 'Send an SMS message via Zyphr. The recipient must be in E.164 format.',\n inputSchema: sendSmsShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.sms.sendSms({\n to: args.to,\n from: args.from,\n body: args.body,\n subscriberId: args.subscriberId,\n scheduledAt: args.scheduledAt ? new Date(args.scheduledAt) : undefined,\n metadata: args.metadata,\n });\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'send_inbox_message', mutates: true }, guards)) {\n server.registerTool(\n 'send_inbox_message',\n {\n title: 'Send in-app inbox message',\n description: 'Deliver an in-app inbox notification to a Zyphr subscriber.',\n inputSchema: sendInboxMessageShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.inbox.sendInApp({\n subscriberId: args.subscriberId,\n title: args.title,\n body: args.body,\n actionUrl: args.actionUrl,\n actionLabel: args.actionLabel,\n imageUrl: args.imageUrl,\n icon: args.icon,\n category: args.category,\n priority: args.priority,\n data: args.data,\n tags: args.tags,\n expiresAt: args.expiresAt ? new Date(args.expiresAt) : undefined,\n });\n });\n },\n );\n }\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { getZyphrClient } from '../client.js';\nimport { isToolEnabled, type ToolGuards } from '../config.js';\nimport { runTool } from '../result.js';\nimport {\n createSubscriberShape,\n findSubscriberShape,\n listSubscribersShape,\n setSubscriberPreferencesShape,\n updateSubscriberShape,\n} from '../schemas.js';\n\nexport function registerSubscriberTools(server: McpServer, guards: ToolGuards): void {\n if (isToolEnabled({ name: 'find_subscriber', mutates: false }, guards)) {\n server.registerTool(\n 'find_subscriber',\n {\n title: 'Find subscriber by external ID',\n description:\n 'Look up a subscriber by the external ID you assigned (typically your application user/customer ID).',\n inputSchema: findSubscriberShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.subscribers.getSubscriberByExternalId(args.externalId);\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'list_subscribers', mutates: false }, guards)) {\n server.registerTool(\n 'list_subscribers',\n {\n title: 'List subscribers',\n description: 'List subscribers, optionally filtered by status or email.',\n inputSchema: listSubscribersShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.subscribers.listSubscribers(\n args.status,\n args.email,\n args.limit,\n args.offset,\n );\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'create_subscriber', mutates: true }, guards)) {\n server.registerTool(\n 'create_subscriber',\n {\n title: 'Create subscriber',\n description: 'Create a new subscriber. `externalId` must be unique within your account.',\n inputSchema: createSubscriberShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.subscribers.createSubscriber({\n externalId: args.externalId,\n email: args.email,\n phone: args.phone,\n name: args.name,\n avatarUrl: args.avatarUrl,\n timezone: args.timezone,\n locale: args.locale,\n metadata: args.metadata,\n });\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'update_subscriber', mutates: true }, guards)) {\n server.registerTool(\n 'update_subscriber',\n {\n title: 'Update subscriber',\n description:\n 'Update a subscriber by Zyphr ID. Pass null for email/phone/name/avatarUrl to clear those fields.',\n inputSchema: updateSubscriberShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.subscribers.updateSubscriber(args.id, {\n email: args.email ?? undefined,\n phone: args.phone ?? undefined,\n name: args.name ?? undefined,\n avatarUrl: args.avatarUrl ?? undefined,\n timezone: args.timezone,\n locale: args.locale,\n metadata: args.metadata,\n status: args.status,\n });\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'set_subscriber_preferences', mutates: true }, guards)) {\n server.registerTool(\n 'set_subscriber_preferences',\n {\n title: 'Set subscriber preferences',\n description:\n 'Set notification preferences for a subscriber. Each preference targets a category and/or channel and toggles `enabled`.',\n inputSchema: setSubscriberPreferencesShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.subscribers.setSubscriberPreferences(args.id, {\n preferences: args.preferences,\n });\n });\n },\n );\n }\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { getZyphrClient } from '../client.js';\nimport { isToolEnabled, type ToolGuards } from '../config.js';\nimport { runTool } from '../result.js';\nimport {\n createTemplateShape,\n getTemplateShape,\n listTemplatesShape,\n renderTemplateShape,\n} from '../schemas.js';\n\nexport function registerTemplateTools(server: McpServer, guards: ToolGuards): void {\n if (isToolEnabled({ name: 'list_templates', mutates: false }, guards)) {\n server.registerTool(\n 'list_templates',\n {\n title: 'List templates',\n description: 'List notification templates in the account.',\n inputSchema: listTemplatesShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.templates.listTemplates(args.limit, args.offset);\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'get_template', mutates: false }, guards)) {\n server.registerTool(\n 'get_template',\n {\n title: 'Get template',\n description: 'Fetch a single template by ID.',\n inputSchema: getTemplateShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.templates.getTemplate(args.id);\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'render_template', mutates: false }, guards)) {\n server.registerTool(\n 'render_template',\n {\n title: 'Render template',\n description:\n 'Preview a template with the given variables WITHOUT sending. Returns the rendered subject/html/text so the AI can show the user what would be sent.',\n inputSchema: renderTemplateShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.templates.renderTemplate(args.id, { variables: args.variables });\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'create_template', mutates: true }, guards)) {\n server.registerTool(\n 'create_template',\n {\n title: 'Create template',\n description: 'Create a new notification template.',\n inputSchema: createTemplateShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.templates.createTemplate({\n name: args.name,\n description: args.description,\n subject: args.subject,\n html: args.html,\n text: args.text,\n });\n });\n },\n );\n }\n}\n","import type { QuickstartChannelMap } from '../quickstart-types.js';\n\nconst DOCS = 'https://docs.zyphr.dev/channels/email';\n\nconst ENV: string[] = ['ZYPHR_API_KEY'];\n\nconst NEXT_STEPS = [\n 'Add ZYPHR_API_KEY to your .env file (run get_sdk_install_for_language to confirm the install).',\n 'Wire the new service into your app entrypoint.',\n 'Verify your sender domain in the Zyphr dashboard before sending to real recipients.',\n];\n\nexport const emailChannel: QuickstartChannelMap = {\n node: {\n sdk: {\n channel: 'email',\n language: 'node',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'src/lib/zyphr.ts',\n purpose: 'Zyphr SDK client singleton',\n contents:\n \"import { Zyphr } from '@zyphr-dev/node-sdk';\\n\\n\" +\n 'export const zyphr = new Zyphr({ apiKey: process.env.ZYPHR_API_KEY! });\\n',\n overwrite: false,\n },\n {\n path: 'src/services/notify.ts',\n purpose: 'Sends a transactional email through Zyphr',\n contents:\n \"import { zyphr } from '../lib/zyphr.js';\\n\\n\" +\n 'export async function sendWelcomeEmail(to: string, name: string) {\\n' +\n ' return await zyphr.emails.sendEmail({\\n' +\n ' to: [{ email: to, name }],\\n' +\n ' subject: `Welcome, ${name}!`,\\n' +\n \" html: `<h1>Welcome aboard, ${name}!</h1><p>Glad you're here.</p>`,\\n\" +\n ' });\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n frameworks: {\n express: {\n channel: 'email',\n language: 'node',\n framework: 'express',\n variant: 'sdk',\n files: [\n {\n path: 'src/lib/zyphr.ts',\n purpose: 'Zyphr SDK client singleton',\n contents:\n \"import { Zyphr } from '@zyphr-dev/node-sdk';\\n\\n\" +\n 'export const zyphr = new Zyphr({ apiKey: process.env.ZYPHR_API_KEY! });\\n',\n overwrite: false,\n },\n {\n path: 'src/routes/notify.ts',\n purpose: 'Express route that sends a welcome email',\n contents:\n \"import { Router } from 'express';\\n\" +\n \"import { zyphr } from '../lib/zyphr.js';\\n\\n\" +\n 'export const notifyRouter = Router();\\n\\n' +\n \"notifyRouter.post('/notify', async (req, res, next) => {\\n\" +\n ' try {\\n' +\n ' const { to, name } = req.body as { to: string; name: string };\\n' +\n ' const result = await zyphr.emails.sendEmail({\\n' +\n ' to: [{ email: to, name }],\\n' +\n ' subject: `Welcome, ${name}!`,\\n' +\n ' html: `<h1>Welcome, ${name}!</h1>`,\\n' +\n ' });\\n' +\n ' res.json(result);\\n' +\n ' } catch (err) {\\n' +\n ' next(err);\\n' +\n ' }\\n' +\n '});\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: [\n ...NEXT_STEPS,\n \"Mount the router in app.ts: app.use('/api', notifyRouter)\",\n 'Test: curl -X POST http://localhost:3000/api/notify -d \\'{\"to\":\"you@example.com\",\"name\":\"You\"}\\' -H \"Content-Type: application/json\"',\n ],\n docsUrl: DOCS,\n },\n nextjs: {\n channel: 'email',\n language: 'node',\n framework: 'nextjs',\n variant: 'sdk',\n files: [\n {\n path: 'src/lib/zyphr.ts',\n purpose: 'Zyphr SDK client singleton (server-only)',\n contents:\n \"import 'server-only';\\n\" +\n \"import { Zyphr } from '@zyphr-dev/node-sdk';\\n\\n\" +\n 'export const zyphr = new Zyphr({ apiKey: process.env.ZYPHR_API_KEY! });\\n',\n overwrite: false,\n },\n {\n path: 'src/app/api/notify/route.ts',\n purpose: 'Next.js App Router route handler that sends a welcome email',\n contents:\n \"import { NextResponse } from 'next/server';\\n\" +\n \"import { zyphr } from '@/lib/zyphr';\\n\\n\" +\n 'export async function POST(req: Request) {\\n' +\n ' const { to, name } = (await req.json()) as { to: string; name: string };\\n' +\n ' const result = await zyphr.emails.sendEmail({\\n' +\n ' to: [{ email: to, name }],\\n' +\n ' subject: `Welcome, ${name}!`,\\n' +\n ' html: `<h1>Welcome, ${name}!</h1>`,\\n' +\n ' });\\n' +\n ' return NextResponse.json(result);\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: [\n ...NEXT_STEPS,\n 'Test: curl -X POST http://localhost:3000/api/notify -d \\'{\"to\":\"you@example.com\",\"name\":\"You\"}\\' -H \"Content-Type: application/json\"',\n ],\n docsUrl: DOCS,\n },\n },\n },\n python: {\n sdk: {\n channel: 'email',\n language: 'python',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'app/zyphr_client.py',\n purpose: 'REST helper for the Zyphr API',\n contents:\n 'import os\\n' +\n 'import requests\\n\\n' +\n 'ZYPHR_API_KEY = os.environ[\"ZYPHR_API_KEY\"]\\n' +\n 'BASE_URL = \"https://api.zyphr.dev/v1\"\\n\\n' +\n 'headers = {\\n' +\n ' \"X-API-Key\": ZYPHR_API_KEY,\\n' +\n ' \"Content-Type\": \"application/json\",\\n' +\n '}\\n\\n' +\n 'def zyphr_request(method, path, json=None, params=None):\\n' +\n ' response = requests.request(\\n' +\n ' method, f\"{BASE_URL}{path}\", headers=headers, json=json, params=params,\\n' +\n ' )\\n' +\n ' response.raise_for_status()\\n' +\n ' return response.json()\\n',\n overwrite: false,\n },\n {\n path: 'app/notify.py',\n purpose: 'Send a welcome email through Zyphr',\n contents:\n 'from .zyphr_client import zyphr_request\\n\\n' +\n 'def send_welcome_email(to: str, name: str) -> dict:\\n' +\n ' return zyphr_request(\"POST\", \"/emails\", json={\\n' +\n ' \"to\": [{\"email\": to, \"name\": name}],\\n' +\n ' \"subject\": f\"Welcome, {name}!\",\\n' +\n ' \"html\": f\"<h1>Welcome, {name}!</h1>\",\\n' +\n ' })\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n frameworks: {\n flask: {\n channel: 'email',\n language: 'python',\n framework: 'flask',\n variant: 'sdk',\n files: [\n {\n path: 'app/zyphr_client.py',\n purpose: 'REST helper for the Zyphr API',\n contents:\n 'import os\\n' +\n 'import requests\\n\\n' +\n 'BASE_URL = \"https://api.zyphr.dev/v1\"\\n\\n' +\n 'def zyphr_request(method, path, json=None, params=None):\\n' +\n ' response = requests.request(\\n' +\n ' method, f\"{BASE_URL}{path}\",\\n' +\n ' headers={\"X-API-Key\": os.environ[\"ZYPHR_API_KEY\"], \"Content-Type\": \"application/json\"},\\n' +\n ' json=json, params=params,\\n' +\n ' )\\n' +\n ' response.raise_for_status()\\n' +\n ' return response.json()\\n',\n overwrite: false,\n },\n {\n path: 'app/routes/notify.py',\n purpose: 'Flask blueprint that sends a welcome email',\n contents:\n 'from flask import Blueprint, request, jsonify\\n' +\n 'from ..zyphr_client import zyphr_request\\n\\n' +\n 'notify_bp = Blueprint(\"notify\", __name__)\\n\\n' +\n '@notify_bp.route(\"/notify\", methods=[\"POST\"])\\n' +\n 'def notify():\\n' +\n ' body = request.get_json() or {}\\n' +\n ' result = zyphr_request(\"POST\", \"/emails\", json={\\n' +\n ' \"to\": [{\"email\": body[\"to\"], \"name\": body.get(\"name\", \"\")}],\\n' +\n ' \"subject\": f\"Welcome, {body.get(\\'name\\', \\'friend\\')}!\",\\n' +\n ' \"html\": f\"<h1>Welcome!</h1>\",\\n' +\n ' })\\n' +\n ' return jsonify(result)\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: [\n ...NEXT_STEPS,\n 'Register the blueprint: app.register_blueprint(notify_bp, url_prefix=\"/api\")',\n ],\n docsUrl: DOCS,\n },\n fastapi: {\n channel: 'email',\n language: 'python',\n framework: 'fastapi',\n variant: 'sdk',\n files: [\n {\n path: 'app/zyphr_client.py',\n purpose: 'REST helper for the Zyphr API',\n contents:\n 'import os\\n' +\n 'import httpx\\n\\n' +\n 'BASE_URL = \"https://api.zyphr.dev/v1\"\\n\\n' +\n 'async def zyphr_request(method: str, path: str, json: dict | None = None) -> dict:\\n' +\n ' async with httpx.AsyncClient() as client:\\n' +\n ' resp = await client.request(\\n' +\n ' method, f\"{BASE_URL}{path}\",\\n' +\n ' headers={\"X-API-Key\": os.environ[\"ZYPHR_API_KEY\"], \"Content-Type\": \"application/json\"},\\n' +\n ' json=json,\\n' +\n ' )\\n' +\n ' resp.raise_for_status()\\n' +\n ' return resp.json()\\n',\n overwrite: false,\n },\n {\n path: 'app/routers/notify.py',\n purpose: 'FastAPI router that sends a welcome email',\n contents:\n 'from fastapi import APIRouter\\n' +\n 'from pydantic import BaseModel, EmailStr\\n' +\n 'from ..zyphr_client import zyphr_request\\n\\n' +\n 'router = APIRouter()\\n\\n' +\n 'class NotifyIn(BaseModel):\\n' +\n ' to: EmailStr\\n' +\n ' name: str\\n\\n' +\n '@router.post(\"/notify\")\\n' +\n 'async def notify(body: NotifyIn):\\n' +\n ' return await zyphr_request(\"POST\", \"/emails\", json={\\n' +\n ' \"to\": [{\"email\": body.to, \"name\": body.name}],\\n' +\n ' \"subject\": f\"Welcome, {body.name}!\",\\n' +\n ' \"html\": f\"<h1>Welcome, {body.name}!</h1>\",\\n' +\n ' })\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: [\n ...NEXT_STEPS,\n 'Install httpx: `pip install httpx` (or `poetry add httpx`)',\n 'Mount the router: app.include_router(router, prefix=\"/api\")',\n ],\n docsUrl: DOCS,\n },\n },\n },\n ruby: {\n sdk: {\n channel: 'email',\n language: 'ruby',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'config/initializers/zyphr.rb',\n purpose: 'Zyphr SDK configuration',\n contents:\n \"require 'zyphr'\\n\\n\" +\n 'Zyphr.configure do |config|\\n' +\n \" config.api_key['X-API-Key'] = ENV.fetch('ZYPHR_API_KEY')\\n\" +\n 'end\\n',\n overwrite: false,\n },\n {\n path: 'app/services/notify_service.rb',\n purpose: 'Service object that sends a welcome email',\n contents:\n 'class NotifyService\\n' +\n ' def self.send_welcome_email(to:, name:)\\n' +\n ' Zyphr::EmailsApi.new.send_email(\\n' +\n ' Zyphr::SendEmailRequest.new(\\n' +\n ' to: [{ email: to, name: name }],\\n' +\n \" subject: \\\"Welcome, #{name}!\\\",\\n\" +\n \" html: \\\"<h1>Welcome, #{name}!</h1>\\\"\\n\" +\n ' )\\n' +\n ' )\\n' +\n ' end\\n' +\n 'end\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n frameworks: {\n rails: {\n channel: 'email',\n language: 'ruby',\n framework: 'rails',\n variant: 'sdk',\n files: [\n {\n path: 'config/initializers/zyphr.rb',\n purpose: 'Zyphr SDK configuration',\n contents:\n \"require 'zyphr'\\n\\n\" +\n 'Zyphr.configure do |config|\\n' +\n \" config.api_key['X-API-Key'] = ENV.fetch('ZYPHR_API_KEY')\\n\" +\n 'end\\n',\n overwrite: false,\n },\n {\n path: 'app/controllers/notify_controller.rb',\n purpose: 'Rails controller that sends a welcome email',\n contents:\n 'class NotifyController < ApplicationController\\n' +\n ' def create\\n' +\n ' result = Zyphr::EmailsApi.new.send_email(\\n' +\n ' Zyphr::SendEmailRequest.new(\\n' +\n ' to: [{ email: params.require(:to), name: params[:name] }],\\n' +\n \" subject: \\\"Welcome, #{params[:name]}!\\\",\\n\" +\n \" html: \\\"<h1>Welcome, #{params[:name]}!</h1>\\\"\\n\" +\n ' )\\n' +\n ' )\\n' +\n ' render json: result\\n' +\n ' end\\n' +\n 'end\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: [\n ...NEXT_STEPS,\n \"Add a route in config/routes.rb: post '/notify', to: 'notify#create'\",\n ],\n docsUrl: DOCS,\n },\n },\n },\n go: {\n sdk: {\n channel: 'email',\n language: 'go',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'internal/zyphr/client.go',\n purpose: 'Thin REST client for the Zyphr API',\n contents:\n 'package zyphr\\n\\n' +\n 'import (\\n' +\n '\\t\"bytes\"\\n' +\n '\\t\"encoding/json\"\\n' +\n '\\t\"fmt\"\\n' +\n '\\t\"io\"\\n' +\n '\\t\"net/http\"\\n' +\n '\\t\"os\"\\n' +\n ')\\n\\n' +\n 'const baseURL = \"https://api.zyphr.dev/v1\"\\n\\n' +\n 'type Client struct {\\n' +\n '\\tAPIKey string\\n' +\n '\\tHTTPClient *http.Client\\n' +\n '}\\n\\n' +\n 'func NewClient() *Client {\\n' +\n '\\treturn &Client{APIKey: os.Getenv(\"ZYPHR_API_KEY\"), HTTPClient: &http.Client{}}\\n' +\n '}\\n\\n' +\n 'func (c *Client) Do(method, path string, body any) ([]byte, error) {\\n' +\n '\\tvar buf io.Reader\\n' +\n '\\tif body != nil {\\n' +\n '\\t\\tb, err := json.Marshal(body)\\n' +\n '\\t\\tif err != nil { return nil, fmt.Errorf(\"marshal: %w\", err) }\\n' +\n '\\t\\tbuf = bytes.NewReader(b)\\n' +\n '\\t}\\n' +\n '\\treq, _ := http.NewRequest(method, baseURL+path, buf)\\n' +\n '\\treq.Header.Set(\"X-API-Key\", c.APIKey)\\n' +\n '\\treq.Header.Set(\"Content-Type\", \"application/json\")\\n' +\n '\\tresp, err := c.HTTPClient.Do(req)\\n' +\n '\\tif err != nil { return nil, err }\\n' +\n '\\tdefer resp.Body.Close()\\n' +\n '\\tdata, _ := io.ReadAll(resp.Body)\\n' +\n '\\tif resp.StatusCode >= 400 { return nil, fmt.Errorf(\"zyphr %d: %s\", resp.StatusCode, data) }\\n' +\n '\\treturn data, nil\\n' +\n '}\\n',\n overwrite: false,\n },\n {\n path: 'internal/notify/notify.go',\n purpose: 'Send a welcome email through Zyphr',\n contents:\n 'package notify\\n\\n' +\n 'import \"yourapp/internal/zyphr\"\\n\\n' +\n 'func SendWelcomeEmail(client *zyphr.Client, to, name string) ([]byte, error) {\\n' +\n '\\treturn client.Do(\"POST\", \"/emails\", map[string]any{\\n' +\n '\\t\\t\"to\": []map[string]string{{\"email\": to, \"name\": name}},\\n' +\n '\\t\\t\"subject\": \"Welcome, \" + name + \"!\",\\n' +\n '\\t\\t\"html\": \"<h1>Welcome, \" + name + \"!</h1>\",\\n' +\n '\\t})\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n php: {\n sdk: {\n channel: 'email',\n language: 'php',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'app/Services/ZyphrClient.php',\n purpose: 'Guzzle-backed Zyphr client',\n contents:\n '<?php\\n\\n' +\n 'namespace App\\\\Services;\\n\\n' +\n 'use GuzzleHttp\\\\Client;\\n\\n' +\n 'class ZyphrClient\\n' +\n '{\\n' +\n ' private Client $http;\\n\\n' +\n ' public function __construct()\\n' +\n ' {\\n' +\n ' $this->http = new Client([\\n' +\n \" 'base_uri' => 'https://api.zyphr.dev/v1/',\\n\" +\n \" 'headers' => [\\n\" +\n \" 'X-API-Key' => getenv('ZYPHR_API_KEY'),\\n\" +\n \" 'Content-Type' => 'application/json',\\n\" +\n ' ],\\n' +\n ' ]);\\n' +\n ' }\\n\\n' +\n ' public function sendWelcomeEmail(string $to, string $name): array\\n' +\n ' {\\n' +\n \" $response = $this->http->post('emails', [\\n\" +\n \" 'json' => [\\n\" +\n \" 'to' => [['email' => $to, 'name' => $name]],\\n\" +\n \" 'subject' => \\\"Welcome, {$name}!\\\",\\n\" +\n \" 'html' => \\\"<h1>Welcome, {$name}!</h1>\\\",\\n\" +\n ' ],\\n' +\n ' ]);\\n' +\n \" return json_decode((string) $response->getBody(), true);\\n\" +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n frameworks: {\n laravel: {\n channel: 'email',\n language: 'php',\n framework: 'laravel',\n variant: 'sdk',\n files: [\n {\n path: 'app/Services/ZyphrClient.php',\n purpose: 'Laravel-friendly Zyphr client',\n contents:\n '<?php\\n\\n' +\n 'namespace App\\\\Services;\\n\\n' +\n 'use Illuminate\\\\Support\\\\Facades\\\\Http;\\n\\n' +\n 'class ZyphrClient\\n' +\n '{\\n' +\n ' public function sendWelcomeEmail(string $to, string $name): array\\n' +\n ' {\\n' +\n ' $response = Http::withHeaders([\\n' +\n \" 'X-API-Key' => config('services.zyphr.api_key'),\\n\" +\n \" 'Content-Type' => 'application/json',\\n\" +\n \" ])->post('https://api.zyphr.dev/v1/emails', [\\n\" +\n \" 'to' => [['email' => $to, 'name' => $name]],\\n\" +\n \" 'subject' => \\\"Welcome, {$name}!\\\",\\n\" +\n \" 'html' => \\\"<h1>Welcome, {$name}!</h1>\\\",\\n\" +\n ' ]);\\n' +\n ' $response->throw();\\n' +\n \" return $response->json();\\n\" +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n {\n path: 'config/services.php (snippet)',\n purpose: 'Register the Zyphr API key under services config',\n contents:\n \"'zyphr' => [\\n\" +\n \" 'api_key' => env('ZYPHR_API_KEY'),\\n\" +\n '],\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n },\n csharp: {\n sdk: {\n channel: 'email',\n language: 'csharp',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'Services/ZyphrClient.cs',\n purpose: 'Singleton-style Zyphr client wrapper',\n contents:\n 'using ZyphrDev.SDK.Api;\\n' +\n 'using ZyphrDev.SDK.Client;\\n' +\n 'using ZyphrDev.SDK.Model;\\n\\n' +\n 'namespace YourApp.Services;\\n\\n' +\n 'public class ZyphrClient\\n' +\n '{\\n' +\n ' private readonly EmailsApi _emails;\\n\\n' +\n ' public ZyphrClient()\\n' +\n ' {\\n' +\n ' var config = new Configuration\\n' +\n ' {\\n' +\n ' ApiKey = new Dictionary<string, string>\\n' +\n ' {\\n' +\n ' { \"X-API-Key\", Environment.GetEnvironmentVariable(\"ZYPHR_API_KEY\")! }\\n' +\n ' }\\n' +\n ' };\\n' +\n ' _emails = new EmailsApi(config);\\n' +\n ' }\\n\\n' +\n ' public async Task<SendEmailResponse> SendWelcomeEmailAsync(string to, string name)\\n' +\n ' {\\n' +\n ' return await _emails.SendEmailAsync(new SendEmailRequest(\\n' +\n ' to: new List<EmailAddress> { new() { Email = to, Name = name } },\\n' +\n ' subject: $\"Welcome, {name}!\",\\n' +\n ' html: $\"<h1>Welcome, {name}!</h1>\"\\n' +\n ' ));\\n' +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n frameworks: {\n aspnetcore: {\n channel: 'email',\n language: 'csharp',\n framework: 'aspnetcore',\n variant: 'sdk',\n files: [\n {\n path: 'Services/ZyphrClient.cs',\n purpose: 'DI-friendly Zyphr client',\n contents:\n 'using ZyphrDev.SDK.Api;\\n' +\n 'using ZyphrDev.SDK.Client;\\n' +\n 'using ZyphrDev.SDK.Model;\\n\\n' +\n 'namespace YourApp.Services;\\n\\n' +\n 'public class ZyphrClient\\n' +\n '{\\n' +\n ' public EmailsApi Emails { get; }\\n\\n' +\n ' public ZyphrClient(IConfiguration cfg)\\n' +\n ' {\\n' +\n ' var config = new Configuration\\n' +\n ' {\\n' +\n ' ApiKey = new Dictionary<string, string>\\n' +\n ' {\\n' +\n ' { \"X-API-Key\", cfg[\"Zyphr:ApiKey\"]! }\\n' +\n ' }\\n' +\n ' };\\n' +\n ' Emails = new EmailsApi(config);\\n' +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n {\n path: 'Controllers/NotifyController.cs',\n purpose: 'ASP.NET Core controller that sends a welcome email',\n contents:\n 'using Microsoft.AspNetCore.Mvc;\\n' +\n 'using YourApp.Services;\\n' +\n 'using ZyphrDev.SDK.Model;\\n\\n' +\n '[ApiController]\\n' +\n '[Route(\"api/[controller]\")]\\n' +\n 'public class NotifyController : ControllerBase\\n' +\n '{\\n' +\n ' private readonly ZyphrClient _zyphr;\\n' +\n ' public NotifyController(ZyphrClient zyphr) => _zyphr = zyphr;\\n\\n' +\n ' public record NotifyIn(string To, string Name);\\n\\n' +\n ' [HttpPost]\\n' +\n ' public async Task<IActionResult> Post([FromBody] NotifyIn body)\\n' +\n ' {\\n' +\n ' var result = await _zyphr.Emails.SendEmailAsync(new SendEmailRequest(\\n' +\n ' to: new List<EmailAddress> { new() { Email = body.To, Name = body.Name } },\\n' +\n ' subject: $\"Welcome, {body.Name}!\",\\n' +\n ' html: $\"<h1>Welcome, {body.Name}!</h1>\"\\n' +\n ' ));\\n' +\n ' return Ok(result);\\n' +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: [\n ...NEXT_STEPS,\n 'Register the client in Program.cs: builder.Services.AddSingleton<ZyphrClient>();',\n ],\n docsUrl: DOCS,\n },\n },\n },\n};\n","import type { QuickstartChannelMap } from '../quickstart-types.js';\n\nconst DOCS = 'https://docs.zyphr.dev/channels/in-app-messaging';\nconst ENV: string[] = ['ZYPHR_API_KEY'];\nconst NEXT_STEPS = [\n 'Add ZYPHR_API_KEY to your .env file.',\n 'Use the matching subscriberId on the client (e.g. @zyphr-dev/inbox-react) to display the message.',\n];\n\nexport const inboxChannel: QuickstartChannelMap = {\n node: {\n sdk: {\n channel: 'inbox',\n language: 'node',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'src/services/inbox.ts',\n purpose: 'Send an in-app inbox message through Zyphr',\n contents:\n \"import { Zyphr } from '@zyphr-dev/node-sdk';\\n\\n\" +\n 'const zyphr = new Zyphr({ apiKey: process.env.ZYPHR_API_KEY! });\\n\\n' +\n 'export async function notifyReportReady(subscriberId: string, reportId: string) {\\n' +\n ' return await zyphr.inbox.sendInApp({\\n' +\n ' subscriberId,\\n' +\n \" title: 'New report ready',\\n\" +\n \" body: 'Your report finished processing — click to view.',\\n\" +\n ' actionUrl: `/reports/${reportId}`,\\n' +\n \" actionLabel: 'View report',\\n\" +\n ' });\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n python: {\n sdk: {\n channel: 'inbox',\n language: 'python',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'app/inbox.py',\n purpose: 'Send an in-app inbox message through Zyphr',\n contents:\n 'from .zyphr_client import zyphr_request\\n\\n' +\n 'def notify_report_ready(subscriber_id: str, report_id: str) -> dict:\\n' +\n ' return zyphr_request(\"POST\", \"/inbox\", json={\\n' +\n ' \"subscriberId\": subscriber_id,\\n' +\n ' \"title\": \"New report ready\",\\n' +\n ' \"body\": \"Your report finished processing — click to view.\",\\n' +\n ' \"actionUrl\": f\"/reports/{report_id}\",\\n' +\n ' \"actionLabel\": \"View report\",\\n' +\n ' })\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n ruby: {\n sdk: {\n channel: 'inbox',\n language: 'ruby',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'app/services/inbox_service.rb',\n purpose: 'Send an in-app inbox message through Zyphr',\n contents:\n 'class InboxService\\n' +\n ' def self.notify_report_ready(subscriber_id:, report_id:)\\n' +\n ' Zyphr::InboxApi.new.send_in_app(\\n' +\n ' Zyphr::SendInAppRequest.new(\\n' +\n ' subscriber_id: subscriber_id,\\n' +\n \" title: 'New report ready',\\n\" +\n \" body: 'Your report finished processing — click to view.',\\n\" +\n \" action_url: \\\"/reports/#{report_id}\\\",\\n\" +\n \" action_label: 'View report'\\n\" +\n ' )\\n' +\n ' )\\n' +\n ' end\\n' +\n 'end\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n go: {\n sdk: {\n channel: 'inbox',\n language: 'go',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'internal/notify/inbox.go',\n purpose: 'Send an in-app inbox message through Zyphr',\n contents:\n 'package notify\\n\\n' +\n 'import \"yourapp/internal/zyphr\"\\n\\n' +\n 'func NotifyReportReady(client *zyphr.Client, subscriberID, reportID string) ([]byte, error) {\\n' +\n '\\treturn client.Do(\"POST\", \"/inbox\", map[string]any{\\n' +\n '\\t\\t\"subscriberId\": subscriberID,\\n' +\n '\\t\\t\"title\": \"New report ready\",\\n' +\n '\\t\\t\"body\": \"Your report finished processing — click to view.\",\\n' +\n '\\t\\t\"actionUrl\": \"/reports/\" + reportID,\\n' +\n '\\t\\t\"actionLabel\": \"View report\",\\n' +\n '\\t})\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n php: {\n sdk: {\n channel: 'inbox',\n language: 'php',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'app/Services/InboxService.php',\n purpose: 'Send an in-app inbox message through Zyphr',\n contents:\n '<?php\\n\\n' +\n 'namespace App\\\\Services;\\n\\n' +\n 'use GuzzleHttp\\\\Client;\\n\\n' +\n 'class InboxService\\n' +\n '{\\n' +\n ' public function notifyReportReady(string $subscriberId, string $reportId): array\\n' +\n ' {\\n' +\n \" $http = new Client([\\n\" +\n \" 'base_uri' => 'https://api.zyphr.dev/v1/',\\n\" +\n \" 'headers' => [\\n\" +\n \" 'X-API-Key' => getenv('ZYPHR_API_KEY'),\\n\" +\n \" 'Content-Type' => 'application/json',\\n\" +\n ' ],\\n' +\n \" ]);\\n\" +\n \" $r = $http->post('inbox', ['json' => [\\n\" +\n \" 'subscriberId' => $subscriberId,\\n\" +\n \" 'title' => 'New report ready',\\n\" +\n \" 'body' => 'Your report finished processing — click to view.',\\n\" +\n \" 'actionUrl' => \\\"/reports/{$reportId}\\\",\\n\" +\n \" 'actionLabel' => 'View report',\\n\" +\n ' ]]);\\n' +\n \" return json_decode((string) $r->getBody(), true);\\n\" +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n csharp: {\n sdk: {\n channel: 'inbox',\n language: 'csharp',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'Services/InboxService.cs',\n purpose: 'Send an in-app inbox message through Zyphr',\n contents:\n 'using ZyphrDev.SDK.Api;\\n' +\n 'using ZyphrDev.SDK.Client;\\n' +\n 'using ZyphrDev.SDK.Model;\\n\\n' +\n 'namespace YourApp.Services;\\n\\n' +\n 'public class InboxService\\n' +\n '{\\n' +\n ' private readonly InboxApi _inbox;\\n' +\n ' public InboxService()\\n' +\n ' {\\n' +\n ' var config = new Configuration\\n' +\n ' {\\n' +\n ' ApiKey = new Dictionary<string, string>\\n' +\n ' {\\n' +\n ' { \"X-API-Key\", Environment.GetEnvironmentVariable(\"ZYPHR_API_KEY\")! }\\n' +\n ' }\\n' +\n ' };\\n' +\n ' _inbox = new InboxApi(config);\\n' +\n ' }\\n\\n' +\n ' public Task<SendInAppResponse> NotifyReportReadyAsync(string subscriberId, string reportId)\\n' +\n ' {\\n' +\n ' return _inbox.SendInAppAsync(new SendInAppRequest(\\n' +\n ' subscriberId: subscriberId,\\n' +\n ' title: \"New report ready\",\\n' +\n ' body: \"Your report finished processing — click to view.\",\\n' +\n ' actionUrl: $\"/reports/{reportId}\",\\n' +\n ' actionLabel: \"View report\"\\n' +\n ' ));\\n' +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n};\n","import type { QuickstartChannelMap } from '../quickstart-types.js';\n\nconst DOCS = 'https://docs.zyphr.dev/channels/push-notifications';\nconst ENV: string[] = ['ZYPHR_API_KEY'];\nconst NEXT_STEPS = [\n 'Add ZYPHR_API_KEY to your .env file.',\n 'Register at least one device for the target subscriber (via the SDK or dashboard) before sending.',\n 'Verify your push provider credentials (APNs/FCM) are configured in the Zyphr dashboard.',\n];\n\nexport const pushChannel: QuickstartChannelMap = {\n node: {\n sdk: {\n channel: 'push',\n language: 'node',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'src/services/push.ts',\n purpose: 'Send a push notification through Zyphr',\n contents:\n \"import { Zyphr } from '@zyphr-dev/node-sdk';\\n\\n\" +\n 'const zyphr = new Zyphr({ apiKey: process.env.ZYPHR_API_KEY! });\\n\\n' +\n 'export async function pushOrderShipped(subscriberId: string, orderId: string) {\\n' +\n ' return await zyphr.push.sendPush({\\n' +\n ' subscriberId,\\n' +\n \" title: 'Order shipped',\\n\" +\n ' body: `Order ${orderId} is on its way.`,\\n' +\n ' data: { orderId },\\n' +\n ' });\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n python: {\n sdk: {\n channel: 'push',\n language: 'python',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'app/push.py',\n purpose: 'Send a push notification through Zyphr',\n contents:\n 'from .zyphr_client import zyphr_request\\n\\n' +\n 'def push_order_shipped(subscriber_id: str, order_id: str) -> dict:\\n' +\n ' return zyphr_request(\"POST\", \"/push\", json={\\n' +\n ' \"subscriberId\": subscriber_id,\\n' +\n ' \"title\": \"Order shipped\",\\n' +\n ' \"body\": f\"Order {order_id} is on its way.\",\\n' +\n ' \"data\": {\"orderId\": order_id},\\n' +\n ' })\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n ruby: {\n sdk: {\n channel: 'push',\n language: 'ruby',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'app/services/push_service.rb',\n purpose: 'Send a push notification through Zyphr',\n contents:\n 'class PushService\\n' +\n ' def self.order_shipped(subscriber_id:, order_id:)\\n' +\n ' Zyphr::PushApi.new.send_push(\\n' +\n ' Zyphr::SendPushRequest.new(\\n' +\n ' subscriber_id: subscriber_id,\\n' +\n \" title: 'Order shipped',\\n\" +\n \" body: \\\"Order #{order_id} is on its way.\\\",\\n\" +\n ' data: { orderId: order_id }\\n' +\n ' )\\n' +\n ' )\\n' +\n ' end\\n' +\n 'end\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n go: {\n sdk: {\n channel: 'push',\n language: 'go',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'internal/notify/push.go',\n purpose: 'Send a push notification through Zyphr',\n contents:\n 'package notify\\n\\n' +\n 'import \"yourapp/internal/zyphr\"\\n\\n' +\n 'func PushOrderShipped(client *zyphr.Client, subscriberID, orderID string) ([]byte, error) {\\n' +\n '\\treturn client.Do(\"POST\", \"/push\", map[string]any{\\n' +\n '\\t\\t\"subscriberId\": subscriberID,\\n' +\n '\\t\\t\"title\": \"Order shipped\",\\n' +\n '\\t\\t\"body\": \"Order \" + orderID + \" is on its way.\",\\n' +\n '\\t\\t\"data\": map[string]string{\"orderId\": orderID},\\n' +\n '\\t})\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n php: {\n sdk: {\n channel: 'push',\n language: 'php',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'app/Services/PushService.php',\n purpose: 'Send a push notification through Zyphr',\n contents:\n '<?php\\n\\n' +\n 'namespace App\\\\Services;\\n\\n' +\n 'use GuzzleHttp\\\\Client;\\n\\n' +\n 'class PushService\\n' +\n '{\\n' +\n ' public function orderShipped(string $subscriberId, string $orderId): array\\n' +\n ' {\\n' +\n \" $http = new Client([\\n\" +\n \" 'base_uri' => 'https://api.zyphr.dev/v1/',\\n\" +\n \" 'headers' => [\\n\" +\n \" 'X-API-Key' => getenv('ZYPHR_API_KEY'),\\n\" +\n \" 'Content-Type' => 'application/json',\\n\" +\n ' ],\\n' +\n \" ]);\\n\" +\n \" $r = $http->post('push', ['json' => [\\n\" +\n \" 'subscriberId' => $subscriberId,\\n\" +\n \" 'title' => 'Order shipped',\\n\" +\n \" 'body' => \\\"Order {$orderId} is on its way.\\\",\\n\" +\n \" 'data' => ['orderId' => $orderId],\\n\" +\n ' ]]);\\n' +\n \" return json_decode((string) $r->getBody(), true);\\n\" +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n csharp: {\n sdk: {\n channel: 'push',\n language: 'csharp',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'Services/PushService.cs',\n purpose: 'Send a push notification through Zyphr',\n contents:\n 'using ZyphrDev.SDK.Api;\\n' +\n 'using ZyphrDev.SDK.Client;\\n' +\n 'using ZyphrDev.SDK.Model;\\n\\n' +\n 'namespace YourApp.Services;\\n\\n' +\n 'public class PushService\\n' +\n '{\\n' +\n ' private readonly PushApi _push;\\n' +\n ' public PushService()\\n' +\n ' {\\n' +\n ' var config = new Configuration\\n' +\n ' {\\n' +\n ' ApiKey = new Dictionary<string, string>\\n' +\n ' {\\n' +\n ' { \"X-API-Key\", Environment.GetEnvironmentVariable(\"ZYPHR_API_KEY\")! }\\n' +\n ' }\\n' +\n ' };\\n' +\n ' _push = new PushApi(config);\\n' +\n ' }\\n\\n' +\n ' public Task<SendPushResponse> OrderShippedAsync(string subscriberId, string orderId)\\n' +\n ' {\\n' +\n ' return _push.SendPushAsync(new SendPushRequest(\\n' +\n ' subscriberId: subscriberId,\\n' +\n ' title: \"Order shipped\",\\n' +\n ' body: $\"Order {orderId} is on its way.\"\\n' +\n ' ));\\n' +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n};\n","import type { QuickstartChannelMap } from '../quickstart-types.js';\n\nconst DOCS = 'https://docs.zyphr.dev/channels/sms';\nconst ENV: string[] = ['ZYPHR_API_KEY'];\nconst NEXT_STEPS = [\n 'Add ZYPHR_API_KEY to your .env file.',\n 'Recipients MUST be in E.164 format (e.g. +14155551234).',\n 'Provision your SMS sender (phone number or alphanumeric sender ID) in the Zyphr dashboard.',\n];\n\nexport const smsChannel: QuickstartChannelMap = {\n node: {\n sdk: {\n channel: 'sms',\n language: 'node',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'src/services/sms.ts',\n purpose: 'Send an SMS through Zyphr',\n contents:\n \"import { Zyphr } from '@zyphr-dev/node-sdk';\\n\\n\" +\n 'const zyphr = new Zyphr({ apiKey: process.env.ZYPHR_API_KEY! });\\n\\n' +\n 'export async function sendOtp(to: string, code: string) {\\n' +\n ' return await zyphr.sms.sendSms({\\n' +\n ' to,\\n' +\n ' body: `Your verification code is ${code}. Expires in 5 minutes.`,\\n' +\n ' });\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n python: {\n sdk: {\n channel: 'sms',\n language: 'python',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'app/sms.py',\n purpose: 'Send an SMS through Zyphr',\n contents:\n 'from .zyphr_client import zyphr_request\\n\\n' +\n 'def send_otp(to: str, code: str) -> dict:\\n' +\n ' return zyphr_request(\"POST\", \"/sms\", json={\\n' +\n ' \"to\": to,\\n' +\n ' \"body\": f\"Your verification code is {code}. Expires in 5 minutes.\",\\n' +\n ' })\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n ruby: {\n sdk: {\n channel: 'sms',\n language: 'ruby',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'app/services/sms_service.rb',\n purpose: 'Send an SMS through Zyphr',\n contents:\n 'class SmsService\\n' +\n ' def self.send_otp(to:, code:)\\n' +\n ' Zyphr::SMSApi.new.send_sms(\\n' +\n ' Zyphr::SendSmsRequest.new(\\n' +\n ' to: to,\\n' +\n \" body: \\\"Your verification code is #{code}. Expires in 5 minutes.\\\"\\n\" +\n ' )\\n' +\n ' )\\n' +\n ' end\\n' +\n 'end\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n go: {\n sdk: {\n channel: 'sms',\n language: 'go',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'internal/notify/sms.go',\n purpose: 'Send an SMS through Zyphr',\n contents:\n 'package notify\\n\\n' +\n 'import \"yourapp/internal/zyphr\"\\n\\n' +\n 'func SendOtp(client *zyphr.Client, to, code string) ([]byte, error) {\\n' +\n '\\treturn client.Do(\"POST\", \"/sms\", map[string]any{\\n' +\n '\\t\\t\"to\": to,\\n' +\n '\\t\\t\"body\": \"Your verification code is \" + code + \". Expires in 5 minutes.\",\\n' +\n '\\t})\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n php: {\n sdk: {\n channel: 'sms',\n language: 'php',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'app/Services/SmsService.php',\n purpose: 'Send an SMS through Zyphr',\n contents:\n '<?php\\n\\n' +\n 'namespace App\\\\Services;\\n\\n' +\n 'use GuzzleHttp\\\\Client;\\n\\n' +\n 'class SmsService\\n' +\n '{\\n' +\n ' public function sendOtp(string $to, string $code): array\\n' +\n ' {\\n' +\n \" $http = new Client([\\n\" +\n \" 'base_uri' => 'https://api.zyphr.dev/v1/',\\n\" +\n \" 'headers' => [\\n\" +\n \" 'X-API-Key' => getenv('ZYPHR_API_KEY'),\\n\" +\n \" 'Content-Type' => 'application/json',\\n\" +\n ' ],\\n' +\n \" ]);\\n\" +\n \" $r = $http->post('sms', ['json' => [\\n\" +\n \" 'to' => $to,\\n\" +\n \" 'body' => \\\"Your verification code is {$code}. Expires in 5 minutes.\\\",\\n\" +\n ' ]]);\\n' +\n \" return json_decode((string) $r->getBody(), true);\\n\" +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n csharp: {\n sdk: {\n channel: 'sms',\n language: 'csharp',\n framework: null,\n variant: 'sdk',\n files: [\n {\n path: 'Services/SmsService.cs',\n purpose: 'Send an SMS through Zyphr',\n contents:\n 'using ZyphrDev.SDK.Api;\\n' +\n 'using ZyphrDev.SDK.Client;\\n' +\n 'using ZyphrDev.SDK.Model;\\n\\n' +\n 'namespace YourApp.Services;\\n\\n' +\n 'public class SmsService\\n' +\n '{\\n' +\n ' private readonly SMSApi _sms;\\n' +\n ' public SmsService()\\n' +\n ' {\\n' +\n ' var config = new Configuration\\n' +\n ' {\\n' +\n ' ApiKey = new Dictionary<string, string>\\n' +\n ' {\\n' +\n ' { \"X-API-Key\", Environment.GetEnvironmentVariable(\"ZYPHR_API_KEY\")! }\\n' +\n ' }\\n' +\n ' };\\n' +\n ' _sms = new SMSApi(config);\\n' +\n ' }\\n\\n' +\n ' public Task<SendSmsResponse> SendOtpAsync(string to, string code)\\n' +\n ' {\\n' +\n ' return _sms.SendSmsAsync(new SendSmsRequest(\\n' +\n ' to: to,\\n' +\n ' body: $\"Your verification code is {code}. Expires in 5 minutes.\"\\n' +\n ' ));\\n' +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n};\n","import type { QuickstartChannelMap } from '../quickstart-types.js';\n\nconst DOCS = 'https://docs.zyphr.dev/features/webhooks-security';\n\nconst ENV: string[] = ['ZYPHR_WEBHOOK_SECRET'];\n\nconst NEXT_STEPS = [\n 'Add ZYPHR_WEBHOOK_SECRET to your .env file — get it from `zyphr.webhooks.rotateWebhookSecret(id)` or the Zyphr dashboard.',\n 'Configure the webhook endpoint URL in the Zyphr dashboard or via `create_webhook`.',\n 'ALWAYS verify signatures before processing payloads — never trust an unverified webhook.',\n 'Reject deliveries whose timestamp is more than 5 minutes from now to prevent replay attacks.',\n];\n\nexport const webhookChannel: QuickstartChannelMap = {\n node: {\n sdk: {\n channel: 'webhook',\n language: 'node',\n framework: null,\n variant: 'webhook-handler',\n files: [\n {\n path: 'src/lib/verifyZyphrWebhook.ts',\n purpose:\n 'Standard Webhooks (HMAC-SHA256) signature + timestamp verification. Mirrors the canonical snippet in apps/docs/docs/features/webhooks-security.md.',\n contents:\n \"import crypto from 'crypto';\\n\\n\" +\n 'export function verifyZyphrWebhook(\\n' +\n ' payload: string,\\n' +\n \" headers: { 'webhook-id': string; 'webhook-timestamp': string; 'webhook-signature': string },\\n\" +\n ' secret: string,\\n' +\n '): boolean {\\n' +\n \" const msgId = headers['webhook-id'];\\n\" +\n \" const timestamp = parseInt(headers['webhook-timestamp'], 10);\\n\" +\n \" const signatures = headers['webhook-signature'];\\n\\n\" +\n ' const now = Math.floor(Date.now() / 1000);\\n' +\n ' if (Math.abs(now - timestamp) > 300) return false;\\n\\n' +\n ' const signedContent = `${msgId}.${timestamp}.${payload}`;\\n' +\n ' const secretBytes = Buffer.from(\\n' +\n \" secret.startsWith('whsec_') ? secret.slice(6) : secret,\\n\" +\n \" 'hex',\\n\" +\n ' );\\n' +\n ' const expected = crypto\\n' +\n \" .createHmac('sha256', secretBytes)\\n\" +\n ' .update(signedContent)\\n' +\n \" .digest('base64');\\n\\n\" +\n \" for (const sig of signatures.split(' ')) {\\n\" +\n ' const sigValue = sig.slice(3);\\n' +\n ' if (\\n' +\n ' sigValue.length === expected.length &&\\n' +\n ' crypto.timingSafeEqual(Buffer.from(sigValue), Buffer.from(expected))\\n' +\n ' ) {\\n' +\n ' return true;\\n' +\n ' }\\n' +\n ' }\\n' +\n ' return false;\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n frameworks: {\n express: {\n channel: 'webhook',\n language: 'node',\n framework: 'express',\n variant: 'webhook-handler',\n files: [\n {\n path: 'src/lib/verifyZyphrWebhook.ts',\n purpose: 'Standard Webhooks signature verification helper',\n contents:\n \"import crypto from 'crypto';\\n\\n\" +\n 'export function verifyZyphrWebhook(payload: string, headers: Record<string,string>, secret: string): boolean {\\n' +\n \" const msgId = headers['webhook-id'];\\n\" +\n \" const timestamp = parseInt(headers['webhook-timestamp'], 10);\\n\" +\n \" const signatures = headers['webhook-signature'] || '';\\n\" +\n ' const now = Math.floor(Date.now() / 1000);\\n' +\n ' if (Math.abs(now - timestamp) > 300) return false;\\n' +\n ' const signedContent = `${msgId}.${timestamp}.${payload}`;\\n' +\n \" const secretBytes = Buffer.from(secret.startsWith('whsec_') ? secret.slice(6) : secret, 'hex');\\n\" +\n \" const expected = crypto.createHmac('sha256', secretBytes).update(signedContent).digest('base64');\\n\" +\n \" return signatures.split(' ').some((sig) => {\\n\" +\n ' const v = sig.slice(3);\\n' +\n ' return v.length === expected.length && crypto.timingSafeEqual(Buffer.from(v), Buffer.from(expected));\\n' +\n ' });\\n' +\n '}\\n',\n overwrite: false,\n },\n {\n path: 'src/routes/zyphrWebhook.ts',\n purpose:\n 'Express route that VERIFIES the signature before processing the webhook. Uses express.raw() so we can hash the exact bytes.',\n contents:\n \"import { Router, raw } from 'express';\\n\" +\n \"import { verifyZyphrWebhook } from '../lib/verifyZyphrWebhook.js';\\n\\n\" +\n 'export const zyphrWebhookRouter = Router();\\n\\n' +\n \"zyphrWebhookRouter.post('/zyphr', raw({ type: 'application/json' }), (req, res) => {\\n\" +\n ' const payload = (req.body as Buffer).toString();\\n' +\n ' const headers = req.headers as Record<string, string>;\\n' +\n ' if (!verifyZyphrWebhook(payload, headers, process.env.ZYPHR_WEBHOOK_SECRET!)) {\\n' +\n \" return res.status(401).send('invalid signature');\\n\" +\n ' }\\n' +\n ' const event = JSON.parse(payload) as { type: string; data: unknown };\\n' +\n \" console.log('zyphr event', event.type);\\n\" +\n ' res.sendStatus(204);\\n' +\n '});\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: [\n ...NEXT_STEPS,\n 'Mount the router BEFORE express.json(): app.use(zyphrWebhookRouter)',\n ],\n docsUrl: DOCS,\n },\n nextjs: {\n channel: 'webhook',\n language: 'node',\n framework: 'nextjs',\n variant: 'webhook-handler',\n files: [\n {\n path: 'src/lib/verifyZyphrWebhook.ts',\n purpose: 'Standard Webhooks signature verification helper',\n contents:\n \"import crypto from 'crypto';\\n\\n\" +\n 'export function verifyZyphrWebhook(payload: string, headers: Headers, secret: string): boolean {\\n' +\n \" const msgId = headers.get('webhook-id') || '';\\n\" +\n \" const timestamp = parseInt(headers.get('webhook-timestamp') || '0', 10);\\n\" +\n \" const signatures = headers.get('webhook-signature') || '';\\n\" +\n ' const now = Math.floor(Date.now() / 1000);\\n' +\n ' if (Math.abs(now - timestamp) > 300) return false;\\n' +\n ' const signedContent = `${msgId}.${timestamp}.${payload}`;\\n' +\n \" const secretBytes = Buffer.from(secret.startsWith('whsec_') ? secret.slice(6) : secret, 'hex');\\n\" +\n \" const expected = crypto.createHmac('sha256', secretBytes).update(signedContent).digest('base64');\\n\" +\n \" return signatures.split(' ').some((sig) => {\\n\" +\n ' const v = sig.slice(3);\\n' +\n ' return v.length === expected.length && crypto.timingSafeEqual(Buffer.from(v), Buffer.from(expected));\\n' +\n ' });\\n' +\n '}\\n',\n overwrite: false,\n },\n {\n path: 'src/app/api/zyphr/webhook/route.ts',\n purpose: 'Next.js App Router webhook handler with signature verification',\n contents:\n \"import { NextResponse } from 'next/server';\\n\" +\n \"import { verifyZyphrWebhook } from '@/lib/verifyZyphrWebhook';\\n\\n\" +\n 'export async function POST(req: Request) {\\n' +\n ' const payload = await req.text();\\n' +\n ' if (!verifyZyphrWebhook(payload, req.headers, process.env.ZYPHR_WEBHOOK_SECRET!)) {\\n' +\n \" return new NextResponse('invalid signature', { status: 401 });\\n\" +\n ' }\\n' +\n ' const event = JSON.parse(payload) as { type: string; data: unknown };\\n' +\n \" console.log('zyphr event', event.type);\\n\" +\n ' return new NextResponse(null, { status: 204 });\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n },\n python: {\n sdk: {\n channel: 'webhook',\n language: 'python',\n framework: null,\n variant: 'webhook-handler',\n files: [\n {\n path: 'app/zyphr_webhook.py',\n purpose: 'Standard Webhooks signature verification helper (verbatim from docs)',\n contents:\n 'import hashlib\\n' +\n 'import hmac\\n' +\n 'import base64\\n' +\n 'import time\\n\\n' +\n 'def verify_zyphr_webhook(payload: str, headers: dict, secret: str) -> bool:\\n' +\n ' msg_id = headers.get(\"webhook-id\", \"\")\\n' +\n ' timestamp = headers.get(\"webhook-timestamp\", \"\")\\n' +\n ' signature = headers.get(\"webhook-signature\", \"\")\\n\\n' +\n ' now = int(time.time())\\n' +\n ' if abs(now - int(timestamp)) > 300:\\n' +\n ' return False\\n\\n' +\n ' signed_content = f\"{msg_id}.{timestamp}.{payload}\"\\n' +\n ' secret_hex = secret.removeprefix(\"whsec_\")\\n' +\n ' secret_bytes = bytes.fromhex(secret_hex)\\n' +\n ' expected = base64.b64encode(\\n' +\n ' hmac.new(secret_bytes, signed_content.encode(), hashlib.sha256).digest()\\n' +\n ' ).decode()\\n\\n' +\n ' for sig in signature.split(\" \"):\\n' +\n ' sig_value = sig.removeprefix(\"v1,\")\\n' +\n ' if hmac.compare_digest(sig_value, expected):\\n' +\n ' return True\\n' +\n ' return False\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n frameworks: {\n flask: {\n channel: 'webhook',\n language: 'python',\n framework: 'flask',\n variant: 'webhook-handler',\n files: [\n {\n path: 'app/zyphr_webhook.py',\n purpose: 'Standard Webhooks signature verification helper',\n contents:\n 'import hashlib, hmac, base64, time\\n\\n' +\n 'def verify_zyphr_webhook(payload: str, headers, secret: str) -> bool:\\n' +\n ' msg_id = headers.get(\"webhook-id\", \"\")\\n' +\n ' timestamp = headers.get(\"webhook-timestamp\", \"\")\\n' +\n ' signature = headers.get(\"webhook-signature\", \"\")\\n' +\n ' if abs(int(time.time()) - int(timestamp)) > 300:\\n' +\n ' return False\\n' +\n ' signed = f\"{msg_id}.{timestamp}.{payload}\"\\n' +\n ' secret_bytes = bytes.fromhex(secret.removeprefix(\"whsec_\"))\\n' +\n ' expected = base64.b64encode(hmac.new(secret_bytes, signed.encode(), hashlib.sha256).digest()).decode()\\n' +\n ' return any(hmac.compare_digest(sig.removeprefix(\"v1,\"), expected) for sig in signature.split(\" \"))\\n',\n overwrite: false,\n },\n {\n path: 'app/routes/zyphr_webhook.py',\n purpose: 'Flask blueprint that verifies the webhook signature before processing',\n contents:\n 'import os, json\\n' +\n 'from flask import Blueprint, request, abort\\n' +\n 'from ..zyphr_webhook import verify_zyphr_webhook\\n\\n' +\n 'zyphr_webhook_bp = Blueprint(\"zyphr_webhook\", __name__)\\n\\n' +\n '@zyphr_webhook_bp.route(\"/webhooks/zyphr\", methods=[\"POST\"])\\n' +\n 'def handle():\\n' +\n ' payload = request.get_data(as_text=True)\\n' +\n ' if not verify_zyphr_webhook(payload, request.headers, os.environ[\"ZYPHR_WEBHOOK_SECRET\"]):\\n' +\n ' abort(401, \"invalid signature\")\\n' +\n ' event = json.loads(payload)\\n' +\n ' print(\"zyphr event\", event.get(\"type\"))\\n' +\n ' return \"\", 204\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n fastapi: {\n channel: 'webhook',\n language: 'python',\n framework: 'fastapi',\n variant: 'webhook-handler',\n files: [\n {\n path: 'app/routers/zyphr_webhook.py',\n purpose: 'FastAPI router that verifies the webhook signature before processing',\n contents:\n 'import os, json, hashlib, hmac, base64, time\\n' +\n 'from fastapi import APIRouter, Request, HTTPException\\n\\n' +\n 'router = APIRouter()\\n\\n' +\n 'def verify_zyphr_webhook(payload: str, headers, secret: str) -> bool:\\n' +\n ' msg_id = headers.get(\"webhook-id\", \"\")\\n' +\n ' timestamp = headers.get(\"webhook-timestamp\", \"\")\\n' +\n ' signature = headers.get(\"webhook-signature\", \"\")\\n' +\n ' if abs(int(time.time()) - int(timestamp)) > 300:\\n' +\n ' return False\\n' +\n ' signed = f\"{msg_id}.{timestamp}.{payload}\"\\n' +\n ' secret_bytes = bytes.fromhex(secret.removeprefix(\"whsec_\"))\\n' +\n ' expected = base64.b64encode(hmac.new(secret_bytes, signed.encode(), hashlib.sha256).digest()).decode()\\n' +\n ' return any(hmac.compare_digest(sig.removeprefix(\"v1,\"), expected) for sig in signature.split(\" \"))\\n\\n' +\n '@router.post(\"/webhooks/zyphr\")\\n' +\n 'async def handle(request: Request):\\n' +\n ' body = (await request.body()).decode()\\n' +\n ' if not verify_zyphr_webhook(body, request.headers, os.environ[\"ZYPHR_WEBHOOK_SECRET\"]):\\n' +\n ' raise HTTPException(status_code=401, detail=\"invalid signature\")\\n' +\n ' event = json.loads(body)\\n' +\n ' print(\"zyphr event\", event.get(\"type\"))\\n' +\n ' return {\"ok\": True}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n },\n ruby: {\n sdk: {\n channel: 'webhook',\n language: 'ruby',\n framework: null,\n variant: 'webhook-handler',\n files: [\n {\n path: 'app/services/zyphr_webhook.rb',\n purpose: 'Standard Webhooks signature verification helper',\n contents:\n \"require 'openssl'\\n\" +\n \"require 'base64'\\n\\n\" +\n 'class ZyphrWebhook\\n' +\n ' def self.verify(payload:, headers:, secret:)\\n' +\n \" msg_id = headers['webhook-id'].to_s\\n\" +\n \" timestamp = headers['webhook-timestamp'].to_i\\n\" +\n \" signatures = headers['webhook-signature'].to_s\\n\\n\" +\n ' return false if (Time.now.to_i - timestamp).abs > 300\\n\\n' +\n ' signed = \"#{msg_id}.#{timestamp}.#{payload}\"\\n' +\n \" hex = secret.start_with?('whsec_') ? secret[6..] : secret\\n\" +\n ' secret_bytes = [hex].pack(\\'H*\\')\\n' +\n \" expected = Base64.strict_encode64(OpenSSL::HMAC.digest('SHA256', secret_bytes, signed))\\n\\n\" +\n \" signatures.split(' ').any? do |sig|\\n\" +\n ' v = sig[3..]\\n' +\n ' v && Rack::Utils.secure_compare(v, expected)\\n' +\n ' end\\n' +\n ' end\\n' +\n 'end\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n frameworks: {\n rails: {\n channel: 'webhook',\n language: 'ruby',\n framework: 'rails',\n variant: 'webhook-handler',\n files: [\n {\n path: 'app/services/zyphr_webhook.rb',\n purpose: 'Standard Webhooks signature verification helper',\n contents:\n \"require 'openssl'\\n\" +\n \"require 'base64'\\n\\n\" +\n 'class ZyphrWebhook\\n' +\n ' def self.verify(payload:, headers:, secret:)\\n' +\n \" msg_id = headers['webhook-id'].to_s\\n\" +\n \" timestamp = headers['webhook-timestamp'].to_i\\n\" +\n \" signatures = headers['webhook-signature'].to_s\\n\" +\n ' return false if (Time.now.to_i - timestamp).abs > 300\\n' +\n ' signed = \"#{msg_id}.#{timestamp}.#{payload}\"\\n' +\n \" hex = secret.start_with?('whsec_') ? secret[6..] : secret\\n\" +\n ' secret_bytes = [hex].pack(\\'H*\\')\\n' +\n \" expected = Base64.strict_encode64(OpenSSL::HMAC.digest('SHA256', secret_bytes, signed))\\n\" +\n \" signatures.split(' ').any? { |sig| sig[3..] && ActiveSupport::SecurityUtils.secure_compare(sig[3..], expected) }\\n\" +\n ' end\\n' +\n 'end\\n',\n overwrite: false,\n },\n {\n path: 'app/controllers/zyphr_webhooks_controller.rb',\n purpose: 'Rails controller that verifies the webhook signature before processing',\n contents:\n 'class ZyphrWebhooksController < ApplicationController\\n' +\n ' skip_before_action :verify_authenticity_token\\n\\n' +\n ' def create\\n' +\n \" payload = request.raw_post\\n\" +\n \" secret = ENV.fetch('ZYPHR_WEBHOOK_SECRET')\\n\" +\n ' unless ZyphrWebhook.verify(payload: payload, headers: request.headers, secret: secret)\\n' +\n ' head :unauthorized and return\\n' +\n ' end\\n' +\n ' event = JSON.parse(payload)\\n' +\n \" Rails.logger.info(\\\"zyphr event #{event['type']}\\\")\\n\" +\n ' head :no_content\\n' +\n ' end\\n' +\n 'end\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: [\n ...NEXT_STEPS,\n \"Add route: post '/webhooks/zyphr', to: 'zyphr_webhooks#create'\",\n ],\n docsUrl: DOCS,\n },\n },\n },\n go: {\n sdk: {\n channel: 'webhook',\n language: 'go',\n framework: null,\n variant: 'webhook-handler',\n files: [\n {\n path: 'internal/zyphr/verify.go',\n purpose:\n 'Standard Webhooks signature verification helper (verbatim from docs).',\n contents:\n 'package zyphr\\n\\n' +\n 'import (\\n' +\n '\\t\"crypto/hmac\"\\n' +\n '\\t\"crypto/sha256\"\\n' +\n '\\t\"encoding/base64\"\\n' +\n '\\t\"encoding/hex\"\\n' +\n '\\t\"math\"\\n' +\n '\\t\"strconv\"\\n' +\n '\\t\"strings\"\\n' +\n '\\t\"time\"\\n' +\n ')\\n\\n' +\n 'func VerifyWebhook(payload, msgID, timestamp, signature, secret string) bool {\\n' +\n '\\tts, err := strconv.ParseInt(timestamp, 10, 64)\\n' +\n '\\tif err != nil { return false }\\n' +\n '\\tif math.Abs(float64(time.Now().Unix()-ts)) > 300 { return false }\\n\\n' +\n '\\tsigned := msgID + \".\" + timestamp + \".\" + payload\\n' +\n '\\tsecretHex := strings.TrimPrefix(secret, \"whsec_\")\\n' +\n '\\tsecretBytes, err := hex.DecodeString(secretHex)\\n' +\n '\\tif err != nil { return false }\\n' +\n '\\tmac := hmac.New(sha256.New, secretBytes)\\n' +\n '\\tmac.Write([]byte(signed))\\n' +\n '\\texpected := base64.StdEncoding.EncodeToString(mac.Sum(nil))\\n\\n' +\n '\\tfor _, sig := range strings.Split(signature, \" \") {\\n' +\n '\\t\\tv := strings.TrimPrefix(sig, \"v1,\")\\n' +\n '\\t\\tif hmac.Equal([]byte(v), []byte(expected)) { return true }\\n' +\n '\\t}\\n' +\n '\\treturn false\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n php: {\n sdk: {\n channel: 'webhook',\n language: 'php',\n framework: null,\n variant: 'webhook-handler',\n files: [\n {\n path: 'app/Webhooks/ZyphrWebhook.php',\n purpose: 'Standard Webhooks signature verification helper (verbatim from docs)',\n contents:\n '<?php\\n\\n' +\n 'namespace App\\\\Webhooks;\\n\\n' +\n 'class ZyphrWebhook\\n' +\n '{\\n' +\n ' public static function verify(string $payload, array $headers, string $secret): bool\\n' +\n ' {\\n' +\n \" $msgId = $headers['webhook-id'] ?? '';\\n\" +\n \" $timestamp = $headers['webhook-timestamp'] ?? '';\\n\" +\n \" $signature = $headers['webhook-signature'] ?? '';\\n\" +\n ' if (abs(time() - intval($timestamp)) > 300) {\\n' +\n ' return false;\\n' +\n ' }\\n' +\n ' $signed = \"{$msgId}.{$timestamp}.{$payload}\";\\n' +\n \" $hex = str_starts_with($secret, 'whsec_') ? substr($secret, 6) : $secret;\\n\" +\n \" $expected = base64_encode(hash_hmac('sha256', $signed, hex2bin($hex), true));\\n\" +\n \" foreach (explode(' ', $signature) as $sig) {\\n\" +\n ' if (hash_equals(substr($sig, 3), $expected)) {\\n' +\n ' return true;\\n' +\n ' }\\n' +\n ' }\\n' +\n ' return false;\\n' +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n frameworks: {\n laravel: {\n channel: 'webhook',\n language: 'php',\n framework: 'laravel',\n variant: 'webhook-handler',\n files: [\n {\n path: 'app/Webhooks/ZyphrWebhook.php',\n purpose: 'Standard Webhooks signature verification helper',\n contents:\n '<?php\\n\\n' +\n 'namespace App\\\\Webhooks;\\n\\n' +\n 'class ZyphrWebhook\\n' +\n '{\\n' +\n ' public static function verify(string $payload, array $headers, string $secret): bool\\n' +\n ' {\\n' +\n \" $msgId = $headers['webhook-id'][0] ?? '';\\n\" +\n \" $timestamp = $headers['webhook-timestamp'][0] ?? '';\\n\" +\n \" $signature = $headers['webhook-signature'][0] ?? '';\\n\" +\n ' if (abs(time() - intval($timestamp)) > 300) {\\n' +\n ' return false;\\n' +\n ' }\\n' +\n ' $signed = \"{$msgId}.{$timestamp}.{$payload}\";\\n' +\n \" $hex = str_starts_with($secret, 'whsec_') ? substr($secret, 6) : $secret;\\n\" +\n \" $expected = base64_encode(hash_hmac('sha256', $signed, hex2bin($hex), true));\\n\" +\n \" foreach (explode(' ', $signature) as $sig) {\\n\" +\n ' if (hash_equals(substr($sig, 3), $expected)) {\\n' +\n ' return true;\\n' +\n ' }\\n' +\n ' }\\n' +\n ' return false;\\n' +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n {\n path: 'app/Http/Controllers/ZyphrWebhookController.php',\n purpose: 'Laravel controller that verifies the webhook signature before processing',\n contents:\n '<?php\\n\\n' +\n 'namespace App\\\\Http\\\\Controllers;\\n\\n' +\n 'use App\\\\Webhooks\\\\ZyphrWebhook;\\n' +\n 'use Illuminate\\\\Http\\\\Request;\\n\\n' +\n 'class ZyphrWebhookController extends Controller\\n' +\n '{\\n' +\n ' public function handle(Request $request)\\n' +\n ' {\\n' +\n \" $payload = $request->getContent();\\n\" +\n \" $secret = config('services.zyphr.webhook_secret');\\n\" +\n \" if (! ZyphrWebhook::verify($payload, $request->headers->all(), $secret)) {\\n\" +\n \" return response('invalid signature', 401);\\n\" +\n ' }\\n' +\n \" $event = json_decode($payload, true);\\n\" +\n \" \\\\Log::info('zyphr event ' . ($event['type'] ?? 'unknown'));\\n\" +\n \" return response()->noContent();\\n\" +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: [\n ...NEXT_STEPS,\n \"Register route in routes/api.php: Route::post('/webhooks/zyphr', [ZyphrWebhookController::class, 'handle']);\",\n \"Exclude this route from CSRF (VerifyCsrfToken::$except).\",\n ],\n docsUrl: DOCS,\n },\n },\n },\n csharp: {\n sdk: {\n channel: 'webhook',\n language: 'csharp',\n framework: null,\n variant: 'webhook-handler',\n files: [\n {\n path: 'Webhooks/ZyphrWebhookVerifier.cs',\n purpose: 'Standard Webhooks signature verification helper',\n contents:\n 'using System.Security.Cryptography;\\n' +\n 'using System.Text;\\n\\n' +\n 'namespace YourApp.Webhooks;\\n\\n' +\n 'public static class ZyphrWebhookVerifier\\n' +\n '{\\n' +\n ' public static bool Verify(string payload, IDictionary<string,string> headers, string secret)\\n' +\n ' {\\n' +\n ' var msgId = headers.TryGetValue(\"webhook-id\", out var id) ? id : \"\";\\n' +\n ' var timestamp = headers.TryGetValue(\"webhook-timestamp\", out var ts) ? long.Parse(ts) : 0;\\n' +\n ' var signatures = headers.TryGetValue(\"webhook-signature\", out var sig) ? sig : \"\";\\n\\n' +\n ' var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();\\n' +\n ' if (Math.Abs(now - timestamp) > 300) return false;\\n\\n' +\n ' var signed = $\"{msgId}.{timestamp}.{payload}\";\\n' +\n ' var hex = secret.StartsWith(\"whsec_\") ? secret[6..] : secret;\\n' +\n ' var secretBytes = Convert.FromHexString(hex);\\n' +\n ' using var hmac = new HMACSHA256(secretBytes);\\n' +\n ' var expected = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(signed)));\\n\\n' +\n ' foreach (var s in signatures.Split(\\' \\'))\\n' +\n ' {\\n' +\n ' var v = s.Length > 3 ? s[3..] : \"\";\\n' +\n ' if (CryptographicOperations.FixedTimeEquals(Encoding.UTF8.GetBytes(v), Encoding.UTF8.GetBytes(expected)))\\n' +\n ' return true;\\n' +\n ' }\\n' +\n ' return false;\\n' +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n frameworks: {\n aspnetcore: {\n channel: 'webhook',\n language: 'csharp',\n framework: 'aspnetcore',\n variant: 'webhook-handler',\n files: [\n {\n path: 'Webhooks/ZyphrWebhookVerifier.cs',\n purpose: 'Standard Webhooks signature verification helper',\n contents:\n 'using System.Security.Cryptography;\\n' +\n 'using System.Text;\\n\\n' +\n 'namespace YourApp.Webhooks;\\n\\n' +\n 'public static class ZyphrWebhookVerifier\\n' +\n '{\\n' +\n ' public static bool Verify(string payload, IHeaderDictionary headers, string secret)\\n' +\n ' {\\n' +\n ' string msgId = headers[\"webhook-id\"].ToString();\\n' +\n ' long timestamp = long.TryParse(headers[\"webhook-timestamp\"], out var t) ? t : 0;\\n' +\n ' string signatures = headers[\"webhook-signature\"].ToString();\\n' +\n ' if (Math.Abs(DateTimeOffset.UtcNow.ToUnixTimeSeconds() - timestamp) > 300) return false;\\n' +\n ' var signed = $\"{msgId}.{timestamp}.{payload}\";\\n' +\n ' var hex = secret.StartsWith(\"whsec_\") ? secret[6..] : secret;\\n' +\n ' using var hmac = new HMACSHA256(Convert.FromHexString(hex));\\n' +\n ' var expected = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(signed)));\\n' +\n ' foreach (var s in signatures.Split(\\' \\'))\\n' +\n ' {\\n' +\n ' var v = s.Length > 3 ? s[3..] : \"\";\\n' +\n ' if (CryptographicOperations.FixedTimeEquals(Encoding.UTF8.GetBytes(v), Encoding.UTF8.GetBytes(expected)))\\n' +\n ' return true;\\n' +\n ' }\\n' +\n ' return false;\\n' +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n {\n path: 'Controllers/ZyphrWebhookController.cs',\n purpose: 'ASP.NET Core controller that verifies the webhook signature before processing',\n contents:\n 'using Microsoft.AspNetCore.Mvc;\\n' +\n 'using YourApp.Webhooks;\\n\\n' +\n '[ApiController]\\n' +\n '[Route(\"webhooks/zyphr\")]\\n' +\n 'public class ZyphrWebhookController : ControllerBase\\n' +\n '{\\n' +\n ' [HttpPost]\\n' +\n ' public async Task<IActionResult> Handle()\\n' +\n ' {\\n' +\n ' using var reader = new StreamReader(Request.Body);\\n' +\n ' var payload = await reader.ReadToEndAsync();\\n' +\n ' var secret = Environment.GetEnvironmentVariable(\"ZYPHR_WEBHOOK_SECRET\")!;\\n' +\n ' if (!ZyphrWebhookVerifier.Verify(payload, Request.Headers, secret))\\n' +\n ' return Unauthorized(\"invalid signature\");\\n' +\n ' Console.WriteLine($\"zyphr event {payload[..Math.Min(80, payload.Length)]}\");\\n' +\n ' return NoContent();\\n' +\n ' }\\n' +\n '}\\n',\n overwrite: false,\n },\n ],\n envVarsNeeded: ENV,\n nextSteps: NEXT_STEPS,\n docsUrl: DOCS,\n },\n },\n },\n};\n","import type { SdkLanguage, QuickstartChannel } from '../../schemas.js';\nimport type { QuickstartChannelMap, QuickstartResult } from '../quickstart-types.js';\nimport { emailChannel } from './email.js';\nimport { inboxChannel } from './inbox.js';\nimport { pushChannel } from './push.js';\nimport { smsChannel } from './sms.js';\nimport { webhookChannel } from './webhook.js';\n\nconst REGISTRY: Record<QuickstartChannel, QuickstartChannelMap> = {\n email: emailChannel,\n push: pushChannel,\n sms: smsChannel,\n inbox: inboxChannel,\n webhook: webhookChannel,\n};\n\nexport interface ResolveQuickstartArgs {\n channel: QuickstartChannel;\n language: SdkLanguage;\n framework?: string;\n}\n\nexport interface ResolveQuickstartResult {\n result: QuickstartResult;\n frameworkRecognized: boolean;\n}\n\nexport function resolveQuickstart(\n args: ResolveQuickstartArgs,\n): ResolveQuickstartResult | null {\n const langMap = REGISTRY[args.channel]?.[args.language];\n if (!langMap) return null;\n\n if (args.framework) {\n const key = args.framework.trim().toLowerCase();\n const fw = langMap.frameworks?.[key];\n if (fw) return { result: fw, frameworkRecognized: true };\n // unknown framework → fall back to plain SDK variant, signal recognition status\n return { result: langMap.sdk, frameworkRecognized: false };\n }\n\n return { result: langMap.sdk, frameworkRecognized: true };\n}\n\nexport const QUICKSTART_REGISTRY = REGISTRY;\n","// TODO(v0.3): auto-generate these snippets from packages/sdk/<lang>/examples/\n// so docs and MCP output never drift. For v0.2 they are hand-maintained and\n// must mirror apps/docs/docs/sdks/<lang>.md exactly.\nimport type { SdkLanguage } from '../schemas.js';\n\nexport interface InstallCommand {\n manager: string;\n command: string;\n}\n\nexport interface InitSnippet {\n imports: string;\n init: string;\n fileExample: string;\n}\n\nexport type SdkKind = 'sdk' | 'rest-client';\n\nexport interface SdkInstallEntry {\n language: SdkLanguage;\n kind: SdkKind;\n packageName: string;\n registry: string;\n registryUrl: string;\n installCommands: InstallCommand[];\n initSnippet: InitSnippet;\n envVarsNeeded: string[];\n docsUrl: string;\n notes?: string;\n}\n\nconst DOCS = 'https://docs.zyphr.dev/sdks';\n\nexport const SDK_INSTALL_TABLE: Record<SdkLanguage, SdkInstallEntry> = {\n node: {\n language: 'node',\n kind: 'sdk',\n packageName: '@zyphr-dev/node-sdk',\n registry: 'npm',\n registryUrl: 'https://www.npmjs.com/package/@zyphr-dev/node-sdk',\n installCommands: [\n { manager: 'npm', command: 'npm install @zyphr-dev/node-sdk' },\n { manager: 'yarn', command: 'yarn add @zyphr-dev/node-sdk' },\n { manager: 'pnpm', command: 'pnpm add @zyphr-dev/node-sdk' },\n ],\n initSnippet: {\n imports: \"import { Zyphr } from '@zyphr-dev/node-sdk';\",\n init: \"export const zyphr = new Zyphr({ apiKey: process.env.ZYPHR_API_KEY! });\",\n fileExample: 'src/lib/zyphr.ts',\n },\n envVarsNeeded: ['ZYPHR_API_KEY'],\n docsUrl: `${DOCS}/node`,\n },\n csharp: {\n language: 'csharp',\n kind: 'sdk',\n packageName: 'ZyphrDev.SDK',\n registry: 'NuGet',\n registryUrl: 'https://www.nuget.org/packages/ZyphrDev.SDK',\n installCommands: [\n { manager: 'dotnet', command: 'dotnet add package ZyphrDev.SDK' },\n { manager: 'nuget', command: 'Install-Package ZyphrDev.SDK' },\n ],\n initSnippet: {\n imports: [\n 'using ZyphrDev.SDK.Api;',\n 'using ZyphrDev.SDK.Client;',\n 'using ZyphrDev.SDK.Model;',\n ].join('\\n'),\n init: [\n 'var config = new Configuration',\n '{',\n ' ApiKey = new Dictionary<string, string>',\n ' {',\n ' { \"X-API-Key\", Environment.GetEnvironmentVariable(\"ZYPHR_API_KEY\")! }',\n ' }',\n '};',\n 'var emails = new EmailsApi(config);',\n ].join('\\n'),\n fileExample: 'Services/ZyphrClient.cs',\n },\n envVarsNeeded: ['ZYPHR_API_KEY'],\n docsUrl: `${DOCS}/csharp`,\n },\n ruby: {\n language: 'ruby',\n kind: 'sdk',\n packageName: 'zyphr',\n registry: 'RubyGems',\n registryUrl: 'https://rubygems.org/gems/zyphr',\n installCommands: [\n { manager: 'gem', command: 'gem install zyphr' },\n { manager: 'bundler', command: \"bundle add zyphr\" },\n ],\n initSnippet: {\n imports: \"require 'zyphr'\",\n init: [\n 'Zyphr.configure do |config|',\n \" config.api_key['X-API-Key'] = ENV.fetch('ZYPHR_API_KEY')\",\n 'end',\n ].join('\\n'),\n fileExample: 'config/initializers/zyphr.rb',\n },\n envVarsNeeded: ['ZYPHR_API_KEY'],\n docsUrl: `${DOCS}/ruby`,\n },\n python: {\n language: 'python',\n kind: 'rest-client',\n packageName: 'requests',\n registry: 'PyPI',\n registryUrl: 'https://pypi.org/project/requests/',\n installCommands: [\n { manager: 'pip', command: 'pip install requests' },\n { manager: 'poetry', command: 'poetry add requests' },\n { manager: 'uv', command: 'uv add requests' },\n ],\n initSnippet: {\n imports: 'import os\\nimport requests',\n init: [\n 'ZYPHR_API_KEY = os.environ[\"ZYPHR_API_KEY\"]',\n 'BASE_URL = \"https://api.zyphr.dev/v1\"',\n '',\n 'headers = {',\n ' \"X-API-Key\": ZYPHR_API_KEY,',\n ' \"Content-Type\": \"application/json\",',\n '}',\n '',\n 'def zyphr_request(method, path, json=None, params=None):',\n ' response = requests.request(',\n ' method, f\"{BASE_URL}{path}\", headers=headers, json=json, params=params,',\n ' )',\n ' response.raise_for_status()',\n ' return response.json()',\n ].join('\\n'),\n fileExample: 'app/zyphr_client.py',\n },\n envVarsNeeded: ['ZYPHR_API_KEY'],\n docsUrl: `${DOCS}/python`,\n notes:\n 'There is no official Zyphr Python SDK yet — the canonical integration is a thin REST wrapper around the requests library.',\n },\n go: {\n language: 'go',\n kind: 'rest-client',\n packageName: 'net/http (stdlib)',\n registry: 'stdlib',\n registryUrl: 'https://pkg.go.dev/net/http',\n installCommands: [\n { manager: 'go', command: '# No install needed — net/http ships with Go.' },\n ],\n initSnippet: {\n imports: [\n 'package zyphr',\n '',\n 'import (',\n '\\t\"bytes\"',\n '\\t\"encoding/json\"',\n '\\t\"fmt\"',\n '\\t\"io\"',\n '\\t\"net/http\"',\n '\\t\"os\"',\n ')',\n ].join('\\n'),\n init: [\n 'const baseURL = \"https://api.zyphr.dev/v1\"',\n '',\n 'type Client struct {',\n '\\tAPIKey string',\n '\\tHTTPClient *http.Client',\n '}',\n '',\n 'func NewClient() *Client {',\n '\\treturn &Client{APIKey: os.Getenv(\"ZYPHR_API_KEY\"), HTTPClient: &http.Client{}}',\n '}',\n '',\n 'func (c *Client) Do(method, path string, body any) ([]byte, error) {',\n '\\tvar buf io.Reader',\n '\\tif body != nil {',\n '\\t\\tb, err := json.Marshal(body)',\n '\\t\\tif err != nil { return nil, fmt.Errorf(\"marshal: %w\", err) }',\n '\\t\\tbuf = bytes.NewReader(b)',\n '\\t}',\n '\\treq, err := http.NewRequest(method, baseURL+path, buf)',\n '\\tif err != nil { return nil, err }',\n '\\treq.Header.Set(\"X-API-Key\", c.APIKey)',\n '\\treq.Header.Set(\"Content-Type\", \"application/json\")',\n '\\tresp, err := c.HTTPClient.Do(req)',\n '\\tif err != nil { return nil, err }',\n '\\tdefer resp.Body.Close()',\n '\\tdata, _ := io.ReadAll(resp.Body)',\n '\\tif resp.StatusCode >= 400 { return nil, fmt.Errorf(\"zyphr %d: %s\", resp.StatusCode, data) }',\n '\\treturn data, nil',\n '}',\n ].join('\\n'),\n fileExample: 'internal/zyphr/client.go',\n },\n envVarsNeeded: ['ZYPHR_API_KEY'],\n docsUrl: `${DOCS}/go`,\n notes:\n 'There is no official Zyphr Go SDK yet — the canonical integration is a thin REST wrapper around net/http.',\n },\n php: {\n language: 'php',\n kind: 'rest-client',\n packageName: 'guzzlehttp/guzzle',\n registry: 'Packagist',\n registryUrl: 'https://packagist.org/packages/guzzlehttp/guzzle',\n installCommands: [\n { manager: 'composer', command: 'composer require guzzlehttp/guzzle' },\n ],\n initSnippet: {\n imports: [\n '<?php',\n '',\n 'use GuzzleHttp\\\\Client;',\n ].join('\\n'),\n init: [\n '$client = new Client([',\n \" 'base_uri' => 'https://api.zyphr.dev/v1/',\",\n \" 'headers' => [\",\n \" 'X-API-Key' => getenv('ZYPHR_API_KEY'),\",\n \" 'Content-Type' => 'application/json',\",\n ' ],',\n ']);',\n ].join('\\n'),\n fileExample: 'app/Services/ZyphrClient.php',\n },\n envVarsNeeded: ['ZYPHR_API_KEY'],\n docsUrl: `${DOCS}/php`,\n notes:\n 'There is no official Zyphr PHP SDK yet — the canonical integration uses Guzzle (or raw cURL).',\n },\n};\n\nexport function resolveInstallEntry(\n language: SdkLanguage,\n packageManager?: string,\n): SdkInstallEntry {\n const entry = SDK_INSTALL_TABLE[language];\n if (!packageManager) return entry;\n\n const normalized = packageManager.trim().toLowerCase();\n const match = entry.installCommands.find((c) => c.manager.toLowerCase() === normalized);\n if (!match) return entry;\n\n return { ...entry, installCommands: [match] };\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { isToolEnabled, type ToolGuards } from '../config.js';\nimport { resolveQuickstart } from '../integration/quickstart/index.js';\nimport { resolveInstallEntry } from '../integration/sdk-snippets.js';\nimport { toolResult } from '../result.js';\nimport { getQuickstartShape, getSdkInstallShape } from '../schemas.js';\n\nexport function registerIntegrationTools(server: McpServer, guards: ToolGuards): void {\n if (isToolEnabled({ name: 'get_sdk_install_for_language', mutates: false }, guards)) {\n server.registerTool(\n 'get_sdk_install_for_language',\n {\n title: 'Get SDK install instructions for a language',\n description:\n 'Returns install commands, init snippet, env vars, and docs URL for the chosen language. Use this when wiring Zyphr into a new project so the AI can drop the correct package + client init code.',\n inputSchema: getSdkInstallShape,\n },\n async (args) => {\n const entry = resolveInstallEntry(args.language, args.packageManager);\n return toolResult(entry);\n },\n );\n }\n\n if (isToolEnabled({ name: 'get_quickstart_for_channel', mutates: false }, guards)) {\n server.registerTool(\n 'get_quickstart_for_channel',\n {\n title: 'Get quickstart code for a channel + language',\n description:\n 'Returns drop-in service file(s) for the chosen Zyphr channel (email/push/sms/inbox/webhook), language, and optional framework (express, nextjs, flask, fastapi, rails, laravel, aspnetcore). Webhook handlers ALWAYS verify HMAC signatures. Unknown frameworks fall back to plain SDK code.',\n inputSchema: getQuickstartShape,\n },\n async (args) => {\n const resolved = resolveQuickstart({\n channel: args.channel,\n language: args.language,\n framework: args.framework,\n });\n if (!resolved) {\n return toolResult({\n error: `No quickstart available for channel=${args.channel} language=${args.language}`,\n });\n }\n const { result, frameworkRecognized } = resolved;\n return toolResult({\n ...result,\n frameworkRecognized,\n requestedFramework: args.framework ?? null,\n });\n },\n );\n }\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { getZyphrClient } from '../client.js';\nimport { isToolEnabled, type ToolGuards } from '../config.js';\nimport { runTool } from '../result.js';\nimport {\n createWebhookShape,\n getWebhookDeliveriesShape,\n listWebhooksShape,\n} from '../schemas.js';\n\nexport function registerWebhookTools(server: McpServer, guards: ToolGuards): void {\n if (isToolEnabled({ name: 'list_webhooks', mutates: false }, guards)) {\n server.registerTool(\n 'list_webhooks',\n {\n title: 'List webhooks',\n description: 'List webhook endpoints configured for the account.',\n inputSchema: listWebhooksShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.webhooks.listWebhooks(args.limit, args.offset);\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'create_webhook', mutates: true }, guards)) {\n server.registerTool(\n 'create_webhook',\n {\n title: 'Create webhook',\n description:\n 'Register a new webhook endpoint. Subscribe to event types like \"email.*\", \"subscriber.created\", or \"*\".',\n inputSchema: createWebhookShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.webhooks.createWebhook({\n url: args.url,\n events: args.events,\n description: args.description,\n secret: args.secret,\n metadata: args.metadata,\n headers: args.headers,\n version: args.version,\n rateLimit: args.rateLimit,\n });\n });\n },\n );\n }\n\n if (isToolEnabled({ name: 'get_webhook_deliveries', mutates: false }, guards)) {\n server.registerTool(\n 'get_webhook_deliveries',\n {\n title: 'List webhook deliveries',\n description:\n 'Inspect delivery history for a webhook endpoint. Filter by status (pending/delivering/delivered/failed/exhausted), event type, or date range. Useful for debugging why a webhook is not firing.',\n inputSchema: getWebhookDeliveriesShape,\n },\n async (args) => {\n return runTool(async () => {\n const zyphr = getZyphrClient();\n return await zyphr.webhooks.listWebhookDeliveries(\n args.webhookId,\n args.status,\n args.eventType,\n args.search,\n args.startDate ? new Date(args.startDate) : undefined,\n args.endDate ? new Date(args.endDate) : undefined,\n args.limit,\n args.offset,\n );\n });\n },\n );\n }\n}\n"],"mappings":";;;AAAA,SAAS,4BAA4B;;;ACArC,SAAS,iBAAiB;;;ACKnB,SAAS,iBAA6B;AAC3C,QAAM,WAAW,QAAQ,IAAI,oBAAoB,UAAU,QAAQ,IAAI,oBAAoB;AAC3F,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,eAAe,MACjB,IAAI;AAAA,IACF,IACG,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,OAAO;AAAA,EACnB,IACA;AACJ,SAAO,EAAE,UAAU,aAAa;AAClC;AAOO,SAAS,cAAc,MAAsB,QAA6B;AAC/E,MAAI,OAAO,cAAc;AACvB,WAAO,OAAO,aAAa,IAAI,KAAK,IAAI;AAAA,EAC1C;AACA,MAAI,OAAO,YAAY,KAAK,SAAS;AACnC,WAAO;AAAA,EACT;AACA,SAAO;AACT;;;AChCA,SAAS,aAAa;AAEtB,IAAM,mBAAmB;AAEzB,IAAI;AAEG,SAAS,iBAAwB;AACtC,MAAI,OAAQ,QAAO;AAEnB,QAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,CAAC,QAAQ;AACX,YAAQ,OAAO;AAAA,MACb;AAAA,IACF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,UAAU,QAAQ,IAAI,kBAAkB;AAC9C,WAAS,IAAI,MAAM,EAAE,QAAQ,QAAQ,CAAC;AACtC,SAAO;AACT;AAEO,SAAS,aAAqB;AACnC,SAAO,QAAQ,IAAI,kBAAkB;AACvC;;;ACvBA,SAAS,YAAY,2BAA2B;AAEzC,SAAS,WAAW,MAA+B;AACxD,SAAO;AAAA,IACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAU,MAAM,MAAM,CAAC,EAAE,CAAC;AAAA,EACjE;AACF;AAEA,eAAsB,QAAQ,IAAqD;AACjF,MAAI;AACF,UAAM,OAAO,MAAM,GAAG;AACtB,WAAO,WAAW,IAAI;AAAA,EACxB,SAAS,KAAc;AACrB,WAAO,MAAM,eAAe,GAAG;AAAA,EACjC;AACF;AAEA,SAAS,aAAa,SAAkD;AACtE,SAAO;AAAA,IACL,SAAS;AAAA,IACT,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAU,SAAS,MAAM,CAAC,EAAE,CAAC;AAAA,EACpE;AACF;AAEA,eAAe,eAAe,KAAuC;AAEnE,MAAI,eAAe,YAAY;AAC7B,UAAM,UAAmC;AAAA,MACvC,MAAM,IAAI;AAAA,MACV,SAAS,IAAI;AAAA,MACb,QAAQ,IAAI;AAAA,IACd;AACA,QAAI,IAAI,KAAM,SAAQ,OAAO,IAAI;AACjC,QAAI,IAAI,UAAW,SAAQ,YAAY,IAAI;AAC3C,QAAI,IAAI,QAAS,SAAQ,UAAU,IAAI;AACvC,QAAI,eAAe,uBAAuB,IAAI,eAAe,QAAW;AACtE,cAAQ,aAAa,IAAI;AAAA,IAC3B;AACA,WAAO,aAAa,OAAO;AAAA,EAC7B;AAGA,MAAI,OAAO,OAAO,QAAQ,YAAY,cAAc,KAAK;AACvD,UAAM,WAAY,IAAgC;AAClD,QAAI,YAAY,OAAO,SAAS,SAAS,YAAY;AACnD,UAAI;AACF,cAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAI,SAAkB;AACtB,YAAI;AACF,mBAAS,KAAK,MAAM,IAAI;AAAA,QAC1B,QAAQ;AAAA,QAER;AACA,eAAO,aAAa,EAAE,QAAQ,SAAS,QAAQ,MAAM,OAAO,CAAC;AAAA,MAC/D,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAEA,QAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,QAAM,OAAO,eAAe,QAAQ,IAAI,OAAO;AAC/C,SAAO,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvC;;;AChEA,SAAS,SAAS;AAElB,IAAM,eAAe,EAAE,OAAO;AAAA,EAC5B,OAAO,EAAE,OAAO,EAAE,MAAM;AAAA,EACxB,MAAM,EAAE,OAAO,EAAE,SAAS;AAC5B,CAAC;AAED,IAAM,YAAY,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,YAAY,CAAC;AAErD,IAAM,iBAAiB;AAAA,EAC5B,IAAI,EACD,MAAM,CAAC,WAAW,EAAE,MAAM,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC,EAC5C,SAAS,sEAAsE;AAAA,EAClF,MAAM,EACH,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,YAAY,CAAC,EACxC,SAAS,EACT,SAAS,+DAA+D;AAAA,EAC3E,SAAS,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,YAAY,CAAC,EAAE,SAAS;AAAA,EAC9D,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,SAAS;AAAA,EACzC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,SAAS;AAAA,EAC1C,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACzB,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,yDAAyD;AAAA,EAC9F,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,sDAAsD;AAAA,EAC3F,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,+CAA+C;AAAA,EAC1F,cAAc,EACX,OAAO,EAAE,QAAQ,CAAC,EAClB,SAAS,EACT,SAAS,4CAA4C;AAAA,EACxD,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EACnC,UAAU,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,SAAS;AAAA,EACzC,cAAc,EAAE,OAAO,EAAE,SAAS;AAAA,EAClC,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,EAC9B,aAAa,EACV,OAAO,EACP,SAAS,EACT,SAAS,EACT,SAAS,2CAA2C;AACzD;AAEO,IAAM,gBAAgB;AAAA,EAC3B,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,8CAA8C;AAAA,EACrF,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,gCAAgC;AAAA,EACzE,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,EAC3B,MAAM,EAAE,OAAO,EAAE,SAAS;AAAA,EAC1B,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,SAAS,EAAE,SAAS,6CAA6C;AAAA,EAC7F,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS;AAAA,EAC/C,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,EAC3B,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AAAA,EACpC,kBAAkB,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,wBAAwB;AAAA,EAC1E,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EACnC,UAAU,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,SAAS;AAAA,EACzC,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,EACjC,cAAc,EAAE,OAAO,EAAE,SAAS;AAAA,EAClC,sBAAsB,EAAE,OAAO,EAAE,SAAS;AAAA,EAC1C,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,EAC9B,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,mCAAmC;AAAA,EAC1E,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS;AACjD;AAEO,IAAM,eAAe;AAAA,EAC1B,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,4DAA4D;AAAA,EAC3F,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,kCAAkC;AAAA,EACvE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACtB,cAAc,EAAE,OAAO,EAAE,SAAS;AAAA,EAClC,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC5C,UAAU,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,SAAS;AAC3C;AAIO,IAAM,eAAe,CAAC,QAAQ,UAAU,QAAQ,MAAM,OAAO,QAAQ;AAGrE,IAAM,qBAAqB,CAAC,SAAS,QAAQ,OAAO,SAAS,SAAS;AAGtE,IAAM,qBAAqB;AAAA,EAChC,SAAS,EACN,KAAK,kBAAkB,EACvB,SAAS,gCAAgC;AAAA,EAC5C,UAAU,EACP,KAAK,CAAC,QAAQ,UAAU,QAAQ,MAAM,OAAO,QAAQ,CAAC,EACtD,SAAS,qCAAqC;AAAA,EACjD,WAAW,EACR,OAAO,EACP,SAAS,EACT;AAAA,IACC;AAAA,EACF;AACJ;AAEO,IAAM,qBAAqB;AAAA,EAChC,UAAU,EACP,KAAK,YAAY,EACjB,SAAS,qCAAqC;AAAA,EACjD,gBAAgB,EACb,OAAO,EACP,SAAS,EACT;AAAA,IACC;AAAA,EACF;AACJ;AAIO,IAAM,qBAAqB;AAAA,EAChC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,EACrD,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS;AAClD;AAEO,IAAM,mBAAmB;AAAA,EAC9B,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,aAAa;AAC9C;AAEO,IAAM,sBAAsB;AAAA,EACjC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,aAAa;AAAA,EAC5C,WAAW,EACR,OAAO,EAAE,QAAQ,CAAC,EAClB,SAAS,sDAAsD;AACpE;AAEO,IAAM,sBAAsB;AAAA,EACjC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACtB,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,EACjC,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,mCAAmC;AAAA,EAC3E,MAAM,EAAE,OAAO,EAAE,SAAS;AAAA,EAC1B,MAAM,EAAE,OAAO,EAAE,SAAS;AAC5B;AAIO,IAAM,sBAAsB;AAAA,EACjC,YAAY,EACT,OAAO,EACP,IAAI,CAAC,EACL,SAAS,4DAA4D;AAC1E;AAEO,IAAM,uBAAuB;AAAA,EAClC,QAAQ,EAAE,KAAK,CAAC,UAAU,UAAU,CAAC,EAAE,SAAS;AAAA,EAChD,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS;AAAA,EACnC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,EACrD,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS;AAClD;AAEO,IAAM,wBAAwB;AAAA,EACnC,YAAY,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,mCAAmC;AAAA,EAC1E,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS;AAAA,EACnC,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,EAC3B,MAAM,EAAE,OAAO,EAAE,SAAS;AAAA,EAC1B,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AAAA,EACrC,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,EAC9B,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,UAAU,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,SAAS;AAC3C;AAEO,IAAM,wBAAwB;AAAA,EACnC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,qBAAqB;AAAA,EACpD,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS;AAAA,EAC9C,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACtC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACrC,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS;AAAA,EAChD,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,EAC9B,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,UAAU,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,SAAS;AAAA,EACzC,QAAQ,EAAE,KAAK,CAAC,UAAU,UAAU,CAAC,EAAE,SAAS;AAClD;AAEO,IAAM,gCAAgC;AAAA,EAC3C,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,qBAAqB;AAAA,EACpD,aAAa,EACV;AAAA,IACC,EAAE,OAAO;AAAA,MACP,YAAY,EAAE,OAAO,EAAE,SAAS;AAAA,MAChC,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,6BAA6B;AAAA,MACrE,SAAS,EAAE,QAAQ,EAAE,SAAS;AAAA,IAChC,CAAC;AAAA,EACH,EACC,IAAI,CAAC;AACV;AAIO,IAAM,oBAAoB;AAAA,EAC/B,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,EACrD,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS;AAClD;AAEO,IAAM,qBAAqB;AAAA,EAChC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,6CAA6C;AAAA,EAC5E,QAAQ,EACL,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,EACvB,IAAI,CAAC,EACL,SAAS,sEAAsE;AAAA,EAClF,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,EACjC,QAAQ,EACL,OAAO,EACP,SAAS,EACT,SAAS,yEAAyE;AAAA,EACrF,UAAU,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,SAAS;AAAA,EACzC,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS,4CAA4C;AAAA,EAC9F,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,EAC7B,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS;AAClD;AAEO,IAAM,4BAA4B;AAAA,EACvC,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,qBAAqB;AAAA,EAC3D,QAAQ,EAAE,KAAK,CAAC,WAAW,cAAc,aAAa,UAAU,WAAW,CAAC,EAAE,SAAS;AAAA,EACvF,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,EAC/B,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC1C,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACxC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,EACrD,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS;AAClD;AAUO,IAAM,iBAAiB;AAAA,EAC5B,QAAQ,EACL,OAAO,EACP,IAAI,CAAC,EACL;AAAA,IACC;AAAA,EACF;AACJ;AAEO,IAAM,mBAAmB,CAAC;AAE1B,IAAM,iBAAiB;AAAA,EAC5B,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,kBAAkB;AACnD;AAEO,IAAM,oBAAoB;AAAA,EAC/B,IAAI,EACD,OAAO,EACP,IAAI,CAAC,EACL;AAAA,IACC;AAAA,EACF;AACJ;AAaO,IAAM,yBAAyB;AAAA,EACpC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,EACrD,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,4DAA4D;AAAA,EACnG,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,wCAAwC;AACjF;AAEO,IAAM,uBAAuB;AAAA,EAClC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,yBAAyB;AAC1D;AAEO,IAAM,0BAA0B;AAAA,EACrC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS,yBAAyB;AAAA,EACnE,MAAM,EACH,OAAO,EACP,IAAI,CAAC,EACL,IAAI,GAAG,EACP,SAAS,EACT,SAAS,kEAAkE;AAAA,EAC9E,UAAU,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,SAAS,EAAE,SAAS,0BAA0B;AAChF;AAEO,IAAM,0BAA0B;AAAA,EACrC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,yBAAyB;AAAA,EACxD,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,EAC1C,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,EAC1C,UAAU,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,SAAS;AAC3C;AAEO,IAAM,+BAA+B;AAAA,EAC1C,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,yBAAyB;AAAA,EACxD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,EACrD,QAAQ,EAAE,OAAO,EAAE,SAAS;AAC9B;AAEO,IAAM,6BAA6B;AAAA,EACxC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,yBAAyB;AAAA,EACxD,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,uCAAuC;AAAA,EAC1E,MAAM,EACH,OAAO,EACP,IAAI,CAAC,EACL,IAAI,EAAE,EACN,SAAS,EACT;AAAA,IACC;AAAA,EACF;AACJ;AAEO,IAAM,oCAAoC;AAAA,EAC/C,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,yBAAyB;AAAA,EACxD,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,qBAAqB;AAAA,EACxD,MAAM,EACH,OAAO,EACP,IAAI,CAAC,EACL,IAAI,EAAE,EACN,SAAS,kCAAkC;AAChD;AAEO,IAAM,gCAAgC;AAAA,EAC3C,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,yBAAyB;AAAA,EACxD,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,+BAA+B;AACpE;AAEO,IAAM,gCAAgC;AAAA,EAC3C,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,qBAAqB;AAC1D;AAEO,IAAM,wBAAwB;AAAA,EACnC,cAAc,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,6CAA6C;AAAA,EACtF,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACvB,MAAM,EAAE,OAAO,EAAE,SAAS;AAAA,EAC1B,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AAAA,EACrC,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,EACjC,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AAAA,EACpC,MAAM,EAAE,OAAO,EAAE,SAAS;AAAA,EAC1B,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,EAC9B,UAAU,EAAE,KAAK,CAAC,OAAO,UAAU,QAAQ,QAAQ,CAAC,EAAE,SAAS;AAAA,EAC/D,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,SAAS;AAAA,EACrC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EACnC,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAC5C;;;AC1TO,SAAS,oBAAoB,QAAmB,QAA0B;AAC/E,MAAI,cAAc,EAAE,MAAM,cAAc,SAAS,KAAK,GAAG,MAAM,GAAG;AAChE,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,aACE;AAAA,QACF,aAAa;AAAA,MACf;AAAA,MACA,OAAO,SAAS;AACd,eAAO,QAAQ,YAAY;AACzB,gBAAM,QAAQ,eAAe;AAC7B,iBAAO,MAAM,MAAM,QAAQ,UAAU,EAAE,QAAQ,KAAK,OAAO,CAAC;AAAA,QAC9D,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,MAAI,cAAc,EAAE,MAAM,gBAAgB,SAAS,MAAM,GAAG,MAAM,GAAG;AACnE,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,aACE;AAAA,QACF,aAAa;AAAA,MACf;AAAA,MACA,YAAY;AACV,eAAO,QAAQ,YAAY;AACzB,gBAAM,QAAQ,eAAe;AAC7B,iBAAO,MAAM,MAAM,QAAQ,YAAY;AAAA,QACzC,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,MAAI,cAAc,EAAE,MAAM,cAAc,SAAS,MAAM,GAAG,MAAM,GAAG;AACjE,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,aACE;AAAA,QACF,aAAa;AAAA,MACf;AAAA,MACA,OAAO,SAAS;AACd,eAAO,QAAQ,YAAY;AACzB,gBAAM,QAAQ,eAAe;AAC7B,iBAAO,MAAM,MAAM,QAAQ,UAAU,KAAK,EAAE;AAAA,QAC9C,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,MAAI,cAAc,EAAE,MAAM,iBAAiB,SAAS,KAAK,GAAG,MAAM,GAAG;AACnE,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,aACE;AAAA,QACF,aAAa;AAAA,MACf;AAAA,MACA,OAAO,SAAS;AACd,eAAO,QAAQ,YAAY;AACzB,gBAAM,QAAQ,eAAe;AAC7B,iBAAO,MAAM,MAAM,QAAQ,aAAa,KAAK,EAAE;AAAA,QACjD,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;;;ACvEO,SAAS,0BAA0B,QAAmB,QAA0B;AACrF,MAAI,cAAc,EAAE,MAAM,sBAAsB,SAAS,MAAM,GAAG,MAAM,GAAG;AACzE,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,aACE;AAAA,QACF,aAAa;AAAA,MACf;AAAA,MACA,OAAO,SAAS;AACd,eAAO,QAAQ,YAAY;AACzB,gBAAM,QAAQ,eAAe;AAC7B,iBAAO,MAAM,MAAM,KAAK,cAAc;AAAA,YACpC,KAAK;AAAA,YACL,KAAK;AAAA,YACL,KAAK;AAAA,UACP;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,MAAI,cAAc,EAAE,MAAM,oBAAoB,SAAS,MAAM,GAAG,MAAM,GAAG;AACvE,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,aAAa;AAAA,QACb,aAAa;AAAA,MACf;AAAA,MACA,OAAO,SAAS;AACd,eAAO,QAAQ,YAAY;AACzB,gBAAM,QAAQ,eAAe;AAC7B,iBAAO,MAAM,MAAM,KAAK,cAAc,gBAAgB,KAAK,EAAE;AAAA,QAC/D,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,MAAI,cAAc,EAAE,MAAM,uBAAuB,SAAS,KAAK,GAAG,MAAM,GAAG;AACzE,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,aACE;AAAA,QACF,aAAa;AAAA,MACf;AAAA,MACA,OAAO,SAAS;AACd,eAAO,QAAQ,YAAY;AACzB,gBAAM,QAAQ,eAAe;AAC7B,iBAAO,MAAM,MAAM,KAAK,cAAc,mBAAmB;AAAA,YACvD,MAAM,KAAK;AAAA,YACX,MAAM,KAAK;AAAA,YACX,UAAU,KAAK;AAAA,UACjB,CAAC;AAAA,QACH,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,MAAI,cAAc,EAAE,MAAM,uBAAuB,SAAS,KAAK,GAAG,MAAM,GAAG;AACzE,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,aAAa;AAAA,QACb,aAAa;AAAA,MACf;AAAA,MACA,OAAO,SAAS;AACd,eAAO,QAAQ,YAAY;AACzB,gBAAM,QAAQ,eAAe;AAC7B,iBAAO,MAAM,MAAM,KAAK,cAAc,mBAAmB,KAAK,IAAI;AAAA,YAChE,MAAM,KAAK;AAAA,YACX,MAAM,KAAK;AAAA,YACX,UAAU,KAAK;AAAA,UACjB,CAAC;AAAA,QACH,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,MAAI,cAAc,EAAE,MAAM,6BAA6B,SAAS,MAAM,GAAG,MAAM,GAAG;AAChF,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,aAAa;AAAA,QACb,aAAa;AAAA,MACf;AAAA,MACA,OAAO,SAAS;AACd,eAAO,QAAQ,YAAY;AACzB,gBAAM,QAAQ,eAAe;AAC7B,iBAAO,MAAM,MAAM,KAAK,cAAc;AAAA,YACpC,KAAK;AAAA,YACL,KAAK;AAAA,YACL,KAAK;AAAA,UACP;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,MAAI,cAAc,EAAE,MAAM,2BAA2B,SAAS,KAAK,GAAG,MAAM,GAAG;AAC7E,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,aACE;AAAA,QACF,aAAa;AAAA,MACf;AAAA,MACA,OAAO,SAAS;AACd,eAAO,QAAQ,YAAY;AACzB,gBAAM,QAAQ,eAAe;AAC7B,iBAAO,MAAM,MAAM,KAAK,cAAc,sBAAsB,KAAK,IAAI;AAAA,YACnE,QAAQ,KAAK;AAAA,YACb,MAAM,KAAK;AAAA,UACb,CAAC;AAAA,QACH,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,MAAI,cAAc,EAAE,MAAM,mCAAmC,SAAS,KAAK,GAAG,MAAM,GAAG;AACrF,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,aACE;AAAA,QACF,aAAa;AAAA,MACf;AAAA,MACA,OAAO,SAAS;AACd,eAAO,QAAQ,YAAY;AACzB,gBAAM,QAAQ,eAAe;AAC7B,iBAAO,MAAM,MAAM,KAAK,cAAc;AAAA,YACpC,KAAK;AAAA,YACL,KAAK;AAAA,YACL,EAAE,MAAM,KAAK,KAAK;AAAA,UACpB;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,MAAI,cAAc,EAAE,MAAM,8BAA8B,SAAS,KAAK,GAAG,MAAM,GAAG;AAChF,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,aACE;AAAA,QACF,aAAa;AAAA,MACf;AAAA,MACA,OAAO,SAAS;AACd,eAAO,QAAQ,YAAY;AACzB,gBAAM,QAAQ,eAAe;AAC7B,gBAAM,MAAM,KAAK,cAAc,yBAAyB,KAAK,IAAI,KAAK,MAAM;AAC5E,iBAAO,EAAE,IAAI,KAAK;AAAA,QACpB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,MAAI,cAAc,EAAE,MAAM,+BAA+B,SAAS,MAAM,GAAG,MAAM,GAAG;AAClF,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,aACE;AAAA,QACF,aAAa;AAAA,MACf;AAAA,MACA,OAAO,SAAS;AACd,eAAO,QAAQ,YAAY;AACzB,gBAAM,QAAQ,eAAe;AAC7B,iBAAO,MAAM,MAAM,KAAK,cAAc,4BAA4B,KAAK,MAAM;AAAA,QAC/E,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;;;ACtMA,SAAS,sBAAsB,OAA8D;AAC3F,MAAI,OAAO,UAAU,SAAU,QAAO,EAAE,OAAO,MAAM;AACrD,MAAI,SAAS,OAAO,UAAU,YAAY,WAAW,OAAO;AAC1D,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,oBAAoB,IAAiD;AAC5E,MAAI,MAAM,QAAQ,EAAE,GAAG;AACrB,WAAO,GAAG,IAAI,qBAAqB,EAAE,OAAO,CAAC,MAA8B,QAAQ,CAAC,CAAC;AAAA,EACvF;AACA,QAAM,MAAM,sBAAsB,EAAE;AACpC,SAAO,MAAM,CAAC,GAAG,IAAI,CAAC;AACxB;AAEO,SAAS,kBAAkB,QAAmB,QAA0B;AAC7E,MAAI,cAAc,EAAE,MAAM,cAAc,SAAS,KAAK,GAAG,MAAM,GAAG;AAChE,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,aACE;AAAA,QACF,aAAa;AAAA,MACf;AAAA,MACA,OAAO,SAAS;AACd,eAAO,QAAQ,YAAY;AACzB,gBAAM,QAAQ,eAAe;AAC7B,iBAAO,MAAM,MAAM,OAAO,UAAU;AAAA,YAClC,IAAI,oBAAoB,KAAK,EAAE;AAAA,YAC/B,MAAM,sBAAsB,KAAK,IAAI;AAAA,YACrC,SAAS,sBAAsB,KAAK,OAAO;AAAA,YAC3C,IAAI,KAAK;AAAA,YACT,KAAK,KAAK;AAAA,YACV,SAAS,KAAK;AAAA,YACd,MAAM,KAAK;AAAA,YACX,MAAM,KAAK;AAAA,YACX,YAAY,KAAK;AAAA,YACjB,cAAc,KAAK;AAAA,YACnB,MAAM,KAAK;AAAA,YACX,UAAU,KAAK;AAAA,YACf,cAAc,KAAK;AAAA,YACnB,UAAU,KAAK;AAAA,YACf,aAAa,KAAK,cAAc,IAAI,KAAK,KAAK,WAAW,IAAI;AAAA,UAC/D,CAAC;AAAA,QACH,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,MAAI,cAAc,EAAE,MAAM,aAAa,SAAS,KAAK,GAAG,MAAM,GAAG;AAC/D,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,aACE;AAAA,QACF,aAAa;AAAA,MACf;AAAA,MACA,OAAO,SAAS;AACd,eAAO,QAAQ,YAAY;AACzB,gBAAM,QAAQ,eAAe;AAC7B,iBAAO,MAAM,MAAM,KAAK,SAAS;AAAA,YAC/B,QAAQ,KAAK;AAAA,YACb,UAAU,KAAK;AAAA,YACf,OAAO,KAAK;AAAA,YACZ,MAAM,KAAK;AAAA,YACX,MAAM,KAAK;AAAA,YACX,OAAO,KAAK;AAAA,YACZ,OAAO,KAAK;AAAA,YACZ,UAAU,KAAK;AAAA,YACf,kBAAkB,KAAK;AAAA,YACvB,MAAM,KAAK;AAAA,YACX,UAAU,KAAK;AAAA,YACf,aAAa,KAAK;AAAA,YAClB,cAAc,KAAK;AAAA,YACnB,sBAAsB,KAAK;AAAA,YAC3B,UAAU,KAAK;AAAA,YACf,OAAO,KAAK;AAAA,YACZ,QAAQ,KAAK,SAAS,IAAI,KAAK,KAAK,MAAM,IAAI;AAAA,YAC9C,OAAO,KAAK;AAAA,UACd,CAAC;AAAA,QACH,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,MAAI,cAAc,EAAE,MAAM,YAAY,SAAS,KAAK,GAAG,MAAM,GAAG;AAC9D,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,aAAa;AAAA,QACb,aAAa;AAAA,MACf;AAAA,MACA,OAAO,SAAS;AACd,eAAO,QAAQ,YAAY;AACzB,gBAAM,QAAQ,eAAe;AAC7B,iBAAO,MAAM,MAAM,IAAI,QAAQ;AAAA,YAC7B,IAAI,KAAK;AAAA,YACT,MAAM,KAAK;AAAA,YACX,MAAM,KAAK;AAAA,YACX,cAAc,KAAK;AAAA,YACnB,aAAa,KAAK,cAAc,IAAI,KAAK,KAAK,WAAW,IAAI;AAAA,YAC7D,UAAU,KAAK;AAAA,UACjB,CAAC;AAAA,QACH,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,MAAI,cAAc,EAAE,MAAM,sBAAsB,SAAS,KAAK,GAAG,MAAM,GAAG;AACxE,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,aAAa;AAAA,QACb,aAAa;AAAA,MACf;AAAA,MACA,OAAO,SAAS;AACd,eAAO,QAAQ,YAAY;AACzB,gBAAM,QAAQ,eAAe;AAC7B,iBAAO,MAAM,MAAM,MAAM,UAAU;AAAA,YACjC,cAAc,KAAK;AAAA,YACnB,OAAO,KAAK;AAAA,YACZ,MAAM,KAAK;AAAA,YACX,WAAW,KAAK;AAAA,YAChB,aAAa,KAAK;AAAA,YAClB,UAAU,KAAK;AAAA,YACf,MAAM,KAAK;AAAA,YACX,UAAU,KAAK;AAAA,YACf,UAAU,KAAK;AAAA,YACf,MAAM,KAAK;AAAA,YACX,MAAM,KAAK;AAAA,YACX,WAAW,KAAK,YAAY,IAAI,KAAK,KAAK,SAAS,IAAI;AAAA,UACzD,CAAC;AAAA,QACH,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;;;AC5IO,SAAS,wBAAwB,QAAmB,QAA0B;AACnF,MAAI,cAAc,EAAE,MAAM,mBAAmB,SAAS,MAAM,GAAG,MAAM,GAAG;AACtE,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,aACE;AAAA,QACF,aAAa;AAAA,MACf;AAAA,MACA,OAAO,SAAS;AACd,eAAO,QAAQ,YAAY;AACzB,gBAAM,QAAQ,eAAe;AAC7B,iBAAO,MAAM,MAAM,YAAY,0BAA0B,KAAK,UAAU;AAAA,QAC1E,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,MAAI,cAAc,EAAE,MAAM,oBAAoB,SAAS,MAAM,GAAG,MAAM,GAAG;AACvE,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,aAAa;AAAA,QACb,aAAa;AAAA,MACf;AAAA,MACA,OAAO,SAAS;AACd,eAAO,QAAQ,YAAY;AACzB,gBAAM,QAAQ,eAAe;AAC7B,iBAAO,MAAM,MAAM,YAAY;AAAA,YAC7B,KAAK;AAAA,YACL,KAAK;AAAA,YACL,KAAK;AAAA,YACL,KAAK;AAAA,UACP;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,MAAI,cAAc,EAAE,MAAM,qBAAqB,SAAS,KAAK,GAAG,MAAM,GAAG;AACvE,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,aAAa;AAAA,QACb,aAAa;AAAA,MACf;AAAA,MACA,OAAO,SAAS;AACd,eAAO,QAAQ,YAAY;AACzB,gBAAM,QAAQ,eAAe;AAC7B,iBAAO,MAAM,MAAM,YAAY,iBAAiB;AAAA,YAC9C,YAAY,KAAK;AAAA,YACjB,OAAO,KAAK;AAAA,YACZ,OAAO,KAAK;AAAA,YACZ,MAAM,KAAK;AAAA,YACX,WAAW,KAAK;AAAA,YAChB,UAAU,KAAK;AAAA,YACf,QAAQ,KAAK;AAAA,YACb,UAAU,KAAK;AAAA,UACjB,CAAC;AAAA,QACH,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,MAAI,cAAc,EAAE,MAAM,qBAAqB,SAAS,KAAK,GAAG,MAAM,GAAG;AACvE,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,aACE;AAAA,QACF,aAAa;AAAA,MACf;AAAA,MACA,OAAO,SAAS;AACd,eAAO,QAAQ,YAAY;AACzB,gBAAM,QAAQ,eAAe;AAC7B,iBAAO,MAAM,MAAM,YAAY,iBAAiB,KAAK,IAAI;AAAA,YACvD,OAAO,KAAK,SAAS;AAAA,YACrB,OAAO,KAAK,SAAS;AAAA,YACrB,MAAM,KAAK,QAAQ;AAAA,YACnB,WAAW,KAAK,aAAa;AAAA,YAC7B,UAAU,KAAK;AAAA,YACf,QAAQ,KAAK;AAAA,YACb,UAAU,KAAK;AAAA,YACf,QAAQ,KAAK;AAAA,UACf,CAAC;AAAA,QACH,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,MAAI,cAAc,EAAE,MAAM,8BAA8B,SAAS,KAAK,GAAG,MAAM,GAAG;AAChF,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,aACE;AAAA,QACF,aAAa;AAAA,MACf;AAAA,MACA,OAAO,SAAS;AACd,eAAO,QAAQ,YAAY;AACzB,gBAAM,QAAQ,eAAe;AAC7B,iBAAO,MAAM,MAAM,YAAY,yBAAyB,KAAK,IAAI;AAAA,YAC/D,aAAa,KAAK;AAAA,UACpB,CAAC;AAAA,QACH,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;;;AClHO,SAAS,sBAAsB,QAAmB,QAA0B;AACjF,MAAI,cAAc,EAAE,MAAM,kBAAkB,SAAS,MAAM,GAAG,MAAM,GAAG;AACrE,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,aAAa;AAAA,QACb,aAAa;AAAA,MACf;AAAA,MACA,OAAO,SAAS;AACd,eAAO,QAAQ,YAAY;AACzB,gBAAM,QAAQ,eAAe;AAC7B,iBAAO,MAAM,MAAM,UAAU,cAAc,KAAK,OAAO,KAAK,MAAM;AAAA,QACpE,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,MAAI,cAAc,EAAE,MAAM,gBAAgB,SAAS,MAAM,GAAG,MAAM,GAAG;AACnE,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,aAAa;AAAA,QACb,aAAa;AAAA,MACf;AAAA,MACA,OAAO,SAAS;AACd,eAAO,QAAQ,YAAY;AACzB,gBAAM,QAAQ,eAAe;AAC7B,iBAAO,MAAM,MAAM,UAAU,YAAY,KAAK,EAAE;AAAA,QAClD,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,MAAI,cAAc,EAAE,MAAM,mBAAmB,SAAS,MAAM,GAAG,MAAM,GAAG;AACtE,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,aACE;AAAA,QACF,aAAa;AAAA,MACf;AAAA,MACA,OAAO,SAAS;AACd,eAAO,QAAQ,YAAY;AACzB,gBAAM,QAAQ,eAAe;AAC7B,iBAAO,MAAM,MAAM,UAAU,eAAe,KAAK,IAAI,EAAE,WAAW,KAAK,UAAU,CAAC;AAAA,QACpF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,MAAI,cAAc,EAAE,MAAM,mBAAmB,SAAS,KAAK,GAAG,MAAM,GAAG;AACrE,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,aAAa;AAAA,QACb,aAAa;AAAA,MACf;AAAA,MACA,OAAO,SAAS;AACd,eAAO,QAAQ,YAAY;AACzB,gBAAM,QAAQ,eAAe;AAC7B,iBAAO,MAAM,MAAM,UAAU,eAAe;AAAA,YAC1C,MAAM,KAAK;AAAA,YACX,aAAa,KAAK;AAAA,YAClB,SAAS,KAAK;AAAA,YACd,MAAM,KAAK;AAAA,YACX,MAAM,KAAK;AAAA,UACb,CAAC;AAAA,QACH,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;;;ACpFA,IAAM,OAAO;AAEb,IAAM,MAAgB,CAAC,eAAe;AAEtC,IAAM,aAAa;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,eAAqC;AAAA,EAChD,MAAM;AAAA,IACJ,KAAK;AAAA,MACH,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,QACL;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UACE;AAAA,UAEF,WAAW;AAAA,QACb;AAAA,QACA;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UACE;AAAA,UAQF,WAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,eAAe;AAAA,MACf,WAAW;AAAA,MACX,SAAS;AAAA,IACX;AAAA,IACA,YAAY;AAAA,MACV,SAAS;AAAA,QACP,SAAS;AAAA,QACT,UAAU;AAAA,QACV,WAAW;AAAA,QACX,SAAS;AAAA,QACT,OAAO;AAAA,UACL;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,YACT,UACE;AAAA,YAEF,WAAW;AAAA,UACb;AAAA,UACA;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,YACT,UACE;AAAA,YAgBF,WAAW;AAAA,UACb;AAAA,QACF;AAAA,QACA,eAAe;AAAA,QACf,WAAW;AAAA,UACT,GAAG;AAAA,UACH;AAAA,UACA;AAAA,QACF;AAAA,QACA,SAAS;AAAA,MACX;AAAA,MACA,QAAQ;AAAA,QACN,SAAS;AAAA,QACT,UAAU;AAAA,QACV,WAAW;AAAA,QACX,SAAS;AAAA,QACT,OAAO;AAAA,UACL;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,YACT,UACE;AAAA,YAGF,WAAW;AAAA,UACb;AAAA,UACA;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,YACT,UACE;AAAA,YAWF,WAAW;AAAA,UACb;AAAA,QACF;AAAA,QACA,eAAe;AAAA,QACf,WAAW;AAAA,UACT,GAAG;AAAA,UACH;AAAA,QACF;AAAA,QACA,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN,KAAK;AAAA,MACH,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,QACL;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UACE;AAAA,UAcF,WAAW;AAAA,QACb;AAAA,QACA;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UACE;AAAA,UAOF,WAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,eAAe;AAAA,MACf,WAAW;AAAA,MACX,SAAS;AAAA,IACX;AAAA,IACA,YAAY;AAAA,MACV,OAAO;AAAA,QACL,SAAS;AAAA,QACT,UAAU;AAAA,QACV,WAAW;AAAA,QACX,SAAS;AAAA,QACT,OAAO;AAAA,UACL;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,YACT,UACE;AAAA,YAWF,WAAW;AAAA,UACb;AAAA,UACA;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,YACT,UACE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAYF,WAAW;AAAA,UACb;AAAA,QACF;AAAA,QACA,eAAe;AAAA,QACf,WAAW;AAAA,UACT,GAAG;AAAA,UACH;AAAA,QACF;AAAA,QACA,SAAS;AAAA,MACX;AAAA,MACA,SAAS;AAAA,QACP,SAAS;AAAA,QACT,UAAU;AAAA,QACV,WAAW;AAAA,QACX,SAAS;AAAA,QACT,OAAO;AAAA,UACL;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,YACT,UACE;AAAA,YAYF,WAAW;AAAA,UACb;AAAA,UACA;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,YACT,UACE;AAAA,YAcF,WAAW;AAAA,UACb;AAAA,QACF;AAAA,QACA,eAAe;AAAA,QACf,WAAW;AAAA,UACT,GAAG;AAAA,UACH;AAAA,UACA;AAAA,QACF;AAAA,QACA,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AAAA,EACA,MAAM;AAAA,IACJ,KAAK;AAAA,MACH,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,QACL;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UACE;AAAA,UAIF,WAAW;AAAA,QACb;AAAA,QACA;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UACE;AAAA,UAWF,WAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,eAAe;AAAA,MACf,WAAW;AAAA,MACX,SAAS;AAAA,IACX;AAAA,IACA,YAAY;AAAA,MACV,OAAO;AAAA,QACL,SAAS;AAAA,QACT,UAAU;AAAA,QACV,WAAW;AAAA,QACX,SAAS;AAAA,QACT,OAAO;AAAA,UACL;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,YACT,UACE;AAAA,YAIF,WAAW;AAAA,UACb;AAAA,UACA;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,YACT,UACE;AAAA,YAYF,WAAW;AAAA,UACb;AAAA,QACF;AAAA,QACA,eAAe;AAAA,QACf,WAAW;AAAA,UACT,GAAG;AAAA,UACH;AAAA,QACF;AAAA,QACA,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AAAA,EACA,IAAI;AAAA,IACF,KAAK;AAAA,MACH,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,QACL;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UACE;AAAA,UAkCF,WAAW;AAAA,QACb;AAAA,QACA;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UACE;AAAA,UASF,WAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,eAAe;AAAA,MACf,WAAW;AAAA,MACX,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EACA,KAAK;AAAA,IACH,KAAK;AAAA,MACH,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,QACL;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UACE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UA4BF,WAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,eAAe;AAAA,MACf,WAAW;AAAA,MACX,SAAS;AAAA,IACX;AAAA,IACA,YAAY;AAAA,MACV,SAAS;AAAA,QACP,SAAS;AAAA,QACT,UAAU;AAAA,QACV,WAAW;AAAA,QACX,SAAS;AAAA,QACT,OAAO;AAAA,UACL;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,YACT,UACE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAmBF,WAAW;AAAA,UACb;AAAA,UACA;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,YACT,UACE;AAAA,YAGF,WAAW;AAAA,UACb;AAAA,QACF;AAAA,QACA,eAAe;AAAA,QACf,WAAW;AAAA,QACX,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN,KAAK;AAAA,MACH,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,QACL;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UACE;AAAA,UA2BF,WAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,eAAe;AAAA,MACf,WAAW;AAAA,MACX,SAAS;AAAA,IACX;AAAA,IACA,YAAY;AAAA,MACV,YAAY;AAAA,QACV,SAAS;AAAA,QACT,UAAU;AAAA,QACV,WAAW;AAAA,QACX,SAAS;AAAA,QACT,OAAO;AAAA,UACL;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,YACT,UACE;AAAA,YAmBF,WAAW;AAAA,UACb;AAAA,UACA;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,YACT,UACE;AAAA,YAqBF,WAAW;AAAA,UACb;AAAA,QACF;AAAA,QACA,eAAe;AAAA,QACf,WAAW;AAAA,UACT,GAAG;AAAA,UACH;AAAA,QACF;AAAA,QACA,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AACF;;;ACnoBA,IAAMA,QAAO;AACb,IAAMC,OAAgB,CAAC,eAAe;AACtC,IAAMC,cAAa;AAAA,EACjB;AAAA,EACA;AACF;AAEO,IAAM,eAAqC;AAAA,EAChD,MAAM;AAAA,IACJ,KAAK;AAAA,MACH,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,QACL;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UACE;AAAA,UAWF,WAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,eAAeD;AAAA,MACf,WAAWC;AAAA,MACX,SAASF;AAAA,IACX;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN,KAAK;AAAA,MACH,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,QACL;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UACE;AAAA,UASF,WAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,eAAeC;AAAA,MACf,WAAWC;AAAA,MACX,SAASF;AAAA,IACX;AAAA,EACF;AAAA,EACA,MAAM;AAAA,IACJ,KAAK;AAAA,MACH,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,QACL;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UACE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAaF,WAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,eAAeC;AAAA,MACf,WAAWC;AAAA,MACX,SAASF;AAAA,IACX;AAAA,EACF;AAAA,EACA,IAAI;AAAA,IACF,KAAK;AAAA,MACH,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,QACL;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UACE;AAAA,UAWF,WAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,eAAeC;AAAA,MACf,WAAWC;AAAA,MACX,SAASF;AAAA,IACX;AAAA,EACF;AAAA,EACA,KAAK;AAAA,IACH,KAAK;AAAA,MACH,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,QACL;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UACE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAwBF,WAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,eAAeC;AAAA,MACf,WAAWC;AAAA,MACX,SAASF;AAAA,IACX;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN,KAAK;AAAA,MACH,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,QACL;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UACE;AAAA,UA6BF,WAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,eAAeC;AAAA,MACf,WAAWC;AAAA,MACX,SAASF;AAAA,IACX;AAAA,EACF;AACF;;;AC3NA,IAAMG,QAAO;AACb,IAAMC,OAAgB,CAAC,eAAe;AACtC,IAAMC,cAAa;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,cAAoC;AAAA,EAC/C,MAAM;AAAA,IACJ,KAAK;AAAA,MACH,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,QACL;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UACE;AAAA,UAUF,WAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,eAAeD;AAAA,MACf,WAAWC;AAAA,MACX,SAASF;AAAA,IACX;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN,KAAK;AAAA,MACH,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,QACL;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UACE;AAAA,UAQF,WAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,eAAeC;AAAA,MACf,WAAWC;AAAA,MACX,SAASF;AAAA,IACX;AAAA,EACF;AAAA,EACA,MAAM;AAAA,IACJ,KAAK;AAAA,MACH,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,QACL;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UACE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAYF,WAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,eAAeC;AAAA,MACf,WAAWC;AAAA,MACX,SAASF;AAAA,IACX;AAAA,EACF;AAAA,EACA,IAAI;AAAA,IACF,KAAK;AAAA,MACH,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,QACL;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UACE;AAAA,UAUF,WAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,eAAeC;AAAA,MACf,WAAWC;AAAA,MACX,SAASF;AAAA,IACX;AAAA,EACF;AAAA,EACA,KAAK;AAAA,IACH,KAAK;AAAA,MACH,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,QACL;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UACE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAuBF,WAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,eAAeC;AAAA,MACf,WAAWC;AAAA,MACX,SAASF;AAAA,IACX;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN,KAAK;AAAA,MACH,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,QACL;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UACE;AAAA,UA2BF,WAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,eAAeC;AAAA,MACf,WAAWC;AAAA,MACX,SAASF;AAAA,IACX;AAAA,EACF;AACF;;;ACrNA,IAAMG,QAAO;AACb,IAAMC,OAAgB,CAAC,eAAe;AACtC,IAAMC,cAAa;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,aAAmC;AAAA,EAC9C,MAAM;AAAA,IACJ,KAAK;AAAA,MACH,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,QACL;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UACE;AAAA,UAQF,WAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,eAAeD;AAAA,MACf,WAAWC;AAAA,MACX,SAASF;AAAA,IACX;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN,KAAK;AAAA,MACH,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,QACL;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UACE;AAAA,UAMF,WAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,eAAeC;AAAA,MACf,WAAWC;AAAA,MACX,SAASF;AAAA,IACX;AAAA,EACF;AAAA,EACA,MAAM;AAAA,IACJ,KAAK;AAAA,MACH,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,QACL;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UACE;AAAA,UAUF,WAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,eAAeC;AAAA,MACf,WAAWC;AAAA,MACX,SAASF;AAAA,IACX;AAAA,EACF;AAAA,EACA,IAAI;AAAA,IACF,KAAK;AAAA,MACH,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,QACL;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UACE;AAAA,UAQF,WAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,eAAeC;AAAA,MACf,WAAWC;AAAA,MACX,SAASF;AAAA,IACX;AAAA,EACF;AAAA,EACA,KAAK;AAAA,IACH,KAAK;AAAA,MACH,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,QACL;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UACE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAqBF,WAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,eAAeC;AAAA,MACf,WAAWC;AAAA,MACX,SAASF;AAAA,IACX;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN,KAAK;AAAA,MACH,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,QACL;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UACE;AAAA,UA0BF,WAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,eAAeC;AAAA,MACf,WAAWC;AAAA,MACX,SAASF;AAAA,IACX;AAAA,EACF;AACF;;;AC1MA,IAAMG,QAAO;AAEb,IAAMC,OAAgB,CAAC,sBAAsB;AAE7C,IAAMC,cAAa;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,iBAAuC;AAAA,EAClD,MAAM;AAAA,IACJ,KAAK;AAAA,MACH,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,QACL;AAAA,UACE,MAAM;AAAA,UACN,SACE;AAAA,UACF,UACE;AAAA,UA+BF,WAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,eAAeD;AAAA,MACf,WAAWC;AAAA,MACX,SAASF;AAAA,IACX;AAAA,IACA,YAAY;AAAA,MACV,SAAS;AAAA,QACP,SAAS;AAAA,QACT,UAAU;AAAA,QACV,WAAW;AAAA,QACX,SAAS;AAAA,QACT,OAAO;AAAA,UACL;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,YACT,UACE;AAAA,YAeF,WAAW;AAAA,UACb;AAAA,UACA;AAAA,YACE,MAAM;AAAA,YACN,SACE;AAAA,YACF,UACE;AAAA,YAaF,WAAW;AAAA,UACb;AAAA,QACF;AAAA,QACA,eAAeC;AAAA,QACf,WAAW;AAAA,UACT,GAAGC;AAAA,UACH;AAAA,QACF;AAAA,QACA,SAASF;AAAA,MACX;AAAA,MACA,QAAQ;AAAA,QACN,SAAS;AAAA,QACT,UAAU;AAAA,QACV,WAAW;AAAA,QACX,SAAS;AAAA,QACT,OAAO;AAAA,UACL;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,YACT,UACE;AAAA,YAeF,WAAW;AAAA,UACb;AAAA,UACA;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,YACT,UACE;AAAA,YAWF,WAAW;AAAA,UACb;AAAA,QACF;AAAA,QACA,eAAeC;AAAA,QACf,WAAWC;AAAA,QACX,SAASF;AAAA,MACX;AAAA,IACF;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN,KAAK;AAAA,MACH,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,QACL;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UACE;AAAA,UAsBF,WAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,eAAeC;AAAA,MACf,WAAWC;AAAA,MACX,SAASF;AAAA,IACX;AAAA,IACA,YAAY;AAAA,MACV,OAAO;AAAA,QACL,SAAS;AAAA,QACT,UAAU;AAAA,QACV,WAAW;AAAA,QACX,SAAS;AAAA,QACT,OAAO;AAAA,UACL;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,YACT,UACE;AAAA,YAWF,WAAW;AAAA,UACb;AAAA,UACA;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,YACT,UACE;AAAA,YAYF,WAAW;AAAA,UACb;AAAA,QACF;AAAA,QACA,eAAeC;AAAA,QACf,WAAWC;AAAA,QACX,SAASF;AAAA,MACX;AAAA,MACA,SAAS;AAAA,QACP,SAAS;AAAA,QACT,UAAU;AAAA,QACV,WAAW;AAAA,QACX,SAAS;AAAA,QACT,OAAO;AAAA,UACL;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,YACT,UACE;AAAA,YAqBF,WAAW;AAAA,UACb;AAAA,QACF;AAAA,QACA,eAAeC;AAAA,QACf,WAAWC;AAAA,QACX,SAASF;AAAA,MACX;AAAA,IACF;AAAA,EACF;AAAA,EACA,MAAM;AAAA,IACJ,KAAK;AAAA,MACH,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,QACL;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UACE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAkBF,WAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,eAAeC;AAAA,MACf,WAAWC;AAAA,MACX,SAASF;AAAA,IACX;AAAA,IACA,YAAY;AAAA,MACV,OAAO;AAAA,QACL,SAAS;AAAA,QACT,UAAU;AAAA,QACV,WAAW;AAAA,QACX,SAAS;AAAA,QACT,OAAO;AAAA,UACL;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,YACT,UACE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAeF,WAAW;AAAA,UACb;AAAA,UACA;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,YACT,UACE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAaF,WAAW;AAAA,UACb;AAAA,QACF;AAAA,QACA,eAAeC;AAAA,QACf,WAAW;AAAA,UACT,GAAGC;AAAA,UACH;AAAA,QACF;AAAA,QACA,SAASF;AAAA,MACX;AAAA,IACF;AAAA,EACF;AAAA,EACA,IAAI;AAAA,IACF,KAAK;AAAA,MACH,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,QACL;AAAA,UACE,MAAM;AAAA,UACN,SACE;AAAA,UACF,UACE;AAAA,UA4BF,WAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,eAAeC;AAAA,MACf,WAAWC;AAAA,MACX,SAASF;AAAA,IACX;AAAA,EACF;AAAA,EACA,KAAK;AAAA,IACH,KAAK;AAAA,MACH,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,QACL;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UACE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAuBF,WAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,eAAeC;AAAA,MACf,WAAWC;AAAA,MACX,SAASF;AAAA,IACX;AAAA,IACA,YAAY;AAAA,MACV,SAAS;AAAA,QACP,SAAS;AAAA,QACT,UAAU;AAAA,QACV,WAAW;AAAA,QACX,SAAS;AAAA,QACT,OAAO;AAAA,UACL;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,YACT,UACE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAuBF,WAAW;AAAA,UACb;AAAA,UACA;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,YACT,UACE;AAAA,YAkBF,WAAW;AAAA,UACb;AAAA,QACF;AAAA,QACA,eAAeC;AAAA,QACf,WAAW;AAAA,UACT,GAAGC;AAAA,UACH;AAAA,UACA;AAAA,QACF;AAAA,QACA,SAASF;AAAA,MACX;AAAA,IACF;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN,KAAK;AAAA,MACH,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,QACL;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UACE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UA0BF,WAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,eAAeC;AAAA,MACf,WAAWC;AAAA,MACX,SAASF;AAAA,IACX;AAAA,IACA,YAAY;AAAA,MACV,YAAY;AAAA,QACV,SAAS;AAAA,QACT,UAAU;AAAA,QACV,WAAW;AAAA,QACX,SAAS;AAAA,QACT,OAAO;AAAA,UACL;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,YACT,UACE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAwBF,WAAW;AAAA,UACb;AAAA,UACA;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,YACT,UACE;AAAA,YAkBF,WAAW;AAAA,UACb;AAAA,QACF;AAAA,QACA,eAAeC;AAAA,QACf,WAAWC;AAAA,QACX,SAASF;AAAA,MACX;AAAA,IACF;AAAA,EACF;AACF;;;AC/oBA,IAAM,WAA4D;AAAA,EAChE,OAAO;AAAA,EACP,MAAM;AAAA,EACN,KAAK;AAAA,EACL,OAAO;AAAA,EACP,SAAS;AACX;AAaO,SAAS,kBACd,MACgC;AAChC,QAAM,UAAU,SAAS,KAAK,OAAO,IAAI,KAAK,QAAQ;AACtD,MAAI,CAAC,QAAS,QAAO;AAErB,MAAI,KAAK,WAAW;AAClB,UAAM,MAAM,KAAK,UAAU,KAAK,EAAE,YAAY;AAC9C,UAAM,KAAK,QAAQ,aAAa,GAAG;AACnC,QAAI,GAAI,QAAO,EAAE,QAAQ,IAAI,qBAAqB,KAAK;AAEvD,WAAO,EAAE,QAAQ,QAAQ,KAAK,qBAAqB,MAAM;AAAA,EAC3D;AAEA,SAAO,EAAE,QAAQ,QAAQ,KAAK,qBAAqB,KAAK;AAC1D;;;ACXA,IAAMG,QAAO;AAEN,IAAM,oBAA0D;AAAA,EACrE,MAAM;AAAA,IACJ,UAAU;AAAA,IACV,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,aAAa;AAAA,IACb,iBAAiB;AAAA,MACf,EAAE,SAAS,OAAO,SAAS,kCAAkC;AAAA,MAC7D,EAAE,SAAS,QAAQ,SAAS,+BAA+B;AAAA,MAC3D,EAAE,SAAS,QAAQ,SAAS,+BAA+B;AAAA,IAC7D;AAAA,IACA,aAAa;AAAA,MACX,SAAS;AAAA,MACT,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,IACA,eAAe,CAAC,eAAe;AAAA,IAC/B,SAAS,GAAGA,KAAI;AAAA,EAClB;AAAA,EACA,QAAQ;AAAA,IACN,UAAU;AAAA,IACV,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,aAAa;AAAA,IACb,iBAAiB;AAAA,MACf,EAAE,SAAS,UAAU,SAAS,kCAAkC;AAAA,MAChE,EAAE,SAAS,SAAS,SAAS,+BAA+B;AAAA,IAC9D;AAAA,IACA,aAAa;AAAA,MACX,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,MACF,EAAE,KAAK,IAAI;AAAA,MACX,MAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,EAAE,KAAK,IAAI;AAAA,MACX,aAAa;AAAA,IACf;AAAA,IACA,eAAe,CAAC,eAAe;AAAA,IAC/B,SAAS,GAAGA,KAAI;AAAA,EAClB;AAAA,EACA,MAAM;AAAA,IACJ,UAAU;AAAA,IACV,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,aAAa;AAAA,IACb,iBAAiB;AAAA,MACf,EAAE,SAAS,OAAO,SAAS,oBAAoB;AAAA,MAC/C,EAAE,SAAS,WAAW,SAAS,mBAAmB;AAAA,IACpD;AAAA,IACA,aAAa;AAAA,MACX,SAAS;AAAA,MACT,MAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,MACF,EAAE,KAAK,IAAI;AAAA,MACX,aAAa;AAAA,IACf;AAAA,IACA,eAAe,CAAC,eAAe;AAAA,IAC/B,SAAS,GAAGA,KAAI;AAAA,EAClB;AAAA,EACA,QAAQ;AAAA,IACN,UAAU;AAAA,IACV,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,aAAa;AAAA,IACb,iBAAiB;AAAA,MACf,EAAE,SAAS,OAAO,SAAS,uBAAuB;AAAA,MAClD,EAAE,SAAS,UAAU,SAAS,sBAAsB;AAAA,MACpD,EAAE,SAAS,MAAM,SAAS,kBAAkB;AAAA,IAC9C;AAAA,IACA,aAAa;AAAA,MACX,SAAS;AAAA,MACT,MAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,EAAE,KAAK,IAAI;AAAA,MACX,aAAa;AAAA,IACf;AAAA,IACA,eAAe,CAAC,eAAe;AAAA,IAC/B,SAAS,GAAGA,KAAI;AAAA,IAChB,OACE;AAAA,EACJ;AAAA,EACA,IAAI;AAAA,IACF,UAAU;AAAA,IACV,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,aAAa;AAAA,IACb,iBAAiB;AAAA,MACf,EAAE,SAAS,MAAM,SAAS,qDAAgD;AAAA,IAC5E;AAAA,IACA,aAAa;AAAA,MACX,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,EAAE,KAAK,IAAI;AAAA,MACX,MAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,EAAE,KAAK,IAAI;AAAA,MACX,aAAa;AAAA,IACf;AAAA,IACA,eAAe,CAAC,eAAe;AAAA,IAC/B,SAAS,GAAGA,KAAI;AAAA,IAChB,OACE;AAAA,EACJ;AAAA,EACA,KAAK;AAAA,IACH,UAAU;AAAA,IACV,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,IACV,aAAa;AAAA,IACb,iBAAiB;AAAA,MACf,EAAE,SAAS,YAAY,SAAS,qCAAqC;AAAA,IACvE;AAAA,IACA,aAAa;AAAA,MACX,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,MACF,EAAE,KAAK,IAAI;AAAA,MACX,MAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,EAAE,KAAK,IAAI;AAAA,MACX,aAAa;AAAA,IACf;AAAA,IACA,eAAe,CAAC,eAAe;AAAA,IAC/B,SAAS,GAAGA,KAAI;AAAA,IAChB,OACE;AAAA,EACJ;AACF;AAEO,SAAS,oBACd,UACA,gBACiB;AACjB,QAAM,QAAQ,kBAAkB,QAAQ;AACxC,MAAI,CAAC,eAAgB,QAAO;AAE5B,QAAM,aAAa,eAAe,KAAK,EAAE,YAAY;AACrD,QAAM,QAAQ,MAAM,gBAAgB,KAAK,CAAC,MAAM,EAAE,QAAQ,YAAY,MAAM,UAAU;AACtF,MAAI,CAAC,MAAO,QAAO;AAEnB,SAAO,EAAE,GAAG,OAAO,iBAAiB,CAAC,KAAK,EAAE;AAC9C;;;AChPO,SAAS,yBAAyB,QAAmB,QAA0B;AACpF,MAAI,cAAc,EAAE,MAAM,gCAAgC,SAAS,MAAM,GAAG,MAAM,GAAG;AACnF,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,aACE;AAAA,QACF,aAAa;AAAA,MACf;AAAA,MACA,OAAO,SAAS;AACd,cAAM,QAAQ,oBAAoB,KAAK,UAAU,KAAK,cAAc;AACpE,eAAO,WAAW,KAAK;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAEA,MAAI,cAAc,EAAE,MAAM,8BAA8B,SAAS,MAAM,GAAG,MAAM,GAAG;AACjF,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,aACE;AAAA,QACF,aAAa;AAAA,MACf;AAAA,MACA,OAAO,SAAS;AACd,cAAM,WAAW,kBAAkB;AAAA,UACjC,SAAS,KAAK;AAAA,UACd,UAAU,KAAK;AAAA,UACf,WAAW,KAAK;AAAA,QAClB,CAAC;AACD,YAAI,CAAC,UAAU;AACb,iBAAO,WAAW;AAAA,YAChB,OAAO,uCAAuC,KAAK,OAAO,aAAa,KAAK,QAAQ;AAAA,UACtF,CAAC;AAAA,QACH;AACA,cAAM,EAAE,QAAQ,oBAAoB,IAAI;AACxC,eAAO,WAAW;AAAA,UAChB,GAAG;AAAA,UACH;AAAA,UACA,oBAAoB,KAAK,aAAa;AAAA,QACxC,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;;;AC3CO,SAAS,qBAAqB,QAAmB,QAA0B;AAChF,MAAI,cAAc,EAAE,MAAM,iBAAiB,SAAS,MAAM,GAAG,MAAM,GAAG;AACpE,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,aAAa;AAAA,QACb,aAAa;AAAA,MACf;AAAA,MACA,OAAO,SAAS;AACd,eAAO,QAAQ,YAAY;AACzB,gBAAM,QAAQ,eAAe;AAC7B,iBAAO,MAAM,MAAM,SAAS,aAAa,KAAK,OAAO,KAAK,MAAM;AAAA,QAClE,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,MAAI,cAAc,EAAE,MAAM,kBAAkB,SAAS,KAAK,GAAG,MAAM,GAAG;AACpE,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,aACE;AAAA,QACF,aAAa;AAAA,MACf;AAAA,MACA,OAAO,SAAS;AACd,eAAO,QAAQ,YAAY;AACzB,gBAAM,QAAQ,eAAe;AAC7B,iBAAO,MAAM,MAAM,SAAS,cAAc;AAAA,YACxC,KAAK,KAAK;AAAA,YACV,QAAQ,KAAK;AAAA,YACb,aAAa,KAAK;AAAA,YAClB,QAAQ,KAAK;AAAA,YACb,UAAU,KAAK;AAAA,YACf,SAAS,KAAK;AAAA,YACd,SAAS,KAAK;AAAA,YACd,WAAW,KAAK;AAAA,UAClB,CAAC;AAAA,QACH,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,MAAI,cAAc,EAAE,MAAM,0BAA0B,SAAS,MAAM,GAAG,MAAM,GAAG;AAC7E,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,aACE;AAAA,QACF,aAAa;AAAA,MACf;AAAA,MACA,OAAO,SAAS;AACd,eAAO,QAAQ,YAAY;AACzB,gBAAM,QAAQ,eAAe;AAC7B,iBAAO,MAAM,MAAM,SAAS;AAAA,YAC1B,KAAK;AAAA,YACL,KAAK;AAAA,YACL,KAAK;AAAA,YACL,KAAK;AAAA,YACL,KAAK,YAAY,IAAI,KAAK,KAAK,SAAS,IAAI;AAAA,YAC5C,KAAK,UAAU,IAAI,KAAK,KAAK,OAAO,IAAI;AAAA,YACxC,KAAK;AAAA,YACL,KAAK;AAAA,UACP;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;;;AlBvEO,IAAM,cAAc;AACpB,IAAM,iBAAiB;AAEvB,SAAS,eAA0B;AACxC,QAAM,SAAS,IAAI;AAAA,IACjB,EAAE,MAAM,aAAa,SAAS,eAAe;AAAA,IAC7C,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,EAAE;AAAA,EAChC;AAEA,QAAM,SAAS,eAAe;AAC9B,oBAAkB,QAAQ,MAAM;AAChC,wBAAsB,QAAQ,MAAM;AACpC,0BAAwB,QAAQ,MAAM;AACtC,uBAAqB,QAAQ,MAAM;AACnC,sBAAoB,QAAQ,MAAM;AAClC,2BAAyB,QAAQ,MAAM;AACvC,4BAA0B,QAAQ,MAAM;AAExC,SAAO;AACT;;;ADzBA,eAAe,OAAsB;AAEnC,MAAI,CAAC,QAAQ,IAAI,eAAe;AAC9B,YAAQ,OAAO;AAAA,MACb;AAAA,IACF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,SAAS,aAAa;AAC5B,QAAM,YAAY,IAAI,qBAAqB;AAC3C,QAAM,OAAO,QAAQ,SAAS;AAE9B,UAAQ,OAAO,MAAM,+BAA+B,WAAW,CAAC;AAAA,CAAK;AACvE;AAEA,KAAK,EAAE,MAAM,CAAC,QAAQ;AACpB,UAAQ,OAAO,MAAM,sBAAsB,eAAe,QAAQ,IAAI,SAAS,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,CAAI;AAC5G,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["DOCS","ENV","NEXT_STEPS","DOCS","ENV","NEXT_STEPS","DOCS","ENV","NEXT_STEPS","DOCS","ENV","NEXT_STEPS","DOCS"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zyphr-dev/mcp-server",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Model Context Protocol server for Zyphr — wraps the Zyphr API as MCP tools for AI clients",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -29,7 +29,7 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "@modelcontextprotocol/sdk": "^1.0.0",
32
- "@zyphr-dev/node-sdk": "^0.1.28",
32
+ "@zyphr-dev/node-sdk": "^0.1.32",
33
33
  "zod": "^3.23.0"
34
34
  },
35
35
  "devDependencies": {