@fayz-ai/saas 0.8.0 → 0.8.2
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.
- package/dist/access/context.d.ts +47 -0
- package/dist/access/context.d.ts.map +1 -0
- package/dist/access/headless.d.ts +21 -0
- package/dist/access/headless.d.ts.map +1 -0
- package/dist/access/index.d.ts +12 -0
- package/dist/access/index.d.ts.map +1 -0
- package/dist/access/limits-registry.d.ts +33 -0
- package/dist/access/limits-registry.d.ts.map +1 -0
- package/dist/access/module-nav.d.ts +15 -0
- package/dist/access/module-nav.d.ts.map +1 -0
- package/dist/access/resolver.d.ts +2 -0
- package/dist/access/resolver.d.ts.map +1 -0
- package/dist/access/types.d.ts +43 -0
- package/dist/access/types.d.ts.map +1 -0
- package/dist/access/upgrade-modal-store.d.ts +23 -0
- package/dist/access/upgrade-modal-store.d.ts.map +1 -0
- package/dist/app/AdminShell.d.ts +3 -0
- package/dist/app/AdminShell.d.ts.map +1 -1
- package/dist/app/admin-app.d.ts +3 -1
- package/dist/app/admin-app.d.ts.map +1 -1
- package/dist/app/config.d.ts +47 -2
- package/dist/app/config.d.ts.map +1 -1
- package/dist/app/core-entities.d.ts +25 -0
- package/dist/app/core-entities.d.ts.map +1 -0
- package/dist/app/derive-contract.d.ts +8 -0
- package/dist/app/derive-contract.d.ts.map +1 -0
- package/dist/app/scaffold.d.ts.map +1 -1
- package/dist/archetype-lookup.d.ts.map +1 -1
- package/dist/billing/index.cjs +6 -2
- package/dist/billing/index.d.ts +2 -0
- package/dist/billing/index.d.ts.map +1 -1
- package/dist/billing/index.js +1 -1
- package/dist/billing/plan-badge.d.ts +17 -0
- package/dist/billing/plan-badge.d.ts.map +1 -0
- package/dist/billing/store.d.ts +6 -1
- package/dist/billing/store.d.ts.map +1 -1
- package/dist/{chunk-3M25GRRU.js → chunk-I3DY47N5.js} +3 -4
- package/dist/chunk-I3DY47N5.js.map +1 -0
- package/dist/chunk-NNFGMUF2.cjs +39 -0
- package/dist/chunk-NNFGMUF2.cjs.map +1 -0
- package/dist/{chunk-4XTQ4V5L.cjs → chunk-SAZLW54Y.cjs} +16 -12
- package/dist/chunk-SAZLW54Y.cjs.map +1 -0
- package/dist/{chunk-IPWCHJPE.cjs → chunk-VKYZACG5.cjs} +5 -6
- package/dist/chunk-VKYZACG5.cjs.map +1 -0
- package/dist/chunk-VN5NTK26.js +36 -0
- package/dist/chunk-VN5NTK26.js.map +1 -0
- package/dist/{chunk-XNWCGJYH.js → chunk-VTEP2AQX.js} +15 -11
- package/dist/chunk-VTEP2AQX.js.map +1 -0
- package/dist/components/shared/ContactPicker.d.ts +92 -0
- package/dist/components/shared/ContactPicker.d.ts.map +1 -0
- package/dist/components/shared/PersonLink.d.ts +12 -1
- package/dist/components/shared/PersonLink.d.ts.map +1 -1
- package/dist/components/shared/create-person.d.ts +20 -0
- package/dist/components/shared/create-person.d.ts.map +1 -0
- package/dist/crud/CrudDetailPage.d.ts.map +1 -1
- package/dist/crud/CrudFormPage.d.ts.map +1 -1
- package/dist/crud/CrudListView.d.ts.map +1 -1
- package/dist/crud/CrudPage.d.ts.map +1 -1
- package/dist/crud/archetypes/AddressesTab.d.ts +21 -0
- package/dist/crud/archetypes/AddressesTab.d.ts.map +1 -0
- package/dist/crud/archetypes/PersonDetailTabs.d.ts +2 -1
- package/dist/crud/archetypes/PersonDetailTabs.d.ts.map +1 -1
- package/dist/crud/createCrudPage.d.ts.map +1 -1
- package/dist/crud/index.d.ts +1 -0
- package/dist/crud/index.d.ts.map +1 -1
- package/dist/index.cjs +4300 -722
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +17 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4080 -542
- package/dist/index.js.map +1 -1
- package/dist/org/index.cjs +11 -11
- package/dist/org/index.js +2 -2
- package/dist/org/store.d.ts.map +1 -1
- package/dist/permissions/context.d.ts +2 -0
- package/dist/permissions/context.d.ts.map +1 -1
- package/dist/permissions/index.cjs +7 -7
- package/dist/permissions/index.js +1 -1
- package/dist/shell/components/auth/ProtectedRoute.d.ts.map +1 -1
- package/dist/shell/components/billing/SoftLimitBanner.d.ts +5 -0
- package/dist/shell/components/billing/SoftLimitBanner.d.ts.map +1 -0
- package/dist/shell/components/billing/SubscriptionPage.d.ts +5 -0
- package/dist/shell/components/billing/SubscriptionPage.d.ts.map +1 -0
- package/dist/shell/components/billing/UpgradeModal.d.ts +3 -0
- package/dist/shell/components/billing/UpgradeModal.d.ts.map +1 -0
- package/dist/shell/components/billing/UpgradePrompt.d.ts +10 -0
- package/dist/shell/components/billing/UpgradePrompt.d.ts.map +1 -0
- package/dist/shell/components/billing/access-contract.d.ts +18 -0
- package/dist/shell/components/billing/access-contract.d.ts.map +1 -0
- package/dist/shell/components/billing/gates.d.ts +14 -0
- package/dist/shell/components/billing/gates.d.ts.map +1 -0
- package/dist/shell/components/billing/index.d.ts +9 -2
- package/dist/shell/components/billing/index.d.ts.map +1 -1
- package/dist/shell/components/chat/ChatFab.d.ts +3 -1
- package/dist/shell/components/chat/ChatFab.d.ts.map +1 -1
- package/dist/shell/components/chat/ChatPanel.d.ts +3 -1
- package/dist/shell/components/chat/ChatPanel.d.ts.map +1 -1
- package/dist/shell/components/chat/ConfirmActionCard.d.ts +13 -0
- package/dist/shell/components/chat/ConfirmActionCard.d.ts.map +1 -0
- package/dist/shell/components/chat/markdown.d.ts +5 -0
- package/dist/shell/components/chat/markdown.d.ts.map +1 -0
- package/dist/shell/components/organization/TeamTab.d.ts.map +1 -1
- package/dist/shell/components/organization/TenantOnboarding.d.ts.map +1 -1
- package/dist/shell/components/organization/index.d.ts +0 -1
- package/dist/shell/components/organization/index.d.ts.map +1 -1
- package/dist/shell/components/settings/ConnectedCompanySettings.d.ts.map +1 -1
- package/dist/shell/components/settings/ConnectedFieldRulesSettings.d.ts.map +1 -1
- package/dist/shell/components/settings/ConnectedSecuritySettings.d.ts.map +1 -1
- package/dist/shell/components/settings/LocationsCrudPage.d.ts +1 -1
- package/dist/shell/components/settings/LocationsCrudPage.d.ts.map +1 -1
- package/dist/shell/components/settings/SettingsPage.d.ts +23 -2
- package/dist/shell/components/settings/SettingsPage.d.ts.map +1 -1
- package/dist/shell/hooks/index.d.ts +3 -2
- package/dist/shell/hooks/index.d.ts.map +1 -1
- package/dist/shell/hooks/useAITools.d.ts +2 -0
- package/dist/shell/hooks/useAITools.d.ts.map +1 -1
- package/dist/shell/hooks/useChat.d.ts +22 -0
- package/dist/shell/hooks/useChat.d.ts.map +1 -1
- package/dist/shell/hooks/useSystemPermission.d.ts +3 -0
- package/dist/shell/hooks/useSystemPermission.d.ts.map +1 -0
- package/dist/shell/hooks/useTenantPluginSettings.d.ts +12 -0
- package/dist/shell/hooks/useTenantPluginSettings.d.ts.map +1 -0
- package/dist/shell/lib/ai-tool-handlers.d.ts +119 -0
- package/dist/shell/lib/ai-tool-handlers.d.ts.map +1 -0
- package/dist/shell/lib/core-ai-tools.d.ts +14 -0
- package/dist/shell/lib/core-ai-tools.d.ts.map +1 -1
- package/dist/shell/lib/create-client-orders-provider.d.ts.map +1 -1
- package/dist/shell/lib/fayz-agent.d.ts +23 -0
- package/dist/shell/lib/fayz-agent.d.ts.map +1 -0
- package/dist/shell/lib/i18n.d.ts.map +1 -1
- package/dist/shell/stores/chat.store.d.ts +69 -0
- package/dist/shell/stores/chat.store.d.ts.map +1 -1
- package/dist/shell/types/billing.d.ts +7 -0
- package/dist/shell/types/billing.d.ts.map +1 -1
- package/dist/shell/types/crud.d.ts +1 -1
- package/dist/shell/types/crud.d.ts.map +1 -1
- package/dist/shell/types/plugins.d.ts +2 -45
- package/dist/shell/types/plugins.d.ts.map +1 -1
- package/dist/stores/createCrudStore.d.ts.map +1 -1
- package/package.json +15 -12
- package/src/access/context.tsx +228 -0
- package/src/access/headless.ts +71 -0
- package/src/access/index.ts +27 -0
- package/src/access/limits-registry.ts +86 -0
- package/src/access/module-nav.test.ts +78 -0
- package/src/access/module-nav.ts +82 -0
- package/src/access/resolver.test.ts +143 -0
- package/src/access/resolver.ts +5 -0
- package/src/access/types.ts +47 -0
- package/src/access/upgrade-modal-store.ts +30 -0
- package/src/app/AdminShell.tsx +451 -68
- package/src/app/admin-app.tsx +46 -9
- package/src/app/config.ts +54 -1
- package/src/app/core-entities.ts +110 -0
- package/src/app/derive-contract.test.ts +155 -0
- package/src/app/derive-contract.ts +214 -0
- package/src/app/scaffold.tsx +4 -0
- package/src/archetype-lookup.ts +8 -2
- package/src/billing/index.ts +2 -0
- package/src/billing/plan-badge.ts +32 -0
- package/src/billing/store.ts +8 -1
- package/src/components/shared/ContactPicker.tsx +394 -0
- package/src/components/shared/PersonLink.tsx +11 -3
- package/src/components/shared/create-person.ts +74 -0
- package/src/crud/CrudDetailPage.tsx +8 -1
- package/src/crud/CrudFormPage.tsx +124 -6
- package/src/crud/CrudListView.tsx +16 -1
- package/src/crud/CrudPage.tsx +27 -5
- package/src/crud/DeleteConfirmDialog.tsx +1 -1
- package/src/crud/archetypes/AddressesTab.tsx +387 -0
- package/src/crud/archetypes/PersonDetailTabs.tsx +5 -0
- package/src/crud/createCrudPage.tsx +2 -0
- package/src/crud/index.ts +6 -0
- package/src/env.d.ts +7 -0
- package/src/index.ts +49 -1
- package/src/org/store.ts +9 -1
- package/src/permissions/context.tsx +29 -15
- package/src/shell/components/auth/ProtectedRoute.tsx +12 -2
- package/src/shell/components/billing/SoftLimitBanner.tsx +81 -0
- package/src/shell/components/billing/SubscriptionPage.tsx +372 -0
- package/src/shell/components/billing/UpgradeModal.tsx +102 -0
- package/src/shell/components/billing/UpgradePrompt.tsx +229 -0
- package/src/shell/components/billing/access-contract.ts +64 -0
- package/src/shell/components/billing/gates.tsx +62 -0
- package/src/shell/components/billing/index.ts +18 -2
- package/src/shell/components/chat/ChatFab.tsx +4 -2
- package/src/shell/components/chat/ChatPanel.tsx +174 -23
- package/src/shell/components/chat/ConfirmActionCard.tsx +57 -0
- package/src/shell/components/chat/markdown.tsx +102 -0
- package/src/shell/components/organization/TeamTab.tsx +21 -4
- package/src/shell/components/organization/TenantOnboarding.tsx +3 -1
- package/src/shell/components/organization/index.ts +0 -1
- package/src/shell/components/settings/ConnectedCompanySettings.tsx +4 -1
- package/src/shell/components/settings/ConnectedFieldRulesSettings.tsx +3 -1
- package/src/shell/components/settings/ConnectedSecuritySettings.tsx +3 -1
- package/src/shell/components/settings/LocationsCrudPage.ts +6 -1
- package/src/shell/components/settings/SettingsPage.tsx +63 -21
- package/src/shell/hooks/index.ts +3 -2
- package/src/shell/hooks/useAITools.ts +27 -23
- package/src/shell/hooks/useChat.ts +381 -16
- package/src/shell/hooks/useSystemPermission.ts +23 -0
- package/src/shell/hooks/useTenantPluginSettings.ts +127 -0
- package/src/shell/lib/ai-tool-handlers.ts +910 -0
- package/src/shell/lib/core-ai-tools.ts +234 -2
- package/src/shell/lib/create-client-orders-provider.ts +24 -2
- package/src/shell/lib/fayz-agent.ts +56 -0
- package/src/shell/lib/i18n.ts +71 -0
- package/src/shell/stores/chat.store.ts +118 -1
- package/src/shell/styles.css +30 -0
- package/src/shell/types/billing.ts +8 -0
- package/src/shell/types/crud.ts +1 -1
- package/src/shell/types/plugins.ts +13 -50
- package/src/stores/createCrudStore.ts +9 -0
- package/dist/chunk-3M25GRRU.js.map +0 -1
- package/dist/chunk-4XTQ4V5L.cjs.map +0 -1
- package/dist/chunk-5GYQK5IW.js +0 -21
- package/dist/chunk-5GYQK5IW.js.map +0 -1
- package/dist/chunk-IPWCHJPE.cjs.map +0 -1
- package/dist/chunk-KYID2MR6.cjs +0 -23
- package/dist/chunk-KYID2MR6.cjs.map +0 -1
- package/dist/chunk-XNWCGJYH.js.map +0 -1
- package/dist/shell/components/billing/BillingPage.d.ts +0 -7
- package/dist/shell/components/billing/BillingPage.d.ts.map +0 -1
- package/dist/shell/components/billing/PaywallGate.d.ts +0 -11
- package/dist/shell/components/billing/PaywallGate.d.ts.map +0 -1
- package/dist/shell/components/organization/PermissionGate.d.ts +0 -11
- package/dist/shell/components/organization/PermissionGate.d.ts.map +0 -1
- package/dist/shell/hooks/useBilling.d.ts +0 -14
- package/dist/shell/hooks/useBilling.d.ts.map +0 -1
- package/dist/shell/hooks/usePermission.d.ts +0 -16
- package/dist/shell/hooks/usePermission.d.ts.map +0 -1
- package/src/shell/components/billing/BillingPage.tsx +0 -56
- package/src/shell/components/billing/PaywallGate.tsx +0 -80
- package/src/shell/components/organization/PermissionGate.tsx +0 -16
- package/src/shell/hooks/useBilling.ts +0 -151
- package/src/shell/hooks/usePermission.ts +0 -101
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AgentContract,
|
|
3
|
+
AIToolContract,
|
|
4
|
+
EntityContract,
|
|
5
|
+
EntityDef,
|
|
6
|
+
FeatureDeclaration,
|
|
7
|
+
LimitDeclaration,
|
|
8
|
+
PluginAITool,
|
|
9
|
+
PluginManifest,
|
|
10
|
+
RegisteredEntity,
|
|
11
|
+
} from '@fayz-ai/core'
|
|
12
|
+
import { getAllEntities, deriveEntityKey } from '@fayz-ai/core'
|
|
13
|
+
import { mergeLimitDeclarations } from '@fayz-ai/core/access'
|
|
14
|
+
import { coreAITools, buildDataPrimitiveTools } from '../shell/lib/core-ai-tools'
|
|
15
|
+
import { CORE_LIMIT_DECLARATIONS, entityDerivedLimitDeclarations } from './../access/limits-registry'
|
|
16
|
+
import { CORE_QUERY_ENTITIES } from './core-entities'
|
|
17
|
+
import type { FayzAppConfig } from './config'
|
|
18
|
+
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// deriveAgentContract — FayzAppConfig → the manifest v3 `agent` section +
|
|
21
|
+
// resolved `limitDeclarations`. This is the "capabilities are derived, never
|
|
22
|
+
// configured" principle applied to the server side: everything the Fayz broker
|
|
23
|
+
// needs to operate the app without a browser is PROJECTED from what the app
|
|
24
|
+
// already declares (entities, plugins, permissions, billing), never re-typed.
|
|
25
|
+
// Pure data out — safe to run at defineSaas time and in `fayz manifest emit`.
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
function projectEntity(key: string, label: string, labelPlural: string | undefined, def: EntityDef): EntityContract | null {
|
|
29
|
+
const data = def.data
|
|
30
|
+
if (!data?.table) return null
|
|
31
|
+
return {
|
|
32
|
+
key,
|
|
33
|
+
label,
|
|
34
|
+
...(labelPlural ? { labelPlural } : {}),
|
|
35
|
+
...(def.limitKey ? { limitKey: def.limitKey } : {}),
|
|
36
|
+
...(def.permission ? { permission: def.permission } : {}),
|
|
37
|
+
data: {
|
|
38
|
+
table: data.table,
|
|
39
|
+
...(data.schema ? { schema: data.schema } : {}),
|
|
40
|
+
...(data.tenantScoped !== undefined ? { tenantScoped: data.tenantScoped } : {}),
|
|
41
|
+
...(data.tenantIdColumn ? { tenantIdColumn: data.tenantIdColumn } : {}),
|
|
42
|
+
...(data.searchColumns ? { searchColumns: data.searchColumns } : {}),
|
|
43
|
+
...(data.selectColumns ? { selectColumns: data.selectColumns } : {}),
|
|
44
|
+
...(data.columnMap ? { columnMap: data.columnMap } : {}),
|
|
45
|
+
...(data.archetype ? { archetype: data.archetype } : {}),
|
|
46
|
+
...(data.archetypeKind ? { archetypeKind: data.archetypeKind } : {}),
|
|
47
|
+
...(data.filters ? { filters: data.filters } : {}),
|
|
48
|
+
},
|
|
49
|
+
fields: (def.fields ?? []).map((f) => ({
|
|
50
|
+
key: f.key,
|
|
51
|
+
label: f.label,
|
|
52
|
+
type: f.type,
|
|
53
|
+
...(f.required ? { required: true } : {}),
|
|
54
|
+
...(f.searchable ? { searchable: true } : {}),
|
|
55
|
+
// The hint is the only place a field says what it EXPECTS rather than
|
|
56
|
+
// what it is called — an owner id to look up, a default not to restate.
|
|
57
|
+
// Dropping it here is what left the agent guessing at write time.
|
|
58
|
+
...(f.hint ? { hint: f.hint } : {}),
|
|
59
|
+
...(Array.isArray(f.options) && f.options.length
|
|
60
|
+
? { options: f.options.map((o) => (typeof o === 'string' ? o : o.value)) }
|
|
61
|
+
: {}),
|
|
62
|
+
...(f.relation?.table ? { relationTable: f.relation.table } : {}),
|
|
63
|
+
})),
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function projectTool(tool: PluginAITool, pluginId?: string): AIToolContract {
|
|
68
|
+
return {
|
|
69
|
+
id: tool.id,
|
|
70
|
+
name: tool.name,
|
|
71
|
+
description: tool.description,
|
|
72
|
+
mode: tool.mode,
|
|
73
|
+
...(tool.parameters ? { parameters: tool.parameters } : {}),
|
|
74
|
+
...(tool.permission ? { permission: tool.permission } : {}),
|
|
75
|
+
...(tool.limitKey ? { limitKey: tool.limitKey } : {}),
|
|
76
|
+
...(tool.execution ? { execution: tool.execution } : {}),
|
|
77
|
+
// Persist tools default to human confirmation — an agent never clicks a
|
|
78
|
+
// gated button, so the contract is where the guard is declared.
|
|
79
|
+
...(tool.mode === 'persist' ? { requiresConfirmation: tool.requiresConfirmation ?? true } : {}),
|
|
80
|
+
...(pluginId ? { pluginId } : {}),
|
|
81
|
+
...(tool.category ? { category: tool.category } : {}),
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Merge plugin-declared features with the app's own, app wins by id — the
|
|
86
|
+
* same rule PermissionsProvider applies to the live matrix. */
|
|
87
|
+
function mergeFeatureDeclarations(
|
|
88
|
+
plugins: PluginManifest[],
|
|
89
|
+
appFeatures: FeatureDeclaration[] | undefined,
|
|
90
|
+
): FeatureDeclaration[] {
|
|
91
|
+
const byId = new Map<string, FeatureDeclaration>()
|
|
92
|
+
for (const plugin of plugins) {
|
|
93
|
+
for (const f of plugin.declaredFeatures ?? []) byId.set(f.id, f)
|
|
94
|
+
}
|
|
95
|
+
for (const f of appFeatures ?? []) byId.set(f.id, f)
|
|
96
|
+
return Array.from(byId.values())
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface DerivedAgentSections {
|
|
100
|
+
agent: AgentContract
|
|
101
|
+
limitDeclarations: LimitDeclaration[]
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function deriveAgentContract(
|
|
105
|
+
config: FayzAppConfig,
|
|
106
|
+
entities: RegisteredEntity[] = getAllEntities(),
|
|
107
|
+
): DerivedAgentSections {
|
|
108
|
+
const plugins = config.plugins ?? []
|
|
109
|
+
|
|
110
|
+
// --- Entities: registered CRUD entities + plugin registry entities --------
|
|
111
|
+
const entityContracts: EntityContract[] = []
|
|
112
|
+
for (const e of entities) {
|
|
113
|
+
if (!e.entityDef) continue
|
|
114
|
+
const projected = projectEntity(e.entityKey, e.label, e.labelPlural, e.entityDef)
|
|
115
|
+
if (projected) entityContracts.push(projected)
|
|
116
|
+
}
|
|
117
|
+
const registries: NonNullable<AgentContract['registries']> = []
|
|
118
|
+
for (const plugin of plugins) {
|
|
119
|
+
for (const registry of plugin.registries ?? []) {
|
|
120
|
+
const entityKey = `${plugin.id}:${registry.id}`
|
|
121
|
+
registries.push({
|
|
122
|
+
pluginId: plugin.id,
|
|
123
|
+
id: registry.id,
|
|
124
|
+
entityKey,
|
|
125
|
+
...(registry.readOnly ? { readOnly: true } : {}),
|
|
126
|
+
})
|
|
127
|
+
const projected = projectEntity(
|
|
128
|
+
entityKey,
|
|
129
|
+
registry.entity.name,
|
|
130
|
+
registry.entity.namePlural,
|
|
131
|
+
registry.entity,
|
|
132
|
+
)
|
|
133
|
+
if (projected) entityContracts.push(projected)
|
|
134
|
+
}
|
|
135
|
+
for (const q of plugin.queryEntities ?? []) {
|
|
136
|
+
const projected = projectEntity(q.key, q.entity.name, q.entity.namePlural, q.entity)
|
|
137
|
+
if (projected) entityContracts.push(projected)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
for (const q of config.agentContract?.queryEntities ?? []) {
|
|
141
|
+
const projected = projectEntity(q.key, q.entity.name, q.entity.namePlural, q.entity)
|
|
142
|
+
if (projected) entityContracts.push(projected)
|
|
143
|
+
}
|
|
144
|
+
// Spine tables whose surface is a detail TAB, not a page — no app declares
|
|
145
|
+
// them as CRUD entities, so without this the agent cannot see them at all.
|
|
146
|
+
for (const q of CORE_QUERY_ENTITIES) {
|
|
147
|
+
const projected = projectEntity(q.key, q.entity.name, q.entity.namePlural, q.entity)
|
|
148
|
+
if (projected) entityContracts.push(projected)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// --- Tools: core + derived reads + plugin-declared --------------------------
|
|
152
|
+
const tools: AIToolContract[] = []
|
|
153
|
+
// Core handlers run in the surface (org store / router) — client plane.
|
|
154
|
+
for (const tool of coreAITools) {
|
|
155
|
+
tools.push({ ...projectTool(tool), execution: { plane: 'client' } })
|
|
156
|
+
}
|
|
157
|
+
// The data primitives: one search + one aggregate over every entity/registry.
|
|
158
|
+
// `entity: '*'` = the target arrives as the call's `entity` argument; the
|
|
159
|
+
// executor resolves it against `agent.entities` and enforces that entity's
|
|
160
|
+
// own read permission.
|
|
161
|
+
const registryMap = new Map(plugins.map((p) => [p.id, p.registries ?? []]))
|
|
162
|
+
const queryEntities = [
|
|
163
|
+
...plugins.flatMap((p) => p.queryEntities ?? []),
|
|
164
|
+
...(config.agentContract?.queryEntities ?? []),
|
|
165
|
+
// CORE_QUERY_ENTITIES is NOT listed here — buildDataPrimitiveTools merges
|
|
166
|
+
// it in itself, for every caller. It is projected into `entityContracts`
|
|
167
|
+
// above because that list is what the server-plane executor resolves a
|
|
168
|
+
// tool call's `entity` argument against.
|
|
169
|
+
]
|
|
170
|
+
for (const tool of buildDataPrimitiveTools({ entities, registries: registryMap, queryEntities })) {
|
|
171
|
+
tools.push({ ...projectTool(tool), execution: { plane: 'server', kind: 'entity_read', entity: '*' } })
|
|
172
|
+
}
|
|
173
|
+
// Plugin-authored tools keep their declared execution (persist tools bind to
|
|
174
|
+
// RPCs via `execution: { kind:'rpc' }`; absent ⇒ client-plane executor).
|
|
175
|
+
for (const plugin of plugins) {
|
|
176
|
+
for (const tool of plugin.aiTools ?? []) {
|
|
177
|
+
tools.push(projectTool(tool, plugin.id))
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// --- Limits: the same 4-layer merge the AccessProvider publishes ----------
|
|
182
|
+
const limitDeclarations = mergeLimitDeclarations(
|
|
183
|
+
CORE_LIMIT_DECLARATIONS,
|
|
184
|
+
entityDerivedLimitDeclarations(entities),
|
|
185
|
+
plugins.flatMap((p) => p.declaredLimits ?? []),
|
|
186
|
+
config.billing?.limitDeclarations,
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
// --- RPCs: plugin-shipped + app-shipped ------------------------------------
|
|
190
|
+
const rpcs = [
|
|
191
|
+
...plugins.flatMap((p) => p.declaredRpcs ?? []),
|
|
192
|
+
...(config.agentContract?.rpcs ?? []),
|
|
193
|
+
]
|
|
194
|
+
|
|
195
|
+
const agent: AgentContract = {
|
|
196
|
+
...(config.chat?.title || config.chat?.systemPrompt
|
|
197
|
+
? {
|
|
198
|
+
persona: {
|
|
199
|
+
...(config.chat.title ? { name: config.chat.title } : {}),
|
|
200
|
+
...(config.chat.systemPrompt ? { systemPrompt: config.chat.systemPrompt } : {}),
|
|
201
|
+
},
|
|
202
|
+
}
|
|
203
|
+
: {}),
|
|
204
|
+
executionPlane: config.agentContract?.executionPlane ?? 'client',
|
|
205
|
+
entities: entityContracts,
|
|
206
|
+
tools,
|
|
207
|
+
...(registries.length ? { registries } : {}),
|
|
208
|
+
declaredFeatures: mergeFeatureDeclarations(plugins, config.permissions?.features),
|
|
209
|
+
...(rpcs.length ? { rpcs } : {}),
|
|
210
|
+
...(config.agentContract?.knowledge ? { domainKnowledge: config.agentContract.knowledge } : {}),
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return { agent, limitDeclarations }
|
|
214
|
+
}
|
package/src/app/scaffold.tsx
CHANGED
|
@@ -14,6 +14,7 @@ import type {
|
|
|
14
14
|
} from '@fayz-ai/core'
|
|
15
15
|
import { AdminProviders, getAuthShellProps } from './admin-app'
|
|
16
16
|
import { AdminShell } from './AdminShell'
|
|
17
|
+
import { deriveAgentContract } from './derive-contract'
|
|
17
18
|
import type { FayzAppConfig, CustomPage } from './config'
|
|
18
19
|
|
|
19
20
|
// ---------------------------------------------------------------------------
|
|
@@ -49,6 +50,7 @@ export function defineSaas(config: FayzAppConfig): AppManifest {
|
|
|
49
50
|
const id = slug(config.name)
|
|
50
51
|
const configRef = getLiveConfigRef(id)
|
|
51
52
|
liveSaasConfigs.set(configRef, config)
|
|
53
|
+
const { agent, limitDeclarations } = deriveAgentContract(config)
|
|
52
54
|
return defineApp({
|
|
53
55
|
id,
|
|
54
56
|
name: config.name,
|
|
@@ -59,6 +61,8 @@ export function defineSaas(config: FayzAppConfig): AppManifest {
|
|
|
59
61
|
theme: config.theme as Record<string, unknown> | undefined,
|
|
60
62
|
permissions: config.permissions,
|
|
61
63
|
billing: config.billing as ManifestBillingConfig | undefined,
|
|
64
|
+
limitDeclarations,
|
|
65
|
+
agent,
|
|
62
66
|
surfaces: {
|
|
63
67
|
admin: {
|
|
64
68
|
scaffold: 'admin',
|
package/src/archetype-lookup.ts
CHANGED
|
@@ -107,10 +107,13 @@ export function createArchetypeLookup(config: ArchetypeLookupConfig): EntityLook
|
|
|
107
107
|
let qb = supabase
|
|
108
108
|
.from(display.table)
|
|
109
109
|
.select('*')
|
|
110
|
-
.eq('is_active', true)
|
|
111
110
|
.order(display.labelField, { ascending: true })
|
|
112
111
|
.limit(limit)
|
|
113
112
|
|
|
113
|
+
qb = config.archetype === 'service'
|
|
114
|
+
? qb.or('is_active.eq.true,is_active.is.null')
|
|
115
|
+
: qb.eq('is_active', true)
|
|
116
|
+
|
|
114
117
|
if (query) qb = qb.ilike(display.labelField, `%${query}%`)
|
|
115
118
|
qb = applyKindFilter(qb)
|
|
116
119
|
|
|
@@ -127,10 +130,13 @@ export function createArchetypeLookup(config: ArchetypeLookupConfig): EntityLook
|
|
|
127
130
|
let qb = supabase
|
|
128
131
|
.from(display.table)
|
|
129
132
|
.select('*')
|
|
130
|
-
.eq('is_active', true)
|
|
131
133
|
.order(display.labelField, { ascending: true })
|
|
132
134
|
.limit(limit)
|
|
133
135
|
|
|
136
|
+
qb = config.archetype === 'service'
|
|
137
|
+
? qb.or('is_active.eq.true,is_active.is.null')
|
|
138
|
+
: qb.eq('is_active', true)
|
|
139
|
+
|
|
134
140
|
qb = applyKindFilter(qb)
|
|
135
141
|
|
|
136
142
|
const { data } = await qb
|
package/src/billing/index.ts
CHANGED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Plan } from '@fayz-ai/core'
|
|
2
|
+
|
|
3
|
+
export interface PlanBadgeInfo {
|
|
4
|
+
/** Display label, e.g. 'Free' or 'Pro'. */
|
|
5
|
+
label: string
|
|
6
|
+
/** Resolved plan id (falls back to the raw org.plan string). */
|
|
7
|
+
planId: string
|
|
8
|
+
/** Paid plan (price > 0) — drives the Crown icon + gold styling in the menus. */
|
|
9
|
+
paid: boolean
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function capitalize(s: string): string {
|
|
13
|
+
return s ? s.charAt(0).toUpperCase() + s.slice(1) : s
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Resolve an org's `plan` string against the configured billing plans into the
|
|
18
|
+
* badge shown next to the user in both menus. Falls back to a capitalized
|
|
19
|
+
* version of the raw string when the id isn't in config (defensive — e.g. a
|
|
20
|
+
* legacy 'free' tenant before billing was wired).
|
|
21
|
+
*/
|
|
22
|
+
export function resolvePlanBadge(
|
|
23
|
+
planId: string | undefined | null,
|
|
24
|
+
plans: Plan[],
|
|
25
|
+
): PlanBadgeInfo {
|
|
26
|
+
const id = planId || 'free'
|
|
27
|
+
const match = plans.find((p) => p.id === id)
|
|
28
|
+
if (match) {
|
|
29
|
+
return { label: match.name, planId: match.id, paid: match.price > 0 }
|
|
30
|
+
}
|
|
31
|
+
return { label: capitalize(id), planId: id, paid: false }
|
|
32
|
+
}
|
package/src/billing/store.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { create } from 'zustand'
|
|
2
|
-
import type { Plan } from '@fayz-ai/core'
|
|
2
|
+
import type { Plan, BillingCheckoutFn } from '@fayz-ai/core'
|
|
3
3
|
|
|
4
4
|
// ---------------------------------------------------------------------------
|
|
5
5
|
// Types
|
|
@@ -31,6 +31,10 @@ export interface BillingState {
|
|
|
31
31
|
invoices: Invoice[]
|
|
32
32
|
plans: Plan[]
|
|
33
33
|
loading: boolean
|
|
34
|
+
/** Payment-gateway seam seeded from `config.billing.onCheckout` (see core
|
|
35
|
+
* BillingConfig). When set, the Subscription page routes plan changes through
|
|
36
|
+
* it instead of writing `plan` straight onto the org. */
|
|
37
|
+
checkout: BillingCheckoutFn | null
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
// ---------------------------------------------------------------------------
|
|
@@ -42,6 +46,7 @@ export interface BillingStore extends BillingState {
|
|
|
42
46
|
setInvoices: (invoices: Invoice[]) => void
|
|
43
47
|
setPlans: (plans: Plan[]) => void
|
|
44
48
|
setLoading: (loading: boolean) => void
|
|
49
|
+
setCheckout: (checkout: BillingCheckoutFn | null) => void
|
|
45
50
|
reset: () => void
|
|
46
51
|
}
|
|
47
52
|
|
|
@@ -50,6 +55,7 @@ const initialState: BillingState = {
|
|
|
50
55
|
invoices: [],
|
|
51
56
|
plans: [],
|
|
52
57
|
loading: false,
|
|
58
|
+
checkout: null,
|
|
53
59
|
}
|
|
54
60
|
|
|
55
61
|
export const useBillingStore = create<BillingStore>((set) => ({
|
|
@@ -58,5 +64,6 @@ export const useBillingStore = create<BillingStore>((set) => ({
|
|
|
58
64
|
setInvoices: (invoices) => set({ invoices }),
|
|
59
65
|
setPlans: (plans) => set({ plans }),
|
|
60
66
|
setLoading: (loading) => set({ loading }),
|
|
67
|
+
setCheckout: (checkout) => set({ checkout }),
|
|
61
68
|
reset: () => set(initialState),
|
|
62
69
|
}))
|