@getjack/jack 0.1.32 → 0.1.34

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 (196) hide show
  1. package/package.json +1 -1
  2. package/src/commands/deploys.ts +95 -0
  3. package/src/commands/link.ts +8 -0
  4. package/src/commands/mcp.ts +179 -4
  5. package/src/commands/rollback.ts +53 -0
  6. package/src/commands/secrets.ts +3 -1
  7. package/src/commands/services.ts +11 -1
  8. package/src/commands/ship.ts +3 -1
  9. package/src/commands/tokens.ts +16 -1
  10. package/src/commands/whoami.ts +43 -8
  11. package/src/index.ts +16 -0
  12. package/src/lib/agent-files.ts +54 -4
  13. package/src/lib/agent-integration.ts +4 -166
  14. package/src/lib/claude-hooks-installer.ts +55 -0
  15. package/src/lib/control-plane.ts +78 -40
  16. package/src/lib/crypto.ts +84 -0
  17. package/src/lib/debug.ts +2 -1
  18. package/src/lib/deploy-upload.ts +13 -3
  19. package/src/lib/hooks.ts +4 -3
  20. package/src/lib/managed-deploy.ts +12 -9
  21. package/src/lib/project-link.ts +6 -0
  22. package/src/lib/project-operations.ts +92 -30
  23. package/src/lib/prompts.ts +2 -2
  24. package/src/lib/telemetry.ts +2 -0
  25. package/src/mcp/README.md +1 -1
  26. package/src/mcp/resources/index.ts +1 -16
  27. package/src/mcp/server.ts +23 -0
  28. package/src/mcp/tools/index.ts +133 -17
  29. package/src/mcp/types.ts +1 -0
  30. package/src/mcp/utils.ts +2 -1
  31. package/src/templates/index.ts +25 -73
  32. package/templates/CLAUDE.md +62 -0
  33. package/templates/ai-chat/.jack.json +10 -5
  34. package/templates/ai-chat/bun.lock +50 -1
  35. package/templates/ai-chat/package.json +5 -0
  36. package/templates/ai-chat/public/app.js +73 -0
  37. package/templates/ai-chat/public/index.html +14 -197
  38. package/templates/ai-chat/schema.sql +14 -0
  39. package/templates/ai-chat/src/index.ts +86 -102
  40. package/templates/ai-chat/wrangler.jsonc +8 -1
  41. package/templates/cron/.jack.json +66 -0
  42. package/templates/cron/bun.lock +23 -0
  43. package/templates/cron/package.json +16 -0
  44. package/templates/cron/schema.sql +24 -0
  45. package/templates/cron/src/index.ts +117 -0
  46. package/templates/cron/src/jobs.ts +139 -0
  47. package/templates/cron/src/webhooks.ts +95 -0
  48. package/templates/cron/tsconfig.json +17 -0
  49. package/templates/cron/wrangler.jsonc +11 -0
  50. package/templates/miniapp/.jack.json +1 -1
  51. package/templates/nextjs/.jack.json +1 -1
  52. package/templates/nextjs-auth/.jack.json +44 -0
  53. package/templates/nextjs-auth/app/api/auth/[...all]/route.ts +11 -0
  54. package/templates/nextjs-auth/app/dashboard/loading.tsx +53 -0
  55. package/templates/nextjs-auth/app/dashboard/page.tsx +73 -0
  56. package/templates/nextjs-auth/app/error.tsx +44 -0
  57. package/templates/nextjs-auth/app/globals.css +1 -0
  58. package/templates/nextjs-auth/app/health/route.ts +3 -0
  59. package/templates/nextjs-auth/app/layout.tsx +24 -0
  60. package/templates/nextjs-auth/app/login/page.tsx +10 -0
  61. package/templates/nextjs-auth/app/page.tsx +86 -0
  62. package/templates/nextjs-auth/app/signup/page.tsx +10 -0
  63. package/templates/nextjs-auth/bun.lock +1065 -0
  64. package/templates/nextjs-auth/cloudflare-env.d.ts +8 -0
  65. package/templates/nextjs-auth/components/auth-form.tsx +191 -0
  66. package/templates/nextjs-auth/components/header.tsx +50 -0
  67. package/templates/nextjs-auth/components/user-menu.tsx +23 -0
  68. package/templates/nextjs-auth/lib/auth-client.ts +3 -0
  69. package/templates/nextjs-auth/lib/auth.ts +43 -0
  70. package/templates/nextjs-auth/lib/utils.ts +6 -0
  71. package/templates/nextjs-auth/middleware.ts +33 -0
  72. package/templates/nextjs-auth/next.config.ts +8 -0
  73. package/templates/nextjs-auth/open-next.config.ts +6 -0
  74. package/templates/nextjs-auth/package.json +33 -0
  75. package/templates/nextjs-auth/postcss.config.mjs +8 -0
  76. package/templates/nextjs-auth/schema.sql +49 -0
  77. package/templates/nextjs-auth/tsconfig.json +28 -0
  78. package/templates/nextjs-auth/wrangler.jsonc +23 -0
  79. package/templates/nextjs-clerk/.jack.json +54 -0
  80. package/templates/nextjs-clerk/app/dashboard/page.tsx +69 -0
  81. package/templates/nextjs-clerk/app/globals.css +1 -0
  82. package/templates/nextjs-clerk/app/health/route.ts +3 -0
  83. package/templates/nextjs-clerk/app/layout.tsx +28 -0
  84. package/templates/nextjs-clerk/app/page.tsx +86 -0
  85. package/templates/nextjs-clerk/app/sign-in/[[...sign-in]]/page.tsx +9 -0
  86. package/templates/nextjs-clerk/app/sign-up/[[...sign-up]]/page.tsx +9 -0
  87. package/templates/nextjs-clerk/bun.lock +1055 -0
  88. package/templates/nextjs-clerk/cloudflare-env.d.ts +3 -0
  89. package/templates/nextjs-clerk/components/header.tsx +40 -0
  90. package/templates/nextjs-clerk/lib/utils.ts +6 -0
  91. package/templates/nextjs-clerk/middleware.ts +18 -0
  92. package/templates/nextjs-clerk/next.config.ts +8 -0
  93. package/templates/nextjs-clerk/open-next.config.ts +6 -0
  94. package/templates/nextjs-clerk/package.json +31 -0
  95. package/templates/nextjs-clerk/postcss.config.mjs +8 -0
  96. package/templates/nextjs-clerk/tsconfig.json +28 -0
  97. package/templates/nextjs-clerk/wrangler.jsonc +17 -0
  98. package/templates/nextjs-shadcn/.jack.json +34 -0
  99. package/templates/nextjs-shadcn/app/dashboard/data.json +614 -0
  100. package/templates/nextjs-shadcn/app/dashboard/page.tsx +55 -0
  101. package/templates/nextjs-shadcn/app/globals.css +126 -0
  102. package/templates/nextjs-shadcn/app/health/route.ts +3 -0
  103. package/templates/nextjs-shadcn/app/layout.tsx +24 -0
  104. package/templates/nextjs-shadcn/app/login/page.tsx +19 -0
  105. package/templates/nextjs-shadcn/app/page.tsx +180 -0
  106. package/templates/nextjs-shadcn/app/showcase.tsx +1262 -0
  107. package/templates/nextjs-shadcn/bun.lock +1789 -0
  108. package/templates/nextjs-shadcn/cloudflare-env.d.ts +4 -0
  109. package/templates/nextjs-shadcn/components/app-sidebar.tsx +175 -0
  110. package/templates/nextjs-shadcn/components/chart-area-interactive.tsx +291 -0
  111. package/templates/nextjs-shadcn/components/data-table.tsx +807 -0
  112. package/templates/nextjs-shadcn/components/login-form.tsx +95 -0
  113. package/templates/nextjs-shadcn/components/nav-documents.tsx +92 -0
  114. package/templates/nextjs-shadcn/components/nav-main.tsx +73 -0
  115. package/templates/nextjs-shadcn/components/nav-projects.tsx +89 -0
  116. package/templates/nextjs-shadcn/components/nav-secondary.tsx +42 -0
  117. package/templates/nextjs-shadcn/components/nav-user.tsx +114 -0
  118. package/templates/nextjs-shadcn/components/section-cards.tsx +102 -0
  119. package/templates/nextjs-shadcn/components/site-header.tsx +30 -0
  120. package/templates/nextjs-shadcn/components/team-switcher.tsx +91 -0
  121. package/templates/nextjs-shadcn/components/ui/accordion.tsx +66 -0
  122. package/templates/nextjs-shadcn/components/ui/alert-dialog.tsx +196 -0
  123. package/templates/nextjs-shadcn/components/ui/alert.tsx +66 -0
  124. package/templates/nextjs-shadcn/components/ui/aspect-ratio.tsx +11 -0
  125. package/templates/nextjs-shadcn/components/ui/avatar.tsx +109 -0
  126. package/templates/nextjs-shadcn/components/ui/badge.tsx +48 -0
  127. package/templates/nextjs-shadcn/components/ui/breadcrumb.tsx +109 -0
  128. package/templates/nextjs-shadcn/components/ui/button-group.tsx +83 -0
  129. package/templates/nextjs-shadcn/components/ui/button.tsx +64 -0
  130. package/templates/nextjs-shadcn/components/ui/calendar.tsx +220 -0
  131. package/templates/nextjs-shadcn/components/ui/card.tsx +92 -0
  132. package/templates/nextjs-shadcn/components/ui/carousel.tsx +241 -0
  133. package/templates/nextjs-shadcn/components/ui/chart.tsx +357 -0
  134. package/templates/nextjs-shadcn/components/ui/checkbox.tsx +32 -0
  135. package/templates/nextjs-shadcn/components/ui/collapsible.tsx +33 -0
  136. package/templates/nextjs-shadcn/components/ui/combobox.tsx +310 -0
  137. package/templates/nextjs-shadcn/components/ui/command.tsx +184 -0
  138. package/templates/nextjs-shadcn/components/ui/context-menu.tsx +252 -0
  139. package/templates/nextjs-shadcn/components/ui/dialog.tsx +158 -0
  140. package/templates/nextjs-shadcn/components/ui/direction.tsx +22 -0
  141. package/templates/nextjs-shadcn/components/ui/drawer.tsx +135 -0
  142. package/templates/nextjs-shadcn/components/ui/dropdown-menu.tsx +257 -0
  143. package/templates/nextjs-shadcn/components/ui/empty.tsx +104 -0
  144. package/templates/nextjs-shadcn/components/ui/field.tsx +248 -0
  145. package/templates/nextjs-shadcn/components/ui/form.tsx +167 -0
  146. package/templates/nextjs-shadcn/components/ui/hover-card.tsx +44 -0
  147. package/templates/nextjs-shadcn/components/ui/input-group.tsx +170 -0
  148. package/templates/nextjs-shadcn/components/ui/input-otp.tsx +77 -0
  149. package/templates/nextjs-shadcn/components/ui/input.tsx +21 -0
  150. package/templates/nextjs-shadcn/components/ui/item.tsx +193 -0
  151. package/templates/nextjs-shadcn/components/ui/kbd.tsx +28 -0
  152. package/templates/nextjs-shadcn/components/ui/label.tsx +24 -0
  153. package/templates/nextjs-shadcn/components/ui/menubar.tsx +276 -0
  154. package/templates/nextjs-shadcn/components/ui/native-select.tsx +53 -0
  155. package/templates/nextjs-shadcn/components/ui/navigation-menu.tsx +168 -0
  156. package/templates/nextjs-shadcn/components/ui/pagination.tsx +127 -0
  157. package/templates/nextjs-shadcn/components/ui/popover.tsx +89 -0
  158. package/templates/nextjs-shadcn/components/ui/progress.tsx +31 -0
  159. package/templates/nextjs-shadcn/components/ui/radio-group.tsx +45 -0
  160. package/templates/nextjs-shadcn/components/ui/resizable.tsx +53 -0
  161. package/templates/nextjs-shadcn/components/ui/scroll-area.tsx +58 -0
  162. package/templates/nextjs-shadcn/components/ui/select.tsx +190 -0
  163. package/templates/nextjs-shadcn/components/ui/separator.tsx +28 -0
  164. package/templates/nextjs-shadcn/components/ui/sheet.tsx +143 -0
  165. package/templates/nextjs-shadcn/components/ui/sidebar.tsx +726 -0
  166. package/templates/nextjs-shadcn/components/ui/skeleton.tsx +13 -0
  167. package/templates/nextjs-shadcn/components/ui/slider.tsx +63 -0
  168. package/templates/nextjs-shadcn/components/ui/sonner.tsx +40 -0
  169. package/templates/nextjs-shadcn/components/ui/spinner.tsx +16 -0
  170. package/templates/nextjs-shadcn/components/ui/switch.tsx +35 -0
  171. package/templates/nextjs-shadcn/components/ui/table.tsx +116 -0
  172. package/templates/nextjs-shadcn/components/ui/tabs.tsx +91 -0
  173. package/templates/nextjs-shadcn/components/ui/textarea.tsx +18 -0
  174. package/templates/nextjs-shadcn/components/ui/toggle-group.tsx +83 -0
  175. package/templates/nextjs-shadcn/components/ui/toggle.tsx +47 -0
  176. package/templates/nextjs-shadcn/components/ui/tooltip.tsx +57 -0
  177. package/templates/nextjs-shadcn/components.json +23 -0
  178. package/templates/nextjs-shadcn/hooks/use-mobile.ts +19 -0
  179. package/templates/nextjs-shadcn/lib/utils.ts +6 -0
  180. package/templates/nextjs-shadcn/next-env.d.ts +6 -0
  181. package/templates/nextjs-shadcn/next.config.ts +8 -0
  182. package/templates/nextjs-shadcn/open-next.config.ts +6 -0
  183. package/templates/nextjs-shadcn/package.json +55 -0
  184. package/templates/nextjs-shadcn/postcss.config.mjs +8 -0
  185. package/templates/nextjs-shadcn/tsconfig.json +28 -0
  186. package/templates/nextjs-shadcn/wrangler.jsonc +23 -0
  187. package/templates/resend/.jack.json +64 -0
  188. package/templates/resend/bun.lock +23 -0
  189. package/templates/resend/package.json +16 -0
  190. package/templates/resend/schema.sql +13 -0
  191. package/templates/resend/src/email.ts +165 -0
  192. package/templates/resend/src/index.ts +108 -0
  193. package/templates/resend/tsconfig.json +17 -0
  194. package/templates/resend/wrangler.jsonc +11 -0
  195. package/templates/saas/.jack.json +1 -1
  196. package/templates/ai-chat/public/chat.js +0 -149
