@fragments-sdk/cli 0.11.1 → 0.12.1
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/ai-client-I6MDWNYA.js +21 -0
- package/dist/bin.js +275 -368
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-PW7QTQA6.js → chunk-4OC7FTJB.js} +2 -2
- package/dist/{chunk-HRFUSSZI.js → chunk-AM4MRTMN.js} +2 -2
- package/dist/{chunk-5G3VZH43.js → chunk-GVDSFQ4E.js} +281 -351
- package/dist/chunk-GVDSFQ4E.js.map +1 -0
- package/dist/chunk-JJ2VRTBU.js +626 -0
- package/dist/chunk-JJ2VRTBU.js.map +1 -0
- package/dist/{chunk-D5PYOXEI.js → chunk-LVWFOLUZ.js} +148 -13
- package/dist/{chunk-D5PYOXEI.js.map → chunk-LVWFOLUZ.js.map} +1 -1
- package/dist/{chunk-WXSR2II7.js → chunk-OQKMEFOS.js} +58 -6
- package/dist/chunk-OQKMEFOS.js.map +1 -0
- package/dist/chunk-SXTKFDCR.js +104 -0
- package/dist/chunk-SXTKFDCR.js.map +1 -0
- package/dist/chunk-T5OMVL7E.js +443 -0
- package/dist/chunk-T5OMVL7E.js.map +1 -0
- package/dist/{chunk-ZM4ZQZWZ.js → chunk-TPWGL2XS.js} +39 -37
- package/dist/chunk-TPWGL2XS.js.map +1 -0
- package/dist/{chunk-OQO55NKV.js → chunk-WFS63PCW.js} +85 -11
- package/dist/chunk-WFS63PCW.js.map +1 -0
- package/dist/core/index.js +9 -1
- package/dist/{discovery-NEOY4MPN.js → discovery-ZJQSXF56.js} +3 -3
- package/dist/{generate-FBHSXR3D.js → generate-RJFS2JWA.js} +4 -4
- package/dist/index.js +7 -6
- package/dist/index.js.map +1 -1
- package/dist/init-ZSX3NRCZ.js +636 -0
- package/dist/init-ZSX3NRCZ.js.map +1 -0
- package/dist/mcp-bin.js +2 -2
- package/dist/{scan-CJF2DOQW.js → scan-3PMCJ4RB.js} +6 -6
- package/dist/scan-generate-SYU4PYZD.js +1115 -0
- package/dist/scan-generate-SYU4PYZD.js.map +1 -0
- package/dist/{service-TQYWY65E.js → service-VMGNJZ42.js} +3 -3
- package/dist/{snapshot-SV2JOFZH.js → snapshot-XOISO2IS.js} +2 -2
- package/dist/{static-viewer-NUBFPKWH.js → static-viewer-5GXH2MGE.js} +3 -3
- package/dist/static-viewer-5GXH2MGE.js.map +1 -0
- package/dist/{test-Z5LVO724.js → test-SI4NSHQX.js} +4 -4
- package/dist/{tokens-CE46OTMD.js → tokens-T6SIVUT5.js} +5 -5
- package/dist/{viewer-DLLJIMCK.js → viewer-7ZEAFBVN.js} +13 -13
- package/package.json +4 -4
- package/src/ai-client.ts +156 -0
- package/src/bin.ts +44 -2
- package/src/build.ts +95 -33
- package/src/commands/__tests__/drift-sync.test.ts +252 -0
- package/src/commands/__tests__/scan-generate.test.ts +497 -45
- package/src/commands/enhance.ts +11 -35
- package/src/commands/init.ts +288 -260
- package/src/commands/scan-generate.ts +740 -139
- package/src/commands/scan.ts +37 -32
- package/src/commands/setup.ts +143 -52
- package/src/commands/sync.ts +357 -0
- package/src/commands/validate.ts +43 -1
- package/src/core/component-extractor.test.ts +282 -0
- package/src/core/component-extractor.ts +1030 -0
- package/src/core/discovery.ts +93 -7
- package/src/service/enhance/props-extractor.ts +235 -13
- package/src/validators.ts +236 -0
- package/dist/chunk-5G3VZH43.js.map +0 -1
- package/dist/chunk-OQO55NKV.js.map +0 -1
- package/dist/chunk-WXSR2II7.js.map +0 -1
- package/dist/chunk-ZM4ZQZWZ.js.map +0 -1
- package/dist/init-UFGK5TCN.js +0 -867
- package/dist/init-UFGK5TCN.js.map +0 -1
- package/dist/scan-generate-SJAN5MVI.js +0 -691
- package/dist/scan-generate-SJAN5MVI.js.map +0 -1
- package/src/ai.ts +0 -266
- package/src/commands/init-framework.ts +0 -414
- package/src/mcp/bin.ts +0 -36
- package/src/migrate/bin.ts +0 -114
- package/src/theme/index.ts +0 -77
- package/src/viewer/bin.ts +0 -86
- package/src/viewer/cli/health.ts +0 -256
- package/src/viewer/cli/index.ts +0 -33
- package/src/viewer/cli/scan.ts +0 -124
- package/src/viewer/cli/utils.ts +0 -174
- /package/dist/{discovery-NEOY4MPN.js.map → ai-client-I6MDWNYA.js.map} +0 -0
- /package/dist/{chunk-PW7QTQA6.js.map → chunk-4OC7FTJB.js.map} +0 -0
- /package/dist/{chunk-HRFUSSZI.js.map → chunk-AM4MRTMN.js.map} +0 -0
- /package/dist/{scan-CJF2DOQW.js.map → discovery-ZJQSXF56.js.map} +0 -0
- /package/dist/{generate-FBHSXR3D.js.map → generate-RJFS2JWA.js.map} +0 -0
- /package/dist/{service-TQYWY65E.js.map → scan-3PMCJ4RB.js.map} +0 -0
- /package/dist/{static-viewer-NUBFPKWH.js.map → service-VMGNJZ42.js.map} +0 -0
- /package/dist/{snapshot-SV2JOFZH.js.map → snapshot-XOISO2IS.js.map} +0 -0
- /package/dist/{test-Z5LVO724.js.map → test-SI4NSHQX.js.map} +0 -0
- /package/dist/{tokens-CE46OTMD.js.map → tokens-T6SIVUT5.js.map} +0 -0
- /package/dist/{viewer-DLLJIMCK.js.map → viewer-7ZEAFBVN.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/scan-generate.ts"],"sourcesContent":["/**\n * fragments init --scan - Generate fragment files from any TypeScript component library\n *\n * Phase 2 of the Universal GenUI strategy. Scans a component directory and\n * generates .fragment.tsx files with TODO markers for uncertain fields.\n *\n * Combines:\n * - Component discovery (core/discovery.ts)\n * - Props extraction (service/enhance/props-extractor.ts)\n * - JSDoc extraction (new: TypeScript AST)\n * - Compound component detection (new: Object.assign pattern)\n * - Confidence scoring with TODO markers\n */\n\nimport { readFile, writeFile, access, mkdir } from \"node:fs/promises\";\nimport { resolve, basename, dirname, relative, join } from \"node:path\";\nimport * as ts from \"typescript\";\nimport pc from \"picocolors\";\nimport { BRAND } from \"../core/index.js\";\nimport {\n discoverAllComponents,\n type DiscoveredComponent,\n} from \"../core/node.js\";\nimport {\n extractPropsFromFile,\n convertToFragmentProps,\n type PropsExtractionResult,\n type ExtractedProp,\n} from \"../service/index.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface ScanGenerateOptions {\n /** Path to scan for components */\n scanPath: string;\n /** Output directory (default: colocated with source) */\n outputDir?: string;\n /** Overwrite existing fragment files */\n force?: boolean;\n /** Custom component file patterns */\n patterns?: string[];\n /** Skip Storybook story extraction */\n skipStorybook?: boolean;\n /** Verbose logging */\n verbose?: boolean;\n}\n\nexport interface ScanGenerateResult {\n success: boolean;\n generated: Array<{\n name: string;\n path: string;\n confidence: number;\n todoCount: number;\n }>;\n skipped: Array<{ name: string; reason: string }>;\n errors: Array<{ name: string; error: string }>;\n averageConfidence: number;\n}\n\ninterface ComponentData {\n component: DiscoveredComponent;\n props: PropsExtractionResult | null;\n jsDoc: string | null;\n compoundChildren: string[];\n storyVariants: StoryVariant[];\n}\n\ninterface StoryVariant {\n name: string;\n args: Record<string, unknown>;\n}\n\ninterface FieldConfidence {\n score: number;\n todoFields: string[];\n}\n\n// ---------------------------------------------------------------------------\n// Main orchestrator\n// ---------------------------------------------------------------------------\n\nexport async function scanGenerate(\n options: ScanGenerateOptions\n): Promise<ScanGenerateResult> {\n const scanPath = resolve(options.scanPath);\n const generated: ScanGenerateResult[\"generated\"] = [];\n const skipped: ScanGenerateResult[\"skipped\"] = [];\n const errors: ScanGenerateResult[\"errors\"] = [];\n\n console.log(pc.cyan(`\\n${BRAND.name} Scan → Generate\\n`));\n console.log(pc.dim(`Scanning: ${scanPath}\\n`));\n\n // Phase 1: Discover components\n console.log(pc.dim(\"Phase 1: Discovering components...\"));\n\n // Use broader patterns than default — the scan path IS the component root,\n // not a project root with src/components/ inside it\n const defaultScanPatterns = [\n \"**/*.tsx\",\n \"**/*.ts\",\n ];\n\n const components = await discoverAllComponents(scanPath, {\n patterns: options.patterns || defaultScanPatterns,\n exclude: [\n \"**/*.test.*\",\n \"**/*.spec.*\",\n \"**/*.stories.*\",\n \"**/*.fragment.*\",\n \"**/*.d.ts\",\n \"**/__tests__/**\",\n \"**/__mocks__/**\",\n \"**/node_modules/**\",\n \"**/dist/**\",\n ],\n });\n\n if (components.length === 0) {\n console.log(\n pc.yellow(\"No components found. Check the path or file patterns.\")\n );\n return {\n success: false,\n generated: [],\n skipped: [],\n errors: [{ name: \"*\", error: \"No components found\" }],\n averageConfidence: 0,\n };\n }\n\n console.log(pc.green(` Found ${components.length} components`));\n\n // Phase 2: Extract data for each component\n console.log(pc.dim(\"\\nPhase 2: Extracting component metadata...\"));\n\n const componentDataList: ComponentData[] = [];\n\n for (const comp of components) {\n let propsResult: PropsExtractionResult | null = null;\n try {\n propsResult = await extractPropsFromFile(comp.sourcePath, {\n propsTypeName: `${comp.name}Props`,\n });\n } catch {\n // Props extraction can fail for complex types — continue gracefully\n }\n\n let jsDoc: string | null = null;\n try {\n jsDoc = await extractComponentJSDoc(comp.sourcePath, comp.name);\n } catch {\n // JSDoc extraction failure is non-fatal\n }\n\n let compoundChildren: string[] = [];\n try {\n compoundChildren = await detectCompoundComponents(comp.sourcePath);\n } catch {\n // Compound detection failure is non-fatal\n }\n\n let storyVariants: StoryVariant[] = [];\n if (!options.skipStorybook && comp.storyPath) {\n try {\n storyVariants = await extractStoryVariantsFromFile(comp.storyPath);\n } catch {\n // Story extraction failure is non-fatal\n }\n }\n\n componentDataList.push({\n component: comp,\n props: propsResult,\n jsDoc,\n compoundChildren,\n storyVariants,\n });\n }\n\n const propsExtracted = componentDataList.filter(\n (d) => d.props?.success && d.props.props.length > 0\n ).length;\n console.log(pc.green(` Extracted props for ${propsExtracted} components`));\n\n // Phase 3: Generate fragment files\n console.log(pc.dim(\"\\nPhase 3: Generating fragment files...\"));\n\n for (const data of componentDataList) {\n const comp = data.component;\n const componentDir = dirname(comp.sourcePath);\n const componentBaseName = basename(comp.sourcePath, \".tsx\");\n\n // Determine output path\n let fragmentDir: string;\n if (options.outputDir) {\n fragmentDir = resolve(options.outputDir, comp.name);\n await mkdir(fragmentDir, { recursive: true });\n } else {\n fragmentDir = componentDir;\n }\n\n const fragmentPath = join(\n fragmentDir,\n `${componentBaseName}${BRAND.fileExtension}`\n );\n\n // Check if fragment already exists\n let fragmentExists = false;\n try {\n await access(fragmentPath);\n fragmentExists = true;\n } catch {\n // Doesn't exist\n }\n\n if (fragmentExists && !options.force) {\n skipped.push({ name: comp.name, reason: \"Fragment already exists\" });\n if (options.verbose) {\n console.log(pc.dim(` Skipping ${comp.name} (fragment exists)`));\n }\n continue;\n }\n\n try {\n // Calculate confidence\n const confidence = calculateFieldConfidence(data);\n\n // Compute import path\n const importPath = computeImportPath(\n fragmentDir,\n comp.sourcePath,\n componentBaseName\n );\n\n // Generate the fragment file\n const content = generateFragmentWithTodos(\n comp.name,\n importPath,\n data,\n confidence\n );\n\n await writeFile(fragmentPath, content, \"utf-8\");\n\n const relPath = relative(process.cwd(), fragmentPath);\n generated.push({\n name: comp.name,\n path: relPath,\n confidence: confidence.score,\n todoCount: confidence.todoFields.length,\n });\n\n const confColor =\n confidence.score >= 70\n ? pc.green\n : confidence.score >= 40\n ? pc.yellow\n : pc.red;\n console.log(\n pc.green(` ✓ ${comp.name}`) +\n pc.dim(` (confidence: `) +\n confColor(`${confidence.score}`) +\n pc.dim(`, TODOs: ${confidence.todoFields.length})`)\n );\n } catch (e) {\n errors.push({\n name: comp.name,\n error: e instanceof Error ? e.message : String(e),\n });\n console.log(pc.red(` ✗ ${comp.name}: ${e instanceof Error ? e.message : String(e)}`));\n }\n }\n\n // Summary\n const avgConfidence =\n generated.length > 0\n ? Math.round(\n generated.reduce((sum, g) => sum + g.confidence, 0) /\n generated.length\n )\n : 0;\n\n console.log(pc.dim(\"\\n────────────────────────────────────────\"));\n console.log(pc.green(`\\n✓ Generated ${generated.length} fragment file(s)`));\n\n if (skipped.length > 0) {\n console.log(\n pc.dim(` Skipped ${skipped.length} (use --force to overwrite)`)\n );\n }\n if (errors.length > 0) {\n console.log(pc.yellow(` ${errors.length} error(s)`));\n }\n\n console.log(pc.dim(` Average confidence: ${avgConfidence}/100`));\n\n const totalTodos = generated.reduce((sum, g) => sum + g.todoCount, 0);\n if (totalTodos > 0) {\n console.log(\n pc.dim(` Total TODOs: ${totalTodos}`) +\n pc.dim(` — search for \"TODO:\" in generated files`)\n );\n }\n\n console.log();\n\n return {\n success: errors.length === 0,\n generated,\n skipped,\n errors,\n averageConfidence: avgConfidence,\n };\n}\n\n// ---------------------------------------------------------------------------\n// JSDoc Extraction\n// ---------------------------------------------------------------------------\n\n/**\n * Extract the JSDoc description from a component's exported declaration.\n * Uses TypeScript AST — no program needed, just source file parsing.\n */\nexport async function extractComponentJSDoc(\n filePath: string,\n componentName?: string\n): Promise<string | null> {\n const content = await readFile(filePath, \"utf-8\");\n return extractComponentJSDocFromSource(content, filePath, componentName);\n}\n\nexport function extractComponentJSDocFromSource(\n source: string,\n filePath: string,\n componentName?: string\n): string | null {\n const sourceFile = ts.createSourceFile(\n filePath,\n source,\n ts.ScriptTarget.ESNext,\n true,\n filePath.endsWith(\".tsx\") ? ts.ScriptKind.TSX : ts.ScriptKind.TS\n );\n\n const targetName =\n componentName || basename(filePath).replace(/\\.(tsx?|jsx?)$/, \"\");\n\n let componentDoc: string | null = null;\n let propsInterfaceDoc: string | null = null;\n\n for (const statement of sourceFile.statements) {\n // export function ComponentName(...)\n if (\n ts.isFunctionDeclaration(statement) &&\n statement.name?.text === targetName &&\n hasExportModifier(statement)\n ) {\n const doc = getLeadingJSDoc(statement, sourceFile);\n if (doc) componentDoc = doc;\n }\n\n // export const ComponentName = ...\n if (\n ts.isVariableStatement(statement) &&\n hasExportModifier(statement)\n ) {\n for (const decl of statement.declarationList.declarations) {\n if (ts.isIdentifier(decl.name) && decl.name.text === targetName) {\n const doc = getLeadingJSDoc(statement, sourceFile);\n if (doc) componentDoc = doc;\n }\n }\n }\n\n // export default function ...\n if (\n ts.isFunctionDeclaration(statement) &&\n hasDefaultExportModifier(statement)\n ) {\n const doc = getLeadingJSDoc(statement, sourceFile);\n if (doc) componentDoc = doc;\n }\n\n // Fallback: JSDoc on the {Name}Props interface\n if (\n ts.isInterfaceDeclaration(statement) &&\n (statement.name.text === `${targetName}Props` ||\n statement.name.text === `${targetName}Properties`)\n ) {\n const doc = getLeadingJSDoc(statement, sourceFile);\n if (doc) propsInterfaceDoc = doc;\n }\n }\n\n // Prefer component-level JSDoc; fall back to props interface JSDoc\n return componentDoc || propsInterfaceDoc;\n}\n\nfunction hasExportModifier(node: ts.Node): boolean {\n const modifiers = ts.canHaveModifiers(node)\n ? ts.getModifiers(node)\n : undefined;\n return modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false;\n}\n\nfunction hasDefaultExportModifier(node: ts.Node): boolean {\n const modifiers = ts.canHaveModifiers(node)\n ? ts.getModifiers(node)\n : undefined;\n if (!modifiers) return false;\n return (\n modifiers.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) &&\n modifiers.some((m) => m.kind === ts.SyntaxKind.DefaultKeyword)\n );\n}\n\nfunction getLeadingJSDoc(\n node: ts.Node,\n sourceFile: ts.SourceFile\n): string | null {\n const fullText = sourceFile.getFullText();\n const nodeStart = node.getFullStart();\n const nodePos = node.getStart(sourceFile);\n const leadingText = fullText.slice(nodeStart, nodePos);\n\n const jsDocMatch = leadingText.match(/\\/\\*\\*([\\s\\S]*?)\\*\\//);\n if (!jsDocMatch) return null;\n\n // Parse JSDoc content — extract description lines, skip tags\n const lines = jsDocMatch[1].split(\"\\n\");\n const descriptionLines: string[] = [];\n\n for (const line of lines) {\n const trimmed = line.replace(/^\\s*\\*?\\s?/, \"\").trim();\n if (trimmed.startsWith(\"@\")) break; // Stop at first tag\n if (trimmed) descriptionLines.push(trimmed);\n }\n\n return descriptionLines.length > 0\n ? descriptionLines.join(\" \")\n : null;\n}\n\n// ---------------------------------------------------------------------------\n// Compound Component Detection\n// ---------------------------------------------------------------------------\n\n/**\n * Detect compound component patterns (Object.assign) in a source file.\n * Returns the names of sub-components found.\n */\nexport async function detectCompoundComponents(\n filePath: string\n): Promise<string[]> {\n const content = await readFile(filePath, \"utf-8\");\n return detectCompoundComponentsFromSource(content, filePath);\n}\n\nexport function detectCompoundComponentsFromSource(\n source: string,\n filePath: string\n): string[] {\n const sourceFile = ts.createSourceFile(\n filePath,\n source,\n ts.ScriptTarget.ESNext,\n true,\n filePath.endsWith(\".tsx\") ? ts.ScriptKind.TSX : ts.ScriptKind.TS\n );\n\n const subComponents: string[] = [];\n\n function visit(node: ts.Node) {\n // Look for Object.assign(Root, { Sub1, Sub2 }) or Object.assign(Root, { Sub1: SubComponent })\n if (\n ts.isCallExpression(node) &&\n ts.isPropertyAccessExpression(node.expression) &&\n ts.isIdentifier(node.expression.expression) &&\n node.expression.expression.text === \"Object\" &&\n node.expression.name.text === \"assign\" &&\n node.arguments.length >= 2\n ) {\n const secondArg = node.arguments[1];\n if (ts.isObjectLiteralExpression(secondArg)) {\n for (const prop of secondArg.properties) {\n if (ts.isShorthandPropertyAssignment(prop)) {\n subComponents.push(prop.name.text);\n } else if (\n ts.isPropertyAssignment(prop) &&\n ts.isIdentifier(prop.name)\n ) {\n subComponents.push(prop.name.text);\n }\n }\n }\n }\n\n ts.forEachChild(node, visit);\n }\n\n ts.forEachChild(sourceFile, visit);\n return subComponents;\n}\n\n// ---------------------------------------------------------------------------\n// Story Variant Extraction (lightweight, from generate.ts patterns)\n// ---------------------------------------------------------------------------\n\nasync function extractStoryVariantsFromFile(\n storyPath: string\n): Promise<StoryVariant[]> {\n const content = await readFile(storyPath, \"utf-8\");\n const variants: StoryVariant[] = [];\n\n const exportMatches = content.matchAll(\n /export\\s+const\\s+([A-Z][a-zA-Z0-9]*)\\s*[=:]/g\n );\n\n for (const match of exportMatches) {\n const name = match[1];\n if (name === \"default\" || name.endsWith(\"Args\") || name.endsWith(\"Meta\")) {\n continue;\n }\n\n const args = extractStoryArgs(content, name);\n variants.push({ name, args });\n }\n\n return variants;\n}\n\nfunction extractStoryArgs(\n content: string,\n storyName: string\n): Record<string, unknown> {\n const storyPattern = new RegExp(\n `export\\\\s+const\\\\s+${storyName}[^=]*=\\\\s*\\\\{([\\\\s\\\\S]*?)\\\\n\\\\};`\n );\n const storyMatch = content.match(storyPattern);\n if (!storyMatch) return {};\n\n const storyBody = storyMatch[1];\n const argsStart = storyBody.indexOf(\"args:\");\n if (argsStart === -1) return {};\n\n const braceStart = storyBody.indexOf(\"{\", argsStart);\n if (braceStart === -1) return {};\n\n let depth = 0;\n let braceEnd = -1;\n for (let i = braceStart; i < storyBody.length; i++) {\n if (storyBody[i] === \"{\") depth++;\n else if (storyBody[i] === \"}\") {\n depth--;\n if (depth === 0) {\n braceEnd = i;\n break;\n }\n }\n }\n if (braceEnd === -1) return {};\n\n const argsBlock = storyBody.slice(braceStart + 1, braceEnd).trim();\n return parseArgsBlock(argsBlock);\n}\n\nfunction parseArgsBlock(argsBlock: string): Record<string, unknown> {\n const args: Record<string, unknown> = {};\n const pairPattern =\n /(\\w+)\\s*:\\s*(?:['\"]([^'\"]*?)['\"]|(true|false)|(\\d+(?:\\.\\d+)?))/g;\n let pairMatch: RegExpExecArray | null;\n\n while ((pairMatch = pairPattern.exec(argsBlock)) !== null) {\n const key = pairMatch[1];\n if (pairMatch[2] !== undefined) {\n args[key] = pairMatch[2];\n } else if (pairMatch[3] !== undefined) {\n args[key] = pairMatch[3] === \"true\";\n } else if (pairMatch[4] !== undefined) {\n args[key] = Number(pairMatch[4]);\n }\n }\n\n return args;\n}\n\n// ---------------------------------------------------------------------------\n// Confidence Scoring\n// ---------------------------------------------------------------------------\n\nexport function calculateFieldConfidence(data: ComponentData): FieldConfidence {\n let score = 0;\n const todoFields: string[] = [];\n\n // Props extracted: +30\n const hasProps =\n data.props?.success && data.props.props.length > 0;\n if (hasProps) {\n score += 30;\n }\n\n // JSDoc description found: +15\n if (data.jsDoc) {\n score += 15;\n } else {\n todoFields.push(\"meta.description\");\n }\n\n // Category inferred from path/name (not fallback): +10\n const category = inferCategory(\n data.component.name,\n data.props?.success ? data.props.props : []\n );\n if (category !== \"Components\") {\n score += 10;\n } else {\n todoFields.push(\"meta.category\");\n }\n\n // Story variants found: +25\n if (data.storyVariants.length > 0) {\n score += 25;\n }\n\n // All prop types resolved (no \"custom\"): +10\n if (hasProps) {\n const allResolved = data.props!.props.every(\n (p) => p.propType.type !== \"custom\"\n );\n if (allResolved) {\n score += 10;\n }\n }\n\n // Compound component detected: +5\n if (data.compoundChildren.length > 0) {\n score += 5;\n }\n\n // Has default values: +5\n if (hasProps) {\n const hasDefaults = data.props!.props.some(\n (p) => p.defaultValue !== undefined\n );\n if (hasDefaults) {\n score += 5;\n }\n }\n\n // usage.when and usage.whenNot always get TODOs (human knowledge needed)\n todoFields.push(\"usage.when\");\n todoFields.push(\"usage.whenNot\");\n\n return { score: Math.min(score, 100), todoFields };\n}\n\n// ---------------------------------------------------------------------------\n// Category / Status / Description inference (adapted from generate.ts + scan.ts)\n// ---------------------------------------------------------------------------\n\nconst CATEGORY_PATTERNS: Record<string, string[]> = {\n Actions: [\"button\", \"action\", \"cta\", \"fab\", \"floatingaction\"],\n Forms: [\n \"form\", \"input\", \"select\", \"checkbox\", \"radio\", \"textarea\", \"field\",\n \"textfield\", \"datepicker\", \"switch\", \"slider\", \"segmented\",\n ],\n Layout: [\n \"layout\", \"container\", \"grid\", \"flex\", \"stack\", \"box\", \"divider\", \"spacer\",\n \"sidebar\",\n ],\n Navigation: [\n \"nav\", \"menu\", \"breadcrumb\", \"tab\", \"link\", \"pagination\", \"stepper\",\n \"topbar\",\n ],\n Feedback: [\n \"alert\", \"toast\", \"notification\", \"message\", \"badge\", \"indicator\",\n \"progress\", \"spinner\", \"loading\", \"loader\", \"lozenge\", \"chip\",\n ],\n \"Data Display\": [\n \"table\", \"list\", \"card\", \"avatar\", \"stat\", \"timeline\", \"tree\", \"datalist\",\n \"datacard\",\n ],\n Overlays: [\n \"modal\", \"dialog\", \"drawer\", \"popover\", \"tooltip\", \"dropdown\",\n \"slidepanel\",\n ],\n Typography: [\"text\", \"heading\", \"title\", \"label\", \"paragraph\"],\n Media: [\"image\", \"video\", \"icon\", \"carousel\"],\n};\n\nfunction inferCategory(\n componentName: string,\n props: ExtractedProp[]\n): string {\n const lower = componentName.toLowerCase();\n\n for (const [category, patterns] of Object.entries(CATEGORY_PATTERNS)) {\n for (const pattern of patterns) {\n if (lower.includes(pattern)) {\n return category;\n }\n }\n }\n\n // Prop-based fallbacks\n const propNames = new Set(props.map((p) => p.name));\n if (propNames.has(\"onClick\") || propNames.has(\"onPress\")) return \"Actions\";\n if (propNames.has(\"value\") || propNames.has(\"defaultValue\")) return \"Forms\";\n if (propNames.has(\"children\")) return \"Layout\";\n\n return \"Components\";\n}\n\nfunction inferStatus(\n filePath: string\n): \"draft\" | \"experimental\" | \"beta\" | \"stable\" | \"deprecated\" {\n const lowerPath = filePath.toLowerCase();\n if (lowerPath.includes(\"/experimental/\") || lowerPath.includes(\"/labs/\"))\n return \"experimental\";\n if (lowerPath.includes(\"/beta/\")) return \"beta\";\n if (lowerPath.includes(\"/deprecated/\") || lowerPath.includes(\"/legacy/\"))\n return \"deprecated\";\n if (lowerPath.includes(\"/draft/\") || lowerPath.includes(\"/wip/\"))\n return \"draft\";\n return \"stable\";\n}\n\nfunction inferDescription(\n componentName: string,\n props: ExtractedProp[]\n): string {\n const words = componentName\n .replace(/([A-Z])/g, \" $1\")\n .trim()\n .toLowerCase();\n\n const propNames = new Set(props.map((p) => p.name));\n const hasOnClick =\n propNames.has(\"onClick\") || propNames.has(\"onPress\");\n const hasValue =\n propNames.has(\"value\") || propNames.has(\"defaultValue\");\n const hasChildren = propNames.has(\"children\");\n\n if (hasOnClick && !hasValue)\n return `Interactive ${words} element for triggering actions`;\n if (hasValue) return `Form ${words} for user input`;\n if (hasChildren) return `Container ${words} for grouping content`;\n return `${words.charAt(0).toUpperCase() + words.slice(1)} component`;\n}\n\nfunction inferAccessibility(props: ExtractedProp[]): {\n role?: string;\n requirements?: string[];\n} {\n const propNames = new Set(props.map((p) => p.name));\n const accessibility: { role?: string; requirements?: string[] } = {};\n\n const hasOnClick =\n propNames.has(\"onClick\") || propNames.has(\"onPress\");\n const hasAriaLabel =\n propNames.has(\"ariaLabel\") || propNames.has(\"aria-label\");\n const hasDisabled = propNames.has(\"disabled\");\n const hasHref = propNames.has(\"href\");\n\n if (hasOnClick && !hasHref) accessibility.role = \"button\";\n else if (hasHref) accessibility.role = \"link\";\n\n const requirements: string[] = [];\n if (hasOnClick && !hasAriaLabel)\n requirements.push(\"Should have visible text or aria-label\");\n if (hasDisabled)\n requirements.push(\n \"Disabled state should be conveyed to assistive technology\"\n );\n if (requirements.length > 0) accessibility.requirements = requirements;\n\n return accessibility;\n}\n\n// ---------------------------------------------------------------------------\n// Import path computation\n// ---------------------------------------------------------------------------\n\nfunction computeImportPath(\n fragmentDir: string,\n sourcePath: string,\n componentBaseName: string\n): string {\n const sourceDir = dirname(sourcePath);\n\n // Colocated: fragment sits next to the source file\n if (fragmentDir === sourceDir) {\n // If source is index.tsx, import from '.'\n if (componentBaseName === \"index\") {\n return \".\";\n }\n return `./${componentBaseName}`;\n }\n\n // Different directory: compute relative path\n let rel = relative(fragmentDir, sourceDir);\n if (!rel.startsWith(\".\")) rel = `./${rel}`;\n if (componentBaseName !== \"index\") {\n rel = `${rel}/${componentBaseName}`;\n }\n return rel;\n}\n\n// ---------------------------------------------------------------------------\n// Fragment file generation with TODO markers\n// ---------------------------------------------------------------------------\n\nfunction escapeQuotes(str: string): string {\n return str.replace(/'/g, \"\\\\'\");\n}\n\nfunction generateFragmentWithTodos(\n componentName: string,\n importPath: string,\n data: ComponentData,\n confidence: FieldConfidence\n): string {\n const props = data.props?.success ? data.props.props : [];\n const isDefaultExport = data.props?.success\n ? !data.props.propsTypeName // heuristic: if no explicit props type, might be default export\n : false;\n // For scan-generated files we always use named import\n const description = data.jsDoc || inferDescription(componentName, props);\n const descriptionTodo = data.jsDoc ? \"\" : \" // TODO: Review description\";\n const category = inferCategory(componentName, props);\n const categoryTodo = category === \"Components\" ? \" // TODO: Set correct category\" : \"\";\n const status = inferStatus(data.component.sourcePath);\n const accessibility = inferAccessibility(props);\n\n // Build props block\n const propsBlock = buildPropsBlock(props);\n\n // Build accessibility block\n const accessibilityBlock = buildAccessibilityBlock(accessibility);\n\n // Build variants\n const variantsBlock = buildVariantsBlock(\n componentName,\n data.storyVariants\n );\n\n // Build compound children comment\n const compoundComment =\n data.compoundChildren.length > 0\n ? `\\n // Compound sub-components detected: ${data.compoundChildren.join(\", \")}`\n : \"\";\n\n return `// Auto-generated by fragments init --scan | Confidence: ${confidence.score}/100\n// ${confidence.todoFields.length} TODO(s) — search for \"TODO:\" and fill in human knowledge\nimport React from 'react';\nimport { defineFragment } from '@fragments-sdk/core';\nimport { ${componentName} } from '${importPath}';\n\nexport default defineFragment({\n component: ${componentName},\n${compoundComment}\n meta: {\n name: '${escapeQuotes(componentName)}',\n description: '${escapeQuotes(description)}',${descriptionTodo}\n category: '${escapeQuotes(category)}',${categoryTodo}\n status: '${status}',\n },\n\n usage: {\n when: [\n // TODO: Describe when to use ${componentName}\n ],\n whenNot: [\n // TODO: Describe when NOT to use ${componentName}\n ],\n },\n\n props: ${propsBlock},${accessibilityBlock}\n\n variants: [\n${variantsBlock}\n ],\n});\n`;\n}\n\nfunction buildPropsBlock(props: ExtractedProp[]): string {\n if (props.length === 0) return \"{}\";\n\n const lines = props.map((prop) => {\n const type = prop.propType.type;\n const parts: string[] = [` type: '${type}'`];\n\n if (prop.description) {\n parts.push(\n ` description: '${escapeQuotes(prop.description.replace(/\\n/g, \" \"))}'`\n );\n }\n\n parts.push(` required: ${prop.required}`);\n\n if (prop.defaultValue !== undefined) {\n parts.push(` default: ${JSON.stringify(prop.defaultValue)}`);\n }\n\n if (prop.enumValues && prop.enumValues.length > 0) {\n parts.push(` values: ${JSON.stringify(prop.enumValues)}`);\n }\n\n const todoComment =\n type === \"custom\" ? \" // TODO: Review type\" : \"\";\n\n return ` ${prop.name}: {\\n${parts.join(\",\\n\")},\\n },${todoComment}`;\n });\n\n return `{\\n${lines.join(\"\\n\")}\\n }`;\n}\n\nfunction buildAccessibilityBlock(accessibility: {\n role?: string;\n requirements?: string[];\n}): string {\n if (\n !accessibility.role &&\n (!accessibility.requirements || accessibility.requirements.length === 0)\n ) {\n return \"\";\n }\n\n const parts: string[] = [];\n if (accessibility.role) {\n parts.push(` role: '${accessibility.role}'`);\n }\n if (accessibility.requirements && accessibility.requirements.length > 0) {\n const reqs = accessibility.requirements\n .map((r) => `'${escapeQuotes(r)}'`)\n .join(\", \");\n parts.push(` requirements: [${reqs}]`);\n }\n\n return `\\n\\n accessibility: {\\n${parts.join(\",\\n\")},\\n },`;\n}\n\nfunction buildVariantsBlock(\n componentName: string,\n storyVariants: StoryVariant[]\n): string {\n const entries: string[] = [];\n\n // Always include a Default variant\n const hasDefault = storyVariants.some((v) => v.name === \"Default\");\n if (!hasDefault) {\n entries.push(formatVariantEntry(componentName, \"Default\", `Default ${componentName}`, {}));\n }\n\n for (const variant of storyVariants) {\n const description = variant.name.replace(/([A-Z])/g, \" $1\").trim();\n entries.push(\n formatVariantEntry(\n componentName,\n variant.name,\n `${description} ${componentName}`,\n variant.args\n )\n );\n }\n\n return entries.join(\"\\n\");\n}\n\nfunction formatVariantEntry(\n componentName: string,\n name: string,\n description: string,\n args: Record<string, unknown>\n): string {\n const jsxCode = buildJsxString(componentName, args);\n return ` {\n name: '${escapeQuotes(name)}',\n description: '${escapeQuotes(description)}',\n code: \\`${jsxCode}\\`,\n render: () => ${jsxCode},\n },`;\n}\n\nfunction buildJsxString(\n componentName: string,\n args: Record<string, unknown>\n): string {\n const { children, ...restArgs } = args;\n const propParts: string[] = [];\n\n for (const [key, value] of Object.entries(restArgs)) {\n if (typeof value === \"string\") {\n propParts.push(`${key}=\"${escapeQuotes(value)}\"`);\n } else if (typeof value === \"boolean\") {\n propParts.push(value ? key : `${key}={false}`);\n } else if (typeof value === \"number\") {\n propParts.push(`${key}={${value}}`);\n }\n }\n\n const propsStr = propParts.length > 0 ? \" \" + propParts.join(\" \") : \"\";\n\n if (typeof children === \"string\") {\n return `<${componentName}${propsStr}>${children}</${componentName}>`;\n }\n\n return `<${componentName}${propsStr} />`;\n}\n"],"mappings":"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","names":[]}
|
package/src/ai.ts
DELETED
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
import type { CompiledFragment, CompiledFragmentsFile } from "./core/index.js";
|
|
2
|
-
import { generateContext } from "./core/index.js";
|
|
3
|
-
|
|
4
|
-
export interface AiSuggestOptions {
|
|
5
|
-
/** The user's prompt describing what they want to build */
|
|
6
|
-
prompt: string;
|
|
7
|
-
/** Optional additional context */
|
|
8
|
-
context?: string;
|
|
9
|
-
/** Compiled fragments to use */
|
|
10
|
-
fragments: CompiledFragment[];
|
|
11
|
-
/** Whether to stream output */
|
|
12
|
-
stream?: boolean;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface AiSuggestResult {
|
|
16
|
-
success: boolean;
|
|
17
|
-
response?: string;
|
|
18
|
-
error?: string;
|
|
19
|
-
tokensUsed?: number;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Call AI API to get component suggestions based on design system context.
|
|
24
|
-
* Supports both Anthropic and OpenAI APIs via environment variables.
|
|
25
|
-
*/
|
|
26
|
-
export async function aiSuggest(options: AiSuggestOptions): Promise<AiSuggestResult> {
|
|
27
|
-
const { prompt, context, fragments, stream = true } = options;
|
|
28
|
-
|
|
29
|
-
// Check for API keys
|
|
30
|
-
const anthropicKey = process.env.ANTHROPIC_API_KEY;
|
|
31
|
-
const openaiKey = process.env.OPENAI_API_KEY;
|
|
32
|
-
|
|
33
|
-
if (!anthropicKey && !openaiKey) {
|
|
34
|
-
return {
|
|
35
|
-
success: false,
|
|
36
|
-
error: "No API key found. Set ANTHROPIC_API_KEY or OPENAI_API_KEY environment variable.",
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Generate design system context
|
|
41
|
-
const { content: systemContext } = generateContext(fragments, {
|
|
42
|
-
format: "markdown",
|
|
43
|
-
compact: false,
|
|
44
|
-
include: {
|
|
45
|
-
props: true,
|
|
46
|
-
variants: true,
|
|
47
|
-
usage: true,
|
|
48
|
-
code: true,
|
|
49
|
-
},
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
// Build the system prompt
|
|
53
|
-
const systemPrompt = `You are a UI developer assistant helping to build interfaces using a specific design system.
|
|
54
|
-
|
|
55
|
-
You have access to the following design system components:
|
|
56
|
-
|
|
57
|
-
${systemContext}
|
|
58
|
-
|
|
59
|
-
When suggesting components:
|
|
60
|
-
1. ONLY use components from this design system - never suggest generic HTML or components not listed above
|
|
61
|
-
2. Explain WHY each component is appropriate for the use case
|
|
62
|
-
3. Show complete, ready-to-use code examples with correct props
|
|
63
|
-
4. Follow the "when to use" and "when NOT to use" guidelines for each component
|
|
64
|
-
5. Consider accessibility and best practices
|
|
65
|
-
6. If multiple approaches are valid, recommend the simplest one first
|
|
66
|
-
|
|
67
|
-
Format your response as:
|
|
68
|
-
1. A brief explanation of the approach
|
|
69
|
-
2. Code example(s) with comments
|
|
70
|
-
3. Any important notes about props or customization`;
|
|
71
|
-
|
|
72
|
-
// Build the user prompt
|
|
73
|
-
const userPrompt = context
|
|
74
|
-
? `${prompt}\n\nAdditional context: ${context}`
|
|
75
|
-
: prompt;
|
|
76
|
-
|
|
77
|
-
// Prefer Anthropic, fallback to OpenAI
|
|
78
|
-
if (anthropicKey) {
|
|
79
|
-
return callAnthropic(anthropicKey, systemPrompt, userPrompt, stream);
|
|
80
|
-
} else {
|
|
81
|
-
return callOpenAI(openaiKey!, systemPrompt, userPrompt, stream);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Call Anthropic's Claude API
|
|
87
|
-
*/
|
|
88
|
-
async function callAnthropic(
|
|
89
|
-
apiKey: string,
|
|
90
|
-
systemPrompt: string,
|
|
91
|
-
userPrompt: string,
|
|
92
|
-
stream: boolean
|
|
93
|
-
): Promise<AiSuggestResult> {
|
|
94
|
-
try {
|
|
95
|
-
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
96
|
-
method: "POST",
|
|
97
|
-
headers: {
|
|
98
|
-
"Content-Type": "application/json",
|
|
99
|
-
"x-api-key": apiKey,
|
|
100
|
-
"anthropic-version": "2023-06-01",
|
|
101
|
-
},
|
|
102
|
-
body: JSON.stringify({
|
|
103
|
-
model: "claude-sonnet-4-20250514",
|
|
104
|
-
max_tokens: 4096,
|
|
105
|
-
system: systemPrompt,
|
|
106
|
-
messages: [
|
|
107
|
-
{
|
|
108
|
-
role: "user",
|
|
109
|
-
content: userPrompt,
|
|
110
|
-
},
|
|
111
|
-
],
|
|
112
|
-
stream: stream,
|
|
113
|
-
}),
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
if (!response.ok) {
|
|
117
|
-
const error = await response.text();
|
|
118
|
-
return {
|
|
119
|
-
success: false,
|
|
120
|
-
error: `Anthropic API error: ${response.status} - ${error}`,
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (stream && response.body) {
|
|
125
|
-
// Stream the response
|
|
126
|
-
const reader = response.body.getReader();
|
|
127
|
-
const decoder = new TextDecoder();
|
|
128
|
-
let fullResponse = "";
|
|
129
|
-
|
|
130
|
-
while (true) {
|
|
131
|
-
const { done, value } = await reader.read();
|
|
132
|
-
if (done) break;
|
|
133
|
-
|
|
134
|
-
const chunk = decoder.decode(value, { stream: true });
|
|
135
|
-
const lines = chunk.split("\n");
|
|
136
|
-
|
|
137
|
-
for (const line of lines) {
|
|
138
|
-
if (line.startsWith("data: ")) {
|
|
139
|
-
const data = line.slice(6);
|
|
140
|
-
if (data === "[DONE]") continue;
|
|
141
|
-
|
|
142
|
-
try {
|
|
143
|
-
const parsed = JSON.parse(data);
|
|
144
|
-
if (parsed.type === "content_block_delta" && parsed.delta?.text) {
|
|
145
|
-
process.stdout.write(parsed.delta.text);
|
|
146
|
-
fullResponse += parsed.delta.text;
|
|
147
|
-
}
|
|
148
|
-
} catch {
|
|
149
|
-
// Skip malformed JSON
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
console.log(); // Final newline
|
|
156
|
-
return {
|
|
157
|
-
success: true,
|
|
158
|
-
response: fullResponse,
|
|
159
|
-
};
|
|
160
|
-
} else {
|
|
161
|
-
const data = await response.json();
|
|
162
|
-
const text = data.content?.[0]?.text ?? "";
|
|
163
|
-
|
|
164
|
-
return {
|
|
165
|
-
success: true,
|
|
166
|
-
response: text,
|
|
167
|
-
tokensUsed: data.usage?.input_tokens + data.usage?.output_tokens,
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
} catch (error) {
|
|
171
|
-
return {
|
|
172
|
-
success: false,
|
|
173
|
-
error: `Failed to call Anthropic API: ${error instanceof Error ? error.message : error}`,
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Call OpenAI's API
|
|
180
|
-
*/
|
|
181
|
-
async function callOpenAI(
|
|
182
|
-
apiKey: string,
|
|
183
|
-
systemPrompt: string,
|
|
184
|
-
userPrompt: string,
|
|
185
|
-
stream: boolean
|
|
186
|
-
): Promise<AiSuggestResult> {
|
|
187
|
-
try {
|
|
188
|
-
const response = await fetch("https://api.openai.com/v1/chat/completions", {
|
|
189
|
-
method: "POST",
|
|
190
|
-
headers: {
|
|
191
|
-
"Content-Type": "application/json",
|
|
192
|
-
Authorization: `Bearer ${apiKey}`,
|
|
193
|
-
},
|
|
194
|
-
body: JSON.stringify({
|
|
195
|
-
model: "gpt-4o",
|
|
196
|
-
messages: [
|
|
197
|
-
{ role: "system", content: systemPrompt },
|
|
198
|
-
{ role: "user", content: userPrompt },
|
|
199
|
-
],
|
|
200
|
-
max_tokens: 4096,
|
|
201
|
-
stream: stream,
|
|
202
|
-
}),
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
if (!response.ok) {
|
|
206
|
-
const error = await response.text();
|
|
207
|
-
return {
|
|
208
|
-
success: false,
|
|
209
|
-
error: `OpenAI API error: ${response.status} - ${error}`,
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
if (stream && response.body) {
|
|
214
|
-
// Stream the response
|
|
215
|
-
const reader = response.body.getReader();
|
|
216
|
-
const decoder = new TextDecoder();
|
|
217
|
-
let fullResponse = "";
|
|
218
|
-
|
|
219
|
-
while (true) {
|
|
220
|
-
const { done, value } = await reader.read();
|
|
221
|
-
if (done) break;
|
|
222
|
-
|
|
223
|
-
const chunk = decoder.decode(value, { stream: true });
|
|
224
|
-
const lines = chunk.split("\n");
|
|
225
|
-
|
|
226
|
-
for (const line of lines) {
|
|
227
|
-
if (line.startsWith("data: ")) {
|
|
228
|
-
const data = line.slice(6);
|
|
229
|
-
if (data === "[DONE]") continue;
|
|
230
|
-
|
|
231
|
-
try {
|
|
232
|
-
const parsed = JSON.parse(data);
|
|
233
|
-
const content = parsed.choices?.[0]?.delta?.content;
|
|
234
|
-
if (content) {
|
|
235
|
-
process.stdout.write(content);
|
|
236
|
-
fullResponse += content;
|
|
237
|
-
}
|
|
238
|
-
} catch {
|
|
239
|
-
// Skip malformed JSON
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
console.log(); // Final newline
|
|
246
|
-
return {
|
|
247
|
-
success: true,
|
|
248
|
-
response: fullResponse,
|
|
249
|
-
};
|
|
250
|
-
} else {
|
|
251
|
-
const data = await response.json();
|
|
252
|
-
const text = data.choices?.[0]?.message?.content ?? "";
|
|
253
|
-
|
|
254
|
-
return {
|
|
255
|
-
success: true,
|
|
256
|
-
response: text,
|
|
257
|
-
tokensUsed: data.usage?.total_tokens,
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
} catch (error) {
|
|
261
|
-
return {
|
|
262
|
-
success: false,
|
|
263
|
-
error: `Failed to call OpenAI API: ${error instanceof Error ? error.message : error}`,
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
}
|