foundrycms 0.1.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 (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +152 -0
  3. package/dist/__tests__/foundry.test.d.ts +2 -0
  4. package/dist/__tests__/foundry.test.d.ts.map +1 -0
  5. package/dist/__tests__/foundry.test.js +1013 -0
  6. package/dist/__tests__/foundry.test.js.map +1 -0
  7. package/dist/config-manager.d.ts +33 -0
  8. package/dist/config-manager.d.ts.map +1 -0
  9. package/dist/config-manager.js +169 -0
  10. package/dist/config-manager.js.map +1 -0
  11. package/dist/hook-system.d.ts +61 -0
  12. package/dist/hook-system.d.ts.map +1 -0
  13. package/dist/hook-system.js +114 -0
  14. package/dist/hook-system.js.map +1 -0
  15. package/dist/index.d.ts +47 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +82 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/page-builder/element-registry.d.ts +47 -0
  20. package/dist/page-builder/element-registry.d.ts.map +1 -0
  21. package/dist/page-builder/element-registry.js +98 -0
  22. package/dist/page-builder/element-registry.js.map +1 -0
  23. package/dist/page-builder/elements/index.d.ts +22 -0
  24. package/dist/page-builder/elements/index.d.ts.map +1 -0
  25. package/dist/page-builder/elements/index.js +770 -0
  26. package/dist/page-builder/elements/index.js.map +1 -0
  27. package/dist/page-builder/renderer.d.ts +14 -0
  28. package/dist/page-builder/renderer.d.ts.map +1 -0
  29. package/dist/page-builder/renderer.js +240 -0
  30. package/dist/page-builder/renderer.js.map +1 -0
  31. package/dist/page-builder/serializer.d.ts +1220 -0
  32. package/dist/page-builder/serializer.d.ts.map +1 -0
  33. package/dist/page-builder/serializer.js +111 -0
  34. package/dist/page-builder/serializer.js.map +1 -0
  35. package/dist/page-builder/template-studio.d.ts +37 -0
  36. package/dist/page-builder/template-studio.d.ts.map +1 -0
  37. package/dist/page-builder/template-studio.js +923 -0
  38. package/dist/page-builder/template-studio.js.map +1 -0
  39. package/dist/page-builder/types.d.ts +99 -0
  40. package/dist/page-builder/types.d.ts.map +1 -0
  41. package/dist/page-builder/types.js +5 -0
  42. package/dist/page-builder/types.js.map +1 -0
  43. package/dist/plugin-system.d.ts +128 -0
  44. package/dist/plugin-system.d.ts.map +1 -0
  45. package/dist/plugin-system.js +252 -0
  46. package/dist/plugin-system.js.map +1 -0
  47. package/dist/plugins/communication.d.ts +6 -0
  48. package/dist/plugins/communication.d.ts.map +1 -0
  49. package/dist/plugins/communication.js +922 -0
  50. package/dist/plugins/communication.js.map +1 -0
  51. package/dist/plugins/core.d.ts +6 -0
  52. package/dist/plugins/core.d.ts.map +1 -0
  53. package/dist/plugins/core.js +675 -0
  54. package/dist/plugins/core.js.map +1 -0
  55. package/dist/plugins/growth.d.ts +6 -0
  56. package/dist/plugins/growth.d.ts.map +1 -0
  57. package/dist/plugins/growth.js +668 -0
  58. package/dist/plugins/growth.js.map +1 -0
  59. package/dist/plugins/index.d.ts +8 -0
  60. package/dist/plugins/index.d.ts.map +1 -0
  61. package/dist/plugins/index.js +43 -0
  62. package/dist/plugins/index.js.map +1 -0
  63. package/dist/plugins/operations.d.ts +7 -0
  64. package/dist/plugins/operations.d.ts.map +1 -0
  65. package/dist/plugins/operations.js +930 -0
  66. package/dist/plugins/operations.js.map +1 -0
  67. package/dist/theme/presets.d.ts +8 -0
  68. package/dist/theme/presets.d.ts.map +1 -0
  69. package/dist/theme/presets.js +257 -0
  70. package/dist/theme/presets.js.map +1 -0
  71. package/dist/theme/types.d.ts +83 -0
  72. package/dist/theme/types.d.ts.map +1 -0
  73. package/dist/theme/types.js +5 -0
  74. package/dist/theme/types.js.map +1 -0
  75. package/package.json +38 -0
@@ -0,0 +1,675 @@
1
+ import { z } from 'zod';
2
+ // ---------------------------------------------------------------------------
3
+ // Forge — Visual Site Builder
4
+ // ---------------------------------------------------------------------------
5
+ export const forgePlugin = {
6
+ id: '@foundry/forge',
7
+ name: 'Forge',
8
+ version: '1.0.0',
9
+ description: 'Visual drag-and-drop site builder with live preview and responsive editing',
10
+ adminPages: [
11
+ {
12
+ path: 'forge',
13
+ label: 'Site Builder',
14
+ icon: 'hammer',
15
+ component: 'ForgeEditorPage',
16
+ order: 1,
17
+ permission: 'forge:edit',
18
+ children: [
19
+ {
20
+ path: 'pages',
21
+ label: 'Pages',
22
+ component: 'ForgePagesListPage',
23
+ permission: 'forge:edit',
24
+ },
25
+ {
26
+ path: 'templates',
27
+ label: 'Templates',
28
+ component: 'ForgeTemplatesPage',
29
+ permission: 'forge:templates',
30
+ },
31
+ {
32
+ path: 'global-styles',
33
+ label: 'Global Styles',
34
+ component: 'ForgeGlobalStylesPage',
35
+ permission: 'forge:styles',
36
+ },
37
+ ],
38
+ },
39
+ ],
40
+ apiRoutes: [
41
+ {
42
+ method: 'GET',
43
+ path: '/api/forge/pages',
44
+ handler: 'forge.listPages',
45
+ middleware: ['auth'],
46
+ description: 'List all site pages',
47
+ },
48
+ {
49
+ method: 'POST',
50
+ path: '/api/forge/pages',
51
+ handler: 'forge.createPage',
52
+ middleware: ['auth', 'permission:forge:edit'],
53
+ description: 'Create a new page',
54
+ },
55
+ {
56
+ method: 'PUT',
57
+ path: '/api/forge/pages/:id',
58
+ handler: 'forge.updatePage',
59
+ middleware: ['auth', 'permission:forge:edit'],
60
+ description: 'Update a page by ID',
61
+ },
62
+ {
63
+ method: 'POST',
64
+ path: '/api/forge/pages/:id/publish',
65
+ handler: 'forge.publishPage',
66
+ middleware: ['auth', 'permission:forge:publish'],
67
+ description: 'Publish a page',
68
+ },
69
+ {
70
+ method: 'DELETE',
71
+ path: '/api/forge/pages/:id',
72
+ handler: 'forge.deletePage',
73
+ middleware: ['auth', 'permission:forge:delete'],
74
+ description: 'Delete a page',
75
+ },
76
+ ],
77
+ pageElements: [
78
+ {
79
+ type: 'forge-section',
80
+ name: 'Section',
81
+ category: 'layout',
82
+ component: 'ForgeSectionElement',
83
+ settingsSchema: z.object({
84
+ backgroundColor: z.string().optional(),
85
+ padding: z.enum(['none', 'sm', 'md', 'lg', 'xl']).default('md'),
86
+ fullWidth: z.boolean().default(false),
87
+ backgroundImage: z.string().url().optional(),
88
+ parallax: z.boolean().default(false),
89
+ }),
90
+ },
91
+ {
92
+ type: 'forge-container',
93
+ name: 'Container',
94
+ category: 'layout',
95
+ component: 'ForgeContainerElement',
96
+ settingsSchema: z.object({
97
+ maxWidth: z.enum(['sm', 'md', 'lg', 'xl', 'full']).default('lg'),
98
+ alignment: z.enum(['left', 'center', 'right']).default('center'),
99
+ }),
100
+ },
101
+ ],
102
+ widgets: [
103
+ {
104
+ id: 'forge-recent-pages',
105
+ name: 'Recent Pages',
106
+ component: 'ForgeRecentPagesWidget',
107
+ areas: ['dashboard'],
108
+ defaultSize: { width: 4, height: 3 },
109
+ },
110
+ ],
111
+ hooks: [
112
+ {
113
+ hook: 'page:before_render',
114
+ handler: (payload) => {
115
+ // Inject Forge builder data attributes for live editing
116
+ if (payload.page && payload.page._forgeEditing) {
117
+ payload.page._renderMode = 'builder';
118
+ payload.page._builderScripts = ['/forge/builder.js'];
119
+ payload.page._builderStyles = ['/forge/builder.css'];
120
+ }
121
+ return payload;
122
+ },
123
+ priority: 1,
124
+ },
125
+ {
126
+ hook: 'page:before_save',
127
+ handler: (payload) => {
128
+ // Validate page structure before saving
129
+ if (payload.page?.rows) {
130
+ for (const row of payload.page.rows) {
131
+ if (!row.columns || row.columns.length === 0) {
132
+ throw new Error(`Forge: Row "${row.id}" must have at least one column`);
133
+ }
134
+ }
135
+ }
136
+ return payload;
137
+ },
138
+ priority: 5,
139
+ },
140
+ ],
141
+ settingsSchema: z.object({
142
+ defaultTemplate: z.string().default('blank'),
143
+ autosaveInterval: z.number().min(5000).max(120000).default(30000),
144
+ enableVersionHistory: z.boolean().default(true),
145
+ maxVersions: z.number().min(1).max(100).default(25),
146
+ livePreview: z.boolean().default(true),
147
+ }),
148
+ async onActivate(core) {
149
+ core.hooks.on('page:after_save', (payload) => {
150
+ // Trigger rebuild of static pages if needed
151
+ if (payload.page?.status === 'published') {
152
+ core.hooks.emit('content:after_save', {
153
+ content: { type: 'page', id: payload.page.id, action: 'publish' },
154
+ });
155
+ }
156
+ });
157
+ },
158
+ async onDeactivate(_core) {
159
+ // Clean up any live-preview websocket connections
160
+ },
161
+ };
162
+ // ---------------------------------------------------------------------------
163
+ // Ledger — Invoicing (ChronoCraft)
164
+ // ---------------------------------------------------------------------------
165
+ export const ledgerPlugin = {
166
+ id: '@foundry/ledger',
167
+ name: 'Ledger',
168
+ version: '1.0.0',
169
+ description: 'Professional invoicing system with Stripe integration, recurring billing, and payment tracking',
170
+ adminPages: [
171
+ {
172
+ path: 'ledger',
173
+ label: 'Invoicing',
174
+ icon: 'file-text',
175
+ component: 'LedgerDashboardPage',
176
+ order: 10,
177
+ permission: 'ledger:view',
178
+ children: [
179
+ {
180
+ path: 'invoices',
181
+ label: 'Invoices',
182
+ component: 'LedgerInvoicesPage',
183
+ permission: 'ledger:view',
184
+ },
185
+ {
186
+ path: 'invoices/new',
187
+ label: 'New Invoice',
188
+ component: 'LedgerInvoiceEditorPage',
189
+ permission: 'ledger:create',
190
+ },
191
+ {
192
+ path: 'clients',
193
+ label: 'Clients',
194
+ component: 'LedgerClientsPage',
195
+ permission: 'ledger:view',
196
+ },
197
+ {
198
+ path: 'recurring',
199
+ label: 'Recurring',
200
+ component: 'LedgerRecurringPage',
201
+ permission: 'ledger:manage',
202
+ },
203
+ {
204
+ path: 'settings',
205
+ label: 'Settings',
206
+ component: 'LedgerSettingsPage',
207
+ permission: 'ledger:settings',
208
+ },
209
+ ],
210
+ },
211
+ ],
212
+ apiRoutes: [
213
+ {
214
+ method: 'GET',
215
+ path: '/api/ledger/invoices',
216
+ handler: 'ledger.listInvoices',
217
+ middleware: ['auth'],
218
+ description: 'List all invoices with optional filters',
219
+ },
220
+ {
221
+ method: 'POST',
222
+ path: '/api/ledger/invoices',
223
+ handler: 'ledger.createInvoice',
224
+ middleware: ['auth', 'permission:ledger:create'],
225
+ description: 'Create a new invoice',
226
+ },
227
+ {
228
+ method: 'GET',
229
+ path: '/api/ledger/invoices/:id',
230
+ handler: 'ledger.getInvoice',
231
+ middleware: ['auth'],
232
+ description: 'Get invoice by ID',
233
+ },
234
+ {
235
+ method: 'PUT',
236
+ path: '/api/ledger/invoices/:id',
237
+ handler: 'ledger.updateInvoice',
238
+ middleware: ['auth', 'permission:ledger:edit'],
239
+ description: 'Update an invoice',
240
+ },
241
+ {
242
+ method: 'POST',
243
+ path: '/api/ledger/invoices/:id/send',
244
+ handler: 'ledger.sendInvoice',
245
+ middleware: ['auth', 'permission:ledger:send'],
246
+ description: 'Email an invoice to the client',
247
+ },
248
+ {
249
+ method: 'POST',
250
+ path: '/api/ledger/webhooks/stripe',
251
+ handler: 'ledger.stripeWebhook',
252
+ description: 'Stripe webhook endpoint for payment events',
253
+ },
254
+ {
255
+ method: 'GET',
256
+ path: '/api/ledger/invoices/:id/pdf',
257
+ handler: 'ledger.generatePdf',
258
+ middleware: ['auth'],
259
+ description: 'Generate PDF for an invoice',
260
+ },
261
+ ],
262
+ widgets: [
263
+ {
264
+ id: 'ledger-revenue-summary',
265
+ name: 'Revenue Summary',
266
+ component: 'LedgerRevenueSummaryWidget',
267
+ areas: ['dashboard'],
268
+ defaultSize: { width: 4, height: 2 },
269
+ },
270
+ {
271
+ id: 'ledger-outstanding',
272
+ name: 'Outstanding Invoices',
273
+ component: 'LedgerOutstandingWidget',
274
+ areas: ['dashboard', 'sidebar'],
275
+ defaultSize: { width: 4, height: 3 },
276
+ },
277
+ ],
278
+ hooks: [
279
+ {
280
+ hook: 'content:before_save',
281
+ handler: (payload) => {
282
+ // Validate invoice data before saving
283
+ if (payload.content?.type === 'invoice') {
284
+ const invoice = payload.content;
285
+ if (!invoice.lineItems || invoice.lineItems.length === 0) {
286
+ throw new Error('Ledger: Invoice must have at least one line item');
287
+ }
288
+ // Recalculate totals
289
+ let subtotal = 0;
290
+ for (const item of invoice.lineItems) {
291
+ item.amount = (item.quantity ?? 1) * (item.rate ?? 0);
292
+ subtotal += item.amount;
293
+ }
294
+ invoice.subtotal = subtotal;
295
+ invoice.tax = subtotal * (invoice.taxRate ?? 0);
296
+ invoice.total = invoice.subtotal + invoice.tax;
297
+ }
298
+ return payload;
299
+ },
300
+ priority: 5,
301
+ },
302
+ ],
303
+ settingsSchema: z.object({
304
+ companyName: z.string().default(''),
305
+ companyAddress: z.string().default(''),
306
+ companyEmail: z.string().email().optional(),
307
+ companyLogo: z.string().url().optional(),
308
+ currency: z.string().default('GBP'),
309
+ defaultPaymentTerms: z.number().min(0).max(90).default(30),
310
+ defaultTaxRate: z.number().min(0).max(1).default(0.2),
311
+ stripeSecretKey: z.string().optional(),
312
+ stripeWebhookSecret: z.string().optional(),
313
+ invoicePrefix: z.string().default('INV'),
314
+ nextInvoiceNumber: z.number().default(1001),
315
+ }),
316
+ async onActivate(core) {
317
+ // Register recurring invoice cron-like check
318
+ core.hooks.on('content:after_save', (payload) => {
319
+ if (payload.content?.type === 'invoice' && payload.content?.status === 'paid') {
320
+ // Check if this invoice has a recurring schedule
321
+ if (payload.content.recurringSchedule) {
322
+ core.hooks.emit('content:before_save', {
323
+ content: {
324
+ type: 'invoice',
325
+ action: 'schedule_next',
326
+ sourceInvoiceId: payload.content.id,
327
+ schedule: payload.content.recurringSchedule,
328
+ },
329
+ });
330
+ }
331
+ }
332
+ });
333
+ },
334
+ async onDeactivate(_core) {
335
+ // Clean up Stripe webhook listeners
336
+ },
337
+ };
338
+ // ---------------------------------------------------------------------------
339
+ // Compass — CRM
340
+ // ---------------------------------------------------------------------------
341
+ export const compassPlugin = {
342
+ id: '@foundry/compass',
343
+ name: 'Compass',
344
+ version: '1.0.0',
345
+ description: 'Customer relationship management with contact lifecycle tracking, deal pipeline, and activity logs',
346
+ adminPages: [
347
+ {
348
+ path: 'compass',
349
+ label: 'CRM',
350
+ icon: 'users',
351
+ component: 'CompassDashboardPage',
352
+ order: 15,
353
+ permission: 'compass:view',
354
+ children: [
355
+ {
356
+ path: 'contacts',
357
+ label: 'Contacts',
358
+ component: 'CompassContactsPage',
359
+ permission: 'compass:view',
360
+ },
361
+ {
362
+ path: 'contacts/:id',
363
+ label: 'Contact Detail',
364
+ component: 'CompassContactDetailPage',
365
+ permission: 'compass:view',
366
+ },
367
+ {
368
+ path: 'deals',
369
+ label: 'Deals',
370
+ component: 'CompassDealsPage',
371
+ permission: 'compass:deals',
372
+ },
373
+ {
374
+ path: 'pipeline',
375
+ label: 'Pipeline',
376
+ component: 'CompassPipelinePage',
377
+ permission: 'compass:deals',
378
+ },
379
+ {
380
+ path: 'activities',
381
+ label: 'Activities',
382
+ component: 'CompassActivitiesPage',
383
+ permission: 'compass:view',
384
+ },
385
+ ],
386
+ },
387
+ ],
388
+ apiRoutes: [
389
+ {
390
+ method: 'GET',
391
+ path: '/api/compass/contacts',
392
+ handler: 'compass.listContacts',
393
+ middleware: ['auth'],
394
+ description: 'List contacts with search and filters',
395
+ },
396
+ {
397
+ method: 'POST',
398
+ path: '/api/compass/contacts',
399
+ handler: 'compass.createContact',
400
+ middleware: ['auth', 'permission:compass:create'],
401
+ description: 'Create a new contact',
402
+ },
403
+ {
404
+ method: 'GET',
405
+ path: '/api/compass/contacts/:id',
406
+ handler: 'compass.getContact',
407
+ middleware: ['auth'],
408
+ description: 'Get contact details',
409
+ },
410
+ {
411
+ method: 'PUT',
412
+ path: '/api/compass/contacts/:id',
413
+ handler: 'compass.updateContact',
414
+ middleware: ['auth', 'permission:compass:edit'],
415
+ description: 'Update a contact',
416
+ },
417
+ {
418
+ method: 'GET',
419
+ path: '/api/compass/deals',
420
+ handler: 'compass.listDeals',
421
+ middleware: ['auth'],
422
+ description: 'List all deals',
423
+ },
424
+ {
425
+ method: 'POST',
426
+ path: '/api/compass/deals',
427
+ handler: 'compass.createDeal',
428
+ middleware: ['auth', 'permission:compass:deals'],
429
+ description: 'Create a new deal',
430
+ },
431
+ {
432
+ method: 'PATCH',
433
+ path: '/api/compass/deals/:id/stage',
434
+ handler: 'compass.updateDealStage',
435
+ middleware: ['auth', 'permission:compass:deals'],
436
+ description: 'Move a deal to a new pipeline stage',
437
+ },
438
+ ],
439
+ widgets: [
440
+ {
441
+ id: 'compass-pipeline-summary',
442
+ name: 'Pipeline Summary',
443
+ component: 'CompassPipelineSummaryWidget',
444
+ areas: ['dashboard'],
445
+ defaultSize: { width: 6, height: 3 },
446
+ },
447
+ {
448
+ id: 'compass-recent-contacts',
449
+ name: 'Recent Contacts',
450
+ component: 'CompassRecentContactsWidget',
451
+ areas: ['dashboard', 'sidebar'],
452
+ defaultSize: { width: 3, height: 4 },
453
+ },
454
+ ],
455
+ hooks: [
456
+ {
457
+ hook: 'content:before_save',
458
+ handler: (payload) => {
459
+ if (payload.content?.type === 'contact') {
460
+ // Normalize email
461
+ if (payload.content.email) {
462
+ payload.content.email = payload.content.email.toLowerCase().trim();
463
+ }
464
+ // Auto-assign lifecycle stage if missing
465
+ if (!payload.content.lifecycleStage) {
466
+ payload.content.lifecycleStage = 'lead';
467
+ }
468
+ // Set timestamps
469
+ if (!payload.content.createdAt) {
470
+ payload.content.createdAt = new Date().toISOString();
471
+ }
472
+ payload.content.updatedAt = new Date().toISOString();
473
+ }
474
+ return payload;
475
+ },
476
+ priority: 5,
477
+ },
478
+ {
479
+ hook: 'content:after_save',
480
+ handler: (payload) => {
481
+ if (payload.content?.type === 'deal' && payload.content?.stage === 'won') {
482
+ // When a deal is won, update the contact's lifecycle stage
483
+ // This would trigger another save with the updated contact
484
+ }
485
+ },
486
+ priority: 10,
487
+ },
488
+ ],
489
+ settingsSchema: z.object({
490
+ pipelineStages: z.array(z.object({
491
+ id: z.string(),
492
+ label: z.string(),
493
+ color: z.string(),
494
+ order: z.number(),
495
+ })).default([
496
+ { id: 'lead', label: 'Lead', color: '#6366f1', order: 0 },
497
+ { id: 'qualified', label: 'Qualified', color: '#8b5cf6', order: 1 },
498
+ { id: 'proposal', label: 'Proposal', color: '#a855f7', order: 2 },
499
+ { id: 'negotiation', label: 'Negotiation', color: '#d946ef', order: 3 },
500
+ { id: 'won', label: 'Won', color: '#22c55e', order: 4 },
501
+ { id: 'lost', label: 'Lost', color: '#ef4444', order: 5 },
502
+ ]),
503
+ lifecycleStages: z.array(z.string()).default([
504
+ 'subscriber', 'lead', 'qualified', 'opportunity', 'customer', 'evangelist',
505
+ ]),
506
+ defaultCurrency: z.string().default('GBP'),
507
+ }),
508
+ async onActivate(core) {
509
+ // Wire up CRM activity logging
510
+ core.hooks.on('content:after_save', (payload) => {
511
+ if (['contact', 'deal'].includes(payload.content?.type)) {
512
+ // Log activity entry
513
+ core.hooks.emit('content:after_save', {
514
+ content: {
515
+ type: 'activity',
516
+ action: 'updated',
517
+ entityType: payload.content.type,
518
+ entityId: payload.content.id,
519
+ timestamp: new Date().toISOString(),
520
+ },
521
+ });
522
+ }
523
+ });
524
+ },
525
+ async onDeactivate(_core) {
526
+ // Cleanup CRM hooks
527
+ },
528
+ };
529
+ // ---------------------------------------------------------------------------
530
+ // Oracle — AI Integration
531
+ // ---------------------------------------------------------------------------
532
+ export const oraclePlugin = {
533
+ id: '@foundry/oracle',
534
+ name: 'Oracle',
535
+ version: '1.0.0',
536
+ description: 'AI-powered content generation, data analysis, SEO suggestions, and intelligent automation',
537
+ adminPages: [
538
+ {
539
+ path: 'oracle',
540
+ label: 'AI Assistant',
541
+ icon: 'sparkles',
542
+ component: 'OracleDashboardPage',
543
+ order: 5,
544
+ permission: 'oracle:view',
545
+ children: [
546
+ {
547
+ path: 'generate',
548
+ label: 'Content Generator',
549
+ component: 'OracleGeneratorPage',
550
+ permission: 'oracle:generate',
551
+ },
552
+ {
553
+ path: 'analyze',
554
+ label: 'Data Analysis',
555
+ component: 'OracleAnalysisPage',
556
+ permission: 'oracle:analyze',
557
+ },
558
+ {
559
+ path: 'suggestions',
560
+ label: 'Suggestions',
561
+ component: 'OracleSuggestionsPage',
562
+ permission: 'oracle:view',
563
+ },
564
+ {
565
+ path: 'usage',
566
+ label: 'AI Usage',
567
+ component: 'OracleUsagePage',
568
+ permission: 'oracle:settings',
569
+ },
570
+ ],
571
+ },
572
+ ],
573
+ apiRoutes: [
574
+ {
575
+ method: 'POST',
576
+ path: '/api/oracle/generate',
577
+ handler: 'oracle.generateContent',
578
+ middleware: ['auth', 'permission:oracle:generate', 'rateLimit:oracle'],
579
+ description: 'Generate content using AI',
580
+ },
581
+ {
582
+ method: 'POST',
583
+ path: '/api/oracle/analyze',
584
+ handler: 'oracle.analyzeData',
585
+ middleware: ['auth', 'permission:oracle:analyze'],
586
+ description: 'Run AI analysis on provided data',
587
+ },
588
+ {
589
+ method: 'POST',
590
+ path: '/api/oracle/suggest/seo',
591
+ handler: 'oracle.suggestSeo',
592
+ middleware: ['auth'],
593
+ description: 'Get AI-powered SEO suggestions for content',
594
+ },
595
+ {
596
+ method: 'POST',
597
+ path: '/api/oracle/rewrite',
598
+ handler: 'oracle.rewriteContent',
599
+ middleware: ['auth', 'permission:oracle:generate'],
600
+ description: 'Rewrite or improve existing content',
601
+ },
602
+ {
603
+ method: 'GET',
604
+ path: '/api/oracle/usage',
605
+ handler: 'oracle.getUsage',
606
+ middleware: ['auth'],
607
+ description: 'Get AI token usage stats',
608
+ },
609
+ ],
610
+ widgets: [
611
+ {
612
+ id: 'oracle-suggestions',
613
+ name: 'AI Suggestions',
614
+ component: 'OracleSuggestionsWidget',
615
+ areas: ['dashboard', 'sidebar'],
616
+ defaultSize: { width: 3, height: 4 },
617
+ },
618
+ {
619
+ id: 'oracle-usage',
620
+ name: 'AI Usage',
621
+ component: 'OracleUsageWidget',
622
+ areas: ['dashboard'],
623
+ defaultSize: { width: 2, height: 2 },
624
+ },
625
+ ],
626
+ hooks: [
627
+ {
628
+ hook: 'content:before_save',
629
+ handler: (payload) => {
630
+ // Auto-generate meta description if missing and AI is enabled
631
+ if (payload.content?.type === 'page' && !payload.content.metaDescription) {
632
+ payload.content._oracleTask = 'generate_meta_description';
633
+ }
634
+ return payload;
635
+ },
636
+ priority: 20,
637
+ },
638
+ {
639
+ hook: 'page:before_render',
640
+ handler: (payload) => {
641
+ // Inject AI-powered content personalization markers
642
+ if (payload.page?._personalize) {
643
+ payload.page._oraclePersonalization = {
644
+ enabled: true,
645
+ segments: payload.page._personalizeSegments ?? ['default'],
646
+ };
647
+ }
648
+ return payload;
649
+ },
650
+ priority: 15,
651
+ },
652
+ ],
653
+ settingsSchema: z.object({
654
+ provider: z.enum(['anthropic', 'openai', 'local']).default('anthropic'),
655
+ apiKey: z.string().optional(),
656
+ model: z.string().default('claude-sonnet-4-20250514'),
657
+ maxTokensPerRequest: z.number().min(100).max(8000).default(2000),
658
+ monthlyTokenBudget: z.number().default(1000000),
659
+ autoSeoSuggestions: z.boolean().default(true),
660
+ autoMetaGeneration: z.boolean().default(false),
661
+ contentTone: z.enum(['professional', 'casual', 'formal', 'friendly']).default('professional'),
662
+ }),
663
+ async onActivate(core) {
664
+ // Register AI content generation pipeline
665
+ core.hooks.on('content:after_save', (payload) => {
666
+ if (payload.content?._oracleTask === 'generate_meta_description') {
667
+ // Queue meta description generation
668
+ }
669
+ });
670
+ },
671
+ async onDeactivate(_core) {
672
+ // Clean up AI processing queue
673
+ },
674
+ };
675
+ //# sourceMappingURL=core.js.map