From 94fd6e569084105ae9f889ec4b86cc7dc74a9747 Mon Sep 17 00:00:00 2001 From: Ben Orloff <99829882+benorloff@users.noreply.github.com> Date: Thu, 21 Mar 2024 21:30:12 -0700 Subject: [PATCH] wp api --- app/(platform)/(dashboard)/ai/page.tsx | 214 +++++++++++++++++- app/(platform)/(dashboard)/dashboard/page.tsx | 8 +- components/ai/site-info.tsx | 27 +++ components/stripe-button.tsx | 12 +- lib/anthropic.ts | 5 + lib/wordpress.ts | 64 ++++++ package-lock.json | 184 ++++++++++++++- package.json | 1 + 8 files changed, 495 insertions(+), 20 deletions(-) create mode 100644 components/ai/site-info.tsx create mode 100644 lib/anthropic.ts diff --git a/app/(platform)/(dashboard)/ai/page.tsx b/app/(platform)/(dashboard)/ai/page.tsx index 2db1f44..c27520f 100644 --- a/app/(platform)/(dashboard)/ai/page.tsx +++ b/app/(platform)/(dashboard)/ai/page.tsx @@ -1,28 +1,218 @@ +"use client" + +import { SiteInfo } from "@/components/ai/site-info"; import { DashboardTitle } from "@/components/dashboard-title" import { SiteSelect } from "@/components/sites/site-select"; +import { Button } from "@/components/ui/button"; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage +} from "@/components/ui/form" +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; import { db } from "@/lib/db"; +import { WpApiEndpoint, wpGet } from "@/lib/wordpress"; import { auth } from "@clerk/nextjs"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { set } from "lodash"; +import { EyeIcon } from "lucide-react"; +import { useState } from "react"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; + +const httpRegex = /^(http|https):/ +const completeUrlRegex = /^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_+.~#?&/=]*)$/ -const AIPage = async () => { - const { userId } = auth(); +const FormSchema = z.object({ + url: z + .string() + .max(255) + .transform((val, ctx) => { + let completeUrl = val; + if (!httpRegex.test(completeUrl)) { + completeUrl = `https://${completeUrl}`; + } + if (!completeUrlRegex.test(completeUrl)) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "Please enter a valid URL", + }); - const sites = await db.site.findMany({ - where: { - members: { - some: { - userId: userId!, - } + return z.NEVER; } - }, - include: { - members: true + return completeUrl; + }), + username: z.string({ + required_error: "Username is required", + }), + appPassword: z.string({ + required_error: "App password is required", + }), +}) + +const AIPage = () => { + // const { userId } = auth(); + + // const sites = await db.site.findMany({ + // where: { + // members: { + // some: { + // userId: userId!, + // } + // } + // }, + // include: { + // members: true + // } + // }) + + const [credentials, setCredentials] = useState>() + const [wpPages, setWpPages] = useState({data: [], headers: []}) + const [wpPosts, setWpPosts] = useState({data: [], headers: []}) + const [wpCategories, setWpCategories] = useState({data: [], headers: []}) + const [wpPlugins, setWpPlugins] = useState({data: [], headers: []}) + const [headers, setHeaders] = useState({}) + + // For testing purposes only + const form = useForm>({ + resolver: zodResolver(FormSchema), + defaultValues: { + url: "demozone.flywheelsites.com", + username: "test", + appPassword: "PQaG iA4n bmZT zTVi IZB6 sj4R", } }) + const onSubmit = (values: z.infer) => { + setCredentials(values); + // SiteInfo(values).then((res) => {setData(res.data), setHeaders(res.headers)}); + wpGet({ + url: values.url, + pretty: true, + username: values.username, + password: values.appPassword, + endpoint: "pages", + }).then((res) => + setWpPages({data: res.data, headers: res.headers}) + ); + wpGet({ + url: values.url, + pretty: true, + username: values.username, + password: values.appPassword, + endpoint: "posts", + }).then((res) => + setWpPosts({data: res.data, headers: res.headers}) + ); + wpGet({ + url: values.url, + pretty: true, + username: values.username, + password: values.appPassword, + endpoint: "plugins", + }).then((res) => + setWpPlugins({data: res.data, headers: res.headers}) + ); + console.log(wpPages, "<-- wpPages state") + console.log(wpPosts, "<-- wpPosts state") + form.reset(); + } + return( <> - + {/* */} +
+ + ( + + URL + + + + + + )} + /> + ( + + Username + + + + + + )} + /> + ( + + Application Password + + + + + + )} + /> + + {(credentials?.url && credentials?.username && credentials?.appPassword) && (

Access Granted

)} + + + {/* {(credentials?.url && credentials?.username && credentials?.appPassword) && ( + + )} */} +
Pages
+ {wpPages.headers.length > 0 && (

Total Pages: {wpPages.headers.find((header) => header[0] === 'x-wp-total')[1]}

)} + {wpPages.data.length > 0 && ( + wpPages.data.map((page: object) => ( +
+

Title: {page.title.rendered}

+

Date: {new Date(page.date).toLocaleString()}

+

URL: {page.link}

+ +
+ + )) + )} +
Posts
+ {wpPosts.headers.length > 0 && (

Total Posts: {wpPosts.headers.find((header) => header[0] === 'x-wp-total')[1]}

)} + {wpPosts.data.length > 0 && ( + wpPosts.data.map((post: object) => ( +
+

Title: {post.title.rendered}

+

Date: {new Date(post.date).toLocaleString()}

+

URL: {post.link}

+ +
+ + )) + )} +
Plugins
+ {wpPlugins.data.length > 0 && ( + wpPlugins.data.map((plugin: object) => ( +
+

Name: {plugin.name}

+

Status: {plugin.status}

+

URI: {plugin.plugin_uri}

+ +
+ + )) + )} + ) }; diff --git a/app/(platform)/(dashboard)/dashboard/page.tsx b/app/(platform)/(dashboard)/dashboard/page.tsx index 2170736..3eb7097 100644 --- a/app/(platform)/(dashboard)/dashboard/page.tsx +++ b/app/(platform)/(dashboard)/dashboard/page.tsx @@ -39,15 +39,15 @@ const DashboardPage = async () => { where: { orgId: orgId!, }, - take: 10, + orderBy: { + createdAt: "desc", + }, + take: 5, }); - console.log(await checkSubscription(), "<-- checkSubscription result") - return ( <>
{greeting}
-

Recently Active Sites

{sites.map((site) => ( diff --git a/components/ai/site-info.tsx b/components/ai/site-info.tsx new file mode 100644 index 0000000..da4cafc --- /dev/null +++ b/components/ai/site-info.tsx @@ -0,0 +1,27 @@ +"use server" + +interface SiteAccessValues { + url: string, + username: string, + appPassword: string, +} + +export const SiteInfo = async (values: SiteAccessValues) => { + + console.log(values, "<-- values from SiteInfo") + const wpApiHost = `${values.url}/wp-json/wp/v2` + console.log(wpApiHost, "<-- wpApiHost") + + // WP API Pages Endpoint + const response = await fetch(`${wpApiHost}/pages`, { + headers: { + "Content-Type": "application/json", + "Authorization": `Basic ${btoa(`${values.username}:${values.appPassword}`)}` + } + }) + + const data = await response.json(); + const headers = response.headers; + + return { data, headers }; +} \ No newline at end of file diff --git a/components/stripe-button.tsx b/components/stripe-button.tsx index fba3d8e..213203c 100644 --- a/components/stripe-button.tsx +++ b/components/stripe-button.tsx @@ -7,6 +7,7 @@ import { Loader2 } from "lucide-react"; import { toast } from "sonner"; import { StripeSession } from "@/actions/create-stripe-session/schema"; +import { cn } from "@/lib/utils"; interface StripeButtonProps { customerId?: string; @@ -39,10 +40,17 @@ export const StripeButton = ({ diff --git a/lib/anthropic.ts b/lib/anthropic.ts new file mode 100644 index 0000000..b8f4cfe --- /dev/null +++ b/lib/anthropic.ts @@ -0,0 +1,5 @@ +import Anthropic from '@anthropic-ai/sdk'; + +export const anthropic = new Anthropic({ + apiKey: process.env.ANTHROPIC_API_KEY!, +}); \ No newline at end of file diff --git a/lib/wordpress.ts b/lib/wordpress.ts index 246df30..f34cb26 100644 --- a/lib/wordpress.ts +++ b/lib/wordpress.ts @@ -1,5 +1,35 @@ "use server" +export type WpApiEndpoint = + "posts" + | "categories" + | "tags" + | "pages" + | "comments" + | "taxonomies" + | "media" + | "users" + | "types" + | "statuses" + | "settings" + | "themes" + | "search" + | "block-types" + | "blocks" + | "block-renderer" + | "block-directory/search" + | "plugins"; + + +export interface WpApiRequest { + url: string, + pretty: boolean, + username: string, + password: string, + endpoint: WpApiEndpoint, + resourceId?: number, +} + export const siteIsWordPress = async (url: string) => { console.log("siteIsWordPress", url) @@ -24,4 +54,38 @@ export const siteIsWordPress = async (url: string) => { // Return the result as a boolean return data!!; +} + +export const wpGet = async ({ + url, + pretty, + username, + password, + endpoint, + resourceId, +}: WpApiRequest) => { + + let data; + let headers; + + let urlPath = `${url}${pretty ? '/wp-json' : '/?rest_route='}/wp/v2/${endpoint}/${resourceId ? `/${resourceId}` : ""}` + console.log(urlPath, "<-- urlPath") + + try { + const response = await fetch(urlPath, { + headers: { + "Content-Type": "application/json", + "Authorization": `Basic ${btoa(`${username}:${password}`)}` + }, + cache: "no-cache", + }) + data = await response.json(); + headers = response.headers; + } catch (error) { + console.error(error); + return { + error: "An error occurred while fetching data from the WordPress API", + } + } + return { data, headers }; } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 24a0012..625311f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "bellhop", "version": "0.1.0", "dependencies": { + "@anthropic-ai/sdk": "^0.19.0", "@clerk/backend": "^0.38.3", "@clerk/nextjs": "^4.29.6", "@clerk/themes": "^1.7.9", @@ -92,6 +93,52 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@anthropic-ai/sdk": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.19.0.tgz", + "integrity": "sha512-mv4JtBGLKtpi7XK6Car2Tb+xwdgOuaMtRxxJoO27tqFESgGWdlxSWU65ntjIXAFFLABbRCtVFsnXTBS1AlF6NQ==", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "digest-fetch": "^1.3.0", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7", + "web-streams-polyfill": "^3.2.1" + } + }, + "node_modules/@anthropic-ai/sdk/node_modules/@types/node": { + "version": "18.19.26", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.26.tgz", + "integrity": "sha512-+wiMJsIwLOYCvUqSdKTrfkS8mpTp+MPINe6+Np4TAGFWWRWiBQ5kSq9nZGCSPkzx9mvT+uEukzpX4MOSCydcvw==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@anthropic-ai/sdk/node_modules/@types/node-fetch": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "node_modules/@anthropic-ai/sdk/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/@babel/runtime": { "version": "7.23.2", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", @@ -2269,6 +2316,17 @@ } } }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/acorn": { "version": "8.11.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", @@ -2299,6 +2357,17 @@ "node": ">=0.4.0" } }, + "node_modules/agentkeepalive": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", + "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -2653,6 +2722,11 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/base-64": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz", + "integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==" + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -2813,6 +2887,14 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "engines": { + "node": "*" + } + }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -3197,6 +3279,14 @@ "node": ">= 8" } }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "engines": { + "node": "*" + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -3317,6 +3407,15 @@ "node": ">=0.3.1" } }, + "node_modules/digest-fetch": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/digest-fetch/-/digest-fetch-1.3.0.tgz", + "integrity": "sha512-CGJuv6iKNM7QyZlM2T3sPAdZWd/p9zQiRNS9G+9COUCwzWFTs0Xp8NF5iePx7wtvhDykReiRRrSeNb4oMmB8lA==", + "dependencies": { + "base-64": "^0.1.0", + "md5": "^2.3.0" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -3931,6 +4030,14 @@ "node": ">=0.10.0" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "engines": { + "node": ">=6" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -4099,6 +4206,31 @@ "node": ">= 6" } }, + "node_modules/form-data-encoder": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", + "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==" + }, + "node_modules/formdata-node": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", + "dependencies": { + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" + }, + "engines": { + "node": ">= 12.20" + } + }, + "node_modules/formdata-node/node_modules/web-streams-polyfill": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "engines": { + "node": ">= 14" + } + }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -4400,6 +4532,14 @@ "node": ">= 0.4" } }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "dependencies": { + "ms": "^2.0.0" + } + }, "node_modules/i": { "version": "0.3.7", "resolved": "https://registry.npmjs.org/i/-/i-0.3.7.tgz", @@ -4547,6 +4687,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -5043,6 +5188,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -5105,8 +5260,7 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/mz": { "version": "2.7.0", @@ -5205,6 +5359,24 @@ "tslib": "^2.0.3" } }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -9715,6 +9887,14 @@ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "devOptional": true }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "engines": { + "node": ">= 8" + } + }, "node_modules/webcrypto-core": { "version": "1.7.8", "resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.7.8.tgz", diff --git a/package.json b/package.json index 4f05557..68b0b9e 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "seed": "ts-node --compiler-options {\"module\":\"CommonJS\"} prisma/seed.ts" }, "dependencies": { + "@anthropic-ai/sdk": "^0.19.0", "@clerk/backend": "^0.38.3", "@clerk/nextjs": "^4.29.6", "@clerk/themes": "^1.7.9",