@mvriu5/payload-ai 0.5.0 → 0.5.8

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.
@@ -5,6 +5,7 @@ import { getModel, getProviderConfig } from "../ai/providerRuntime.js";
5
5
  import { isAIProvider } from "../ai/providerOptions.js";
6
6
  import { containsSensitiveData } from "../ai/sensitiveData.js";
7
7
  import { buildPromptWithMentionContext, collectBlocks, describeField, getAllowedCollectionSlugs, getMentionContext } from "../payload/schemaContext.js";
8
+ import { getCollectionPermissions, isCollectionActionAllowed } from "../payload/collectionPermissions.js";
8
9
  export const createAIChatEndpointHandler = (options = {})=>async (req)=>{
9
10
  if (!req.user) return Response.json({
10
11
  error: "Unauthorized"
@@ -26,8 +27,10 @@ export const createAIChatEndpointHandler = (options = {})=>async (req)=>{
26
27
  status: 400
27
28
  });
28
29
  const provider = requestedProvider;
30
+ const userApiKey = options.allowUserApiKeys === false ? null : user.aiApiKey;
29
31
  const providerConfig = getProviderConfig({
30
- apiKey: user.aiApiKey,
32
+ apiKey: userApiKey,
33
+ defaultModels: options.models?.defaults,
31
34
  model: body?.model,
32
35
  provider
33
36
  });
@@ -48,7 +51,7 @@ export const createAIChatEndpointHandler = (options = {})=>async (req)=>{
48
51
  };
49
52
  if (!providerConfig.apiKey) {
50
53
  return Response.json({
51
- error: `Add a ${provider} API key to your account settings first.`
54
+ error: options.allowUserApiKeys === false ? `Configure a ${provider} API key in the server environment first.` : `Add a ${provider} API key to your account settings or configure it in the server environment first.`
52
55
  }, {
53
56
  status: 400
54
57
  });
@@ -93,6 +96,17 @@ export const createAIChatEndpointHandler = (options = {})=>async (req)=>{
93
96
  req
94
97
  });
95
98
  const collectionSlugSchema = z.enum(collectionSlugs);
99
+ const getDisallowedCollectionActionError = (collection, action)=>{
100
+ if (isCollectionActionAllowed({
101
+ action,
102
+ permissions: options.collections,
103
+ req,
104
+ slug: collection
105
+ })) return null;
106
+ return {
107
+ error: `${action} is not enabled for collection: ${collection}`
108
+ };
109
+ };
96
110
  const tools = {
97
111
  getDoc: {
98
112
  description: "Read one document by collection slug and document id.",
@@ -117,6 +131,10 @@ export const createAIChatEndpointHandler = (options = {})=>async (req)=>{
117
131
  return allowedCollections.map((collection)=>({
118
132
  fields: collection.fields.map(describeField),
119
133
  label: collection.labels?.plural || collection.slug,
134
+ permissions: getCollectionPermissions({
135
+ permissions: options.collections,
136
+ slug: collection.slug
137
+ }),
120
138
  slug: collection.slug
121
139
  }));
122
140
  }
@@ -158,6 +176,8 @@ export const createAIChatEndpointHandler = (options = {})=>async (req)=>{
158
176
  label: z.string().min(1)
159
177
  }),
160
178
  execute: async ({ collection, data, label })=>{
179
+ const permissionError = getDisallowedCollectionActionError(collection, "create");
180
+ if (permissionError) return permissionError;
161
181
  const proposal = {
162
182
  action: "create",
163
183
  collection,
@@ -175,6 +195,8 @@ export const createAIChatEndpointHandler = (options = {})=>async (req)=>{
175
195
  label: z.string().min(1)
176
196
  }),
177
197
  execute: async ({ collection, id, label })=>{
198
+ const permissionError = getDisallowedCollectionActionError(collection, "delete");
199
+ if (permissionError) return permissionError;
178
200
  const proposal = {
179
201
  action: "delete",
180
202
  collection,
@@ -193,6 +215,8 @@ export const createAIChatEndpointHandler = (options = {})=>async (req)=>{
193
215
  label: z.string().min(1)
194
216
  }),
195
217
  execute: async ({ collection, data, id, label })=>{
218
+ const permissionError = getDisallowedCollectionActionError(collection, "update");
219
+ if (permissionError) return permissionError;
196
220
  const proposal = {
197
221
  action: "update",
198
222
  collection,
@@ -1,6 +1,7 @@
1
1
  import type { PayloadHandler } from "payload";
2
+ import { type ResolvedAICollectionPermissionMap } from "../payload/collectionPermissions.js";
2
3
  type AIMentionSuggestionsEndpointOptions = {
3
- collections?: string[];
4
+ collections?: ResolvedAICollectionPermissionMap;
4
5
  };
5
6
  export declare const createAIMentionSuggestionsEndpointHandler: (options?: AIMentionSuggestionsEndpointOptions) => PayloadHandler;
6
7
  export {};
@@ -1,9 +1,4 @@
1
- import { isInternalCollection } from "../payload/shared.js";
2
- const isAllowedCollectionSlug = (slug, collections)=>{
3
- if (isInternalCollection(slug)) return false;
4
- if (!collections) return true;
5
- return collections.includes(slug);
6
- };
1
+ import { isCollectionActionAllowed } from "../payload/collectionPermissions.js";
7
2
  const getDocLabel = (doc, useAsTitle)=>{
8
3
  const titleField = useAsTitle && typeof doc[useAsTitle] === "string" ? doc[useAsTitle] : null;
9
4
  return (titleField || doc.title || doc.name || doc.email || doc.id || "Untitled").toString();
@@ -22,7 +17,12 @@ export const createAIMentionSuggestionsEndpointHandler = (options = {})=>async (
22
17
  });
23
18
  const suggestions = [];
24
19
  const collections = req.payload.config.collections.filter((collection)=>{
25
- if (!isAllowedCollectionSlug(collection.slug, options.collections)) return false;
20
+ if (!isCollectionActionAllowed({
21
+ action: "read",
22
+ permissions: options.collections,
23
+ req,
24
+ slug: collection.slug
25
+ })) return false;
26
26
  if (collectionSlug) return collection.slug === collectionSlug;
27
27
  return true;
28
28
  });
package/dist/index.d.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import type { CollectionSlug, Config } from "payload";
2
2
  import { type AIModelConfig } from "./ai/providerOptions.js";
3
+ import { type AICollectionPermissionConfig } from "./payload/collectionPermissions.js";
3
4
  export type PayloadAiPluginOptions = {
4
- collections?: Partial<Record<CollectionSlug, true>>;
5
+ allowUserApiKeys?: boolean;
6
+ collections?: Partial<Record<CollectionSlug, AICollectionPermissionConfig>>;
5
7
  disabled?: boolean;
6
8
  models?: AIModelConfig;
7
9
  };
package/dist/index.js CHANGED
@@ -2,11 +2,9 @@ import { aiProviders, getResolvedAIModelConfig } from "./ai/providerOptions.js";
2
2
  import { createAIApplyActionEndpointHandler } from "./endpoints/aiApplyActionEndpointHandler.js";
3
3
  import { createAIChatEndpointHandler } from "./endpoints/aiChatEndpointHandler.js";
4
4
  import { createAIMentionSuggestionsEndpointHandler } from "./endpoints/aiMentionSuggestionsEndpointHandler.js";
5
- const getEnabledCollections = (collections)=>{
6
- if (!collections) return undefined;
7
- return Object.entries(collections).filter(([, enabled])=>enabled).map(([slug])=>slug);
8
- };
9
- const addAccountFields = (config)=>{
5
+ import { resolveCollectionPermissions } from "./payload/collectionPermissions.js";
6
+ import { isInternalCollection } from "./payload/shared.js";
7
+ const addAccountFields = ({ allowUserApiKeys, config })=>{
10
8
  const adminUserSlug = config.admin?.user;
11
9
  if (!adminUserSlug || !config.collections) return;
12
10
  const userCollection = config.collections.find((c)=>c.slug === adminUserSlug);
@@ -16,29 +14,40 @@ const addAccountFields = (config)=>{
16
14
  type: "select",
17
15
  defaultValue: "openai",
18
16
  options: aiProviders
19
- }, {
20
- name: "aiApiKey",
21
- type: "text",
22
- admin: {
23
- components: {
24
- Field: "payload-ai-plugin/client#AIApiKeyField"
25
- }
26
- }
27
17
  });
18
+ if (allowUserApiKeys) {
19
+ userCollection.fields.push({
20
+ name: "aiApiKey",
21
+ type: "text",
22
+ admin: {
23
+ components: {
24
+ Field: "payload-ai-plugin/client#AIApiKeyField"
25
+ },
26
+ description: "Optional. If empty, the plugin uses the provider API key from environment variables."
27
+ }
28
+ });
29
+ }
28
30
  };
29
31
  export const payloadAiPlugin = (pluginOptions)=>(config)=>{
30
32
  const incomingOnInit = config.onInit;
31
- const enabledCollections = getEnabledCollections(pluginOptions.collections);
33
+ const collectionPermissions = resolveCollectionPermissions(pluginOptions.collections);
34
+ const allowUserApiKeys = pluginOptions.allowUserApiKeys !== false;
32
35
  const modelConfig = getResolvedAIModelConfig(pluginOptions.models);
33
36
  if (!config.collections) config.collections = [];
34
- addAccountFields(config);
37
+ addAccountFields({
38
+ allowUserApiKeys,
39
+ config
40
+ });
35
41
  if (pluginOptions.disabled) return config;
42
+ const mentionCollectionSlugs = config.collections.filter((collection)=>!isInternalCollection(collection.slug)).filter((collection)=>collectionPermissions ? Boolean(collectionPermissions[collection.slug]?.read) : true).map((collection)=>collection.slug);
36
43
  if (!config.endpoints) config.endpoints = [];
37
44
  if (!config.admin) config.admin = {};
38
45
  config.admin.custom = {
39
46
  ...config.admin.custom || {},
40
47
  payloadAiPlugin: {
41
48
  ...config.admin.custom?.payloadAiPlugin || {},
49
+ collectionSlugs: mentionCollectionSlugs,
50
+ allowUserApiKeys,
42
51
  models: modelConfig
43
52
  }
44
53
  };
@@ -47,21 +56,23 @@ export const payloadAiPlugin = (pluginOptions)=>(config)=>{
47
56
  config.admin.components.beforeDashboard.push(`payload-ai-plugin/client#AIInput`);
48
57
  config.endpoints.push({
49
58
  handler: createAIChatEndpointHandler({
50
- collections: enabledCollections
59
+ allowUserApiKeys,
60
+ collections: collectionPermissions,
61
+ models: modelConfig
51
62
  }),
52
63
  method: "post",
53
64
  path: "/ai-chat"
54
65
  });
55
66
  config.endpoints.push({
56
67
  handler: createAIApplyActionEndpointHandler({
57
- collections: enabledCollections
68
+ collections: collectionPermissions
58
69
  }),
59
70
  method: "post",
60
71
  path: "/ai-apply-action"
61
72
  });
62
73
  config.endpoints.push({
63
74
  handler: createAIMentionSuggestionsEndpointHandler({
64
- collections: enabledCollections
75
+ collections: collectionPermissions
65
76
  }),
66
77
  method: "post",
67
78
  path: "/ai-mention-suggestions"
@@ -0,0 +1,22 @@
1
+ import type { PayloadHandler } from "payload";
2
+ export type AICollectionAction = "create" | "delete" | "read" | "update";
3
+ export type AICollectionPermissions = Partial<Record<AICollectionAction, boolean>>;
4
+ export type AICollectionPermissionConfig = true | AICollectionPermissions;
5
+ export type AICollectionPermissionMap = Partial<Record<string, AICollectionPermissionConfig>>;
6
+ export type ResolvedAICollectionPermissionMap = Partial<Record<string, Record<AICollectionAction, boolean>>>;
7
+ export declare const resolveCollectionPermissions: (collections?: AICollectionPermissionMap) => ResolvedAICollectionPermissionMap | undefined;
8
+ export declare const getCollectionSlugsForAction: ({ action, permissions, req, }: {
9
+ action: AICollectionAction;
10
+ permissions?: ResolvedAICollectionPermissionMap;
11
+ req: Parameters<PayloadHandler>[0];
12
+ }) => string[];
13
+ export declare const isCollectionActionAllowed: ({ action, permissions, req, slug, }: {
14
+ action: AICollectionAction;
15
+ permissions?: ResolvedAICollectionPermissionMap;
16
+ req: Parameters<PayloadHandler>[0];
17
+ slug: string;
18
+ }) => boolean;
19
+ export declare const getCollectionPermissions: ({ permissions, slug, }: {
20
+ permissions?: ResolvedAICollectionPermissionMap;
21
+ slug: string;
22
+ }) => Record<AICollectionAction, boolean>;
@@ -0,0 +1,68 @@
1
+ import { isInternalCollection } from "./shared.js";
2
+ const allActions = [
3
+ "create",
4
+ "delete",
5
+ "read",
6
+ "update"
7
+ ];
8
+ const getKnownCollectionSlugs = (req)=>{
9
+ return req.payload.config.collections.map((collection)=>collection.slug).filter((slug)=>!isInternalCollection(slug));
10
+ };
11
+ export const resolveCollectionPermissions = (collections)=>{
12
+ if (!collections) return undefined;
13
+ return Object.fromEntries(Object.entries(collections).map(([slug, config])=>{
14
+ if (config === true) {
15
+ return [
16
+ slug,
17
+ {
18
+ create: true,
19
+ delete: true,
20
+ read: true,
21
+ update: true
22
+ }
23
+ ];
24
+ }
25
+ if (!config) {
26
+ return [
27
+ slug,
28
+ {
29
+ create: false,
30
+ delete: false,
31
+ read: false,
32
+ update: false
33
+ }
34
+ ];
35
+ }
36
+ return [
37
+ slug,
38
+ {
39
+ create: Boolean(config.create),
40
+ delete: Boolean(config.delete),
41
+ read: Boolean(config.read),
42
+ update: Boolean(config.update)
43
+ }
44
+ ];
45
+ }));
46
+ };
47
+ export const getCollectionSlugsForAction = ({ action, permissions, req })=>{
48
+ const knownSlugs = getKnownCollectionSlugs(req);
49
+ if (!permissions) return knownSlugs;
50
+ return knownSlugs.filter((slug)=>Boolean(permissions[slug]?.[action]));
51
+ };
52
+ export const isCollectionActionAllowed = ({ action, permissions, req, slug })=>{
53
+ if (!getKnownCollectionSlugs(req).includes(slug)) return false;
54
+ if (!permissions) return true;
55
+ return Boolean(permissions[slug]?.[action]);
56
+ };
57
+ export const getCollectionPermissions = ({ permissions, slug })=>{
58
+ if (!permissions) {
59
+ return Object.fromEntries(allActions.map((action)=>[
60
+ action,
61
+ true
62
+ ]));
63
+ }
64
+ return permissions[slug] || Object.fromEntries(allActions.map((action)=>[
65
+ action,
66
+ false
67
+ ]));
68
+ };
@@ -1,4 +1,5 @@
1
1
  import type { PayloadHandler } from "payload";
2
+ import { type ResolvedAICollectionPermissionMap } from "./collectionPermissions.js";
2
3
  export type AIChatMention = {
3
4
  collection?: string;
4
5
  id?: string;
@@ -43,7 +44,7 @@ export declare const collectBlocks: ({ fields, parent, }: {
43
44
  parent: string;
44
45
  slug: string;
45
46
  })[];
46
- export declare const getAllowedCollectionSlugs: (req: Parameters<PayloadHandler>[0], collections?: string[]) => string[];
47
+ export declare const getAllowedCollectionSlugs: (req: Parameters<PayloadHandler>[0], collections?: ResolvedAICollectionPermissionMap) => string[];
47
48
  export declare const getMentionContext: ({ blockContexts, collectionSlugs, globalSlugs, mentions, req, }: MentionContext) => Promise<Record<string, unknown>[]>;
48
49
  export declare const buildPromptWithMentionContext: ({ mentionContext, prompt, }: {
49
50
  mentionContext: Record<string, unknown>[];
@@ -1,3 +1,4 @@
1
+ import { getCollectionSlugsForAction } from "./collectionPermissions.js";
1
2
  import { getSerializableLabel, isInternalCollection } from "./shared.js";
2
3
  const getSerializableRelationTo = (relationTo)=>{
3
4
  if (typeof relationTo === "string") {
@@ -71,9 +72,11 @@ export const collectBlocks = ({ fields, parent })=>{
71
72
  return blocks;
72
73
  };
73
74
  export const getAllowedCollectionSlugs = (req, collections)=>{
74
- const configuredSlugs = req.payload.config.collections.map((collection)=>collection.slug).filter((slug)=>!isInternalCollection(slug));
75
- if (!collections) return configuredSlugs;
76
- return configuredSlugs.filter((slug)=>collections.includes(slug));
75
+ return getCollectionSlugsForAction({
76
+ action: "read",
77
+ permissions: collections,
78
+ req
79
+ });
77
80
  };
78
81
  export const getMentionContext = async ({ blockContexts, collectionSlugs, globalSlugs, mentions, req })=>{
79
82
  if (!mentions || mentions.length === 0) return [];
package/package.json CHANGED
@@ -1,126 +1,117 @@
1
1
  {
2
- "name": "@mvriu5/payload-ai",
3
- "version": "0.5.0",
4
- "description": "AI assistant plugin for Payload CMS with provider selection, CMS mentions, and signed action proposals.",
5
- "keywords": [
6
- "payload",
7
- "payloadcms",
8
- "payload-plugin",
9
- "ai",
10
- "cms",
11
- "openai",
12
- "anthropic",
13
- "google-generative-ai",
14
- "groq",
15
- "mistral"
16
- ],
17
- "author": "Marius",
18
- "license": "MIT",
19
- "homepage": "https://github.com/mvriu5/payload-ai-plugin#readme",
20
- "repository": {
21
- "type": "git",
22
- "url": "git+https://github.com/mvriu5/payload-ai-plugin.git"
23
- },
24
- "bugs": {
25
- "url": "https://github.com/mvriu5/payload-ai-plugin/issues"
26
- },
27
- "type": "module",
28
- "exports": {
29
- ".": {
30
- "import": "./dist/index.js",
31
- "types": "./dist/index.d.ts",
32
- "default": "./dist/index.js"
33
- },
34
- "./client": {
35
- "import": "./dist/exports/client.js",
36
- "types": "./dist/exports/client.d.ts",
37
- "default": "./dist/exports/client.js"
38
- }
39
- },
40
- "main": "./dist/index.js",
41
- "types": "./dist/index.d.ts",
42
- "files": [
43
- "dist"
44
- ],
45
- "scripts": {
46
- "build": "pnpm copyfiles && pnpm build:types && pnpm build:swc",
47
- "build:swc": "swc ./src -d ./dist --config-file .swcrc --strip-leading-paths",
48
- "build:types": "tsc --outDir dist --rootDir ./src",
49
- "clean": "rimraf {dist,*.tsbuildinfo}",
50
- "copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png,json}\" dist/",
51
- "dev": "next dev dev --turbo",
52
- "dev:generate-importmap": "pnpm dev:payload generate:importmap",
53
- "dev:generate-types": "pnpm dev:payload generate:types",
54
- "dev:payload": "cross-env PAYLOAD_CONFIG_PATH=./dev/payload.config.ts payload",
55
- "generate:importmap": "pnpm dev:generate-importmap",
56
- "generate:types": "pnpm dev:generate-types",
57
- "lint": "eslint",
58
- "lint:fix": "eslint ./src --fix",
59
- "prepublishOnly": "pnpm clean && pnpm build"
60
- },
61
- "devDependencies": {
62
- "@payloadcms/db-postgres": "3.84.1",
63
- "@payloadcms/next": "3.84.1",
64
- "@payloadcms/richtext-lexical": "3.84.1",
65
- "@payloadcms/ui": "3.84.1",
66
- "@swc/cli": "0.6.0",
67
- "@types/node": "22.19.9",
68
- "@types/react": "19.2.14",
69
- "@types/react-dom": "19.2.3",
70
- "copyfiles": "2.4.1",
71
- "cross-env": "^7.0.3",
72
- "eslint": "^9.23.0",
73
- "graphql": "^16.8.1",
74
- "knip": "^6.16.1",
75
- "next": "16.2.6",
76
- "payload": "3.84.1",
77
- "react": "19.2.6",
78
- "react-dom": "19.2.6",
79
- "rimraf": "3.0.2",
80
- "sharp": "0.34.2",
81
- "typescript": "5.7.3",
82
- "vite-tsconfig-paths": "6.0.5",
83
- "vitest": "4.0.18"
84
- },
85
- "peerDependencies": {
86
- "payload": "^3.84.1"
87
- },
88
- "engines": {
89
- "node": "^18.20.2 || >=20.9.0",
90
- "pnpm": "^9 || ^10"
91
- },
92
- "publishConfig": {
93
- "access": "public"
94
- },
95
- "pnpm": {
96
- "onlyBuiltDependencies": [
97
- "sharp",
98
- "esbuild",
99
- "unrs-resolver"
100
- ]
101
- },
102
- "registry": "https://registry.npmjs.org/",
103
- "knip": {
104
- "entry": [
105
- "dev/payload.config.ts",
106
- "dev/next.config.mjs",
107
- "dev/app/**/*.{ts,tsx}"
2
+ "name": "@mvriu5/payload-ai",
3
+ "version": "0.5.8",
4
+ "description": "AI assistant plugin for Payload CMS with provider selection, CMS mentions, and signed action proposals.",
5
+ "keywords": [
6
+ "payload",
7
+ "payloadcms",
8
+ "payload-plugin",
9
+ "ai",
10
+ "cms",
11
+ "openai",
12
+ "anthropic",
13
+ "google-generative-ai",
14
+ "mistral"
108
15
  ],
109
- "ignore": [
110
- "dev/payload-types.ts"
16
+ "author": "Marius",
17
+ "license": "MIT",
18
+ "homepage": "https://github.com/mvriu5/payload-ai-plugin#readme",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/mvriu5/payload-ai-plugin.git"
22
+ },
23
+ "bugs": {
24
+ "url": "https://github.com/mvriu5/payload-ai-plugin/issues"
25
+ },
26
+ "type": "module",
27
+ "exports": {
28
+ ".": {
29
+ "import": "./dist/index.js",
30
+ "types": "./dist/index.d.ts",
31
+ "default": "./dist/index.js"
32
+ },
33
+ "./client": {
34
+ "import": "./dist/exports/client.js",
35
+ "types": "./dist/exports/client.d.ts",
36
+ "default": "./dist/exports/client.js"
37
+ }
38
+ },
39
+ "main": "./dist/index.js",
40
+ "types": "./dist/index.d.ts",
41
+ "files": [
42
+ "dist"
111
43
  ],
112
- "project": [
113
- "src/**/*.{ts,tsx}",
114
- "dev/**/*.{ts,tsx,mjs}"
115
- ]
116
- },
117
- "dependencies": {
118
- "@ai-sdk/anthropic": "^3.0.81",
119
- "@ai-sdk/google": "^3.0.80",
120
- "@ai-sdk/groq": "^3.0.39",
121
- "@ai-sdk/mistral": "^3.0.37",
122
- "@ai-sdk/openai": "^3.0.67",
123
- "ai": "^6.0.193",
124
- "zod": "^4.4.3"
125
- }
44
+ "scripts": {
45
+ "build": "pnpm copyfiles && pnpm build:types && pnpm build:swc",
46
+ "build:swc": "swc ./src -d ./dist --config-file .swcrc --strip-leading-paths",
47
+ "build:types": "tsc --outDir dist --rootDir ./src",
48
+ "clean": "rimraf {dist,*.tsbuildinfo}",
49
+ "copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png,json}\" dist/",
50
+ "dev": "next dev dev --turbo",
51
+ "dev:generate-importmap": "pnpm dev:payload generate:importmap",
52
+ "dev:generate-types": "pnpm dev:payload generate:types",
53
+ "dev:payload": "cross-env PAYLOAD_CONFIG_PATH=./dev/payload.config.ts payload",
54
+ "generate:importmap": "pnpm dev:generate-importmap",
55
+ "generate:types": "pnpm dev:generate-types",
56
+ "lint": "eslint",
57
+ "lint:fix": "eslint ./src --fix",
58
+ "prepublishOnly": "pnpm clean && pnpm build"
59
+ },
60
+ "devDependencies": {
61
+ "@payloadcms/db-postgres": "3.84.1",
62
+ "@payloadcms/next": "3.84.1",
63
+ "@payloadcms/richtext-lexical": "3.84.1",
64
+ "@payloadcms/ui": "3.84.1",
65
+ "@swc/cli": "0.8.1",
66
+ "@types/node": "25.9.3",
67
+ "@types/react": "19.2.17",
68
+ "@types/react-dom": "19.2.3",
69
+ "copyfiles": "2.4.1",
70
+ "cross-env": "^7.0.3",
71
+ "eslint": "^10.5.0",
72
+ "graphql": "^16.8.1",
73
+ "knip": "^6.16.1",
74
+ "next": "16.2.9",
75
+ "payload": "3.84.1",
76
+ "prettier": "^3.8.4",
77
+ "react": "19.2.7",
78
+ "react-dom": "19.2.6",
79
+ "rimraf": "6.1.3",
80
+ "sharp": "0.35.1",
81
+ "typescript": "5.9.3",
82
+ "vite-tsconfig-paths": "6.1.1",
83
+ "vitest": "4.1.9"
84
+ },
85
+ "peerDependencies": {
86
+ "payload": "^3.84.1"
87
+ },
88
+ "engines": {
89
+ "node": "^18.20.2 || >=20.9.0",
90
+ "pnpm": "^9 || ^10 || ^11"
91
+ },
92
+ "publishConfig": {
93
+ "access": "public"
94
+ },
95
+ "knip": {
96
+ "entry": [
97
+ "dev/payload.config.ts",
98
+ "dev/next.config.mjs",
99
+ "dev/app/**/*.{ts,tsx}"
100
+ ],
101
+ "ignore": [
102
+ "dev/payload-types.ts"
103
+ ],
104
+ "project": [
105
+ "src/**/*.{ts,tsx}",
106
+ "dev/**/*.{ts,tsx,mjs}"
107
+ ]
108
+ },
109
+ "dependencies": {
110
+ "@ai-sdk/anthropic": "^3.0.81",
111
+ "@ai-sdk/google": "^3.0.80",
112
+ "@ai-sdk/mistral": "^3.0.37",
113
+ "@ai-sdk/openai": "^3.0.67",
114
+ "ai": "^6.0.193",
115
+ "zod": "^4.4.3"
116
+ }
126
117
  }