@hale-bopp/valentino-engine 2.1.0 → 2.3.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 (102) hide show
  1. package/README.md +63 -2
  2. package/dist/bin/commands/cockpit.d.ts +16 -0
  3. package/dist/bin/commands/cockpit.d.ts.map +1 -0
  4. package/dist/bin/commands/cockpit.js +205 -0
  5. package/dist/bin/commands/cockpit.js.map +1 -0
  6. package/dist/bin/commands/theme-audit.d.ts +7 -0
  7. package/dist/bin/commands/theme-audit.d.ts.map +1 -0
  8. package/dist/bin/commands/theme-audit.js +70 -0
  9. package/dist/bin/commands/theme-audit.js.map +1 -0
  10. package/dist/bin/commands/validate.d.ts.map +1 -1
  11. package/dist/bin/commands/validate.js +94 -1
  12. package/dist/bin/commands/validate.js.map +1 -1
  13. package/dist/bin/valentino.js +15 -1
  14. package/dist/bin/valentino.js.map +1 -1
  15. package/dist/cockpit-server.d.ts +17 -0
  16. package/dist/cockpit-server.d.ts.map +1 -0
  17. package/dist/cockpit-server.js +505 -0
  18. package/dist/cockpit-server.js.map +1 -0
  19. package/dist/cockpit-web/index.html +979 -0
  20. package/dist/core/cockpit-api.d.ts +99 -0
  21. package/dist/core/cockpit-api.d.ts.map +1 -0
  22. package/dist/core/cockpit-api.js +269 -0
  23. package/dist/core/cockpit-api.js.map +1 -0
  24. package/dist/core/cockpit-repl.d.ts +51 -0
  25. package/dist/core/cockpit-repl.d.ts.map +1 -0
  26. package/dist/core/cockpit-repl.js +217 -0
  27. package/dist/core/cockpit-repl.js.map +1 -0
  28. package/dist/core/contrast-usage-probe.d.ts +84 -0
  29. package/dist/core/contrast-usage-probe.d.ts.map +1 -0
  30. package/dist/core/contrast-usage-probe.js +244 -0
  31. package/dist/core/contrast-usage-probe.js.map +1 -0
  32. package/dist/core/contrast-usage-probe.test.d.ts +2 -0
  33. package/dist/core/contrast-usage-probe.test.d.ts.map +1 -0
  34. package/dist/core/contrast-usage-probe.test.js +186 -0
  35. package/dist/core/contrast-usage-probe.test.js.map +1 -0
  36. package/dist/core/editor.d.ts +80 -0
  37. package/dist/core/editor.d.ts.map +1 -0
  38. package/dist/core/editor.js +421 -0
  39. package/dist/core/editor.js.map +1 -0
  40. package/dist/core/gallery.d.ts +53 -0
  41. package/dist/core/gallery.d.ts.map +1 -0
  42. package/dist/core/gallery.js +123 -0
  43. package/dist/core/gallery.js.map +1 -0
  44. package/dist/core/gallery.test.d.ts +2 -0
  45. package/dist/core/gallery.test.d.ts.map +1 -0
  46. package/dist/core/gallery.test.js +127 -0
  47. package/dist/core/gallery.test.js.map +1 -0
  48. package/dist/core/intent-parser.d.ts +67 -0
  49. package/dist/core/intent-parser.d.ts.map +1 -0
  50. package/dist/core/intent-parser.js +485 -0
  51. package/dist/core/intent-parser.js.map +1 -0
  52. package/dist/core/openrouter-client.d.ts +38 -0
  53. package/dist/core/openrouter-client.d.ts.map +1 -0
  54. package/dist/core/openrouter-client.js +123 -0
  55. package/dist/core/openrouter-client.js.map +1 -0
  56. package/dist/core/page-generator.d.ts +85 -0
  57. package/dist/core/page-generator.d.ts.map +1 -0
  58. package/dist/core/page-generator.js +353 -0
  59. package/dist/core/page-generator.js.map +1 -0
  60. package/dist/core/page-generator.test.d.ts +2 -0
  61. package/dist/core/page-generator.test.d.ts.map +1 -0
  62. package/dist/core/page-generator.test.js +138 -0
  63. package/dist/core/page-generator.test.js.map +1 -0
  64. package/dist/core/page-status.d.ts +15 -1
  65. package/dist/core/page-status.d.ts.map +1 -1
  66. package/dist/core/page-status.js +21 -0
  67. package/dist/core/page-status.js.map +1 -1
  68. package/dist/core/project-adapter.d.ts +73 -0
  69. package/dist/core/project-adapter.d.ts.map +1 -0
  70. package/dist/core/project-adapter.js +364 -0
  71. package/dist/core/project-adapter.js.map +1 -0
  72. package/dist/core/schema-export.d.ts +32 -0
  73. package/dist/core/schema-export.d.ts.map +1 -0
  74. package/dist/core/schema-export.js +493 -0
  75. package/dist/core/schema-export.js.map +1 -0
  76. package/dist/core/theme-audit.d.ts +104 -0
  77. package/dist/core/theme-audit.d.ts.map +1 -0
  78. package/dist/core/theme-audit.js +127 -0
  79. package/dist/core/theme-audit.js.map +1 -0
  80. package/dist/core/theme-audit.test.d.ts +2 -0
  81. package/dist/core/theme-audit.test.d.ts.map +1 -0
  82. package/dist/core/theme-audit.test.js +198 -0
  83. package/dist/core/theme-audit.test.js.map +1 -0
  84. package/dist/core/url-import.d.ts +41 -0
  85. package/dist/core/url-import.d.ts.map +1 -0
  86. package/dist/core/url-import.js +207 -0
  87. package/dist/core/url-import.js.map +1 -0
  88. package/dist/core/video-import.d.ts +46 -0
  89. package/dist/core/video-import.d.ts.map +1 -0
  90. package/dist/core/video-import.js +192 -0
  91. package/dist/core/video-import.js.map +1 -0
  92. package/dist/core/visual-import.d.ts +42 -0
  93. package/dist/core/visual-import.d.ts.map +1 -0
  94. package/dist/core/visual-import.js +227 -0
  95. package/dist/core/visual-import.js.map +1 -0
  96. package/dist/index.d.ts +31 -2
  97. package/dist/index.d.ts.map +1 -1
  98. package/dist/index.js +31 -1
  99. package/dist/index.js.map +1 -1
  100. package/dist/mcp/index.js +32 -0
  101. package/dist/mcp/index.js.map +1 -1
  102. package/package.json +10 -4
