@portel/photon-core 2.2.0 → 2.4.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.
- package/dist/asset-discovery.d.ts +25 -0
- package/dist/asset-discovery.d.ts.map +1 -0
- package/dist/asset-discovery.js +144 -0
- package/dist/asset-discovery.js.map +1 -0
- package/dist/base.d.ts +1 -1
- package/dist/base.d.ts.map +1 -1
- package/dist/base.js +12 -6
- package/dist/base.js.map +1 -1
- package/dist/class-detection.d.ts +32 -0
- package/dist/class-detection.d.ts.map +1 -0
- package/dist/class-detection.js +86 -0
- package/dist/class-detection.js.map +1 -0
- package/dist/compiler.d.ts +22 -0
- package/dist/compiler.d.ts.map +1 -0
- package/dist/compiler.js +48 -0
- package/dist/compiler.js.map +1 -0
- package/dist/config.d.ts +63 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +117 -0
- package/dist/config.js.map +1 -0
- package/dist/design-system/tokens.d.ts +66 -0
- package/dist/design-system/tokens.d.ts.map +1 -1
- package/dist/design-system/tokens.js +324 -44
- package/dist/design-system/tokens.js.map +1 -1
- package/dist/env-utils.d.ts +61 -0
- package/dist/env-utils.d.ts.map +1 -0
- package/dist/env-utils.js +171 -0
- package/dist/env-utils.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp-apps.d.ts +130 -0
- package/dist/mcp-apps.d.ts.map +1 -0
- package/dist/mcp-apps.js +87 -0
- package/dist/mcp-apps.js.map +1 -0
- package/dist/mime-types.d.ts +13 -0
- package/dist/mime-types.d.ts.map +1 -0
- package/dist/mime-types.js +47 -0
- package/dist/mime-types.js.map +1 -0
- package/dist/rendering/index.d.ts +49 -0
- package/dist/rendering/index.d.ts.map +1 -1
- package/dist/rendering/index.js +153 -0
- package/dist/rendering/index.js.map +1 -1
- package/dist/schema-extractor.d.ts +18 -1
- package/dist/schema-extractor.d.ts.map +1 -1
- package/dist/schema-extractor.js +81 -11
- package/dist/schema-extractor.js.map +1 -1
- package/dist/types.d.ts +75 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/validation.d.ts +51 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +249 -0
- package/dist/validation.js.map +1 -0
- package/dist/version-check.d.ts +22 -0
- package/dist/version-check.d.ts.map +1 -0
- package/dist/version-check.js +91 -0
- package/dist/version-check.js.map +1 -0
- package/package.json +12 -2
- package/src/asset-discovery.ts +160 -0
- package/src/base.ts +12 -5
- package/src/class-detection.ts +94 -0
- package/src/compiler.ts +57 -0
- package/src/config.ts +134 -0
- package/src/design-system/tokens.ts +381 -57
- package/src/env-utils.ts +216 -0
- package/src/index.ts +106 -0
- package/src/mcp-apps.ts +204 -0
- package/src/mime-types.ts +49 -0
- package/src/rendering/index.ts +197 -0
- package/src/schema-extractor.ts +95 -12
- package/src/types.ts +82 -0
- package/src/validation.ts +363 -0
- package/src/version-check.ts +92 -0
package/src/rendering/index.ts
CHANGED
|
@@ -19,6 +19,8 @@ export * from './field-renderers.js';
|
|
|
19
19
|
export * from './template-engine.js';
|
|
20
20
|
export * from '../design-system/index.js';
|
|
21
21
|
|
|
22
|
+
import { analyzeFields, type FieldMapping } from './field-analyzer.js';
|
|
23
|
+
import { selectLayout, type LayoutType } from './layout-selector.js';
|
|
22
24
|
import { generateFieldAnalyzerJS } from './field-analyzer.js';
|
|
23
25
|
import { generateLayoutSelectorJS } from './layout-selector.js';
|
|
24
26
|
import { generateComponentsJS, generateComponentCSS } from './components.js';
|
|
@@ -61,3 +63,198 @@ export function generateSmartRenderingCSS(): string {
|
|
|
61
63
|
generateTemplateEngineCSS(),
|
|
62
64
|
].join('\n');
|
|
63
65
|
}
|
|
66
|
+
|
|
67
|
+
// ===== Smart Rendering Utilities =====
|
|
68
|
+
// Shared by NCP, Lumina, and other runtimes
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Check if data would benefit from rich HTML rendering
|
|
72
|
+
* (arrays of objects, nested structures, etc.)
|
|
73
|
+
*/
|
|
74
|
+
export function shouldUseRichRendering(data: any): boolean {
|
|
75
|
+
if (!data) return false;
|
|
76
|
+
|
|
77
|
+
// Arrays of objects benefit from list/grid rendering
|
|
78
|
+
if (Array.isArray(data) && data.length > 0 && typeof data[0] === 'object') {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Nested objects benefit from tree/card rendering
|
|
83
|
+
if (typeof data === 'object' && !Array.isArray(data)) {
|
|
84
|
+
const values = Object.values(data);
|
|
85
|
+
if (values.some(v => typeof v === 'object' && v !== null)) {
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Generate an HTML content block for MCP tool responses.
|
|
95
|
+
* Analyses data, selects layout, and embeds CSS/JS for rich rendering.
|
|
96
|
+
*/
|
|
97
|
+
export function generateHTMLContent(
|
|
98
|
+
data: any,
|
|
99
|
+
options: {
|
|
100
|
+
title?: string;
|
|
101
|
+
format?: LayoutType;
|
|
102
|
+
standalone?: boolean;
|
|
103
|
+
theme?: 'light' | 'dark';
|
|
104
|
+
} = {}
|
|
105
|
+
): string {
|
|
106
|
+
const { title, format, standalone = false, theme } = options;
|
|
107
|
+
|
|
108
|
+
let fieldMapping: FieldMapping | undefined;
|
|
109
|
+
if (data && typeof data === 'object') {
|
|
110
|
+
fieldMapping = analyzeFields(data);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const layout = format || selectLayout(data);
|
|
114
|
+
|
|
115
|
+
const renderScript = `
|
|
116
|
+
<script>
|
|
117
|
+
${generateSmartRenderingJS()}
|
|
118
|
+
|
|
119
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
120
|
+
const container = document.getElementById('smart-render-container');
|
|
121
|
+
const data = ${JSON.stringify(data)};
|
|
122
|
+
const layout = '${layout}';
|
|
123
|
+
const fieldMapping = ${JSON.stringify(fieldMapping || {})};
|
|
124
|
+
|
|
125
|
+
if (window.TemplateEngine && window.TemplateEngine.render) {
|
|
126
|
+
container.innerHTML = window.TemplateEngine.render(data, {
|
|
127
|
+
layout,
|
|
128
|
+
fieldMapping,
|
|
129
|
+
title: ${JSON.stringify(title || '')}
|
|
130
|
+
});
|
|
131
|
+
} else {
|
|
132
|
+
container.innerHTML = '<pre>' + JSON.stringify(data, null, 2) + '</pre>';
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
</script>
|
|
136
|
+
`;
|
|
137
|
+
|
|
138
|
+
const themeVars = theme ? `
|
|
139
|
+
:root {
|
|
140
|
+
--bg-primary: ${theme === 'dark' ? '#0a0a0a' : '#ffffff'};
|
|
141
|
+
--text-primary: ${theme === 'dark' ? '#f5f5f5' : '#1a1a1a'};
|
|
142
|
+
}
|
|
143
|
+
` : '';
|
|
144
|
+
|
|
145
|
+
const styles = `
|
|
146
|
+
<style>
|
|
147
|
+
${generateSmartRenderingCSS()}
|
|
148
|
+
${themeVars}
|
|
149
|
+
</style>
|
|
150
|
+
`;
|
|
151
|
+
|
|
152
|
+
const content = `
|
|
153
|
+
${styles}
|
|
154
|
+
<div id="smart-render-container" class="smart-render">
|
|
155
|
+
${title ? `<h2 class="smart-render-title">${title}</h2>` : ''}
|
|
156
|
+
<div class="loading">Loading...</div>
|
|
157
|
+
</div>
|
|
158
|
+
${renderScript}
|
|
159
|
+
`;
|
|
160
|
+
|
|
161
|
+
if (standalone) {
|
|
162
|
+
return `<!DOCTYPE html>
|
|
163
|
+
<html${theme ? ` data-theme="${theme}"` : ''}>
|
|
164
|
+
<head>
|
|
165
|
+
<meta charset="UTF-8">
|
|
166
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
167
|
+
<title>${title || 'Result'}</title>
|
|
168
|
+
</head>
|
|
169
|
+
<body>
|
|
170
|
+
${content}
|
|
171
|
+
</body>
|
|
172
|
+
</html>`;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return content;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Create an MCP content block with HTML type
|
|
180
|
+
*/
|
|
181
|
+
export function createHTMLContentBlock(
|
|
182
|
+
data: any,
|
|
183
|
+
options: {
|
|
184
|
+
title?: string;
|
|
185
|
+
format?: LayoutType;
|
|
186
|
+
} = {}
|
|
187
|
+
): { type: 'text'; text: string; mimeType?: string } {
|
|
188
|
+
return {
|
|
189
|
+
type: 'text',
|
|
190
|
+
text: generateHTMLContent(data, { ...options, standalone: false }),
|
|
191
|
+
mimeType: 'text/html'
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Generate an HTML fragment for embedding, returning separate html/css/js parts
|
|
197
|
+
*/
|
|
198
|
+
export function generateSmartRenderFragment(
|
|
199
|
+
data: any,
|
|
200
|
+
options: { format?: LayoutType } = {}
|
|
201
|
+
): { html: string; css: string; js: string } {
|
|
202
|
+
const { format } = options;
|
|
203
|
+
|
|
204
|
+
let fieldMapping: FieldMapping | undefined;
|
|
205
|
+
if (data && typeof data === 'object') {
|
|
206
|
+
fieldMapping = analyzeFields(data);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const layout = format || selectLayout(data);
|
|
210
|
+
|
|
211
|
+
const html = `<div id="smart-render-${Date.now()}" class="smart-render-container"></div>`;
|
|
212
|
+
const css = generateSmartRenderingCSS();
|
|
213
|
+
const js = `
|
|
214
|
+
${generateSmartRenderingJS()}
|
|
215
|
+
(function() {
|
|
216
|
+
const containers = document.querySelectorAll('.smart-render-container');
|
|
217
|
+
const container = containers[containers.length - 1];
|
|
218
|
+
const data = ${JSON.stringify(data)};
|
|
219
|
+
if (window.TemplateEngine && window.TemplateEngine.render) {
|
|
220
|
+
container.innerHTML = window.TemplateEngine.render(data, {
|
|
221
|
+
layout: '${layout}',
|
|
222
|
+
fieldMapping: ${JSON.stringify(fieldMapping || {})}
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
})();
|
|
226
|
+
`;
|
|
227
|
+
|
|
228
|
+
return { html, css, js };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Generate MCP content blocks with smart rendering.
|
|
233
|
+
* Returns JSON text block + optional HTML block for rich clients.
|
|
234
|
+
*/
|
|
235
|
+
export function generateMCPSmartContent(
|
|
236
|
+
data: any,
|
|
237
|
+
options: { includeHtml?: boolean; format?: LayoutType } = {}
|
|
238
|
+
): Array<{ type: string; text?: string; mimeType?: string }> {
|
|
239
|
+
const { includeHtml = true, format } = options;
|
|
240
|
+
|
|
241
|
+
const content: Array<{ type: string; text?: string; mimeType?: string }> = [];
|
|
242
|
+
|
|
243
|
+
// Always include JSON for compatibility
|
|
244
|
+
content.push({
|
|
245
|
+
type: 'text',
|
|
246
|
+
text: JSON.stringify(data, null, 2)
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// Optionally include HTML for rich rendering
|
|
250
|
+
if (includeHtml && shouldUseRichRendering(data)) {
|
|
251
|
+
const { html, css, js } = generateSmartRenderFragment(data, { format });
|
|
252
|
+
content.push({
|
|
253
|
+
type: 'text',
|
|
254
|
+
text: `<style>${css}</style>${html}<script>${js}</script>`,
|
|
255
|
+
mimeType: 'text/html'
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return content;
|
|
260
|
+
}
|
package/src/schema-extractor.ts
CHANGED
|
@@ -10,12 +10,14 @@
|
|
|
10
10
|
|
|
11
11
|
import * as fs from 'fs/promises';
|
|
12
12
|
import * as ts from 'typescript';
|
|
13
|
-
import { ExtractedSchema, ConstructorParam, TemplateInfo, StaticInfo, OutputFormat, YieldInfo, MCPDependency, PhotonDependency, ResolvedInjection, PhotonAssets, UIAsset, PromptAsset, ResourceAsset } from './types.js';
|
|
13
|
+
import { ExtractedSchema, ConstructorParam, TemplateInfo, StaticInfo, OutputFormat, YieldInfo, MCPDependency, PhotonDependency, CLIDependency, ResolvedInjection, PhotonAssets, UIAsset, PromptAsset, ResourceAsset, ConfigSchema, ConfigParam } from './types.js';
|
|
14
14
|
|
|
15
15
|
export interface ExtractedMetadata {
|
|
16
16
|
tools: ExtractedSchema[];
|
|
17
17
|
templates: TemplateInfo[];
|
|
18
18
|
statics: StaticInfo[];
|
|
19
|
+
/** Configuration schema from configure() method */
|
|
20
|
+
configSchema?: ConfigSchema;
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
/**
|
|
@@ -43,6 +45,13 @@ export class SchemaExtractor {
|
|
|
43
45
|
const templates: TemplateInfo[] = [];
|
|
44
46
|
const statics: StaticInfo[] = [];
|
|
45
47
|
|
|
48
|
+
// Configuration schema tracking
|
|
49
|
+
let configSchema: ConfigSchema = {
|
|
50
|
+
hasConfigureMethod: false,
|
|
51
|
+
hasGetConfigMethod: false,
|
|
52
|
+
params: [],
|
|
53
|
+
};
|
|
54
|
+
|
|
46
55
|
try {
|
|
47
56
|
// If source doesn't contain a class declaration, wrap it in one
|
|
48
57
|
let sourceToParse = source;
|
|
@@ -67,6 +76,34 @@ export class SchemaExtractor {
|
|
|
67
76
|
return;
|
|
68
77
|
}
|
|
69
78
|
|
|
79
|
+
// Handle configuration convention methods specially
|
|
80
|
+
if (methodName === 'configure') {
|
|
81
|
+
configSchema.hasConfigureMethod = true;
|
|
82
|
+
const jsdoc = this.getJSDocComment(member, sourceFile);
|
|
83
|
+
configSchema.description = this.extractDescription(jsdoc);
|
|
84
|
+
|
|
85
|
+
// Extract configure() parameters as config schema
|
|
86
|
+
const paramsType = this.getFirstParameterType(member, sourceFile);
|
|
87
|
+
if (paramsType) {
|
|
88
|
+
const { properties, required } = this.buildSchemaFromType(paramsType, sourceFile);
|
|
89
|
+
const paramDocs = this.extractParamDocs(jsdoc);
|
|
90
|
+
|
|
91
|
+
configSchema.params = Object.keys(properties).map(name => ({
|
|
92
|
+
name,
|
|
93
|
+
type: properties[name].type || 'string',
|
|
94
|
+
description: paramDocs.get(name) || properties[name].description,
|
|
95
|
+
required: required.includes(name),
|
|
96
|
+
defaultValue: properties[name].default,
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
return; // Don't add configure() as a tool
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (methodName === 'getConfig') {
|
|
103
|
+
configSchema.hasGetConfigMethod = true;
|
|
104
|
+
return; // Don't add getConfig() as a tool
|
|
105
|
+
}
|
|
106
|
+
|
|
70
107
|
const jsdoc = this.getJSDocComment(member, sourceFile);
|
|
71
108
|
|
|
72
109
|
// Check if this is an async generator method (has asterisk token)
|
|
@@ -147,6 +184,9 @@ export class SchemaExtractor {
|
|
|
147
184
|
const scheduled = this.extractScheduled(jsdoc, methodName);
|
|
148
185
|
const locked = this.extractLocked(jsdoc, methodName);
|
|
149
186
|
|
|
187
|
+
// Check for static keyword on the method
|
|
188
|
+
const isStaticMethod = member.modifiers?.some(m => m.kind === ts.SyntaxKind.StaticKeyword) || false;
|
|
189
|
+
|
|
150
190
|
tools.push({
|
|
151
191
|
name: methodName,
|
|
152
192
|
description,
|
|
@@ -159,6 +199,7 @@ export class SchemaExtractor {
|
|
|
159
199
|
...(yields && yields.length > 0 ? { yields } : {}),
|
|
160
200
|
...(isStateful ? { isStateful: true } : {}),
|
|
161
201
|
...(autorun ? { autorun: true } : {}),
|
|
202
|
+
...(isStaticMethod ? { isStatic: true } : {}),
|
|
162
203
|
// Daemon features
|
|
163
204
|
...(webhook !== undefined ? { webhook } : {}),
|
|
164
205
|
...(scheduled ? { scheduled } : {}),
|
|
@@ -188,7 +229,12 @@ export class SchemaExtractor {
|
|
|
188
229
|
console.error('Failed to parse TypeScript source:', error.message);
|
|
189
230
|
}
|
|
190
231
|
|
|
191
|
-
|
|
232
|
+
// Only include configSchema if there's a configure() method
|
|
233
|
+
const result: ExtractedMetadata = { tools, templates, statics };
|
|
234
|
+
if (configSchema.hasConfigureMethod) {
|
|
235
|
+
result.configSchema = configSchema;
|
|
236
|
+
}
|
|
237
|
+
return result;
|
|
192
238
|
}
|
|
193
239
|
|
|
194
240
|
/**
|
|
@@ -610,21 +656,24 @@ export class SchemaExtractor {
|
|
|
610
656
|
* Extract main description from JSDoc comment
|
|
611
657
|
*/
|
|
612
658
|
private extractDescription(jsdocContent: string): string {
|
|
613
|
-
// Split by @param to get only the description part
|
|
614
|
-
const
|
|
659
|
+
// Split by @param to get only the description part (also stop at other @tags)
|
|
660
|
+
const beforeTags = jsdocContent.split(/@(?:param|example|returns?|throws?|see|since|deprecated|version|author|license|ui|icon|format|stateful|autorun|webhook|cron|scheduled|locked|Template|Static|mcp|photon|cli|tags|dependencies|csp|visibility)\b/)[0];
|
|
615
661
|
|
|
616
662
|
// Remove leading * from each line and trim
|
|
617
|
-
const lines =
|
|
663
|
+
const lines = beforeTags
|
|
618
664
|
.split('\n')
|
|
619
665
|
.map((line) => line.trim().replace(/^\*\s?/, ''))
|
|
620
|
-
.filter((line) => line
|
|
666
|
+
.filter((line) => line !== ''); // Keep non-empty lines
|
|
667
|
+
|
|
668
|
+
// Take lines up to the first markdown heading (## sections are extended docs)
|
|
669
|
+
const descLines: string[] = [];
|
|
670
|
+
for (const line of lines) {
|
|
671
|
+
if (line.startsWith('#')) break;
|
|
672
|
+
descLines.push(line);
|
|
673
|
+
}
|
|
621
674
|
|
|
622
|
-
//
|
|
623
|
-
|
|
624
|
-
const meaningfulLines = lines.filter(line => line.length > 5); // Filter out short lines
|
|
625
|
-
const description = meaningfulLines.length > 0
|
|
626
|
-
? meaningfulLines[meaningfulLines.length - 1]
|
|
627
|
-
: lines.join(' ');
|
|
675
|
+
// Join all description lines into a single string
|
|
676
|
+
const description = descLines.join(' ');
|
|
628
677
|
|
|
629
678
|
// Clean up multiple spaces
|
|
630
679
|
return description.replace(/\s+/g, ' ').trim() || 'No description';
|
|
@@ -1376,6 +1425,40 @@ export class SchemaExtractor {
|
|
|
1376
1425
|
return 'marketplace';
|
|
1377
1426
|
}
|
|
1378
1427
|
|
|
1428
|
+
/**
|
|
1429
|
+
* Extract CLI dependencies from source code
|
|
1430
|
+
* Parses @cli tags in file-level or class-level JSDoc comments
|
|
1431
|
+
*
|
|
1432
|
+
* Format: @cli <name> - <install_url>
|
|
1433
|
+
*
|
|
1434
|
+
* Example:
|
|
1435
|
+
* ```
|
|
1436
|
+
* /**
|
|
1437
|
+
* * @cli git - https://git-scm.com/downloads
|
|
1438
|
+
* * @cli ffmpeg - https://ffmpeg.org/download.html
|
|
1439
|
+
* *\/
|
|
1440
|
+
* ```
|
|
1441
|
+
*/
|
|
1442
|
+
extractCLIDependencies(source: string): CLIDependency[] {
|
|
1443
|
+
const dependencies: CLIDependency[] = [];
|
|
1444
|
+
|
|
1445
|
+
// Match @cli <name> or @cli <name> - <url> pattern
|
|
1446
|
+
// The URL is optional
|
|
1447
|
+
const cliRegex = /@cli\s+(\w[\w-]*)\s*(?:-\s*([^\s*@\n]+))?/g;
|
|
1448
|
+
|
|
1449
|
+
let match;
|
|
1450
|
+
while ((match = cliRegex.exec(source)) !== null) {
|
|
1451
|
+
const [, name, installUrl] = match;
|
|
1452
|
+
|
|
1453
|
+
dependencies.push({
|
|
1454
|
+
name: name.trim(),
|
|
1455
|
+
installUrl: installUrl?.trim(),
|
|
1456
|
+
});
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
return dependencies;
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1379
1462
|
/**
|
|
1380
1463
|
* Resolve all injections for a Photon class
|
|
1381
1464
|
* Determines how each constructor parameter should be injected:
|
package/src/types.ts
CHANGED
|
@@ -63,6 +63,8 @@ export interface ExtractedSchema {
|
|
|
63
63
|
isStateful?: boolean;
|
|
64
64
|
/** True if this method should auto-execute when selected (idempotent, no required params) */
|
|
65
65
|
autorun?: boolean;
|
|
66
|
+
/** True if this is a static method (class-level, no instance needed) */
|
|
67
|
+
isStatic?: boolean;
|
|
66
68
|
|
|
67
69
|
// ═══ DAEMON FEATURES ═══
|
|
68
70
|
|
|
@@ -192,6 +194,24 @@ export interface PhotonDependency {
|
|
|
192
194
|
sourceType: 'marketplace' | 'github' | 'npm' | 'local';
|
|
193
195
|
}
|
|
194
196
|
|
|
197
|
+
/**
|
|
198
|
+
* CLI Dependency - System command-line tool required by a Photon
|
|
199
|
+
*
|
|
200
|
+
* Declared via @cli annotation:
|
|
201
|
+
* ```
|
|
202
|
+
* /**
|
|
203
|
+
* * @cli git - https://git-scm.com/downloads
|
|
204
|
+
* * @cli ffmpeg - https://ffmpeg.org/download.html
|
|
205
|
+
* *\/
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
export interface CLIDependency {
|
|
209
|
+
/** CLI command name (e.g., 'git', 'ffmpeg') */
|
|
210
|
+
name: string;
|
|
211
|
+
/** Install URL or instructions */
|
|
212
|
+
installUrl?: string;
|
|
213
|
+
}
|
|
214
|
+
|
|
195
215
|
// ════════════════════════════════════════════════════════════════════════════
|
|
196
216
|
// PHOTON ASSETS - Static files referenced via @ui, @prompt, @resource
|
|
197
217
|
// ════════════════════════════════════════════════════════════════════════════
|
|
@@ -501,3 +521,65 @@ export interface WorkflowRun {
|
|
|
501
521
|
ts: number;
|
|
502
522
|
};
|
|
503
523
|
}
|
|
524
|
+
|
|
525
|
+
// ══════════════════════════════════════════════════════════════════════════════
|
|
526
|
+
// CONFIGURATION CONVENTION
|
|
527
|
+
// ══════════════════════════════════════════════════════════════════════════════
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Configuration parameter extracted from configure() method
|
|
531
|
+
*/
|
|
532
|
+
export interface ConfigParam {
|
|
533
|
+
/** Parameter name */
|
|
534
|
+
name: string;
|
|
535
|
+
/** Parameter type (string, number, boolean, etc.) */
|
|
536
|
+
type: string;
|
|
537
|
+
/** Description from JSDoc */
|
|
538
|
+
description?: string;
|
|
539
|
+
/** Whether the parameter is required */
|
|
540
|
+
required: boolean;
|
|
541
|
+
/** Default value if any */
|
|
542
|
+
defaultValue?: any;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Configuration schema extracted from a Photon's configure() method
|
|
547
|
+
*
|
|
548
|
+
* The configure() method is a by-convention method for photon configuration.
|
|
549
|
+
* Similar to how main() makes a photon a UI application, configure() makes
|
|
550
|
+
* it a configurable photon.
|
|
551
|
+
*
|
|
552
|
+
* When present, the framework will:
|
|
553
|
+
* 1. Extract parameter schema from the method signature
|
|
554
|
+
* 2. Present a configuration UI during install/setup
|
|
555
|
+
* 3. Store config at ~/.photon/{photonName}/config.json
|
|
556
|
+
* 4. Make config available via getConfig()
|
|
557
|
+
*
|
|
558
|
+
* Example:
|
|
559
|
+
* ```typescript
|
|
560
|
+
* export default class MyPhoton extends PhotonMCP {
|
|
561
|
+
* async configure(params: {
|
|
562
|
+
* apiEndpoint: string;
|
|
563
|
+
* maxRetries?: number;
|
|
564
|
+
* }) {
|
|
565
|
+
* // Save config - framework handles storage
|
|
566
|
+
* return { success: true };
|
|
567
|
+
* }
|
|
568
|
+
*
|
|
569
|
+
* async getConfig() {
|
|
570
|
+
* // Read config - framework handles loading
|
|
571
|
+
* return loadPhotonConfig('my-photon');
|
|
572
|
+
* }
|
|
573
|
+
* }
|
|
574
|
+
* ```
|
|
575
|
+
*/
|
|
576
|
+
export interface ConfigSchema {
|
|
577
|
+
/** Whether configure() method exists */
|
|
578
|
+
hasConfigureMethod: boolean;
|
|
579
|
+
/** Whether getConfig() method exists */
|
|
580
|
+
hasGetConfigMethod: boolean;
|
|
581
|
+
/** Configuration parameters from configure() signature */
|
|
582
|
+
params: ConfigParam[];
|
|
583
|
+
/** Description from configure() JSDoc */
|
|
584
|
+
description?: string;
|
|
585
|
+
}
|