diff --git a/demo/.gitignore b/demo/.gitignore new file mode 100644 index 00000000..e7682c36 --- /dev/null +++ b/demo/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +out/ +.remotion/ +*.log diff --git a/demo/README.md b/demo/README.md new file mode 100644 index 00000000..2357f762 --- /dev/null +++ b/demo/README.md @@ -0,0 +1,85 @@ +# Primitive CLI demo video + +A [Remotion](https://www.remotion.dev/) project that renders a short, stylized +terminal demo of the Primitive CLI first-run journey: + +1. **Install** — `npm install -g @primitivedotdev/cli` +2. **Sign up** — `primitive signup` (email OTP) → `primitive signup confirm` +3. **Inbox** — `primitive inbox setup` shows the ready receive address +4. **Send** — `primitive send --to … --body …` + +The terminal is *driven by a transcript* — commands type out and output reveals +frame-by-frame — but every output line is **real**, not invented. The text was +captured by actually running `@primitivedotdev/cli` v1.2.0 and the Primitive API +(`npm install`, `primitive --version`, a real `primitive signup` against staging, +a real `getInboxStatus`, and a real `/send-mail` response) and reproduced verbatim +from the command source. The provenance of each line is documented at the top of +[`src/script.ts`](src/script.ts). Identity is a clean demo persona +(`you@acme.dev` / Acme), but the flow, flags, messages, field names, and +formatting are exactly what the CLI emits. + +This package is intentionally **standalone** — it is not part of the pnpm SDK +workspace (`pnpm-workspace.yaml`), so its Remotion + headless-Chromium +dependencies stay out of every SDK contributor's install and out of the SDK CI +path filters. + +## Preview + +```bash +cd demo +npm install +npm run studio # opens Remotion Studio at http://localhost:3000 +``` + +Scrub the four compositions in the left sidebar. + +## Render + +The first render downloads a headless Chromium build (one-time, via +`@remotion/renderer`). Outputs land in `out/` (gitignored). + +Compositions run at **60fps**, and the mp4/webm scripts render at **`--scale=2`** +(2× the composition resolution, i.e. true 4K for landscape) with a high-quality +CRF: + +```bash +npm run render:landscape # out/demo-landscape.mp4 3840x2160 (4K) 60fps h264 +npm run render:square # out/demo-square.mp4 2160x2160 60fps h264 +npm run render:vertical # out/demo-vertical.mp4 2160x3840 60fps h264 +npm run render:webm # out/demo.webm 3840x2160 (4K) 60fps vp9 +npm run render:gif # out/demo.gif 1920x1080 30fps loop +npm run render:all # the three mp4s + the gif +``` + +> GIF note: the GIF format is hard-capped at 50fps (and uses centisecond frame +> timing), so it can't carry the 60fps the video runs at. `render:gif` renders +> every 2nd frame for a clean 30fps loop (`--every-nth-frame=2`). For a true +> 60fps clip, use the webm. Adjust quality/size via `--scale` and `--crf` in the +> scripts (lower CRF = higher quality + larger file). + +All four compositions share one `DemoVideo` component +([`src/DemoVideo.tsx`](src/DemoVideo.tsx)) parameterized by a `format` prop; +they are registered in [`src/Root.tsx`](src/Root.tsx). + +## Embedding the clip + +`out/` is gitignored, so to show the clip in a README on GitHub you must either +commit the rendered asset (e.g. copy `out/demo.gif` to `demo/preview.gif` and +reference it) or upload it to a CDN / GitHub release and link that URL. The GIF +is ~7 MB; `render:webm` produces a smaller file if you prefer. + +```markdown + +![Primitive CLI demo](preview.gif) +``` + +## Customizing + +- **Transcript & timing** — [`src/script.ts`](src/script.ts): the ordered + commands, output lines, typing speed (`cps`), and holds. +- **Colors & fonts** — [`src/theme.ts`](src/theme.ts): the terminal palette, + accent color, and per-format layout. Drop a real brand SVG into `public/` and + wire it into [`src/components/Logo.tsx`](src/components/Logo.tsx) to replace the + built-in mark. +- **Layout per format** — `margins` / `cardScale` in + [`src/DemoVideo.tsx`](src/DemoVideo.tsx). diff --git a/demo/demo-old/a1.png b/demo/demo-old/a1.png new file mode 100644 index 00000000..90050050 Binary files /dev/null and b/demo/demo-old/a1.png differ diff --git a/demo/demo-old/a1b.png b/demo/demo-old/a1b.png new file mode 100644 index 00000000..a020c082 Binary files /dev/null and b/demo/demo-old/a1b.png differ diff --git a/demo/demo-old/demo-agent-landscape.mp4 b/demo/demo-old/demo-agent-landscape.mp4 new file mode 100644 index 00000000..689a5013 Binary files /dev/null and b/demo/demo-old/demo-agent-landscape.mp4 differ diff --git a/demo/demo-old/demo-agent-square.mp4 b/demo/demo-old/demo-agent-square.mp4 new file mode 100644 index 00000000..9eaca1c1 Binary files /dev/null and b/demo/demo-old/demo-agent-square.mp4 differ diff --git a/demo/demo-old/demo-agent-vertical.mp4 b/demo/demo-old/demo-agent-vertical.mp4 new file mode 100644 index 00000000..e37a3468 Binary files /dev/null and b/demo/demo-old/demo-agent-vertical.mp4 differ diff --git a/demo/demo-old/demo-agent.webm b/demo/demo-old/demo-agent.webm new file mode 100644 index 00000000..c5bbaa7d Binary files /dev/null and b/demo/demo-old/demo-agent.webm differ diff --git a/demo/demo-old/demo-combined-square.mp4 b/demo/demo-old/demo-combined-square.mp4 new file mode 100644 index 00000000..7fc36a64 Binary files /dev/null and b/demo/demo-old/demo-combined-square.mp4 differ diff --git a/demo/demo-old/demo-combined-vertical.mp4 b/demo/demo-old/demo-combined-vertical.mp4 new file mode 100644 index 00000000..98a4d67f Binary files /dev/null and b/demo/demo-old/demo-combined-vertical.mp4 differ diff --git a/demo/demo-old/demo-combined.webm b/demo/demo-old/demo-combined.webm new file mode 100644 index 00000000..da8a463b Binary files /dev/null and b/demo/demo-old/demo-combined.webm differ diff --git a/demo/demo-old/demo-explained-landscape.mp4 b/demo/demo-old/demo-explained-landscape.mp4 new file mode 100644 index 00000000..d4604c3a Binary files /dev/null and b/demo/demo-old/demo-explained-landscape.mp4 differ diff --git a/demo/demo-old/demo-explained-square.mp4 b/demo/demo-old/demo-explained-square.mp4 new file mode 100644 index 00000000..97fd1f41 Binary files /dev/null and b/demo/demo-old/demo-explained-square.mp4 differ diff --git a/demo/demo-old/demo-explained-vertical.mp4 b/demo/demo-old/demo-explained-vertical.mp4 new file mode 100644 index 00000000..044fedd2 Binary files /dev/null and b/demo/demo-old/demo-explained-vertical.mp4 differ diff --git a/demo/demo-old/demo-explained.webm b/demo/demo-old/demo-explained.webm new file mode 100644 index 00000000..a213659e Binary files /dev/null and b/demo/demo-old/demo-explained.webm differ diff --git a/demo/demo-old/demo-landscape.mp4 b/demo/demo-old/demo-landscape.mp4 new file mode 100644 index 00000000..a6737bd2 Binary files /dev/null and b/demo/demo-old/demo-landscape.mp4 differ diff --git a/demo/demo-old/demo-network-landscape.mp4 b/demo/demo-old/demo-network-landscape.mp4 new file mode 100644 index 00000000..152c9951 Binary files /dev/null and b/demo/demo-old/demo-network-landscape.mp4 differ diff --git a/demo/demo-old/demo-network-square.mp4 b/demo/demo-old/demo-network-square.mp4 new file mode 100644 index 00000000..79b40345 Binary files /dev/null and b/demo/demo-old/demo-network-square.mp4 differ diff --git a/demo/demo-old/demo-network-vertical.mp4 b/demo/demo-old/demo-network-vertical.mp4 new file mode 100644 index 00000000..1c4b23b5 Binary files /dev/null and b/demo/demo-old/demo-network-vertical.mp4 differ diff --git a/demo/demo-old/demo-network.webm b/demo/demo-old/demo-network.webm new file mode 100644 index 00000000..c271c296 Binary files /dev/null and b/demo/demo-old/demo-network.webm differ diff --git a/demo/demo-old/demo-square.mp4 b/demo/demo-old/demo-square.mp4 new file mode 100644 index 00000000..8878de0e Binary files /dev/null and b/demo/demo-old/demo-square.mp4 differ diff --git a/demo/demo-old/demo-vertical.mp4 b/demo/demo-old/demo-vertical.mp4 new file mode 100644 index 00000000..575200bf Binary files /dev/null and b/demo/demo-old/demo-vertical.mp4 differ diff --git a/demo/demo-old/demo.gif b/demo/demo-old/demo.gif new file mode 100644 index 00000000..7917fa0b Binary files /dev/null and b/demo/demo-old/demo.gif differ diff --git a/demo/package-lock.json b/demo/package-lock.json new file mode 100644 index 00000000..056315a7 --- /dev/null +++ b/demo/package-lock.json @@ -0,0 +1,2943 @@ +{ + "name": "@primitivedotdev/demo-video", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@primitivedotdev/demo-video", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "@remotion/cli": "4.0.474", + "@remotion/google-fonts": "4.0.474", + "react": "19.0.0", + "react-dom": "19.0.0", + "remotion": "4.0.474" + }, + "devDependencies": { + "@types/react": "19.0.0", + "@types/react-dom": "19.0.0", + "typescript": "5.7.2" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.1.tgz", + "integrity": "sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==", + "license": "MIT", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", + "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.0.tgz", + "integrity": "sha512-l9Oo58x0HOP5znGzVhYW9U3e5wVuA4LAZU2AGezTmkhO1CgQRFDhDg4nneHsu/t3WniXg9QrG2nIXL/ZS8ln8Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.0.tgz", + "integrity": "sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz", + "integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz", + "integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz", + "integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz", + "integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz", + "integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz", + "integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz", + "integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz", + "integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz", + "integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz", + "integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz", + "integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz", + "integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz", + "integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz", + "integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz", + "integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz", + "integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz", + "integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz", + "integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz", + "integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz", + "integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz", + "integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz", + "integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz", + "integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz", + "integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz", + "integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz", + "integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mediabunny/aac-encoder": { + "version": "1.45.0", + "resolved": "https://registry.npmjs.org/@mediabunny/aac-encoder/-/aac-encoder-1.45.0.tgz", + "integrity": "sha512-vLQw8cY7Me6pvTTMkMhOiH9UCuINzfTOETCeDxbGNeNfDqc/7QlxloUH1Ylp/Zz2ek0O8kc6YdygV2vWAPakrA==", + "license": "MPL-2.0", + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/Vanilagy" + }, + "peerDependencies": { + "mediabunny": "^1.0.0" + } + }, + "node_modules/@mediabunny/flac-encoder": { + "version": "1.45.0", + "resolved": "https://registry.npmjs.org/@mediabunny/flac-encoder/-/flac-encoder-1.45.0.tgz", + "integrity": "sha512-LfKbAMZVkxRS7PpEIVnWOY/l0KcHv+rjO7pYY3O0TPCZvbHWfrnQjn8JPacPIfuq6Yv7r4f8lhcl7yHSynoRkQ==", + "license": "MPL-2.0", + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/Vanilagy" + }, + "peerDependencies": { + "mediabunny": "^1.0.0" + } + }, + "node_modules/@mediabunny/mp3-encoder": { + "version": "1.45.0", + "resolved": "https://registry.npmjs.org/@mediabunny/mp3-encoder/-/mp3-encoder-1.45.0.tgz", + "integrity": "sha512-Bobi6AaQYEc7TWmPJ8Q0/hcUtBN7pLUC2qjoC7oZR4FcGqGztby6k7A1SWlmswoMOEIhYsOrgDaemrSDAC0QVQ==", + "license": "MPL-2.0", + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/Vanilagy" + }, + "peerDependencies": { + "mediabunny": "^1.0.0" + } + }, + "node_modules/@module-federation/error-codes": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@module-federation/error-codes/-/error-codes-0.22.0.tgz", + "integrity": "sha512-xF9SjnEy7vTdx+xekjPCV5cIHOGCkdn3pIxo9vU7gEZMIw0SvAEdsy6Uh17xaCpm8V0FWvR0SZoK9Ik6jGOaug==", + "license": "MIT" + }, + "node_modules/@module-federation/runtime": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.22.0.tgz", + "integrity": "sha512-38g5iPju2tPC3KHMPxRKmy4k4onNp6ypFPS1eKGsNLUkXgHsPMBFqAjDw96iEcjri91BrahG4XcdyKi97xZzlA==", + "license": "MIT", + "dependencies": { + "@module-federation/error-codes": "0.22.0", + "@module-federation/runtime-core": "0.22.0", + "@module-federation/sdk": "0.22.0" + } + }, + "node_modules/@module-federation/runtime-core": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@module-federation/runtime-core/-/runtime-core-0.22.0.tgz", + "integrity": "sha512-GR1TcD6/s7zqItfhC87zAp30PqzvceoeDGYTgF3Vx2TXvsfDrhP6Qw9T4vudDQL3uJRne6t7CzdT29YyVxlgIA==", + "license": "MIT", + "dependencies": { + "@module-federation/error-codes": "0.22.0", + "@module-federation/sdk": "0.22.0" + } + }, + "node_modules/@module-federation/runtime-tools": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-0.22.0.tgz", + "integrity": "sha512-4ScUJ/aUfEernb+4PbLdhM/c60VHl698Gn1gY21m9vyC1Ucn69fPCA1y2EwcCB7IItseRMoNhdcWQnzt/OPCNA==", + "license": "MIT", + "dependencies": { + "@module-federation/runtime": "0.22.0", + "@module-federation/webpack-bundler-runtime": "0.22.0" + } + }, + "node_modules/@module-federation/sdk": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.22.0.tgz", + "integrity": "sha512-x4aFNBKn2KVQRuNVC5A7SnrSCSqyfIWmm1DvubjbO9iKFe7ith5niw8dqSFBekYBg2Fwy+eMg4sEFNVvCAdo6g==", + "license": "MIT" + }, + "node_modules/@module-federation/webpack-bundler-runtime": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-0.22.0.tgz", + "integrity": "sha512-aM8gCqXu+/4wBmJtVeMeeMN5guw3chf+2i6HajKtQv7SJfxV/f4IyNQJUeUQu9HfiAZHjqtMV5Lvq/Lvh8LdyA==", + "license": "MIT", + "dependencies": { + "@module-federation/runtime": "0.22.0", + "@module-federation/sdk": "0.22.0" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.7.tgz", + "integrity": "sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.5.0", + "@emnapi/runtime": "^1.5.0", + "@tybys/wasm-util": "^0.10.1" + } + }, + "node_modules/@remotion/bundler": { + "version": "4.0.474", + "resolved": "https://registry.npmjs.org/@remotion/bundler/-/bundler-4.0.474.tgz", + "integrity": "sha512-rNriINhA+89m00QkQlkn0VHYfByVhf7yTXQ6Meh56uEqH9kyvipESJDeOYdU2J0aVYOApYuBAWkpJU/RWmWzuw==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "@remotion/media-parser": "4.0.474", + "@remotion/studio": "4.0.474", + "@remotion/studio-shared": "4.0.474", + "@remotion/timeline-utils": "4.0.474", + "@rspack/core": "1.7.6", + "@rspack/plugin-react-refresh": "1.6.1", + "css-loader": "7.1.4", + "esbuild": "0.28.0", + "react-refresh": "0.18.0", + "remotion": "4.0.474", + "style-loader": "4.0.0", + "webpack": "5.105.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@remotion/cli": { + "version": "4.0.474", + "resolved": "https://registry.npmjs.org/@remotion/cli/-/cli-4.0.474.tgz", + "integrity": "sha512-mPa7fG+L+AjEiL0ggPLCyepE1I/NjNZVR0meEFd50uXhE+SGu/xktRdSyIHMIO1a7A6G+CdkZLxt1ZcA7ZZtdQ==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "@remotion/bundler": "4.0.474", + "@remotion/media-utils": "4.0.474", + "@remotion/player": "4.0.474", + "@remotion/renderer": "4.0.474", + "@remotion/studio": "4.0.474", + "@remotion/studio-server": "4.0.474", + "@remotion/studio-shared": "4.0.474", + "dotenv": "17.3.1", + "minimist": "1.2.6", + "prompts": "2.4.2", + "remotion": "4.0.474" + }, + "bin": { + "remotion": "remotion-cli.js", + "remotionb": "remotionb-cli.js", + "remotiond": "remotiond-cli.js" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@remotion/compositor-darwin-arm64": { + "version": "4.0.474", + "resolved": "https://registry.npmjs.org/@remotion/compositor-darwin-arm64/-/compositor-darwin-arm64-4.0.474.tgz", + "integrity": "sha512-/U3k+ijua/uhkQdzhoDK/2UO7CHjcb9IYyNlZXjWQVGYRPn/3HVbL8izuAgQTp03jyLc1yA4nXJD6XzpXB/qjA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@remotion/compositor-darwin-x64": { + "version": "4.0.474", + "resolved": "https://registry.npmjs.org/@remotion/compositor-darwin-x64/-/compositor-darwin-x64-4.0.474.tgz", + "integrity": "sha512-dCnx0aqJSqVFh8p7MTl9b6BA0hZnNA0wkCToZx+jYMEh9dEZ4ui0B8S0ZDMFPZTkO/CHFcsDcqICqPT2IeceTw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@remotion/compositor-linux-arm64-gnu": { + "version": "4.0.474", + "resolved": "https://registry.npmjs.org/@remotion/compositor-linux-arm64-gnu/-/compositor-linux-arm64-gnu-4.0.474.tgz", + "integrity": "sha512-wmpm96qsagBQ3u35zhHAQpmYLX4GY8LSVnMzMNiZ2JW7q/dj4tH9g9pp/zNRTsgHlIshdFBYWLRpKbLtLIHRtQ==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@remotion/compositor-linux-arm64-musl": { + "version": "4.0.474", + "resolved": "https://registry.npmjs.org/@remotion/compositor-linux-arm64-musl/-/compositor-linux-arm64-musl-4.0.474.tgz", + "integrity": "sha512-mwNStu9WrCSgdCNywwy3surSz4lprirX/Vj9fAIPWpbwi1PQxypVwCtkEvZ33BlIQspwUYF7QWtCTPk17+YdFg==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@remotion/compositor-linux-x64-gnu": { + "version": "4.0.474", + "resolved": "https://registry.npmjs.org/@remotion/compositor-linux-x64-gnu/-/compositor-linux-x64-gnu-4.0.474.tgz", + "integrity": "sha512-GuarYSSbXtc7Gu9ElicDOIIYiaBogDDt10lK49u+mXDr6QVQ3U9ZuzniaMHOUqNTP34Jy2rVDzhWyWynBbIvfA==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@remotion/compositor-linux-x64-musl": { + "version": "4.0.474", + "resolved": "https://registry.npmjs.org/@remotion/compositor-linux-x64-musl/-/compositor-linux-x64-musl-4.0.474.tgz", + "integrity": "sha512-3FUFZtwGKOB6KVZF2VMv6PlPYKIvJsyFqiT9WLF5FHOtxQm3h4RugrFZ//YDKazln7/qYOSXNejMH+2AIwQqzw==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@remotion/compositor-win32-x64-msvc": { + "version": "4.0.474", + "resolved": "https://registry.npmjs.org/@remotion/compositor-win32-x64-msvc/-/compositor-win32-x64-msvc-4.0.474.tgz", + "integrity": "sha512-iH90tsLMUUsUa2R/zJi++A5SPMvdzQvDTmmixRf3wN7VK4q8J4Gmqa7APpv8BR+F+Ta98fnkq7rMNWf1J6vmsA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@remotion/google-fonts": { + "version": "4.0.474", + "resolved": "https://registry.npmjs.org/@remotion/google-fonts/-/google-fonts-4.0.474.tgz", + "integrity": "sha512-xSaXC7OXVbXP5D0FMMFNF9Uh9hNC2b1i1QtcY7cnY6E+Tz4ijzrcER+vTxQCwKJNPNN2sDmC/HXWMRRCkek/0Q==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "remotion": "4.0.474" + } + }, + "node_modules/@remotion/licensing": { + "version": "4.0.474", + "resolved": "https://registry.npmjs.org/@remotion/licensing/-/licensing-4.0.474.tgz", + "integrity": "sha512-WwmgcncAvZAjX9X0BIR4euk31R82mGoDyzVPFuBTHZ8pvg8kNuykcUk6fTLrMhGfL9UoTsUOWjUxBHQ3yUsRMg==", + "license": "MIT" + }, + "node_modules/@remotion/media-parser": { + "version": "4.0.474", + "resolved": "https://registry.npmjs.org/@remotion/media-parser/-/media-parser-4.0.474.tgz", + "integrity": "sha512-/MN8Hy2MGdRv2Oj3i43IxJTi7V5FXTFsmcu6NM0zXZBE9Crd620hnnTkmY1MVmNZ6dzJvPOFrGIQPu+Zr5rkng==", + "license": "Remotion License https://remotion.dev/license" + }, + "node_modules/@remotion/media-utils": { + "version": "4.0.474", + "resolved": "https://registry.npmjs.org/@remotion/media-utils/-/media-utils-4.0.474.tgz", + "integrity": "sha512-bAPPMTOqrX/CWrKhG37f+V07cX8SeHrHKaSIrwoXZHNnyrzi+as/X7OzohNH5pCIP+Irq5Yr8DNN4ljL3L/SFw==", + "license": "MIT", + "dependencies": { + "mediabunny": "1.45.0", + "remotion": "4.0.474" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@remotion/player": { + "version": "4.0.474", + "resolved": "https://registry.npmjs.org/@remotion/player/-/player-4.0.474.tgz", + "integrity": "sha512-BjV6anyFGXVazRVn8O61OoeUbuyRJzoS2A6EPa0EUpSJUyjFkIio6hNzaPDPn/W2+d9+EpLX8hdjSsmOzxfnbg==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "remotion": "4.0.474" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@remotion/renderer": { + "version": "4.0.474", + "resolved": "https://registry.npmjs.org/@remotion/renderer/-/renderer-4.0.474.tgz", + "integrity": "sha512-P8Yi6gb+ChNVg0ZtFUClR8/bHtzXZRcwpXVtX8tyzgyggKw1chOew2O+oQ4dDoKfExMJtvizFQpj/9YwOkRWzA==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "@remotion/licensing": "4.0.474", + "@remotion/streaming": "4.0.474", + "execa": "5.1.1", + "remotion": "4.0.474", + "source-map": "0.8.0-beta.0", + "ws": "8.20.1" + }, + "optionalDependencies": { + "@remotion/compositor-darwin-arm64": "4.0.474", + "@remotion/compositor-darwin-x64": "4.0.474", + "@remotion/compositor-linux-arm64-gnu": "4.0.474", + "@remotion/compositor-linux-arm64-musl": "4.0.474", + "@remotion/compositor-linux-x64-gnu": "4.0.474", + "@remotion/compositor-linux-x64-musl": "4.0.474", + "@remotion/compositor-win32-x64-msvc": "4.0.474" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@remotion/streaming": { + "version": "4.0.474", + "resolved": "https://registry.npmjs.org/@remotion/streaming/-/streaming-4.0.474.tgz", + "integrity": "sha512-IVrNi+3bf1w/nv39EbzOs0zOsq3w8mipeXJvlgPfMHtsKmo1cxsjHuxz3QXQoIo0kDBwQJvfuzBq88nbH1ZHUw==", + "license": "MIT" + }, + "node_modules/@remotion/studio": { + "version": "4.0.474", + "resolved": "https://registry.npmjs.org/@remotion/studio/-/studio-4.0.474.tgz", + "integrity": "sha512-jtSSp3UHwi2sdlZSz3jzSoF3YYAAyMiTu92GB0ly3V3QSUY1cl89akc5vvfDVNhNLHPiVn0dhg51QEJdFSt35Q==", + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.31", + "@remotion/media-utils": "4.0.474", + "@remotion/player": "4.0.474", + "@remotion/renderer": "4.0.474", + "@remotion/studio-shared": "4.0.474", + "@remotion/timeline-utils": "4.0.474", + "@remotion/web-renderer": "4.0.474", + "@remotion/zod-types": "4.0.474", + "mediabunny": "1.45.0", + "memfs": "3.4.3", + "open": "8.4.2", + "remotion": "4.0.474", + "semver": "7.5.3", + "zod": "4.3.6" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@remotion/studio-server": { + "version": "4.0.474", + "resolved": "https://registry.npmjs.org/@remotion/studio-server/-/studio-server-4.0.474.tgz", + "integrity": "sha512-TTs5hLaP/8Li4senbIVxFrwaQ3EPWScrUlFb0BFe2Zl2sTxD5Oe4L6Cy5PHPGO0VQaeWx8zYoDdnhJDd4w6Zeg==", + "license": "MIT", + "dependencies": { + "@babel/parser": "7.24.1", + "@babel/types": "7.24.0", + "@remotion/bundler": "4.0.474", + "@remotion/renderer": "4.0.474", + "@remotion/studio-shared": "4.0.474", + "memfs": "3.4.3", + "open": "8.4.2", + "prettier": "3.8.1", + "recast": "0.23.11", + "remotion": "4.0.474", + "semver": "7.5.3" + } + }, + "node_modules/@remotion/studio-shared": { + "version": "4.0.474", + "resolved": "https://registry.npmjs.org/@remotion/studio-shared/-/studio-shared-4.0.474.tgz", + "integrity": "sha512-70BLgTkPOyumJ0E1eNgIbtON0Q1A0enVUf3KWuamx3W+72tkqiBR77oO1shKa8uwXGDVBFKonmVZP9L/3zSVdw==", + "license": "MIT", + "dependencies": { + "remotion": "4.0.474" + } + }, + "node_modules/@remotion/timeline-utils": { + "version": "4.0.474", + "resolved": "https://registry.npmjs.org/@remotion/timeline-utils/-/timeline-utils-4.0.474.tgz", + "integrity": "sha512-mgH3Hzxez5oUt0tp1qC09iHhGag9IfDOw//MJlb6CTibj5lRchlNFV8Do+beE7QuZe4e+blG0Ru150J+5juEzg==", + "license": "MIT", + "dependencies": { + "mediabunny": "1.45.0" + } + }, + "node_modules/@remotion/web-renderer": { + "version": "4.0.474", + "resolved": "https://registry.npmjs.org/@remotion/web-renderer/-/web-renderer-4.0.474.tgz", + "integrity": "sha512-4tZk2cvBv+vp5lMO3cMfjvPi71U6m8Rxd8c4Vx3wkZl0IMHz3mDwddYlH8EU8NDcR3Xw4G1lnO68XsTsEoaHcw==", + "license": "UNLICENSED", + "dependencies": { + "@mediabunny/aac-encoder": "1.45.0", + "@mediabunny/flac-encoder": "1.45.0", + "@mediabunny/mp3-encoder": "1.45.0", + "@remotion/licensing": "4.0.474", + "mediabunny": "1.45.0", + "remotion": "4.0.474" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@remotion/zod-types": { + "version": "4.0.474", + "resolved": "https://registry.npmjs.org/@remotion/zod-types/-/zod-types-4.0.474.tgz", + "integrity": "sha512-B7OXy2rSPH39KzIvnFnrKwjP6qWkOyr3wmXggZaybV8alFx1d5v3t+eZWCqPu5HH2iXbPk22K2yo0wcfhppp5A==", + "license": "MIT", + "dependencies": { + "remotion": "4.0.474" + }, + "peerDependencies": { + "zod": "4.3.6" + } + }, + "node_modules/@rspack/binding": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@rspack/binding/-/binding-1.7.6.tgz", + "integrity": "sha512-/NrEcfo8Gx22hLGysanrV6gHMuqZSxToSci/3M4kzEQtF5cPjfOv5pqeLK/+B6cr56ul/OmE96cCdWcXeVnFjQ==", + "license": "MIT", + "optionalDependencies": { + "@rspack/binding-darwin-arm64": "1.7.6", + "@rspack/binding-darwin-x64": "1.7.6", + "@rspack/binding-linux-arm64-gnu": "1.7.6", + "@rspack/binding-linux-arm64-musl": "1.7.6", + "@rspack/binding-linux-x64-gnu": "1.7.6", + "@rspack/binding-linux-x64-musl": "1.7.6", + "@rspack/binding-wasm32-wasi": "1.7.6", + "@rspack/binding-win32-arm64-msvc": "1.7.6", + "@rspack/binding-win32-ia32-msvc": "1.7.6", + "@rspack/binding-win32-x64-msvc": "1.7.6" + } + }, + "node_modules/@rspack/binding-darwin-arm64": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-1.7.6.tgz", + "integrity": "sha512-NZ9AWtB1COLUX1tA9HQQvWpTy07NSFfKBU8A6ylWd5KH8AePZztpNgLLAVPTuNO4CZXYpwcoclf8jG/luJcQdQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rspack/binding-darwin-x64": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-x64/-/binding-darwin-x64-1.7.6.tgz", + "integrity": "sha512-J2g6xk8ZS7uc024dNTGTHxoFzFovAZIRixUG7PiciLKTMP78svbSSWrmW6N8oAsAkzYfJWwQpVgWfFNRHvYxSw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rspack/binding-linux-arm64-gnu": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.7.6.tgz", + "integrity": "sha512-eQfcsaxhFrv5FmtaA7+O1F9/2yFDNIoPZzV/ZvqvFz5bBXVc4FAm/1fVpBg8Po/kX1h0chBc7Xkpry3cabFW8w==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rspack/binding-linux-arm64-musl": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.7.6.tgz", + "integrity": "sha512-DfQXKiyPIl7i1yECHy4eAkSmlUzzsSAbOjgMuKn7pudsWf483jg0UUYutNgXSlBjc/QSUp7906Cg8oty9OfwPA==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rspack/binding-linux-x64-gnu": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.7.6.tgz", + "integrity": "sha512-NdA+2X3lk2GGrMMnTGyYTzM3pn+zNjaqXqlgKmFBXvjfZqzSsKq3pdD1KHZCd5QHN+Fwvoszj0JFsquEVhE1og==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rspack/binding-linux-x64-musl": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-musl/-/binding-linux-x64-musl-1.7.6.tgz", + "integrity": "sha512-rEy6MHKob02t/77YNgr6dREyJ0e0tv1X6Xsg8Z5E7rPXead06zefUbfazj4RELYySWnM38ovZyJAkPx/gOn3VA==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rspack/binding-wasm32-wasi": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@rspack/binding-wasm32-wasi/-/binding-wasm32-wasi-1.7.6.tgz", + "integrity": "sha512-YupOrz0daSG+YBbCIgpDgzfMM38YpChv+afZpaxx5Ml7xPeAZIIdgWmLHnQ2rts73N2M1NspAiBwV00Xx0N4Vg==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "1.0.7" + } + }, + "node_modules/@rspack/binding-win32-arm64-msvc": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@rspack/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.7.6.tgz", + "integrity": "sha512-INj7aVXjBvlZ84kEhSK4kJ484ub0i+BzgnjDWOWM1K+eFYDZjLdAsQSS3fGGXwVc3qKbPIssFfnftATDMTEJHQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rspack/binding-win32-ia32-msvc": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@rspack/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.7.6.tgz", + "integrity": "sha512-lXGvC+z67UMcw58In12h8zCa9IyYRmuptUBMItQJzu+M278aMuD1nETyGLL7e4+OZ2lvrnnBIcjXN1hfw2yRzw==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rspack/binding-win32-x64-msvc": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@rspack/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.7.6.tgz", + "integrity": "sha512-zeUxEc0ZaPpmaYlCeWcjSJUPuRRySiSHN23oJ2Xyw0jsQ01Qm4OScPdr0RhEOFuK/UE+ANyRtDo4zJsY52Hadw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rspack/core": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@rspack/core/-/core-1.7.6.tgz", + "integrity": "sha512-Iax6UhrfZqJajA778c1d5DBFbSIqPOSrI34kpNIiNpWd8Jq7mFIa+Z60SQb5ZQDZuUxcCZikjz5BxinFjTkg7Q==", + "license": "MIT", + "dependencies": { + "@module-federation/runtime-tools": "0.22.0", + "@rspack/binding": "1.7.6", + "@rspack/lite-tapable": "1.1.0" + }, + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "@swc/helpers": ">=0.5.1" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@rspack/lite-tapable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rspack/lite-tapable/-/lite-tapable-1.1.0.tgz", + "integrity": "sha512-E2B0JhYFmVAwdDiG14+DW0Di4Ze4Jg10Pc4/lILUrd5DRCaklduz2OvJ5HYQ6G+hd+WTzqQb3QnDNfK4yvAFYw==", + "license": "MIT" + }, + "node_modules/@rspack/plugin-react-refresh": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@rspack/plugin-react-refresh/-/plugin-react-refresh-1.6.1.tgz", + "integrity": "sha512-eqqW5645VG3CzGzFgNg5HqNdHVXY+567PGjtDhhrM8t67caxmsSzRmT5qfoEIfBcGgFkH9vEg7kzXwmCYQdQDw==", + "license": "MIT", + "dependencies": { + "error-stack-parser": "^2.1.4", + "html-entities": "^2.6.0" + }, + "peerDependencies": { + "react-refresh": ">=0.10.0 <1.0.0", + "webpack-hot-middleware": "2.x" + }, + "peerDependenciesMeta": { + "webpack-hot-middleware": { + "optional": true + } + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", + "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/dom-mediacapture-transform": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/@types/dom-mediacapture-transform/-/dom-mediacapture-transform-0.1.11.tgz", + "integrity": "sha512-Y2p+nGf1bF2XMttBnsVPHUWzRRZzqUoJAKmiP10b5umnO6DDrWI0BrGDJy1pOHoOULVmGSfFNkQrAlC5dcj6nQ==", + "license": "MIT", + "dependencies": { + "@types/dom-webcodecs": "*" + } + }, + "node_modules/@types/dom-webcodecs": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/@types/dom-webcodecs/-/dom-webcodecs-0.1.13.tgz", + "integrity": "sha512-O5hkiFIcjjszPIYyUSyvScyvrBoV3NOEEZx/pMlsu44TKzWNkLVBBxnxJz42in5n3QIolYOcBYFCPZZ0h8SkwQ==", + "license": "MIT" + }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "license": "MIT", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "license": "MIT", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.9.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.2.tgz", + "integrity": "sha512-G05zqtJhcDLb8uslf5EjCxXg9G1KQxiV8OS0R26IC//Eoyitzqe8z37I7cqvnZlrlSfgocQRfSn/AHBZJJFyGw==", + "license": "MIT", + "dependencies": { + "undici-types": ">=7.24.0 <7.24.7" + } + }, + "node_modules/@types/react": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.0.tgz", + "integrity": "sha512-MY3oPudxvMYyesqs/kW1Bh8y9VqSmf+tzqw3ae8a9DZW68pUe3zAdHeI1jc6iAysuRdACnVknHP8AhwD4/dxtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.0.tgz", + "integrity": "sha512-1KfiQKsH1o00p9m5ag12axHQSb3FOU9H20UTrujVSkNhuCrRHiQWFqgEnTNK5ZNfnzZv8UWrnXVqCmCF9fgY3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "license": "MIT", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "license": "Apache-2.0", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "license": "BSD-3-Clause" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "license": "Apache-2.0" + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-phases": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "acorn": "^8.14.0" + } + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/ast-types": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz", + "integrity": "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.35", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.35.tgz", + "integrity": "sha512-honAfLBde0HAFLdNyBEfuuENkF6zR+ozxqxa/2zJKHBe1qzLqyTSeRKpdPEHAP03rlDGyQOPnCSxnVpVqQo9Mg==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001797", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001797.tgz", + "integrity": "sha512-l8xKG+gwAIExZGl9FrF7KUwuOmk6wbEPC9Xoy/RtnWv1XG0Q4LFlagaLpUv3Kiza3W/wm27zy0yWJEieYKAP6w==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-loader": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.4.tgz", + "integrity": "sha512-vv3J9tlOl04WjiMvHQI/9tmIrCxVrj6PFbHemBB1iihpeRbi/I4h033eoFIhwxBBqLhI0KYFS7yvynBFhIZfTw==", + "license": "MIT", + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.40", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.6.3" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || ^1.0.0 || ^2.0.0-0", + "webpack": "^5.27.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/css-loader/node_modules/semver": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", + "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", + "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.371", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.371.tgz", + "integrity": "sha512-e9htk9mAYL6AzmkEhSvVVw7IWGSBJ/Bqdn2eRyRLrj1g6sncN4WbFt5qnILYoCktktr45pyjIrOiRvBThQ808w==", + "license": "ISC" + }, + "node_modules/enhanced-resolve": { + "version": "5.23.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.23.0.tgz", + "integrity": "sha512-yJN/BOOLxcOW2aQgeif9mSnaUB8KtvmMMp56oA1kx1CRfBKbhZm2pJ+NBY+3eOboHxix8lfjWpHE0Ei5U8RbSA==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "license": "MIT", + "dependencies": { + "stackframe": "^1.3.4" + } + }, + "node_modules/es-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz", + "integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.0", + "@esbuild/android-arm": "0.28.0", + "@esbuild/android-arm64": "0.28.0", + "@esbuild/android-x64": "0.28.0", + "@esbuild/darwin-arm64": "0.28.0", + "@esbuild/darwin-x64": "0.28.0", + "@esbuild/freebsd-arm64": "0.28.0", + "@esbuild/freebsd-x64": "0.28.0", + "@esbuild/linux-arm": "0.28.0", + "@esbuild/linux-arm64": "0.28.0", + "@esbuild/linux-ia32": "0.28.0", + "@esbuild/linux-loong64": "0.28.0", + "@esbuild/linux-mips64el": "0.28.0", + "@esbuild/linux-ppc64": "0.28.0", + "@esbuild/linux-riscv64": "0.28.0", + "@esbuild/linux-s390x": "0.28.0", + "@esbuild/linux-x64": "0.28.0", + "@esbuild/netbsd-arm64": "0.28.0", + "@esbuild/netbsd-x64": "0.28.0", + "@esbuild/openbsd-arm64": "0.28.0", + "@esbuild/openbsd-x64": "0.28.0", + "@esbuild/openharmony-arm64": "0.28.0", + "@esbuild/sunos-x64": "0.28.0", + "@esbuild/win32-arm64": "0.28.0", + "@esbuild/win32-ia32": "0.28.0", + "@esbuild/win32-x64": "0.28.0" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", + "license": "Unlicense" + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "license": "BSD-2-Clause" + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/html-entities": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz", + "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ], + "license": "MIT" + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/loader-runner": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.2.tgz", + "integrity": "sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==", + "license": "MIT", + "engines": { + "node": ">=6.11.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mediabunny": { + "version": "1.45.0", + "resolved": "https://registry.npmjs.org/mediabunny/-/mediabunny-1.45.0.tgz", + "integrity": "sha512-oK3sMMYbucoF6LUX62L/2M9d+p9ve6KDQgL87kNfhsB0/XmTe9iRLUcgQgg9Gpgvi8Sb96zYfOUL6i17y0bdNg==", + "license": "MPL-2.0", + "workspaces": [ + ".", + "packages/*" + ], + "dependencies": { + "@types/dom-mediacapture-transform": "^0.1.11", + "@types/dom-webcodecs": "0.1.13" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/Vanilagy" + } + }, + "node_modules/memfs": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.3.tgz", + "integrity": "sha512-eivjfi7Ahr6eQTn44nvTnR60e4a1Fs1Via2kCR5lHo/kyNoiMWaXCNJ/GpSd0ilXas2JSOl9B5FTIhflXu0hlg==", + "license": "Unlicense", + "dependencies": { + "fs-monkey": "1.0.3" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "license": "MIT" + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.47", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.47.tgz", + "integrity": "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz", + "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", + "license": "MIT", + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^7.0.0", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", + "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "license": "ISC", + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-selector-parser": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.2.tgz", + "integrity": "sha512-Wjvt4scRFouioIInHf51IFNP4ltJ2EngJM+cZPGiqbKetBfmP3vpdPV8ID2S6JS6/jdo74N8+aEYH9lQr2C6sA==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "license": "MIT" + }, + "node_modules/prettier": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", + "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz", + "integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.25.0" + }, + "peerDependencies": { + "react": "^19.0.0" + } + }, + "node_modules/react-refresh": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/recast": { + "version": "0.23.11", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.11.tgz", + "integrity": "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==", + "license": "MIT", + "dependencies": { + "ast-types": "^0.16.1", + "esprima": "~4.0.0", + "source-map": "~0.6.1", + "tiny-invariant": "^1.3.3", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/recast/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/remotion": { + "version": "4.0.474", + "resolved": "https://registry.npmjs.org/remotion/-/remotion-4.0.474.tgz", + "integrity": "sha512-0dlDJLXlhB8fp/22Qz4Kn7YkFzhs04ymOLolp4TUQeJ+ltuYzt9w+MSuA6HI6NO8Eoc35y6QecXmofEETlMUBg==", + "license": "SEE LICENSE IN LICENSE.md", + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/scheduler": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz", + "integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==", + "license": "MIT" + }, + "node_modules/schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/semver": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT" + }, + "node_modules/source-map": { + "version": "0.8.0-beta.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", + "deprecated": "The work that was done in this beta branch won't be included in future versions", + "license": "BSD-3-Clause", + "dependencies": { + "whatwg-url": "^7.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", + "license": "MIT" + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/style-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-4.0.0.tgz", + "integrity": "sha512-1V4WqhhZZgjVAVJyt7TdDPZoPBPNHbekX4fWnCJL1yQukhCeZhJySUL+gL9y6sNdN95uEOS83Y55SqHcP7MzLA==", + "license": "MIT", + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.27.0" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser": { + "version": "5.48.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.48.0.tgz", + "integrity": "sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==", + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.6.1.tgz", + "integrity": "sha512-201R5j+sJpK8nFWwKVyNfZot8FaJbLZDq5evriVzbV1wDtSXDjRUDRfJzHpAaxFDMEhsZL1QkeqM61wgsS3KaQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@minify-html/node": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "@swc/css": { + "optional": true + }, + "@swc/html": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "cssnano": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "html-minifier-terser": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "postcss": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", + "license": "MIT", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/typescript": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/watchpack": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", + "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", + "license": "MIT", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "license": "BSD-2-Clause" + }, + "node_modules/webpack": { + "version": "5.105.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.0.tgz", + "integrity": "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==", + "license": "MIT", + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.15.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.28.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.19.0", + "es-module-lexer": "^2.0.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.3.1", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", + "terser-webpack-plugin": "^5.3.16", + "watchpack": "^2.5.1", + "webpack-sources": "^3.3.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-sources": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.5.0.tgz", + "integrity": "sha512-HPuy+uuoTCaaoEoI1LQ3JN9+vrPBvEesnnX1jADHy728cHSMlq4wUc4afYqahq2B1mhQVZxCXOkNTnXltr+2vQ==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "license": "MIT", + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/ws": { + "version": "8.20.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.1.tgz", + "integrity": "sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/demo/package.json b/demo/package.json new file mode 100644 index 00000000..dac29063 --- /dev/null +++ b/demo/package.json @@ -0,0 +1,67 @@ +{ + "name": "@primitivedotdev/demo-video", + "version": "0.1.0", + "private": true, + "description": "Remotion marketing demo: install → sign up → inbox → send with the Primitive CLI. Standalone; not part of the pnpm SDK workspace.", + "license": "MIT", + "type": "module", + "scripts": { + "studio": "remotion studio", + "typecheck": "tsc --noEmit", + "render:landscape": "remotion render Demo-Landscape out/demo-landscape.mp4 --scale=2 --crf=16", + "render:square": "remotion render Demo-Square out/demo-square.mp4 --scale=2 --crf=16", + "render:vertical": "remotion render Demo-Vertical out/demo-vertical.mp4 --scale=2 --crf=16", + "render:gif": "remotion render Demo-Gif out/demo.gif --codec=gif --scale=1.5 --every-nth-frame=2", + "render:webm": "remotion render Demo-Landscape out/demo.webm --codec=vp9 --scale=2 --crf=18", + "render:all": "npm run render:landscape && npm run render:square && npm run render:vertical && npm run render:gif", + "render:explained-landscape": "remotion render Explained-Landscape out/demo-explained-landscape.mp4 --scale=2 --crf=16", + "render:explained-square": "remotion render Explained-Square out/demo-explained-square.mp4 --scale=2 --crf=16", + "render:explained-vertical": "remotion render Explained-Vertical out/demo-explained-vertical.mp4 --scale=2 --crf=16", + "render:explained-webm": "remotion render Explained-Landscape out/demo-explained.webm --codec=vp9 --scale=2 --crf=18", + "render:explained-all": "npm run render:explained-webm && npm run render:explained-landscape && npm run render:explained-square && npm run render:explained-vertical", + "render:agent-landscape": "remotion render Agent-Landscape out/demo-agent-landscape.mp4 --scale=2 --crf=16", + "render:agent-square": "remotion render Agent-Square out/demo-agent-square.mp4 --scale=2 --crf=16", + "render:agent-vertical": "remotion render Agent-Vertical out/demo-agent-vertical.mp4 --scale=2 --crf=16", + "render:agent-webm": "remotion render Agent-Landscape out/demo-agent.webm --codec=vp9 --scale=2 --crf=18", + "render:agent-all": "npm run render:agent-webm && npm run render:agent-landscape && npm run render:agent-square && npm run render:agent-vertical", + "render:network-landscape": "remotion render Network-Landscape out/demo-network-landscape.mp4 --scale=2 --crf=16", + "render:network-square": "remotion render Network-Square out/demo-network-square.mp4 --scale=2 --crf=16", + "render:network-vertical": "remotion render Network-Vertical out/demo-network-vertical.mp4 --scale=2 --crf=16", + "render:network-webm": "remotion render Network-Landscape out/demo-network.webm --codec=vp9 --scale=2 --crf=18", + "render:network-all": "npm run render:network-webm && npm run render:network-landscape && npm run render:network-square && npm run render:network-vertical", + "render:agent-signup-landscape": "remotion render AgentSignup-Landscape out/demo-agent-signup-landscape.mp4 --scale=2 --crf=16", + "render:agent-signup-square": "remotion render AgentSignup-Square out/demo-agent-signup-square.mp4 --scale=2 --crf=16", + "render:agent-signup-vertical": "remotion render AgentSignup-Vertical out/demo-agent-signup-vertical.mp4 --scale=2 --crf=16", + "render:agent-signup-webm": "remotion render AgentSignup-Landscape out/demo-agent-signup.webm --codec=vp9 --scale=2 --crf=18", + "render:agent-signup-all": "npm run render:agent-signup-webm && npm run render:agent-signup-landscape && npm run render:agent-signup-square && npm run render:agent-signup-vertical", + "render:x402-landscape": "remotion render X402-Landscape out/demo-x402-landscape.mp4 --scale=2 --crf=16", + "render:x402-square": "remotion render X402-Square out/demo-x402-square.mp4 --scale=2 --crf=16", + "render:x402-vertical": "remotion render X402-Vertical out/demo-x402-vertical.mp4 --scale=2 --crf=16", + "render:x402-webm": "remotion render X402-Landscape out/demo-x402.webm --codec=vp9 --scale=2 --crf=18", + "render:x402-all": "npm run render:x402-webm && npm run render:x402-landscape && npm run render:x402-square && npm run render:x402-vertical", + "render:chatgpt-landscape": "remotion render ChatGPT-Landscape out/demo-chatgpt-landscape.mp4 --scale=2 --crf=16 --concurrency=6", + "render:chatgpt-square": "remotion render ChatGPT-Square out/demo-chatgpt-square.mp4 --scale=2 --crf=16", + "render:chatgpt-vertical": "remotion render ChatGPT-Vertical out/demo-chatgpt-vertical.mp4 --scale=2 --crf=16", + "render:chatgpt-webm": "remotion render ChatGPT-Landscape out/demo-chatgpt.webm --codec=vp9 --scale=2 --crf=18", + "render:chatgpt-all": "npm run render:chatgpt-webm && npm run render:chatgpt-landscape && npm run render:chatgpt-square && npm run render:chatgpt-vertical", + "render:combined-landscape": "remotion render Combined-Landscape out/demo-combined-landscape.mp4 --scale=2 --crf=16 --concurrency=6", + "render:combined-segments": "remotion render Combined-Intro out/seg-1-intro.mp4 --scale=2 --crf=16 --concurrency=6 && remotion render Combined-Step1 out/seg-2-signup.mp4 --scale=2 --crf=16 --concurrency=6 && remotion render Combined-Step2 out/seg-3-deploy.mp4 --scale=2 --crf=16 --concurrency=6 && remotion render Combined-Step3 out/seg-4-chat.mp4 --scale=2 --crf=16 --concurrency=6 && remotion render Combined-Benchmarks out/seg-5-benchmarks.mp4 --scale=2 --crf=16 --concurrency=6", + "render:combined-nobench-landscape": "remotion render Combined-NoBench-Landscape out/demo-combined-nobench-landscape.mp4 --scale=2 --crf=16 --concurrency=6", + "render:combined-square": "remotion render Combined-Square out/demo-combined-square.mp4 --scale=2 --crf=16", + "render:combined-vertical": "remotion render Combined-Vertical out/demo-combined-vertical.mp4 --scale=2 --crf=16", + "render:combined-webm": "remotion render Combined-Landscape out/demo-combined.webm --codec=vp9 --scale=2 --crf=18", + "render:combined-all": "npm run render:combined-webm && npm run render:combined-landscape && npm run render:combined-square && npm run render:combined-vertical" + }, + "dependencies": { + "@remotion/cli": "4.0.474", + "@remotion/google-fonts": "4.0.474", + "react": "19.0.0", + "react-dom": "19.0.0", + "remotion": "4.0.474" + }, + "devDependencies": { + "@types/react": "19.0.0", + "@types/react-dom": "19.0.0", + "typescript": "5.7.2" + } +} diff --git a/demo/public/PrimitiveLogoMono.woff2 b/demo/public/PrimitiveLogoMono.woff2 new file mode 100644 index 00000000..9e42fe72 Binary files /dev/null and b/demo/public/PrimitiveLogoMono.woff2 differ diff --git a/demo/public/logo.txt b/demo/public/logo.txt new file mode 100644 index 00000000..27d40ac0 --- /dev/null +++ b/demo/public/logo.txt @@ -0,0 +1,25 @@ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣤⣤⣿⡛⠛⠛⠛⠛⠛⣿⣧⣤⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣰⡶⠾⠛⠛⠀⠀⠀⢷⡆⠀⠀⠀⠀⠀⠀⠈⠛⢻⣷⣶⣆⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣾⠉⠀⠀⠀⠀⠀⠀⠀⠸⢷⡆⠀⠀⠀⠀⠀⠀⢀⣸⡇⠈⠹⢷⣆⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣶⠶⠶⣶⣀⡀⠀⠀⠀⠀⠀⠀⠀⢸⡇⠀⠀⠀⠀⠀⠀⢸⡏⠁⠀⠀⠈⠹⢷⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⠉⠀⠀⠉⠹⠿⣧⣤⠀⠀⠀⠀⠀⠘⠃⠀⠀⠀⠀⠀⣤⠟⠁⠀⠀⠀⠀⠀⠈⣿⣤⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⢰⣤⣬⣅⣀⠀⠀⠀⠀⠀⠀⠛⡇⠀⣀⣠⣤⣼⡟⠛⠿⣿⣤⣤⣀⠀⠀⠀⠀⠀⠀⢀⣠⡤⠿⣤⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⢠⣤⣄⣉⠉⠹⠶⢦⣤⡀⠀⠀⠀⢀⣶⠛⠛⠀⠀⠀⠀⠀⠀⠀⠛⠻⣆⡀⢀⣀⣀⡰⠾⠏⠁⠀⣿⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⣈⣉⠉⠉⠀⠀⠀⠀⠀⠛⠳⠆⢠⣿⠋⠀⠀⠀⠀⣰⡶⠶⢶⣆⠀⠀⠹⣷⠈⠉⠉⠁⠀⠀⠀⠀⣿⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠛⠛⠻⠿⠿⣦⣤⡄⣀⣀⠀⢠⡼⠇⠀⠀⠀⠀⣶⠉⠁⣀⠈⢹⠀⠀⠀⣿⠀⠀⠀⠀⠀⠀⠀⠀⣿⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⢰⣶⣦⣤⣤⣤⡄⠀⠀⠉⠉⠃⢸⣷⠶⠏⠉⠉⠀⠿⣄⡀⠛⠛⠋⠀⢀⣰⡟⢀⣆⣀⠀⠀⠀⠀⣤⠿⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⣤⡄⠀⠀⣀⣀⣀⣤⣤⣤⡄⢸⣿⠀⠀⠀⠀⢠⡄⠹⢿⣤⣤⣤⣼⠟⠃⠀⠀⠀⠉⠛⠛⠛⣿⠛⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠰⠿⠿⠟⠛⠛⠉⠉⠀⠀⠀⠀⠀⠿⣆⡀⣀⣶⠛⠀⠀⠀⣠⠄⠀⠀⠿⣤⠀⠀⠀⠀⠀⢀⣶⠿⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⣷⣿⠀⠀⠀⠀⠀⣿⠀⠀⠀⠀⠙⢧⣤⠀⣀⣰⠾⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠛⠿⢦⣄⣀⣀⣿⡀⠀⠀⠀⣀⣀⡶⠶⠏⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠹⠿⠿⠿⠿⠿⠏⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ diff --git a/demo/public/openai-logo.svg b/demo/public/openai-logo.svg new file mode 100644 index 00000000..ebbdab0e --- /dev/null +++ b/demo/public/openai-logo.svg @@ -0,0 +1 @@ +OpenAI \ No newline at end of file diff --git a/demo/public/primitive-logo.svg b/demo/public/primitive-logo.svg new file mode 100644 index 00000000..dcef21be --- /dev/null +++ b/demo/public/primitive-logo.svg @@ -0,0 +1,167 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/remotion.config.ts b/demo/remotion.config.ts new file mode 100644 index 00000000..441d9013 --- /dev/null +++ b/demo/remotion.config.ts @@ -0,0 +1,7 @@ +import { Config } from "@remotion/cli/config"; + +Config.setVideoImageFormat("jpeg"); +Config.setOverwriteOutput(true); +// Note: CRF is set per-codec via the --crf flag where supported (the gif codec +// rejects it), so it is intentionally not configured globally here. H.264 +// defaults to CRF 18, which is what these marketing renders want. diff --git a/demo/src/DemoVideo.tsx b/demo/src/DemoVideo.tsx new file mode 100644 index 00000000..8c3ab2dc --- /dev/null +++ b/demo/src/DemoVideo.tsx @@ -0,0 +1,93 @@ +import { AbsoluteFill, interpolate, Series, useCurrentFrame, useVideoConfig } from "remotion"; +import { Session } from "./components/Session"; +import { StepLabel } from "./components/StepLabel"; +import { Terminal } from "./components/Terminal"; +import { TitleCard } from "./components/TitleCard"; +import { introTagline, introTitle, outroInstall, outroRepo } from "./script"; +import { type Format, f, theme } from "./theme"; +import { buildTimeline } from "./timeline"; + +export const INTRO_FRAMES = f(70); +export const OUTRO_FRAMES = f(95); +export const SESSION_TAIL = f(24); + +const margins: Record = { landscape: 96, square: 52, vertical: 56 }; +const cardScale: Record = { landscape: 1, square: 0.78, vertical: 0.95 }; + +export const sessionFrames = (): number => buildTimeline().total + SESSION_TAIL; +export const totalFrames = (): number => INTRO_FRAMES + sessionFrames() + OUTRO_FRAMES; + +const FadeIn: React.FC<{ children: React.ReactNode; frames?: number }> = ({ children, frames = f(12) }) => { + const frame = useCurrentFrame(); + const opacity = interpolate(frame, [0, frames], [0, 1], { extrapolateRight: "clamp" }); + return {children}; +}; + +export const DemoVideo: React.FC<{ format: Format }> = ({ format }) => { + const { width, height } = useVideoConfig(); + const timeline = buildTimeline(); + const fontSize = format === "square" ? 26 : 30; + const labelSize = fontSize * 0.78; + const margin = margins[format]; + + const labelBlock = labelSize * 2.4; + const gap = margin * 0.55; + const termW = width - margin * 2; + const termH = height - margin * 2 - labelBlock - gap; + + return ( + + + + + + + + + + + + + +
+ +
+ + + +
+
+
+ + + + + + + + +
+ +
+ ); +}; + +// Faint CRT scanlines over everything — nods to the brand's retro-computing / +// scanline motif without harming legibility. +export const Scanlines: React.FC = () => ( + +); diff --git a/demo/src/DemoVideoAgent.tsx b/demo/src/DemoVideoAgent.tsx new file mode 100644 index 00000000..85ea669e --- /dev/null +++ b/demo/src/DemoVideoAgent.tsx @@ -0,0 +1,71 @@ +import { AbsoluteFill, interpolate, Series, useCurrentFrame, useVideoConfig } from "remotion"; +import { Session } from "./components/Session"; +import { StepLabel } from "./components/StepLabel"; +import { Terminal } from "./components/Terminal"; +import { TitleCard } from "./components/TitleCard"; +import { INTRO_FRAMES, OUTRO_FRAMES, Scanlines, SESSION_TAIL } from "./DemoVideo"; +import { outroInstall, outroRepo } from "./script"; +import { agentIntroTagline, agentIntroTitle, agentSections } from "./scriptAgent"; +import { type Format, f, theme } from "./theme"; +import { buildTimeline } from "./timeline"; + +const margins: Record = { landscape: 96, square: 52, vertical: 56 }; +const cardScale: Record = { landscape: 1, square: 0.78, vertical: 0.95 }; + +export const agentSessionFrames = (): number => buildTimeline(agentSections).total + SESSION_TAIL; +export const agentTotalFrames = (): number => INTRO_FRAMES + agentSessionFrames() + OUTRO_FRAMES; + +const FadeIn: React.FC<{ children: React.ReactNode; frames?: number }> = ({ children, frames = f(12) }) => { + const frame = useCurrentFrame(); + const opacity = interpolate(frame, [0, frames], [0, 1], { extrapolateRight: "clamp" }); + return {children}; +}; + +export const DemoVideoAgent: React.FC<{ format: Format }> = ({ format }) => { + const { width, height } = useVideoConfig(); + const timeline = buildTimeline(agentSections); + const fontSize = format === "square" ? 25 : 29; + const labelSize = fontSize * 0.78; + const margin = margins[format]; + + const labelBlock = labelSize * 2.4; + const gap = margin * 0.55; + const termW = width - margin * 2; + const termH = height - margin * 2 - labelBlock - gap; + + return ( + + + + + + + + + + + + + +
+ +
+ + + +
+
+
+ + + + + + + + +
+ +
+ ); +}; diff --git a/demo/src/DemoVideoAgentSignup.tsx b/demo/src/DemoVideoAgentSignup.tsx new file mode 100644 index 00000000..f01df84b --- /dev/null +++ b/demo/src/DemoVideoAgentSignup.tsx @@ -0,0 +1,130 @@ +import { AbsoluteFill, interpolate, Series, useCurrentFrame, useVideoConfig } from "remotion"; +import { ExplainerPanel } from "./components/ExplainerPanel"; +import { Session } from "./components/Session"; +import { StepLabel } from "./components/StepLabel"; +import { Terminal } from "./components/Terminal"; +import { TitleCard } from "./components/TitleCard"; +import { Scanlines, SESSION_TAIL } from "./DemoVideo"; +import { + agentSignupCaption, + agentSignupIntroTitle, + agentSignupOutroInstall, + agentSignupSections, +} from "./scriptAgentSignup"; +import { type Format, f, theme } from "./theme"; +import { buildTimeline } from "./timeline"; + +// Explains the new agent signup flow: terminal + plain-language narration panel. +// Bookended by a 3-second logo card captioned "agent signup" at the start and +// end, per the brief. Paced tight so the whole clip lands under 20s while the +// narration panel still has time to read. +const SLOW = { holdScale: 1.3, leadInFrames: 36 }; + +// Logo bookends: 3s at the start, 1.5s at the end. (FPS = 60; f() maps from the +// 30fps baseline, so f(90)=3s and f(45)=1.5s.) +export const INTRO_LOGO_FRAMES = f(90); +export const OUTRO_LOGO_FRAMES = f(45); + +// Extra dwell on the final content screen (step 2's output) before the closing +// logo — holds the finished terminal on screen an additional 2.5s. +export const END_PAUSE_FRAMES = f(75); + +export const agentSignupSessionFrames = (): number => + buildTimeline(agentSignupSections, SLOW).total + SESSION_TAIL + END_PAUSE_FRAMES; +export const agentSignupTotalFrames = (): number => INTRO_LOGO_FRAMES + agentSignupSessionFrames() + OUTRO_LOGO_FRAMES; + +const cardScale: Record = { landscape: 1, square: 0.78, vertical: 0.95 }; + +interface Cfg { + margin: number; + gap: number; + row: boolean; + fontSize: number; + panelFont: number; + labelSize: number; +} +const cfg: Record = { + landscape: { margin: 80, gap: 40, row: true, fontSize: 28, panelFont: 24, labelSize: 22 }, + square: { margin: 44, gap: 26, row: false, fontSize: 23, panelFont: 18, labelSize: 18 }, + vertical: { margin: 48, gap: 32, row: false, fontSize: 30, panelFont: 25, labelSize: 23 }, +}; + +const FadeIn: React.FC<{ children: React.ReactNode; frames?: number }> = ({ children, frames = f(12) }) => { + const frame = useCurrentFrame(); + const opacity = interpolate(frame, [0, frames], [0, 1], { extrapolateRight: "clamp" }); + return {children}; +}; + +export const DemoVideoAgentSignup: React.FC<{ format: Format }> = ({ format }) => { + const { width, height } = useVideoConfig(); + const timeline = buildTimeline(agentSignupSections, SLOW); + const c = cfg[format]; + + const innerW = width - c.margin * 2; + const labelBlock = c.labelSize * 2.4; + const areaH = height - c.margin * 2 - labelBlock - c.gap; + + let termW: number; + let termH: number; + let panelW: number; + let panelH: number; + if (c.row) { + panelW = Math.round(innerW * 0.36); + termW = innerW - panelW - c.gap; + termH = areaH; + panelH = areaH; + } else { + panelH = Math.round(areaH * (format === "vertical" ? 0.26 : 0.32)); + termH = areaH - panelH - c.gap; + termW = innerW; + panelW = innerW; + } + + return ( + + + {/* Logo bookend — 3s, captioned "agent signup". */} + + + + + + + + + + + +
+ +
+
+ + + +
+ +
+
+
+
+
+ + {/* Logo bookend — 1.5s, captioned "agent signup" + CLI CTA. */} + + + + + + + +
+ +
+ ); +}; diff --git a/demo/src/DemoVideoChatGPT.tsx b/demo/src/DemoVideoChatGPT.tsx new file mode 100644 index 00000000..09c4d883 --- /dev/null +++ b/demo/src/DemoVideoChatGPT.tsx @@ -0,0 +1,72 @@ +import { AbsoluteFill, interpolate, Series, useCurrentFrame, useVideoConfig } from "remotion"; +import { ChatGPTApp, type ChatFormat } from "./components/ChatGPTApp"; +import { TitleCard } from "./components/TitleCard"; +import { INTRO_FRAMES, OUTRO_FRAMES, Scanlines } from "./DemoVideo"; +import { + buildChatTimeline, + chatGptIntroTagline, + chatGptIntroTitle, + chatGptOutroPrimary, + chatGptOutroUrl, +} from "./scriptChatGPT"; +import { type Format, f, theme } from "./theme"; + +const BODY_TAIL = f(22); +const cardScale: Record = { landscape: 1, square: 0.78, vertical: 0.95 }; + +const fmtConfig: Record = { + landscape: { colMax: 860, baseFont: 27, s: 1.42, widgetMax: 860, sidebar: true, pad: 40 }, + square: { colMax: 920, baseFont: 27, s: 1.46, widgetMax: 920, sidebar: false, pad: 36 }, + vertical: { colMax: 960, baseFont: 32, s: 1.62, widgetMax: 960, sidebar: false, pad: 40 }, +}; + +export const chatBodySessionFrames = (): number => f(buildChatTimeline().total) + BODY_TAIL; +export const chatGptTotalFrames = (): number => INTRO_FRAMES + chatBodySessionFrames() + OUTRO_FRAMES; + +const FadeIn: React.FC<{ children: React.ReactNode; frames?: number }> = ({ children, frames = f(12) }) => { + const frame = useCurrentFrame(); + const opacity = interpolate(frame, [0, frames], [0, 1], { extrapolateRight: "clamp" }); + return {children}; +}; + +export const DemoVideoChatGPT: React.FC<{ format: Format }> = ({ format }) => { + const { width, height } = useVideoConfig(); + const timeline = buildChatTimeline(); + const fmt = fmtConfig[format]; + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; diff --git a/demo/src/DemoVideoCombined.tsx b/demo/src/DemoVideoCombined.tsx new file mode 100644 index 00000000..ad535f7c --- /dev/null +++ b/demo/src/DemoVideoCombined.tsx @@ -0,0 +1,125 @@ +import { AbsoluteFill, interpolate, Series, useCurrentFrame, useVideoConfig } from "remotion"; +import { BenchmarksSlide } from "./components/BenchmarksSlide"; +import { ExplainerPanel } from "./components/ExplainerPanel"; +import { Session } from "./components/Session"; +import { StepLabel } from "./components/StepLabel"; +import { Terminal } from "./components/Terminal"; +import { TitleCard } from "./components/TitleCard"; +import { INTRO_FRAMES, Scanlines, SESSION_TAIL } from "./DemoVideo"; +import { combinedIntroTagline, combinedIntroTitle, combinedSections } from "./scriptCombined"; +import { type Format, f, theme } from "./theme"; +import { buildTimeline } from "./timeline"; + +// Combined demo (4 phases), explained side panel, paced to ~60s, closing on a +// benchmarks slide. +// clearPerSection gives each phase a fresh screen, so the lead-in is just a +// brief beat (not a long blank). Dwell time lives in holdScale (content on +// screen), not blank lead-ins. +// clearPerSection gives each phase a fresh screen. Snappy start (~0.5s, no +// sitting still), +2s end-dwell per step, and time spread evenly across each +// step's pauses (holdScale) rather than lumped. Benchmark slide is a fixed 10s. +export const COMBINED_INTRO_FRAMES = INTRO_FRAMES + f(120); // +4s on the "primitive" start screen +export const BENCH_FRAMES = f(420); // 14s benchmark slide +export const OUTRO_FRAMES = f(120); // 4s return to the opening logo +export const SLOW = { holdScale: 3.5, leadInFrames: 15, sectionEndHoldFrames: 60 }; + +export const combinedSessionFrames = (): number => buildTimeline(combinedSections, SLOW).total + SESSION_TAIL; +export const combinedTotalFrames = (): number => COMBINED_INTRO_FRAMES + combinedSessionFrames() + BENCH_FRAMES; +export const combinedTotalFramesNoBench = (): number => COMBINED_INTRO_FRAMES + combinedSessionFrames() + OUTRO_FRAMES; + +const cardScale: Record = { landscape: 1, square: 0.78, vertical: 0.95 }; + +interface Cfg { + margin: number; + gap: number; + row: boolean; + fontSize: number; + panelFont: number; + labelSize: number; +} +const cfg: Record = { + landscape: { margin: 80, gap: 40, row: true, fontSize: 27, panelFont: 32, labelSize: 22 }, + square: { margin: 44, gap: 26, row: false, fontSize: 22, panelFont: 24, labelSize: 18 }, + vertical: { margin: 48, gap: 32, row: false, fontSize: 29, panelFont: 34, labelSize: 23 }, +}; + +const FadeIn: React.FC<{ children: React.ReactNode; frames?: number }> = ({ children, frames = f(12) }) => { + const frame = useCurrentFrame(); + const opacity = interpolate(frame, [0, frames], [0, 1], { extrapolateRight: "clamp" }); + return {children}; +}; + +export const DemoVideoCombined: React.FC<{ format: Format; showBenchmarks?: boolean }> = ({ format, showBenchmarks = true }) => { + const { width, height } = useVideoConfig(); + const timeline = buildTimeline(combinedSections, SLOW); + const c = cfg[format]; + + const innerW = width - c.margin * 2; + const labelBlock = c.labelSize * 2.4; + const areaH = height - c.margin * 2 - labelBlock - c.gap; + + let termW: number; + let termH: number; + let panelW: number; + let panelH: number; + if (c.row) { + panelW = Math.round(innerW * 0.36); + termW = innerW - panelW - c.gap; + termH = areaH; + panelH = areaH; + } else { + panelH = Math.round(areaH * (format === "vertical" ? 0.26 : 0.32)); + termH = areaH - panelH - c.gap; + termW = innerW; + panelW = innerW; + } + + return ( + + + + + + + + + + + + + +
+ +
+
+ + + +
+ +
+
+
+
+
+ + {showBenchmarks ? ( + + + + + + ) : ( + + + + + + + + )} +
+ +
+ ); +}; diff --git a/demo/src/DemoVideoCombinedSegment.tsx b/demo/src/DemoVideoCombinedSegment.tsx new file mode 100644 index 00000000..5e142083 --- /dev/null +++ b/demo/src/DemoVideoCombinedSegment.tsx @@ -0,0 +1,105 @@ +import { AbsoluteFill, interpolate, useCurrentFrame, useVideoConfig } from "remotion"; +import { BenchmarksSlide } from "./components/BenchmarksSlide"; +import { ExplainerPanel } from "./components/ExplainerPanel"; +import { Session } from "./components/Session"; +import { StepLabel } from "./components/StepLabel"; +import { Terminal } from "./components/Terminal"; +import { TitleCard } from "./components/TitleCard"; +import { Scanlines } from "./DemoVideo"; +import { BENCH_FRAMES, COMBINED_INTRO_FRAMES, SLOW } from "./DemoVideoCombined"; +import { combinedIntroTagline, combinedIntroTitle, combinedSections } from "./scriptCombined"; +import { type Format, f, theme } from "./theme"; +import { buildTimeline } from "./timeline"; + +// The combined demo, split into standalone clips: the intro card, each step, +// and the benchmarks slide. Each renders identically to its slice of the full +// video, starting at frame 0. +export type SegmentKind = "intro" | "step1" | "step2" | "step3" | "benchmarks"; + +const STEP_INDEX: Record = { step1: 0, step2: 1, step3: 2 }; + +export const segmentFrames = (kind: SegmentKind): number => { + if (kind === "intro") return COMBINED_INTRO_FRAMES; + if (kind === "benchmarks") return BENCH_FRAMES; + return buildTimeline([combinedSections[STEP_INDEX[kind]]], SLOW).total; +}; + +const cardScale: Record = { landscape: 1, square: 0.78, vertical: 0.95 }; + +interface Cfg { + margin: number; + gap: number; + row: boolean; + fontSize: number; + panelFont: number; + labelSize: number; +} +const cfg: Record = { + landscape: { margin: 80, gap: 40, row: true, fontSize: 27, panelFont: 32, labelSize: 22 }, + square: { margin: 44, gap: 26, row: false, fontSize: 22, panelFont: 24, labelSize: 18 }, + vertical: { margin: 48, gap: 32, row: false, fontSize: 29, panelFont: 34, labelSize: 23 }, +}; + +const FadeIn: React.FC<{ children: React.ReactNode }> = ({ children }) => { + const frame = useCurrentFrame(); + const opacity = interpolate(frame, [0, f(12)], [0, 1], { extrapolateRight: "clamp" }); + return {children}; +}; + +export const DemoVideoCombinedSegment: React.FC<{ format: Format; kind: SegmentKind }> = ({ format, kind }) => { + const { width, height } = useVideoConfig(); + + let inner: React.ReactNode; + if (kind === "intro") { + inner = ( + + + + ); + } else if (kind === "benchmarks") { + inner = ; + } else { + const c = cfg[format]; + const timeline = buildTimeline([combinedSections[STEP_INDEX[kind]]], SLOW); + const innerW = width - c.margin * 2; + const labelBlock = c.labelSize * 2.4; + const areaH = height - c.margin * 2 - labelBlock - c.gap; + let termW: number; + let termH: number; + let panelW: number; + let panelH: number; + if (c.row) { + panelW = Math.round(innerW * 0.36); + termW = innerW - panelW - c.gap; + termH = areaH; + panelH = areaH; + } else { + panelH = Math.round(areaH * (format === "vertical" ? 0.26 : 0.32)); + termH = areaH - panelH - c.gap; + termW = innerW; + panelW = innerW; + } + inner = ( + +
+ +
+
+ + + +
+ +
+
+
+ ); + } + + return ( + + {inner} + + + ); +}; diff --git a/demo/src/DemoVideoExplained.tsx b/demo/src/DemoVideoExplained.tsx new file mode 100644 index 00000000..8de62f51 --- /dev/null +++ b/demo/src/DemoVideoExplained.tsx @@ -0,0 +1,105 @@ +import { AbsoluteFill, interpolate, Series, useCurrentFrame, useVideoConfig } from "remotion"; +import { ExplainerPanel } from "./components/ExplainerPanel"; +import { Session } from "./components/Session"; +import { StepLabel } from "./components/StepLabel"; +import { Terminal } from "./components/Terminal"; +import { TitleCard } from "./components/TitleCard"; +import { INTRO_FRAMES, OUTRO_FRAMES, Scanlines, SESSION_TAIL } from "./DemoVideo"; +import { introTagline, introTitle, outroInstall, outroRepo, sections } from "./script"; +import { type Format, f, theme } from "./theme"; +import { buildTimeline } from "./timeline"; + +// Slower, narrated cut: longer holds + a reading pause before each step. +const SLOW = { holdScale: 1.9, leadInFrames: 55 }; + +export const explainedSessionFrames = (): number => buildTimeline(sections, SLOW).total + SESSION_TAIL; +export const explainedTotalFrames = (): number => INTRO_FRAMES + explainedSessionFrames() + OUTRO_FRAMES; + +const cardScale: Record = { landscape: 1, square: 0.78, vertical: 0.95 }; + +interface Cfg { + margin: number; + gap: number; + row: boolean; + fontSize: number; + panelFont: number; + labelSize: number; +} +const cfg: Record = { + landscape: { margin: 80, gap: 40, row: true, fontSize: 28, panelFont: 24, labelSize: 22 }, + square: { margin: 44, gap: 26, row: false, fontSize: 23, panelFont: 18, labelSize: 18 }, + vertical: { margin: 48, gap: 32, row: false, fontSize: 30, panelFont: 25, labelSize: 23 }, +}; + +const FadeIn: React.FC<{ children: React.ReactNode; frames?: number }> = ({ children, frames = f(12) }) => { + const frame = useCurrentFrame(); + const opacity = interpolate(frame, [0, frames], [0, 1], { extrapolateRight: "clamp" }); + return {children}; +}; + +export const DemoVideoExplained: React.FC<{ format: Format }> = ({ format }) => { + const { width, height } = useVideoConfig(); + const timeline = buildTimeline(sections, SLOW); + const c = cfg[format]; + + const innerW = width - c.margin * 2; + const labelBlock = c.labelSize * 2.4; + const areaH = height - c.margin * 2 - labelBlock - c.gap; + + let termW: number; + let termH: number; + let panelW: number; + let panelH: number; + if (c.row) { + panelW = Math.round(innerW * 0.36); + termW = innerW - panelW - c.gap; + termH = areaH; + panelH = areaH; + } else { + panelH = Math.round(areaH * (format === "vertical" ? 0.26 : 0.32)); + termH = areaH - panelH - c.gap; + termW = innerW; + panelW = innerW; + } + + return ( + + + + + + + + + + + + + +
+ +
+
+ + + +
+ +
+
+
+
+
+ + + + + + + + +
+ +
+ ); +}; diff --git a/demo/src/DemoVideoNetwork.tsx b/demo/src/DemoVideoNetwork.tsx new file mode 100644 index 00000000..bf3af987 --- /dev/null +++ b/demo/src/DemoVideoNetwork.tsx @@ -0,0 +1,111 @@ +import { AbsoluteFill, interpolate, Series, useCurrentFrame, useVideoConfig } from "remotion"; +import { ExplainerPanel } from "./components/ExplainerPanel"; +import { Session } from "./components/Session"; +import { StepLabel } from "./components/StepLabel"; +import { Terminal } from "./components/Terminal"; +import { TitleCard } from "./components/TitleCard"; +import { INTRO_FRAMES, OUTRO_FRAMES, Scanlines, SESSION_TAIL } from "./DemoVideo"; +import { + networkIntroTagline, + networkIntroTitle, + networkOutroInstall, + networkOutroRepo, + networkSections, +} from "./scriptNetwork"; +import { type Format, f, theme } from "./theme"; +import { buildTimeline } from "./timeline"; + +// YC cut: minimal + explanatory, paced to ~60s. Terminal + narration panel. +const SLOW = { holdScale: 3.7, leadInFrames: 165 }; + +export const networkSessionFrames = (): number => buildTimeline(networkSections, SLOW).total + SESSION_TAIL; +export const networkTotalFrames = (): number => INTRO_FRAMES + networkSessionFrames() + OUTRO_FRAMES; + +const cardScale: Record = { landscape: 1, square: 0.78, vertical: 0.95 }; + +interface Cfg { + margin: number; + gap: number; + row: boolean; + fontSize: number; + panelFont: number; + labelSize: number; +} +const cfg: Record = { + landscape: { margin: 80, gap: 40, row: true, fontSize: 28, panelFont: 24, labelSize: 22 }, + square: { margin: 44, gap: 26, row: false, fontSize: 23, panelFont: 18, labelSize: 18 }, + vertical: { margin: 48, gap: 32, row: false, fontSize: 30, panelFont: 25, labelSize: 23 }, +}; + +const FadeIn: React.FC<{ children: React.ReactNode; frames?: number }> = ({ children, frames = f(12) }) => { + const frame = useCurrentFrame(); + const opacity = interpolate(frame, [0, frames], [0, 1], { extrapolateRight: "clamp" }); + return {children}; +}; + +export const DemoVideoNetwork: React.FC<{ format: Format }> = ({ format }) => { + const { width, height } = useVideoConfig(); + const timeline = buildTimeline(networkSections, SLOW); + const c = cfg[format]; + + const innerW = width - c.margin * 2; + const labelBlock = c.labelSize * 2.4; + const areaH = height - c.margin * 2 - labelBlock - c.gap; + + let termW: number; + let termH: number; + let panelW: number; + let panelH: number; + if (c.row) { + panelW = Math.round(innerW * 0.36); + termW = innerW - panelW - c.gap; + termH = areaH; + panelH = areaH; + } else { + panelH = Math.round(areaH * (format === "vertical" ? 0.26 : 0.32)); + termH = areaH - panelH - c.gap; + termW = innerW; + panelW = innerW; + } + + return ( + + + + + + + + + + + + + +
+ +
+
+ + + +
+ +
+
+
+
+
+ + + + + + + + +
+ +
+ ); +}; diff --git a/demo/src/DemoVideoX402.tsx b/demo/src/DemoVideoX402.tsx new file mode 100644 index 00000000..e1747f4a --- /dev/null +++ b/demo/src/DemoVideoX402.tsx @@ -0,0 +1,130 @@ +import { AbsoluteFill, interpolate, Series, useCurrentFrame, useVideoConfig } from "remotion"; +import { ExplainerPanel } from "./components/ExplainerPanel"; +import { Session } from "./components/Session"; +import { StepLabel } from "./components/StepLabel"; +import { Terminal } from "./components/Terminal"; +import { TitleCard } from "./components/TitleCard"; +import { Scanlines, SESSION_TAIL } from "./DemoVideo"; +import { + x402Caption, + x402IntroTitle, + x402OutroInstall, + x402Sections, +} from "./scriptX402"; +import { type Format, f, theme } from "./theme"; +import { buildTimeline } from "./timeline"; + +// Explains x402 agent-to-agent payments: terminal + plain-language narration +// panel, three beats (request → sign & settle → guardrails). Bookended by a +// logo card captioned "agent payments", matching the agent-signup clip so the +// launch-week series reads as one set. +const SLOW = { holdScale: 1.3, leadInFrames: 36 }; + +// Logo bookends: 3s at the start, 1.5s at the end. (FPS = 60; f() maps from the +// 30fps baseline, so f(90)=3s and f(45)=1.5s.) +export const INTRO_LOGO_FRAMES = f(90); +export const OUTRO_LOGO_FRAMES = f(45); + +// Extra dwell on the final content screen (the spend policy) before the closing +// logo — holds the finished terminal on screen an additional 2.5s. +export const END_PAUSE_FRAMES = f(75); + +export const x402SessionFrames = (): number => + buildTimeline(x402Sections, SLOW).total + SESSION_TAIL + END_PAUSE_FRAMES; +export const x402TotalFrames = (): number => INTRO_LOGO_FRAMES + x402SessionFrames() + OUTRO_LOGO_FRAMES; + +const cardScale: Record = { landscape: 1, square: 0.78, vertical: 0.95 }; + +interface Cfg { + margin: number; + gap: number; + row: boolean; + fontSize: number; + panelFont: number; + labelSize: number; +} +const cfg: Record = { + landscape: { margin: 80, gap: 40, row: true, fontSize: 28, panelFont: 24, labelSize: 22 }, + square: { margin: 44, gap: 26, row: false, fontSize: 23, panelFont: 18, labelSize: 18 }, + vertical: { margin: 48, gap: 32, row: false, fontSize: 30, panelFont: 25, labelSize: 23 }, +}; + +const FadeIn: React.FC<{ children: React.ReactNode; frames?: number }> = ({ children, frames = f(12) }) => { + const frame = useCurrentFrame(); + const opacity = interpolate(frame, [0, frames], [0, 1], { extrapolateRight: "clamp" }); + return {children}; +}; + +export const DemoVideoX402: React.FC<{ format: Format }> = ({ format }) => { + const { width, height } = useVideoConfig(); + const timeline = buildTimeline(x402Sections, SLOW); + const c = cfg[format]; + + const innerW = width - c.margin * 2; + const labelBlock = c.labelSize * 2.4; + const areaH = height - c.margin * 2 - labelBlock - c.gap; + + let termW: number; + let termH: number; + let panelW: number; + let panelH: number; + if (c.row) { + panelW = Math.round(innerW * 0.36); + termW = innerW - panelW - c.gap; + termH = areaH; + panelH = areaH; + } else { + panelH = Math.round(areaH * (format === "vertical" ? 0.26 : 0.32)); + termH = areaH - panelH - c.gap; + termW = innerW; + panelW = innerW; + } + + return ( + + + {/* Logo bookend — 3s, captioned "agent payments". */} + + + + + + + + + + + +
+ +
+
+ + + +
+ +
+
+
+
+
+ + {/* Logo bookend — 1.5s, captioned "agent payments" + SDK install CTA. */} + + + + + + + +
+ +
+ ); +}; diff --git a/demo/src/Root.tsx b/demo/src/Root.tsx new file mode 100644 index 00000000..fac6f7a3 --- /dev/null +++ b/demo/src/Root.tsx @@ -0,0 +1,313 @@ +import { Composition } from "remotion"; +import { DemoVideo, totalFrames } from "./DemoVideo"; +import { DemoVideoAgent, agentTotalFrames } from "./DemoVideoAgent"; +import { DemoVideoAgentSignup, agentSignupTotalFrames } from "./DemoVideoAgentSignup"; +import { DemoVideoChatGPT, chatGptTotalFrames } from "./DemoVideoChatGPT"; +import { DemoVideoCombined, combinedTotalFrames, combinedTotalFramesNoBench } from "./DemoVideoCombined"; +import { DemoVideoCombinedSegment, type SegmentKind, segmentFrames } from "./DemoVideoCombinedSegment"; +import { DemoVideoExplained, explainedTotalFrames } from "./DemoVideoExplained"; +import { DemoVideoNetwork, networkTotalFrames } from "./DemoVideoNetwork"; +import { DemoVideoX402, x402TotalFrames } from "./DemoVideoX402"; +import { FPS } from "./theme"; + +export const Root: React.FC = () => { + const duration = totalFrames(); + const explainedDuration = explainedTotalFrames(); + const agentDuration = agentTotalFrames(); + const networkDuration = networkTotalFrames(); + const agentSignupDuration = agentSignupTotalFrames(); + const x402Duration = x402TotalFrames(); + const chatGptDuration = chatGptTotalFrames(); + const combinedDuration = combinedTotalFrames(); + const combinedDurationNoBench = combinedTotalFramesNoBench(); + return ( + <> + + + + + + {/* Narrated cut (slower + explainer side panel) for non-technical viewers. */} + + + + + {/* Storyline 2: give your agent email (Claude Code MCP → build → deploy → chat). */} + + + + + {/* Storyline 3 (minimal): get any agent on the network — one CLI signup. */} + + + + + {/* Explains the new agent signup flow: start → verify → live inbox. + Logo bookends (3s each) captioned "agent signup". */} + + + + + {/* x402 agent-to-agent payments (launch-week-02): request → sign & settle → + guardrails. Logo bookends captioned "agent payments". */} + + + + + {/* Storyline: Primitive inside a ChatGPT App (Apps SDK). The connector + runs in a mocked ChatGPT conversation and renders the real Primitive + email-console widget inline. ~22s. */} + + + + + {/* Combined demo: signup → deploy function → add skill → chat with it (explained, ~60s). */} + + + + + {/* Combined demo without the closing benchmarks slide. */} + + + + + {/* Combined demo split into 5 standalone clips (intro · steps · benchmarks). */} + {(["intro", "step1", "step2", "step3", "benchmarks"] as SegmentKind[]).map((kind) => ( + + ))} + + ); +}; diff --git a/demo/src/components/BenchmarksSlide.tsx b/demo/src/components/BenchmarksSlide.tsx new file mode 100644 index 00000000..3d479524 --- /dev/null +++ b/demo/src/components/BenchmarksSlide.tsx @@ -0,0 +1,96 @@ +import { interpolate, spring, useCurrentFrame, useVideoConfig } from "remotion"; +import { f, monoFamily, theme } from "../theme"; +import { Logo } from "./Logo"; + +interface Stat { + value: string; + label: string; +} + +const STATS: Stat[] = [ + { value: "~250ms", label: "end-to-end, one primitive address to another" }, + { value: "2 min", label: "to fully deploy an agent to primitive" }, + { value: "40× cheaper", label: "98% margins vs. other agent-native email platforms" }, + { value: "#1 in Dev Tools\n#2 Overall", label: "on ora.ai — a leading agent-readiness benchmark" }, +]; + +// Deck-style closing slide: primitive-branded benchmarks grid. +export const BenchmarksSlide: React.FC<{ format: "landscape" | "square" | "vertical" }> = ({ format }) => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + const scale = format === "square" ? 0.8 : format === "vertical" ? 0.92 : 1; + const cols = format === "vertical" ? 1 : 2; + + const enter = spring({ frame, fps, config: { damping: 200 }, durationInFrames: f(20) }); + const headerY = interpolate(enter, [0, 1], [20, 0]); + + return ( +
+ {/* Brand header */} +
+ +
+ primitive +
+
+ by the numbers +
+
+ + {/* Stats grid */} +
+ {STATS.map((stat, i) => { + const local = spring({ frame, fps, config: { damping: 200 }, durationInFrames: f(18), delay: f(8) + i * f(7) }); + return ( +
+
+ {stat.value} +
+
+ {stat.label} +
+
+ ); + })} +
+ + {/* Footer URL */} +
+ https://primitive.dev +
+
+ ); +}; diff --git a/demo/src/components/ChatGPTApp.tsx b/demo/src/components/ChatGPTApp.tsx new file mode 100644 index 00000000..01ee6e91 --- /dev/null +++ b/demo/src/components/ChatGPTApp.tsx @@ -0,0 +1,450 @@ +import { Img, interpolate, staticFile, useCurrentFrame } from "remotion"; +import { f, monoFamily, uiSansFamily } from "../theme"; +import { type Step, type TimedStep } from "../scriptChatGPT"; +import { EmailWidget } from "./EmailWidget"; + +// A mocked ChatGPT (Apps SDK host) conversation, matched to the real product: +// pure-black canvas, a collapsed icon rail, the ChatGPT⌄ / Upgrade top bar, and +// the pill composer (+ · Ask anything · mic · voice). The host chrome is Inter; +// the inline result card is the genuine Primitive email-console widget. + +// ChatGPT dark chrome tokens (matched to the live app). +const C = { + bg: "#000000", + userBubble: "#303030", + text: "#ececec", + muted: "#b4b4b4", + faint: "#8e8e8e", + hair: "rgba(255,255,255,0.10)", + railIcon: "#c4c4c4", + composer: "#2f2f2f", + chipBg: "#1e1e1e", + upgradeBlue: "#7aa2ff", + avatarTeal: "#0f9d77", +} as const; + +export interface ChatFormat { + colMax: number; + baseFont: number; + s: number; // EmailWidget scale + widgetMax: number; + sidebar: boolean; // show the icon rail (desktop) vs. mobile top bar + pad: number; +} + +// Official OpenAI logomark (simple-icons, viewBox 0 0 24 24). +const OPENAI_PATH = + "M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z"; + +const OpenAIMark: React.FC<{ size: number; color?: string }> = ({ size, color = C.text }) => ( + + + +); + +// Generic stroked icon wrapper. +const Ico: React.FC<{ size: number; color?: string; children: React.ReactNode; fill?: boolean }> = ({ + size, + color = C.railIcon, + children, + fill, +}) => ( + + {children} + +); + +const IconNewChat: React.FC<{ size: number; color?: string }> = ({ size, color }) => ( + + + + +); +const IconSearch: React.FC<{ size: number; color?: string }> = ({ size, color }) => ( + + + + +); +const IconBubble: React.FC<{ size: number; color?: string }> = ({ size, color }) => ( + + + +); +const IconShare: React.FC<{ size: number; color?: string }> = ({ size, color }) => ( + + + + + +); +const IconDashedCircle: React.FC<{ size: number; color?: string }> = ({ size, color }) => ( + + + +); +const IconPlus: React.FC<{ size: number; color?: string }> = ({ size, color }) => ( + + + +); +const IconMic: React.FC<{ size: number; color?: string }> = ({ size, color }) => ( + + + + +); +const IconArrowUp: React.FC<{ size: number; color: string }> = ({ size, color }) => ( + + + +); +const IconSparkle: React.FC<{ size: number; color: string }> = ({ size, color }) => ( + + + +); +// Voice/dictation waveform inside the white circle. +const Waveform: React.FC<{ size: number; color: string }> = ({ size, color }) => { + const bars = [0.34, 0.62, 1, 0.62, 0.34]; + return ( + + {bars.map((h, i) => { + const x = 5 + i * 3.5; + const bh = 12 * h; + return ; + })} + + ); +}; + +// The real Primitive app icon (green ammonite on black), rounded like an app tile. +const PrimitiveMark: React.FC<{ size: number }> = ({ size }) => ( +
+ +
+); + +const TypingDots: React.FC<{ color: string; size: number }> = ({ color, size }) => { + const frame = useCurrentFrame(); + return ( + + {[0, 1, 2].map((i) => { + const phase = (frame / f(18) + i / 3) % 1; + const o = 0.3 + 0.7 * (0.5 + 0.5 * Math.sin(phase * Math.PI * 2)); + return ; + })} + + ); +}; + +// The "Used Primitive" tool row + result card: a live "Working" state that +// resolves into a pill, then reveals the email-console widget below it. +const ToolBlock: React.FC<{ ts: TimedStep; fmt: ChatFormat }> = ({ ts, fmt }) => { + const frame = useCurrentFrame(); + const step = ts.step as Extract; + const resolved = frame >= f(ts.resolveAt ?? 0); + const showWidget = frame >= f(ts.widgetAt ?? 0); + const fs = fmt.baseFont * 0.66; + const enter = interpolate(frame - f(ts.workingAt ?? 0), [0, f(8)], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" }); + return ( +
+
+ + {resolved ? ( + <> + Used + Primitive + {step.tool} + + + ) : ( + <> + {step.working} + + + )} +
+ {showWidget ? ( +
+ +
+ ) : null} +
+ ); +}; + +// Live "waiting for reply" beat — the tension before the inbound reply lands. +const WaitRow: React.FC<{ ts: TimedStep; fmt: ChatFormat }> = ({ ts, fmt }) => { + const frame = useCurrentFrame(); + const step = ts.step as Extract; + const fs = fmt.baseFont * 0.7; + const pulse = 0.55 + 0.45 * (0.5 + 0.5 * Math.sin(((frame % f(50)) / f(50)) * Math.PI * 2)); + return ( +
+
+ +
+ {step.label} + +
+ ); +}; + +const UserBubble: React.FC<{ ts: TimedStep; fmt: ChatFormat }> = ({ ts, fmt }) => { + const frame = useCurrentFrame(); + const step = ts.step as Extract; + const since = frame - f(ts.sendAt ?? 0); + const opacity = interpolate(since, [0, f(7)], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" }); + const dy = interpolate(since, [0, f(7)], [10, 0], { extrapolateLeft: "clamp", extrapolateRight: "clamp" }); + return ( +
+
+ {step.text} +
+
+ ); +}; + +const SayBlock: React.FC<{ ts: TimedStep; fmt: ChatFormat }> = ({ ts, fmt }) => { + const frame = useCurrentFrame(); + const step = ts.step as Extract; + const shown = Math.round( + interpolate(frame - f(ts.start), [0, f(ts.textDur ?? 1)], [0, step.text.length], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + }), + ); + const streaming = shown < step.text.length; + return ( +
+ {step.text.slice(0, shown)} + {streaming ? : null} +
+ ); +}; + +const Composer: React.FC<{ text: string; typing: boolean; fmt: ChatFormat }> = ({ text, typing, fmt }) => { + const frame = useCurrentFrame(); + const caretOn = Math.floor(frame / f(16)) % 2 === 0; + const bf = fmt.baseFont; + const hasText = text.length > 0; + const btn = bf * 1.7; + return ( +
+ + + {text || "Ask anything"} + {typing && caretOn ? | : null} + + {hasText ? ( +
+ +
+ ) : ( + <> + +
+ +
+ + )} +
+ ); +}; + +// Collapsed desktop icon rail. +const IconRail: React.FC<{ fmt: ChatFormat; width: number }> = ({ fmt, width }) => { + const icon = fmt.baseFont * 0.96; + const railBtn = (node: React.ReactNode) => ( +
{node}
+ ); + return ( +
+ {railBtn()} +
+ {railBtn()} + {railBtn()} + {railBtn()} +
+
+ AP +
+
+ ); +}; + +export const ChatGPTApp: React.FC<{ + steps: TimedStep[]; + fmt: ChatFormat; + width: number; + height: number; +}> = ({ steps, fmt, width, height }) => { + const frame = useCurrentFrame(); + + // Composer state: partial text while a user step is being typed, else empty. + let composerText = ""; + let composerTyping = false; + for (const ts of steps) { + if (ts.step.kind !== "user") continue; + if (frame >= f(ts.typeStart ?? 0) && frame < f(ts.sendAt ?? 0)) { + const prog = interpolate(frame - f(ts.typeStart ?? 0), [0, f(ts.typeDur ?? 1)], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" }); + composerText = ts.step.text.slice(0, Math.round(prog * ts.step.text.length)); + composerTyping = true; + break; + } + } + + const railW = Math.round(fmt.baseFont * 3.4); + const bf = fmt.baseFont; + + // A user step appears in the scrollback only once sent; every other step + // appears at its start frame. + const visible = (ts: TimedStep) => (ts.step.kind === "user" ? frame >= f(ts.sendAt ?? 0) : frame >= f(ts.start)); + + return ( +
+ {fmt.sidebar ? : null} +
+ {/* top bar */} +
+
+ {!fmt.sidebar ? : null} + ChatGPT + +
+
+ + + Upgrade + + + +
+
+ + {/* conversation (bottom-anchored, newest in view) */} +
+
+ {steps.map((ts, i) => { + if (!visible(ts)) return null; + if (ts.step.kind === "user") return ; + if (ts.step.kind === "tool") return ; + if (ts.step.kind === "wait") return ; + return ; + })} +
+
+ + {/* composer */} +
+
+ +
+
+
+
+ ); +}; diff --git a/demo/src/components/Cursor.tsx b/demo/src/components/Cursor.tsx new file mode 100644 index 00000000..06245c77 --- /dev/null +++ b/demo/src/components/Cursor.tsx @@ -0,0 +1,22 @@ +import { useCurrentFrame } from "remotion"; +import { f, theme } from "../theme"; + +// A blinking block cursor. `solid` keeps it fully on (used while typing). +export const Cursor: React.FC<{ fontSize: number; solid?: boolean }> = ({ fontSize, solid }) => { + const frame = useCurrentFrame(); + const on = solid ? true : Math.floor(frame / f(16)) % 2 === 0; + return ( + + ); +}; diff --git a/demo/src/components/EmailWidget.tsx b/demo/src/components/EmailWidget.tsx new file mode 100644 index 00000000..fd20baf8 --- /dev/null +++ b/demo/src/components/EmailWidget.tsx @@ -0,0 +1,214 @@ +import { Fragment } from "react"; +import { interpolate, useCurrentFrame } from "remotion"; +import { monoFamily } from "../theme"; +import type { WidgetEmail, WidgetPayload } from "../scriptChatGPT"; + +// Pixel-faithful re-render of the live Primitive email-console widget +// (ui://widget/primitive-email-v1.html) in its DARK theme — the exact card a +// user sees inline in ChatGPT. Every token, label, and layout rule mirrors the +// widget's own CSS + render() (header + status badge, metrics grid, "Messages" +// list / "Summary" key-values, "Raw result" disclosure). `s` scales the +// widget's native CSS px up to the video's resolution. + +// Dark brand tokens (the widget's :root[data-theme="dark"]). +const D = { + bg: "#1d1d1d", + fg: "#f4f4f4", + muted: "#9a9a9a", + border: "#f4f4f4", + badgeBg: "#181818", + badgeFg: "#f4f4f4", + okBg: "#14532d", + okFg: "#bbf7d0", + codeBg: "#181818", +} as const; + +const toolTitles: Record = { + getAccount: "Account", + getInboxStatus: "Inbox status", + listEmails: "Inbound emails", + searchEmails: "Email search", + getEmail: "Email details", + replyToEmail: "Reply sent", + sendEmail: "Email sent", +}; + +function isArray(d: WidgetPayload["data"]): d is WidgetEmail[] { + return Array.isArray(d); +} + +const Metric: React.FC<{ label: string; value: string; s: number }> = ({ label, value, s }) => ( +
+
+ {label} +
+
{value}
+
+); + +export const EmailWidget: React.FC<{ payload: WidgetPayload; s: number; appearAt: number }> = ({ + payload, + s, + appearAt, +}) => { + const frame = useCurrentFrame(); + const since = frame - appearAt; + const opacity = interpolate(since, [0, 14], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" }); + const dy = interpolate(since, [0, 18], [18 * s, 0], { extrapolateLeft: "clamp", extrapolateRight: "clamp" }); + + const title = toolTitles[payload.tool] ?? "Primitive result"; + const statusText = `HTTP ${payload.status}`; + + // renderMetrics, faithful to the widget. + const arr = isArray(payload.data) ? payload.data : null; + const obj = isArray(payload.data) ? null : payload.data; + const metrics: [string, string][] = [ + ["Tool", title], + ["Status", String(payload.status)], + ]; + if (arr) metrics.push(["Items", String(arr.length)]); + if (obj?.id) metrics.push(["ID", obj.id]); + if (obj?.status) metrics.push(["Record status", obj.status]); + + // Summary key-values (scalar entries of an object payload). + const summary = obj ? Object.entries(obj).slice(0, 8) : []; + + return ( +
+
+ {/* header */} +
+
+
+ {title} +
+
+ Latest Primitive MCP result +
+
+
+ {statusText} +
+
+ + {/* metrics grid */} +
+ {metrics.map(([label, value]) => ( + + ))} +
+ + {/* Messages list (array payload) */} + {arr ? ( +
+
+ Messages +
+
+ {arr.slice(0, 6).map((email, i) => { + const meta = [email.from_email && `From ${email.from_email}`, email.to_email && `To ${email.to_email}`, email.status] + .filter(Boolean) + .join(" | "); + const rowOpacity = interpolate(since, [10 + i * 6, 22 + i * 6], [0, 1], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + }); + return ( +
+
{email.subject}
+
{meta}
+
+ ); + })} +
+
+ ) : null} + + {/* Summary key-values (object payload) */} + {summary.length ? ( +
+
+ Summary +
+
+ {summary.map(([k, v]) => ( + +
{k}
+
{v}
+
+ ))} +
+
+ ) : null} + + {/* Raw result disclosure (collapsed, as the widget ships it) */} +
+ + Raw result +
+
+
+ ); +}; diff --git a/demo/src/components/ExplainerPanel.tsx b/demo/src/components/ExplainerPanel.tsx new file mode 100644 index 00000000..c202cd3a --- /dev/null +++ b/demo/src/components/ExplainerPanel.tsx @@ -0,0 +1,102 @@ +import { interpolate, useCurrentFrame } from "remotion"; +import { f, monoFamily, theme } from "../theme"; +import type { Section } from "../script"; +import type { SectionRange } from "../timeline"; + +// A side panel for non-technical viewers: names the current step, explains in +// plain language what's happening, and previews what's coming next. `sections` +// is the storyline being narrated (drives "step n / N" and the "next" preview). +export const ExplainerPanel: React.FC<{ + ranges: SectionRange[]; + fontSize: number; + sections: Section[]; + lowercase?: boolean; +}> = ({ ranges, fontSize, sections, lowercase }) => { + const labelCase = lowercase ? "lowercase" : "uppercase"; + const frame = useCurrentFrame(); + const NARRATED = sections.filter((s) => s.explainer); + const currentRange = ranges.find((r) => frame >= r.start && frame < r.end) ?? ranges[ranges.length - 1]; + const idx = NARRATED.findIndex((s) => s.id === currentRange.id); + const section = NARRATED[Math.max(0, idx)]; + const explainer = section?.explainer; + if (!explainer) return null; + + const nextHeading = NARRATED[idx + 1]?.explainer?.heading ?? "You're all set up"; + const since = frame - currentRange.start; + const opacity = interpolate(since, [0, f(10)], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" }); + const dx = interpolate(since, [0, f(12)], [18, 0], { extrapolateLeft: "clamp", extrapolateRight: "clamp" }); + + return ( +
+ {/* Progress badge */} +
+ Step {idx + 1} / {NARRATED.length} +
+ +
+
+ {explainer.heading} +
+
+ {explainer.body} +
+
+ + {/* What's coming next */} +
+ Next → {nextHeading} +
+
+ ); +}; diff --git a/demo/src/components/Logo.tsx b/demo/src/components/Logo.tsx new file mode 100644 index 00000000..52425287 --- /dev/null +++ b/demo/src/components/Logo.tsx @@ -0,0 +1,40 @@ +import { useCurrentFrame, useVideoConfig } from "remotion"; +import { LOGO_FONT, theme } from "../theme"; +import { LOGO_ART } from "../logoArt"; + +// The real primitive.dev mark: braille-art rendered in the custom logo font, +// scaleX(1.25), with the site's idle conic-gradient hue-shift (6s loop). +export const Logo: React.FC<{ fontSize: number }> = ({ fontSize }) => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + const loopFrames = fps * 6; // 6s hueShift loop, matching globals.css + const hue = ((frame % loopFrames) / loopFrames) * 360; + + return ( +
+      {LOGO_ART}
+    
+ ); +}; + +const monoFallback = 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace'; diff --git a/demo/src/components/OutputLine.tsx b/demo/src/components/OutputLine.tsx new file mode 100644 index 00000000..4a023ea5 --- /dev/null +++ b/demo/src/components/OutputLine.tsx @@ -0,0 +1,31 @@ +import { interpolate, useCurrentFrame } from "remotion"; +import { f, monoFamily, toneColor } from "../theme"; +import type { OutLineV } from "../timeline"; + +// Renders one pre-authored output line, fading/sliding in at its start frame. +export const OutputLine: React.FC<{ line: OutLineV; fontSize: number }> = ({ line, fontSize }) => { + const frame = useCurrentFrame(); + const elapsed = frame - line.start; + const opacity = interpolate(elapsed, [0, f(8)], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" }); + const dy = interpolate(elapsed, [0, f(8)], [6, 0], { extrapolateLeft: "clamp", extrapolateRight: "clamp" }); + const indent = " ".repeat(line.line.indent ?? 0); + + return ( +
+ {indent} + {line.line.text} +
+ ); +}; diff --git a/demo/src/components/Session.tsx b/demo/src/components/Session.tsx new file mode 100644 index 00000000..9deb091e --- /dev/null +++ b/demo/src/components/Session.tsx @@ -0,0 +1,47 @@ +import { useCurrentFrame } from "remotion"; +import type { Timeline } from "../timeline"; +import { OutputLine } from "./OutputLine"; +import { TypedLine } from "./TypedLine"; + +// Renders the terminal scrollback: every line whose start frame has passed, +// in order. The most recently started line owns the cursor while it is a +// command (typing or holding before its output appears). +// +// clearPerSection: render only the CURRENT section's lines, so the screen +// "refreshes" at each phase boundary instead of accumulating scrollback. +export const Session: React.FC<{ timeline: Timeline; fontSize: number; clearPerSection?: boolean }> = ({ + timeline, + fontSize, + clearPerSection, +}) => { + const frame = useCurrentFrame(); + + let lines = timeline.lines; + if (clearPerSection) { + const cur = + timeline.ranges.find((r) => frame >= r.start && frame < r.end) ?? timeline.ranges[timeline.ranges.length - 1]; + lines = timeline.lines.filter((l) => l.sectionId === cur.id); + } + + let cursorOwner = -1; + for (let i = 0; i < lines.length; i++) { + if (lines[i].start <= frame) cursorOwner = i; + } + if (cursorOwner >= 0 && lines[cursorOwner].kind !== "cmd") cursorOwner = -1; + + return ( + <> + {lines.map((line, i) => { + if (line.start > frame) return null; + const key = `${line.sectionId}-${line.kind}-${i}`; + if (line.kind === "cmd") { + return ; + } + if (line.kind === "out") { + return ; + } + return
; + })} + + ); +}; diff --git a/demo/src/components/StepLabel.tsx b/demo/src/components/StepLabel.tsx new file mode 100644 index 00000000..8775fa34 --- /dev/null +++ b/demo/src/components/StepLabel.tsx @@ -0,0 +1,41 @@ +import { interpolate, useCurrentFrame } from "remotion"; +import { f, monoFamily, theme } from "../theme"; +import type { SectionRange } from "../timeline"; + +// Brutalist step badge: square, green fill, thick foreground border, hard +// offset shadow, uppercase + tracked — same language as the site's Beta badge. +export const StepLabel: React.FC<{ ranges: SectionRange[]; fontSize: number; lowercase?: boolean }> = ({ + ranges, + fontSize, + lowercase, +}) => { + const frame = useCurrentFrame(); + const current = ranges.find((r) => frame >= r.start && frame < r.end) ?? ranges[ranges.length - 1]; + const since = frame - current.start; + const opacity = interpolate(since, [0, f(8)], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" }); + const pop = interpolate(since, [0, f(10)], [0.9, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" }); + + return ( +
+ {current.label} +
+ ); +}; diff --git a/demo/src/components/Terminal.tsx b/demo/src/components/Terminal.tsx new file mode 100644 index 00000000..da99fc04 --- /dev/null +++ b/demo/src/components/Terminal.tsx @@ -0,0 +1,83 @@ +import { monoFamily, theme } from "../theme"; + +const Dot: React.FC<{ color: string }> = ({ color }) => ( + // Square dots to match the brutalist (no border-radius) brand. +
+); + +// Brutalist terminal window: sharp corners, thick foreground border, hard +// offset shadow (no blur). Body is bottom-anchored so new lines push older +// ones up, like a real terminal. +export const Terminal: React.FC<{ + width: number; + height: number; + title: string; + bodyPadding: number; + children: React.ReactNode; + // Anchor body content to the top (fresh-screen feel) instead of the bottom + // (scrollback feel). + anchorTop?: boolean; +}> = ({ width, height, title, bodyPadding, children, anchorTop }) => { + return ( +
+
+ + + +
+ {title} +
+
+
+ {children} +
+
+ ); +}; diff --git a/demo/src/components/TitleCard.tsx b/demo/src/components/TitleCard.tsx new file mode 100644 index 00000000..dd86d539 --- /dev/null +++ b/demo/src/components/TitleCard.tsx @@ -0,0 +1,102 @@ +import { interpolate, spring, useCurrentFrame, useVideoConfig } from "remotion"; +import { f, monoFamily, theme } from "../theme"; +import { Logo } from "./Logo"; + +// Intro/outro card: braille-art mark + lowercase mono wordmark + supporting +// line, brutalist styling. `mono` renders `primary` as a green command chip. +export const TitleCard: React.FC<{ + wordmark: string; + primary: string; + secondary?: string; + scale: number; + mono?: boolean; + // Render the (non-mono) tagline lowercase as a sentence instead of the + // default uppercase tracked label. + taglineLower?: boolean; + // Optional green mono chip rendered at the very bottom (e.g. a CTA URL), + // below the tagline/secondary line. + chip?: string; +}> = ({ wordmark, primary, secondary, scale, mono, taglineLower, chip }) => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + + const enter = spring({ frame, fps, config: { damping: 200 }, durationInFrames: f(24) }); + const y = interpolate(enter, [0, 1], [24, 0]); + + return ( +
+ +
+ {wordmark} +
+ {mono ? ( + // Install command as a brutalist green-bordered chip. +
+ {primary} +
+ ) : ( +
+ {primary} +
+ )} + {secondary ? ( +
+ {secondary} +
+ ) : null} + {chip ? ( +
+ {chip} +
+ ) : null} +
+ ); +}; diff --git a/demo/src/components/TypedLine.tsx b/demo/src/components/TypedLine.tsx new file mode 100644 index 00000000..3269d631 --- /dev/null +++ b/demo/src/components/TypedLine.tsx @@ -0,0 +1,35 @@ +import { useCurrentFrame } from "remotion"; +import { monoFamily, toneColor } from "../theme"; +import type { CmdLine } from "../timeline"; +import { Cursor } from "./Cursor"; + +// Renders one command line, typing the text character-by-character. +// Shows a solid cursor while typing and a blinking cursor during the hold. +export const TypedLine: React.FC<{ line: CmdLine; fontSize: number; cursorActive: boolean }> = ({ + line, + fontSize, + cursorActive, +}) => { + const frame = useCurrentFrame(); + const elapsed = frame - line.start; + const progress = Math.min(1, Math.max(0, elapsed / line.typeDuration)); + const shown = Math.round(progress * line.text.length); + const typing = elapsed < line.typeDuration; + + return ( +
+ {line.prefix} + {line.text.slice(0, shown)} + {cursorActive ? : null} +
+ ); +}; diff --git a/demo/src/index.ts b/demo/src/index.ts new file mode 100644 index 00000000..e78af8ba --- /dev/null +++ b/demo/src/index.ts @@ -0,0 +1,4 @@ +import { registerRoot } from "remotion"; +import { Root } from "./Root"; + +registerRoot(Root); diff --git a/demo/src/logoArt.ts b/demo/src/logoArt.ts new file mode 100644 index 00000000..ca3036d3 --- /dev/null +++ b/demo/src/logoArt.ts @@ -0,0 +1,3 @@ +// AUTO-GENERATED (cropped) from primitive-mono-repo/web/app/logo.txt — the real +// brand braille-art mark. Blank padding rows trimmed. Re-copy if the mark changes. +export const LOGO_ART = "⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣤⣤⣿⡛⠛⠛⠛⠛⠛⣿⣧⣤⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀\n⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣰⡶⠾⠛⠛⠀⠀⠀⢷⡆⠀⠀⠀⠀⠀⠀⠈⠛⢻⣷⣶⣆⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀\n⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣾⠉⠀⠀⠀⠀⠀⠀⠀⠸⢷⡆⠀⠀⠀⠀⠀⠀⢀⣸⡇⠈⠹⢷⣆⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀\n⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣶⠶⠶⣶⣀⡀⠀⠀⠀⠀⠀⠀⠀⢸⡇⠀⠀⠀⠀⠀⠀⢸⡏⠁⠀⠀⠈⠹⢷⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀\n⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⠉⠀⠀⠉⠹⠿⣧⣤⠀⠀⠀⠀⠀⠘⠃⠀⠀⠀⠀⠀⣤⠟⠁⠀⠀⠀⠀⠀⠈⣿⣤⠀⠀⠀⠀⠀⠀⠀⠀\n⠀⠀⠀⠀⠀⠀⠀⢰⣤⣬⣅⣀⠀⠀⠀⠀⠀⠀⠛⡇⠀⣀⣠⣤⣼⡟⠛⠿⣿⣤⣤⣀⠀⠀⠀⠀⠀⠀⢀⣠⡤⠿⣤⠀⠀⠀⠀⠀⠀⠀\n⠀⠀⠀⠀⠀⠀⢠⣤⣄⣉⠉⠹⠶⢦⣤⡀⠀⠀⠀⢀⣶⠛⠛⠀⠀⠀⠀⠀⠀⠀⠛⠻⣆⡀⢀⣀⣀⡰⠾⠏⠁⠀⣿⠀⠀⠀⠀⠀⠀⠀\n⠀⠀⠀⠀⠀⠀⣈⣉⠉⠉⠀⠀⠀⠀⠀⠛⠳⠆⢠⣿⠋⠀⠀⠀⠀⣰⡶⠶⢶⣆⠀⠀⠹⣷⠈⠉⠉⠁⠀⠀⠀⠀⣿⠀⠀⠀⠀⠀⠀⠀\n⠀⠀⠀⠀⠀⠀⠛⠛⠻⠿⠿⣦⣤⡄⣀⣀⠀⢠⡼⠇⠀⠀⠀⠀⣶⠉⠁⣀⠈⢹⠀⠀⠀⣿⠀⠀⠀⠀⠀⠀⠀⠀⣿⠀⠀⠀⠀⠀⠀⠀\n⠀⠀⠀⠀⠀⢰⣶⣦⣤⣤⣤⡄⠀⠀⠉⠉⠃⢸⣷⠶⠏⠉⠉⠀⠿⣄⡀⠛⠛⠋⠀⢀⣰⡟⢀⣆⣀⠀⠀⠀⠀⣤⠿⠀⠀⠀⠀⠀⠀⠀\n⠀⠀⠀⠀⠀⠀⣤⡄⠀⠀⣀⣀⣀⣤⣤⣤⡄⢸⣿⠀⠀⠀⠀⢠⡄⠹⢿⣤⣤⣤⣼⠟⠃⠀⠀⠀⠉⠛⠛⠛⣿⠛⠀⠀⠀⠀⠀⠀⠀⠀\n⠀⠀⠀⠀⠀⠰⠿⠿⠟⠛⠛⠉⠉⠀⠀⠀⠀⠀⠿⣆⡀⣀⣶⠛⠀⠀⠀⣠⠄⠀⠀⠿⣤⠀⠀⠀⠀⠀⢀⣶⠿⠀⠀⠀⠀⠀⠀⠀⠀⠀\n⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⣷⣿⠀⠀⠀⠀⠀⣿⠀⠀⠀⠀⠙⢧⣤⠀⣀⣰⠾⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀\n⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠛⠿⢦⣄⣀⣀⣿⡀⠀⠀⠀⣀⣀⡶⠶⠏⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀\n⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠹⠿⠿⠿⠿⠿⠏⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀"; diff --git a/demo/src/script.ts b/demo/src/script.ts new file mode 100644 index 00000000..9c64c85c --- /dev/null +++ b/demo/src/script.ts @@ -0,0 +1,235 @@ +// The transcript for the demo. Every output line below is REAL — captured by +// running the actual @primitivedotdev/cli (v1.2.0) and the Primitive API, or +// reproduced verbatim from the command source. Nothing here is invented. +// +// Sources of truth: +// - install: real `npm install -g @primitivedotdev/cli` + `primitive --version` +// - signup: real run against staging (terms text, "Sent a 6-digit ...", +// "The code expires in 30 minutes."); confirm lines from +// cli-node/src/oclif/commands/signup.ts L800-803. +// - inbox: cli-node/src/oclif/commands/inbox-setup.ts formatInboxSetupGuide, +// populated from a real getInboxStatus response (ready account). +// - send: a REAL authenticated /send-mail envelope captured with wait:true +// (queue_id null, 64-hex client_idempotency_key, real smtp_* and +// delivery_status fields). CLI prints the inner data object via +// JSON.stringify(data, null, 2). +// - version: real `primitive --version` on this machine (node-v25.9.0). +// +// Identity is a clean demo persona (you@acme.dev / Acme / acme.dev + the managed +// receive domain swift-fox.primitive.email). Persona substitutions are limited +// to: email/org name/domain names, the org UUID, and the placeholder OTP 418207. +// The command flow, flags, messages, field names, value FORMATS, and formatting +// are exactly what the CLI emits. The inbox readiness counts (3 endpoints / +// 7 Functions) and the send envelope are real captured values. + +export type Tone = "default" | "dim" | "success" | "url" | "accent" | "key" | "string" | "amber"; + +export interface OutLine { + text: string; + tone?: Tone; + indent?: number; +} + +export interface CommandEntry { + kind: "command"; + prefix?: string; + prefixTone?: Tone; + text: string; + cps?: number; + hold?: number; +} + +export interface OutputEntry { + kind: "output"; + lines: OutLine[]; + perLine?: number; + hold?: number; +} + +export interface BlankEntry { + kind: "blank"; +} + +export type Entry = CommandEntry | OutputEntry | BlankEntry; + +export interface Explainer { + // Plain-language narration for the side panel (non-technical audience). + heading: string; + body: string; +} + +export interface Section { + id: string; + label: string; + explainer?: Explainer; + entries: Entry[]; +} + +// Brand voice from the marketing site: lowercase wordmark, "email for agents". +export const introTitle = "primitive"; +export const introTagline = "email for agents"; +export const outroInstall = "npm i -g @primitivedotdev/cli"; +export const outroRepo = "github.com/primitivedotdev/sdks"; + +export const sections: Section[] = [ + { + id: "install", + label: "1 · Install", + explainer: { + heading: "Install the tool", + body: "We start by installing Primitive's command-line tool. One line adds it to your computer — no dashboard and no setup to click through.", + }, + entries: [ + { kind: "command", text: "npm install -g @primitivedotdev/cli", cps: 28, hold: 12 }, + { + kind: "output", + perLine: 4, + lines: [ + { text: "" }, + { text: "added 65 packages in 1s", tone: "dim" }, + { text: "" }, + { text: "15 packages are looking for funding", tone: "dim" }, + { text: " run `npm fund` for details", tone: "dim" }, + ], + hold: 16, + }, + { kind: "command", text: "primitive --version", cps: 26, hold: 10 }, + { + kind: "output", + lines: [{ text: "@primitivedotdev/cli/1.2.0 darwin-arm64 node-v25.9.0", tone: "dim" }], + hold: 26, + }, + ], + }, + { + id: "signup", + label: "2 · Sign up", + explainer: { + heading: "Create an account", + body: "You can sign up right here in the terminal. Primitive emails you a 6-digit code to confirm it's really you, then logs you in — no browser needed.", + }, + entries: [ + { kind: "blank" }, + { kind: "command", text: "primitive signup you@acme.dev", cps: 24, hold: 8 }, + { + kind: "output", + perLine: 6, + lines: [ + { text: "By continuing, you agree to Primitive's Terms of Service and Privacy Policy:" }, + { text: " https://primitive.dev/terms", tone: "url" }, + { text: " https://primitive.dev/privacy", tone: "url" }, + ], + hold: 6, + }, + // The prompt and the user's typed "yes" share one line in a real TTY. + { kind: "command", prefix: "Type 'yes' to continue: ", prefixTone: "default", text: "yes", cps: 9, hold: 12 }, + { + kind: "output", + perLine: 7, + lines: [ + { text: "Sent a 6-digit verification code to you@acme.dev." }, + { text: "The code expires in 30 minutes.", tone: "dim" }, + { text: "Run `primitive signup confirm you@acme.dev ` to finish.", tone: "dim" }, + ], + hold: 18, + }, + { kind: "blank" }, + { kind: "command", text: "primitive signup confirm you@acme.dev 418207", cps: 26, hold: 10 }, + { + kind: "output", + perLine: 8, + lines: [ + { text: "Logged in to org 6b21f4e0-9c3a-4d77-b1e2-3f8a0c5d1e94 (Acme).", tone: "success" }, + { text: "Saved credentials to ~/.config/primitive/credentials.json.", tone: "dim" }, + ], + hold: 26, + }, + ], + }, + { + id: "inbox", + label: "3 · Inbox", + explainer: { + heading: "Your inbox is ready", + body: "Every new account comes with a real email address your AI agents can receive mail at — already set up and ready to go.", + }, + entries: [ + { kind: "blank" }, + { kind: "command", text: "primitive inbox setup", cps: 26, hold: 10 }, + { + kind: "output", + perLine: 4, + lines: [ + { text: "Inbound setup", tone: "accent" }, + { text: "" }, + { text: "Inbound mail is ready and at least one processing route is enabled." }, + { text: "" }, + { text: "Readiness: ready", tone: "success" }, + { text: "Receiving: yes", tone: "success" }, + { text: "Processing: yes", tone: "success" }, + { text: "Mode: actively processed", tone: "dim" }, + { text: "" }, + { text: "Receive address: inbox@swift-fox.primitive.email", tone: "accent" }, + { text: "Receive domain: swift-fox.primitive.email (Primitive-managed)", tone: "dim" }, + { text: "" }, + { text: "Domains" }, + { text: "- acme.dev: ready, receive yes, process yes, routes 1", tone: "dim" }, + { text: "- swift-fox.primitive.email: ready, receive yes, process yes, routes 1", tone: "dim" }, + { text: "" }, + { text: "Processing routes: 3 enabled endpoint(s), 7 deployed Function(s)" }, + ], + hold: 28, + }, + ], + }, + { + id: "send", + label: "4 · Send", + explainer: { + heading: "Send an email", + body: "One command sends a real email through Primitive — and it waits for the receiving mail server to confirm delivery with a “250 OK”.", + }, + entries: [ + { kind: "blank" }, + { kind: "command", text: "primitive send --to inbox@swift-fox.primitive.email \\", cps: 26 }, + { kind: "command", prefix: " ", prefixTone: "dim", text: '--subject "Hello from the CLI" \\', cps: 30 }, + { + kind: "command", + prefix: " ", + prefixTone: "dim", + text: '--body "Sent straight from my terminal." --wait', + cps: 34, + hold: 14, + }, + { + // Real /send-mail envelope captured from a genuine authenticated send + // (wait:true). queue_id is null, client_idempotency_key is a 64-hex + // digest, and the smtp_* / delivery_status fields are the real wait + // outcome. Only the from/to addresses are mapped to the demo persona. + kind: "output", + perLine: 4, + lines: [ + { text: "{" }, + { text: '"id": "238772f2-81fe-4f98-9829-93c6a546b79d",', tone: "string", indent: 2 }, + { text: '"status": "delivered",', tone: "accent", indent: 2 }, + { text: '"from": "agent@acme.dev",', tone: "string", indent: 2 }, + { text: '"queue_id": null,', tone: "dim", indent: 2 }, + { text: '"accepted": [', tone: "string", indent: 2 }, + { text: '"inbox@swift-fox.primitive.email"', tone: "string", indent: 4 }, + { text: "],", indent: 2 }, + { text: '"rejected": [],', tone: "string", indent: 2 }, + { text: '"client_idempotency_key": "24f940d0db002e08bcda7d7d9b610511752dd8e6fa29be51a825ff47a8ff83a4",', tone: "string", indent: 2 }, + { text: '"request_id": "a694fd98-f3f2-4365-88ab-0f82447c8b28",', tone: "string", indent: 2 }, + { text: '"content_hash": "d5456fb6ad9a7c4d796f5f6f08f79b7f5a560a5b13cbfb7b823c69f75630cd90",', tone: "string", indent: 2 }, + { text: '"idempotent_replay": false,', tone: "dim", indent: 2 }, + { text: '"delivery_status": "delivered",', tone: "success", indent: 2 }, + { text: '"smtp_response_code": 250,', tone: "success", indent: 2 }, + { text: '"smtp_response_text": "250 2.0.0 Ok: message accepted",', tone: "string", indent: 2 }, + { text: '"smtp_enhanced_status_code": "2.0.0"', tone: "dim", indent: 2 }, + { text: "}" }, + ], + hold: 50, + }, + ], + }, +]; diff --git a/demo/src/scriptAgent.ts b/demo/src/scriptAgent.ts new file mode 100644 index 00000000..69afe769 --- /dev/null +++ b/demo/src/scriptAgent.ts @@ -0,0 +1,182 @@ +// Storyline 2: "Give your agent email." Same brand + terminal machinery as the +// SDK demo, different transcript. Every line is grounded in real product +// behavior: +// - beat 1: the agent gets an email via the CLI — real `primitive signup` +// (terms + 6-digit code, captured from a real staging run) and `primitive +// whoami` (formatWhoamiSummary), whose "Managed inbox: any-local-part@..." +// line is the agent's assigned *.primitive.email address. +// - beat 2: real `primitive functions init` scaffold output (email-reply +// template) + trimmed real lines from the mono-repo dev-help-agent handler +// (apps/dev-help-agent-fn/handler.ts). +// - beat 3: real `primitive functions deploy` output — CreateFunctionResult +// {id,name,deploy_status} JSON + the route-bound stderr hint. +// - beat 4: real `primitive chat` transcript shape (cli-node .../chat.ts +// formatChatResponse); the reply body is an accurate answer about the real +// /v1/send-mail API. Identity is the demo persona (agent@acme.dev). + +import type { Section } from "./script"; + +export const agentIntroTitle = "primitive"; +export const agentIntroTagline = "give your agent email"; + +export const agentSections: Section[] = [ + { + id: "email", + label: "1 · Give your agent email", + entries: [ + { kind: "command", text: "primitive signup agent@acme.dev", cps: 26, hold: 8 }, + { + kind: "output", + perLine: 6, + lines: [ + { text: "By continuing, you agree to Primitive's Terms of Service and Privacy Policy:" }, + { text: " https://primitive.dev/terms", tone: "url" }, + { text: " https://primitive.dev/privacy", tone: "url" }, + ], + hold: 4, + }, + { kind: "command", prefix: "Type 'yes' to continue: ", prefixTone: "default", text: "yes", cps: 9, hold: 10 }, + { + kind: "output", + perLine: 7, + lines: [ + { text: "Sent a 6-digit verification code to agent@acme.dev." }, + { text: "Run `primitive signup confirm agent@acme.dev ` to finish.", tone: "dim" }, + ], + hold: 14, + }, + { kind: "blank" }, + { kind: "command", text: "primitive signup confirm agent@acme.dev 418207", cps: 26, hold: 10 }, + { + kind: "output", + perLine: 8, + lines: [{ text: "Logged in to org 6b21f4e0-9c3a-4d77-b1e2-3f8a0c5d1e94 (Acme).", tone: "success" }], + hold: 12, + }, + { kind: "blank" }, + { kind: "command", text: "primitive whoami", cps: 24, hold: 10 }, + { + kind: "output", + perLine: 6, + lines: [ + { text: "Authenticated as agent@acme.dev" }, + { text: "Account id: 9b3c1a7e-2f44-4d18-bc60-1e5a7d2c9f01", tone: "dim" }, + { text: "Plan: free", tone: "dim" }, + { text: "Managed inbox: any-local-part@swift-fox.primitive.email", tone: "accent" }, + ], + hold: 28, + }, + ], + }, + { + id: "build", + label: "2 · Build the agent", + entries: [ + { kind: "blank" }, + { kind: "command", text: "primitive functions init dev-help-agent", cps: 26, hold: 10 }, + { + kind: "output", + perLine: 5, + lines: [ + { text: "Scaffolded ~/dev-help-agent from email-reply template." }, + { text: "Next:", tone: "dim" }, + { text: " cd ~/dev-help-agent", tone: "dim" }, + { text: " npm install", tone: "dim" }, + { text: " npm run deploy", tone: "dim" }, + ], + hold: 16, + }, + { kind: "blank" }, + { kind: "command", text: "cat handler.ts", cps: 22, hold: 10 }, + { + kind: "output", + perLine: 6, + lines: [ + { text: "import { createPrimitiveClient, normalizeReceivedEmail }", tone: "dim" }, + { text: " from '@primitivedotdev/sdk/api'", tone: "dim" }, + { text: "" }, + { text: "const ALLOWED_RECIPIENT = 'dev_help@agent.primitive.dev'" }, + { text: "" }, + { text: "// receive the email, ask the model, reply with the answer", tone: "dim" }, + { text: "const email = normalizeReceivedEmail(event)" }, + { text: "const question = buildQuestion(email.subject, email.text)" }, + { text: "const answer = await askAgent(env, question)" }, + { text: "" }, + { text: "const client = createPrimitiveClient({ apiKey: env.PRIMITIVE_API_KEY })" }, + { text: "await client.reply(email, { text: answer })", tone: "accent" }, + ], + hold: 30, + }, + ], + }, + { + id: "deploy", + label: "3 · Deploy it", + entries: [ + { kind: "blank" }, + { kind: "command", text: "primitive functions deploy --name dev-help-agent --file ./dist/handler.js", cps: 34, hold: 14 }, + { + kind: "output", + perLine: 5, + lines: [ + { text: "{" }, + { text: '"id": "a3f2c1d4-7b8e-4a90-bc11-2e6f5a0d9c34",', tone: "string", indent: 2 }, + { text: '"name": "dev-help-agent",', tone: "string", indent: 2 }, + { text: '"deploy_status": "deployed"', tone: "accent", indent: 2 }, + { text: "}" }, + { text: "" }, + { text: "Route bound. Function will receive inbound mail.", tone: "success" }, + ], + hold: 30, + }, + ], + }, + { + id: "chat", + label: "4 · Chat with it", + entries: [ + { kind: "blank" }, + { kind: "command", text: 'primitive chat dev_help@agent.primitive.dev "How do I send an email with the API?"', cps: 36, hold: 10 }, + { + kind: "output", + perLine: 6, + lines: [{ text: "Message sent; waiting for reply from dev_help@agent.primitive.dev", tone: "amber" }], + hold: 26, + }, + { + kind: "output", + perLine: 5, + lines: [ + { text: "Reply received", tone: "success" }, + { text: "" }, + { text: "Sent", tone: "accent" }, + { text: " To: dev_help@agent.primitive.dev", tone: "dim" }, + { text: " From: agent@acme.dev", tone: "dim" }, + { text: " Subject: How do I send an email with the API?", tone: "dim" }, + { text: " Delivery status: delivered", tone: "success" }, + { text: "" }, + { text: "Reply", tone: "accent" }, + { text: " From: dev_help@agent.primitive.dev", tone: "dim" }, + { text: " Subject: Re: How do I send an email with the API?", tone: "dim" }, + { text: " Received: 2026-06-15T17:42:08Z", tone: "dim" }, + { text: " Match: fallback, matched by sender/time window", tone: "dim" }, + ], + hold: 10, + }, + { + kind: "output", + perLine: 6, + lines: [ + { text: "Response body (text; use --json for parsing)", tone: "accent" }, + { text: "----- BEGIN RESPONSE -----", tone: "dim" }, + { text: "Send a POST to https://api.primitive.dev/v1/send-mail with" }, + { text: "Authorization: Bearer prim_ and a JSON body:" }, + { text: '{ "from", "to", "subject", "body_text" }.', tone: "string" }, + { text: "Add an Idempotency-Key header so retries are safe." }, + { text: "----- END RESPONSE -----", tone: "dim" }, + ], + hold: 55, + }, + ], + }, +]; diff --git a/demo/src/scriptAgentSignup.ts b/demo/src/scriptAgentSignup.ts new file mode 100644 index 00000000..cb50e825 --- /dev/null +++ b/demo/src/scriptAgentSignup.ts @@ -0,0 +1,94 @@ +// Storyline: "the agent self-signup flow" — the REAL emailless, zero-touch path +// a headless agent uses to give ITSELF an identity, driven through the Primitive +// CLI. One command creates the account with no email and no human; the returned +// `prim_` API key + provisioned `*.primitive.email` inbox work immediately, on +// the reply-only `agent` plan. +// +// Source of truth (primitive-mono-repo + sdks): +// - CLI command: `primitive agent create-agent-account` (alias `primitive +// agent create`) — the generated `createAgentAccount` operation +// (sdks/cli-node/src/oclif/index.ts COMMAND_ALIASES; sdks/openapi +// /agent/accounts → operationId createAgentAccount). Generated operation +// commands print the bare `data` object (api-command.ts +// operationOutputPayload), so the `{ success, data }` envelope is unwrapped. +// - Request: CreateAgentAccountInput { terms_accepted: true, device_name? }. +// - Response: AgentAccountResult { api_key, org_id, address, plan, limits, +// upgrade } (sdks/openapi/primitive-api.yaml). Endpoint + handler: +// packages/api/src/routes/v1/agent.ts → createEmaillessAgentAccount. +// - Payoff: `primitive account show` (alias for getAccount) → GET /v1/account +// returns { …customer, plan, entitlements, managed_inbox_address, limits } +// (packages/api/src/routes/v1/account.ts). An emailless account's `email` +// is null — the literal "emailless" proof — and the `agent` plan grants +// `send_mail` + `send_to_known_addresses` (reply-only). +// +// Everything shown is REAL. `address` / `managed_inbox_address` use the +// documented example `brave-crow.primitive.email` (auth.md / generateName). The +// `api_key` is a SECRET shown exactly once, so its value is elided to `prim_…` +// rather than invented; the org UUID is truncated; nested `limits` / `upgrade` +// objects are abbreviated with the `"...": "..."` convention auth.md itself +// uses. No command, flag, field name, or shape is faked. + +import type { Section } from "./script"; + +export const agentSignupIntroTitle = "primitive"; +export const agentSignupCaption = "agent signup"; +// Outro reference: the actual one-command self-signup. +export const agentSignupOutroInstall = "primitive agent create-agent-account"; +export const agentSignupOutroRepo = "primitive.dev/auth.md"; + +export const agentSignupSections: Section[] = [ + { + id: "register", + label: "1 · Self-register", + explainer: { + heading: "The agent signs itself up", + body: "One command — no email, no human, no browser. Primitive returns a prim_ API key and a managed *.primitive.email inbox on the spot. This is the zero-touch path for headless agents.", + }, + entries: [ + // hold ~52 (30fps-baseline) → with holdScale 1.3 + f() ≈ 2.3s pause after + // the command runs, before the response prints. + { kind: "command", text: "primitive agent create-agent-account --terms-accepted", cps: 60, hold: 52 }, + { + kind: "output", + perLine: 3, + lines: [ + { text: "{" }, + { text: '"api_key": "prim_…",', indent: 2 }, + { text: '"org_id": "6b21f4e0-9c3a-4d77-…",', indent: 2 }, + { text: '"address": "brave-crow.primitive.email",', indent: 2 }, + { text: '"plan": "agent",', indent: 2 }, + { text: '"limits": { "send_per_hour": 10, "send_per_day": 50 },', indent: 2 }, + { text: '"upgrade": { "plan": "developer", "...": "..." }', indent: 2 }, + { text: "}" }, + ], + hold: 12, + }, + ], + }, + { + id: "use", + label: "2 · It's live", + explainer: { + heading: "The key works immediately", + body: "Bearer the new key on any call. The account is emailless (email: null) on the reply-only agent plan, with its own inbox — ready to receive mail and reply. Upgrade later by confirming an email.", + }, + entries: [ + { kind: "blank" }, + // hold ~52 → ~2.3s pause after the command runs, before the response. + { kind: "command", text: "primitive account show --api-key prim_…", cps: 48, hold: 52 }, + { + kind: "output", + perLine: 3, + lines: [ + { text: "{" }, + { text: '"email": null,', indent: 2 }, + { text: '"plan": "agent",', indent: 2 }, + { text: '"managed_inbox_address": "brave-crow.primitive.email",', indent: 2 }, + { text: '"entitlements": ["send_mail", "send_to_known_addresses"]', indent: 2 }, + { text: "}" }, + ], + hold: 16, + }, + ], + }, +]; diff --git a/demo/src/scriptChatGPT.ts b/demo/src/scriptChatGPT.ts new file mode 100644 index 00000000..4f9f0160 --- /dev/null +++ b/demo/src/scriptChatGPT.ts @@ -0,0 +1,174 @@ +// Storyline: "Primitive, inside ChatGPT" — a live agent↔agent email round-trip. +// The user asks ChatGPT to email another company's scheduling agent and WAIT; +// ChatGPT sends real mail via the Primitive connector, watches the thread, a +// real reply lands, and ChatGPT confirms back — all in one flow. This is the +// thing only a real two-way inbox makes possible. +// +// Faithfulness: the inline cards are the genuine Primitive email-console widget +// (resources/read ui://widget/primitive-email-v1.html), tool names + titles +// match the connector's toolTitles map (sendEmail→"Email sent", +// getEmail→"Email details", replyToEmail→"Reply sent"), and every payload uses +// the widget's structuredContent shape { tool, status, data } (object → Summary +// key/values). Identity is the Acme demo persona on its managed inbox. + +import { f } from "./theme"; + +export const chatGptIntroTitle = "primitive"; +export const chatGptIntroTagline = "email, inside ChatGPT"; +export const chatGptOutroPrimary = "now live in the ChatGPT app directory"; +export const chatGptOutroUrl = "primitive.dev/chatgpt"; + +export interface WidgetEmail { + subject: string; + from_email?: string; + to_email?: string; + status?: string; +} + +export interface WidgetPayload { + tool: string; + status: number; + // array → Messages list; object → Summary key/values. + data: WidgetEmail[] | Record; +} + +// A conversation is an ordered list of steps. Each step renders in the +// scrollback in order; the timeline below assigns absolute frames. +export type Step = + | { kind: "user"; text: string } + | { kind: "tool"; tool: string; working: string; payload: WidgetPayload } + | { kind: "wait"; label: string } + | { kind: "say"; text: string }; + +export const chatSteps: Step[] = [ + { + kind: "user", + text: "Email the scheduling agent at scheduling@amber-finch.primitive.email — ask for their earliest demo slot next week, then wait for the reply.", + }, + { + kind: "tool", + tool: "sendEmail", + working: "Sending your email", + payload: { + tool: "sendEmail", + status: 200, + data: { + id: "em_7c41a9d2", + to_email: "scheduling@amber-finch.primitive.email", + subject: "Demo availability next week", + status: "delivered", + }, + }, + }, + { kind: "say", text: "Sent to the scheduling agent. Watching the thread for their reply…" }, + { kind: "wait", label: "Waiting for reply from scheduling@amber-finch.primitive.email" }, + { + kind: "tool", + tool: "getEmail", + working: "Reading their reply", + payload: { + tool: "getEmail", + status: 200, + data: { + from_email: "scheduling@amber-finch.primitive.email", + subject: "Re: Demo availability next week", + message: "Thursday 10:00 AM PT works for us — I'll hold the slot.", + received: "42s ago", + status: "received", + }, + }, + }, + { kind: "say", text: "The scheduling agent replied — earliest slot is Thursday, 10:00 AM PT. Want me to confirm it?" }, + { kind: "user", text: "Yes, confirm Thursday 10 AM." }, + { + kind: "tool", + tool: "replyToEmail", + working: "Confirming the slot", + payload: { + tool: "replyToEmail", + status: 200, + data: { + id: "em_7c41b0f5", + to_email: "scheduling@amber-finch.primitive.email", + subject: "Re: Demo availability next week", + status: "delivered", + }, + }, + }, + { kind: "say", text: "Done ✓ Confirmed Thursday, 10:00 AM PT — the whole thread is in your Primitive inbox." }, +]; + +// ---- Timeline (baseline 30fps frames; f() scales to render fps) ---- + +const TYPE_CPS = 32; // composer typing speed +const ASSISTANT_CPS = 52; // reply streaming speed +const FPS_BASE = 30; + +const USER_PRE = 6; // idle before the user starts typing +const USER_SEND_PAUSE = 10; // pause after typing, before send +const USER_SEND_HOLD = 12; // dwell after the bubble lands +const TOOL_WORKING = 28; // "Working" shown before the chip resolves +const TOOL_TO_WIDGET = 12; // chip resolve → card reveal +const WIDGET_DWELL = 92; // hold the card on screen to read it +const SAY_HOLD = 30; // dwell after a streamed reply +const WAIT_DUR = 112; // live "waiting for reply" beat + +const typeFrames = (text: string, cps: number) => Math.max(8, Math.round((text.length / cps) * FPS_BASE)); + +export interface TimedStep { + step: Step; + start: number; + end: number; + // user + typeStart?: number; + typeDur?: number; + sendAt?: number; + // tool + workingAt?: number; + resolveAt?: number; + widgetAt?: number; + // say + textDur?: number; +} + +export interface ChatTimeline { + steps: TimedStep[]; + total: number; // baseline frames +} + +export function buildChatTimeline(steps: Step[] = chatSteps): ChatTimeline { + const timed: TimedStep[] = []; + let cursor = 0; + + for (const step of steps) { + const start = cursor; + if (step.kind === "user") { + const typeStart = start + USER_PRE; + const typeDur = typeFrames(step.text, TYPE_CPS); + const sendAt = typeStart + typeDur + USER_SEND_PAUSE; + const end = sendAt + USER_SEND_HOLD; + timed.push({ step, start, end, typeStart, typeDur, sendAt }); + cursor = end; + } else if (step.kind === "tool") { + const workingAt = start; + const resolveAt = start + TOOL_WORKING; + const widgetAt = resolveAt + TOOL_TO_WIDGET; + const end = widgetAt + WIDGET_DWELL; + timed.push({ step, start, end, workingAt, resolveAt, widgetAt }); + cursor = end; + } else if (step.kind === "wait") { + const end = start + WAIT_DUR; + timed.push({ step, start, end }); + cursor = end; + } else { + const textDur = typeFrames(step.text, ASSISTANT_CPS); + const end = start + textDur + SAY_HOLD; + timed.push({ step, start, end, textDur }); + cursor = end; + } + } + + return { steps: timed, total: cursor }; +} + +export const chatBodyFrames = (): number => f(buildChatTimeline().total); diff --git a/demo/src/scriptCombined.ts b/demo/src/scriptCombined.ts new file mode 100644 index 00000000..48d78ab4 --- /dev/null +++ b/demo/src/scriptCombined.ts @@ -0,0 +1,107 @@ +// Combined demo: sign up → deploy a Function → add the skill → chat with it. +// Explained side-panel cut, ~60s, screen clears between phases, benchmarks slide +// at the end. Headings / side content are lowercase; terminal lines keep CLI +// casing. ids/codes/domains are clean mocks (OK per direction); the deployed +// agent (dev_help) is what we chat at dev_help@agent.acme.com. + +import type { Section } from "./script"; + +export const combinedIntroTitle = "primitive"; +export const combinedIntroTagline = "email infrastructure built for agents"; + +const FN_ID = "f7c0e9a2"; + +export const combinedSections: Section[] = [ + { + id: "signup", + label: "1 · sign up", + explainer: { + heading: "get on the network", + body: "this is all it takes to put an agent on the network: one signup, one code. it now has its own email address.", + }, + entries: [ + { kind: "command", text: "primitive signup you@acme.dev --accept-terms", cps: 30, hold: 8 }, + { + kind: "output", + perLine: 6, + lines: [ + { text: "Sent a 6-digit verification code to you@acme.dev." }, + { text: "Run `primitive signup confirm you@acme.dev ` to finish.", tone: "dim" }, + ], + hold: 8, + }, + { kind: "command", text: "primitive signup confirm you@acme.dev 481920", cps: 30, hold: 8 }, + { kind: "output", perLine: 6, lines: [{ text: "Logged in to org acme (Acme).", tone: "success" }], hold: 6 }, + { kind: "command", text: "primitive whoami", cps: 26, hold: 8 }, + { + kind: "output", + perLine: 6, + lines: [ + { text: "Authenticated as you@acme.dev" }, + { text: "Managed inbox: any@agent.acme.com", tone: "accent" }, + ], + hold: 18, + }, + ], + }, + { + id: "deploy", + label: "2 · deploy an agent", + explainer: { + heading: "deploy an agent", + body: "ship a function that runs on every email your agent receives — live at your address in one command.", + }, + entries: [ + { kind: "command", text: "primitive functions init dev_help --template support", cps: 30, hold: 6 }, + { kind: "output", perLine: 6, lines: [{ text: "Scaffolded ~/dev_help from support template.", tone: "dim" }], hold: 8 }, + { kind: "command", text: "primitive functions deploy --name dev_help --source . --wait", cps: 34, hold: 8 }, + { + kind: "output", + perLine: 5, + lines: [ + { text: "{" }, + { text: `"id": "${FN_ID}",`, tone: "string", indent: 2 }, + { text: '"name": "dev_help",', tone: "string", indent: 2 }, + { text: '"deploy_status": "deployed"', tone: "accent", indent: 2 }, + { text: "}" }, + { text: "" }, + { text: "any agent can now send to dev_help@agent.acme.com", tone: "success" }, + ], + hold: 18, + }, + ], + }, + { + id: "chat", + label: "3 · use the cli & chat", + explainer: { + heading: "use the cli and chat", + body: "any agent can leverage the primitive cli to communicate with you and handle your requests.", + }, + entries: [ + { kind: "command", text: 'primitive chat dev_help@agent.acme.com "how to add a secret?"', cps: 36, hold: 8 }, + { + kind: "output", + perLine: 6, + lines: [{ text: "Message sent; waiting for reply from dev_help@agent.acme.com", tone: "amber" }], + hold: 14, + }, + { + kind: "output", + perLine: 5, + lines: [ + { text: "Reply received", tone: "success" }, + { text: "" }, + { text: "Response body (text)", tone: "accent" }, + { text: "----- BEGIN RESPONSE -----", tone: "dim" }, + { text: "Store it as a Function secret, then read it from env:" }, + { text: " primitive functions set-secret --key OPENAI_KEY \\" }, + { text: " --value-from-env OPENAI_KEY --redeploy" }, + { text: "It's injected as env.OPENAI_KEY at runtime." }, + { text: "----- END RESPONSE -----", tone: "dim" }, + ], + hold: 40, + }, + ], + }, +]; diff --git a/demo/src/scriptNetwork.ts b/demo/src/scriptNetwork.ts new file mode 100644 index 00000000..7566359a --- /dev/null +++ b/demo/src/scriptNetwork.ts @@ -0,0 +1,106 @@ +// YC cut: "get any agent on the network." Minimal + explanatory, ~60s. The story +// is the SKILL: add it, then watch an agent leverage email on its own. All real. +// +// beat 1 (add the skill): real `npx skills add primitivedotdev/skills` output +// (skills.sh CLI — Clack ◇/● milestones; spinner frames + risk-assessment box +// omitted for legibility, nothing altered). Installs primitive-chat + +// primitive-inbox to every detected coding agent (Claude Code, Codex, Zed…). +// beat 2 (agent leverages email): the agent now has the `primitive chat` verb +// and uses it to email another agent (dev_help@agent.primitive.dev) and get +// the answer back. Real `primitive chat` transcript shape (chat.ts +// formatChatResponse); the reply is an accurate answer about a real CLI flow +// (`functions set-secret --value-from-env … --redeploy`, the scaffold's own +// documented pattern). from = agent@ (chat's default sender). +// +// Skill source: ~/repos/skills (github.com/primitivedotdev/skills). Identity is +// the demo persona; commands/flags/output match the real CLIs. + +import type { Section } from "./script"; + +export const networkIntroTitle = "primitive"; +export const networkIntroTagline = "get your agent on the network"; +export const networkOutroInstall = "npx skills add primitivedotdev/skills"; +export const networkOutroRepo = "skills.sh/primitivedotdev/skills"; + +export const networkSections: Section[] = [ + { + id: "skill", + label: "add the skill", + explainer: { + heading: "Add the skill", + body: "One command teaches your coding agent — Claude Code, Codex, and 50+ others — to send email and get the reply. That's the whole on-ramp. Any product can do this.", + }, + entries: [ + { kind: "command", text: "npx skills add primitivedotdev/skills", cps: 28, hold: 12 }, + { + kind: "output", + perLine: 6, + lines: [ + { text: "◇ Source: https://github.com/primitivedotdev/skills.git", tone: "dim" }, + { text: "◇ Repository cloned", tone: "dim" }, + { text: "◇ Found 2 skills" }, + { text: "● Installing to: Claude Code, Codex, Zed", tone: "accent" }, + { text: "◇ Installation Summary" }, + { text: " ./.agents/skills/primitive-chat", tone: "dim" }, + { text: " ./.agents/skills/primitive-inbox", tone: "dim" }, + { text: "◇ Installation complete", tone: "success" }, + ], + hold: 26, + }, + ], + }, + { + id: "chat", + label: "now it just emails", + explainer: { + heading: "Now it just emails", + body: "Stuck on a tool, your agent emails the vendor's own agent and gets the answer back — one command, no SMTP, no API keys, no human in the loop.", + }, + entries: [ + { kind: "blank" }, + { + kind: "command", + text: 'primitive chat dev_help@agent.primitive.dev "How do I store a secret for my Function?"', + cps: 38, + hold: 10, + }, + { + kind: "output", + perLine: 6, + lines: [{ text: "Message sent; waiting for reply from dev_help@agent.primitive.dev", tone: "amber" }], + hold: 24, + }, + { + kind: "output", + perLine: 5, + lines: [ + { text: "Reply received", tone: "success" }, + { text: "" }, + { text: "Sent", tone: "accent" }, + { text: " To: dev_help@agent.primitive.dev", tone: "dim" }, + { text: " From: agent@swift-fox.primitive.email", tone: "dim" }, + { text: " Delivery status: delivered", tone: "success" }, + { text: "" }, + { text: "Reply", tone: "accent" }, + { text: " From: dev_help@agent.primitive.dev", tone: "dim" }, + { text: " Match: fallback, matched by sender/time window", tone: "dim" }, + ], + hold: 8, + }, + { + kind: "output", + perLine: 6, + lines: [ + { text: "Response body (text; use --json for parsing)", tone: "accent" }, + { text: "----- BEGIN RESPONSE -----", tone: "dim" }, + { text: "Set it as a Function secret, then read it from env in your handler:" }, + { text: " primitive functions set-secret --id --key OPENAI_KEY \\" }, + { text: " --value-from-env OPENAI_KEY --redeploy" }, + { text: "It's injected as env.OPENAI_KEY at runtime; redeploy picks it up.", tone: "dim" }, + { text: "----- END RESPONSE -----", tone: "dim" }, + ], + hold: 50, + }, + ], + }, +]; diff --git a/demo/src/scriptX402.ts b/demo/src/scriptX402.ts new file mode 100644 index 00000000..77200db5 --- /dev/null +++ b/demo/src/scriptX402.ts @@ -0,0 +1,107 @@ +// Storyline: "x402 agent-to-agent payments" — the launch-week-02 companion to +// the agent-signup clip. We watch ONE agent's run transcript: mid-task it needs +// a GPU compute job it can't run locally, reaches a compute-provider agent over +// email, clears the payment ITSELF, and gets the result back in the thread. +// +// TRUTHFULNESS (this feature is SDK-driven, not auto-wired to email — verified +// in primitive-mono-repo): +// - x402 today is explicit SDK calls: the payee calls `x402.charge()` to quote +// a price; the payer calls `x402.pay(challenge, { signer })` to sign locally +// and settle. There is NO platform mechanism that auto-injects a 402 onto an +// email reply (x402.ts mints a SYNTHETIC interaction_id `${uuid}@x402 +// .primitive`; x402_challenges has no FK to any email/thread; engine.test.ts +// notes interaction-wiring is future work: "approval/x402 WILL"). So this +// transcript shows the AGENT doing the work: the provider quoted with one +// charge() call and emailed the challenge back; our agent reads it and pays. +// Email is the (real) channel; the payment is an explicit (real) SDK action. +// - Every transcript line maps to a real op: sending/receiving email is core +// Primitive; `x402.pay()` → POST /v1/x402/challenges/:id/pay; the spend +// policy (kill-switch / per-payment / daily / allowlist) is enforced +// server-side on pay; `settle_tx` is the real on-chain hash from the x402 +// facilitator. `buyer-agent.ts` is illustrative glue around those calls. +// - base-sepolia is the demoable network (settles for free via the public +// facilitator; mainnet `base` is real USDC). Amounts are testnet USDC base +// units (6 decimals): "2000000" = 2 USDC, "5000000"/"50000000" = 5/50 USDC. +// - Persona address (gpu-farm.primitive.email) is a demo identity, per the +// other clips; the SECRET/unknowable values are truncated for display, never +// invented: the resolved `pay_to` (0x…), the on-chain `settle_tx` (0x…). +// +// Source of truth: sdks/sdk-node/src/x402/client.ts (charge/pay/setSpendPolicy), +// README "x402 agent-to-agent payments"; packages/api/src/routes/v1/x402.ts. + +import type { Section } from "./script"; + +export const x402IntroTitle = "primitive"; +export const x402Caption = "agent payments"; +// Outro reference: install the SDK that ships the x402 client. +export const x402OutroInstall = "npm i @primitivedotdev/sdk"; +export const x402OutroRepo = "primitive.dev/blog/launch-week-02"; + +export const x402Sections: Section[] = [ + { + id: "job", + label: "1 · The job", + explainer: { + heading: "Your agent needs compute", + body: "Mid-task it needs a GPU job it can't run locally. It reaches a compute-provider agent over email and gets back a price — not a signup wall. The provider quoted it with one x402 call; our agent just reads the reply.", + }, + entries: [ + { kind: "command", text: "npx tsx buyer-agent.ts", cps: 60, hold: 44 }, + { + kind: "output", + perLine: 4, + lines: [ + { text: "job: embed 2M documents — no local GPU", tone: "accent" }, + { text: "→ emailing jobs@gpu-farm.primitive.email …", tone: "dim" }, + { text: "← reply: queued · 2 USDC to start the run", tone: "amber" }, + ], + hold: 16, + }, + ], + }, + { + id: "pay", + label: "2 · Clear it", + explainer: { + heading: "It pays — within your limits", + body: "The agent signs an EIP-3009 authorization with its own key (the key never leaves it) and Primitive settles on Base. Non-custodial. It stays inside the spend policy you set — over the cap, the payment is declined and logged.", + }, + entries: [ + { kind: "blank" }, + { + kind: "output", + perLine: 4, + lines: [ + { text: "→ challenge: 2 USDC · base-sepolia", tone: "default" }, + { text: " pay_to 0x8f3c…b21c", tone: "dim" }, + { text: "→ spend policy: ≤5 USDC · ≤50/day ✓ ok", tone: "default" }, + { text: "→ signing transferWithAuthorization …", tone: "dim" }, + { text: "→ x402.pay() → settled ✓", tone: "success" }, + { text: " tx 0x… ↗ basescan", tone: "url" }, + ], + hold: 18, + }, + ], + }, + { + id: "settled", + label: "3 · Settled", + explainer: { + heading: "Work comes back, with a receipt", + body: "The provider runs the job and returns the result in the same thread. One email exchange, one on-chain payment — no invoice, no Stripe account, no human in the loop — and a receipt you can audit.", + }, + entries: [ + { kind: "blank" }, + { + kind: "output", + perLine: 4, + lines: [ + { text: "← job complete · 2M embeddings ready", tone: "accent" }, + { text: "→ replying to the thread with the output", tone: "dim" }, + { text: "✓ done · paid 2 USDC · 0 humans · 1 thread", tone: "success" }, + ], + hold: 14, + }, + ], + }, +]; diff --git a/demo/src/theme.ts b/demo/src/theme.ts new file mode 100644 index 00000000..81b75d3b --- /dev/null +++ b/demo/src/theme.ts @@ -0,0 +1,125 @@ +import { loadFont as loadMono } from "@remotion/google-fonts/JetBrainsMono"; +import { loadFont as loadInter } from "@remotion/google-fonts/Inter"; +import { continueRender, delayRender, staticFile } from "remotion"; + +// Brand system mirrored from primitive-mono-repo/web (app/globals.css): +// brutalist / retro-computing — JetBrains Mono everywhere, sharp corners, +// thick borders, hard offset shadows, green primary, base #1d1d1d / #f4f4f4. +const mono = loadMono("normal", { + weights: ["400", "700", "800"], + subsets: ["latin"], + ignoreTooManyRequestsWarning: true, +}); + +export const monoFamily = mono.fontFamily; +// The wordmark/titles use mono too (the site sets `body { font-mono }`). +export const sansFamily = mono.fontFamily; + +// Inter — used ONLY for the mocked ChatGPT host chrome (sidebar, message +// bubbles, composer) so it reads as the real app. The Primitive email widget +// embedded inside it stays JetBrains Mono, on-brand, exactly like production. +const inter = loadInter("normal", { + weights: ["400", "500", "600", "700"], + subsets: ["latin"], + ignoreTooManyRequestsWarning: true, +}); +export const uiSansFamily = inter.fontFamily; + +// The custom logo face used to render the braille-art mark (PrimitiveLogoMono). +export const LOGO_FONT = "PrimitiveLogoMono"; +if (typeof document !== "undefined") { + const handle = delayRender("load-logo-font"); + const face = new FontFace(LOGO_FONT, `url(${staticFile("PrimitiveLogoMono.woff2")})`); + face + .load() + .then((loaded) => { + document.fonts.add(loaded); + continueRender(handle); + }) + .catch(() => continueRender(handle)); +} + +export type Format = "landscape" | "square" | "vertical"; + +export const BASE_FPS = 30; +export const FPS = 60; +export const f = (baselineFrames: number): number => Math.round((baselineFrames * FPS) / BASE_FPS); + +// Dark-mode brand tokens from globals.css :root / .dark. +export const theme = { + // base + baseDark: "#1d1d1d", + baseDarkSecondary: "#181818", + baseLight: "#f4f4f4", + muted: "#9a9a9a", // base-muted-dark + + // app surface (flat — brutalist, no gradient glow) + pageBg: "#1d1d1d", + + // terminal window: dark surface, thick light foreground border, hard shadow + windowBg: "#181818", + windowBorder: "#f4f4f4", + titlebarBg: "#1d1d1d", + titlebarText: "#9a9a9a", + // brutalist hard offset shadow color (foreground) + hardShadow: "#f4f4f4", + dotRed: "#dc2626", + dotYellow: "#fbbf24", + dotGreen: "#15803d", + + // text + text: "#f4f4f4", + dim: "#9a9a9a", + prompt: "#4ade80", // bright green $ + command: "#f4f4f4", + accent: "#22c55e", // brand green (bright on dark) + accentDeep: "#15803d", // primary green + amber: "#fbbf24", + url: "#57c7ff", // dark-mode logo-scanline blue + jsonKey: "#57c7ff", + jsonString: "#f4f4f4", + success: "#4ade80", + cursor: "#4ade80", + + // animated braille glyph (dark-mode --logo-idle-* + reveal/glitch) + logoIdle1: "hsl(150, 90%, 65%)", + logoIdle2: "hsl(175, 80%, 55%)", + logoIdle3: "hsl(200, 80%, 60%)", + logoGlitch: "#ff6ac1", + + cardText: "#f4f4f4", + cardDim: "#9a9a9a", +} as const; + +export type Tone = "default" | "dim" | "success" | "url" | "accent" | "key" | "string" | "amber"; + +export function toneColor(tone: Tone | undefined): string { + switch (tone) { + case "dim": + return theme.dim; + case "success": + return theme.success; + case "url": + return theme.url; + case "accent": + return theme.accent; + case "amber": + return theme.amber; + case "key": + return theme.jsonKey; + case "string": + return theme.jsonString; + default: + return theme.text; + } +} + +// Per-format layout tuning. +export const layout: Record< + Format, + { width: number; height: number; fontSize: number; maxLines: number; padding: number } +> = { + landscape: { width: 1920, height: 1080, fontSize: 30, maxLines: 22, padding: 120 }, + square: { width: 1080, height: 1080, fontSize: 26, maxLines: 20, padding: 60 }, + vertical: { width: 1080, height: 1920, fontSize: 30, maxLines: 24, padding: 60 }, +}; diff --git a/demo/src/timeline.ts b/demo/src/timeline.ts new file mode 100644 index 00000000..fcb1a71e --- /dev/null +++ b/demo/src/timeline.ts @@ -0,0 +1,125 @@ +import { FPS, f } from "./theme"; +import { type Entry, type OutLine, type Section, type Tone, sections } from "./script"; + +// Baseline (30fps) frame counts; converted to the render rate via f() at use. +const DEFAULT_CPS = 26; +const DEFAULT_HOLD = 10; +const DEFAULT_PERLINE = 6; +const BLANK_FRAMES = 7; + +export interface CmdLine { + kind: "cmd"; + start: number; + typeDuration: number; + endHold: number; + prefix: string; + prefixTone: Tone; + text: string; + sectionId: string; +} + +export interface OutLineV { + kind: "out"; + start: number; + line: OutLine; + sectionId: string; +} + +export interface BlankLine { + kind: "blank"; + start: number; + sectionId: string; +} + +export type VLine = CmdLine | OutLineV | BlankLine; + +export interface SectionRange { + id: string; + label: string; + start: number; + end: number; +} + +export interface Timeline { + lines: VLine[]; + ranges: SectionRange[]; + total: number; +} + +function typeFrames(text: string, cps: number): number { + // cps is chars/second, so this is already real-time-correct at any FPS. + return Math.max(f(6), Math.ceil((text.length / cps) * FPS)); +} + +export interface TimelineOptions { + // Multiplier applied to every hold/pause (1 = authored pacing, >1 = slower). + holdScale?: number; + // Idle pause (30fps-baseline frames) inserted at the start of each section, + // before its first entry — gives the viewer time to read the explainer. + leadInFrames?: number; + // Extra dwell (30fps-baseline frames) held at the END of each section, with + // the finished output still on screen, before the next section begins. + sectionEndHoldFrames?: number; + // Pad each section to this many FINAL frames so every step takes equal + // wall-time (even distribution). The slack is held at the end (content on + // screen). Sections naturally longer than this are left as-is. + equalizeSectionFinalFrames?: number; +} + +export function buildTimeline(secs: Section[] = sections, opts: TimelineOptions = {}): Timeline { + const holdScale = opts.holdScale ?? 1; + const leadIn = f(opts.leadInFrames ?? 0); + const sectionEndHold = f(opts.sectionEndHoldFrames ?? 0); + const scaleHold = (baseline: number) => f(Math.round(baseline * holdScale)); + + const lines: VLine[] = []; + const ranges: SectionRange[] = []; + let cursor = 0; + + for (const section of secs) { + const sectionStart = cursor; + // Lead-in pause: terminal sits idle while the explainer panel is read. + cursor += leadIn; + for (const entry of section.entries as Entry[]) { + if (entry.kind === "blank") { + lines.push({ kind: "blank", start: cursor, sectionId: section.id }); + cursor += f(BLANK_FRAMES); + continue; + } + if (entry.kind === "command") { + const cps = entry.cps ?? DEFAULT_CPS; + const dur = typeFrames(entry.text, cps); + const endHold = scaleHold(entry.hold ?? DEFAULT_HOLD); + lines.push({ + kind: "cmd", + start: cursor, + typeDuration: dur, + endHold, + prefix: entry.prefix ?? "$ ", + prefixTone: entry.prefixTone ?? "accent", + text: entry.text, + sectionId: section.id, + }); + cursor += dur + endHold; + continue; + } + // output + const perLine = f(entry.perLine ?? DEFAULT_PERLINE); + entry.lines.forEach((line, i) => { + lines.push({ kind: "out", start: cursor + i * perLine, line, sectionId: section.id }); + }); + cursor += entry.lines.length * perLine + scaleHold(entry.hold ?? DEFAULT_HOLD); + } + // Hold the finished step on screen before clearing to the next. + cursor += sectionEndHold; + // Equalize step wall-time: pad to the target so every step is the same + // length (slack held at the end, content on screen). + const equalizeTo = opts.equalizeSectionFinalFrames; + if (equalizeTo && cursor - sectionStart < equalizeTo) { + cursor = sectionStart + equalizeTo; + } + ranges.push({ id: section.id, label: section.label, start: sectionStart, end: cursor }); + } + + return { lines, ranges, total: cursor }; +} diff --git a/demo/tsconfig.json b/demo/tsconfig.json new file mode 100644 index 00000000..aa2d7080 --- /dev/null +++ b/demo/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "Bundler", + "jsx": "react-jsx", + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true + }, + "include": ["src", "remotion.config.ts"] +} diff --git a/openapi/primitive-api.codegen.json b/openapi/primitive-api.codegen.json index fe39aed9..fcfdafc1 100644 --- a/openapi/primitive-api.codegen.json +++ b/openapi/primitive-api.codegen.json @@ -4114,6 +4114,72 @@ } } }, + "/sent-emails/{id}/reply": { + "get": { + "operationId": "awaitReply", + "summary": "Get (or wait for) the reply to a sent email", + "description": "Returns the first threaded inbound reply to a send, keyed by\nthe inbound email's `reply_to_sent_email_id`. This is the\ncanonical \"did a reply arrive for this send?\" call: after\n/send-mail, poll (or long-poll) here instead of hand-rolling\nan /emails/search loop.\n\nBy default the call returns immediately with `reply: null`\nwhen nothing has arrived yet. Set `wait=true` to long-poll:\nthe server holds the request up to `wait_timeout_ms`\n(default 10000, max 30000), returning as soon as the first\nreply lands. On a wait that elapses with no reply, the\nresponse has `reply: null` and `timed_out: true`.\n\nThe reply object is compact (headers plus bodies); fetch\n`/emails/{id}` with `reply.id` for the fully parsed record,\nor `/threads/{thread_id}` for the whole back-and-forth.\n", + "tags": [ + "Sending" + ], + "parameters": [ + { + "$ref": "#/components/parameters/ResourceId" + }, + { + "name": "wait", + "in": "query", + "schema": { + "type": "boolean" + }, + "description": "When true, long-poll up to `wait_timeout_ms` for the\nfirst reply to arrive instead of returning immediately.\nMirrors send-mail's server-side `wait` semantics. The\nserver accepts only the literal strings `true` / `false`\n(standard boolean query serialization); omitted means\nno wait.\n" + }, + { + "name": "wait_timeout_ms", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1000, + "maximum": 30000 + }, + "description": "Maximum time to hold the request when `wait=true`.\nServer default is 10000. NOT given an OpenAPI `default`\non purpose, matching the `/emails` `wait` parameter: a\ndefault makes some generators send the value on every\ncall.\n" + } + ], + "responses": { + "200": { + "description": "Reply status for the send (reply may be null)", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/AwaitReplyResult" + } + } + } + ] + } + } + } + }, + "400": { + "$ref": "#/components/responses/ValidationError" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound" + } + } + } + }, "/threads/{id}": { "parameters": [ { @@ -11908,6 +11974,106 @@ } ] }, + "ReplyEmail": { + "type": "object", + "description": "A threaded inbound reply to one of the org's sends, keyed by\nthe inbound email's `reply_to_sent_email_id`. Compact on\npurpose: enough to read the reply and decide what to do\nnext, with `/emails/{id}` available for the fully parsed\ndetail.\n", + "properties": { + "id": { + "type": "string", + "format": "uuid", + "description": "Inbound email row id; usable with `/emails/{id}`." + }, + "thread_id": { + "type": "string", + "format": "uuid", + "description": "Conversation thread id; usable with `/threads/{id}`.", + "nullable": true + }, + "reply_to_sent_email_id": { + "type": "string", + "format": "uuid", + "description": "The sent-email id this inbound message replied to.", + "nullable": true + }, + "from_email": { + "type": "string", + "description": "Sender of the reply (From header when present, else envelope sender)." + }, + "to_email": { + "type": "string", + "description": "Recipient address the reply arrived at." + }, + "subject": { + "type": "string", + "nullable": true + }, + "body_text": { + "type": "string", + "description": "Plain-text body of the reply, when present.", + "nullable": true + }, + "body_html": { + "type": "string", + "description": "HTML body of the reply, when present.", + "nullable": true + }, + "received_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "status": { + "type": "string", + "description": "Inbound processing status of the reply row. Typically an\n`EmailStatus` value, but left as an open string here to\nmatch the server contract, which does not narrow it.\n" + } + }, + "required": [ + "id", + "thread_id", + "reply_to_sent_email_id", + "from_email", + "to_email", + "subject", + "body_text", + "body_html", + "received_at", + "status" + ] + }, + "AwaitReplyResult": { + "type": "object", + "description": "Result of `/sent-emails/{id}/reply`. `reply` is null when no\nreply has arrived yet (no-wait call, or the wait timed out).\n", + "properties": { + "sent_email_id": { + "type": "string", + "format": "uuid", + "description": "The send this lookup was keyed on (echoes the path id)." + }, + "reply": { + "allOf": [ + { + "$ref": "#/components/schemas/ReplyEmail" + } + ], + "nullable": true + }, + "waited": { + "type": "boolean", + "description": "Whether the call ran in long-poll mode (`wait=true`)." + }, + "timed_out": { + "type": "boolean", + "description": "True only when a `wait=true` call elapsed its timeout with no reply." + } + }, + "required": [ + "sent_email_id", + "reply", + "waited", + "timed_out" + ], + "additionalProperties": false + }, "ReplyInput": { "type": "object", "additionalProperties": false, diff --git a/openapi/primitive-api.yaml b/openapi/primitive-api.yaml index b724cc8c..70431df4 100644 --- a/openapi/primitive-api.yaml +++ b/openapi/primitive-api.yaml @@ -3053,6 +3053,72 @@ paths: '404': $ref: '#/components/responses/NotFound' + /sent-emails/{id}/reply: + get: + operationId: awaitReply + summary: Get (or wait for) the reply to a sent email + description: | + Returns the first threaded inbound reply to a send, keyed by + the inbound email's `reply_to_sent_email_id`. This is the + canonical "did a reply arrive for this send?" call: after + /send-mail, poll (or long-poll) here instead of hand-rolling + an /emails/search loop. + + By default the call returns immediately with `reply: null` + when nothing has arrived yet. Set `wait=true` to long-poll: + the server holds the request up to `wait_timeout_ms` + (default 10000, max 30000), returning as soon as the first + reply lands. On a wait that elapses with no reply, the + response has `reply: null` and `timed_out: true`. + + The reply object is compact (headers plus bodies); fetch + `/emails/{id}` with `reply.id` for the fully parsed record, + or `/threads/{thread_id}` for the whole back-and-forth. + tags: [Sending] + parameters: + - $ref: '#/components/parameters/ResourceId' + - name: wait + in: query + schema: + type: boolean + description: | + When true, long-poll up to `wait_timeout_ms` for the + first reply to arrive instead of returning immediately. + Mirrors send-mail's server-side `wait` semantics. The + server accepts only the literal strings `true` / `false` + (standard boolean query serialization); omitted means + no wait. + - name: wait_timeout_ms + in: query + schema: + type: integer + minimum: 1000 + maximum: 30000 + description: | + Maximum time to hold the request when `wait=true`. + Server default is 10000. NOT given an OpenAPI `default` + on purpose, matching the `/emails` `wait` parameter: a + default makes some generators send the value on every + call. + responses: + '200': + description: Reply status for the send (reply may be null) + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/SuccessEnvelope' + - type: object + properties: + data: + $ref: '#/components/schemas/AwaitReplyResult' + '400': + $ref: '#/components/responses/ValidationError' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + # --------------------------------------------------------------------------- # Threads # --------------------------------------------------------------------------- @@ -8536,6 +8602,75 @@ components: carried only a plain-text body, or when bodies have been discarded post-send. + ReplyEmail: + type: object + description: | + A threaded inbound reply to one of the org's sends, keyed by + the inbound email's `reply_to_sent_email_id`. Compact on + purpose: enough to read the reply and decide what to do + next, with `/emails/{id}` available for the fully parsed + detail. + properties: + id: + type: string + format: uuid + description: Inbound email row id; usable with `/emails/{id}`. + thread_id: + type: [string, 'null'] + format: uuid + description: Conversation thread id; usable with `/threads/{id}`. + reply_to_sent_email_id: + type: [string, 'null'] + format: uuid + description: The sent-email id this inbound message replied to. + from_email: + type: string + description: Sender of the reply (From header when present, else envelope sender). + to_email: + type: string + description: Recipient address the reply arrived at. + subject: + type: [string, 'null'] + body_text: + type: [string, 'null'] + description: Plain-text body of the reply, when present. + body_html: + type: [string, 'null'] + description: HTML body of the reply, when present. + received_at: + type: [string, 'null'] + format: date-time + status: + type: string + description: | + Inbound processing status of the reply row. Typically an + `EmailStatus` value, but left as an open string here to + match the server contract, which does not narrow it. + required: [id, thread_id, reply_to_sent_email_id, from_email, to_email, subject, body_text, body_html, received_at, status] + + AwaitReplyResult: + type: object + description: | + Result of `/sent-emails/{id}/reply`. `reply` is null when no + reply has arrived yet (no-wait call, or the wait timed out). + properties: + sent_email_id: + type: string + format: uuid + description: The send this lookup was keyed on (echoes the path id). + reply: + allOf: + - $ref: '#/components/schemas/ReplyEmail' + nullable: true + waited: + type: boolean + description: Whether the call ran in long-poll mode (`wait=true`). + timed_out: + type: boolean + description: True only when a `wait=true` call elapsed its timeout with no reply. + required: [sent_email_id, reply, waited, timed_out] + additionalProperties: false + ReplyInput: type: object additionalProperties: false diff --git a/packages/api-core/src/api/index.ts b/packages/api-core/src/api/index.ts index e04782cd..fe619edc 100644 --- a/packages/api-core/src/api/index.ts +++ b/packages/api-core/src/api/index.ts @@ -1,4 +1,4 @@ // This file is auto-generated by @hey-api/openapi-ts -export { addDomain, cliLogout, createAgentAccount, createAgentClaimLink, createChallenge, createEmailChallenge, createEndpoint, createFilter, createFunction, createFunctionSecret, createOrgSecret, createRegistry, createRoute, createWakeAuthorization, createWakeSchedule, decideRegistryRequest, defineAgent, deleteDomain, deleteEmail, deleteEndpoint, deleteFilter, deleteFunction, deleteFunctionSecret, deleteMemory, deleteOrgSecret, deleteRegistry, deleteRoute, deleteWakeAuthorization, deleteWakeSchedule, discardEmailContent, downloadAttachments, downloadDomainZoneFile, downloadRawEmail, getAccount, getAgent, getChallenge, getConversation, getEmail, getFunction, getFunctionRouting, getFunctionTestRunTrace, getInboxStatus, getMemory, getOrgRoutingTopology, getRegistry, getSendPermissions, getSentEmail, getSpendPolicy, getStorageStats, getTemplate, getTemplateInstall, getThread, getWakeSchedule, getWebhookSecret, installTemplate, listDeclinedPayments, listDeliveries, listDomains, listEmails, listEndpoints, listFilters, listFunctionLogs, listFunctions, listFunctionSecrets, listOrgSecrets, listPayoutAddresses, listRegistries, listRegistryAgents, listRegistryRequests, listRoutes, listSentEmails, listTemplates, listWakeAuthorizations, listWakeDispatches, listWakeSchedules, type Options, payChallenge, pollCliLogin, publishAgent, registerPayoutAddress, reorderRoutes, replayDelivery, replayEmailWebhooks, replyToEmail, resendAgentSignupVerification, resendCliSignupVerification, resolveRegistryHandle, rotateWebhookSecret, runWakeSchedule, searchEmails, searchMemories, semanticSearch, sendEmail, setFunctionRoute, setFunctionSecret, setMemory, setOrgSecret, simulateRoute, startAgentClaim, startAgentSignup, startCliLogin, startCliSignup, testEndpoint, testFunction, unpublishAgent, unsetFunctionRoute, updateAccount, updateDomain, updateEndpoint, updateFilter, updateFunction, updateRegistry, updateRoute, updateSpendPolicy, updateWakeAuthorization, updateWakeSchedule, verifyAgentClaim, verifyAgentSignup, verifyCliSignup, verifyDomain } from './sdk.gen.js'; -export type { Account, AccountUpdated, AddDomainData, AddDomainError, AddDomainErrors, AddDomainInput, AddDomainResponse, AddDomainResponses, AgentAccountResult, AgentAccountUpgradeHint, AgentClaimLinkResult, AgentClaimResult, AgentClaimStartResult, AgentOrgRef, AgentSignupResendResult, AgentSignupStartResult, AgentSignupVerifyResult, ClientOptions, CliLoginPollResult, CliLoginStartResult, CliLogoutData, CliLogoutError, CliLogoutErrors, CliLogoutInput, CliLogoutResponse, CliLogoutResponses, CliLogoutResult, CliSignupResendResult, CliSignupStartResult, CliSignupVerifyResult, Conversation, ConversationMessage, CreateAgentAccountData, CreateAgentAccountError, CreateAgentAccountErrors, CreateAgentAccountInput, CreateAgentAccountResponse, CreateAgentAccountResponses, CreateAgentClaimLinkData, CreateAgentClaimLinkError, CreateAgentClaimLinkErrors, CreateAgentClaimLinkInput, CreateAgentClaimLinkResponse, CreateAgentClaimLinkResponses, CreateChallengeData, CreateChallengeError, CreateChallengeErrors, CreateChallengeInput, CreateChallengeResponse, CreateChallengeResponses, CreateEmailChallengeData, CreateEmailChallengeError, CreateEmailChallengeErrors, CreateEmailChallengeInput, CreateEmailChallengeResponse, CreateEmailChallengeResponses, CreateEndpointData, CreateEndpointError, CreateEndpointErrors, CreateEndpointInput, CreateEndpointResponse, CreateEndpointResponses, CreateFilterData, CreateFilterError, CreateFilterErrors, CreateFilterInput, CreateFilterResponse, CreateFilterResponses, CreateFunctionData, CreateFunctionError, CreateFunctionErrors, CreateFunctionInput, CreateFunctionResponse, CreateFunctionResponses, CreateFunctionResult, CreateFunctionSecretData, CreateFunctionSecretError, CreateFunctionSecretErrors, CreateFunctionSecretInput, CreateFunctionSecretResponse, CreateFunctionSecretResponses, CreateOrgSecretData, CreateOrgSecretError, CreateOrgSecretErrors, CreateOrgSecretInput, CreateOrgSecretResponse, CreateOrgSecretResponses, CreateRegistryData, CreateRegistryError, CreateRegistryErrors, CreateRegistryInput, CreateRegistryResponse, CreateRegistryResponses, CreateRouteData, CreateRouteError, CreateRouteErrors, CreateRouteInput, CreateRouteResponse, CreateRouteResponses, CreateWakeAuthorizationData, CreateWakeAuthorizationError, CreateWakeAuthorizationErrors, CreateWakeAuthorizationInput, CreateWakeAuthorizationResponse, CreateWakeAuthorizationResponses, CreateWakeScheduleData, CreateWakeScheduleError, CreateWakeScheduleErrors, CreateWakeScheduleInput, CreateWakeScheduleResponse, CreateWakeScheduleResponses, Cursor, DecideRegistryRequestData, DecideRegistryRequestError, DecideRegistryRequestErrors, DecideRegistryRequestInput, DecideRegistryRequestResponse, DecideRegistryRequestResponses, DefineAgentData, DefineAgentError, DefineAgentErrors, DefineAgentInput, DefineAgentResponse, DefineAgentResponses, DeleteDomainData, DeleteDomainError, DeleteDomainErrors, DeleteDomainResponse, DeleteDomainResponses, DeleteEmailData, DeleteEmailError, DeleteEmailErrors, DeleteEmailResponse, DeleteEmailResponses, DeleteEndpointData, DeleteEndpointError, DeleteEndpointErrors, DeleteEndpointResponse, DeleteEndpointResponses, DeleteFilterData, DeleteFilterError, DeleteFilterErrors, DeleteFilterResponse, DeleteFilterResponses, DeleteFunctionData, DeleteFunctionError, DeleteFunctionErrors, DeleteFunctionResponse, DeleteFunctionResponses, DeleteFunctionSecretData, DeleteFunctionSecretError, DeleteFunctionSecretErrors, DeleteFunctionSecretResponse, DeleteFunctionSecretResponses, DeleteMemoryData, DeleteMemoryError, DeleteMemoryErrors, DeleteMemoryResponse, DeleteMemoryResponses, DeleteMemoryResult, DeleteOrgSecretData, DeleteOrgSecretError, DeleteOrgSecretErrors, DeleteOrgSecretResponse, DeleteOrgSecretResponses, DeleteRegistryData, DeleteRegistryError, DeleteRegistryErrors, DeleteRegistryResponse, DeleteRegistryResponses, DeleteRouteData, DeleteRouteError, DeleteRouteErrors, DeleteRouteResponse, DeleteRouteResponses, DeleteWakeAuthorizationData, DeleteWakeAuthorizationError, DeleteWakeAuthorizationErrors, DeleteWakeAuthorizationResponse, DeleteWakeAuthorizationResponses, DeleteWakeScheduleData, DeleteWakeScheduleError, DeleteWakeScheduleErrors, DeleteWakeScheduleResponse, DeleteWakeScheduleResponses, DeliveryStatus, DeliverySummary, DiscardContentResult, DiscardEmailContentData, DiscardEmailContentError, DiscardEmailContentErrors, DiscardEmailContentResponse, DiscardEmailContentResponses, DkimSignature, Domain, DomainDnsRecord, DomainVerifyResult, DownloadAttachmentsData, DownloadAttachmentsError, DownloadAttachmentsErrors, DownloadAttachmentsResponse, DownloadAttachmentsResponses, DownloadDomainZoneFileData, DownloadDomainZoneFileError, DownloadDomainZoneFileErrors, DownloadDomainZoneFileResponse, DownloadDomainZoneFileResponses, DownloadRawEmailData, DownloadRawEmailError, DownloadRawEmailErrors, DownloadRawEmailResponse, DownloadRawEmailResponses, EmailAddress, EmailAttachment, EmailAuth, EmailDetail, EmailDetailReply, EmailSearchFacetBucket, EmailSearchFacets, EmailSearchHighlights, EmailSearchMeta, EmailSearchResult, EmailStatus, EmailSummary, EmailWebhookStatus, Endpoint, ErrorResponse, Filter, FunctionDeployStatus, FunctionDetail, FunctionListItem, FunctionLogRow, FunctionRouteBody, FunctionRouteResult, FunctionRouting, FunctionSecretListItem, FunctionSecretWriteResult, FunctionTestRun, FunctionTestRunDelivery, FunctionTestRunDeliveryEndpoint, FunctionTestRunInboundEmail, FunctionTestRunOutboundRequest, FunctionTestRunReply, FunctionTestRunSend, FunctionTestRunState, FunctionTestRunTrace, GateDenial, GateFix, GetAccountData, GetAccountError, GetAccountErrors, GetAccountResponse, GetAccountResponses, GetAgentData, GetAgentError, GetAgentErrors, GetAgentResponse, GetAgentResponses, GetChallengeData, GetChallengeError, GetChallengeErrors, GetChallengeResponse, GetChallengeResponses, GetConversationData, GetConversationError, GetConversationErrors, GetConversationResponse, GetConversationResponses, GetEmailData, GetEmailError, GetEmailErrors, GetEmailResponse, GetEmailResponses, GetFunctionData, GetFunctionError, GetFunctionErrors, GetFunctionResponse, GetFunctionResponses, GetFunctionRoutingData, GetFunctionRoutingError, GetFunctionRoutingErrors, GetFunctionRoutingResponse, GetFunctionRoutingResponses, GetFunctionTestRunTraceData, GetFunctionTestRunTraceError, GetFunctionTestRunTraceErrors, GetFunctionTestRunTraceResponse, GetFunctionTestRunTraceResponses, GetInboxStatusData, GetInboxStatusError, GetInboxStatusErrors, GetInboxStatusResponse, GetInboxStatusResponses, GetMemoryData, GetMemoryError, GetMemoryErrors, GetMemoryResponse, GetMemoryResponses, GetOrgRoutingTopologyData, GetOrgRoutingTopologyError, GetOrgRoutingTopologyErrors, GetOrgRoutingTopologyResponse, GetOrgRoutingTopologyResponses, GetRegistryData, GetRegistryError, GetRegistryErrors, GetRegistryResponse, GetRegistryResponses, GetSendPermissionsData, GetSendPermissionsError, GetSendPermissionsErrors, GetSendPermissionsResponse, GetSendPermissionsResponses, GetSentEmailData, GetSentEmailError, GetSentEmailErrors, GetSentEmailResponse, GetSentEmailResponses, GetSpendPolicyData, GetSpendPolicyError, GetSpendPolicyErrors, GetSpendPolicyResponse, GetSpendPolicyResponses, GetStorageStatsData, GetStorageStatsError, GetStorageStatsErrors, GetStorageStatsResponse, GetStorageStatsResponses, GetTemplateData, GetTemplateError, GetTemplateErrors, GetTemplateInstallData, GetTemplateInstallError, GetTemplateInstallErrors, GetTemplateInstallResponse, GetTemplateInstallResponses, GetTemplateResponse, GetTemplateResponses, GetThreadData, GetThreadError, GetThreadErrors, GetThreadResponse, GetThreadResponses, GetWakeScheduleData, GetWakeScheduleError, GetWakeScheduleErrors, GetWakeScheduleResponse, GetWakeScheduleResponses, GetWebhookSecretData, GetWebhookSecretError, GetWebhookSecretErrors, GetWebhookSecretResponse, GetWebhookSecretResponses, IdempotencyKey, InboxStatus, InboxStatusDomain, InboxStatusEndpointSummary, InboxStatusFunctionSummary, InboxStatusNextAction, InboxStatusRecentEmailSummary, InstallTemplateBody, InstallTemplateData, InstallTemplateError, InstallTemplateErrors, InstallTemplateResponse, InstallTemplateResponses, Limit, ListDeclinedPaymentsData, ListDeclinedPaymentsError, ListDeclinedPaymentsErrors, ListDeclinedPaymentsResponse, ListDeclinedPaymentsResponses, ListDeliveriesData, ListDeliveriesError, ListDeliveriesErrors, ListDeliveriesResponse, ListDeliveriesResponses, ListDomainsData, ListDomainsError, ListDomainsErrors, ListDomainsResponse, ListDomainsResponses, ListEmailsData, ListEmailsError, ListEmailsErrors, ListEmailsResponse, ListEmailsResponses, ListEndpointsData, ListEndpointsError, ListEndpointsErrors, ListEndpointsResponse, ListEndpointsResponses, ListEnvelope, ListFiltersData, ListFiltersError, ListFiltersErrors, ListFiltersResponse, ListFiltersResponses, ListFunctionLogsData, ListFunctionLogsError, ListFunctionLogsErrors, ListFunctionLogsResponse, ListFunctionLogsResponses, ListFunctionsData, ListFunctionSecretsData, ListFunctionSecretsError, ListFunctionSecretsErrors, ListFunctionSecretsResponse, ListFunctionSecretsResponses, ListFunctionsError, ListFunctionsErrors, ListFunctionsResponse, ListFunctionsResponses, ListOrgSecretsData, ListOrgSecretsError, ListOrgSecretsErrors, ListOrgSecretsResponse, ListOrgSecretsResponses, ListPayoutAddressesData, ListPayoutAddressesError, ListPayoutAddressesErrors, ListPayoutAddressesResponse, ListPayoutAddressesResponses, ListRegistriesData, ListRegistriesError, ListRegistriesErrors, ListRegistriesResponse, ListRegistriesResponses, ListRegistryAgentsData, ListRegistryAgentsResponse, ListRegistryAgentsResponses, ListRegistryRequestsData, ListRegistryRequestsError, ListRegistryRequestsErrors, ListRegistryRequestsResponse, ListRegistryRequestsResponses, ListRoutesData, ListRoutesError, ListRoutesErrors, ListRoutesResponse, ListRoutesResponses, ListSentEmailsData, ListSentEmailsError, ListSentEmailsErrors, ListSentEmailsResponse, ListSentEmailsResponses, ListTemplatesData, ListTemplatesError, ListTemplatesErrors, ListTemplatesResponse, ListTemplatesResponses, ListWakeAuthorizationsData, ListWakeAuthorizationsError, ListWakeAuthorizationsErrors, ListWakeAuthorizationsResponse, ListWakeAuthorizationsResponses, ListWakeDispatchesData, ListWakeDispatchesError, ListWakeDispatchesErrors, ListWakeDispatchesResponse, ListWakeDispatchesResponses, ListWakeSchedulesData, ListWakeSchedulesError, ListWakeSchedulesErrors, ListWakeSchedulesResponse, ListWakeSchedulesResponses, MemoryJsonValue, MemoryKeyQuery, MemoryRecord, MemoryRecordWithValue, MemoryResolvedScope, MemoryScope, MemoryScopeId, MemoryScopeQueryType, NumericString, OrgSecretListItem, OrgSecretWriteResult, PaginationMeta, ParsedEmailData, PayChallengeData, PayChallengeError, PayChallengeErrors, PayChallengeInput, PayChallengeResponse, PayChallengeResponses, PlanLimits, PollCliLoginData, PollCliLoginError, PollCliLoginErrors, PollCliLoginInput, PollCliLoginResponse, PollCliLoginResponses, PublishAgentData, PublishAgentError, PublishAgentErrors, PublishAgentInput, PublishAgentResponse, PublishAgentResponses, PublishAgentResult, PublishPolicy, RecipientRoute, RegisterPayoutAddressData, RegisterPayoutAddressError, RegisterPayoutAddressErrors, RegisterPayoutAddressInput, RegisterPayoutAddressResponse, RegisterPayoutAddressResponses, Registry, RegistryAgent, RegistryRequest, ReorderRoutesData, ReorderRoutesError, ReorderRoutesErrors, ReorderRoutesInput, ReorderRoutesResponse, ReorderRoutesResponses, ReplayDeliveryData, ReplayDeliveryError, ReplayDeliveryErrors, ReplayDeliveryResponse, ReplayDeliveryResponses, ReplayEmailWebhooksData, ReplayEmailWebhooksError, ReplayEmailWebhooksErrors, ReplayEmailWebhooksResponse, ReplayEmailWebhooksResponses, ReplayResult, ReplyInput, ReplyToEmailData, ReplyToEmailError, ReplyToEmailErrors, ReplyToEmailResponse, ReplyToEmailResponses, ResendAgentSignupVerificationData, ResendAgentSignupVerificationError, ResendAgentSignupVerificationErrors, ResendAgentSignupVerificationInput, ResendAgentSignupVerificationResponse, ResendAgentSignupVerificationResponses, ResendCliSignupVerificationData, ResendCliSignupVerificationError, ResendCliSignupVerificationErrors, ResendCliSignupVerificationInput, ResendCliSignupVerificationResponse, ResendCliSignupVerificationResponses, ResolveRegistryHandleData, ResolveRegistryHandleError, ResolveRegistryHandleErrors, ResolveRegistryHandleResponse, ResolveRegistryHandleResponses, ResourceId, RotateWebhookSecretData, RotateWebhookSecretError, RotateWebhookSecretErrors, RotateWebhookSecretResponse, RotateWebhookSecretResponses, RouteEvaluatedEntry, RoutingTopology, RunWakeScheduleData, RunWakeScheduleError, RunWakeScheduleErrors, RunWakeScheduleResponse, RunWakeScheduleResponses, SearchEmailsData, SearchEmailsError, SearchEmailsErrors, SearchEmailsResponse, SearchEmailsResponses, SearchMemoriesData, SearchMemoriesError, SearchMemoriesErrors, SearchMemoriesResponse, SearchMemoriesResponses, SemanticSearchCoverage, SemanticSearchData, SemanticSearchError, SemanticSearchErrors, SemanticSearchField, SemanticSearchInput, SemanticSearchMeta, SemanticSearchResponse, SemanticSearchResponses, SemanticSearchResult, SemanticSearchScoreBreakdown, SemanticSearchSnippet, SendEmailData, SendEmailError, SendEmailErrors, SendEmailResponse, SendEmailResponses, SendMailAttachment, SendMailInput, SendMailPayloadRef, SendMailResult, SendPermissionAddress, SendPermissionAnyRecipient, SendPermissionManagedZone, SendPermissionRule, SendPermissionsMeta, SendPermissionYourDomain, SentEmailDetail, SentEmailStatus, SentEmailSummary, SetFunctionRouteData, SetFunctionRouteError, SetFunctionRouteErrors, SetFunctionRouteResponse, SetFunctionRouteResponses, SetFunctionSecretData, SetFunctionSecretError, SetFunctionSecretErrors, SetFunctionSecretInput, SetFunctionSecretResponse, SetFunctionSecretResponses, SetMemoryData, SetMemoryError, SetMemoryErrors, SetMemoryInput, SetMemoryResponse, SetMemoryResponses, SetOrgSecretData, SetOrgSecretError, SetOrgSecretErrors, SetOrgSecretInput, SetOrgSecretResponse, SetOrgSecretResponses, SimulateRouteData, SimulateRouteError, SimulateRouteErrors, SimulateRouteInput, SimulateRouteResponse, SimulateRouteResponses, SimulateRouteResult, StartAgentClaimData, StartAgentClaimError, StartAgentClaimErrors, StartAgentClaimInput, StartAgentClaimResponse, StartAgentClaimResponses, StartAgentSignupData, StartAgentSignupError, StartAgentSignupErrors, StartAgentSignupInput, StartAgentSignupResponse, StartAgentSignupResponses, StartCliLoginData, StartCliLoginError, StartCliLoginErrors, StartCliLoginInput, StartCliLoginResponse, StartCliLoginResponses, StartCliSignupData, StartCliSignupError, StartCliSignupErrors, StartCliSignupInput, StartCliSignupResponse, StartCliSignupResponses, StorageStats, SuccessEnvelope, TemplateAuthor, TemplateInstall, TemplateInstallState, TemplateInstallStatus, TemplateManifest, TemplateRegistryDetail, TemplateRegistryPage, TemplateRegistryStatus, TemplateRegistrySummary, TemplateSecret, TemplateSecretGroup, TemplateSetup, TemplateSource, TemplateVariable, TemplateVariableValidation, TestEndpointData, TestEndpointError, TestEndpointErrors, TestEndpointResponse, TestEndpointResponses, TestFunctionData, TestFunctionError, TestFunctionErrors, TestFunctionResponse, TestFunctionResponses, TestInvocationResult, TestResult, Thread, ThreadMessage, UnpublishAgentData, UnpublishAgentError, UnpublishAgentErrors, UnpublishAgentResponse, UnpublishAgentResponses, UnsetFunctionRouteData, UnsetFunctionRouteError, UnsetFunctionRouteErrors, UnsetFunctionRouteResponse, UnsetFunctionRouteResponses, UnverifiedDomain, UpdateAccountData, UpdateAccountError, UpdateAccountErrors, UpdateAccountInput, UpdateAccountResponse, UpdateAccountResponses, UpdateDomainData, UpdateDomainError, UpdateDomainErrors, UpdateDomainInput, UpdateDomainResponse, UpdateDomainResponses, UpdateEndpointData, UpdateEndpointError, UpdateEndpointErrors, UpdateEndpointInput, UpdateEndpointResponse, UpdateEndpointResponses, UpdateFilterData, UpdateFilterError, UpdateFilterErrors, UpdateFilterInput, UpdateFilterResponse, UpdateFilterResponses, UpdateFunctionData, UpdateFunctionError, UpdateFunctionErrors, UpdateFunctionInput, UpdateFunctionResponse, UpdateFunctionResponses, UpdateRegistryData, UpdateRegistryError, UpdateRegistryErrors, UpdateRegistryInput, UpdateRegistryResponse, UpdateRegistryResponses, UpdateRouteData, UpdateRouteError, UpdateRouteErrors, UpdateRouteInput, UpdateRouteResponse, UpdateRouteResponses, UpdateSpendPolicyData, UpdateSpendPolicyError, UpdateSpendPolicyErrors, UpdateSpendPolicyInput, UpdateSpendPolicyResponse, UpdateSpendPolicyResponses, UpdateWakeAuthorizationData, UpdateWakeAuthorizationError, UpdateWakeAuthorizationErrors, UpdateWakeAuthorizationInput, UpdateWakeAuthorizationResponse, UpdateWakeAuthorizationResponses, UpdateWakeScheduleData, UpdateWakeScheduleError, UpdateWakeScheduleErrors, UpdateWakeScheduleInput, UpdateWakeScheduleResponse, UpdateWakeScheduleResponses, VerifiedDomain, VerifyAgentClaimData, VerifyAgentClaimError, VerifyAgentClaimErrors, VerifyAgentClaimInput, VerifyAgentClaimResponse, VerifyAgentClaimResponses, VerifyAgentSignupData, VerifyAgentSignupError, VerifyAgentSignupErrors, VerifyAgentSignupInput, VerifyAgentSignupResponse, VerifyAgentSignupResponses, VerifyCliSignupData, VerifyCliSignupError, VerifyCliSignupErrors, VerifyCliSignupInput, VerifyCliSignupResponse, VerifyCliSignupResponses, VerifyDomainData, VerifyDomainError, VerifyDomainErrors, VerifyDomainResponse, VerifyDomainResponses, WakeAuthorization, WakeDispatch, WakeSchedule, WebhookSecret, X402Challenge, X402DeclinedPayment, X402EmailChallenge, X402EmailChallengeDetails, X402NonceBinding, X402PaymentPayload, X402PaymentRequirements, X402PayoutAddress, X402Receipt, X402SpendPolicy } from './types.gen.js'; +export { addDomain, awaitReply, cliLogout, createAgentAccount, createAgentClaimLink, createChallenge, createEmailChallenge, createEndpoint, createFilter, createFunction, createFunctionSecret, createOrgSecret, createRegistry, createRoute, createWakeAuthorization, createWakeSchedule, decideRegistryRequest, defineAgent, deleteDomain, deleteEmail, deleteEndpoint, deleteFilter, deleteFunction, deleteFunctionSecret, deleteMemory, deleteOrgSecret, deleteRegistry, deleteRoute, deleteWakeAuthorization, deleteWakeSchedule, discardEmailContent, downloadAttachments, downloadDomainZoneFile, downloadRawEmail, getAccount, getAgent, getChallenge, getConversation, getEmail, getFunction, getFunctionRouting, getFunctionTestRunTrace, getInboxStatus, getMemory, getOrgRoutingTopology, getRegistry, getSendPermissions, getSentEmail, getSpendPolicy, getStorageStats, getTemplate, getTemplateInstall, getThread, getWakeSchedule, getWebhookSecret, installTemplate, listDeclinedPayments, listDeliveries, listDomains, listEmails, listEndpoints, listFilters, listFunctionLogs, listFunctions, listFunctionSecrets, listOrgSecrets, listPayoutAddresses, listRegistries, listRegistryAgents, listRegistryRequests, listRoutes, listSentEmails, listTemplates, listWakeAuthorizations, listWakeDispatches, listWakeSchedules, type Options, payChallenge, pollCliLogin, publishAgent, registerPayoutAddress, reorderRoutes, replayDelivery, replayEmailWebhooks, replyToEmail, resendAgentSignupVerification, resendCliSignupVerification, resolveRegistryHandle, rotateWebhookSecret, runWakeSchedule, searchEmails, searchMemories, semanticSearch, sendEmail, setFunctionRoute, setFunctionSecret, setMemory, setOrgSecret, simulateRoute, startAgentClaim, startAgentSignup, startCliLogin, startCliSignup, testEndpoint, testFunction, unpublishAgent, unsetFunctionRoute, updateAccount, updateDomain, updateEndpoint, updateFilter, updateFunction, updateRegistry, updateRoute, updateSpendPolicy, updateWakeAuthorization, updateWakeSchedule, verifyAgentClaim, verifyAgentSignup, verifyCliSignup, verifyDomain } from './sdk.gen.js'; +export type { Account, AccountUpdated, AddDomainData, AddDomainError, AddDomainErrors, AddDomainInput, AddDomainResponse, AddDomainResponses, AgentAccountResult, AgentAccountUpgradeHint, AgentClaimLinkResult, AgentClaimResult, AgentClaimStartResult, AgentOrgRef, AgentSignupResendResult, AgentSignupStartResult, AgentSignupVerifyResult, AwaitReplyData, AwaitReplyError, AwaitReplyErrors, AwaitReplyResponse, AwaitReplyResponses, AwaitReplyResult, ClientOptions, CliLoginPollResult, CliLoginStartResult, CliLogoutData, CliLogoutError, CliLogoutErrors, CliLogoutInput, CliLogoutResponse, CliLogoutResponses, CliLogoutResult, CliSignupResendResult, CliSignupStartResult, CliSignupVerifyResult, Conversation, ConversationMessage, CreateAgentAccountData, CreateAgentAccountError, CreateAgentAccountErrors, CreateAgentAccountInput, CreateAgentAccountResponse, CreateAgentAccountResponses, CreateAgentClaimLinkData, CreateAgentClaimLinkError, CreateAgentClaimLinkErrors, CreateAgentClaimLinkInput, CreateAgentClaimLinkResponse, CreateAgentClaimLinkResponses, CreateChallengeData, CreateChallengeError, CreateChallengeErrors, CreateChallengeInput, CreateChallengeResponse, CreateChallengeResponses, CreateEmailChallengeData, CreateEmailChallengeError, CreateEmailChallengeErrors, CreateEmailChallengeInput, CreateEmailChallengeResponse, CreateEmailChallengeResponses, CreateEndpointData, CreateEndpointError, CreateEndpointErrors, CreateEndpointInput, CreateEndpointResponse, CreateEndpointResponses, CreateFilterData, CreateFilterError, CreateFilterErrors, CreateFilterInput, CreateFilterResponse, CreateFilterResponses, CreateFunctionData, CreateFunctionError, CreateFunctionErrors, CreateFunctionInput, CreateFunctionResponse, CreateFunctionResponses, CreateFunctionResult, CreateFunctionSecretData, CreateFunctionSecretError, CreateFunctionSecretErrors, CreateFunctionSecretInput, CreateFunctionSecretResponse, CreateFunctionSecretResponses, CreateOrgSecretData, CreateOrgSecretError, CreateOrgSecretErrors, CreateOrgSecretInput, CreateOrgSecretResponse, CreateOrgSecretResponses, CreateRegistryData, CreateRegistryError, CreateRegistryErrors, CreateRegistryInput, CreateRegistryResponse, CreateRegistryResponses, CreateRouteData, CreateRouteError, CreateRouteErrors, CreateRouteInput, CreateRouteResponse, CreateRouteResponses, CreateWakeAuthorizationData, CreateWakeAuthorizationError, CreateWakeAuthorizationErrors, CreateWakeAuthorizationInput, CreateWakeAuthorizationResponse, CreateWakeAuthorizationResponses, CreateWakeScheduleData, CreateWakeScheduleError, CreateWakeScheduleErrors, CreateWakeScheduleInput, CreateWakeScheduleResponse, CreateWakeScheduleResponses, Cursor, DecideRegistryRequestData, DecideRegistryRequestError, DecideRegistryRequestErrors, DecideRegistryRequestInput, DecideRegistryRequestResponse, DecideRegistryRequestResponses, DefineAgentData, DefineAgentError, DefineAgentErrors, DefineAgentInput, DefineAgentResponse, DefineAgentResponses, DeleteDomainData, DeleteDomainError, DeleteDomainErrors, DeleteDomainResponse, DeleteDomainResponses, DeleteEmailData, DeleteEmailError, DeleteEmailErrors, DeleteEmailResponse, DeleteEmailResponses, DeleteEndpointData, DeleteEndpointError, DeleteEndpointErrors, DeleteEndpointResponse, DeleteEndpointResponses, DeleteFilterData, DeleteFilterError, DeleteFilterErrors, DeleteFilterResponse, DeleteFilterResponses, DeleteFunctionData, DeleteFunctionError, DeleteFunctionErrors, DeleteFunctionResponse, DeleteFunctionResponses, DeleteFunctionSecretData, DeleteFunctionSecretError, DeleteFunctionSecretErrors, DeleteFunctionSecretResponse, DeleteFunctionSecretResponses, DeleteMemoryData, DeleteMemoryError, DeleteMemoryErrors, DeleteMemoryResponse, DeleteMemoryResponses, DeleteMemoryResult, DeleteOrgSecretData, DeleteOrgSecretError, DeleteOrgSecretErrors, DeleteOrgSecretResponse, DeleteOrgSecretResponses, DeleteRegistryData, DeleteRegistryError, DeleteRegistryErrors, DeleteRegistryResponse, DeleteRegistryResponses, DeleteRouteData, DeleteRouteError, DeleteRouteErrors, DeleteRouteResponse, DeleteRouteResponses, DeleteWakeAuthorizationData, DeleteWakeAuthorizationError, DeleteWakeAuthorizationErrors, DeleteWakeAuthorizationResponse, DeleteWakeAuthorizationResponses, DeleteWakeScheduleData, DeleteWakeScheduleError, DeleteWakeScheduleErrors, DeleteWakeScheduleResponse, DeleteWakeScheduleResponses, DeliveryStatus, DeliverySummary, DiscardContentResult, DiscardEmailContentData, DiscardEmailContentError, DiscardEmailContentErrors, DiscardEmailContentResponse, DiscardEmailContentResponses, DkimSignature, Domain, DomainDnsRecord, DomainVerifyResult, DownloadAttachmentsData, DownloadAttachmentsError, DownloadAttachmentsErrors, DownloadAttachmentsResponse, DownloadAttachmentsResponses, DownloadDomainZoneFileData, DownloadDomainZoneFileError, DownloadDomainZoneFileErrors, DownloadDomainZoneFileResponse, DownloadDomainZoneFileResponses, DownloadRawEmailData, DownloadRawEmailError, DownloadRawEmailErrors, DownloadRawEmailResponse, DownloadRawEmailResponses, EmailAddress, EmailAttachment, EmailAuth, EmailDetail, EmailDetailReply, EmailSearchFacetBucket, EmailSearchFacets, EmailSearchHighlights, EmailSearchMeta, EmailSearchResult, EmailStatus, EmailSummary, EmailWebhookStatus, Endpoint, ErrorResponse, Filter, FunctionDeployStatus, FunctionDetail, FunctionListItem, FunctionLogRow, FunctionRouteBody, FunctionRouteResult, FunctionRouting, FunctionSecretListItem, FunctionSecretWriteResult, FunctionTestRun, FunctionTestRunDelivery, FunctionTestRunDeliveryEndpoint, FunctionTestRunInboundEmail, FunctionTestRunOutboundRequest, FunctionTestRunReply, FunctionTestRunSend, FunctionTestRunState, FunctionTestRunTrace, GateDenial, GateFix, GetAccountData, GetAccountError, GetAccountErrors, GetAccountResponse, GetAccountResponses, GetAgentData, GetAgentError, GetAgentErrors, GetAgentResponse, GetAgentResponses, GetChallengeData, GetChallengeError, GetChallengeErrors, GetChallengeResponse, GetChallengeResponses, GetConversationData, GetConversationError, GetConversationErrors, GetConversationResponse, GetConversationResponses, GetEmailData, GetEmailError, GetEmailErrors, GetEmailResponse, GetEmailResponses, GetFunctionData, GetFunctionError, GetFunctionErrors, GetFunctionResponse, GetFunctionResponses, GetFunctionRoutingData, GetFunctionRoutingError, GetFunctionRoutingErrors, GetFunctionRoutingResponse, GetFunctionRoutingResponses, GetFunctionTestRunTraceData, GetFunctionTestRunTraceError, GetFunctionTestRunTraceErrors, GetFunctionTestRunTraceResponse, GetFunctionTestRunTraceResponses, GetInboxStatusData, GetInboxStatusError, GetInboxStatusErrors, GetInboxStatusResponse, GetInboxStatusResponses, GetMemoryData, GetMemoryError, GetMemoryErrors, GetMemoryResponse, GetMemoryResponses, GetOrgRoutingTopologyData, GetOrgRoutingTopologyError, GetOrgRoutingTopologyErrors, GetOrgRoutingTopologyResponse, GetOrgRoutingTopologyResponses, GetRegistryData, GetRegistryError, GetRegistryErrors, GetRegistryResponse, GetRegistryResponses, GetSendPermissionsData, GetSendPermissionsError, GetSendPermissionsErrors, GetSendPermissionsResponse, GetSendPermissionsResponses, GetSentEmailData, GetSentEmailError, GetSentEmailErrors, GetSentEmailResponse, GetSentEmailResponses, GetSpendPolicyData, GetSpendPolicyError, GetSpendPolicyErrors, GetSpendPolicyResponse, GetSpendPolicyResponses, GetStorageStatsData, GetStorageStatsError, GetStorageStatsErrors, GetStorageStatsResponse, GetStorageStatsResponses, GetTemplateData, GetTemplateError, GetTemplateErrors, GetTemplateInstallData, GetTemplateInstallError, GetTemplateInstallErrors, GetTemplateInstallResponse, GetTemplateInstallResponses, GetTemplateResponse, GetTemplateResponses, GetThreadData, GetThreadError, GetThreadErrors, GetThreadResponse, GetThreadResponses, GetWakeScheduleData, GetWakeScheduleError, GetWakeScheduleErrors, GetWakeScheduleResponse, GetWakeScheduleResponses, GetWebhookSecretData, GetWebhookSecretError, GetWebhookSecretErrors, GetWebhookSecretResponse, GetWebhookSecretResponses, IdempotencyKey, InboxStatus, InboxStatusDomain, InboxStatusEndpointSummary, InboxStatusFunctionSummary, InboxStatusNextAction, InboxStatusRecentEmailSummary, InstallTemplateBody, InstallTemplateData, InstallTemplateError, InstallTemplateErrors, InstallTemplateResponse, InstallTemplateResponses, Limit, ListDeclinedPaymentsData, ListDeclinedPaymentsError, ListDeclinedPaymentsErrors, ListDeclinedPaymentsResponse, ListDeclinedPaymentsResponses, ListDeliveriesData, ListDeliveriesError, ListDeliveriesErrors, ListDeliveriesResponse, ListDeliveriesResponses, ListDomainsData, ListDomainsError, ListDomainsErrors, ListDomainsResponse, ListDomainsResponses, ListEmailsData, ListEmailsError, ListEmailsErrors, ListEmailsResponse, ListEmailsResponses, ListEndpointsData, ListEndpointsError, ListEndpointsErrors, ListEndpointsResponse, ListEndpointsResponses, ListEnvelope, ListFiltersData, ListFiltersError, ListFiltersErrors, ListFiltersResponse, ListFiltersResponses, ListFunctionLogsData, ListFunctionLogsError, ListFunctionLogsErrors, ListFunctionLogsResponse, ListFunctionLogsResponses, ListFunctionsData, ListFunctionSecretsData, ListFunctionSecretsError, ListFunctionSecretsErrors, ListFunctionSecretsResponse, ListFunctionSecretsResponses, ListFunctionsError, ListFunctionsErrors, ListFunctionsResponse, ListFunctionsResponses, ListOrgSecretsData, ListOrgSecretsError, ListOrgSecretsErrors, ListOrgSecretsResponse, ListOrgSecretsResponses, ListPayoutAddressesData, ListPayoutAddressesError, ListPayoutAddressesErrors, ListPayoutAddressesResponse, ListPayoutAddressesResponses, ListRegistriesData, ListRegistriesError, ListRegistriesErrors, ListRegistriesResponse, ListRegistriesResponses, ListRegistryAgentsData, ListRegistryAgentsResponse, ListRegistryAgentsResponses, ListRegistryRequestsData, ListRegistryRequestsError, ListRegistryRequestsErrors, ListRegistryRequestsResponse, ListRegistryRequestsResponses, ListRoutesData, ListRoutesError, ListRoutesErrors, ListRoutesResponse, ListRoutesResponses, ListSentEmailsData, ListSentEmailsError, ListSentEmailsErrors, ListSentEmailsResponse, ListSentEmailsResponses, ListTemplatesData, ListTemplatesError, ListTemplatesErrors, ListTemplatesResponse, ListTemplatesResponses, ListWakeAuthorizationsData, ListWakeAuthorizationsError, ListWakeAuthorizationsErrors, ListWakeAuthorizationsResponse, ListWakeAuthorizationsResponses, ListWakeDispatchesData, ListWakeDispatchesError, ListWakeDispatchesErrors, ListWakeDispatchesResponse, ListWakeDispatchesResponses, ListWakeSchedulesData, ListWakeSchedulesError, ListWakeSchedulesErrors, ListWakeSchedulesResponse, ListWakeSchedulesResponses, MemoryJsonValue, MemoryKeyQuery, MemoryRecord, MemoryRecordWithValue, MemoryResolvedScope, MemoryScope, MemoryScopeId, MemoryScopeQueryType, NumericString, OrgSecretListItem, OrgSecretWriteResult, PaginationMeta, ParsedEmailData, PayChallengeData, PayChallengeError, PayChallengeErrors, PayChallengeInput, PayChallengeResponse, PayChallengeResponses, PlanLimits, PollCliLoginData, PollCliLoginError, PollCliLoginErrors, PollCliLoginInput, PollCliLoginResponse, PollCliLoginResponses, PublishAgentData, PublishAgentError, PublishAgentErrors, PublishAgentInput, PublishAgentResponse, PublishAgentResponses, PublishAgentResult, PublishPolicy, RecipientRoute, RegisterPayoutAddressData, RegisterPayoutAddressError, RegisterPayoutAddressErrors, RegisterPayoutAddressInput, RegisterPayoutAddressResponse, RegisterPayoutAddressResponses, Registry, RegistryAgent, RegistryRequest, ReorderRoutesData, ReorderRoutesError, ReorderRoutesErrors, ReorderRoutesInput, ReorderRoutesResponse, ReorderRoutesResponses, ReplayDeliveryData, ReplayDeliveryError, ReplayDeliveryErrors, ReplayDeliveryResponse, ReplayDeliveryResponses, ReplayEmailWebhooksData, ReplayEmailWebhooksError, ReplayEmailWebhooksErrors, ReplayEmailWebhooksResponse, ReplayEmailWebhooksResponses, ReplayResult, ReplyEmail, ReplyInput, ReplyToEmailData, ReplyToEmailError, ReplyToEmailErrors, ReplyToEmailResponse, ReplyToEmailResponses, ResendAgentSignupVerificationData, ResendAgentSignupVerificationError, ResendAgentSignupVerificationErrors, ResendAgentSignupVerificationInput, ResendAgentSignupVerificationResponse, ResendAgentSignupVerificationResponses, ResendCliSignupVerificationData, ResendCliSignupVerificationError, ResendCliSignupVerificationErrors, ResendCliSignupVerificationInput, ResendCliSignupVerificationResponse, ResendCliSignupVerificationResponses, ResolveRegistryHandleData, ResolveRegistryHandleError, ResolveRegistryHandleErrors, ResolveRegistryHandleResponse, ResolveRegistryHandleResponses, ResourceId, RotateWebhookSecretData, RotateWebhookSecretError, RotateWebhookSecretErrors, RotateWebhookSecretResponse, RotateWebhookSecretResponses, RouteEvaluatedEntry, RoutingTopology, RunWakeScheduleData, RunWakeScheduleError, RunWakeScheduleErrors, RunWakeScheduleResponse, RunWakeScheduleResponses, SearchEmailsData, SearchEmailsError, SearchEmailsErrors, SearchEmailsResponse, SearchEmailsResponses, SearchMemoriesData, SearchMemoriesError, SearchMemoriesErrors, SearchMemoriesResponse, SearchMemoriesResponses, SemanticSearchCoverage, SemanticSearchData, SemanticSearchError, SemanticSearchErrors, SemanticSearchField, SemanticSearchInput, SemanticSearchMeta, SemanticSearchResponse, SemanticSearchResponses, SemanticSearchResult, SemanticSearchScoreBreakdown, SemanticSearchSnippet, SendEmailData, SendEmailError, SendEmailErrors, SendEmailResponse, SendEmailResponses, SendMailAttachment, SendMailInput, SendMailPayloadRef, SendMailResult, SendPermissionAddress, SendPermissionAnyRecipient, SendPermissionManagedZone, SendPermissionRule, SendPermissionsMeta, SendPermissionYourDomain, SentEmailDetail, SentEmailStatus, SentEmailSummary, SetFunctionRouteData, SetFunctionRouteError, SetFunctionRouteErrors, SetFunctionRouteResponse, SetFunctionRouteResponses, SetFunctionSecretData, SetFunctionSecretError, SetFunctionSecretErrors, SetFunctionSecretInput, SetFunctionSecretResponse, SetFunctionSecretResponses, SetMemoryData, SetMemoryError, SetMemoryErrors, SetMemoryInput, SetMemoryResponse, SetMemoryResponses, SetOrgSecretData, SetOrgSecretError, SetOrgSecretErrors, SetOrgSecretInput, SetOrgSecretResponse, SetOrgSecretResponses, SimulateRouteData, SimulateRouteError, SimulateRouteErrors, SimulateRouteInput, SimulateRouteResponse, SimulateRouteResponses, SimulateRouteResult, StartAgentClaimData, StartAgentClaimError, StartAgentClaimErrors, StartAgentClaimInput, StartAgentClaimResponse, StartAgentClaimResponses, StartAgentSignupData, StartAgentSignupError, StartAgentSignupErrors, StartAgentSignupInput, StartAgentSignupResponse, StartAgentSignupResponses, StartCliLoginData, StartCliLoginError, StartCliLoginErrors, StartCliLoginInput, StartCliLoginResponse, StartCliLoginResponses, StartCliSignupData, StartCliSignupError, StartCliSignupErrors, StartCliSignupInput, StartCliSignupResponse, StartCliSignupResponses, StorageStats, SuccessEnvelope, TemplateAuthor, TemplateInstall, TemplateInstallState, TemplateInstallStatus, TemplateManifest, TemplateRegistryDetail, TemplateRegistryPage, TemplateRegistryStatus, TemplateRegistrySummary, TemplateSecret, TemplateSecretGroup, TemplateSetup, TemplateSource, TemplateVariable, TemplateVariableValidation, TestEndpointData, TestEndpointError, TestEndpointErrors, TestEndpointResponse, TestEndpointResponses, TestFunctionData, TestFunctionError, TestFunctionErrors, TestFunctionResponse, TestFunctionResponses, TestInvocationResult, TestResult, Thread, ThreadMessage, UnpublishAgentData, UnpublishAgentError, UnpublishAgentErrors, UnpublishAgentResponse, UnpublishAgentResponses, UnsetFunctionRouteData, UnsetFunctionRouteError, UnsetFunctionRouteErrors, UnsetFunctionRouteResponse, UnsetFunctionRouteResponses, UnverifiedDomain, UpdateAccountData, UpdateAccountError, UpdateAccountErrors, UpdateAccountInput, UpdateAccountResponse, UpdateAccountResponses, UpdateDomainData, UpdateDomainError, UpdateDomainErrors, UpdateDomainInput, UpdateDomainResponse, UpdateDomainResponses, UpdateEndpointData, UpdateEndpointError, UpdateEndpointErrors, UpdateEndpointInput, UpdateEndpointResponse, UpdateEndpointResponses, UpdateFilterData, UpdateFilterError, UpdateFilterErrors, UpdateFilterInput, UpdateFilterResponse, UpdateFilterResponses, UpdateFunctionData, UpdateFunctionError, UpdateFunctionErrors, UpdateFunctionInput, UpdateFunctionResponse, UpdateFunctionResponses, UpdateRegistryData, UpdateRegistryError, UpdateRegistryErrors, UpdateRegistryInput, UpdateRegistryResponse, UpdateRegistryResponses, UpdateRouteData, UpdateRouteError, UpdateRouteErrors, UpdateRouteInput, UpdateRouteResponse, UpdateRouteResponses, UpdateSpendPolicyData, UpdateSpendPolicyError, UpdateSpendPolicyErrors, UpdateSpendPolicyInput, UpdateSpendPolicyResponse, UpdateSpendPolicyResponses, UpdateWakeAuthorizationData, UpdateWakeAuthorizationError, UpdateWakeAuthorizationErrors, UpdateWakeAuthorizationInput, UpdateWakeAuthorizationResponse, UpdateWakeAuthorizationResponses, UpdateWakeScheduleData, UpdateWakeScheduleError, UpdateWakeScheduleErrors, UpdateWakeScheduleInput, UpdateWakeScheduleResponse, UpdateWakeScheduleResponses, VerifiedDomain, VerifyAgentClaimData, VerifyAgentClaimError, VerifyAgentClaimErrors, VerifyAgentClaimInput, VerifyAgentClaimResponse, VerifyAgentClaimResponses, VerifyAgentSignupData, VerifyAgentSignupError, VerifyAgentSignupErrors, VerifyAgentSignupInput, VerifyAgentSignupResponse, VerifyAgentSignupResponses, VerifyCliSignupData, VerifyCliSignupError, VerifyCliSignupErrors, VerifyCliSignupInput, VerifyCliSignupResponse, VerifyCliSignupResponses, VerifyDomainData, VerifyDomainError, VerifyDomainErrors, VerifyDomainResponse, VerifyDomainResponses, WakeAuthorization, WakeDispatch, WakeSchedule, WebhookSecret, X402Challenge, X402DeclinedPayment, X402EmailChallenge, X402EmailChallengeDetails, X402NonceBinding, X402PaymentPayload, X402PaymentRequirements, X402PayoutAddress, X402Receipt, X402SpendPolicy } from './types.gen.js'; diff --git a/packages/api-core/src/api/sdk.gen.ts b/packages/api-core/src/api/sdk.gen.ts index af7bd108..cbd6dae2 100644 --- a/packages/api-core/src/api/sdk.gen.ts +++ b/packages/api-core/src/api/sdk.gen.ts @@ -2,7 +2,7 @@ import type { Client, Options as Options2, TDataShape } from './client/index.js'; import { client } from './client.gen.js'; -import type { AddDomainData, AddDomainErrors, AddDomainResponses, CliLogoutData, CliLogoutErrors, CliLogoutResponses, CreateAgentAccountData, CreateAgentAccountErrors, CreateAgentAccountResponses, CreateAgentClaimLinkData, CreateAgentClaimLinkErrors, CreateAgentClaimLinkResponses, CreateChallengeData, CreateChallengeErrors, CreateChallengeResponses, CreateEmailChallengeData, CreateEmailChallengeErrors, CreateEmailChallengeResponses, CreateEndpointData, CreateEndpointErrors, CreateEndpointResponses, CreateFilterData, CreateFilterErrors, CreateFilterResponses, CreateFunctionData, CreateFunctionErrors, CreateFunctionResponses, CreateFunctionSecretData, CreateFunctionSecretErrors, CreateFunctionSecretResponses, CreateOrgSecretData, CreateOrgSecretErrors, CreateOrgSecretResponses, CreateRegistryData, CreateRegistryErrors, CreateRegistryResponses, CreateRouteData, CreateRouteErrors, CreateRouteResponses, CreateWakeAuthorizationData, CreateWakeAuthorizationErrors, CreateWakeAuthorizationResponses, CreateWakeScheduleData, CreateWakeScheduleErrors, CreateWakeScheduleResponses, DecideRegistryRequestData, DecideRegistryRequestErrors, DecideRegistryRequestResponses, DefineAgentData, DefineAgentErrors, DefineAgentResponses, DeleteDomainData, DeleteDomainErrors, DeleteDomainResponses, DeleteEmailData, DeleteEmailErrors, DeleteEmailResponses, DeleteEndpointData, DeleteEndpointErrors, DeleteEndpointResponses, DeleteFilterData, DeleteFilterErrors, DeleteFilterResponses, DeleteFunctionData, DeleteFunctionErrors, DeleteFunctionResponses, DeleteFunctionSecretData, DeleteFunctionSecretErrors, DeleteFunctionSecretResponses, DeleteMemoryData, DeleteMemoryErrors, DeleteMemoryResponses, DeleteOrgSecretData, DeleteOrgSecretErrors, DeleteOrgSecretResponses, DeleteRegistryData, DeleteRegistryErrors, DeleteRegistryResponses, DeleteRouteData, DeleteRouteErrors, DeleteRouteResponses, DeleteWakeAuthorizationData, DeleteWakeAuthorizationErrors, DeleteWakeAuthorizationResponses, DeleteWakeScheduleData, DeleteWakeScheduleErrors, DeleteWakeScheduleResponses, DiscardEmailContentData, DiscardEmailContentErrors, DiscardEmailContentResponses, DownloadAttachmentsData, DownloadAttachmentsErrors, DownloadAttachmentsResponses, DownloadDomainZoneFileData, DownloadDomainZoneFileErrors, DownloadDomainZoneFileResponses, DownloadRawEmailData, DownloadRawEmailErrors, DownloadRawEmailResponses, GetAccountData, GetAccountErrors, GetAccountResponses, GetAgentData, GetAgentErrors, GetAgentResponses, GetChallengeData, GetChallengeErrors, GetChallengeResponses, GetConversationData, GetConversationErrors, GetConversationResponses, GetEmailData, GetEmailErrors, GetEmailResponses, GetFunctionData, GetFunctionErrors, GetFunctionResponses, GetFunctionRoutingData, GetFunctionRoutingErrors, GetFunctionRoutingResponses, GetFunctionTestRunTraceData, GetFunctionTestRunTraceErrors, GetFunctionTestRunTraceResponses, GetInboxStatusData, GetInboxStatusErrors, GetInboxStatusResponses, GetMemoryData, GetMemoryErrors, GetMemoryResponses, GetOrgRoutingTopologyData, GetOrgRoutingTopologyErrors, GetOrgRoutingTopologyResponses, GetRegistryData, GetRegistryErrors, GetRegistryResponses, GetSendPermissionsData, GetSendPermissionsErrors, GetSendPermissionsResponses, GetSentEmailData, GetSentEmailErrors, GetSentEmailResponses, GetSpendPolicyData, GetSpendPolicyErrors, GetSpendPolicyResponses, GetStorageStatsData, GetStorageStatsErrors, GetStorageStatsResponses, GetTemplateData, GetTemplateErrors, GetTemplateInstallData, GetTemplateInstallErrors, GetTemplateInstallResponses, GetTemplateResponses, GetThreadData, GetThreadErrors, GetThreadResponses, GetWakeScheduleData, GetWakeScheduleErrors, GetWakeScheduleResponses, GetWebhookSecretData, GetWebhookSecretErrors, GetWebhookSecretResponses, InstallTemplateData, InstallTemplateErrors, InstallTemplateResponses, ListDeclinedPaymentsData, ListDeclinedPaymentsErrors, ListDeclinedPaymentsResponses, ListDeliveriesData, ListDeliveriesErrors, ListDeliveriesResponses, ListDomainsData, ListDomainsErrors, ListDomainsResponses, ListEmailsData, ListEmailsErrors, ListEmailsResponses, ListEndpointsData, ListEndpointsErrors, ListEndpointsResponses, ListFiltersData, ListFiltersErrors, ListFiltersResponses, ListFunctionLogsData, ListFunctionLogsErrors, ListFunctionLogsResponses, ListFunctionsData, ListFunctionSecretsData, ListFunctionSecretsErrors, ListFunctionSecretsResponses, ListFunctionsErrors, ListFunctionsResponses, ListOrgSecretsData, ListOrgSecretsErrors, ListOrgSecretsResponses, ListPayoutAddressesData, ListPayoutAddressesErrors, ListPayoutAddressesResponses, ListRegistriesData, ListRegistriesErrors, ListRegistriesResponses, ListRegistryAgentsData, ListRegistryAgentsResponses, ListRegistryRequestsData, ListRegistryRequestsErrors, ListRegistryRequestsResponses, ListRoutesData, ListRoutesErrors, ListRoutesResponses, ListSentEmailsData, ListSentEmailsErrors, ListSentEmailsResponses, ListTemplatesData, ListTemplatesErrors, ListTemplatesResponses, ListWakeAuthorizationsData, ListWakeAuthorizationsErrors, ListWakeAuthorizationsResponses, ListWakeDispatchesData, ListWakeDispatchesErrors, ListWakeDispatchesResponses, ListWakeSchedulesData, ListWakeSchedulesErrors, ListWakeSchedulesResponses, PayChallengeData, PayChallengeErrors, PayChallengeResponses, PollCliLoginData, PollCliLoginErrors, PollCliLoginResponses, PublishAgentData, PublishAgentErrors, PublishAgentResponses, RegisterPayoutAddressData, RegisterPayoutAddressErrors, RegisterPayoutAddressResponses, ReorderRoutesData, ReorderRoutesErrors, ReorderRoutesResponses, ReplayDeliveryData, ReplayDeliveryErrors, ReplayDeliveryResponses, ReplayEmailWebhooksData, ReplayEmailWebhooksErrors, ReplayEmailWebhooksResponses, ReplyToEmailData, ReplyToEmailErrors, ReplyToEmailResponses, ResendAgentSignupVerificationData, ResendAgentSignupVerificationErrors, ResendAgentSignupVerificationResponses, ResendCliSignupVerificationData, ResendCliSignupVerificationErrors, ResendCliSignupVerificationResponses, ResolveRegistryHandleData, ResolveRegistryHandleErrors, ResolveRegistryHandleResponses, RotateWebhookSecretData, RotateWebhookSecretErrors, RotateWebhookSecretResponses, RunWakeScheduleData, RunWakeScheduleErrors, RunWakeScheduleResponses, SearchEmailsData, SearchEmailsErrors, SearchEmailsResponses, SearchMemoriesData, SearchMemoriesErrors, SearchMemoriesResponses, SemanticSearchData, SemanticSearchErrors, SemanticSearchResponses, SendEmailData, SendEmailErrors, SendEmailResponses, SetFunctionRouteData, SetFunctionRouteErrors, SetFunctionRouteResponses, SetFunctionSecretData, SetFunctionSecretErrors, SetFunctionSecretResponses, SetMemoryData, SetMemoryErrors, SetMemoryResponses, SetOrgSecretData, SetOrgSecretErrors, SetOrgSecretResponses, SimulateRouteData, SimulateRouteErrors, SimulateRouteResponses, StartAgentClaimData, StartAgentClaimErrors, StartAgentClaimResponses, StartAgentSignupData, StartAgentSignupErrors, StartAgentSignupResponses, StartCliLoginData, StartCliLoginErrors, StartCliLoginResponses, StartCliSignupData, StartCliSignupErrors, StartCliSignupResponses, TestEndpointData, TestEndpointErrors, TestEndpointResponses, TestFunctionData, TestFunctionErrors, TestFunctionResponses, UnpublishAgentData, UnpublishAgentErrors, UnpublishAgentResponses, UnsetFunctionRouteData, UnsetFunctionRouteErrors, UnsetFunctionRouteResponses, UpdateAccountData, UpdateAccountErrors, UpdateAccountResponses, UpdateDomainData, UpdateDomainErrors, UpdateDomainResponses, UpdateEndpointData, UpdateEndpointErrors, UpdateEndpointResponses, UpdateFilterData, UpdateFilterErrors, UpdateFilterResponses, UpdateFunctionData, UpdateFunctionErrors, UpdateFunctionResponses, UpdateRegistryData, UpdateRegistryErrors, UpdateRegistryResponses, UpdateRouteData, UpdateRouteErrors, UpdateRouteResponses, UpdateSpendPolicyData, UpdateSpendPolicyErrors, UpdateSpendPolicyResponses, UpdateWakeAuthorizationData, UpdateWakeAuthorizationErrors, UpdateWakeAuthorizationResponses, UpdateWakeScheduleData, UpdateWakeScheduleErrors, UpdateWakeScheduleResponses, VerifyAgentClaimData, VerifyAgentClaimErrors, VerifyAgentClaimResponses, VerifyAgentSignupData, VerifyAgentSignupErrors, VerifyAgentSignupResponses, VerifyCliSignupData, VerifyCliSignupErrors, VerifyCliSignupResponses, VerifyDomainData, VerifyDomainErrors, VerifyDomainResponses } from './types.gen.js'; +import type { AddDomainData, AddDomainErrors, AddDomainResponses, AwaitReplyData, AwaitReplyErrors, AwaitReplyResponses, CliLogoutData, CliLogoutErrors, CliLogoutResponses, CreateAgentAccountData, CreateAgentAccountErrors, CreateAgentAccountResponses, CreateAgentClaimLinkData, CreateAgentClaimLinkErrors, CreateAgentClaimLinkResponses, CreateChallengeData, CreateChallengeErrors, CreateChallengeResponses, CreateEmailChallengeData, CreateEmailChallengeErrors, CreateEmailChallengeResponses, CreateEndpointData, CreateEndpointErrors, CreateEndpointResponses, CreateFilterData, CreateFilterErrors, CreateFilterResponses, CreateFunctionData, CreateFunctionErrors, CreateFunctionResponses, CreateFunctionSecretData, CreateFunctionSecretErrors, CreateFunctionSecretResponses, CreateOrgSecretData, CreateOrgSecretErrors, CreateOrgSecretResponses, CreateRegistryData, CreateRegistryErrors, CreateRegistryResponses, CreateRouteData, CreateRouteErrors, CreateRouteResponses, CreateWakeAuthorizationData, CreateWakeAuthorizationErrors, CreateWakeAuthorizationResponses, CreateWakeScheduleData, CreateWakeScheduleErrors, CreateWakeScheduleResponses, DecideRegistryRequestData, DecideRegistryRequestErrors, DecideRegistryRequestResponses, DefineAgentData, DefineAgentErrors, DefineAgentResponses, DeleteDomainData, DeleteDomainErrors, DeleteDomainResponses, DeleteEmailData, DeleteEmailErrors, DeleteEmailResponses, DeleteEndpointData, DeleteEndpointErrors, DeleteEndpointResponses, DeleteFilterData, DeleteFilterErrors, DeleteFilterResponses, DeleteFunctionData, DeleteFunctionErrors, DeleteFunctionResponses, DeleteFunctionSecretData, DeleteFunctionSecretErrors, DeleteFunctionSecretResponses, DeleteMemoryData, DeleteMemoryErrors, DeleteMemoryResponses, DeleteOrgSecretData, DeleteOrgSecretErrors, DeleteOrgSecretResponses, DeleteRegistryData, DeleteRegistryErrors, DeleteRegistryResponses, DeleteRouteData, DeleteRouteErrors, DeleteRouteResponses, DeleteWakeAuthorizationData, DeleteWakeAuthorizationErrors, DeleteWakeAuthorizationResponses, DeleteWakeScheduleData, DeleteWakeScheduleErrors, DeleteWakeScheduleResponses, DiscardEmailContentData, DiscardEmailContentErrors, DiscardEmailContentResponses, DownloadAttachmentsData, DownloadAttachmentsErrors, DownloadAttachmentsResponses, DownloadDomainZoneFileData, DownloadDomainZoneFileErrors, DownloadDomainZoneFileResponses, DownloadRawEmailData, DownloadRawEmailErrors, DownloadRawEmailResponses, GetAccountData, GetAccountErrors, GetAccountResponses, GetAgentData, GetAgentErrors, GetAgentResponses, GetChallengeData, GetChallengeErrors, GetChallengeResponses, GetConversationData, GetConversationErrors, GetConversationResponses, GetEmailData, GetEmailErrors, GetEmailResponses, GetFunctionData, GetFunctionErrors, GetFunctionResponses, GetFunctionRoutingData, GetFunctionRoutingErrors, GetFunctionRoutingResponses, GetFunctionTestRunTraceData, GetFunctionTestRunTraceErrors, GetFunctionTestRunTraceResponses, GetInboxStatusData, GetInboxStatusErrors, GetInboxStatusResponses, GetMemoryData, GetMemoryErrors, GetMemoryResponses, GetOrgRoutingTopologyData, GetOrgRoutingTopologyErrors, GetOrgRoutingTopologyResponses, GetRegistryData, GetRegistryErrors, GetRegistryResponses, GetSendPermissionsData, GetSendPermissionsErrors, GetSendPermissionsResponses, GetSentEmailData, GetSentEmailErrors, GetSentEmailResponses, GetSpendPolicyData, GetSpendPolicyErrors, GetSpendPolicyResponses, GetStorageStatsData, GetStorageStatsErrors, GetStorageStatsResponses, GetTemplateData, GetTemplateErrors, GetTemplateInstallData, GetTemplateInstallErrors, GetTemplateInstallResponses, GetTemplateResponses, GetThreadData, GetThreadErrors, GetThreadResponses, GetWakeScheduleData, GetWakeScheduleErrors, GetWakeScheduleResponses, GetWebhookSecretData, GetWebhookSecretErrors, GetWebhookSecretResponses, InstallTemplateData, InstallTemplateErrors, InstallTemplateResponses, ListDeclinedPaymentsData, ListDeclinedPaymentsErrors, ListDeclinedPaymentsResponses, ListDeliveriesData, ListDeliveriesErrors, ListDeliveriesResponses, ListDomainsData, ListDomainsErrors, ListDomainsResponses, ListEmailsData, ListEmailsErrors, ListEmailsResponses, ListEndpointsData, ListEndpointsErrors, ListEndpointsResponses, ListFiltersData, ListFiltersErrors, ListFiltersResponses, ListFunctionLogsData, ListFunctionLogsErrors, ListFunctionLogsResponses, ListFunctionsData, ListFunctionSecretsData, ListFunctionSecretsErrors, ListFunctionSecretsResponses, ListFunctionsErrors, ListFunctionsResponses, ListOrgSecretsData, ListOrgSecretsErrors, ListOrgSecretsResponses, ListPayoutAddressesData, ListPayoutAddressesErrors, ListPayoutAddressesResponses, ListRegistriesData, ListRegistriesErrors, ListRegistriesResponses, ListRegistryAgentsData, ListRegistryAgentsResponses, ListRegistryRequestsData, ListRegistryRequestsErrors, ListRegistryRequestsResponses, ListRoutesData, ListRoutesErrors, ListRoutesResponses, ListSentEmailsData, ListSentEmailsErrors, ListSentEmailsResponses, ListTemplatesData, ListTemplatesErrors, ListTemplatesResponses, ListWakeAuthorizationsData, ListWakeAuthorizationsErrors, ListWakeAuthorizationsResponses, ListWakeDispatchesData, ListWakeDispatchesErrors, ListWakeDispatchesResponses, ListWakeSchedulesData, ListWakeSchedulesErrors, ListWakeSchedulesResponses, PayChallengeData, PayChallengeErrors, PayChallengeResponses, PollCliLoginData, PollCliLoginErrors, PollCliLoginResponses, PublishAgentData, PublishAgentErrors, PublishAgentResponses, RegisterPayoutAddressData, RegisterPayoutAddressErrors, RegisterPayoutAddressResponses, ReorderRoutesData, ReorderRoutesErrors, ReorderRoutesResponses, ReplayDeliveryData, ReplayDeliveryErrors, ReplayDeliveryResponses, ReplayEmailWebhooksData, ReplayEmailWebhooksErrors, ReplayEmailWebhooksResponses, ReplyToEmailData, ReplyToEmailErrors, ReplyToEmailResponses, ResendAgentSignupVerificationData, ResendAgentSignupVerificationErrors, ResendAgentSignupVerificationResponses, ResendCliSignupVerificationData, ResendCliSignupVerificationErrors, ResendCliSignupVerificationResponses, ResolveRegistryHandleData, ResolveRegistryHandleErrors, ResolveRegistryHandleResponses, RotateWebhookSecretData, RotateWebhookSecretErrors, RotateWebhookSecretResponses, RunWakeScheduleData, RunWakeScheduleErrors, RunWakeScheduleResponses, SearchEmailsData, SearchEmailsErrors, SearchEmailsResponses, SearchMemoriesData, SearchMemoriesErrors, SearchMemoriesResponses, SemanticSearchData, SemanticSearchErrors, SemanticSearchResponses, SendEmailData, SendEmailErrors, SendEmailResponses, SetFunctionRouteData, SetFunctionRouteErrors, SetFunctionRouteResponses, SetFunctionSecretData, SetFunctionSecretErrors, SetFunctionSecretResponses, SetMemoryData, SetMemoryErrors, SetMemoryResponses, SetOrgSecretData, SetOrgSecretErrors, SetOrgSecretResponses, SimulateRouteData, SimulateRouteErrors, SimulateRouteResponses, StartAgentClaimData, StartAgentClaimErrors, StartAgentClaimResponses, StartAgentSignupData, StartAgentSignupErrors, StartAgentSignupResponses, StartCliLoginData, StartCliLoginErrors, StartCliLoginResponses, StartCliSignupData, StartCliSignupErrors, StartCliSignupResponses, TestEndpointData, TestEndpointErrors, TestEndpointResponses, TestFunctionData, TestFunctionErrors, TestFunctionResponses, UnpublishAgentData, UnpublishAgentErrors, UnpublishAgentResponses, UnsetFunctionRouteData, UnsetFunctionRouteErrors, UnsetFunctionRouteResponses, UpdateAccountData, UpdateAccountErrors, UpdateAccountResponses, UpdateDomainData, UpdateDomainErrors, UpdateDomainResponses, UpdateEndpointData, UpdateEndpointErrors, UpdateEndpointResponses, UpdateFilterData, UpdateFilterErrors, UpdateFilterResponses, UpdateFunctionData, UpdateFunctionErrors, UpdateFunctionResponses, UpdateRegistryData, UpdateRegistryErrors, UpdateRegistryResponses, UpdateRouteData, UpdateRouteErrors, UpdateRouteResponses, UpdateSpendPolicyData, UpdateSpendPolicyErrors, UpdateSpendPolicyResponses, UpdateWakeAuthorizationData, UpdateWakeAuthorizationErrors, UpdateWakeAuthorizationResponses, UpdateWakeScheduleData, UpdateWakeScheduleErrors, UpdateWakeScheduleResponses, VerifyAgentClaimData, VerifyAgentClaimErrors, VerifyAgentClaimResponses, VerifyAgentSignupData, VerifyAgentSignupErrors, VerifyAgentSignupResponses, VerifyCliSignupData, VerifyCliSignupErrors, VerifyCliSignupResponses, VerifyDomainData, VerifyDomainErrors, VerifyDomainResponses } from './types.gen.js'; export type Options = Options2 & { /** @@ -1224,6 +1224,33 @@ export const getSentEmail = (options: Opti ...options }); +/** + * Get (or wait for) the reply to a sent email + * + * Returns the first threaded inbound reply to a send, keyed by + * the inbound email's `reply_to_sent_email_id`. This is the + * canonical "did a reply arrive for this send?" call: after + * /send-mail, poll (or long-poll) here instead of hand-rolling + * an /emails/search loop. + * + * By default the call returns immediately with `reply: null` + * when nothing has arrived yet. Set `wait=true` to long-poll: + * the server holds the request up to `wait_timeout_ms` + * (default 10000, max 30000), returning as soon as the first + * reply lands. On a wait that elapses with no reply, the + * response has `reply: null` and `timed_out: true`. + * + * The reply object is compact (headers plus bodies); fetch + * `/emails/{id}` with `reply.id` for the fully parsed record, + * or `/threads/{thread_id}` for the whole back-and-forth. + * + */ +export const awaitReply = (options: Options) => (options.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/sent-emails/{id}/reply', + ...options +}); + /** * Get a conversation thread by id * diff --git a/packages/api-core/src/api/types.gen.ts b/packages/api-core/src/api/types.gen.ts index 7cd16ddc..0b1cc3ed 100644 --- a/packages/api-core/src/api/types.gen.ts +++ b/packages/api-core/src/api/types.gen.ts @@ -2405,6 +2405,75 @@ export type SentEmailDetail = SentEmailSummary & { body_html?: string | null; }; +/** + * A threaded inbound reply to one of the org's sends, keyed by + * the inbound email's `reply_to_sent_email_id`. Compact on + * purpose: enough to read the reply and decide what to do + * next, with `/emails/{id}` available for the fully parsed + * detail. + * + */ +export type ReplyEmail = { + /** + * Inbound email row id; usable with `/emails/{id}`. + */ + id: string; + /** + * Conversation thread id; usable with `/threads/{id}`. + */ + thread_id: string | null; + /** + * The sent-email id this inbound message replied to. + */ + reply_to_sent_email_id: string | null; + /** + * Sender of the reply (From header when present, else envelope sender). + */ + from_email: string; + /** + * Recipient address the reply arrived at. + */ + to_email: string; + subject: string | null; + /** + * Plain-text body of the reply, when present. + */ + body_text: string | null; + /** + * HTML body of the reply, when present. + */ + body_html: string | null; + received_at: string | null; + /** + * Inbound processing status of the reply row. Typically an + * `EmailStatus` value, but left as an open string here to + * match the server contract, which does not narrow it. + * + */ + status: string; +}; + +/** + * Result of `/sent-emails/{id}/reply`. `reply` is null when no + * reply has arrived yet (no-wait call, or the wait timed out). + * + */ +export type AwaitReplyResult = { + /** + * The send this lookup was keyed on (echoes the path id). + */ + sent_email_id: string; + reply: ReplyEmail | null; + /** + * Whether the call ran in long-poll mode (`wait=true`). + */ + waited: boolean; + /** + * True only when a `wait=true` call elapsed its timeout with no reply. + */ + timed_out: boolean; +}; + /** * Body shape for `/emails/{id}/reply`. Intentionally narrow: * recipients (`to`), subject, and threading headers @@ -6780,6 +6849,66 @@ export type GetSentEmailResponses = { export type GetSentEmailResponse = GetSentEmailResponses[keyof GetSentEmailResponses]; +export type AwaitReplyData = { + body?: never; + path: { + /** + * Resource UUID + */ + id: string; + }; + query?: { + /** + * When true, long-poll up to `wait_timeout_ms` for the + * first reply to arrive instead of returning immediately. + * Mirrors send-mail's server-side `wait` semantics. The + * server accepts only the literal strings `true` / `false` + * (standard boolean query serialization); omitted means + * no wait. + * + */ + wait?: boolean; + /** + * Maximum time to hold the request when `wait=true`. + * Server default is 10000. NOT given an OpenAPI `default` + * on purpose, matching the `/emails` `wait` parameter: a + * default makes some generators send the value on every + * call. + * + */ + wait_timeout_ms?: number; + }; + url: '/sent-emails/{id}/reply'; +}; + +export type AwaitReplyErrors = { + /** + * Invalid request parameters + */ + 400: ErrorResponse; + /** + * Invalid or missing API key + */ + 401: ErrorResponse; + /** + * Resource not found + */ + 404: ErrorResponse; +}; + +export type AwaitReplyError = AwaitReplyErrors[keyof AwaitReplyErrors]; + +export type AwaitReplyResponses = { + /** + * Reply status for the send (reply may be null) + */ + 200: SuccessEnvelope & { + data?: AwaitReplyResult; + }; +}; + +export type AwaitReplyResponse = AwaitReplyResponses[keyof AwaitReplyResponses]; + export type GetThreadData = { body?: never; path: { diff --git a/packages/api-core/src/openapi/openapi.generated.ts b/packages/api-core/src/openapi/openapi.generated.ts index bcd99ff4..ed88e805 100644 --- a/packages/api-core/src/openapi/openapi.generated.ts +++ b/packages/api-core/src/openapi/openapi.generated.ts @@ -4121,6 +4121,72 @@ export const openapiDocument: Record = { } } }, + "/sent-emails/{id}/reply": { + "get": { + "operationId": "awaitReply", + "summary": "Get (or wait for) the reply to a sent email", + "description": "Returns the first threaded inbound reply to a send, keyed by\nthe inbound email's `reply_to_sent_email_id`. This is the\ncanonical \"did a reply arrive for this send?\" call: after\n/send-mail, poll (or long-poll) here instead of hand-rolling\nan /emails/search loop.\n\nBy default the call returns immediately with `reply: null`\nwhen nothing has arrived yet. Set `wait=true` to long-poll:\nthe server holds the request up to `wait_timeout_ms`\n(default 10000, max 30000), returning as soon as the first\nreply lands. On a wait that elapses with no reply, the\nresponse has `reply: null` and `timed_out: true`.\n\nThe reply object is compact (headers plus bodies); fetch\n`/emails/{id}` with `reply.id` for the fully parsed record,\nor `/threads/{thread_id}` for the whole back-and-forth.\n", + "tags": [ + "Sending" + ], + "parameters": [ + { + "$ref": "#/components/parameters/ResourceId" + }, + { + "name": "wait", + "in": "query", + "schema": { + "type": "boolean" + }, + "description": "When true, long-poll up to `wait_timeout_ms` for the\nfirst reply to arrive instead of returning immediately.\nMirrors send-mail's server-side `wait` semantics. The\nserver accepts only the literal strings `true` / `false`\n(standard boolean query serialization); omitted means\nno wait.\n" + }, + { + "name": "wait_timeout_ms", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1000, + "maximum": 30000 + }, + "description": "Maximum time to hold the request when `wait=true`.\nServer default is 10000. NOT given an OpenAPI `default`\non purpose, matching the `/emails` `wait` parameter: a\ndefault makes some generators send the value on every\ncall.\n" + } + ], + "responses": { + "200": { + "description": "Reply status for the send (reply may be null)", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/AwaitReplyResult" + } + } + } + ] + } + } + } + }, + "400": { + "$ref": "#/components/responses/ValidationError" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound" + } + } + } + }, "/threads/{id}": { "parameters": [ { @@ -12213,6 +12279,118 @@ export const openapiDocument: Record = { } ] }, + "ReplyEmail": { + "type": "object", + "description": "A threaded inbound reply to one of the org's sends, keyed by\nthe inbound email's `reply_to_sent_email_id`. Compact on\npurpose: enough to read the reply and decide what to do\nnext, with `/emails/{id}` available for the fully parsed\ndetail.\n", + "properties": { + "id": { + "type": "string", + "format": "uuid", + "description": "Inbound email row id; usable with `/emails/{id}`." + }, + "thread_id": { + "type": [ + "string", + "null" + ], + "format": "uuid", + "description": "Conversation thread id; usable with `/threads/{id}`." + }, + "reply_to_sent_email_id": { + "type": [ + "string", + "null" + ], + "format": "uuid", + "description": "The sent-email id this inbound message replied to." + }, + "from_email": { + "type": "string", + "description": "Sender of the reply (From header when present, else envelope sender)." + }, + "to_email": { + "type": "string", + "description": "Recipient address the reply arrived at." + }, + "subject": { + "type": [ + "string", + "null" + ] + }, + "body_text": { + "type": [ + "string", + "null" + ], + "description": "Plain-text body of the reply, when present." + }, + "body_html": { + "type": [ + "string", + "null" + ], + "description": "HTML body of the reply, when present." + }, + "received_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Inbound processing status of the reply row. Typically an\n`EmailStatus` value, but left as an open string here to\nmatch the server contract, which does not narrow it.\n" + } + }, + "required": [ + "id", + "thread_id", + "reply_to_sent_email_id", + "from_email", + "to_email", + "subject", + "body_text", + "body_html", + "received_at", + "status" + ] + }, + "AwaitReplyResult": { + "type": "object", + "description": "Result of `/sent-emails/{id}/reply`. `reply` is null when no\nreply has arrived yet (no-wait call, or the wait timed out).\n", + "properties": { + "sent_email_id": { + "type": "string", + "format": "uuid", + "description": "The send this lookup was keyed on (echoes the path id)." + }, + "reply": { + "allOf": [ + { + "$ref": "#/components/schemas/ReplyEmail" + } + ], + "nullable": true + }, + "waited": { + "type": "boolean", + "description": "Whether the call ran in long-poll mode (`wait=true`)." + }, + "timed_out": { + "type": "boolean", + "description": "True only when a `wait=true` call elapsed its timeout with no reply." + } + }, + "required": [ + "sent_email_id", + "reply", + "waited", + "timed_out" + ], + "additionalProperties": false + }, "ReplyInput": { "type": "object", "additionalProperties": false, diff --git a/packages/api-core/src/openapi/operations.generated.ts b/packages/api-core/src/openapi/operations.generated.ts index 40b0e979..c32b944b 100644 --- a/packages/api-core/src/openapi/operations.generated.ts +++ b/packages/api-core/src/openapi/operations.generated.ts @@ -10538,6 +10538,157 @@ export const operationManifest: PrimitiveOperationManifest[] = [ "tag": "Search", "tagCommand": "search" }, + { + "binaryResponse": false, + "bodyRequired": false, + "command": "await-reply", + "description": "Returns the first threaded inbound reply to a send, keyed by\nthe inbound email's `reply_to_sent_email_id`. This is the\ncanonical \"did a reply arrive for this send?\" call: after\n/send-mail, poll (or long-poll) here instead of hand-rolling\nan /emails/search loop.\n\nBy default the call returns immediately with `reply: null`\nwhen nothing has arrived yet. Set `wait=true` to long-poll:\nthe server holds the request up to `wait_timeout_ms`\n(default 10000, max 30000), returning as soon as the first\nreply lands. On a wait that elapses with no reply, the\nresponse has `reply: null` and `timed_out: true`.\n\nThe reply object is compact (headers plus bodies); fetch\n`/emails/{id}` with `reply.id` for the fully parsed record,\nor `/threads/{thread_id}` for the whole back-and-forth.\n", + "hasJsonBody": false, + "method": "GET", + "operationId": "awaitReply", + "path": "/sent-emails/{id}/reply", + "pathParams": [ + { + "description": "Resource UUID", + "enum": null, + "name": "id", + "required": true, + "type": "string" + } + ], + "queryParams": [ + { + "description": "When true, long-poll up to `wait_timeout_ms` for the\nfirst reply to arrive instead of returning immediately.\nMirrors send-mail's server-side `wait` semantics. The\nserver accepts only the literal strings `true` / `false`\n(standard boolean query serialization); omitted means\nno wait.\n", + "enum": null, + "name": "wait", + "required": false, + "type": "boolean" + }, + { + "description": "Maximum time to hold the request when `wait=true`.\nServer default is 10000. NOT given an OpenAPI `default`\non purpose, matching the `/emails` `wait` parameter: a\ndefault makes some generators send the value on every\ncall.\n", + "enum": null, + "maximum": 30000, + "minimum": 1000, + "name": "wait_timeout_ms", + "required": false, + "type": "integer" + } + ], + "requestSchema": null, + "responseSchema": { + "type": "object", + "description": "Result of `/sent-emails/{id}/reply`. `reply` is null when no\nreply has arrived yet (no-wait call, or the wait timed out).\n", + "properties": { + "sent_email_id": { + "type": "string", + "format": "uuid", + "description": "The send this lookup was keyed on (echoes the path id)." + }, + "reply": { + "allOf": [ + { + "type": "object", + "description": "A threaded inbound reply to one of the org's sends, keyed by\nthe inbound email's `reply_to_sent_email_id`. Compact on\npurpose: enough to read the reply and decide what to do\nnext, with `/emails/{id}` available for the fully parsed\ndetail.\n", + "properties": { + "id": { + "type": "string", + "format": "uuid", + "description": "Inbound email row id; usable with `/emails/{id}`." + }, + "thread_id": { + "type": [ + "string", + "null" + ], + "format": "uuid", + "description": "Conversation thread id; usable with `/threads/{id}`." + }, + "reply_to_sent_email_id": { + "type": [ + "string", + "null" + ], + "format": "uuid", + "description": "The sent-email id this inbound message replied to." + }, + "from_email": { + "type": "string", + "description": "Sender of the reply (From header when present, else envelope sender)." + }, + "to_email": { + "type": "string", + "description": "Recipient address the reply arrived at." + }, + "subject": { + "type": [ + "string", + "null" + ] + }, + "body_text": { + "type": [ + "string", + "null" + ], + "description": "Plain-text body of the reply, when present." + }, + "body_html": { + "type": [ + "string", + "null" + ], + "description": "HTML body of the reply, when present." + }, + "received_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Inbound processing status of the reply row. Typically an\n`EmailStatus` value, but left as an open string here to\nmatch the server contract, which does not narrow it.\n" + } + }, + "required": [ + "id", + "thread_id", + "reply_to_sent_email_id", + "from_email", + "to_email", + "subject", + "body_text", + "body_html", + "received_at", + "status" + ] + } + ], + "nullable": true + }, + "waited": { + "type": "boolean", + "description": "Whether the call ran in long-poll mode (`wait=true`)." + }, + "timed_out": { + "type": "boolean", + "description": "True only when a `wait=true` call elapsed its timeout with no reply." + } + }, + "required": [ + "sent_email_id", + "reply", + "waited", + "timed_out" + ], + "additionalProperties": false + }, + "sdkName": "awaitReply", + "summary": "Get (or wait for) the reply to a sent email", + "tag": "Sending", + "tagCommand": "sending" + }, { "binaryResponse": false, "bodyRequired": false, diff --git a/sdk-go/api/oas_client_gen.go b/sdk-go/api/oas_client_gen.go index 1510ab71..0dec6f4b 100644 --- a/sdk-go/api/oas_client_gen.go +++ b/sdk-go/api/oas_client_gen.go @@ -38,6 +38,25 @@ type Invoker interface { // // POST /domains AddDomain(ctx context.Context, request *AddDomainInput) (AddDomainRes, error) + // AwaitReply invokes awaitReply operation. + // + // Returns the first threaded inbound reply to a send, keyed by + // the inbound email's `reply_to_sent_email_id`. This is the + // canonical "did a reply arrive for this send?" call: after + // /send-mail, poll (or long-poll) here instead of hand-rolling + // an /emails/search loop. + // By default the call returns immediately with `reply: null` + // when nothing has arrived yet. Set `wait=true` to long-poll: + // the server holds the request up to `wait_timeout_ms` + // (default 10000, max 30000), returning as soon as the first + // reply lands. On a wait that elapses with no reply, the + // response has `reply: null` and `timed_out: true`. + // The reply object is compact (headers plus bodies); fetch + // `/emails/{id}` with `reply.id` for the fully parsed record, + // or `/threads/{thread_id}` for the whole back-and-forth. + // + // GET /sent-emails/{id}/reply + AwaitReply(ctx context.Context, params AwaitReplyParams) (AwaitReplyRes, error) // CliLogout invokes cliLogout operation. // // Revokes the OAuth grant used to authenticate the request. API-key @@ -1390,6 +1409,183 @@ func (c *Client) sendAddDomain(ctx context.Context, request *AddDomainInput) (re return result, nil } +// AwaitReply invokes awaitReply operation. +// +// Returns the first threaded inbound reply to a send, keyed by +// the inbound email's `reply_to_sent_email_id`. This is the +// canonical "did a reply arrive for this send?" call: after +// /send-mail, poll (or long-poll) here instead of hand-rolling +// an /emails/search loop. +// By default the call returns immediately with `reply: null` +// when nothing has arrived yet. Set `wait=true` to long-poll: +// the server holds the request up to `wait_timeout_ms` +// (default 10000, max 30000), returning as soon as the first +// reply lands. On a wait that elapses with no reply, the +// response has `reply: null` and `timed_out: true`. +// The reply object is compact (headers plus bodies); fetch +// `/emails/{id}` with `reply.id` for the fully parsed record, +// or `/threads/{thread_id}` for the whole back-and-forth. +// +// GET /sent-emails/{id}/reply +func (c *Client) AwaitReply(ctx context.Context, params AwaitReplyParams) (AwaitReplyRes, error) { + res, err := c.sendAwaitReply(ctx, params) + return res, err +} + +func (c *Client) sendAwaitReply(ctx context.Context, params AwaitReplyParams) (res AwaitReplyRes, err error) { + otelAttrs := []attribute.KeyValue{ + otelogen.OperationID("awaitReply"), + semconv.HTTPRequestMethodKey.String("GET"), + semconv.URLTemplateKey.String("/sent-emails/{id}/reply"), + } + otelAttrs = append(otelAttrs, c.cfg.Attributes...) + + // Run stopwatch. + startTime := time.Now() + defer func() { + // Use floating point division here for higher precision (instead of Millisecond method). + elapsedDuration := time.Since(startTime) + c.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), metric.WithAttributes(otelAttrs...)) + }() + + // Increment request counter. + c.requests.Add(ctx, 1, metric.WithAttributes(otelAttrs...)) + + // Start a span for this request. + ctx, span := c.cfg.Tracer.Start(ctx, AwaitReplyOperation, + trace.WithAttributes(otelAttrs...), + clientSpanKind, + ) + // Track stage for error reporting. + var stage string + defer func() { + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, stage) + c.errors.Add(ctx, 1, metric.WithAttributes(otelAttrs...)) + } + span.End() + }() + + stage = "BuildURL" + u := uri.Clone(c.requestURL(ctx)) + var pathParts [3]string + pathParts[0] = "/sent-emails/" + { + // Encode "id" parameter. + e := uri.NewPathEncoder(uri.PathEncoderConfig{ + Param: "id", + Style: uri.PathStyleSimple, + Explode: false, + }) + if err := func() error { + return e.EncodeValue(conv.UUIDToString(params.ID)) + }(); err != nil { + return res, errors.Wrap(err, "encode path") + } + encoded, err := e.Result() + if err != nil { + return res, errors.Wrap(err, "encode path") + } + pathParts[1] = encoded + } + pathParts[2] = "/reply" + uri.AddPathParts(u, pathParts[:]...) + + stage = "EncodeQueryParams" + q := uri.NewQueryEncoder() + { + // Encode "wait" parameter. + cfg := uri.QueryParameterEncodingConfig{ + Name: "wait", + Style: uri.QueryStyleForm, + Explode: true, + } + + if err := q.EncodeParam(cfg, func(e uri.Encoder) error { + if val, ok := params.Wait.Get(); ok { + return e.EncodeValue(conv.BoolToString(val)) + } + return nil + }); err != nil { + return res, errors.Wrap(err, "encode query") + } + } + { + // Encode "wait_timeout_ms" parameter. + cfg := uri.QueryParameterEncodingConfig{ + Name: "wait_timeout_ms", + Style: uri.QueryStyleForm, + Explode: true, + } + + if err := q.EncodeParam(cfg, func(e uri.Encoder) error { + if val, ok := params.WaitTimeoutMs.Get(); ok { + return e.EncodeValue(conv.IntToString(val)) + } + return nil + }); err != nil { + return res, errors.Wrap(err, "encode query") + } + } + u.RawQuery = q.Values().Encode() + + stage = "EncodeRequest" + r, err := ht.NewRequest(ctx, "GET", u) + if err != nil { + return res, errors.Wrap(err, "create request") + } + + { + type bitset = [1]uint8 + var satisfied bitset + { + stage = "Security:BearerAuth" + switch err := c.securityBearerAuth(ctx, AwaitReplyOperation, r); { + case err == nil: // if NO error + satisfied[0] |= 1 << 0 + case errors.Is(err, ogenerrors.ErrSkipClientSecurity): + // Skip this security. + default: + return res, errors.Wrap(err, "security \"BearerAuth\"") + } + } + + if ok := func() bool { + nextRequirement: + for _, requirement := range []bitset{ + {0b00000001}, + } { + for i, mask := range requirement { + if satisfied[i]&mask != mask { + continue nextRequirement + } + } + return true + } + return false + }(); !ok { + return res, ogenerrors.ErrSecurityRequirementIsNotSatisfied + } + } + + stage = "SendRequest" + resp, err := c.cfg.Client.Do(r) + if err != nil { + return res, errors.Wrap(err, "do request") + } + body := resp.Body + defer body.Close() + + stage = "DecodeResponse" + result, err := decodeAwaitReplyResponse(resp) + if err != nil { + return res, errors.Wrap(err, "decode response") + } + + return result, nil +} + // CliLogout invokes cliLogout operation. // // Revokes the OAuth grant used to authenticate the request. API-key diff --git a/sdk-go/api/oas_handlers_gen.go b/sdk-go/api/oas_handlers_gen.go index 7559ba1a..2776749a 100644 --- a/sdk-go/api/oas_handlers_gen.go +++ b/sdk-go/api/oas_handlers_gen.go @@ -224,6 +224,214 @@ func (s *Server) handleAddDomainRequest(args [0]string, argsEscaped bool, w http } } +// handleAwaitReplyRequest handles awaitReply operation. +// +// Returns the first threaded inbound reply to a send, keyed by +// the inbound email's `reply_to_sent_email_id`. This is the +// canonical "did a reply arrive for this send?" call: after +// /send-mail, poll (or long-poll) here instead of hand-rolling +// an /emails/search loop. +// By default the call returns immediately with `reply: null` +// when nothing has arrived yet. Set `wait=true` to long-poll: +// the server holds the request up to `wait_timeout_ms` +// (default 10000, max 30000), returning as soon as the first +// reply lands. On a wait that elapses with no reply, the +// response has `reply: null` and `timed_out: true`. +// The reply object is compact (headers plus bodies); fetch +// `/emails/{id}` with `reply.id` for the fully parsed record, +// or `/threads/{thread_id}` for the whole back-and-forth. +// +// GET /sent-emails/{id}/reply +func (s *Server) handleAwaitReplyRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter + otelAttrs := []attribute.KeyValue{ + otelogen.OperationID("awaitReply"), + semconv.HTTPRequestMethodKey.String("GET"), + semconv.HTTPRouteKey.String("/sent-emails/{id}/reply"), + } + // Add attributes from config. + otelAttrs = append(otelAttrs, s.cfg.Attributes...) + + // Start a span for this request. + ctx, span := s.cfg.Tracer.Start(r.Context(), AwaitReplyOperation, + trace.WithAttributes(otelAttrs...), + serverSpanKind, + ) + defer span.End() + + // Add Labeler to context. + labeler := &Labeler{attrs: otelAttrs} + ctx = contextWithLabeler(ctx, labeler) + + // Run stopwatch. + startTime := time.Now() + defer func() { + elapsedDuration := time.Since(startTime) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) + + // Increment request counter. + s.requests.Add(ctx, 1, attrOpt) + + // Use floating point division here for higher precision (instead of Millisecond method). + s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) + }() + + var ( + recordError = func(stage string, err error) { + span.RecordError(err) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + code := statusWriter.status + if code < 100 || code >= 500 { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) + } + err error + opErrContext = ogenerrors.OperationContext{ + Name: AwaitReplyOperation, + ID: "awaitReply", + } + ) + { + type bitset = [1]uint8 + var satisfied bitset + { + sctx, ok, err := s.securityBearerAuth(ctx, AwaitReplyOperation, r) + if err != nil { + err = &ogenerrors.SecurityError{ + OperationContext: opErrContext, + Security: "BearerAuth", + Err: err, + } + defer recordError("Security:BearerAuth", err) + s.cfg.ErrorHandler(ctx, w, r, err) + return + } + if ok { + satisfied[0] |= 1 << 0 + ctx = sctx + } + } + + if ok := func() bool { + nextRequirement: + for _, requirement := range []bitset{ + {0b00000001}, + } { + for i, mask := range requirement { + if satisfied[i]&mask != mask { + continue nextRequirement + } + } + return true + } + return false + }(); !ok { + err = &ogenerrors.SecurityError{ + OperationContext: opErrContext, + Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied, + } + defer recordError("Security", err) + s.cfg.ErrorHandler(ctx, w, r, err) + return + } + } + params, err := decodeAwaitReplyParams(args, argsEscaped, r) + if err != nil { + err = &ogenerrors.DecodeParamsError{ + OperationContext: opErrContext, + Err: err, + } + defer recordError("DecodeParams", err) + s.cfg.ErrorHandler(ctx, w, r, err) + return + } + + var rawBody []byte + + var response AwaitReplyRes + if m := s.cfg.Middleware; m != nil { + mreq := middleware.Request{ + Context: ctx, + OperationName: AwaitReplyOperation, + OperationSummary: "Get (or wait for) the reply to a sent email", + OperationID: "awaitReply", + Body: nil, + RawBody: rawBody, + Params: middleware.Parameters{ + { + Name: "id", + In: "path", + }: params.ID, + { + Name: "wait", + In: "query", + }: params.Wait, + { + Name: "wait_timeout_ms", + In: "query", + }: params.WaitTimeoutMs, + }, + Raw: r, + } + + type ( + Request = struct{} + Params = AwaitReplyParams + Response = AwaitReplyRes + ) + response, err = middleware.HookMiddleware[ + Request, + Params, + Response, + ]( + m, + mreq, + unpackAwaitReplyParams, + func(ctx context.Context, request Request, params Params) (response Response, err error) { + response, err = s.h.AwaitReply(ctx, params) + return response, err + }, + ) + } else { + response, err = s.h.AwaitReply(ctx, params) + } + if err != nil { + defer recordError("Internal", err) + s.cfg.ErrorHandler(ctx, w, r, err) + return + } + + if err := encodeAwaitReplyResponse(response, w, span); err != nil { + defer recordError("EncodeResponse", err) + if !errors.Is(err, ht.ErrInternalServerErrorResponse) { + s.cfg.ErrorHandler(ctx, w, r, err) + } + return + } +} + // handleCliLogoutRequest handles cliLogout operation. // // Revokes the OAuth grant used to authenticate the request. API-key diff --git a/sdk-go/api/oas_interfaces_gen.go b/sdk-go/api/oas_interfaces_gen.go index bcc7e7ef..b628293d 100644 --- a/sdk-go/api/oas_interfaces_gen.go +++ b/sdk-go/api/oas_interfaces_gen.go @@ -5,6 +5,10 @@ type AddDomainRes interface { addDomainRes() } +type AwaitReplyRes interface { + awaitReplyRes() +} + type CliLogoutRes interface { cliLogoutRes() } diff --git a/sdk-go/api/oas_json_gen.go b/sdk-go/api/oas_json_gen.go index bc458595..b1963ae1 100644 --- a/sdk-go/api/oas_json_gen.go +++ b/sdk-go/api/oas_json_gen.go @@ -2495,6 +2495,376 @@ func (s *AgentSignupVerifyResultTokenType) UnmarshalJSON(data []byte) error { return s.Decode(d) } +// Encode encodes AwaitReplyBadRequest as json. +func (s *AwaitReplyBadRequest) Encode(e *jx.Encoder) { + unwrapped := (*ErrorResponse)(s) + + unwrapped.Encode(e) +} + +// Decode decodes AwaitReplyBadRequest from json. +func (s *AwaitReplyBadRequest) Decode(d *jx.Decoder) error { + if s == nil { + return errors.New("invalid: unable to decode AwaitReplyBadRequest to nil") + } + var unwrapped ErrorResponse + if err := func() error { + if err := unwrapped.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "alias") + } + *s = AwaitReplyBadRequest(unwrapped) + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s *AwaitReplyBadRequest) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *AwaitReplyBadRequest) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + +// Encode encodes AwaitReplyNotFound as json. +func (s *AwaitReplyNotFound) Encode(e *jx.Encoder) { + unwrapped := (*ErrorResponse)(s) + + unwrapped.Encode(e) +} + +// Decode decodes AwaitReplyNotFound from json. +func (s *AwaitReplyNotFound) Decode(d *jx.Decoder) error { + if s == nil { + return errors.New("invalid: unable to decode AwaitReplyNotFound to nil") + } + var unwrapped ErrorResponse + if err := func() error { + if err := unwrapped.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "alias") + } + *s = AwaitReplyNotFound(unwrapped) + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s *AwaitReplyNotFound) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *AwaitReplyNotFound) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + +// Encode implements json.Marshaler. +func (s *AwaitReplyOK) Encode(e *jx.Encoder) { + e.ObjStart() + s.encodeFields(e) + e.ObjEnd() +} + +// encodeFields encodes fields. +func (s *AwaitReplyOK) encodeFields(e *jx.Encoder) { + { + e.FieldStart("success") + e.Bool(true) + } + { + e.FieldStart("data") + s.Data.Encode(e) + } +} + +var jsonFieldsNameOfAwaitReplyOK = [2]string{ + 0: "success", + 1: "data", +} + +// Decode decodes AwaitReplyOK from json. +func (s *AwaitReplyOK) Decode(d *jx.Decoder) error { + if s == nil { + return errors.New("invalid: unable to decode AwaitReplyOK to nil") + } + var requiredBitSet [1]uint8 + + if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error { + switch string(k) { + case "success": + requiredBitSet[0] |= 1 << 0 + if err := func() error { + v, err := d.Bool() + s.Success = bool(v) + if err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"success\"") + } + case "data": + requiredBitSet[0] |= 1 << 1 + if err := func() error { + if err := s.Data.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"data\"") + } + default: + return d.Skip() + } + return nil + }); err != nil { + return errors.Wrap(err, "decode AwaitReplyOK") + } + // Validate required fields. + var failures []validate.FieldError + for i, mask := range [1]uint8{ + 0b00000011, + } { + if result := (requiredBitSet[i] & mask) ^ mask; result != 0 { + // Mask only required fields and check equality to mask using XOR. + // + // If XOR result is not zero, result is not equal to expected, so some fields are missed. + // Bits of fields which would be set are actually bits of missed fields. + missed := bits.OnesCount8(result) + for bitN := 0; bitN < missed; bitN++ { + bitIdx := bits.TrailingZeros8(result) + fieldIdx := i*8 + bitIdx + var name string + if fieldIdx < len(jsonFieldsNameOfAwaitReplyOK) { + name = jsonFieldsNameOfAwaitReplyOK[fieldIdx] + } else { + name = strconv.Itoa(fieldIdx) + } + failures = append(failures, validate.FieldError{ + Name: name, + Error: validate.ErrFieldRequired, + }) + // Reset bit. + result &^= 1 << bitIdx + } + } + } + if len(failures) > 0 { + return &validate.Error{Fields: failures} + } + + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s *AwaitReplyOK) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *AwaitReplyOK) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + +// Encode implements json.Marshaler. +func (s *AwaitReplyResult) Encode(e *jx.Encoder) { + e.ObjStart() + s.encodeFields(e) + e.ObjEnd() +} + +// encodeFields encodes fields. +func (s *AwaitReplyResult) encodeFields(e *jx.Encoder) { + { + e.FieldStart("sent_email_id") + json.EncodeUUID(e, s.SentEmailID) + } + { + e.FieldStart("reply") + s.Reply.Encode(e) + } + { + e.FieldStart("waited") + e.Bool(s.Waited) + } + { + e.FieldStart("timed_out") + e.Bool(s.TimedOut) + } +} + +var jsonFieldsNameOfAwaitReplyResult = [4]string{ + 0: "sent_email_id", + 1: "reply", + 2: "waited", + 3: "timed_out", +} + +// Decode decodes AwaitReplyResult from json. +func (s *AwaitReplyResult) Decode(d *jx.Decoder) error { + if s == nil { + return errors.New("invalid: unable to decode AwaitReplyResult to nil") + } + var requiredBitSet [1]uint8 + + if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error { + switch string(k) { + case "sent_email_id": + requiredBitSet[0] |= 1 << 0 + if err := func() error { + v, err := json.DecodeUUID(d) + s.SentEmailID = v + if err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"sent_email_id\"") + } + case "reply": + requiredBitSet[0] |= 1 << 1 + if err := func() error { + if err := s.Reply.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"reply\"") + } + case "waited": + requiredBitSet[0] |= 1 << 2 + if err := func() error { + v, err := d.Bool() + s.Waited = bool(v) + if err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"waited\"") + } + case "timed_out": + requiredBitSet[0] |= 1 << 3 + if err := func() error { + v, err := d.Bool() + s.TimedOut = bool(v) + if err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"timed_out\"") + } + default: + return errors.Errorf("unexpected field %q", k) + } + return nil + }); err != nil { + return errors.Wrap(err, "decode AwaitReplyResult") + } + // Validate required fields. + var failures []validate.FieldError + for i, mask := range [1]uint8{ + 0b00001111, + } { + if result := (requiredBitSet[i] & mask) ^ mask; result != 0 { + // Mask only required fields and check equality to mask using XOR. + // + // If XOR result is not zero, result is not equal to expected, so some fields are missed. + // Bits of fields which would be set are actually bits of missed fields. + missed := bits.OnesCount8(result) + for bitN := 0; bitN < missed; bitN++ { + bitIdx := bits.TrailingZeros8(result) + fieldIdx := i*8 + bitIdx + var name string + if fieldIdx < len(jsonFieldsNameOfAwaitReplyResult) { + name = jsonFieldsNameOfAwaitReplyResult[fieldIdx] + } else { + name = strconv.Itoa(fieldIdx) + } + failures = append(failures, validate.FieldError{ + Name: name, + Error: validate.ErrFieldRequired, + }) + // Reset bit. + result &^= 1 << bitIdx + } + } + } + if len(failures) > 0 { + return &validate.Error{Fields: failures} + } + + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s *AwaitReplyResult) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *AwaitReplyResult) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + +// Encode encodes AwaitReplyUnauthorized as json. +func (s *AwaitReplyUnauthorized) Encode(e *jx.Encoder) { + unwrapped := (*ErrorResponse)(s) + + unwrapped.Encode(e) +} + +// Decode decodes AwaitReplyUnauthorized from json. +func (s *AwaitReplyUnauthorized) Decode(d *jx.Decoder) error { + if s == nil { + return errors.New("invalid: unable to decode AwaitReplyUnauthorized to nil") + } + var unwrapped ErrorResponse + if err := func() error { + if err := unwrapped.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "alias") + } + *s = AwaitReplyUnauthorized(unwrapped) + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s *AwaitReplyUnauthorized) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *AwaitReplyUnauthorized) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + // Encode implements json.Marshaler. func (s *CliLoginPollResult) Encode(e *jx.Encoder) { e.ObjStart() @@ -36041,6 +36411,50 @@ func (s *NilInt) UnmarshalJSON(data []byte) error { return s.Decode(d) } +// Encode encodes ReplyEmail as json. +func (o NilReplyEmail) Encode(e *jx.Encoder) { + if o.Null { + e.Null() + return + } + o.Value.Encode(e) +} + +// Decode decodes ReplyEmail from json. +func (o *NilReplyEmail) Decode(d *jx.Decoder) error { + if o == nil { + return errors.New("invalid: unable to decode NilReplyEmail to nil") + } + if d.Next() == jx.Null { + if err := d.Null(); err != nil { + return err + } + + var v ReplyEmail + o.Value = v + o.Null = true + return nil + } + o.Null = false + if err := o.Value.Decode(d); err != nil { + return err + } + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s NilReplyEmail) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *NilReplyEmail) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + // Encode encodes RoutingTopologyDomainsItemRoutedFunction as json. func (o NilRoutingTopologyDomainsItemRoutedFunction) Encode(e *jx.Encoder) { if o.Null { @@ -43524,6 +43938,244 @@ func (s *ReplayResult) UnmarshalJSON(data []byte) error { return s.Decode(d) } +// Encode implements json.Marshaler. +func (s *ReplyEmail) Encode(e *jx.Encoder) { + e.ObjStart() + s.encodeFields(e) + e.ObjEnd() +} + +// encodeFields encodes fields. +func (s *ReplyEmail) encodeFields(e *jx.Encoder) { + { + e.FieldStart("id") + json.EncodeUUID(e, s.ID) + } + { + e.FieldStart("thread_id") + s.ThreadID.Encode(e) + } + { + e.FieldStart("reply_to_sent_email_id") + s.ReplyToSentEmailID.Encode(e) + } + { + e.FieldStart("from_email") + e.Str(s.FromEmail) + } + { + e.FieldStart("to_email") + e.Str(s.ToEmail) + } + { + e.FieldStart("subject") + s.Subject.Encode(e) + } + { + e.FieldStart("body_text") + s.BodyText.Encode(e) + } + { + e.FieldStart("body_html") + s.BodyHTML.Encode(e) + } + { + e.FieldStart("received_at") + s.ReceivedAt.Encode(e, json.EncodeDateTime) + } + { + e.FieldStart("status") + e.Str(s.Status) + } +} + +var jsonFieldsNameOfReplyEmail = [10]string{ + 0: "id", + 1: "thread_id", + 2: "reply_to_sent_email_id", + 3: "from_email", + 4: "to_email", + 5: "subject", + 6: "body_text", + 7: "body_html", + 8: "received_at", + 9: "status", +} + +// Decode decodes ReplyEmail from json. +func (s *ReplyEmail) Decode(d *jx.Decoder) error { + if s == nil { + return errors.New("invalid: unable to decode ReplyEmail to nil") + } + var requiredBitSet [2]uint8 + + if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error { + switch string(k) { + case "id": + requiredBitSet[0] |= 1 << 0 + if err := func() error { + v, err := json.DecodeUUID(d) + s.ID = v + if err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"id\"") + } + case "thread_id": + requiredBitSet[0] |= 1 << 1 + if err := func() error { + if err := s.ThreadID.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"thread_id\"") + } + case "reply_to_sent_email_id": + requiredBitSet[0] |= 1 << 2 + if err := func() error { + if err := s.ReplyToSentEmailID.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"reply_to_sent_email_id\"") + } + case "from_email": + requiredBitSet[0] |= 1 << 3 + if err := func() error { + v, err := d.Str() + s.FromEmail = string(v) + if err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"from_email\"") + } + case "to_email": + requiredBitSet[0] |= 1 << 4 + if err := func() error { + v, err := d.Str() + s.ToEmail = string(v) + if err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"to_email\"") + } + case "subject": + requiredBitSet[0] |= 1 << 5 + if err := func() error { + if err := s.Subject.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"subject\"") + } + case "body_text": + requiredBitSet[0] |= 1 << 6 + if err := func() error { + if err := s.BodyText.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"body_text\"") + } + case "body_html": + requiredBitSet[0] |= 1 << 7 + if err := func() error { + if err := s.BodyHTML.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"body_html\"") + } + case "received_at": + requiredBitSet[1] |= 1 << 0 + if err := func() error { + if err := s.ReceivedAt.Decode(d, json.DecodeDateTime); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"received_at\"") + } + case "status": + requiredBitSet[1] |= 1 << 1 + if err := func() error { + v, err := d.Str() + s.Status = string(v) + if err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"status\"") + } + default: + return d.Skip() + } + return nil + }); err != nil { + return errors.Wrap(err, "decode ReplyEmail") + } + // Validate required fields. + var failures []validate.FieldError + for i, mask := range [2]uint8{ + 0b11111111, + 0b00000011, + } { + if result := (requiredBitSet[i] & mask) ^ mask; result != 0 { + // Mask only required fields and check equality to mask using XOR. + // + // If XOR result is not zero, result is not equal to expected, so some fields are missed. + // Bits of fields which would be set are actually bits of missed fields. + missed := bits.OnesCount8(result) + for bitN := 0; bitN < missed; bitN++ { + bitIdx := bits.TrailingZeros8(result) + fieldIdx := i*8 + bitIdx + var name string + if fieldIdx < len(jsonFieldsNameOfReplyEmail) { + name = jsonFieldsNameOfReplyEmail[fieldIdx] + } else { + name = strconv.Itoa(fieldIdx) + } + failures = append(failures, validate.FieldError{ + Name: name, + Error: validate.ErrFieldRequired, + }) + // Reset bit. + result &^= 1 << bitIdx + } + } + } + if len(failures) > 0 { + return &validate.Error{Fields: failures} + } + + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s *ReplyEmail) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *ReplyEmail) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + // Encode implements json.Marshaler. func (s *ReplyInput) Encode(e *jx.Encoder) { e.ObjStart() diff --git a/sdk-go/api/oas_operations_gen.go b/sdk-go/api/oas_operations_gen.go index 46e5cd54..96e806bc 100644 --- a/sdk-go/api/oas_operations_gen.go +++ b/sdk-go/api/oas_operations_gen.go @@ -7,6 +7,7 @@ type OperationName = string const ( AddDomainOperation OperationName = "AddDomain" + AwaitReplyOperation OperationName = "AwaitReply" CliLogoutOperation OperationName = "CliLogout" CreateAgentAccountOperation OperationName = "CreateAgentAccount" CreateAgentClaimLinkOperation OperationName = "CreateAgentClaimLink" diff --git a/sdk-go/api/oas_parameters_gen.go b/sdk-go/api/oas_parameters_gen.go index 526c3a1b..5b9d11b6 100644 --- a/sdk-go/api/oas_parameters_gen.go +++ b/sdk-go/api/oas_parameters_gen.go @@ -16,6 +16,211 @@ import ( "github.com/ogen-go/ogen/validate" ) +// AwaitReplyParams is parameters of awaitReply operation. +type AwaitReplyParams struct { + // Resource UUID. + ID uuid.UUID + // When true, long-poll up to `wait_timeout_ms` for the + // first reply to arrive instead of returning immediately. + // Mirrors send-mail's server-side `wait` semantics. The + // server accepts only the literal strings `true` / `false` + // (standard boolean query serialization); omitted means + // no wait. + Wait OptBool `json:",omitempty,omitzero"` + // Maximum time to hold the request when `wait=true`. + // Server default is 10000. NOT given an OpenAPI `default` + // on purpose, matching the `/emails` `wait` parameter: a + // default makes some generators send the value on every + // call. + WaitTimeoutMs OptInt `json:",omitempty,omitzero"` +} + +func unpackAwaitReplyParams(packed middleware.Parameters) (params AwaitReplyParams) { + { + key := middleware.ParameterKey{ + Name: "id", + In: "path", + } + params.ID = packed[key].(uuid.UUID) + } + { + key := middleware.ParameterKey{ + Name: "wait", + In: "query", + } + if v, ok := packed[key]; ok { + params.Wait = v.(OptBool) + } + } + { + key := middleware.ParameterKey{ + Name: "wait_timeout_ms", + In: "query", + } + if v, ok := packed[key]; ok { + params.WaitTimeoutMs = v.(OptInt) + } + } + return params +} + +func decodeAwaitReplyParams(args [1]string, argsEscaped bool, r *http.Request) (params AwaitReplyParams, _ error) { + q := uri.NewQueryDecoder(r.URL.Query()) + // Decode path: id. + if err := func() error { + param := args[0] + if argsEscaped { + unescaped, err := url.PathUnescape(args[0]) + if err != nil { + return errors.Wrap(err, "unescape path") + } + param = unescaped + } + if len(param) > 0 { + d := uri.NewPathDecoder(uri.PathDecoderConfig{ + Param: "id", + Value: param, + Style: uri.PathStyleSimple, + Explode: false, + }) + + if err := func() error { + val, err := d.DecodeValue() + if err != nil { + return err + } + + c, err := conv.ToUUID(val) + if err != nil { + return err + } + + params.ID = c + return nil + }(); err != nil { + return err + } + } else { + return validate.ErrFieldRequired + } + return nil + }(); err != nil { + return params, &ogenerrors.DecodeParamError{ + Name: "id", + In: "path", + Err: err, + } + } + // Decode query: wait. + if err := func() error { + cfg := uri.QueryParameterDecodingConfig{ + Name: "wait", + Style: uri.QueryStyleForm, + Explode: true, + } + + if err := q.HasParam(cfg); err == nil { + if err := q.DecodeParam(cfg, func(d uri.Decoder) error { + var paramsDotWaitVal bool + if err := func() error { + val, err := d.DecodeValue() + if err != nil { + return err + } + + c, err := conv.ToBool(val) + if err != nil { + return err + } + + paramsDotWaitVal = c + return nil + }(); err != nil { + return err + } + params.Wait.SetTo(paramsDotWaitVal) + return nil + }); err != nil { + return err + } + } + return nil + }(); err != nil { + return params, &ogenerrors.DecodeParamError{ + Name: "wait", + In: "query", + Err: err, + } + } + // Decode query: wait_timeout_ms. + if err := func() error { + cfg := uri.QueryParameterDecodingConfig{ + Name: "wait_timeout_ms", + Style: uri.QueryStyleForm, + Explode: true, + } + + if err := q.HasParam(cfg); err == nil { + if err := q.DecodeParam(cfg, func(d uri.Decoder) error { + var paramsDotWaitTimeoutMsVal int + if err := func() error { + val, err := d.DecodeValue() + if err != nil { + return err + } + + c, err := conv.ToInt(val) + if err != nil { + return err + } + + paramsDotWaitTimeoutMsVal = c + return nil + }(); err != nil { + return err + } + params.WaitTimeoutMs.SetTo(paramsDotWaitTimeoutMsVal) + return nil + }); err != nil { + return err + } + if err := func() error { + if value, ok := params.WaitTimeoutMs.Get(); ok { + if err := func() error { + if err := (validate.Int{ + MinSet: true, + Min: 1000, + MaxSet: true, + Max: 30000, + MinExclusive: false, + MaxExclusive: false, + MultipleOfSet: false, + MultipleOf: 0, + Pattern: nil, + }).Validate(int64(value)); err != nil { + return errors.Wrap(err, "int") + } + return nil + }(); err != nil { + return err + } + } + return nil + }(); err != nil { + return err + } + } + return nil + }(); err != nil { + return params, &ogenerrors.DecodeParamError{ + Name: "wait_timeout_ms", + In: "query", + Err: err, + } + } + return params, nil +} + // CreateEmailChallengeParams is parameters of createEmailChallenge operation. type CreateEmailChallengeParams struct { // Optional idempotency key. Retrying a request with the same key returns diff --git a/sdk-go/api/oas_response_decoders_gen.go b/sdk-go/api/oas_response_decoders_gen.go index 6596dbd4..a1a32416 100644 --- a/sdk-go/api/oas_response_decoders_gen.go +++ b/sdk-go/api/oas_response_decoders_gen.go @@ -198,6 +198,179 @@ func decodeAddDomainResponse(resp *http.Response) (res AddDomainRes, _ error) { return res, validate.UnexpectedStatusCodeWithResponse(resp) } +func decodeAwaitReplyResponse(resp *http.Response) (res AwaitReplyRes, _ error) { + switch resp.StatusCode { + case 200: + // Code 200. + ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return res, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + buf, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + d := jx.DecodeBytes(buf) + + var response AwaitReplyOK + if err := func() error { + if err := response.Decode(d); err != nil { + return err + } + if err := d.Skip(); err != io.EOF { + return errors.New("unexpected trailing data") + } + return nil + }(); err != nil { + err = &ogenerrors.DecodeBodyError{ + ContentType: ct, + Body: buf, + Err: err, + } + return res, err + } + return &response, nil + default: + return res, validate.InvalidContentType(ct) + } + case 400: + // Code 400. + ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return res, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + buf, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + d := jx.DecodeBytes(buf) + + var response AwaitReplyBadRequest + if err := func() error { + if err := response.Decode(d); err != nil { + return err + } + if err := d.Skip(); err != io.EOF { + return errors.New("unexpected trailing data") + } + return nil + }(); err != nil { + err = &ogenerrors.DecodeBodyError{ + ContentType: ct, + Body: buf, + Err: err, + } + return res, err + } + // Validate response. + if err := func() error { + if err := response.Validate(); err != nil { + return err + } + return nil + }(); err != nil { + return res, errors.Wrap(err, "validate") + } + return &response, nil + default: + return res, validate.InvalidContentType(ct) + } + case 401: + // Code 401. + ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return res, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + buf, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + d := jx.DecodeBytes(buf) + + var response AwaitReplyUnauthorized + if err := func() error { + if err := response.Decode(d); err != nil { + return err + } + if err := d.Skip(); err != io.EOF { + return errors.New("unexpected trailing data") + } + return nil + }(); err != nil { + err = &ogenerrors.DecodeBodyError{ + ContentType: ct, + Body: buf, + Err: err, + } + return res, err + } + // Validate response. + if err := func() error { + if err := response.Validate(); err != nil { + return err + } + return nil + }(); err != nil { + return res, errors.Wrap(err, "validate") + } + return &response, nil + default: + return res, validate.InvalidContentType(ct) + } + case 404: + // Code 404. + ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return res, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + buf, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + d := jx.DecodeBytes(buf) + + var response AwaitReplyNotFound + if err := func() error { + if err := response.Decode(d); err != nil { + return err + } + if err := d.Skip(); err != io.EOF { + return errors.New("unexpected trailing data") + } + return nil + }(); err != nil { + err = &ogenerrors.DecodeBodyError{ + ContentType: ct, + Body: buf, + Err: err, + } + return res, err + } + // Validate response. + if err := func() error { + if err := response.Validate(); err != nil { + return err + } + return nil + }(); err != nil { + return res, errors.Wrap(err, "validate") + } + return &response, nil + default: + return res, validate.InvalidContentType(ct) + } + } + return res, validate.UnexpectedStatusCodeWithResponse(resp) +} + func decodeCliLogoutResponse(resp *http.Response) (res CliLogoutRes, _ error) { switch resp.StatusCode { case 200: diff --git a/sdk-go/api/oas_response_encoders_gen.go b/sdk-go/api/oas_response_encoders_gen.go index 25bd6017..bff85042 100644 --- a/sdk-go/api/oas_response_encoders_gen.go +++ b/sdk-go/api/oas_response_encoders_gen.go @@ -73,6 +73,65 @@ func encodeAddDomainResponse(response AddDomainRes, w http.ResponseWriter, span } } +func encodeAwaitReplyResponse(response AwaitReplyRes, w http.ResponseWriter, span trace.Span) error { + switch response := response.(type) { + case *AwaitReplyOK: + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(200) + span.SetStatus(codes.Ok, http.StatusText(200)) + + e := new(jx.Encoder) + response.Encode(e) + if _, err := e.WriteTo(w); err != nil { + return errors.Wrap(err, "write") + } + + return nil + + case *AwaitReplyBadRequest: + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(400) + span.SetStatus(codes.Error, http.StatusText(400)) + + e := new(jx.Encoder) + response.Encode(e) + if _, err := e.WriteTo(w); err != nil { + return errors.Wrap(err, "write") + } + + return nil + + case *AwaitReplyUnauthorized: + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(401) + span.SetStatus(codes.Error, http.StatusText(401)) + + e := new(jx.Encoder) + response.Encode(e) + if _, err := e.WriteTo(w); err != nil { + return errors.Wrap(err, "write") + } + + return nil + + case *AwaitReplyNotFound: + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(404) + span.SetStatus(codes.Error, http.StatusText(404)) + + e := new(jx.Encoder) + response.Encode(e) + if _, err := e.WriteTo(w); err != nil { + return errors.Wrap(err, "write") + } + + return nil + + default: + return errors.Errorf("unexpected response type: %T", response) + } +} + func encodeCliLogoutResponse(response CliLogoutRes, w http.ResponseWriter, span trace.Span) error { switch response := response.(type) { case *CliLogoutOK: diff --git a/sdk-go/api/oas_router_gen.go b/sdk-go/api/oas_router_gen.go index ca055a74..9a75a84a 100644 --- a/sdk-go/api/oas_router_gen.go +++ b/sdk-go/api/oas_router_gen.go @@ -11,284 +11,287 @@ import ( ) var ( - rn56AllowedHeaders = map[string]string{ + rn59AllowedHeaders = map[string]string{ "GET": "Authorization", "PATCH": "Authorization,Content-Type", } - rn74AllowedHeaders = map[string]string{ + rn75AllowedHeaders = map[string]string{ "GET": "Authorization", } - rn83AllowedHeaders = map[string]string{ + rn84AllowedHeaders = map[string]string{ "GET": "Authorization", } - rn113AllowedHeaders = map[string]string{ + rn114AllowedHeaders = map[string]string{ "POST": "Authorization", } - rn4AllowedHeaders = map[string]string{ + rn7AllowedHeaders = map[string]string{ "POST": "Content-Type", } - rn6AllowedHeaders = map[string]string{ + rn9AllowedHeaders = map[string]string{ "POST": "Authorization,Content-Type", } - rn125AllowedHeaders = map[string]string{ + rn126AllowedHeaders = map[string]string{ "POST": "Authorization,Content-Type", } - rn134AllowedHeaders = map[string]string{ + rn135AllowedHeaders = map[string]string{ "POST": "Authorization,Content-Type", } - rn108AllowedHeaders = map[string]string{ + rn109AllowedHeaders = map[string]string{ "POST": "Content-Type", } - rn127AllowedHeaders = map[string]string{ + rn128AllowedHeaders = map[string]string{ "POST": "Content-Type", } - rn135AllowedHeaders = map[string]string{ + rn136AllowedHeaders = map[string]string{ "POST": "Content-Type", } - rn29AllowedHeaders = map[string]string{ + rn32AllowedHeaders = map[string]string{ "POST": "Authorization,Content-Type", } - rn99AllowedHeaders = map[string]string{ + rn100AllowedHeaders = map[string]string{ "POST": "Content-Type", } - rn129AllowedHeaders = map[string]string{ + rn130AllowedHeaders = map[string]string{ "POST": "Content-Type", } - rn3AllowedHeaders = map[string]string{ + rn6AllowedHeaders = map[string]string{ "POST": "Authorization,Content-Type", } - rn110AllowedHeaders = map[string]string{ + rn111AllowedHeaders = map[string]string{ "POST": "Content-Type", } - rn131AllowedHeaders = map[string]string{ + rn132AllowedHeaders = map[string]string{ "POST": "Content-Type", } - rn136AllowedHeaders = map[string]string{ + rn137AllowedHeaders = map[string]string{ "POST": "Content-Type", } rn1AllowedHeaders = map[string]string{ "GET": "Authorization", "POST": "Authorization,Content-Type", } - rn31AllowedHeaders = map[string]string{ + rn34AllowedHeaders = map[string]string{ "DELETE": "Authorization", "PATCH": "Authorization,Content-Type", } - rn138AllowedHeaders = map[string]string{ + rn139AllowedHeaders = map[string]string{ "POST": "Authorization", } - rn53AllowedHeaders = map[string]string{ + rn56AllowedHeaders = map[string]string{ "GET": "Authorization", } - rn88AllowedHeaders = map[string]string{ + rn89AllowedHeaders = map[string]string{ "GET": "Authorization", } - rn115AllowedHeaders = map[string]string{ + rn116AllowedHeaders = map[string]string{ "GET": "Authorization", } - rn34AllowedHeaders = map[string]string{ + rn37AllowedHeaders = map[string]string{ "DELETE": "Authorization", "GET": "Authorization", } - rn52AllowedHeaders = map[string]string{ + rn55AllowedHeaders = map[string]string{ "GET": "Authorization", } - rn61AllowedHeaders = map[string]string{ + rn64AllowedHeaders = map[string]string{ "GET": "Authorization", } - rn50AllowedHeaders = map[string]string{ + rn53AllowedHeaders = map[string]string{ "POST": "Authorization", } - rn54AllowedHeaders = map[string]string{ + rn57AllowedHeaders = map[string]string{ "GET": "Authorization", } - rn105AllowedHeaders = map[string]string{ + rn106AllowedHeaders = map[string]string{ "POST": "Authorization", } - rn107AllowedHeaders = map[string]string{ + rn108AllowedHeaders = map[string]string{ "POST": "Authorization,Content-Type", } - rn10AllowedHeaders = map[string]string{ + rn13AllowedHeaders = map[string]string{ "GET": "Authorization", "POST": "Authorization,Content-Type", } - rn36AllowedHeaders = map[string]string{ + rn39AllowedHeaders = map[string]string{ "DELETE": "Authorization", "PATCH": "Authorization,Content-Type", } - rn132AllowedHeaders = map[string]string{ + rn133AllowedHeaders = map[string]string{ "POST": "Authorization", } - rn11AllowedHeaders = map[string]string{ + rn14AllowedHeaders = map[string]string{ "GET": "Authorization", "POST": "Authorization,Content-Type", } - rn38AllowedHeaders = map[string]string{ + rn41AllowedHeaders = map[string]string{ "DELETE": "Authorization", "PATCH": "Authorization,Content-Type", } - rn13AllowedHeaders = map[string]string{ + rn16AllowedHeaders = map[string]string{ "GET": "Authorization", "POST": "Authorization,Content-Type", } - rn68AllowedHeaders = map[string]string{ + rn71AllowedHeaders = map[string]string{ "GET": "Authorization", } - rn15AllowedHeaders = map[string]string{ + rn18AllowedHeaders = map[string]string{ "DELETE": "Authorization", "GET": "Authorization", "PUT": "Authorization,Content-Type", } - rn89AllowedHeaders = map[string]string{ + rn90AllowedHeaders = map[string]string{ "GET": "Authorization", } - rn122AllowedHeaders = map[string]string{ + rn123AllowedHeaders = map[string]string{ "DELETE": "Authorization", "PUT": "Authorization,Content-Type", } - rn63AllowedHeaders = map[string]string{ + rn66AllowedHeaders = map[string]string{ "GET": "Authorization", } - rn16AllowedHeaders = map[string]string{ + rn19AllowedHeaders = map[string]string{ "GET": "Authorization", "POST": "Authorization,Content-Type", } - rn40AllowedHeaders = map[string]string{ + rn43AllowedHeaders = map[string]string{ "DELETE": "Authorization", "PUT": "Authorization,Content-Type", } - rn133AllowedHeaders = map[string]string{ + rn134AllowedHeaders = map[string]string{ "POST": "Authorization,Content-Type", } - rn66AllowedHeaders = map[string]string{ + rn69AllowedHeaders = map[string]string{ "GET": "Authorization", } - rn67AllowedHeaders = map[string]string{ + rn70AllowedHeaders = map[string]string{ "GET": "Authorization", } - rn41AllowedHeaders = map[string]string{ + rn44AllowedHeaders = map[string]string{ "DELETE": "Authorization,X-Primitive-Function-Id", "GET": "Authorization,X-Primitive-Function-Id", "PUT": "Authorization,Content-Type,X-Primitive-Function-Id", } - rn116AllowedHeaders = map[string]string{ + rn117AllowedHeaders = map[string]string{ "GET": "Authorization,X-Primitive-Function-Id", } - rn17AllowedHeaders = map[string]string{ + rn20AllowedHeaders = map[string]string{ "GET": "Authorization", "POST": "Authorization,Content-Type", } - rn43AllowedHeaders = map[string]string{ + rn46AllowedHeaders = map[string]string{ "DELETE": "Authorization", "PUT": "Authorization,Content-Type", } - rn18AllowedHeaders = map[string]string{ + rn21AllowedHeaders = map[string]string{ "GET": "Authorization", "POST": "Authorization,Content-Type", } - rn25AllowedHeaders = map[string]string{ + rn28AllowedHeaders = map[string]string{ "DELETE": "Authorization", "PATCH": "Authorization,Content-Type", } - rn92AllowedHeaders = map[string]string{ + rn93AllowedHeaders = map[string]string{ "POST": "Authorization,Content-Type", } - rn112AllowedHeaders = map[string]string{ + rn113AllowedHeaders = map[string]string{ "DELETE": "Authorization", } - rn93AllowedHeaders = map[string]string{ + rn94AllowedHeaders = map[string]string{ "GET": "Authorization", } - rn27AllowedHeaders = map[string]string{ + rn30AllowedHeaders = map[string]string{ "POST": "Authorization,Content-Type", } - rn20AllowedHeaders = map[string]string{ + rn23AllowedHeaders = map[string]string{ "GET": "Authorization", "POST": "Authorization,Content-Type", } - rn100AllowedHeaders = map[string]string{ + rn101AllowedHeaders = map[string]string{ "POST": "Authorization,Content-Type", } - rn123AllowedHeaders = map[string]string{ + rn124AllowedHeaders = map[string]string{ "POST": "Authorization,Content-Type", } - rn45AllowedHeaders = map[string]string{ + rn48AllowedHeaders = map[string]string{ "DELETE": "Authorization", "PATCH": "Authorization,Content-Type", } - rn118AllowedHeaders = map[string]string{ + rn119AllowedHeaders = map[string]string{ "POST": "Authorization,Content-Type", } - rn120AllowedHeaders = map[string]string{ + rn121AllowedHeaders = map[string]string{ "POST": "Authorization,Content-Type,Idempotency-Key", } - rn69AllowedHeaders = map[string]string{ + rn73AllowedHeaders = map[string]string{ "GET": "Authorization", } - rn94AllowedHeaders = map[string]string{ + rn95AllowedHeaders = map[string]string{ "GET": "Authorization", } - rn72AllowedHeaders = map[string]string{ + rn4AllowedHeaders = map[string]string{ "GET": "Authorization", } - rn78AllowedHeaders = map[string]string{ + rn5AllowedHeaders = map[string]string{ "GET": "Authorization", } - rn84AllowedHeaders = map[string]string{ + rn79AllowedHeaders = map[string]string{ + "GET": "Authorization", + } + rn85AllowedHeaders = map[string]string{ "POST": "Authorization,Content-Type", } - rn81AllowedHeaders = map[string]string{ + rn82AllowedHeaders = map[string]string{ "GET": "Authorization", } - rn21AllowedHeaders = map[string]string{ + rn24AllowedHeaders = map[string]string{ "GET": "Authorization", "POST": "Authorization,Content-Type", } - rn47AllowedHeaders = map[string]string{ + rn50AllowedHeaders = map[string]string{ "DELETE": "Authorization", "PATCH": "Authorization,Content-Type", } - rn96AllowedHeaders = map[string]string{ + rn97AllowedHeaders = map[string]string{ "GET": "Authorization", } - rn23AllowedHeaders = map[string]string{ + rn26AllowedHeaders = map[string]string{ "GET": "Authorization", "POST": "Authorization,Content-Type", } - rn49AllowedHeaders = map[string]string{ + rn52AllowedHeaders = map[string]string{ "DELETE": "Authorization", "GET": "Authorization", "PATCH": "Authorization,Content-Type", } - rn114AllowedHeaders = map[string]string{ + rn115AllowedHeaders = map[string]string{ "POST": "Authorization", } - rn87AllowedHeaders = map[string]string{ + rn88AllowedHeaders = map[string]string{ "GET": "Authorization", } - rn103AllowedHeaders = map[string]string{ + rn104AllowedHeaders = map[string]string{ "POST": "Authorization", } - rn7AllowedHeaders = map[string]string{ + rn10AllowedHeaders = map[string]string{ "POST": "Authorization,Content-Type", } - rn60AllowedHeaders = map[string]string{ + rn63AllowedHeaders = map[string]string{ "GET": "Authorization", } - rn97AllowedHeaders = map[string]string{ + rn98AllowedHeaders = map[string]string{ "POST": "Authorization,Content-Type", } - rn85AllowedHeaders = map[string]string{ + rn86AllowedHeaders = map[string]string{ "GET": "Authorization", } - rn9AllowedHeaders = map[string]string{ + rn12AllowedHeaders = map[string]string{ "POST": "Authorization,Content-Type,Idempotency-Key", } - rn90AllowedHeaders = map[string]string{ + rn91AllowedHeaders = map[string]string{ "GET": "Authorization", "POST": "Authorization,Content-Type", } - rn73AllowedHeaders = map[string]string{ + rn74AllowedHeaders = map[string]string{ "GET": "Authorization", "PUT": "Authorization,Content-Type", } @@ -374,7 +377,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET,PATCH", - allowedHeaders: rn56AllowedHeaders, + allowedHeaders: rn59AllowedHeaders, acceptPost: "", acceptPatch: "application/json", }) @@ -411,7 +414,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET", - allowedHeaders: rn74AllowedHeaders, + allowedHeaders: rn75AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -435,7 +438,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET", - allowedHeaders: rn83AllowedHeaders, + allowedHeaders: rn84AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -460,7 +463,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "POST", - allowedHeaders: rn113AllowedHeaders, + allowedHeaders: rn114AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -515,7 +518,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "POST", - allowedHeaders: rn4AllowedHeaders, + allowedHeaders: rn7AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -552,7 +555,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "POST", - allowedHeaders: rn6AllowedHeaders, + allowedHeaders: rn9AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -577,7 +580,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "POST", - allowedHeaders: rn125AllowedHeaders, + allowedHeaders: rn126AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -602,7 +605,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "POST", - allowedHeaders: rn134AllowedHeaders, + allowedHeaders: rn135AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -641,7 +644,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "POST", - allowedHeaders: rn108AllowedHeaders, + allowedHeaders: rn109AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -666,7 +669,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "POST", - allowedHeaders: rn127AllowedHeaders, + allowedHeaders: rn128AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -691,7 +694,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "POST", - allowedHeaders: rn135AllowedHeaders, + allowedHeaders: rn136AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -719,7 +722,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "POST", - allowedHeaders: rn29AllowedHeaders, + allowedHeaders: rn32AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -822,7 +825,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "POST", - allowedHeaders: rn99AllowedHeaders, + allowedHeaders: rn100AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -847,7 +850,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "POST", - allowedHeaders: rn129AllowedHeaders, + allowedHeaders: rn130AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -874,7 +877,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "POST", - allowedHeaders: rn3AllowedHeaders, + allowedHeaders: rn6AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -913,7 +916,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "POST", - allowedHeaders: rn110AllowedHeaders, + allowedHeaders: rn111AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -938,7 +941,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "POST", - allowedHeaders: rn131AllowedHeaders, + allowedHeaders: rn132AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -963,7 +966,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "POST", - allowedHeaders: rn136AllowedHeaders, + allowedHeaders: rn137AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -1032,7 +1035,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "DELETE,PATCH", - allowedHeaders: rn31AllowedHeaders, + allowedHeaders: rn34AllowedHeaders, acceptPost: "", acceptPatch: "application/json", }) @@ -1071,7 +1074,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "POST", - allowedHeaders: rn138AllowedHeaders, + allowedHeaders: rn139AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -1098,7 +1101,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET", - allowedHeaders: rn53AllowedHeaders, + allowedHeaders: rn56AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -1140,7 +1143,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET", - allowedHeaders: rn88AllowedHeaders, + allowedHeaders: rn89AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -1177,7 +1180,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET", - allowedHeaders: rn115AllowedHeaders, + allowedHeaders: rn116AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -1210,7 +1213,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "DELETE,GET", - allowedHeaders: rn34AllowedHeaders, + allowedHeaders: rn37AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -1249,7 +1252,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET", - allowedHeaders: rn52AllowedHeaders, + allowedHeaders: rn55AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -1276,7 +1279,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET", - allowedHeaders: rn61AllowedHeaders, + allowedHeaders: rn64AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -1303,7 +1306,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "POST", - allowedHeaders: rn50AllowedHeaders, + allowedHeaders: rn53AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -1342,7 +1345,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET", - allowedHeaders: rn54AllowedHeaders, + allowedHeaders: rn57AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -1381,7 +1384,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "POST", - allowedHeaders: rn105AllowedHeaders, + allowedHeaders: rn106AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -1408,7 +1411,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "POST", - allowedHeaders: rn107AllowedHeaders, + allowedHeaders: rn108AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -1444,7 +1447,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET,POST", - allowedHeaders: rn10AllowedHeaders, + allowedHeaders: rn13AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -1483,7 +1486,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "DELETE,PATCH", - allowedHeaders: rn36AllowedHeaders, + allowedHeaders: rn39AllowedHeaders, acceptPost: "", acceptPatch: "application/json", }) @@ -1510,7 +1513,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "POST", - allowedHeaders: rn132AllowedHeaders, + allowedHeaders: rn133AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -1554,7 +1557,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET,POST", - allowedHeaders: rn11AllowedHeaders, + allowedHeaders: rn14AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -1594,7 +1597,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "DELETE,PATCH", - allowedHeaders: rn38AllowedHeaders, + allowedHeaders: rn41AllowedHeaders, acceptPost: "", acceptPatch: "application/json", }) @@ -1622,7 +1625,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET,POST", - allowedHeaders: rn13AllowedHeaders, + allowedHeaders: rn16AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -1659,7 +1662,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET", - allowedHeaders: rn68AllowedHeaders, + allowedHeaders: rn71AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -1696,7 +1699,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "DELETE,GET,PUT", - allowedHeaders: rn15AllowedHeaders, + allowedHeaders: rn18AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -1735,7 +1738,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET", - allowedHeaders: rn89AllowedHeaders, + allowedHeaders: rn90AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -1778,7 +1781,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "DELETE,PUT", - allowedHeaders: rn122AllowedHeaders, + allowedHeaders: rn123AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -1805,7 +1808,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET", - allowedHeaders: rn63AllowedHeaders, + allowedHeaders: rn66AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -1837,7 +1840,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET,POST", - allowedHeaders: rn16AllowedHeaders, + allowedHeaders: rn19AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -1879,7 +1882,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "DELETE,PUT", - allowedHeaders: rn40AllowedHeaders, + allowedHeaders: rn43AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -1907,7 +1910,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "POST", - allowedHeaders: rn133AllowedHeaders, + allowedHeaders: rn134AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -1956,7 +1959,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET", - allowedHeaders: rn66AllowedHeaders, + allowedHeaders: rn69AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -1993,7 +1996,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET", - allowedHeaders: rn67AllowedHeaders, + allowedHeaders: rn70AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -2021,7 +2024,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "DELETE,GET,PUT", - allowedHeaders: rn41AllowedHeaders, + allowedHeaders: rn44AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -2046,7 +2049,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET", - allowedHeaders: rn116AllowedHeaders, + allowedHeaders: rn117AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -2074,7 +2077,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET,POST", - allowedHeaders: rn17AllowedHeaders, + allowedHeaders: rn20AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -2114,7 +2117,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "DELETE,PUT", - allowedHeaders: rn43AllowedHeaders, + allowedHeaders: rn46AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -2154,7 +2157,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET,POST", - allowedHeaders: rn18AllowedHeaders, + allowedHeaders: rn21AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -2197,7 +2200,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "DELETE,GET,PATCH", - allowedHeaders: rn25AllowedHeaders, + allowedHeaders: rn28AllowedHeaders, acceptPost: "", acceptPatch: "application/json", }) @@ -2239,7 +2242,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET,POST", - allowedHeaders: rn92AllowedHeaders, + allowedHeaders: rn93AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -2281,7 +2284,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "DELETE,GET", - allowedHeaders: rn112AllowedHeaders, + allowedHeaders: rn113AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -2309,7 +2312,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET", - allowedHeaders: rn93AllowedHeaders, + allowedHeaders: rn94AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -2346,7 +2349,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "POST", - allowedHeaders: rn27AllowedHeaders, + allowedHeaders: rn30AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -2380,7 +2383,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET,POST", - allowedHeaders: rn20AllowedHeaders, + allowedHeaders: rn23AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -2417,7 +2420,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "POST", - allowedHeaders: rn100AllowedHeaders, + allowedHeaders: rn101AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -2443,7 +2446,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "POST", - allowedHeaders: rn123AllowedHeaders, + allowedHeaders: rn124AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -2477,7 +2480,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "DELETE,PATCH", - allowedHeaders: rn45AllowedHeaders, + allowedHeaders: rn48AllowedHeaders, acceptPost: "", acceptPatch: "application/json", }) @@ -2518,7 +2521,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "POST", - allowedHeaders: rn118AllowedHeaders, + allowedHeaders: rn119AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -2567,7 +2570,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "POST", - allowedHeaders: rn120AllowedHeaders, + allowedHeaders: rn121AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -2592,7 +2595,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET", - allowedHeaders: rn69AllowedHeaders, + allowedHeaders: rn73AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -2618,7 +2621,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET", - allowedHeaders: rn94AllowedHeaders, + allowedHeaders: rn95AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -2636,16 +2639,15 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { } // Param: "id" - // Leaf parameter, slashes are prohibited + // Match until "/" idx := strings.IndexByte(elem, '/') - if idx >= 0 { - break + if idx < 0 { + idx = len(elem) } - args[0] = elem - elem = "" + args[0] = elem[:idx] + elem = elem[idx:] if len(elem) == 0 { - // Leaf node. switch r.Method { case "GET": s.handleGetSentEmailRequest([1]string{ @@ -2654,7 +2656,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET", - allowedHeaders: rn72AllowedHeaders, + allowedHeaders: rn4AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -2662,6 +2664,35 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } + switch elem[0] { + case '/': // Prefix: "/reply" + + if l := len("/reply"); len(elem) >= l && elem[0:l] == "/reply" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch r.Method { + case "GET": + s.handleAwaitReplyRequest([1]string{ + args[0], + }, elemIsEscaped, w, r) + default: + s.notAllowed(w, r, notAllowedParams{ + allowedMethods: "GET", + allowedHeaders: rn5AllowedHeaders, + acceptPost: "", + acceptPatch: "", + }) + } + + return + } + + } } @@ -2744,7 +2775,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET", - allowedHeaders: rn78AllowedHeaders, + allowedHeaders: rn79AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -2800,7 +2831,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "POST", - allowedHeaders: rn84AllowedHeaders, + allowedHeaders: rn85AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -2840,7 +2871,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET", - allowedHeaders: rn81AllowedHeaders, + allowedHeaders: rn82AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -2892,7 +2923,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET,POST", - allowedHeaders: rn21AllowedHeaders, + allowedHeaders: rn24AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -2932,7 +2963,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "DELETE,PATCH", - allowedHeaders: rn47AllowedHeaders, + allowedHeaders: rn50AllowedHeaders, acceptPost: "", acceptPatch: "application/json", }) @@ -2959,7 +2990,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET", - allowedHeaders: rn96AllowedHeaders, + allowedHeaders: rn97AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -2985,7 +3016,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET,POST", - allowedHeaders: rn23AllowedHeaders, + allowedHeaders: rn26AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -3028,7 +3059,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "DELETE,GET,PATCH", - allowedHeaders: rn49AllowedHeaders, + allowedHeaders: rn52AllowedHeaders, acceptPost: "", acceptPatch: "application/json", }) @@ -3055,7 +3086,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "POST", - allowedHeaders: rn114AllowedHeaders, + allowedHeaders: rn115AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -3085,7 +3116,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET", - allowedHeaders: rn87AllowedHeaders, + allowedHeaders: rn88AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -3133,7 +3164,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "POST", - allowedHeaders: rn103AllowedHeaders, + allowedHeaders: rn104AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -3175,7 +3206,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "POST", - allowedHeaders: rn7AllowedHeaders, + allowedHeaders: rn10AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -3210,7 +3241,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET", - allowedHeaders: rn60AllowedHeaders, + allowedHeaders: rn63AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -3237,7 +3268,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "POST", - allowedHeaders: rn97AllowedHeaders, + allowedHeaders: rn98AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -3266,7 +3297,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET", - allowedHeaders: rn85AllowedHeaders, + allowedHeaders: rn86AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -3291,7 +3322,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "POST", - allowedHeaders: rn9AllowedHeaders, + allowedHeaders: rn12AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -3318,7 +3349,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET,POST", - allowedHeaders: rn90AllowedHeaders, + allowedHeaders: rn91AllowedHeaders, acceptPost: "application/json", acceptPatch: "", }) @@ -3345,7 +3376,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: s.notAllowed(w, r, notAllowedParams{ allowedMethods: "GET,PUT", - allowedHeaders: rn73AllowedHeaders, + allowedHeaders: rn74AllowedHeaders, acceptPost: "", acceptPatch: "", }) @@ -5828,16 +5859,15 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) { } // Param: "id" - // Leaf parameter, slashes are prohibited + // Match until "/" idx := strings.IndexByte(elem, '/') - if idx >= 0 { - break + if idx < 0 { + idx = len(elem) } - args[0] = elem - elem = "" + args[0] = elem[:idx] + elem = elem[idx:] if len(elem) == 0 { - // Leaf node. switch method { case "GET": r.name = GetSentEmailOperation @@ -5852,6 +5882,33 @@ func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) { return } } + switch elem[0] { + case '/': // Prefix: "/reply" + + if l := len("/reply"); len(elem) >= l && elem[0:l] == "/reply" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch method { + case "GET": + r.name = AwaitReplyOperation + r.summary = "Get (or wait for) the reply to a sent email" + r.operationID = "awaitReply" + r.operationGroup = "" + r.pathPattern = "/sent-emails/{id}/reply" + r.args = args + r.count = 1 + return r, true + default: + return + } + } + + } } diff --git a/sdk-go/api/oas_schemas_gen.go b/sdk-go/api/oas_schemas_gen.go index ae10f4fb..91bedab0 100644 --- a/sdk-go/api/oas_schemas_gen.go +++ b/sdk-go/api/oas_schemas_gen.go @@ -1023,6 +1023,99 @@ func (s *AgentSignupVerifyResultTokenType) UnmarshalText(data []byte) error { } } +type AwaitReplyBadRequest ErrorResponse + +func (*AwaitReplyBadRequest) awaitReplyRes() {} + +type AwaitReplyNotFound ErrorResponse + +func (*AwaitReplyNotFound) awaitReplyRes() {} + +// Merged schema. +type AwaitReplyOK struct { + Success bool `json:"success"` + Data AwaitReplyResult `json:"data"` +} + +// GetSuccess returns the value of Success. +func (s *AwaitReplyOK) GetSuccess() bool { + return s.Success +} + +// GetData returns the value of Data. +func (s *AwaitReplyOK) GetData() AwaitReplyResult { + return s.Data +} + +// SetSuccess sets the value of Success. +func (s *AwaitReplyOK) SetSuccess(val bool) { + s.Success = val +} + +// SetData sets the value of Data. +func (s *AwaitReplyOK) SetData(val AwaitReplyResult) { + s.Data = val +} + +func (*AwaitReplyOK) awaitReplyRes() {} + +// Result of `/sent-emails/{id}/reply`. `reply` is null when no +// reply has arrived yet (no-wait call, or the wait timed out). +// Ref: #/components/schemas/AwaitReplyResult +type AwaitReplyResult struct { + // The send this lookup was keyed on (echoes the path id). + SentEmailID uuid.UUID `json:"sent_email_id"` + Reply NilReplyEmail `json:"reply"` + // Whether the call ran in long-poll mode (`wait=true`). + Waited bool `json:"waited"` + // True only when a `wait=true` call elapsed its timeout with no reply. + TimedOut bool `json:"timed_out"` +} + +// GetSentEmailID returns the value of SentEmailID. +func (s *AwaitReplyResult) GetSentEmailID() uuid.UUID { + return s.SentEmailID +} + +// GetReply returns the value of Reply. +func (s *AwaitReplyResult) GetReply() NilReplyEmail { + return s.Reply +} + +// GetWaited returns the value of Waited. +func (s *AwaitReplyResult) GetWaited() bool { + return s.Waited +} + +// GetTimedOut returns the value of TimedOut. +func (s *AwaitReplyResult) GetTimedOut() bool { + return s.TimedOut +} + +// SetSentEmailID sets the value of SentEmailID. +func (s *AwaitReplyResult) SetSentEmailID(val uuid.UUID) { + s.SentEmailID = val +} + +// SetReply sets the value of Reply. +func (s *AwaitReplyResult) SetReply(val NilReplyEmail) { + s.Reply = val +} + +// SetWaited sets the value of Waited. +func (s *AwaitReplyResult) SetWaited(val bool) { + s.Waited = val +} + +// SetTimedOut sets the value of TimedOut. +func (s *AwaitReplyResult) SetTimedOut(val bool) { + s.TimedOut = val +} + +type AwaitReplyUnauthorized ErrorResponse + +func (*AwaitReplyUnauthorized) awaitReplyRes() {} + type BearerAuth struct { Token string Roles []string @@ -13912,6 +14005,51 @@ func (o NilInt) Or(d int) int { return d } +// NewNilReplyEmail returns new NilReplyEmail with value set to v. +func NewNilReplyEmail(v ReplyEmail) NilReplyEmail { + return NilReplyEmail{ + Value: v, + } +} + +// NilReplyEmail is nullable ReplyEmail. +type NilReplyEmail struct { + Value ReplyEmail + Null bool +} + +// SetTo sets value to v. +func (o *NilReplyEmail) SetTo(v ReplyEmail) { + o.Null = false + o.Value = v +} + +// IsNull returns true if value is Null. +func (o NilReplyEmail) IsNull() bool { return o.Null } + +// SetToNull sets value to null. +func (o *NilReplyEmail) SetToNull() { + o.Null = true + var v ReplyEmail + o.Value = v +} + +// Get returns value and boolean that denotes whether value was set. +func (o NilReplyEmail) Get() (v ReplyEmail, ok bool) { + if o.Null { + return v, false + } + return o.Value, true +} + +// Or returns value if set, or given parameter if does not. +func (o NilReplyEmail) Or(d ReplyEmail) ReplyEmail { + if v, ok := o.Get(); ok { + return v + } + return d +} + // NewNilRoutingTopologyDomainsItemRoutedFunction returns new NilRoutingTopologyDomainsItemRoutedFunction with value set to v. func NewNilRoutingTopologyDomainsItemRoutedFunction(v RoutingTopologyDomainsItemRoutedFunction) NilRoutingTopologyDomainsItemRoutedFunction { return NilRoutingTopologyDomainsItemRoutedFunction{ @@ -19162,6 +19300,135 @@ func (s *ReplayResult) SetFailed(val int) { s.Failed = val } +// A threaded inbound reply to one of the org's sends, keyed by +// the inbound email's `reply_to_sent_email_id`. Compact on +// purpose: enough to read the reply and decide what to do +// next, with `/emails/{id}` available for the fully parsed +// detail. +// Ref: #/components/schemas/ReplyEmail +type ReplyEmail struct { + // Inbound email row id; usable with `/emails/{id}`. + ID uuid.UUID `json:"id"` + // Conversation thread id; usable with `/threads/{id}`. + ThreadID NilUUID `json:"thread_id"` + // The sent-email id this inbound message replied to. + ReplyToSentEmailID NilUUID `json:"reply_to_sent_email_id"` + // Sender of the reply (From header when present, else envelope sender). + FromEmail string `json:"from_email"` + // Recipient address the reply arrived at. + ToEmail string `json:"to_email"` + Subject NilString `json:"subject"` + // Plain-text body of the reply, when present. + BodyText NilString `json:"body_text"` + // HTML body of the reply, when present. + BodyHTML NilString `json:"body_html"` + ReceivedAt NilDateTime `json:"received_at"` + // Inbound processing status of the reply row. Typically an + // `EmailStatus` value, but left as an open string here to + // match the server contract, which does not narrow it. + Status string `json:"status"` +} + +// GetID returns the value of ID. +func (s *ReplyEmail) GetID() uuid.UUID { + return s.ID +} + +// GetThreadID returns the value of ThreadID. +func (s *ReplyEmail) GetThreadID() NilUUID { + return s.ThreadID +} + +// GetReplyToSentEmailID returns the value of ReplyToSentEmailID. +func (s *ReplyEmail) GetReplyToSentEmailID() NilUUID { + return s.ReplyToSentEmailID +} + +// GetFromEmail returns the value of FromEmail. +func (s *ReplyEmail) GetFromEmail() string { + return s.FromEmail +} + +// GetToEmail returns the value of ToEmail. +func (s *ReplyEmail) GetToEmail() string { + return s.ToEmail +} + +// GetSubject returns the value of Subject. +func (s *ReplyEmail) GetSubject() NilString { + return s.Subject +} + +// GetBodyText returns the value of BodyText. +func (s *ReplyEmail) GetBodyText() NilString { + return s.BodyText +} + +// GetBodyHTML returns the value of BodyHTML. +func (s *ReplyEmail) GetBodyHTML() NilString { + return s.BodyHTML +} + +// GetReceivedAt returns the value of ReceivedAt. +func (s *ReplyEmail) GetReceivedAt() NilDateTime { + return s.ReceivedAt +} + +// GetStatus returns the value of Status. +func (s *ReplyEmail) GetStatus() string { + return s.Status +} + +// SetID sets the value of ID. +func (s *ReplyEmail) SetID(val uuid.UUID) { + s.ID = val +} + +// SetThreadID sets the value of ThreadID. +func (s *ReplyEmail) SetThreadID(val NilUUID) { + s.ThreadID = val +} + +// SetReplyToSentEmailID sets the value of ReplyToSentEmailID. +func (s *ReplyEmail) SetReplyToSentEmailID(val NilUUID) { + s.ReplyToSentEmailID = val +} + +// SetFromEmail sets the value of FromEmail. +func (s *ReplyEmail) SetFromEmail(val string) { + s.FromEmail = val +} + +// SetToEmail sets the value of ToEmail. +func (s *ReplyEmail) SetToEmail(val string) { + s.ToEmail = val +} + +// SetSubject sets the value of Subject. +func (s *ReplyEmail) SetSubject(val NilString) { + s.Subject = val +} + +// SetBodyText sets the value of BodyText. +func (s *ReplyEmail) SetBodyText(val NilString) { + s.BodyText = val +} + +// SetBodyHTML sets the value of BodyHTML. +func (s *ReplyEmail) SetBodyHTML(val NilString) { + s.BodyHTML = val +} + +// SetReceivedAt sets the value of ReceivedAt. +func (s *ReplyEmail) SetReceivedAt(val NilDateTime) { + s.ReceivedAt = val +} + +// SetStatus sets the value of Status. +func (s *ReplyEmail) SetStatus(val string) { + s.Status = val +} + // Body shape for `/emails/{id}/reply`. Intentionally narrow: // recipients (`to`), subject, and threading headers // (`in_reply_to`, `references`) are derived server-side from diff --git a/sdk-go/api/oas_security_gen.go b/sdk-go/api/oas_security_gen.go index 5538dc9e..abe8f09d 100644 --- a/sdk-go/api/oas_security_gen.go +++ b/sdk-go/api/oas_security_gen.go @@ -39,6 +39,7 @@ func findAuthorization(h http.Header, prefix string) (string, bool) { // operationRolesBearerAuth is a private map storing roles per operation. var operationRolesBearerAuth = map[string][]string{ AddDomainOperation: []string{}, + AwaitReplyOperation: []string{}, CliLogoutOperation: []string{}, CreateAgentClaimLinkOperation: []string{}, CreateChallengeOperation: []string{}, diff --git a/sdk-go/api/oas_server_gen.go b/sdk-go/api/oas_server_gen.go index 1ccf384b..341d5644 100644 --- a/sdk-go/api/oas_server_gen.go +++ b/sdk-go/api/oas_server_gen.go @@ -18,6 +18,25 @@ type Handler interface { // // POST /domains AddDomain(ctx context.Context, req *AddDomainInput) (AddDomainRes, error) + // AwaitReply implements awaitReply operation. + // + // Returns the first threaded inbound reply to a send, keyed by + // the inbound email's `reply_to_sent_email_id`. This is the + // canonical "did a reply arrive for this send?" call: after + // /send-mail, poll (or long-poll) here instead of hand-rolling + // an /emails/search loop. + // By default the call returns immediately with `reply: null` + // when nothing has arrived yet. Set `wait=true` to long-poll: + // the server holds the request up to `wait_timeout_ms` + // (default 10000, max 30000), returning as soon as the first + // reply lands. On a wait that elapses with no reply, the + // response has `reply: null` and `timed_out: true`. + // The reply object is compact (headers plus bodies); fetch + // `/emails/{id}` with `reply.id` for the fully parsed record, + // or `/threads/{thread_id}` for the whole back-and-forth. + // + // GET /sent-emails/{id}/reply + AwaitReply(ctx context.Context, params AwaitReplyParams) (AwaitReplyRes, error) // CliLogout implements cliLogout operation. // // Revokes the OAuth grant used to authenticate the request. API-key diff --git a/sdk-go/api/oas_unimplemented_gen.go b/sdk-go/api/oas_unimplemented_gen.go index 81549a1c..db778609 100644 --- a/sdk-go/api/oas_unimplemented_gen.go +++ b/sdk-go/api/oas_unimplemented_gen.go @@ -26,6 +26,28 @@ func (UnimplementedHandler) AddDomain(ctx context.Context, req *AddDomainInput) return r, ht.ErrNotImplemented } +// AwaitReply implements awaitReply operation. +// +// Returns the first threaded inbound reply to a send, keyed by +// the inbound email's `reply_to_sent_email_id`. This is the +// canonical "did a reply arrive for this send?" call: after +// /send-mail, poll (or long-poll) here instead of hand-rolling +// an /emails/search loop. +// By default the call returns immediately with `reply: null` +// when nothing has arrived yet. Set `wait=true` to long-poll: +// the server holds the request up to `wait_timeout_ms` +// (default 10000, max 30000), returning as soon as the first +// reply lands. On a wait that elapses with no reply, the +// response has `reply: null` and `timed_out: true`. +// The reply object is compact (headers plus bodies); fetch +// `/emails/{id}` with `reply.id` for the fully parsed record, +// or `/threads/{thread_id}` for the whole back-and-forth. +// +// GET /sent-emails/{id}/reply +func (UnimplementedHandler) AwaitReply(ctx context.Context, params AwaitReplyParams) (r AwaitReplyRes, _ error) { + return r, ht.ErrNotImplemented +} + // CliLogout implements cliLogout operation. // // Revokes the OAuth grant used to authenticate the request. API-key diff --git a/sdk-go/api/oas_validators_gen.go b/sdk-go/api/oas_validators_gen.go index 5417a137..9df63f72 100644 --- a/sdk-go/api/oas_validators_gen.go +++ b/sdk-go/api/oas_validators_gen.go @@ -478,6 +478,30 @@ func (s AgentSignupVerifyResultTokenType) Validate() error { } } +func (s *AwaitReplyBadRequest) Validate() error { + alias := (*ErrorResponse)(s) + if err := alias.Validate(); err != nil { + return err + } + return nil +} + +func (s *AwaitReplyNotFound) Validate() error { + alias := (*ErrorResponse)(s) + if err := alias.Validate(); err != nil { + return err + } + return nil +} + +func (s *AwaitReplyUnauthorized) Validate() error { + alias := (*ErrorResponse)(s) + if err := alias.Validate(); err != nil { + return err + } + return nil +} + func (s *CliLoginPollResult) Validate() error { if s == nil { return validate.ErrNilPointer diff --git a/sdk-python/src/primitive/api/api/sending/await_reply.py b/sdk-python/src/primitive/api/api/sending/await_reply.py new file mode 100644 index 00000000..15f637f1 --- /dev/null +++ b/sdk-python/src/primitive/api/api/sending/await_reply.py @@ -0,0 +1,299 @@ +from http import HTTPStatus +from typing import Any, cast +from urllib.parse import quote + +import httpx + +from ...client import AuthenticatedClient, Client +from ...types import Response, UNSET +from ... import errors + +from ...models.await_reply_response_200 import AwaitReplyResponse200 +from ...models.error_response import ErrorResponse +from ...types import UNSET, Unset +from typing import cast +from uuid import UUID + + + +def _get_kwargs( + id: UUID, + *, + wait: bool | Unset = UNSET, + wait_timeout_ms: int | Unset = UNSET, + +) -> dict[str, Any]: + + + + + params: dict[str, Any] = {} + + params["wait"] = wait + + params["wait_timeout_ms"] = wait_timeout_ms + + + params = {k: v for k, v in params.items() if v is not UNSET and v is not None} + + + _kwargs: dict[str, Any] = { + "method": "get", + "url": "/sent-emails/{id}/reply".format(id=quote(str(id), safe=""),), + "params": params, + } + + + return _kwargs + + + +def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> AwaitReplyResponse200 | ErrorResponse | None: + if response.status_code == 200: + response_200 = AwaitReplyResponse200.from_dict(response.json()) + + + + return response_200 + + if response.status_code == 400: + response_400 = ErrorResponse.from_dict(response.json()) + + + + return response_400 + + if response.status_code == 401: + response_401 = ErrorResponse.from_dict(response.json()) + + + + return response_401 + + if response.status_code == 404: + response_404 = ErrorResponse.from_dict(response.json()) + + + + return response_404 + + if client.raise_on_unexpected_status: + raise errors.UnexpectedStatus(response.status_code, response.content) + else: + return None + + +def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[AwaitReplyResponse200 | ErrorResponse]: + return Response( + status_code=HTTPStatus(response.status_code), + content=response.content, + headers=response.headers, + parsed=_parse_response(client=client, response=response), + ) + + +def sync_detailed( + id: UUID, + *, + client: AuthenticatedClient | Client, + wait: bool | Unset = UNSET, + wait_timeout_ms: int | Unset = UNSET, + +) -> Response[AwaitReplyResponse200 | ErrorResponse]: + r""" Get (or wait for) the reply to a sent email + + Returns the first threaded inbound reply to a send, keyed by + the inbound email's `reply_to_sent_email_id`. This is the + canonical \"did a reply arrive for this send?\" call: after + /send-mail, poll (or long-poll) here instead of hand-rolling + an /emails/search loop. + + By default the call returns immediately with `reply: null` + when nothing has arrived yet. Set `wait=true` to long-poll: + the server holds the request up to `wait_timeout_ms` + (default 10000, max 30000), returning as soon as the first + reply lands. On a wait that elapses with no reply, the + response has `reply: null` and `timed_out: true`. + + The reply object is compact (headers plus bodies); fetch + `/emails/{id}` with `reply.id` for the fully parsed record, + or `/threads/{thread_id}` for the whole back-and-forth. + + Args: + id (UUID): + wait (bool | Unset): + wait_timeout_ms (int | Unset): + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Response[AwaitReplyResponse200 | ErrorResponse] + """ + + + kwargs = _get_kwargs( + id=id, +wait=wait, +wait_timeout_ms=wait_timeout_ms, + + ) + + response = client.get_httpx_client().request( + **kwargs, + ) + + return _build_response(client=client, response=response) + +def sync( + id: UUID, + *, + client: AuthenticatedClient | Client, + wait: bool | Unset = UNSET, + wait_timeout_ms: int | Unset = UNSET, + +) -> AwaitReplyResponse200 | ErrorResponse | None: + r""" Get (or wait for) the reply to a sent email + + Returns the first threaded inbound reply to a send, keyed by + the inbound email's `reply_to_sent_email_id`. This is the + canonical \"did a reply arrive for this send?\" call: after + /send-mail, poll (or long-poll) here instead of hand-rolling + an /emails/search loop. + + By default the call returns immediately with `reply: null` + when nothing has arrived yet. Set `wait=true` to long-poll: + the server holds the request up to `wait_timeout_ms` + (default 10000, max 30000), returning as soon as the first + reply lands. On a wait that elapses with no reply, the + response has `reply: null` and `timed_out: true`. + + The reply object is compact (headers plus bodies); fetch + `/emails/{id}` with `reply.id` for the fully parsed record, + or `/threads/{thread_id}` for the whole back-and-forth. + + Args: + id (UUID): + wait (bool | Unset): + wait_timeout_ms (int | Unset): + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + AwaitReplyResponse200 | ErrorResponse + """ + + + return sync_detailed( + id=id, +client=client, +wait=wait, +wait_timeout_ms=wait_timeout_ms, + + ).parsed + +async def asyncio_detailed( + id: UUID, + *, + client: AuthenticatedClient | Client, + wait: bool | Unset = UNSET, + wait_timeout_ms: int | Unset = UNSET, + +) -> Response[AwaitReplyResponse200 | ErrorResponse]: + r""" Get (or wait for) the reply to a sent email + + Returns the first threaded inbound reply to a send, keyed by + the inbound email's `reply_to_sent_email_id`. This is the + canonical \"did a reply arrive for this send?\" call: after + /send-mail, poll (or long-poll) here instead of hand-rolling + an /emails/search loop. + + By default the call returns immediately with `reply: null` + when nothing has arrived yet. Set `wait=true` to long-poll: + the server holds the request up to `wait_timeout_ms` + (default 10000, max 30000), returning as soon as the first + reply lands. On a wait that elapses with no reply, the + response has `reply: null` and `timed_out: true`. + + The reply object is compact (headers plus bodies); fetch + `/emails/{id}` with `reply.id` for the fully parsed record, + or `/threads/{thread_id}` for the whole back-and-forth. + + Args: + id (UUID): + wait (bool | Unset): + wait_timeout_ms (int | Unset): + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Response[AwaitReplyResponse200 | ErrorResponse] + """ + + + kwargs = _get_kwargs( + id=id, +wait=wait, +wait_timeout_ms=wait_timeout_ms, + + ) + + response = await client.get_async_httpx_client().request( + **kwargs + ) + + return _build_response(client=client, response=response) + +async def asyncio( + id: UUID, + *, + client: AuthenticatedClient | Client, + wait: bool | Unset = UNSET, + wait_timeout_ms: int | Unset = UNSET, + +) -> AwaitReplyResponse200 | ErrorResponse | None: + r""" Get (or wait for) the reply to a sent email + + Returns the first threaded inbound reply to a send, keyed by + the inbound email's `reply_to_sent_email_id`. This is the + canonical \"did a reply arrive for this send?\" call: after + /send-mail, poll (or long-poll) here instead of hand-rolling + an /emails/search loop. + + By default the call returns immediately with `reply: null` + when nothing has arrived yet. Set `wait=true` to long-poll: + the server holds the request up to `wait_timeout_ms` + (default 10000, max 30000), returning as soon as the first + reply lands. On a wait that elapses with no reply, the + response has `reply: null` and `timed_out: true`. + + The reply object is compact (headers plus bodies); fetch + `/emails/{id}` with `reply.id` for the fully parsed record, + or `/threads/{thread_id}` for the whole back-and-forth. + + Args: + id (UUID): + wait (bool | Unset): + wait_timeout_ms (int | Unset): + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + AwaitReplyResponse200 | ErrorResponse + """ + + + return (await asyncio_detailed( + id=id, +client=client, +wait=wait, +wait_timeout_ms=wait_timeout_ms, + + )).parsed diff --git a/sdk-python/src/primitive/api/models/__init__.py b/sdk-python/src/primitive/api/models/__init__.py index 85afe978..c953df84 100644 --- a/sdk-python/src/primitive/api/models/__init__.py +++ b/sdk-python/src/primitive/api/models/__init__.py @@ -18,6 +18,8 @@ from .agent_signup_verify_result import AgentSignupVerifyResult from .agent_signup_verify_result_auth_method import AgentSignupVerifyResultAuthMethod from .agent_signup_verify_result_token_type import AgentSignupVerifyResultTokenType +from .await_reply_response_200 import AwaitReplyResponse200 +from .await_reply_result import AwaitReplyResult from .cli_login_poll_result import CliLoginPollResult from .cli_login_poll_result_auth_method import CliLoginPollResultAuthMethod from .cli_login_poll_result_token_type import CliLoginPollResultTokenType @@ -275,6 +277,7 @@ from .replay_delivery_response_200 import ReplayDeliveryResponse200 from .replay_email_webhooks_response_200 import ReplayEmailWebhooksResponse200 from .replay_result import ReplayResult +from .reply_email import ReplyEmail from .reply_input import ReplyInput from .reply_to_email_response_200 import ReplyToEmailResponse200 from .resend_agent_signup_verification_input import ResendAgentSignupVerificationInput @@ -473,6 +476,8 @@ "AgentSignupVerifyResult", "AgentSignupVerifyResultAuthMethod", "AgentSignupVerifyResultTokenType", + "AwaitReplyResponse200", + "AwaitReplyResult", "CliLoginPollResult", "CliLoginPollResultAuthMethod", "CliLoginPollResultTokenType", @@ -730,6 +735,7 @@ "ReplayDeliveryResponse200", "ReplayEmailWebhooksResponse200", "ReplayResult", + "ReplyEmail", "ReplyInput", "ReplyToEmailResponse200", "ResendAgentSignupVerificationInput", diff --git a/sdk-python/src/primitive/api/models/await_reply_response_200.py b/sdk-python/src/primitive/api/models/await_reply_response_200.py new file mode 100644 index 00000000..9aa264f4 --- /dev/null +++ b/sdk-python/src/primitive/api/models/await_reply_response_200.py @@ -0,0 +1,101 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar, BinaryIO, TextIO, TYPE_CHECKING, Generator + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +from ..types import UNSET, Unset + +from typing import cast + +if TYPE_CHECKING: + from ..models.await_reply_result import AwaitReplyResult + + + + + +T = TypeVar("T", bound="AwaitReplyResponse200") + + + +@_attrs_define +class AwaitReplyResponse200: + """ + Attributes: + success (bool): + data (AwaitReplyResult | Unset): Result of `/sent-emails/{id}/reply`. `reply` is null when no + reply has arrived yet (no-wait call, or the wait timed out). + """ + + success: bool + data: AwaitReplyResult | Unset = UNSET + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + + + + + def to_dict(self) -> dict[str, Any]: + from ..models.await_reply_result import AwaitReplyResult + success = self.success + + data: dict[str, Any] | Unset = UNSET + if not isinstance(self.data, Unset): + data = self.data.to_dict() + + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({ + "success": success, + }) + if data is not UNSET: + field_dict["data"] = data + + return field_dict + + + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + from ..models.await_reply_result import AwaitReplyResult + d = dict(src_dict) + success = d.pop("success") + + _data = d.pop("data", UNSET) + data: AwaitReplyResult | Unset + if isinstance(_data, Unset): + data = UNSET + else: + data = AwaitReplyResult.from_dict(_data) + + + + + await_reply_response_200 = cls( + success=success, + data=data, + ) + + + await_reply_response_200.additional_properties = d + return await_reply_response_200 + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/sdk-python/src/primitive/api/models/await_reply_result.py b/sdk-python/src/primitive/api/models/await_reply_result.py new file mode 100644 index 00000000..26e9fbe9 --- /dev/null +++ b/sdk-python/src/primitive/api/models/await_reply_result.py @@ -0,0 +1,113 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar, BinaryIO, TextIO, TYPE_CHECKING, Generator + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +from ..types import UNSET, Unset + +from typing import cast +from uuid import UUID + +if TYPE_CHECKING: + from ..models.reply_email import ReplyEmail + + + + + +T = TypeVar("T", bound="AwaitReplyResult") + + + +@_attrs_define +class AwaitReplyResult: + """ Result of `/sent-emails/{id}/reply`. `reply` is null when no + reply has arrived yet (no-wait call, or the wait timed out). + + Attributes: + sent_email_id (UUID): The send this lookup was keyed on (echoes the path id). + reply (None | ReplyEmail): + waited (bool): Whether the call ran in long-poll mode (`wait=true`). + timed_out (bool): True only when a `wait=true` call elapsed its timeout with no reply. + """ + + sent_email_id: UUID + reply: None | ReplyEmail + waited: bool + timed_out: bool + + + + + + def to_dict(self) -> dict[str, Any]: + from ..models.reply_email import ReplyEmail + sent_email_id = str(self.sent_email_id) + + reply: dict[str, Any] | None + if isinstance(self.reply, ReplyEmail): + reply = self.reply.to_dict() + else: + reply = self.reply + + waited = self.waited + + timed_out = self.timed_out + + + field_dict: dict[str, Any] = {} + + field_dict.update({ + "sent_email_id": sent_email_id, + "reply": reply, + "waited": waited, + "timed_out": timed_out, + }) + + return field_dict + + + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + from ..models.reply_email import ReplyEmail + d = dict(src_dict) + sent_email_id = UUID(d.pop("sent_email_id")) + + + + + def _parse_reply(data: object) -> None | ReplyEmail: + if data is None: + return data + try: + if not isinstance(data, dict): + raise TypeError() + reply_type_1 = ReplyEmail.from_dict(data) + + + + return reply_type_1 + except (TypeError, ValueError, AttributeError, KeyError): + pass + return cast(None | ReplyEmail, data) + + reply = _parse_reply(d.pop("reply")) + + + waited = d.pop("waited") + + timed_out = d.pop("timed_out") + + await_reply_result = cls( + sent_email_id=sent_email_id, + reply=reply, + waited=waited, + timed_out=timed_out, + ) + + return await_reply_result + diff --git a/sdk-python/src/primitive/api/models/reply_email.py b/sdk-python/src/primitive/api/models/reply_email.py new file mode 100644 index 00000000..9f4d07c3 --- /dev/null +++ b/sdk-python/src/primitive/api/models/reply_email.py @@ -0,0 +1,243 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar, BinaryIO, TextIO, TYPE_CHECKING, Generator + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +from ..types import UNSET, Unset + +from dateutil.parser import isoparse +from typing import cast +from uuid import UUID +import datetime + + + + + + +T = TypeVar("T", bound="ReplyEmail") + + + +@_attrs_define +class ReplyEmail: + """ A threaded inbound reply to one of the org's sends, keyed by + the inbound email's `reply_to_sent_email_id`. Compact on + purpose: enough to read the reply and decide what to do + next, with `/emails/{id}` available for the fully parsed + detail. + + Attributes: + id (UUID): Inbound email row id; usable with `/emails/{id}`. + thread_id (None | UUID): Conversation thread id; usable with `/threads/{id}`. + reply_to_sent_email_id (None | UUID): The sent-email id this inbound message replied to. + from_email (str): Sender of the reply (From header when present, else envelope sender). + to_email (str): Recipient address the reply arrived at. + subject (None | str): + body_text (None | str): Plain-text body of the reply, when present. + body_html (None | str): HTML body of the reply, when present. + received_at (datetime.datetime | None): + status (str): Inbound processing status of the reply row. Typically an + `EmailStatus` value, but left as an open string here to + match the server contract, which does not narrow it. + """ + + id: UUID + thread_id: None | UUID + reply_to_sent_email_id: None | UUID + from_email: str + to_email: str + subject: None | str + body_text: None | str + body_html: None | str + received_at: datetime.datetime | None + status: str + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + + + + + def to_dict(self) -> dict[str, Any]: + id = str(self.id) + + thread_id: None | str + if isinstance(self.thread_id, UUID): + thread_id = str(self.thread_id) + else: + thread_id = self.thread_id + + reply_to_sent_email_id: None | str + if isinstance(self.reply_to_sent_email_id, UUID): + reply_to_sent_email_id = str(self.reply_to_sent_email_id) + else: + reply_to_sent_email_id = self.reply_to_sent_email_id + + from_email = self.from_email + + to_email = self.to_email + + subject: None | str + subject = self.subject + + body_text: None | str + body_text = self.body_text + + body_html: None | str + body_html = self.body_html + + received_at: None | str + if isinstance(self.received_at, datetime.datetime): + received_at = self.received_at.isoformat() + else: + received_at = self.received_at + + status = self.status + + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({ + "id": id, + "thread_id": thread_id, + "reply_to_sent_email_id": reply_to_sent_email_id, + "from_email": from_email, + "to_email": to_email, + "subject": subject, + "body_text": body_text, + "body_html": body_html, + "received_at": received_at, + "status": status, + }) + + return field_dict + + + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + id = UUID(d.pop("id")) + + + + + def _parse_thread_id(data: object) -> None | UUID: + if data is None: + return data + try: + if not isinstance(data, str): + raise TypeError() + thread_id_type_0 = UUID(data) + + + + return thread_id_type_0 + except (TypeError, ValueError, AttributeError, KeyError): + pass + return cast(None | UUID, data) + + thread_id = _parse_thread_id(d.pop("thread_id")) + + + def _parse_reply_to_sent_email_id(data: object) -> None | UUID: + if data is None: + return data + try: + if not isinstance(data, str): + raise TypeError() + reply_to_sent_email_id_type_0 = UUID(data) + + + + return reply_to_sent_email_id_type_0 + except (TypeError, ValueError, AttributeError, KeyError): + pass + return cast(None | UUID, data) + + reply_to_sent_email_id = _parse_reply_to_sent_email_id(d.pop("reply_to_sent_email_id")) + + + from_email = d.pop("from_email") + + to_email = d.pop("to_email") + + def _parse_subject(data: object) -> None | str: + if data is None: + return data + return cast(None | str, data) + + subject = _parse_subject(d.pop("subject")) + + + def _parse_body_text(data: object) -> None | str: + if data is None: + return data + return cast(None | str, data) + + body_text = _parse_body_text(d.pop("body_text")) + + + def _parse_body_html(data: object) -> None | str: + if data is None: + return data + return cast(None | str, data) + + body_html = _parse_body_html(d.pop("body_html")) + + + def _parse_received_at(data: object) -> datetime.datetime | None: + if data is None: + return data + try: + if not isinstance(data, str): + raise TypeError() + received_at_type_0 = isoparse(data) + + + + return received_at_type_0 + except (TypeError, ValueError, AttributeError, KeyError): + pass + return cast(datetime.datetime | None, data) + + received_at = _parse_received_at(d.pop("received_at")) + + + status = d.pop("status") + + reply_email = cls( + id=id, + thread_id=thread_id, + reply_to_sent_email_id=reply_to_sent_email_id, + from_email=from_email, + to_email=to_email, + subject=subject, + body_text=body_text, + body_html=body_html, + received_at=received_at, + status=status, + ) + + + reply_email.additional_properties = d + return reply_email + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties