@getjack/jack 0.1.19 → 0.1.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/package.json +5 -2
  2. package/src/commands/down.ts +11 -1
  3. package/src/commands/init.ts +19 -6
  4. package/src/commands/new.ts +56 -4
  5. package/src/commands/publish.ts +1 -1
  6. package/src/lib/agents.ts +3 -1
  7. package/src/lib/auth/ensure-auth.test.ts +3 -3
  8. package/src/lib/control-plane.ts +15 -1
  9. package/src/lib/deploy-upload.ts +26 -1
  10. package/src/lib/hooks.ts +232 -1
  11. package/src/lib/managed-deploy.ts +13 -6
  12. package/src/lib/managed-down.ts +66 -45
  13. package/src/lib/progress.ts +76 -5
  14. package/src/lib/project-list.ts +6 -1
  15. package/src/lib/project-operations.ts +21 -31
  16. package/src/lib/project-resolver.ts +1 -1
  17. package/src/lib/zip-packager.ts +36 -7
  18. package/src/templates/index.ts +1 -1
  19. package/src/templates/types.ts +16 -0
  20. package/templates/CLAUDE.md +172 -5
  21. package/templates/miniapp/.jack.json +1 -3
  22. package/templates/saas/.jack.json +154 -0
  23. package/templates/saas/AGENTS.md +333 -0
  24. package/templates/saas/bun.lock +925 -0
  25. package/templates/saas/components.json +21 -0
  26. package/templates/saas/index.html +12 -0
  27. package/templates/saas/package.json +75 -0
  28. package/templates/saas/public/icon.png +0 -0
  29. package/templates/saas/public/og.png +0 -0
  30. package/templates/saas/schema.sql +73 -0
  31. package/templates/saas/src/auth.ts +77 -0
  32. package/templates/saas/src/client/App.tsx +63 -0
  33. package/templates/saas/src/client/components/ProtectedRoute.tsx +29 -0
  34. package/templates/saas/src/client/components/ThemeToggle.tsx +32 -0
  35. package/templates/saas/src/client/components/ui/accordion.tsx +62 -0
  36. package/templates/saas/src/client/components/ui/alert-dialog.tsx +133 -0
  37. package/templates/saas/src/client/components/ui/alert.tsx +60 -0
  38. package/templates/saas/src/client/components/ui/aspect-ratio.tsx +9 -0
  39. package/templates/saas/src/client/components/ui/avatar.tsx +39 -0
  40. package/templates/saas/src/client/components/ui/badge.tsx +39 -0
  41. package/templates/saas/src/client/components/ui/breadcrumb.tsx +102 -0
  42. package/templates/saas/src/client/components/ui/button-group.tsx +78 -0
  43. package/templates/saas/src/client/components/ui/button.tsx +60 -0
  44. package/templates/saas/src/client/components/ui/card.tsx +75 -0
  45. package/templates/saas/src/client/components/ui/carousel.tsx +228 -0
  46. package/templates/saas/src/client/components/ui/chart.tsx +326 -0
  47. package/templates/saas/src/client/components/ui/checkbox.tsx +29 -0
  48. package/templates/saas/src/client/components/ui/collapsible.tsx +19 -0
  49. package/templates/saas/src/client/components/ui/command.tsx +159 -0
  50. package/templates/saas/src/client/components/ui/context-menu.tsx +224 -0
  51. package/templates/saas/src/client/components/ui/dialog.tsx +127 -0
  52. package/templates/saas/src/client/components/ui/drawer.tsx +124 -0
  53. package/templates/saas/src/client/components/ui/dropdown-menu.tsx +226 -0
  54. package/templates/saas/src/client/components/ui/empty.tsx +94 -0
  55. package/templates/saas/src/client/components/ui/field.tsx +232 -0
  56. package/templates/saas/src/client/components/ui/form.tsx +152 -0
  57. package/templates/saas/src/client/components/ui/hover-card.tsx +38 -0
  58. package/templates/saas/src/client/components/ui/input-group.tsx +158 -0
  59. package/templates/saas/src/client/components/ui/input-otp.tsx +68 -0
  60. package/templates/saas/src/client/components/ui/input.tsx +21 -0
  61. package/templates/saas/src/client/components/ui/item.tsx +172 -0
  62. package/templates/saas/src/client/components/ui/kbd.tsx +28 -0
  63. package/templates/saas/src/client/components/ui/label.tsx +21 -0
  64. package/templates/saas/src/client/components/ui/menubar.tsx +250 -0
  65. package/templates/saas/src/client/components/ui/navigation-menu.tsx +161 -0
  66. package/templates/saas/src/client/components/ui/pagination.tsx +106 -0
  67. package/templates/saas/src/client/components/ui/popover.tsx +42 -0
  68. package/templates/saas/src/client/components/ui/progress.tsx +26 -0
  69. package/templates/saas/src/client/components/ui/radio-group.tsx +45 -0
  70. package/templates/saas/src/client/components/ui/resizable.tsx +46 -0
  71. package/templates/saas/src/client/components/ui/scroll-area.tsx +56 -0
  72. package/templates/saas/src/client/components/ui/select.tsx +173 -0
  73. package/templates/saas/src/client/components/ui/separator.tsx +28 -0
  74. package/templates/saas/src/client/components/ui/sheet.tsx +128 -0
  75. package/templates/saas/src/client/components/ui/sidebar.tsx +694 -0
  76. package/templates/saas/src/client/components/ui/skeleton.tsx +13 -0
  77. package/templates/saas/src/client/components/ui/slider.tsx +58 -0
  78. package/templates/saas/src/client/components/ui/sonner.tsx +38 -0
  79. package/templates/saas/src/client/components/ui/spinner.tsx +16 -0
  80. package/templates/saas/src/client/components/ui/switch.tsx +28 -0
  81. package/templates/saas/src/client/components/ui/table.tsx +90 -0
  82. package/templates/saas/src/client/components/ui/tabs.tsx +54 -0
  83. package/templates/saas/src/client/components/ui/textarea.tsx +18 -0
  84. package/templates/saas/src/client/components/ui/toggle-group.tsx +80 -0
  85. package/templates/saas/src/client/components/ui/toggle.tsx +44 -0
  86. package/templates/saas/src/client/components/ui/tooltip.tsx +57 -0
  87. package/templates/saas/src/client/hooks/use-mobile.ts +19 -0
  88. package/templates/saas/src/client/hooks/useAuth.ts +14 -0
  89. package/templates/saas/src/client/hooks/useSubscription.ts +86 -0
  90. package/templates/saas/src/client/index.css +165 -0
  91. package/templates/saas/src/client/lib/auth-client.ts +7 -0
  92. package/templates/saas/src/client/lib/plans.ts +82 -0
  93. package/templates/saas/src/client/lib/utils.ts +6 -0
  94. package/templates/saas/src/client/main.tsx +15 -0
  95. package/templates/saas/src/client/pages/DashboardPage.tsx +394 -0
  96. package/templates/saas/src/client/pages/ForgotPasswordPage.tsx +153 -0
  97. package/templates/saas/src/client/pages/HomePage.tsx +285 -0
  98. package/templates/saas/src/client/pages/LoginPage.tsx +169 -0
  99. package/templates/saas/src/client/pages/PricingPage.tsx +467 -0
  100. package/templates/saas/src/client/pages/ResetPasswordPage.tsx +200 -0
  101. package/templates/saas/src/client/pages/SignupPage.tsx +192 -0
  102. package/templates/saas/src/index.ts +208 -0
  103. package/templates/saas/tsconfig.json +18 -0
  104. package/templates/saas/vite.config.ts +14 -0
  105. package/templates/saas/wrangler.jsonc +20 -0