@@ -0,0 +1,485 @@
1
+ /**
2
+ * Intent Parser — Natural language → CockpitAction.
3
+ * Feature #778 (Il Sarto Parla), PBI #780 (Phase 1).
4
+ *
5
+ * Two modes:
6
+ * - Local (rule-based): regex patterns for IT/EN, always works, zero API.
7
+ * - LLM (callback): delegates to external LLM, validates output, falls back to local.
8
+ *
9
+ * Pure functions, no I/O, no LLM client.
10
+ */
11
+ import { COCKPIT_SECTION_TYPES } from './cockpit-api.js';
12
+ import { getCockpitActionSchema, getAllSectionSchemas } from './schema-export.js';
13
+ // ---------------------------------------------------------------------------
14
+ // Section type aliases (IT + EN)
15
+ // ---------------------------------------------------------------------------
16
+ const SECTION_ALIASES = {
17
+ // IT
18
+ 'eroe': 'hero', 'intestazione': 'hero', 'banner': 'hero',
19
+ 'carte': 'cards', 'schede': 'cards', 'card': 'cards',
20
+ 'confronto': 'comparison', 'comparazione': 'comparison',
21
+ 'azione': 'cta', 'chiamata': 'cta',
22
+ 'modulo': 'form', 'formulario': 'form',
23
+ 'manifesto': 'manifesto',
24
+ 'spaziatore': 'spacer', 'spazio': 'spacer',
25
+ 'statistiche': 'stats', 'numeri': 'stats', 'metriche': 'stats',
26
+ 'come funziona': 'how-it-works', 'passi': 'how-it-works', 'fasi': 'how-it-works',
27
+ 'consulente': 'advisor', 'consigliere': 'advisor',
28
+ 'diagramma': 'mermaid-diagram', 'grafico': 'mermaid-diagram',
29
+ 'lista dati': 'data-list', 'tabella': 'data-list',
30
+ 'azione form': 'action-form',
31
+ 'catalogo': 'valentino-catalog',
32
+ // EN passthrough
33
+ 'hero': 'hero', 'cards': 'cards', 'comparison': 'comparison',
34
+ 'cta': 'cta', 'form': 'form', 'spacer': 'spacer',
35
+ 'stats': 'stats', 'how-it-works': 'how-it-works', 'advisor': 'advisor',
36
+ 'mermaid-diagram': 'mermaid-diagram', 'data-list': 'data-list',
37
+ 'action-form': 'action-form', 'valentino-catalog': 'valentino-catalog',
38
+ 'showcase-intro': 'showcase-intro', 'component-showcase': 'component-showcase',
39
+ 'agent-dashboard': 'agent-dashboard', 'agent-graph': 'agent-graph',
40
+ 'agent-list': 'agent-list', 'mermaid': 'mermaid-diagram',
41
+ };
42
+ function resolveSectionType(raw) {
43
+ const normalized = raw.toLowerCase().trim();
44
+ if (COCKPIT_SECTION_TYPES.includes(normalized))
45
+ return normalized;
46
+ return SECTION_ALIASES[normalized] || null;
47
+ }
48
+ // ---------------------------------------------------------------------------
49
+ // Number parsing (IT + EN)
50
+ // ---------------------------------------------------------------------------
51
+ const WORD_NUMBERS = {
52
+ 'prima': 0, 'primo': 0, 'first': 0,
53
+ 'seconda': 1, 'secondo': 1, 'second': 1,
54
+ 'terza': 2, 'terzo': 2, 'third': 2,
55
+ 'quarta': 3, 'quarto': 3, 'fourth': 3,
56
+ 'quinta': 4, 'quinto': 4, 'fifth': 4,
57
+ 'ultima': -1, 'ultimo': -1, 'last': -1,
58
+ 'penultima': -2, 'penultimo': -2,
59
+ };
60
+ function parseIndex(raw, totalSections) {
61
+ const trimmed = raw.trim().toLowerCase();
62
+ // Word numbers
63
+ if (trimmed in WORD_NUMBERS) {
64
+ const val = WORD_NUMBERS[trimmed];
65
+ return val < 0 ? totalSections + val : val;
66
+ }
67
+ // Numeric (1-based in user language → 0-based)
68
+ const num = parseInt(trimmed, 10);
69
+ if (!isNaN(num)) {
70
+ // If user says "sezione 1" they mean index 0
71
+ return num > 0 ? num - 1 : num;
72
+ }
73
+ return null;
74
+ }
75
+ // --- QUERY patterns ---
76
+ /** Helper: check if input contains any of the given words (prefix-match for IT conjugation) */
77
+ function hasWord(input, ...prefixes) {
78
+ const lower = input.toLowerCase();
79
+ return prefixes.some(p => new RegExp(`\\b${p}\\w*\\b`, 'i').test(lower));
80
+ }
81
+ function hasAny(input, ...words) {
82
+ const lower = input.toLowerCase();
83
+ return words.some(w => lower.includes(w));
84
+ }
85
+ const queryPatterns = [
86
+ // list section types (must be before list-sections)
87
+ (input) => {
88
+ if (hasAny(input, 'tipi', 'types', 'type') && hasAny(input, 'sezion', 'section', 'disponibil', 'available')) {
89
+ return {
90
+ action: { action: 'query', query: { type: 'list-section-types' } },
91
+ confidence: 'high',
92
+ description: 'List available section types',
93
+ };
94
+ }
95
+ return null;
96
+ },
97
+ // get section by index — must be before list-sections
98
+ (input, spec) => {
99
+ const match = input.match(/(?:sezion\w*|section)\s*#?\s*(\d+|prima|primo|second[oa]|terz[oa]|quart[oa]|quint[oa]|ultim[oa]|first|second|third|fourth|fifth|last)\b/i);
100
+ if (match && hasWord(input, 'mostr', 'show', 'dettagl', 'detail', 'vedi', 'see')) {
101
+ const idx = parseIndex(match[1], spec.sections.length);
102
+ if (idx !== null && idx >= 0 && idx < spec.sections.length) {
103
+ return {
104
+ action: { action: 'query', query: { type: 'get-section', index: idx } },
105
+ confidence: 'high',
106
+ description: `Show section #${idx}`,
107
+ };
108
+ }
109
+ }
110
+ return null;
111
+ },
112
+ // list sections
113
+ (input) => {
114
+ if ((hasWord(input, 'mostr', 'elenc', 'list', 'show') || hasAny(input, 'quali', 'quante')) &&
115
+ hasAny(input, 'sezion', 'section')) {
116
+ return {
117
+ action: { action: 'query', query: { type: 'list-sections' } },
118
+ confidence: 'high',
119
+ description: 'List all sections',
120
+ };
121
+ }
122
+ return null;
123
+ },
124
+ // describe page
125
+ (input) => {
126
+ if ((hasWord(input, 'descriv', 'describe', 'panoramic', 'overview', 'struttur', 'structure') &&
127
+ hasAny(input, 'pagin', 'page')) ||
128
+ (hasAny(input, 'com\'è', 'come è', 'com è') && hasAny(input, 'fatt', 'struttur'))) {
129
+ return {
130
+ action: { action: 'query', query: { type: 'describe-page' } },
131
+ confidence: 'high',
132
+ description: 'Describe page structure',
133
+ };
134
+ }
135
+ return null;
136
+ },
137
+ // validate
138
+ (input) => {
139
+ if (hasWord(input, 'valida', 'validate', 'check', 'controll', 'verific')) {
140
+ return {
141
+ action: { action: 'query', query: { type: 'validate' } },
142
+ confidence: 'high',
143
+ description: 'Validate page spec',
144
+ };
145
+ }
146
+ return null;
147
+ },
148
+ ];
149
+ // --- MUTATION patterns ---
150
+ const mutationPatterns = [
151
+ // add section — extract last known section type word from input
152
+ (input, spec) => {
153
+ if (!hasWord(input, 'aggiung', 'add', 'inserisc', 'insert', 'mett', 'put'))
154
+ return null;
155
+ // Extract all words and find a section type
156
+ const words = input.toLowerCase().split(/\s+/);
157
+ let sectionType = null;
158
+ for (const word of words) {
159
+ const resolved = resolveSectionType(word);
160
+ if (resolved) {
161
+ sectionType = resolved;
162
+ break;
163
+ }
164
+ }
165
+ // Try two-word combos too (e.g., "how-it-works", "data-list")
166
+ if (!sectionType) {
167
+ for (let i = 0; i < words.length - 1; i++) {
168
+ const combo = `${words[i]}-${words[i + 1]}`;
169
+ const resolved = resolveSectionType(combo);
170
+ if (resolved) {
171
+ sectionType = resolved;
172
+ break;
173
+ }
174
+ }
175
+ }
176
+ if (!sectionType)
177
+ return null;
178
+ // Check for position
179
+ let atIndex;
180
+ const posMatch = input.match(/(?:posizione|position|alla|al|at|in)\s+(\d+|inizio|fine|start|end)/i);
181
+ if (posMatch) {
182
+ const posRaw = posMatch[1].toLowerCase();
183
+ if (posRaw === 'fine' || posRaw === 'end')
184
+ atIndex = undefined;
185
+ else if (posRaw === 'inizio' || posRaw === 'start')
186
+ atIndex = 0;
187
+ else {
188
+ const parsed = parseIndex(posRaw, spec.sections.length);
189
+ if (parsed !== null)
190
+ atIndex = parsed;
191
+ }
192
+ }
193
+ const section = buildMinimalSection(sectionType);
194
+ return {
195
+ action: { action: 'add-section', section, atIndex },
196
+ confidence: 'medium',
197
+ description: `Add ${sectionType} section${atIndex != null ? ` at position ${atIndex}` : ''}`,
198
+ };
199
+ },
200
+ // remove section by index or type
201
+ (input, spec) => {
202
+ if (!hasWord(input, 'rimuov', 'remove', 'elimin', 'delete', 'togl'))
203
+ return null;
204
+ // By index: "rimuovi sezione 2"
205
+ const idxMatch = input.match(/(?:sezion\w*|section)\s*#?\s*(\d+|prima|primo|second[oa]|terz[oa]|quart[oa]|quint[oa]|ultim[oa]|first|second|third|fourth|fifth|last)/i);
206
+ if (idxMatch) {
207
+ const idx = parseIndex(idxMatch[1], spec.sections.length);
208
+ if (idx !== null && idx >= 0 && idx < spec.sections.length) {
209
+ return {
210
+ action: { action: 'remove-section', sectionIndex: idx },
211
+ confidence: 'high',
212
+ description: `Remove section #${idx} (${spec.sections[idx].type})`,
213
+ };
214
+ }
215
+ }
216
+ // By type: find last word that resolves to a section type
217
+ const words = input.toLowerCase().split(/\s+/);
218
+ for (let i = words.length - 1; i >= 0; i--) {
219
+ const resolved = resolveSectionType(words[i]);
220
+ if (resolved) {
221
+ const idx = spec.sections.findIndex(s => s.type === resolved);
222
+ if (idx !== -1) {
223
+ return {
224
+ action: { action: 'remove-section', sectionIndex: idx },
225
+ confidence: 'medium',
226
+ description: `Remove first ${resolved} section (#${idx})`,
227
+ };
228
+ }
229
+ }
230
+ }
231
+ return null;
232
+ },
233
+ // move section
234
+ (input, spec) => {
235
+ // "sposta sezione 3 in posizione 1", "move section 2 to position 0"
236
+ const match = input.match(/\b(?:sposta\w*|move|muovi\w*)\b.*\b(?:sezion[ie]|section)\b\s*#?(\d+|prima|primo|second[oa]|terz[oa]|ultim[oa]|first|second|third|last).*?(?:posizione?\s*|position\s*|a\s+|to\s+)#?(\d+|prima|primo|second[oa]|terz[oa]|ultim[oa]|inizio|fine|first|last|start|end)/i);
237
+ if (match) {
238
+ const from = parseIndex(match[1], spec.sections.length);
239
+ let to;
240
+ const toRaw = match[2].toLowerCase();
241
+ if (toRaw === 'inizio' || toRaw === 'start')
242
+ to = 0;
243
+ else if (toRaw === 'fine' || toRaw === 'end')
244
+ to = spec.sections.length - 1;
245
+ else
246
+ to = parseIndex(match[2], spec.sections.length);
247
+ if (from !== null && to !== null && from >= 0 && to >= 0) {
248
+ return {
249
+ action: { action: 'move-section', fromIndex: from, toIndex: to },
250
+ confidence: 'medium',
251
+ description: `Move section from #${from} to #${to}`,
252
+ };
253
+ }
254
+ }
255
+ return null;
256
+ },
257
+ // edit section field
258
+ (input, spec) => {
259
+ // "cambia il titolo dell'hero in X", "change hero title to X"
260
+ const match = input.match(/\b(?:cambia\w*|change|modifica\w*|edit|aggiorna\w*|update|sett?a|set)\b.*?\b(?:(?:il|la|lo|the|l['']?)\s+)?(\w+)\b.*?\b(?:dell['']?|del|della|of\s+the|of)\s*([\w\s-]+?)(?:\s+(?:in|to|con|a|=)\s+(.+))?$/i);
261
+ if (match) {
262
+ const fieldRaw = match[1].toLowerCase();
263
+ const targetRaw = match[2].trim();
264
+ const value = match[3]?.trim();
265
+ const sectionType = resolveSectionType(targetRaw);
266
+ if (!sectionType || !value)
267
+ return null;
268
+ const idx = spec.sections.findIndex(s => s.type === sectionType);
269
+ if (idx === -1)
270
+ return null;
271
+ // Map common field names
272
+ const fieldMap = {
273
+ 'titolo': 'titleKey', 'title': 'titleKey',
274
+ 'sottotitolo': 'subtitleKey', 'subtitle': 'subtitleKey',
275
+ 'tagline': 'taglineKey',
276
+ 'descrizione': 'descKey', 'description': 'descKey',
277
+ 'superficie': 'presentation.surface', 'surface': 'presentation.surface',
278
+ 'tono': 'presentation.tone', 'tone': 'presentation.tone',
279
+ };
280
+ const field = fieldMap[fieldRaw] || fieldRaw;
281
+ return {
282
+ action: { action: 'edit-section', sectionIndex: idx, patch: { [field]: value } },
283
+ confidence: 'medium',
284
+ description: `Edit ${sectionType} section: set ${field} = "${value}"`,
285
+ };
286
+ }
287
+ return null;
288
+ },
289
+ // edit page-level field
290
+ (input) => {
291
+ // "cambia il profilo della pagina in product-surface"
292
+ const match = input.match(/\b(?:cambia\w*|change|sett?a|set)\b.*?\b(?:profilo|profile)\b.*?\b(?:pagina|page)\b.*?(?:in|to|=)\s+(.+)$/i);
293
+ if (match) {
294
+ return {
295
+ action: { action: 'edit-page', patch: { profile: match[1].trim() } },
296
+ confidence: 'medium',
297
+ description: `Set page profile to "${match[1].trim()}"`,
298
+ };
299
+ }
300
+ // "cambia il titolo della pagina in X"
301
+ const titleMatch = input.match(/\b(?:cambia\w*|change|sett?a|set)\b.*?\b(?:titolo|title)\b.*?\b(?:pagina|page)\b.*?(?:in|to|=)\s+(.+)$/i);
302
+ if (titleMatch) {
303
+ return {
304
+ action: { action: 'edit-page', patch: { titleKey: titleMatch[1].trim() } },
305
+ confidence: 'medium',
306
+ description: `Set page title to "${titleMatch[1].trim()}"`,
307
+ };
308
+ }
309
+ return null;
310
+ },
311
+ ];
312
+ // ---------------------------------------------------------------------------
313
+ // Minimal section builder
314
+ // ---------------------------------------------------------------------------
315
+ function buildMinimalSection(sectionType) {
316
+ const prefix = `new.${sectionType}`;
317
+ switch (sectionType) {
318
+ case 'hero':
319
+ return { type: 'hero', titleKey: `${prefix}.title`, presentation: { surface: 'shell-dark', rhythmProfile: 'hero' } };
320
+ case 'cards':
321
+ return { type: 'cards', variant: 'catalog', titleKey: `${prefix}.title`, items: [{ titleKey: `${prefix}.item1.title` }], presentation: { rhythmProfile: 'feature' } };
322
+ case 'cta':
323
+ return { type: 'cta', titleKey: `${prefix}.title`, presentation: { surface: 'accent', rhythmProfile: 'proof' } };
324
+ case 'stats':
325
+ return { type: 'stats', items: [{ valueKey: `${prefix}.item1.value`, labelKey: `${prefix}.item1.label` }], presentation: { rhythmProfile: 'metrics' } };
326
+ case 'how-it-works':
327
+ return { type: 'how-it-works', steps: [{ numKey: '1', titleKey: `${prefix}.step1.title`, descKey: `${prefix}.step1.desc` }], presentation: { rhythmProfile: 'feature' } };
328
+ case 'form':
329
+ return { type: 'form', titleKey: `${prefix}.title`, submitKey: `${prefix}.submit`, fields: [{ name: 'email', type: 'email', labelKey: `${prefix}.email` }] };
330
+ case 'comparison':
331
+ return { type: 'comparison', titleKey: `${prefix}.title`, left: { titleKey: `${prefix}.left`, itemsKeys: [] }, right: { titleKey: `${prefix}.right`, itemsKeys: [] } };
332
+ case 'manifesto':
333
+ return { type: 'manifesto', presentation: { surface: 'reading-light', rhythmProfile: 'reading' } };
334
+ case 'spacer':
335
+ return { type: 'spacer', size: 'md' };
336
+ case 'advisor':
337
+ return { type: 'advisor', titleKey: `${prefix}.title`, submitKey: `${prefix}.submit`, fallbackTitleKey: `${prefix}.fallback.title`, fallbackBodyKey: `${prefix}.fallback.body`, prompts: [] };
338
+ case 'mermaid-diagram':
339
+ return { type: 'mermaid-diagram', mermaidCode: 'graph LR\n A-->B' };
340
+ case 'data-list':
341
+ return { type: 'data-list', dataUrl: '/api/data', columns: [{ key: 'id', labelKey: `${prefix}.col.id` }] };
342
+ case 'action-form':
343
+ return { type: 'action-form', titleKey: `${prefix}.title`, submitUrl: '/api/submit', submitKey: `${prefix}.submit`, successKey: `${prefix}.success`, fields: [{ name: 'name', type: 'text', labelKey: `${prefix}.name` }] };
344
+ default:
345
+ return { type: sectionType, presentation: {} };
346
+ }
347
+ }
348
+ // ---------------------------------------------------------------------------
349
+ // Local parser
350
+ // ---------------------------------------------------------------------------
351
+ function parseLocal(input, spec) {
352
+ // Try query patterns first (read-only, safer)
353
+ for (const matcher of queryPatterns) {
354
+ const result = matcher(input, spec);
355
+ if (result)
356
+ return { intent: result, mode: 'local', raw: input };
357
+ }
358
+ // Then mutation patterns
359
+ for (const matcher of mutationPatterns) {
360
+ const result = matcher(input, spec);
361
+ if (result)
362
+ return { intent: result, mode: 'local', raw: input };
363
+ }
364
+ return { intent: null, mode: 'local', raw: input };
365
+ }
366
+ // ---------------------------------------------------------------------------
367
+ // LLM parser
368
+ // ---------------------------------------------------------------------------
369
+ function buildLlmPrompt(input, spec) {
370
+ const sectionSummary = spec.sections.map((s, i) => {
371
+ const titleKey = ('titleKey' in s) ? s.titleKey : undefined;
372
+ return ` ${i}: ${s.type}${titleKey ? ` (${titleKey})` : ''}`;
373
+ }).join('\n');
374
+ return [
375
+ 'You are the Valentino Cockpit intent parser.',
376
+ 'Parse the operator\'s request into a structured CockpitAction JSON.',
377
+ '',
378
+ '## Current page structure',
379
+ `id: ${spec.id}`,
380
+ `profile: ${spec.profile || 'none'}`,
381
+ `sections:`,
382
+ sectionSummary,
383
+ '',
384
+ '## Operator request',
385
+ `"${input}"`,
386
+ '',
387
+ '## Rules',
388
+ '- Return ONLY valid JSON matching CockpitAction schema',
389
+ '- For queries use: { "action": "query", "query": { "type": "..." } }',
390
+ '- For mutations use the appropriate action type',
391
+ '- Section indices are 0-based',
392
+ '- If the request is ambiguous, prefer a query over a mutation',
393
+ '- If you cannot parse the request, return: { "action": "query", "query": { "type": "describe-page" } }',
394
+ '',
395
+ 'Return ONLY valid JSON, no markdown fences, no explanation.',
396
+ ].join('\n');
397
+ }
398
+ function buildSectionSummary(spec) {
399
+ return spec.sections.map((s, i) => ({
400
+ index: i,
401
+ type: s.type,
402
+ ...('titleKey' in s ? { titleKey: s.titleKey } : {}),
403
+ }));
404
+ }
405
+ async function parseWithLlm(input, spec, llm) {
406
+ const context = {
407
+ pageSpec: spec,
408
+ actionSchema: getCockpitActionSchema(),
409
+ sectionSchemas: getAllSectionSchemas(),
410
+ sectionSummary: buildSectionSummary(spec),
411
+ };
412
+ const prompt = buildLlmPrompt(input, spec);
413
+ try {
414
+ const raw = await llm(prompt, context);
415
+ let parsed = raw;
416
+ if (typeof raw === 'string') {
417
+ const cleaned = raw.replace(/^```(?:json)?\s*\n?/i, '').replace(/\n?```\s*$/i, '').trim();
418
+ try {
419
+ parsed = JSON.parse(cleaned);
420
+ }
421
+ catch {
422
+ // Fall back to local
423
+ const local = parseLocal(input, spec);
424
+ return { ...local, mode: 'llm', fallbackReason: 'LLM returned invalid JSON' };
425
+ }
426
+ }
427
+ if (typeof parsed === 'object' && parsed !== null && 'action' in parsed) {
428
+ const action = parsed;
429
+ return {
430
+ intent: {
431
+ action,
432
+ confidence: 'high',
433
+ description: `LLM parsed: ${action.action}`,
434
+ },
435
+ mode: 'llm',
436
+ raw: input,
437
+ };
438
+ }
439
+ const local = parseLocal(input, spec);
440
+ return { ...local, mode: 'llm', fallbackReason: 'LLM returned non-action object' };
441
+ }
442
+ catch (err) {
443
+ const local = parseLocal(input, spec);
444
+ return {
445
+ ...local,
446
+ mode: 'llm',
447
+ fallbackReason: `LLM call failed: ${err instanceof Error ? err.message : String(err)}`,
448
+ };
449
+ }
450
+ }
451
+ // ---------------------------------------------------------------------------
452
+ // Public API
453
+ // ---------------------------------------------------------------------------
454
+ /**
455
+ * Parse a natural language operator request into a CockpitAction.
456
+ *
457
+ * Supports IT and EN. If `llm` callback is provided, tries LLM first
458
+ * with automatic fallback to local rule-based parser.
459
+ *
460
+ * @example
461
+ * ```ts
462
+ * // Local mode
463
+ * const result = parseIntent("mostrami le sezioni", pageSpec);
464
+ *
465
+ * // LLM mode with fallback
466
+ * const result = await parseIntent("aggiungi una hero", pageSpec, myLlm);
467
+ * ```
468
+ */
469
+ export async function parseIntent(input, spec, llm) {
470
+ if (llm) {
471
+ return parseWithLlm(input, spec, llm);
472
+ }
473
+ return parseLocal(input, spec);
474
+ }
475
+ /**
476
+ * Synchronous local-only parsing. No LLM, always works.
477
+ */
478
+ export function parseIntentLocal(input, spec) {
479
+ return parseLocal(input, spec);
480
+ }
481
+ /**
482
+ * Exported for testing.
483
+ */
484
+ export { resolveSectionType, parseIndex, buildMinimalSection, buildSectionSummary };
485
+ //# sourceMappingURL=intent-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"intent-parser.js","sourceRoot":"","sources":["../../src/core/intent-parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * OpenRouter LLM Client — Minimal HTTP client for intent parsing.
3
+ * Feature #778 (Il Sarto Parla).
4
+ *
5
+ * Zero external dependencies — uses Node built-in fetch (Node 18+).
6
+ * Implements IntentLlmCallback interface for plug-and-play with intent-parser.
7
+ */
8
+ import type { IntentLlmCallback } from './intent-parser.js';
9
+ export type OpenRouterConfig = {
10
+ apiKey: string;
11
+ model?: string;
12
+ /** Base URL override (default: https://openrouter.ai/api/v1) */
13
+ baseUrl?: string;
14
+ /** Max tokens for response (default: 1024) */
15
+ maxTokens?: number;
16
+ /** Temperature (default: 0.1 — low for structured output) */
17
+ temperature?: number;
18
+ };
19
+ /**
20
+ * Create an IntentLlmCallback wired to OpenRouter.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * const llm = createOpenRouterCallback({ apiKey: 'sk-or-...' });
25
+ * const result = await parseIntent("aggiungi una hero", spec, llm);
26
+ * ```
27
+ */
28
+ export declare function createOpenRouterCallback(config: OpenRouterConfig): IntentLlmCallback;
29
+ /**
30
+ * Test connectivity to OpenRouter.
31
+ * Returns true if the API key is valid.
32
+ */
33
+ export declare function testOpenRouterConnection(config: OpenRouterConfig): Promise<{
34
+ ok: boolean;
35
+ error?: string;
36
+ model?: string;
37
+ }>;
38
+ //# sourceMappingURL=openrouter-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openrouter-client.d.ts","sourceRoot":"","sources":["../../src/core/openrouter-client.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAoB,MAAM,oBAAoB,CAAC;AAM9E,MAAM,MAAM,gBAAgB,GAAG;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6DAA6D;IAC7D,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAoGF;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,gBAAgB,GAAG,iBAAiB,CA4BpF;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CASjI"}