@fayz-ai/sdk 0.9.0-next.1 → 0.10.0

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 (54) hide show
  1. package/README.md +2 -2
  2. package/dist/ai-builder.js +1 -1
  3. package/dist/{chunk-2YJXKDHO.js → chunk-6UZOXHGJ.js} +11 -6
  4. package/dist/chunk-6UZOXHGJ.js.map +1 -0
  5. package/dist/{chunk-V4GWI7WH.js → chunk-7UXTYT6I.js} +65 -4
  6. package/dist/chunk-7UXTYT6I.js.map +1 -0
  7. package/dist/{chunk-7XM4LONE.js → chunk-BJFMBVMP.js} +2 -2
  8. package/dist/chunk-BJFMBVMP.js.map +1 -0
  9. package/dist/chunk-HA5O3CW7.js +181 -0
  10. package/dist/chunk-HA5O3CW7.js.map +1 -0
  11. package/dist/chunk-VRUPJGFZ.js +135 -0
  12. package/dist/chunk-VRUPJGFZ.js.map +1 -0
  13. package/dist/deprecations.d.ts +98 -0
  14. package/dist/deprecations.d.ts.map +1 -0
  15. package/dist/deprecations.js +3 -0
  16. package/dist/deprecations.js.map +1 -0
  17. package/dist/index.d.ts +2 -0
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +5 -4
  20. package/dist/release-channels.js +1 -1
  21. package/dist/shop.d.ts +32 -0
  22. package/dist/shop.d.ts.map +1 -1
  23. package/dist/shop.js +2 -1
  24. package/dist/supported-surface.d.ts +8 -1
  25. package/dist/supported-surface.d.ts.map +1 -1
  26. package/dist/supported-surface.js +1 -1
  27. package/dist/vite.d.ts +30 -1
  28. package/dist/vite.d.ts.map +1 -1
  29. package/dist/vite.js +10 -2
  30. package/dist/vite.js.map +1 -1
  31. package/package.json +7 -14
  32. package/dist/chunk-2YJXKDHO.js.map +0 -1
  33. package/dist/chunk-7XM4LONE.js.map +0 -1
  34. package/dist/chunk-V4GWI7WH.js.map +0 -1
  35. package/dist/chunk-YZQW3BJA.js +0 -135
  36. package/dist/chunk-YZQW3BJA.js.map +0 -1
  37. package/src/agent.test.ts +0 -114
  38. package/src/agent.ts +0 -335
  39. package/src/ai-builder.ts +0 -115
  40. package/src/app-params.ts +0 -69
  41. package/src/client.test.ts +0 -176
  42. package/src/client.ts +0 -229
  43. package/src/index.ts +0 -94
  44. package/src/release-channels.json +0 -103
  45. package/src/release-channels.test.ts +0 -41
  46. package/src/release-channels.ts +0 -41
  47. package/src/runtime.test.ts +0 -123
  48. package/src/runtime.ts +0 -211
  49. package/src/shop.test.ts +0 -114
  50. package/src/shop.ts +0 -1118
  51. package/src/supported-surface.json +0 -40
  52. package/src/supported-surface.ts +0 -33
  53. package/src/types.ts +0 -67
  54. package/src/vite.ts +0 -121