@@ -119,16 +119,18 @@ Templates can define hooks in `.jack.json` that run at specific lifecycle points
119
119
  | `url` | `url` | Prints label + URL |
120
120
  | `clipboard` | `text` | Prints text |
121
121
  | `pause` | _(none)_ | Skipped |
122
- | `require` | `source`, `key` | Validates, prints setup if provided |
122
+ | `require` | `source`, `key` | Validates, prints setup if provided. Supports `onMissing: "prompt" \| "generate"` |
123
123
  | `shell` | `command` | Runs with stdin ignored |
124
- | `prompt` | `message` | Skipped (supports `validate: "json" | "accountAssociation"`) |
124
+ | `prompt` | `message` | Skipped. Supports `secret: true` for masked input, `validate`, `writeJson`, `deployAfter` |
125
125
  | `writeJson` | `path`, `set` | Runs (safe in CI) |
126
+ | `stripe-setup` | `plans` | Creates Stripe products/prices, saves price IDs to secrets |
126
127
 
127
128
  ### Hook Lifecycle
128
129
 
129
130
  ```json
130
131
  {
131
132
  "hooks": {
133
+ "preCreate": [...], // During project creation (secret collection, auto-generation)
132
134
  "preDeploy": [...], // Before wrangler deploy (validation)
133
135
  "postDeploy": [...] // After successful deploy (notifications, testing)
134
136
  }
@@ -145,9 +147,10 @@ Templates can define hooks in `.jack.json` that run at specific lifecycle points
145
147
  | `clipboard` | Copy text to clipboard | `{"action": "clipboard", "text": "{{url}}", "message": "Copied!"}` |
146
148
  | `shell` | Execute shell command | `{"action": "shell", "command": "curl {{url}}/health"}` |
147
149
  | `pause` | Wait for Enter key | `{"action": "pause", "message": "Press Enter..."}` |
148
- | `require` | Verify secret or env | `{"action": "require", "source": "secret", "key": "API_KEY"}` |
149
- | `prompt` | Prompt for input and update JSON file | `{"action": "prompt", "message": "Paste JSON", "validate": "json", "successMessage": "Saved", "writeJson": {"path": "public/data.json", "set": {"data": {"from": "input"}}}}` |
150
+ | `require` | Verify secret/env, optionally prompt or generate | `{"action": "require", "source": "secret", "key": "API_KEY", "onMissing": "prompt"}` |
151
+ | `prompt` | Prompt for input, optionally masked | `{"action": "prompt", "message": "Secret:", "secret": true, "writeJson": {...}}` |
150
152
  | `writeJson` | Update JSON file with template vars | `{"action": "writeJson", "path": "public/data.json", "set": {"siteUrl": "{{url}}"}}` |
153
+ | `stripe-setup` | Create Stripe products/prices | `{"action": "stripe-setup", "plans": [{"name": "Pro", "priceKey": "STRIPE_PRO_PRICE_ID", "amount": 1900, "interval": "month"}]}` |
151
154
 
152
155
  ### Non-Interactive Mode
153
156
 
@@ -169,7 +172,7 @@ These variables are substituted at runtime (different from template placeholders
169
172
  |----------|-------|--------------|
170
173
  | `{{url}}` | Full deployed URL | postDeploy |
171
174
  | `{{domain}}` | Domain without protocol | postDeploy |
172
- | `{{name}}` | Project name | preDeploy, postDeploy |
175
+ | `{{name}}` | Project name | preCreate, preDeploy, postDeploy |
173
176
 
174
177
  ### Example: API Template Hooks
175
178
 
@@ -205,6 +208,170 @@ These variables are substituted at runtime (different from template placeholders
205
208
  }
206
209
  ```