@@ -7,6 +7,10 @@ import { JackError, JackErrorCode } from "../../lib/errors.ts";
7
7
  import { getDeployMode, getProjectId } from "../../lib/project-link.ts";
8
8
  import { createProject, deployProject, getProjectStatus } from "../../lib/project-operations.ts";
9
9
  import { listAllProjects } from "../../lib/project-resolver.ts";
10
+ import { createCronSchedule } from "../../lib/services/cron-create.ts";
11
+ import { deleteCronSchedule } from "../../lib/services/cron-delete.ts";
12
+ import { listCronSchedules } from "../../lib/services/cron-list.ts";
13
+ import { testCronExpression } from "../../lib/services/cron-test.ts";
10
14
  import { createDatabase } from "../../lib/services/db-create.ts";
11
15
  import {
12
16
  DestructiveOperationError,
@@ -15,10 +19,6 @@ import {
15
19
  wrapResultsForMcp,
16
20
  } from "../../lib/services/db-execute.ts";
17
21
  import { listDatabases } from "../../lib/services/db-list.ts";
18
- import { createCronSchedule } from "../../lib/services/cron-create.ts";
19
- import { deleteCronSchedule } from "../../lib/services/cron-delete.ts";
20
- import { listCronSchedules } from "../../lib/services/cron-list.ts";
21
- import { testCronExpression } from "../../lib/services/cron-test.ts";
22
22
  import {
23
23
  assignDomain,
24
24
  connectDomain,
@@ -41,7 +41,12 @@ import { formatErrorResponse, formatSuccessResponse } from "../utils.ts";
41
41
  // Tool schemas
42
42
  const CreateProjectSchema = z.object({
43
43
  name: z.string().optional().describe("Project name (auto-generated if not provided)"),
44
- template: z.string().optional().describe("Template to use (e.g., 'miniapp', 'api')"),
44
+ template: z
45
+ .string()
46
+ .optional()
47
+ .describe(
48
+ "Template to use (e.g., 'miniapp', 'api'). Also supports forking: use 'username/slug' for published projects or 'my-project' to fork your own.",
49
+ ),
45
50
  });
46
51
 
47
52
  const DeployProjectSchema = z.object({
@@ -49,6 +54,12 @@ const DeployProjectSchema = z.object({
49
54
  .string()
50
55
  .optional()
51
56
  .describe("Path to project directory (defaults to current directory)"),
57
+ message: z
58
+ .string()
59
+ .optional()
60
+ .describe(
61
+ "Deploy message describing what changed and why (e.g., 'Add user auth', 'Fix CORS bug')",
62
+ ),
52
63
  });
53
64
 
54
65
  const GetProjectStatusSchema = z.object({
@@ -200,6 +211,19 @@ const TailLogsSchema = z.object({
200
211
  .describe("How long to listen before returning (default: 2000ms, max: 10000ms)"),
201
212
  });
202
213
 
214
+ const RollbackProjectSchema = z.object({
215
+ deployment_id: z
216
+ .string()
217
+ .optional()
218
+ .describe(
219
+ "Specific deployment ID to roll back to (defaults to previous successful deployment)",
220
+ ),
221
+ project_path: z
222
+ .string()
223
+ .optional()
224
+ .describe("Path to project directory (defaults to current directory)"),
225
+ });
226
+
203
227
  const ListDomainsSchema = z.object({});
204
228
 
205
229
  const ConnectDomainSchema = z.object({
@@ -220,7 +244,9 @@ const DisconnectDomainSchema = z.object({
220
244
  });
221
245
 
222
246
  const CreateCronSchema = z.object({
223
- expression: z.string().describe("Cron expression (e.g., '0 * * * *' for hourly, '*/15 * * * *' for every 15 min)"),
247
+ expression: z
248
+ .string()
249
+ .describe("Cron expression (e.g., '0 * * * *' for hourly, '*/15 * * * *' for every 15 min)"),
224
250
  project_path: z
225
251
  .string()
226
252
  .optional()
@@ -264,7 +290,7 @@ export function registerTools(server: McpServer, _options: McpServerOptions, deb
264
290
  {
265
291
  name: "create_project",
266
292
  description:
267
- "Create a new Cloudflare Workers project from a template. Automatically installs dependencies, deploys to Cloudflare, and registers the project.",
293
+ "Create a new project from a template. Automatically installs dependencies, deploys, and registers the project. Also supports forking: pass a 'username/slug' template to fork a published project, or a project slug to fork your own.",
268
294
  inputSchema: {
269
295
  type: "object",
270
296
  properties: {
@@ -274,7 +300,8 @@ export function registerTools(server: McpServer, _options: McpServerOptions, deb
274
300
  },
275
301
  template: {
276
302
  type: "string",
277
- description: "Template to use (e.g., 'miniapp', 'api')",
303
+ description:
304
+ "Template to use (e.g., 'miniapp', 'api'). Also supports forking: use 'username/slug' for published projects or 'my-project' to fork your own.",
278
305
  },
279
306
  },
280
307
  },
@@ -282,7 +309,7 @@ export function registerTools(server: McpServer, _options: McpServerOptions, deb
282
309
  {
283
310
  name: "deploy_project",
284
311
  description:
285
- "Deploy an existing project to Cloudflare Workers. Builds the project if needed and pushes to production.",
312
+ "Deploy an existing project. Builds if needed and pushes to production. Always provide a 'message' describing what changed and why (e.g., 'Add user auth', 'Fix CORS bug').",
286
313
  inputSchema: {
287
314
  type: "object",
288
315
  properties: {
@@ -290,13 +317,18 @@ export function registerTools(server: McpServer, _options: McpServerOptions, deb
290
317
  type: "string",
291
318
  description: "Path to project directory (defaults to current directory)",
292
319
  },
320
+ message: {
321
+ type: "string",
322
+ description:
323
+ "Deploy message describing what changed and why (e.g., 'Add user auth', 'Fix CORS bug')",
324
+ },
293
325
  },
294
326
  },
295
327
  },
296
328
  {
297
329
  name: "get_project_status",
298
330
  description:
299
- "Get detailed status information for a specific project, including deployment status, local path, and backup status.",
331
+ "Get live deployment state: URL, last deploy time, deploy count, status (live/failed), and deploy source. Call this first to understand what's currently deployed before making changes.",
300
332
  inputSchema: {
301
333
  type: "object",
302
334
  properties: {
@@ -347,7 +379,7 @@ export function registerTools(server: McpServer, _options: McpServerOptions, deb
347
379
  {
348
380
  name: "tail_logs",
349
381
  description:
350
- "Collect a short sample of live log events (JSON) from a managed (jack cloud) project. Useful for agentic debugging.",
382
+ "Collect live log events from production. Use after deploying to verify changes work, or to debug errors. Returns JSON log entries with timestamps and messages.",
351
383
  inputSchema: {
352
384
  type: "object",
353
385
  properties: {
@@ -370,6 +402,27 @@ export function registerTools(server: McpServer, _options: McpServerOptions, deb
370
402
  },
371
403
  },
372
404
  },
405
+ {
406
+ name: "rollback_project",
407
+ description:
408
+ "Roll back a managed (jack cloud) project to a previous deployment. " +
409
+ "Defaults to the previous successful deployment if no deployment_id is specified. " +
410
+ "Only rolls back code — database state and secrets are unchanged.",
411
+ inputSchema: {
412
+ type: "object",
413
+ properties: {
414
+ deployment_id: {
415
+ type: "string",
416
+ description:
417
+ "Specific deployment ID to roll back to (defaults to previous successful deployment)",
418
+ },
419
+ project_path: {
420
+ type: "string",
421
+ description: "Path to project directory (defaults to current directory)",
422
+ },
423
+ },
424
+ },
425
+ },
373
426
  {
374
427
  name: "create_database",
375
428
  description:
@@ -654,13 +707,14 @@ export function registerTools(server: McpServer, _options: McpServerOptions, deb
654
707
  {
655
708
  name: "create_cron",
656
709
  description:
657
- "Create a cron schedule for a managed (Jack Cloud) project. Minimum interval is 15 minutes. The worker must have a scheduled() handler or POST /__scheduled route.",
710
+ "Create a cron schedule for a managed (Jack Cloud) project. Minimum interval is 15 minutes. The worker must handle POST /__scheduled requests — Cloudflare's native scheduled() export does not work with Jack Cloud crons.",
658
711
  inputSchema: {
659
712
  type: "object",
660
713
  properties: {
661
714
  expression: {
662
715
  type: "string",
663
- description: "Cron expression (e.g., '0 * * * *' for hourly, '*/15 * * * *' for every 15 min)",
716
+ description:
717
+ "Cron expression (e.g., '0 * * * *' for hourly, '*/15 * * * *' for every 15 min)",
664
718
  },
665
719
  project_path: {
666
720
  type: "string",
@@ -719,7 +773,8 @@ export function registerTools(server: McpServer, _options: McpServerOptions, deb
719
773
  trigger_production: {
720
774
  type: "boolean",
721
775
  default: false,
722
- description: "Whether to trigger the cron handler on production (requires managed project)",
776
+ description:
777
+ "Whether to trigger the cron handler on production (requires managed project)",
723
778
  },
724
779
  },
725
780
  required: ["expression"],
@@ -777,12 +832,13 @@ export function registerTools(server: McpServer, _options: McpServerOptions, deb
777
832
 
778
833
  const wrappedDeployProject = withTelemetry(
779
834
  "deploy_project",
780
- async (projectPath?: string) => {
835
+ async (projectPath?: string, message?: string) => {
781
836
  const result = await deployProject({
782
837
  projectPath,
783
838
  interactive: false,
784
839
  includeSecrets: false,
785
840
  includeSync: false,
841
+ message,
786
842
  });
787
843
 
788
844
  // Track business event
@@ -795,7 +851,7 @@ export function registerTools(server: McpServer, _options: McpServerOptions, deb
795
851
  { platform: "mcp" },
796
852
  );
797
853
 
798
- const result = await wrappedDeployProject(args.project_path);
854
+ const result = await wrappedDeployProject(args.project_path, args.message);
799
855
 
800
856
  return {
801
857
  content: [
@@ -1046,6 +1102,60 @@ export function registerTools(server: McpServer, _options: McpServerOptions, deb
1046
1102
  };
1047
1103
  }
1048
1104
 
1105
+ case "rollback_project": {
1106
+ const args = RollbackProjectSchema.parse(request.params.arguments ?? {});
1107
+ const projectPath = args.project_path ?? process.cwd();
1108
+
1109
+ const deployMode = await getDeployMode(projectPath);
1110
+ if (deployMode !== "managed") {
1111
+ throw new JackError(
1112
+ JackErrorCode.VALIDATION_ERROR,
1113
+ "Rollback is only available for managed (jack cloud) projects",
1114
+ "For BYOC projects, use wrangler to manage deployments.",
1115
+ );
1116
+ }
1117
+
1118
+ const projectId = await getProjectId(projectPath);
1119
+ if (!projectId) {
1120
+ throw new JackError(
1121
+ JackErrorCode.PROJECT_NOT_FOUND,
1122
+ "Project not found",
1123
+ "Run this from a linked jack cloud project directory (has .jack/project.json).",
1124
+ );
1125
+ }
1126
+
1127
+ // Import rollbackDeployment from control-plane
1128
+ const { rollbackDeployment } = await import("../../lib/control-plane.ts");
1129
+
1130
+ const wrappedRollback = withTelemetry(
1131
+ "rollback_project",
1132
+ async (id: string, deploymentId?: string) => rollbackDeployment(id, deploymentId),
1133
+ { platform: "mcp" },
1134
+ );
1135
+
1136
+ const result = await wrappedRollback(projectId, args.deployment_id);
1137
+
1138
+ return {
1139
+ content: [
1140
+ {
1141
+ type: "text",
1142
+ text: JSON.stringify(
1143
+ formatSuccessResponse(
1144
+ {
1145
+ ...result.deployment,
1146
+ message:
1147
+ "Code rolled back successfully. Database state and secrets are unchanged.",
1148
+ },
1149
+ startTime,
1150
+ ),
1151
+ null,
1152
+ 2,
1153
+ ),
1154
+ },
1155
+ ],
1156
+ };
1157
+ }
1158
+
1049
1159
  case "create_database": {
1050
1160
  const args = CreateDatabaseSchema.parse(request.params.arguments ?? {});
1051
1161
  const projectPath = args.project_path ?? process.cwd();
@@ -1268,7 +1378,13 @@ export function registerTools(server: McpServer, _options: McpServerOptions, deb
1268
1378
  content: [
1269
1379
  {
1270
1380
  type: "text",
1271
- text: JSON.stringify(formatSuccessResponse(result, startTime), null, 2),
1381
+ text: JSON.stringify(
1382
+ formatSuccessResponse(result, startTime, [
1383
+ "Vectorize indexes have eventual consistency. Newly inserted vectors typically take 2-3 minutes to become queryable.",
1384
+ ]),
1385
+ null,
1386
+ 2,
1387
+ ),
1272
1388
  },
1273
1389
  ],
1274
1390
  };
package/src/mcp/types.ts CHANGED
@@ -5,6 +5,7 @@
5
5
  export interface McpToolResponse<T = unknown> {
6
6
  success: boolean;
7
7
  data?: T;
8
+ notes?: string[]; // Situational context for AI agents (e.g. eventual consistency caveats)
8
9
  error?: {
9
10
  code: string; // Machine-readable: 'AUTH_FAILED', 'PROJECT_NOT_FOUND'
10
11
  message: string; // Human-readable description
package/src/mcp/utils.ts CHANGED
@@ -5,10 +5,11 @@ import { McpErrorCode, type McpToolResponse } from "./types.ts";
5
5
  /**
6
6
  * Format a successful MCP tool response
7
7
  */
8
- export function formatSuccessResponse<T>(data: T, startTime: number): McpToolResponse<T> {
8
+ export function formatSuccessResponse<T>(data: T, startTime: number, notes?: string[]): McpToolResponse<T> {
9
9
  return {
10
10
  success: true,
11
11
  data,
12
+ ...(notes?.length && { notes }),
12
13
  meta: {
13
14
  duration_ms: Date.now() - startTime,
14
15
  jack_version: packageJson.version,
@@ -14,11 +14,15 @@ export const BUILTIN_TEMPLATES = [
14
14
  "hello",
15
15
  "miniapp",
16
16
  "api",
17
+ "cron",
18
+ "resend",
17
19
  "nextjs",
18
20
  "saas",
19
- "simple-api-starter",
20
21
  "ai-chat",
21
22
  "semantic-search",
23
+ "nextjs-shadcn",
24
+ "nextjs-clerk",
25
+ "nextjs-auth",
22
26
  ];
23
27
 
24
28
  /**
@@ -211,64 +215,32 @@ function extractMetadataFromZip(zipData: ArrayBuffer): Record<string, unknown> {
211
215
  }
212
216
 
213
217
  /**
214
- * Fetch a published template from jack cloud (public endpoint, no auth)
218
+ * Fetch a remote template (own project or published).
219
+ * Both paths require auth.
215
220
  */
216
- async function fetchPublishedTemplate(username: string, slug: string): Promise<Template> {
217
- const response = await fetch(
218
- `${getControlApiUrl()}/v1/projects/${encodeURIComponent(username)}/${encodeURIComponent(slug)}/source`,
219
- );
220
-
221
- if (!response.ok) {
222
- if (response.status === 404) {
223
- throw new Error(
224
- `Template not found: ${username}/${slug}\n\nMake sure the project exists and is published with: jack publish`,
225
- );
226
- }
227
- throw new Error(`Failed to fetch template: ${response.status}`);
228
- }
229
-
230
- const zipData = await response.arrayBuffer();
231
- const metadata = extractMetadataFromZip(zipData);
232
- const files = extractZipToFiles(zipData);
233
-
234
- return {
235
- description: (metadata.description as string) || `Fork of ${username}/${slug}`,
236
- secrets: (metadata.secrets as string[]) || [],
237
- optionalSecrets: metadata.optionalSecrets as Template["optionalSecrets"],
238
- envVars: metadata.envVars as Template["envVars"],
239
- capabilities: metadata.capabilities as Template["capabilities"],
240
- requires: metadata.requires as Template["requires"],
241
- hooks: metadata.hooks as Template["hooks"],
242
- agentContext: metadata.agentContext as Template["agentContext"],
243
- intent: metadata.intent as Template["intent"],
244
- files,
245
- };
246
- }
247
-
248
- /**
249
- * Fetch user's own project as a template (authenticated)
250
- */
251
- async function fetchUserTemplate(slug: string): Promise<Template | null> {
221
+ async function fetchRemoteTemplate(identifier: string): Promise<Template | null> {
252
222
  const { authFetch } = await import("../lib/auth/index.ts");
253
223
 
254
- const response = await authFetch(
255
- `${getControlApiUrl()}/v1/me/projects/${encodeURIComponent(slug)}/source`,
256
- );
257
-
258
- if (response.status === 404) {
259
- return null; // Not found, will try other sources
224
+ // Route to the correct endpoint based on format
225
+ let url: string;
226
+ if (identifier.includes("/")) {
227
+ const [owner, slug] = identifier.split("/", 2) as [string, string];
228
+ url = `${getControlApiUrl()}/v1/projects/${encodeURIComponent(owner)}/${encodeURIComponent(slug)}/source`;
229
+ } else {
230
+ url = `${getControlApiUrl()}/v1/me/projects/${encodeURIComponent(identifier)}/source`;
260
231
  }
261
232
 
262
- if (!response.ok) {
263
- throw new Error(`Failed to fetch your project: ${response.status}`);
264
- }
233
+ const response = await authFetch(url);
234
+
235
+ if (response.status === 404) return null;
236
+ if (!response.ok) throw new Error(`Failed to fetch template: ${response.status}`);
265
237
 
266
238
  const zipData = await response.arrayBuffer();
267
239
  const metadata = extractMetadataFromZip(zipData);
268
240
  const files = extractZipToFiles(zipData);
269
241
 
270
242
  return {
271
- description: (metadata.description as string) || `Your project: ${slug}`,
243
+ description: (metadata.description as string) || `Fork of ${identifier}`,
272
244
  secrets: (metadata.secrets as string[]) || [],
273
245
  optionalSecrets: metadata.optionalSecrets as Template["optionalSecrets"],
274
246
  envVars: metadata.envVars as Template["envVars"],
@@ -285,33 +257,13 @@ async function fetchUserTemplate(slug: string): Promise<Template | null> {
285
257
  * Resolve template by name
286
258
  */
287
259
  export async function resolveTemplate(template?: string): Promise<Template> {
288
- // No template hello (omakase default)
289
- if (!template) {
290
- return loadTemplate("hello");
291
- }
260
+ if (!template) return loadTemplate("hello");
261
+ if (BUILTIN_TEMPLATES.includes(template)) return loadTemplate(template);
292
262
 
293
- // Built-in template
294
- if (BUILTIN_TEMPLATES.includes(template)) {
295
- return loadTemplate(template);
296
- }
297
-
298
- // username/slug format - fetch from jack cloud
299
- if (template.includes("/")) {
300
- const [username, slug] = template.split("/", 2) as [string, string];
301
- return fetchPublishedTemplate(username, slug);
302
- }
303
-
304
- // Try as user's own project first
305
- try {
306
- const userTemplate = await fetchUserTemplate(template);
307
- if (userTemplate) {
308
- return userTemplate;
309
- }
310
- } catch (_err) {
311
- // If auth fails or project not found, fall through to error
312
- }
263
+ // Remote: "username/slug" or "my-own-project"
264
+ const result = await fetchRemoteTemplate(template);
265
+ if (result) return result;
313
266
 
314
- // Unknown template
315
267
  throw new Error(
316
268
  `Unknown template: ${template}\n\nAvailable built-in templates: ${BUILTIN_TEMPLATES.join(", ")}\nOr use username/slug format for published projects`,
317
269
  );
@@ -114,6 +114,27 @@ Before shipping a new template:
114
114
  - [ ] Install time < 5s (cold cache + lockfile)
115
115
  - [ ] All scripts work without global tools except wrangler
116
116
  - [ ] `.gitignore` includes `.env`, `.dev.vars`, `.secrets.json`
117
+ - [ ] Prebuilt builds without env vars: `bun run build` succeeds with zero secrets set
118
+ - [ ] If template has `schema.sql`, verify it's included in prebuild output
119
+
120
+ ## Prebuild Compatibility (IMPORTANT)
121
+
122
+ Templates are prebuilt at release time **without any env vars or secrets**. The prebuild runs `bun run build` in a clean environment. If the build depends on runtime env vars, it will fail silently (only discovered when someone runs `prebuild-templates.ts`).
123
+
124
+ **Common failure:** A provider component (e.g., `ClerkProvider`, `AuthProvider`) in the root layout requires an API key at import time. Next.js tries to statically prerender pages and the provider throws because the key is missing.
125
+
126
+ **Fix:** Add `export const dynamic = "force-dynamic"` to the root layout. This tells Next.js to skip static prerendering for all pages, deferring to runtime where env vars are available.
127
+
128
+ ```tsx
129
+ // app/layout.tsx
130
+ export const dynamic = "force-dynamic"; // Required: provider needs runtime env vars
131
+
132
+ export default function RootLayout({ children }) {
133
+ return <SomeProvider>{children}</SomeProvider>;
134
+ }
135
+ ```
136
+
137
+ **General rule:** If a template imports any SDK/provider that reads `process.env` or `env.*` at initialization, that template's layout or pages must be force-dynamic to survive a zero-env-var prebuild.
117
138
 
118
139
  ## Placeholder System
119
140
 
@@ -568,6 +589,47 @@ if (!baseUrl) {
568
589
 
569
590
  The Host header approach is reliable because Cloudflare always sets it to the actual domain being accessed.
570
591
 
592
+ ## OpenNext (Next.js on Cloudflare) Gotchas
593
+
594
+ Templates using Next.js via `@opennextjs/cloudflare` have platform-specific limitations. These apply to all Next.js templates (`nextjs-auth`, `nextjs-shadcn`, `nextjs-clerk`, etc.).
595
+
596
+ ### Client-side navigation after auth state changes: use window.location, not router.push
597
+
598
+ OpenNext's webpack runtime has an empty chunk URL resolver (`r.u=e=>{}`). This means `router.push()` to a page whose chunks aren't already loaded fails with `ChunkLoadError: Loading chunk X failed`.
599
+
600
+ **Rule:** After any auth state change (sign-in, sign-up, sign-out), always use `window.location.href` for a full page reload. This ensures middleware, server components, and cookies re-evaluate with the new session.
601
+
602
+ ```tsx
603
+ // BAD — ChunkLoadError on OpenNext
604
+ await authClient.signOut();
605
+ router.push("/");
606
+ router.refresh();
607
+
608
+ // GOOD — full reload, clean auth state
609
+ await authClient.signOut();
610
+ window.location.href = "/";
611
+ ```
612
+
613
+ `<Link>` components work fine for normal navigation because Next.js prefetches their chunks via `<script>` tags in the HTML.
614
+
615
+ ### Pages using getCloudflareContext() need `export const dynamic = 'force-dynamic'`
616
+
617
+ Without this, Next.js tries to statically prerender the page at build time, which fails because `getCloudflareContext()` is only available at runtime in the Workers environment.
618
+
619
+ ```tsx
620
+ import { getAuth } from "@/lib/auth";
621
+ export const dynamic = "force-dynamic";
622
+
623
+ export default async function DashboardPage() {
624
+ const auth = await getAuth(); // calls getCloudflareContext() internally
625
+ // ...
626
+ }
627
+ ```
628
+
629
+ ### Edge middleware cannot use Node.js APIs
630
+
631
+ Next.js middleware runs in the edge runtime. Don't call `auth.api.getSession()` (requires `perf_hooks`) — use cookie-only checks instead.
632
+
571
633
  ## Adding New Templates
572
634
 
573
635
  1. Create directory: `templates/my-template/`
@@ -1,11 +1,16 @@
1
1
  {
2
2
  "name": "ai-chat",
3
- "description": "AI chat with streaming UI (free Cloudflare AI)",
3
+ "description": "AI chat with streaming, persistence, and multi-model support",
4
4
  "secrets": [],
5
- "capabilities": ["ai"],
5
+ "capabilities": ["ai", "db"],
6
+ "requires": ["DB"],
6
7
  "intent": {
7
- "keywords": ["ai", "chat", "llm", "llama", "completion", "chatbot"],
8
- "examples": ["AI chatbot", "chat interface", "LLM chat app"]
8
+ "keywords": ["ai", "chat", "llm", "llama", "completion", "chatbot", "streaming"],
9
+ "examples": ["AI chatbot", "chat interface", "LLM chat app", "persistent chat"]
10
+ },
11
+ "agentContext": {
12
+ "summary": "An AI chat app with streaming UI, D1 chat persistence, and multi-model support via AI SDK.",
13
+ "full_text": "## Project Structure\n\n- `src/index.ts` - Hono API with chat endpoints and AI SDK integration\n- `src/jack-ai.ts` - Jack AI proxy wrapper (do not modify)\n- `public/index.html` - React app shell\n- `public/app.js` - Chat UI with useChat hook from AI SDK\n- `schema.sql` - D1 schema for chat and message persistence\n\n## AI Integration\n\nUses AI SDK with `workers-ai-provider` routed through Jack's metered AI proxy.\n\n### Changing Models\n\nEdit the model string in `src/index.ts`:\n```typescript\nmodel: provider('@cf/meta/llama-3.3-70b-instruct-fp8-fast')\n```\n\nAvailable models: https://developers.cloudflare.com/workers-ai/models/\n\n## Chat Persistence\n\nChats and messages are stored in D1:\n- `chats` table: chat metadata (id, created_at)\n- `messages` table: individual messages (role, content, chat_id)\n\nMessages persist across page refreshes. Each browser session creates a new chat.\n\n## API Endpoints\n\n- `POST /api/chat` - Send message and get streaming AI response\n- `GET /api/chat/:id` - Load chat history\n- `POST /api/chat/new` - Create a new chat\n\n## Resources\n\n- [AI SDK Docs](https://sdk.vercel.ai/docs)\n- [Workers AI Models](https://developers.cloudflare.com/workers-ai/models)"
9
14
  },
10
15
  "hooks": {
11
16
  "postDeploy": [
@@ -17,7 +22,7 @@
17
22
  {
18
23
  "action": "box",
19
24
  "title": "{{name}}",
20
- "lines": ["{{url}}", "", "jack open to view in browser"]
25
+ "lines": ["{{url}}", "", "Chat history saved to D1.", "jack open to start chatting"]
21
26
  }
22
27
  ]
23
28
  }
@@ -4,6 +4,11 @@
4
4
  "workspaces": {
5
5
  "": {
6
6
  "name": "jack-template",
7
+ "dependencies": {
8
+ "ai": "^4",
9
+ "hono": "^4.6.0",
10
+ "workers-ai-provider": "^0.7",
11
+ },
7
12
  "devDependencies": {
8
13
  "@cloudflare/workers-types": "^4.20241205.0",
9
14
  "typescript": "^5.0.0",
@@ -11,8 +16,52 @@
11
16
  },
12
17
  },
13
18
  "packages": {
14
- "@cloudflare/workers-types": ["@cloudflare/workers-types@4.20260120.0", "", {}, "sha512-B8pueG+a5S+mdK3z8oKu1ShcxloZ7qWb68IEyLLaepvdryIbNC7JVPcY0bWsjS56UQVKc5fnyRge3yZIwc9bxw=="],
19
+ "@ai-sdk/provider": ["@ai-sdk/provider@1.1.3", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg=="],
20
+
21
+ "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@2.2.8", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "nanoid": "^3.3.8", "secure-json-parse": "^2.7.0" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA=="],
22
+
23
+ "@ai-sdk/react": ["@ai-sdk/react@1.2.12", "", { "dependencies": { "@ai-sdk/provider-utils": "2.2.8", "@ai-sdk/ui-utils": "1.2.11", "swr": "^2.2.5", "throttleit": "2.1.0" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "zod": "^3.23.8" }, "optionalPeers": ["zod"] }, "sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g=="],
24
+
25
+ "@ai-sdk/ui-utils": ["@ai-sdk/ui-utils@1.2.11", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w=="],
26
+
27
+ "@cloudflare/workers-types": ["@cloudflare/workers-types@4.20260212.0", "", {}, "sha512-ZK+e8T/2tWBCrE8PoAi9oqTxcBen9Apq+dxbsy1R5LFVdB6M4pY+oP49OFuHTTezrvNXbyvmzbf/vjtrCPGdNg=="],
28
+
29
+ "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
30
+
31
+ "@types/diff-match-patch": ["@types/diff-match-patch@1.0.36", "", {}, "sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg=="],
32
+
33
+ "ai": ["ai@4.3.19", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8", "@ai-sdk/react": "1.2.12", "@ai-sdk/ui-utils": "1.2.11", "@opentelemetry/api": "1.9.0", "jsondiffpatch": "0.6.0" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "zod": "^3.23.8" }, "optionalPeers": ["react"] }, "sha512-dIE2bfNpqHN3r6IINp9znguYdhIOheKW2LDigAMrgt/upT3B8eBGPSCblENvaZGoq+hxaN9fSMzjWpbqloP+7Q=="],
34
+
35
+ "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
36
+
37
+ "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
38
+
39
+ "diff-match-patch": ["diff-match-patch@1.0.5", "", {}, "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw=="],
40
+
41
+ "hono": ["hono@4.11.9", "", {}, "sha512-Eaw2YTGM6WOxA6CXbckaEvslr2Ne4NFsKrvc0v97JD5awbmeBLO5w9Ho9L9kmKonrwF9RJlW6BxT1PVv/agBHQ=="],
42
+
43
+ "json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="],
44
+
45
+ "jsondiffpatch": ["jsondiffpatch@0.6.0", "", { "dependencies": { "@types/diff-match-patch": "^1.0.36", "chalk": "^5.3.0", "diff-match-patch": "^1.0.5" }, "bin": { "jsondiffpatch": "bin/jsondiffpatch.js" } }, "sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ=="],
46
+
47
+ "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
48
+
49
+ "react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="],
50
+
51
+ "secure-json-parse": ["secure-json-parse@2.7.0", "", {}, "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw=="],
52
+
53
+ "swr": ["swr@2.4.0", "", { "dependencies": { "dequal": "^2.0.3", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-sUlC20T8EOt1pHmDiqueUWMmRRX03W7w5YxovWX7VR2KHEPCTMly85x05vpkP5i6Bu4h44ePSMD9Tc+G2MItFw=="],
54
+
55
+ "throttleit": ["throttleit@2.1.0", "", {}, "sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw=="],
15
56
 
16
57
  "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
58
+
59
+ "use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="],
60
+
61
+ "workers-ai-provider": ["workers-ai-provider@0.7.5", "", { "dependencies": { "@ai-sdk/provider": "^1.1.3", "@ai-sdk/provider-utils": "^2.2.8" } }, "sha512-dhCwgc3D65oDDTpH3k8Gf0Ek7KItzvaQidn2N5L5cqLo3WG8GM/4+Nr4rU56o8O3oZRsloB1gUCHYaRv2j7Y0A=="],
62
+
63
+ "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
64
+
65
+ "zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="],
17
66
  }
18
67
  }
@@ -7,6 +7,11 @@
7
7
  "dev": "wrangler dev",
8
8
  "deploy": "wrangler deploy"
9
9
  },
10
+ "dependencies": {
11
+ "ai": "^4",
12
+ "hono": "^4.6.0",
13
+ "workers-ai-provider": "^0.7"
14
+ },
10
15
  "devDependencies": {
11
16
  "@cloudflare/workers-types": "^4.20241205.0",
12
17
  "typescript": "^5.0.0"