package/src/agent.test.ts DELETED
@@ -1,114 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest'
2
-
3
- import { createFayzAgentClient, FayzAgentError } from './agent'
4
-
5
- function jsonResponse(body: unknown, status = 200): Response {
6
- return new Response(JSON.stringify(body), {
7
- status,
8
- headers: { 'Content-Type': 'application/json' },
9
- })
10
- }
11
-
12
- function client(fetcher: typeof fetch) {
13
- return createFayzAgentClient({
14
- baseUrl: 'http://localhost:5173/api',
15
- projectId: 'project-1',
16
- publishableKey: 'fayzpk_test',
17
- fetcher,
18
- })
19
- }
20
-
21
- describe('createFayzAgentClient', () => {
22
- it('targets the public broker and presents the publishable key', async () => {
23
- const fetcher = vi.fn<typeof fetch>().mockResolvedValue(
24
- jsonResponse({ conversationId: 'conv-1', content: 'Hi', toolCalls: [] }),
25
- )
26
-
27
- await client(fetcher).chat({ message: 'Hello' })
28
-
29
- const [url, init] = fetcher.mock.calls[0]
30
- expect(url).toBe('http://localhost:5173/api/public/projects/project-1/agents/chat')
31
- expect((init?.headers as Record<string, string>)['x-fayz-agent-key']).toBe('fayzpk_test')
32
- expect(JSON.parse(init?.body as string)).toEqual({ message: 'Hello' })
33
- })
34
-
35
- it('forwards the tool catalog and returns the calls the model asked for', async () => {
36
- const toolCall = { id: 'call-1', name: 'getTeamMembers', arguments: { role: 'staff' } }
37
- const fetcher = vi.fn<typeof fetch>().mockResolvedValue(
38
- jsonResponse({ conversationId: 'conv-1', content: '', toolCalls: [toolCall] }),
39
- )
40
-
41
- const response = await client(fetcher).chat({
42
- message: "Who's on my team?",
43
- tools: [
44
- {
45
- name: 'getTeamMembers',
46
- description: 'Lists team members.',
47
- parameters: { type: 'object', properties: { role: { type: 'string' } } },
48
- },
49
- ],
50
- })
51
-
52
- expect(response.toolCalls).toEqual([toolCall])
53
- expect(JSON.parse(fetcher.mock.calls[0][1]?.body as string).tools).toHaveLength(1)
54
- })
55
-
56
- it('sends tool results with the conversation id and no new message', async () => {
57
- const fetcher = vi.fn<typeof fetch>().mockResolvedValue(
58
- jsonResponse({ conversationId: 'conv-1', content: 'Ana and Bruno.', toolCalls: [] }),
59
- )
60
-
61
- const response = await client(fetcher).chat({
62
- conversationId: 'conv-1',
63
- toolResults: [{ toolCallId: 'call-1', content: '{"total":2}' }],
64
- })
65
-
66
- const body = JSON.parse(fetcher.mock.calls[0][1]?.body as string)
67
- expect(body).toEqual({
68
- conversationId: 'conv-1',
69
- toolResults: [{ toolCallId: 'call-1', content: '{"total":2}' }],
70
- })
71
- expect(response.content).toBe('Ana and Bruno.')
72
- expect(response.toolCalls).toEqual([])
73
- })
74
-
75
- it('tolerates a response that omits toolCalls', async () => {
76
- const fetcher = vi.fn<typeof fetch>().mockResolvedValue(
77
- jsonResponse({ conversationId: 'conv-1', content: 'Hi' }),
78
- )
79
-
80
- const response = await client(fetcher).chat({ message: 'Hello' })
81
-
82
- expect(response.toolCalls).toEqual([])
83
- })
84
-
85
- it('surfaces the broker error message and status', async () => {
86
- const fetcher = vi
87
- .fn<typeof fetch>()
88
- .mockResolvedValue(jsonResponse({ error: 'Invalid agent key' }, 401))
89
-
90
- await expect(client(fetcher).chat({ message: 'Hello' })).rejects.toMatchObject({
91
- name: 'FayzAgentError',
92
- status: 401,
93
- message: 'Invalid agent key',
94
- })
95
- })
96
-
97
- it('falls back to a generic message when the error body is not JSON', async () => {
98
- const fetcher = vi
99
- .fn<typeof fetch>()
100
- .mockResolvedValue(new Response('gateway down', { status: 502 }))
101
-
102
- await expect(client(fetcher).getInfo()).rejects.toThrow(FayzAgentError)
103
- })
104
-
105
- it('reads the agent identity from /config', async () => {
106
- const agent = { id: 'a-1', name: 'Glow', description: null, icon: '💇' }
107
- const fetcher = vi.fn<typeof fetch>().mockResolvedValue(jsonResponse({ agent }))
108
-
109
- expect(await client(fetcher).getInfo()).toEqual(agent)
110
- expect(fetcher.mock.calls[0][0]).toBe(
111
- 'http://localhost:5173/api/public/projects/project-1/agents/config',
112
- )
113
- })
114
- })
package/src/agent.ts DELETED
@@ -1,335 +0,0 @@
1
- /**
2
- * Client for the Fayz agent broker — the runtime surface a generated app uses
3
- * to reach the AI agent configured for its project in Fayz.
4
- *
5
- * Mirrors `createFayzShopProvider`: the app holds a publishable key, Fayz holds
6
- * the model credentials. The key ships in the bundle the same way a Supabase
7
- * anon key does; it only ever resolves to the one agent its web channel belongs
8
- * to, and the broker rate-limits per project.
9
- *
10
- * Tools are client-plane. The app declares what it can do (`tools`), the model
11
- * decides what to call, and the app executes the calls against its own session
12
- * before handing results back. Fayz never needs the app's data credentials.
13
- */
14
-
15
- export interface FayzAgentClientOptions {
16
- /** Fayz API base, e.g. https://fayz.ai/api */
17
- baseUrl: string
18
- projectId: string
19
- /** Publishable key of the project's WEB agent channel. */
20
- publishableKey: string
21
- fetcher?: typeof fetch
22
- }
23
-
24
- export interface FayzAgentTool {
25
- name: string
26
- description: string
27
- /** JSON Schema for the tool's arguments. */
28
- parameters?: Record<string, unknown>
29
- }
30
-
31
- export interface FayzAgentToolCall {
32
- id: string
33
- name: string
34
- arguments: Record<string, unknown>
35
- }
36
-
37
- export interface FayzAgentToolResult {
38
- toolCallId: string
39
- content: string
40
- }
41
-
42
- /**
43
- * A file the user attached, described rather than uploaded.
44
- *
45
- * The bytes stay in the browser: a spreadsheet of clients or patients is
46
- * personal data, and handing it to a model provider so it can "take a look" is
47
- * a disclosure, not a convenience. What travels is the shape — sheets,
48
- * headers, how many rows, the inferred type per column and a handful of sample
49
- * rows. Enough to plan an import; never the data set itself.
50
- */
51
- export interface AgentAttachmentColumn {
52
- name: string
53
- type: 'text' | 'number' | 'date' | 'boolean' | 'email' | 'phone'
54
- /** Literal shape when known ('dd/mm/yyyy', '1.234,56'). */
55
- format?: string
56
- /** Why the column is not the type it looks like (ambiguous dates, mixed values). */
57
- note?: string
58
- }
59
-
60
- export interface AgentAttachmentSheet {
61
- name: string
62
- headers: string[]
63
- rowCount?: number
64
- sample: string[][]
65
- columnTypes: AgentAttachmentColumn[]
66
- }
67
-
68
- export interface AgentAttachmentDigest {
69
- id: string
70
- name: string
71
- mime: string
72
- size: number
73
- kind: 'spreadsheet' | 'csv' | 'text' | 'image' | 'pdf' | 'binary'
74
- /** Multi-tab spreadsheets. Single-tab files use the flat fields below. */
75
- sheets?: AgentAttachmentSheet[]
76
- headers?: string[]
77
- rowCount?: number
78
- sample?: string[][]
79
- columnTypes?: AgentAttachmentColumn[]
80
- /** Head of a text/markdown file. */
81
- preview?: string
82
- /** What the reader had to guess at, truncate or give up on. */
83
- warnings?: string[]
84
- }
85
-
86
- export interface FayzAgentChatInput {
87
- /** New end-user message. Omit when replying with tool results. */
88
- message?: string
89
- conversationId?: string
90
- /** Identity of the end user, required by channels with an INTERNAL audience. */
91
- externalUserId?: string
92
- /** Human label for that user, so the owner's dashboard shows a name. */
93
- externalUserName?: string
94
- tools?: FayzAgentTool[]
95
- toolResults?: FayzAgentToolResult[]
96
- /** Decision for a server-parked write (broker PENDING_CONFIRMATION step). */
97
- confirmAction?: { id: string; approved: boolean }
98
- /** Runtime context the agent should know about (page, locale, tenant). */
99
- context?: Record<string, unknown>
100
- /** Files the user attached this turn, as digests — never as bytes. */
101
- attachments?: AgentAttachmentDigest[]
102
- signal?: AbortSignal
103
- }
104
-
105
- /** A server-plane write the broker parked pending human confirmation. */
106
- export interface FayzAgentPendingAction {
107
- id: string
108
- toolName: string
109
- title?: string
110
- params?: Record<string, unknown>
111
- }
112
-
113
- export interface FayzAgentChatResponse {
114
- conversationId: string
115
- content: string
116
- toolCalls: FayzAgentToolCall[]
117
- /** Present when the broker is waiting on a confirmAction for this write. */
118
- pendingAction?: FayzAgentPendingAction
119
- }
120
-
121
- export interface FayzAgentInfo {
122
- id: string
123
- name: string
124
- description: string | null
125
- icon: string | null
126
- }
127
-
128
- export class FayzAgentError extends Error {
129
- constructor(
130
- message: string,
131
- readonly status: number,
132
- ) {
133
- super(message)
134
- this.name = 'FayzAgentError'
135
- }
136
- }
137
-
138
- export interface FayzAgentConversationSummary {
139
- id: string
140
- title: string | null
141
- /** Last activity — the field the list is ordered by. */
142
- updatedAt: string
143
- }
144
-
145
- export interface FayzAgentConversationMessage {
146
- id: string
147
- role: string
148
- content: string
149
- createdAt: string
150
- /** Files the user attached to this turn, as the digest that was sent. Only
151
- * the description — the bytes never left the browser that produced them. */
152
- attachments?: AgentAttachmentDigest[]
153
- /** Tool calls this reply made, with their results — so a resumed thread shows
154
- * the same trace and record links it showed live. */
155
- toolCalls?: Array<{
156
- name: string
157
- arguments?: Record<string, unknown>
158
- result?: string
159
- }>
160
- }
161
-
162
- export interface FayzAgentConversationDetail {
163
- id: string
164
- title: string | null
165
- messages: FayzAgentConversationMessage[]
166
- }
167
-
168
- export interface FayzAgentStreamCallbacks {
169
- /** Fired per text delta with the accumulated text so far (fill-in-place UIs). */
170
- onDelta?: (accumulated: string, delta: string) => void
171
- }
172
-
173
- export interface FayzAgentClient {
174
- /** Identity of the agent behind the key, for rendering its name/icon. */
175
- getInfo(signal?: AbortSignal): Promise<FayzAgentInfo>
176
- chat(input: FayzAgentChatInput): Promise<FayzAgentChatResponse>
177
- /**
178
- * Like `chat`, but asks the broker for an SSE stream (`Accept:
179
- * text/event-stream`) and reports text deltas as they arrive. Falls back
180
- * transparently to the JSON round-trip when the broker predates streaming —
181
- * callers can always use this and treat `onDelta` as best-effort.
182
- */
183
- chatStream(input: FayzAgentChatInput, callbacks?: FayzAgentStreamCallbacks): Promise<FayzAgentChatResponse>
184
- /** The signed-in user's own threads, most-recently-active first. */
185
- listConversations(externalUserId?: string, signal?: AbortSignal): Promise<FayzAgentConversationSummary[]>
186
- /** One thread with its transcript — 404s on another user's thread. */
187
- getConversation(
188
- conversationId: string,
189
- externalUserId?: string,
190
- signal?: AbortSignal,
191
- ): Promise<FayzAgentConversationDetail>
192
- }
193
-
194
- function normalizeResponse(body: Partial<FayzAgentChatResponse>): FayzAgentChatResponse {
195
- return {
196
- conversationId: body.conversationId ?? '',
197
- content: body.content ?? '',
198
- toolCalls: body.toolCalls ?? [],
199
- ...(body.pendingAction ? { pendingAction: body.pendingAction } : {}),
200
- }
201
- }
202
-
203
- export function createFayzAgentClient(options: FayzAgentClientOptions): FayzAgentClient {
204
- const fetcher = options.fetcher ?? globalThis.fetch
205
- const base = `${options.baseUrl.replace(/\/+$/, '')}/public/projects/${options.projectId}/agents`
206
- const headers = {
207
- 'Content-Type': 'application/json',
208
- 'x-fayz-agent-key': options.publishableKey,
209
- }
210
-
211
- async function request<T>(path: string, init: RequestInit): Promise<T> {
212
- const response = await fetcher(`${base}${path}`, { ...init, headers })
213
- if (!response.ok) {
214
- const body = (await response.json().catch(() => null)) as { error?: string } | null
215
- throw new FayzAgentError(body?.error ?? `Agent request failed (${response.status})`, response.status)
216
- }
217
- return (await response.json()) as T
218
- }
219
-
220
- return {
221
- async getInfo(signal) {
222
- const body = await request<{ agent: FayzAgentInfo }>('/config', { method: 'GET', signal })
223
- return body.agent
224
- },
225
-
226
- async chat({ signal, ...input }) {
227
- const body = await request<Partial<FayzAgentChatResponse>>('/chat', {
228
- method: 'POST',
229
- body: JSON.stringify(input),
230
- signal,
231
- })
232
- return normalizeResponse(body)
233
- },
234
-
235
- async chatStream({ signal, ...input }, callbacks) {
236
- const response = await fetcher(`${base}/chat`, {
237
- method: 'POST',
238
- body: JSON.stringify(input),
239
- signal,
240
- headers: { ...headers, Accept: 'text/event-stream' },
241
- })
242
- if (!response.ok) {
243
- const body = (await response.json().catch(() => null)) as { error?: string } | null
244
- throw new FayzAgentError(body?.error ?? `Agent request failed (${response.status})`, response.status)
245
- }
246
- // Broker predates streaming (or chose not to): plain JSON round-trip.
247
- const contentType = response.headers.get('content-type') ?? ''
248
- if (!contentType.includes('text/event-stream') || !response.body) {
249
- return normalizeResponse((await response.json()) as Partial<FayzAgentChatResponse>)
250
- }
251
-
252
- const reader = response.body.getReader()
253
- const decoder = new TextDecoder()
254
- let buffer = ''
255
- let accumulated = ''
256
- let done: Partial<FayzAgentChatResponse> | null = null
257
-
258
- const dispatch = (eventName: string, data: string) => {
259
- if (!data) return
260
- let payload: Record<string, unknown>
261
- try {
262
- payload = JSON.parse(data) as Record<string, unknown>
263
- } catch {
264
- return
265
- }
266
- if (eventName === 'delta') {
267
- const text = typeof payload.text === 'string' ? payload.text : ''
268
- if (text) {
269
- accumulated += text
270
- callbacks?.onDelta?.(accumulated, text)
271
- }
272
- } else if (eventName === 'done') {
273
- done = payload as Partial<FayzAgentChatResponse>
274
- } else if (eventName === 'error') {
275
- throw new FayzAgentError(
276
- typeof payload.error === 'string' ? payload.error : 'Agent stream failed',
277
- response.status,
278
- )
279
- }
280
- }
281
-
282
- let eventName = 'message'
283
- let dataLines: string[] = []
284
- const processLine = (line: string) => {
285
- if (line === '') {
286
- dispatch(eventName, dataLines.join('\n'))
287
- eventName = 'message'
288
- dataLines = []
289
- } else if (line.startsWith('event:')) {
290
- eventName = line.slice(6).trim()
291
- } else if (line.startsWith('data:')) {
292
- dataLines.push(line.slice(5).trimStart())
293
- }
294
- }
295
-
296
- for (;;) {
297
- const { value, done: streamDone } = await reader.read()
298
- if (streamDone) break
299
- buffer += decoder.decode(value, { stream: true })
300
- let newline = buffer.indexOf('\n')
301
- while (newline >= 0) {
302
- processLine(buffer.slice(0, newline).replace(/\r$/, ''))
303
- buffer = buffer.slice(newline + 1)
304
- newline = buffer.indexOf('\n')
305
- }
306
- }
307
- processLine(buffer.replace(/\r$/, ''))
308
- processLine('')
309
-
310
- // The final `done` event carries the same payload shape as the JSON
311
- // route; accumulated deltas back-fill `content` if the broker omits it.
312
- const finalPayload: Partial<FayzAgentChatResponse> = { ...(done ?? {}) }
313
- if (!finalPayload.content) finalPayload.content = accumulated
314
- return normalizeResponse(finalPayload)
315
- },
316
-
317
- async listConversations(externalUserId, signal) {
318
- const query = externalUserId ? `?externalUserId=${encodeURIComponent(externalUserId)}` : ''
319
- const body = await request<{ conversations?: FayzAgentConversationSummary[] }>(
320
- `/conversations${query}`,
321
- { method: 'GET', signal },
322
- )
323
- return body.conversations ?? []
324
- },
325
-
326
- async getConversation(conversationId, externalUserId, signal) {
327
- const query = externalUserId ? `?externalUserId=${encodeURIComponent(externalUserId)}` : ''
328
- const body = await request<{ conversation: FayzAgentConversationDetail }>(
329
- `/conversations/${encodeURIComponent(conversationId)}${query}`,
330
- { method: 'GET', signal },
331
- )
332
- return body.conversation
333
- },
334
- }
335
- }
package/src/ai-builder.ts DELETED
@@ -1,115 +0,0 @@
1
- // The AI Builder request taxonomy — the contract the platform's request
2
- // classifier targets. The classifier itself lives in the `fayz` platform repo;
3
- // this is the canonical source of truth for the request classes and the
4
- // architecture layer each is allowed to touch (docs/ARCHITECTURE.md).
5
-
6
- /** Ownership layer a request may modify. */
7
- export type FayzLayer = 'app' | 'plugin-config' | 'private-extension' | 'platform' | 'none'
8
-
9
- export type AiBuilderRequestClass =
10
- | 'app-edit'
11
- | 'plugin-config'
12
- | 'private-extension'
13
- | 'platform-or-plugin-upgrade'
14
- | 'unsafe-blocked'
15
-
16
- export interface AiBuilderRequestClassDef {
17
- id: AiBuilderRequestClass
18
- label: string
19
- /** Which ownership layer this class is allowed to modify. */
20
- layer: FayzLayer
21
- /** Who may perform it. */
22
- editableBy: 'ai' | 'partner' | 'fayz' | 'none'
23
- needsCode: boolean
24
- needsDeploy: boolean
25
- description: string
26
- examples: string[]
27
- }
28
-
29
- export const AI_BUILDER_REQUEST_CLASSES: Record<AiBuilderRequestClass, AiBuilderRequestClassDef> = {
30
- 'app-edit': {
31
- id: 'app-edit',
32
- label: 'App edit',
33
- layer: 'app',
34
- editableBy: 'ai',
35
- needsCode: false,
36
- needsDeploy: false,
37
- description:
38
- 'Change the generated app: pages, routes, layout, theme, copy, blocks, slots. Manifest-level edits (ladder 1–4) need no code; custom components/pages (ladder 5–6) are app-owned code.',
39
- examples: [
40
- 'Change the brand color and headline',
41
- 'Add a new landing page with a hero and product grid',
42
- 'Reorder dashboard cards',
43
- ],
44
- },
45
- 'plugin-config': {
46
- id: 'plugin-config',
47
- label: 'Plugin config',
48
- layer: 'plugin-config',
49
- editableBy: 'ai',
50
- needsCode: false,
51
- needsDeploy: false,
52
- description:
53
- 'Configure an installed plugin as manifest data: enabled modules, fields, statuses, rules, feature flags, labels, currency.',
54
- examples: [
55
- 'Enable the reconciliation module in financial',
56
- 'Add a "VIP" status to clients',
57
- 'Turn on delivery pricing in the menu',
58
- ],
59
- },
60
- 'private-extension': {
61
- id: 'private-extension',
62
- label: 'Private extension',
63
- layer: 'private-extension',
64
- editableBy: 'partner',
65
- needsCode: true,
66
- needsDeploy: true,
67
- description:
68
- 'Add new business behaviour as an app-local plugin (own entities, data, migrations, AI tools, connectors). The sanctioned partner/Silvio path — scaffold with `fayz create plugin`. See CUSTOMIZATION.md.',
69
- examples: [
70
- 'Build a loyalty-points engine with its own table',
71
- 'Add a custom importer for a legacy ERP',
72
- 'Integrate a provider Fayz does not support yet (adapter)',
73
- ],
74
- },
75
- 'platform-or-plugin-upgrade': {
76
- id: 'platform-or-plugin-upgrade',
77
- label: 'Platform / plugin upgrade',
78
- layer: 'platform',
79
- editableBy: 'fayz',
80
- needsCode: true,
81
- needsDeploy: true,
82
- description:
83
- 'Change owned by Fayz: bump SDK/plugin versions, run manifest/DB migrations, evolve an engine. Not editable from a generated app.',
84
- examples: [
85
- 'Upgrade @fayz-ai/core across the fleet',
86
- 'Migrate manifests v2 → v3',
87
- 'Add a capability to an official plugin',
88
- ],
89
- },
90
- 'unsafe-blocked': {
91
- id: 'unsafe-blocked',
92
- label: 'Unsafe / blocked',
93
- layer: 'none',
94
- editableBy: 'none',
95
- needsCode: false,
96
- needsDeploy: false,
97
- description:
98
- 'Requests that would break the boundary and must be refused or rerouted: mutating SDK/runtime/plugin internals, forking an SDK page, importing a provider SDK directly into app code, or bypassing tenancy/permissions/security.',
99
- examples: [
100
- 'Edit the code inside @fayz-ai/plugin-crm',
101
- 'Copy an SDK page into the app and modify it',
102
- 'Call the Supabase client directly from a storefront component',
103
- 'Disable row-level security for a tenant',
104
- ],
105
- },
106
- }
107
-
108
- export const AI_BUILDER_REQUEST_CLASS_IDS = Object.keys(
109
- AI_BUILDER_REQUEST_CLASSES,
110
- ) as AiBuilderRequestClass[]
111
-
112
- /** True when a request class is permitted (anything but unsafe-blocked). */
113
- export function isAllowedRequestClass(id: AiBuilderRequestClass): boolean {
114
- return id !== 'unsafe-blocked'
115
- }
package/src/app-params.ts DELETED
@@ -1,69 +0,0 @@
1
- import type { FayzAppParams, FayzRuntimeEnvironment } from './types'
2
-
3
- function readSearchParams(): URLSearchParams {
4
- const href = globalThis.location?.href
5
- return href ? new URL(href).searchParams : new URLSearchParams()
6
- }
7
-
8
- function readMeta(name: string): string | undefined {
9
- const documentRef = globalThis.document
10
- const value = documentRef?.querySelector<HTMLMetaElement>(`meta[name="${name}"]`)?.content?.trim()
11
- return value || undefined
12
- }
13
-
14
- function readEnv(name: string): string | undefined {
15
- const env = (import.meta as unknown as { env?: Record<string, string | undefined> }).env
16
- return env?.[name]?.trim() || undefined
17
- }
18
-
19
- function firstValue(...values: Array<string | undefined>): string | undefined {
20
- return values.find((value) => value && value.trim())?.trim()
21
- }
22
-
23
- function normalizeEnvironment(value?: string): FayzRuntimeEnvironment {
24
- return value === 'production' ? 'production' : 'preview'
25
- }
26
-
27
- export function resolveAppParams(): FayzAppParams {
28
- const search = readSearchParams()
29
- const token = firstValue(
30
- search.get('token') ?? undefined,
31
- search.get('fayz_token') ?? undefined,
32
- readMeta('fayz-token'),
33
- readEnv('VITE_FAYZ_TOKEN'),
34
- )
35
- const appId = firstValue(
36
- search.get('appId') ?? undefined,
37
- search.get('app_id') ?? undefined,
38
- readMeta('fayz-app-id'),
39
- readEnv('VITE_FAYZ_APP_ID'),
40
- )
41
- const projectId = firstValue(
42
- search.get('projectId') ?? undefined,
43
- search.get('project_id') ?? undefined,
44
- readMeta('fayz-project-id'),
45
- readEnv('VITE_FAYZ_PROJECT_ID'),
46
- )
47
- const apiBaseUrl = firstValue(
48
- search.get('apiBaseUrl') ?? undefined,
49
- search.get('api_base_url') ?? undefined,
50
- readMeta('fayz-api-base-url'),
51
- readEnv('VITE_FAYZ_API_BASE_URL'),
52
- globalThis.location?.origin,
53
- ) ?? ''
54
- const environment = normalizeEnvironment(firstValue(
55
- search.get('environment') ?? undefined,
56
- readMeta('fayz-environment'),
57
- readEnv('VITE_FAYZ_ENVIRONMENT'),
58
- ))
59
-
60
- return {
61
- apiBaseUrl,
62
- environment,
63
- ...(appId ? { appId } : {}),
64
- ...(projectId ? { projectId } : {}),
65
- ...(token ? { token } : {}),
66
- }
67
- }
68
-
69
- export const appParams = resolveAppParams()