207
210
 
211
+ ### Advanced Hook Features
212
+
213
+ These features support complex setup wizards (like the SaaS template with Stripe):
214
+
215
+ #### 1. `require` + `onMissing: "prompt" | "generate"`
216
+
217
+ The `require` action supports automatic secret collection when a secret is missing:
218
+
219
+ ```json
220
+ {
221
+ "action": "require",
222
+ "source": "secret",
223
+ "key": "STRIPE_SECRET_KEY",
224
+ "onMissing": "prompt",
225
+ "promptMessage": "Enter your Stripe Secret Key (sk_test_...):",
226
+ "setupUrl": "https://dashboard.stripe.com/apikeys"
227
+ }
228
+ ```
229
+
230
+ **Behavior:**
231
+ - If secret exists → continue (shows "Using saved KEY")
232
+ - If secret missing + interactive → prompt user, save to `.secrets.json`
233
+ - If secret missing + non-interactive → fail with setup instructions
234
+
235
+ **Auto-generate secrets with `onMissing: "generate"`:**
236
+
237
+ ```json
238
+ {
239
+ "action": "require",
240
+ "source": "secret",
241
+ "key": "BETTER_AUTH_SECRET",
242
+ "message": "Generating authentication secret...",
243
+ "onMissing": "generate",
244
+ "generateCommand": "openssl rand -base64 32"
245
+ }
246
+ ```
247
+
248
+ This runs the command, captures stdout, and saves it as the secret automatically.
249
+
250
+ #### 2. `stripe-setup` Action
251
+
252
+ Automatically creates Stripe products and prices, saving the price IDs as secrets:
253
+
254
+ ```json
255
+ {
256
+ "action": "stripe-setup",
257
+ "message": "Setting up Stripe subscription plans...",
258
+ "plans": [
259
+ {
260
+ "name": "Pro",
261
+ "priceKey": "STRIPE_PRO_PRICE_ID",
262
+ "amount": 1900,
263
+ "interval": "month",
264
+ "description": "Pro monthly subscription"
265
+ },
266
+ {
267
+ "name": "Enterprise",
268
+ "priceKey": "STRIPE_ENTERPRISE_PRICE_ID",
269
+ "amount": 9900,
270
+ "interval": "month"
271
+ }
272
+ ]
273
+ }
274
+ ```
275
+
276
+ **Behavior:**
277
+ - Requires `STRIPE_SECRET_KEY` to be set first
278
+ - Checks for existing prices by lookup key (`jack_pro_month`)
279
+ - Creates product + price if not found
280
+ - Saves price IDs to secrets
281
+
282
+ #### 3. `prompt` with `secret` Flag
283
+
284
+ Mask sensitive input (like API keys):
285
+
286
+ ```json
287
+ {
288
+ "action": "prompt",
289
+ "message": "Paste your webhook signing secret (whsec_...):",
290
+ "secret": true,
291
+ "writeJson": {
292
+ "path": ".secrets.json",
293
+ "set": { "STRIPE_WEBHOOK_SECRET": { "from": "input" } }
294
+ }
295
+ }
296
+ ```
297
+
298
+ #### 4. `prompt` with `deployAfter`
299
+
300
+ Automatically redeploy after user provides input:
301
+
302
+ ```json
303
+ {
304
+ "action": "prompt",
305
+ "message": "Paste webhook signing secret:",
306
+ "secret": true,
307
+ "deployAfter": true,
308
+ "deployMessage": "Deploying with webhook support...",
309
+ "writeJson": {
310
+ "path": ".secrets.json",
311
+ "set": { "STRIPE_WEBHOOK_SECRET": { "from": "input" } }
312
+ }
313
+ }
314
+ ```
315
+
316
+ ### Design Principles
317
+
318
+ When extending the hook system:
319
+
320
+ 1. **Extend existing actions** - prefer `require+onMissing` over a new `requireOrPrompt` action
321
+ 2. **Non-interactive fallback** - every interactive feature must degrade gracefully in CI/MCP
322
+ 3. **Secrets via `.secrets.json`** - use `writeJson` with `.secrets.json` for secret storage
323
+
324
+ ### Example: SaaS Template Setup Wizard
325
+
326
+ The `saas` template uses `preCreate` hooks for a complete setup wizard:
327
+
328
+ ```json
329
+ {
330
+ "hooks": {
331
+ "preCreate": [
332
+ {
333
+ "action": "require",
334
+ "source": "secret",
335
+ "key": "STRIPE_SECRET_KEY",
336
+ "message": "Stripe API key required for payments",
337
+ "setupUrl": "https://dashboard.stripe.com/apikeys",
338
+ "onMissing": "prompt",
339
+ "promptMessage": "Enter your Stripe Secret Key (sk_test_... or sk_live_...):"
340
+ },
341
+ {
342
+ "action": "require",
343
+ "source": "secret",
344
+ "key": "BETTER_AUTH_SECRET",
345
+ "message": "Generating authentication secret...",
346
+ "onMissing": "generate",
347
+ "generateCommand": "openssl rand -base64 32"
348
+ },
349
+ {
350
+ "action": "stripe-setup",
351
+ "message": "Setting up Stripe subscription plans...",
352
+ "plans": [
353
+ {"name": "Pro", "priceKey": "STRIPE_PRO_PRICE_ID", "amount": 1900, "interval": "month"},
354
+ {"name": "Enterprise", "priceKey": "STRIPE_ENTERPRISE_PRICE_ID", "amount": 9900, "interval": "month"}
355
+ ]
356
+ }
357
+ ],
358
+ "postDeploy": [
359
+ {"action": "box", "title": "Your SaaS is live!", "lines": ["{{url}}"]},
360
+ {"action": "clipboard", "text": "{{url}}/api/auth/stripe/webhook", "message": "Webhook URL copied"},
361
+ {"action": "prompt", "message": "Paste your webhook signing secret (whsec_...):", "secret": true, "deployAfter": true, "writeJson": {"path": ".secrets.json", "set": {"STRIPE_WEBHOOK_SECRET": {"from": "input"}}}}
362
+ ]
363
+ }
364
+ }
365
+ ```
366
+
367
+ This creates a guided wizard that:
368
+ 1. Prompts for Stripe key (with setup URL)
369
+ 2. Auto-generates auth secret
370
+ 3. Creates Stripe products/prices automatically
371
+ 4. Deploys the app
372
+ 5. Guides through webhook setup
373
+ 6. Re-deploys with webhook secret
374
+
208
375
  ## Farcaster Miniapp Embeds
209
376
 
210
377
  When a cast includes a URL, Farcaster scrapes it for `fc:miniapp` meta tags to render a rich embed.
@@ -38,9 +38,7 @@
38
38
  {
39
39
  "action": "box",
40
40
  "title": "Deployed: {{name}}",
41
- "lines": [
42
- "{{url}}"
43
- ]
41
+ "lines": ["{{url}}"]
44
42
  },
45
43
  {
46
44
  "action": "writeJson",
@@ -0,0 +1,154 @@
1
+ {
2
+ "name": "saas",
3
+ "description": "SaaS starter (Auth + Payments + React)",
4
+ "secrets": ["STRIPE_SECRET_KEY", "BETTER_AUTH_SECRET", "STRIPE_PRO_PRICE_ID", "STRIPE_ENTERPRISE_PRICE_ID"],
5
+ "capabilities": ["db"],
6
+ "requires": ["DB"],
7
+ "intent": {
8
+ "keywords": ["saas", "subscription", "auth", "payment", "stripe", "membership"],
9
+ "examples": ["subscription app", "paid membership site", "saas product"]
10
+ },
11
+ "agentContext": {
12
+ "summary": "A SaaS starter with Better Auth authentication, Stripe payments, React + Vite frontend, and Hono API on Cloudflare Workers with D1 SQLite database.",
13
+ "full_text": "## Project Structure\n\n- `src/index.ts` - Hono API entry point with auth and payment routes\n- `src/auth.ts` - Better Auth configuration with Stripe plugin\n- `src/client/App.tsx` - React application entry point\n- `src/client/lib/auth-client.ts` - Better Auth client\n- `src/client/hooks/` - useAuth, useSubscription hooks\n- `src/client/pages/` - Page components (HomePage, DashboardPage, etc.)\n- `src/client/components/ui/` - shadcn/ui components\n- `schema.sql` - D1 database schema (user, session, account, subscription)\n- `wrangler.jsonc` - Cloudflare Workers configuration\n\n## Authentication\n\nUses Better Auth with email/password. Auth routes are handled at `/api/auth/*`.\n\n### Client-side\n```tsx\nimport { authClient } from './lib/auth-client';\n\n// Sign up\nawait authClient.signUp.email({ email, password, name });\n\n// Sign in\nawait authClient.signIn.email({ email, password });\n\n// Get session\nconst { data: session } = await authClient.getSession();\n```\n\n## Payments (Stripe)\n\nUses Better Auth Stripe plugin for subscription management.\n\n### Upgrade Flow\n```tsx\nimport { authClient } from './lib/auth-client';\n\nasync function handleUpgrade(plan: 'pro' | 'enterprise') {\n const { data, error } = await authClient.subscription.upgrade({\n plan,\n successUrl: '/dashboard?upgraded=true',\n cancelUrl: '/pricing',\n });\n\n if (data?.url) {\n window.location.href = data.url; // Redirect to Stripe Checkout\n }\n}\n```\n\n### Check Subscription\n```tsx\nconst { data: subscriptions } = await authClient.subscription.list();\nconst activeSubscription = subscriptions?.find(s =>\n s.status === 'active' || s.status === 'trialing'\n);\nconst plan = activeSubscription?.plan || 'free';\n```\n\n## Webhook Setup\n\nStripe webhooks are handled at `/api/auth/stripe/webhook`. Required events:\n- `checkout.session.completed`\n- `customer.subscription.created`\n- `customer.subscription.updated`\n- `customer.subscription.deleted`\n\n## Database Schema\n\nUses Better Auth's default schema (singular table names, camelCase columns):\n- `user` - User accounts\n- `session` - Active sessions\n- `account` - OAuth/password credentials\n- `verification` - Email verification tokens\n- `subscription` - Stripe subscriptions\n\n## Environment Variables\n\n- `BETTER_AUTH_SECRET` - Random secret for auth tokens (generate with: openssl rand -base64 32)\n- `STRIPE_SECRET_KEY` - Stripe API secret key\n- `STRIPE_WEBHOOK_SECRET` - Stripe webhook signing secret (whsec_...)\n- `STRIPE_PRO_PRICE_ID` - Stripe price ID for Pro plan\n- `STRIPE_ENTERPRISE_PRICE_ID` - Stripe price ID for Enterprise plan\n\n## Resources\n\n- [Better Auth Docs](https://www.betterauth.com/docs)\n- [Better Auth Stripe Plugin](https://www.betterauth.com/docs/plugins/stripe)\n- [Stripe Subscriptions](https://docs.stripe.com/billing/subscriptions)\n- [Hono Documentation](https://hono.dev)\n- [Cloudflare D1 Docs](https://developers.cloudflare.com/d1)"
14
+ },
15
+ "hooks": {
16
+ "preCreate": [
17
+ {
18
+ "action": "require",
19
+ "source": "secret",
20
+ "key": "STRIPE_SECRET_KEY",
21
+ "message": "Stripe API key required for payments",
22
+ "setupUrl": "https://dashboard.stripe.com/apikeys",
23
+ "onMissing": "prompt",
24
+ "promptMessage": "Enter your Stripe Secret Key (sk_test_... or sk_live_...):"
25
+ },
26
+ {
27
+ "action": "require",
28
+ "source": "secret",
29
+ "key": "BETTER_AUTH_SECRET",
30
+ "message": "Generating authentication secret...",
31
+ "onMissing": "generate",
32
+ "generateCommand": "openssl rand -base64 32"
33
+ },
34
+ {
35
+ "action": "stripe-setup",
36
+ "message": "Setting up Stripe subscription plans...",
37
+ "plans": [
38
+ {
39
+ "name": "Pro",
40
+ "priceKey": "STRIPE_PRO_PRICE_ID",
41
+ "amount": 1900,
42
+ "interval": "month",
43
+ "description": "Pro monthly subscription"
44
+ },
45
+ {
46
+ "name": "Enterprise",
47
+ "priceKey": "STRIPE_ENTERPRISE_PRICE_ID",
48
+ "amount": 9900,
49
+ "interval": "month",
50
+ "description": "Enterprise monthly subscription"
51
+ }
52
+ ]
53
+ }
54
+ ],
55
+ "preDeploy": [
56
+ {
57
+ "action": "require",
58
+ "source": "secret",
59
+ "key": "STRIPE_SECRET_KEY"
60
+ }
61
+ ],
62
+ "postDeploy": [
63
+ {
64
+ "action": "box",
65
+ "title": "Your SaaS is live!",
66
+ "lines": ["{{url}}"]
67
+ },
68
+ {
69
+ "action": "clipboard",
70
+ "text": "{{url}}/api/auth/stripe/webhook",
71
+ "message": "Webhook URL copied to clipboard"
72
+ },
73
+ {
74
+ "action": "message",
75
+ "text": ""
76
+ },
77
+ {
78
+ "action": "message",
79
+ "text": "━━━ Stripe Webhook Setup ━━━"
80
+ },
81
+ {
82
+ "action": "message",
83
+ "text": ""
84
+ },
85
+ {
86
+ "action": "message",
87
+ "text": "1. Create webhook endpoint in Stripe"
88
+ },
89
+ {
90
+ "action": "message",
91
+ "text": "2. Set endpoint URL to: {{url}}/api/auth/stripe/webhook"
92
+ },
93
+ {
94
+ "action": "message",
95
+ "text": "3. Select these events:"
96
+ },
97
+ {
98
+ "action": "message",
99
+ "text": " • checkout.session.completed"
100
+ },
101
+ {
102
+ "action": "message",
103
+ "text": " • customer.subscription.created"
104
+ },
105
+ {
106
+ "action": "message",
107
+ "text": " • customer.subscription.updated"
108
+ },
109
+ {
110
+ "action": "message",
111
+ "text": " • customer.subscription.deleted"
112
+ },
113
+ {
114
+ "action": "message",
115
+ "text": ""
116
+ },
117
+ {
118
+ "action": "url",
119
+ "url": "https://dashboard.stripe.com/webhooks",
120
+ "label": "Open Stripe webhooks",
121
+ "prompt": true
122
+ },
123
+ {
124
+ "action": "prompt",
125
+ "message": "Paste your webhook signing secret (whsec_...):",
126
+ "required": false,
127
+ "secret": true,
128
+ "successMessage": "Webhook secret saved!",
129
+ "deployAfter": true,
130
+ "deployMessage": "Deploying with webhook support...",
131
+ "writeJson": {
132
+ "path": ".secrets.json",
133
+ "set": { "STRIPE_WEBHOOK_SECRET": { "from": "input" } }
134
+ }
135
+ },
136
+ {
137
+ "action": "message",
138
+ "text": ""
139
+ },
140
+ {
141
+ "action": "message",
142
+ "text": "━━━ Next Steps ━━━"
143
+ },
144
+ {
145
+ "action": "message",
146
+ "text": "• Test card: 4242 4242 4242 4242 (any future date, any CVC)"
147
+ },
148
+ {
149
+ "action": "message",
150
+ "text": "• Customize theme: https://ui.shadcn.com/create"
151
+ }
152
+ ]
153
+ }
154
+ }